모바일 반응형 페이지 css 수정 및 세부페이지 작업
This commit is contained in:
parent
8d7d25cce8
commit
014597e4c2
@ -25,6 +25,21 @@ return [
|
||||
],
|
||||
],
|
||||
|
||||
'main_nav' => [
|
||||
['label' => '홈', 'route' => 'web.home', 'key' => 'home'],
|
||||
['label' => '상품권 구매', 'route' => 'web.buy.index', 'key' => 'buy'],
|
||||
['label' => '상품권 판매', 'route' => 'web.sell.index', 'key' => 'sell'],
|
||||
['label' => '거래내역', 'route' => 'web.trade.index', 'key' => 'trade'],
|
||||
],
|
||||
|
||||
// 모바일 드로어에서 보여줄 “섹션 구성”
|
||||
'drawer_sections' => [
|
||||
'main' => ['title' => '메뉴', 'items' => 'main_nav'],
|
||||
'cs' => ['title' => '고객센터', 'items' => 'cs_tabs'],
|
||||
'policy' => ['title' => '약관 및 정책', 'items' => 'policy_tabs'],
|
||||
'mypage' => ['title' => '마이페이지', 'items' => 'mypage_tabs'],
|
||||
],
|
||||
|
||||
'cs_nav' => [
|
||||
'title' => '고객센터',
|
||||
'subtitle' => '문의 및 안내', // 필요하면 '안내/문의' 같은 문구 넣어도 됨
|
||||
|
||||
@ -1222,6 +1222,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||
gap: 12px;
|
||||
margin: 0px 0 16px;
|
||||
padding-left:8px;
|
||||
border-bottom: 1px solid rgba(0,0,0,.08);
|
||||
}
|
||||
.subpage-title{
|
||||
font-size: 18px;
|
||||
@ -2377,3 +2378,358 @@ img, video, svg, iframe, canvas { max-width: 100%; height: auto; }
|
||||
/* If any component uses nowrap, allow scroll inside it */
|
||||
.policy-toc{ max-width: 100%; }
|
||||
|
||||
/* =========================
|
||||
Mobile Tabs (subnav--tabs)
|
||||
- no horizontal overflow
|
||||
- centered text
|
||||
- menu-like segmented buttons
|
||||
========================= */
|
||||
|
||||
/* 기본: 모바일에서 2열 그리드(안전하게 안 넘침) */
|
||||
.subnav--tabs{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 화면 조금 넓으면 3열 */
|
||||
@media (min-width: 420px){
|
||||
.subnav--tabs{
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* 탭(메뉴) 버튼 */
|
||||
.subnav-tab{
|
||||
display: flex;
|
||||
align-items: center; /* ✅ 세로 중앙 */
|
||||
justify-content: center; /* ✅ 가로 중앙 */
|
||||
text-align: center;
|
||||
|
||||
min-width: 0; /* ✅ grid item overflow 방지 */
|
||||
min-height: 42px;
|
||||
|
||||
padding: 10px 10px;
|
||||
border-radius: 14px;
|
||||
|
||||
border: 1px solid rgba(0,0,0,.10);
|
||||
background: #fff;
|
||||
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
|
||||
color: rgba(0,0,0,.78);
|
||||
text-decoration: none;
|
||||
|
||||
/* ✅ 한 줄 유지 + 길면 ... */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
/* ✅ '메뉴'같은 클릭감 */
|
||||
transition: background-color .12s ease, border-color .12s ease, color .12s ease, transform .06s ease;
|
||||
}
|
||||
|
||||
.subnav-tab:hover{
|
||||
background: rgba(0,0,0,.02);
|
||||
}
|
||||
|
||||
.subnav-tab:active{
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
/* 활성 탭 */
|
||||
.subnav-tab.is-active{
|
||||
background: rgba(11, 99, 255, .08);
|
||||
border-color: rgba(11, 99, 255, .35);
|
||||
color: #0b63ff;
|
||||
box-shadow: inset 0 0 0 1px rgba(11, 99, 255, .18);
|
||||
}
|
||||
|
||||
/* 접근성: 키보드 포커스 */
|
||||
.subnav-tab:focus-visible{
|
||||
outline: 2px solid rgba(11, 99, 255, .55);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Subpage Header layout */
|
||||
.subpage-header{
|
||||
display:flex;
|
||||
align-items:baseline; /* 제목/설명 기준선 맞춤 */
|
||||
gap:14px;
|
||||
padding: 10px 0 14px;
|
||||
}
|
||||
|
||||
.subpage-title{
|
||||
margin:0;
|
||||
padding-top:7px;
|
||||
font-weight:900;
|
||||
letter-spacing:-0.03em;
|
||||
}
|
||||
|
||||
.subpage-desc{
|
||||
margin:0;
|
||||
color: rgba(0,0,0,.65);
|
||||
line-height:1.6;
|
||||
|
||||
/* ✅ 데스크톱에서는 한 줄에 잘리게(캡처처럼) */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* ✅ 모바일: desc를 아래로 떨어뜨리기 */
|
||||
@media (max-width: 640px){
|
||||
.subpage-header{
|
||||
flex-direction: column;
|
||||
align-items:flex-start;
|
||||
gap:6px;
|
||||
}
|
||||
.subpage-desc{
|
||||
white-space: normal; /* 모바일에서는 줄바꿈 허용 */
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================
|
||||
Mobile Drawer
|
||||
========================= */
|
||||
|
||||
/* 기본은 숨김(PC) */
|
||||
.mobile-menu-btn { display:none; }
|
||||
|
||||
/* 모바일에서만 노출 */
|
||||
@media (max-width: 960px){
|
||||
.mobile-menu-btn{
|
||||
display:inline-flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
width:42px;
|
||||
height:42px;
|
||||
border:1px solid rgba(0,0,0,.10);
|
||||
background:#fff;
|
||||
border-radius:12px;
|
||||
cursor:pointer;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlay */
|
||||
.mobile-drawer-overlay{
|
||||
position:fixed;
|
||||
inset:0;
|
||||
background: rgba(0,0,0,.35);
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
/* Drawer */
|
||||
.mobile-drawer{
|
||||
position:fixed;
|
||||
top:0;
|
||||
right:0;
|
||||
height:100dvh;
|
||||
width:min(86vw, 360px);
|
||||
background:#fff;
|
||||
z-index:9999;
|
||||
|
||||
transform: translateX(102%);
|
||||
transition: transform .18s ease;
|
||||
box-shadow: -16px 0 40px rgba(0,0,0,.18);
|
||||
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
}
|
||||
|
||||
.mobile-drawer.is-open{
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.mobile-drawer__head{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
padding:14px 14px;
|
||||
border-bottom:1px solid rgba(0,0,0,.08);
|
||||
}
|
||||
|
||||
.mobile-drawer__title{
|
||||
font-weight:900;
|
||||
letter-spacing:-0.02em;
|
||||
}
|
||||
|
||||
.mobile-drawer__close{
|
||||
width:38px;
|
||||
height:38px;
|
||||
border-radius:10px;
|
||||
border:1px solid rgba(0,0,0,.10);
|
||||
background:#fff;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.mobile-drawer__nav{
|
||||
padding:10px 10px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:8px;
|
||||
}
|
||||
|
||||
.mobile-drawer__link{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
height:44px;
|
||||
padding:0 12px;
|
||||
border-radius:12px;
|
||||
text-decoration:none;
|
||||
color: rgba(0,0,0,.86);
|
||||
border:1px solid rgba(0,0,0,.08);
|
||||
background: rgba(0,0,0,.02);
|
||||
font-weight:800;
|
||||
}
|
||||
|
||||
.mobile-drawer__link:hover{
|
||||
background: rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.mobile-drawer__foot{
|
||||
margin-top:auto;
|
||||
padding:12px;
|
||||
border-top:1px solid rgba(0,0,0,.08);
|
||||
}
|
||||
|
||||
.mobile-drawer__cta{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
height:44px;
|
||||
border-radius:14px;
|
||||
background:#0b63ff;
|
||||
color:#fff;
|
||||
text-decoration:none;
|
||||
font-weight:900;
|
||||
}
|
||||
|
||||
/* 스크롤 잠금 */
|
||||
body.is-drawer-open{
|
||||
overflow:hidden;
|
||||
touch-action:none;
|
||||
}
|
||||
|
||||
/* ===== Mobile Drawer Modern UI ===== */
|
||||
|
||||
.mobile-drawer__body{
|
||||
padding: 10px 12px 16px;
|
||||
overflow:auto;
|
||||
}
|
||||
|
||||
.m-usercard{
|
||||
margin: 12px;
|
||||
padding: 12px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(135deg, rgba(11,99,255,.10), rgba(0,0,0,.02));
|
||||
border: 1px solid rgba(0,0,0,.08);
|
||||
}
|
||||
|
||||
.m-usercard__row{
|
||||
display:flex;
|
||||
gap: 10px;
|
||||
align-items:center;
|
||||
}
|
||||
|
||||
.m-avatar{
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 14px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
background: #0b63ff;
|
||||
color:#fff;
|
||||
font-weight: 900;
|
||||
}
|
||||
.m-avatar--ghost{
|
||||
background: rgba(0,0,0,.06);
|
||||
color: rgba(0,0,0,.55);
|
||||
}
|
||||
|
||||
.m-usercard__name{
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.m-usercard__meta{
|
||||
font-size: 12px;
|
||||
color: rgba(0,0,0,.65);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.m-usercard__actions{
|
||||
display:flex;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.m-pill{
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
display:inline-flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
border-radius: 999px;
|
||||
background:#0b63ff;
|
||||
color:#fff;
|
||||
text-decoration:none;
|
||||
font-weight: 900;
|
||||
border: 1px solid rgba(11,99,255,.35);
|
||||
}
|
||||
.m-pill--ghost{
|
||||
background:#fff;
|
||||
color:#0b63ff;
|
||||
}
|
||||
|
||||
.m-navsec{
|
||||
padding: 0 12px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.m-navsec__title{
|
||||
font-weight: 900;
|
||||
margin: 0 0 8px;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.m-navsec__hint{
|
||||
font-size: 12px;
|
||||
color: rgba(0,0,0,.60);
|
||||
margin: -4px 0 10px;
|
||||
}
|
||||
|
||||
.m-grid{
|
||||
display:grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.m-tile{
|
||||
height: 35px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(0,0,0,.08);
|
||||
background: #fff;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
font-size:15px;
|
||||
font-weight: 600;
|
||||
text-decoration:none;
|
||||
color: rgba(0,0,0,.84);
|
||||
transition: transform .06s ease, background-color .12s ease;
|
||||
min-width:0;
|
||||
text-align:center;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.m-tile:hover{ background: rgba(0,0,0,.02); }
|
||||
.m-tile:active{ transform: translateY(1px); }
|
||||
|
||||
.m-tile--ghost{
|
||||
background: rgba(0,0,0,.02);
|
||||
}
|
||||
|
||||
@ -117,14 +117,45 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
// --- Mobile Menu Toggle ---
|
||||
const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
|
||||
// In a real implementation, we would toggle a drawer here.
|
||||
// For this prototype, we'll just log it or alert.
|
||||
if(mobileMenuBtn) {
|
||||
mobileMenuBtn.addEventListener('click', () => {
|
||||
alert('모바일 메뉴 드로어 열림 (구현 예정)');
|
||||
// --- Mobile Drawer Toggle ---
|
||||
(() => {
|
||||
const btn = document.querySelector('.mobile-menu-btn');
|
||||
const drawer = document.querySelector('.mobile-drawer');
|
||||
const overlay = document.querySelector('.mobile-drawer-overlay');
|
||||
|
||||
if (!btn || !drawer || !overlay) return;
|
||||
|
||||
const open = () => {
|
||||
drawer.classList.add('is-open');
|
||||
overlay.hidden = false;
|
||||
drawer.setAttribute('aria-hidden', 'false');
|
||||
btn.setAttribute('aria-expanded', 'true');
|
||||
document.body.classList.add('is-drawer-open');
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
drawer.classList.remove('is-open');
|
||||
overlay.hidden = true;
|
||||
drawer.setAttribute('aria-hidden', 'true');
|
||||
btn.setAttribute('aria-expanded', 'false');
|
||||
document.body.classList.remove('is-drawer-open');
|
||||
};
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
drawer.classList.contains('is-open') ? close() : open();
|
||||
});
|
||||
}
|
||||
|
||||
// overlay 클릭 or 닫기 버튼
|
||||
document.querySelectorAll('[data-drawer-close]').forEach(el => {
|
||||
el.addEventListener('click', close);
|
||||
});
|
||||
|
||||
// ESC 닫기
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && drawer.classList.contains('is-open')) close();
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
// --- Sub Hero Carousel ---
|
||||
(function initSubHero(){
|
||||
|
||||
@ -37,14 +37,19 @@
|
||||
<a href="/register" class="btn btn-primary" style="padding: 8px 20px;">회원가입</a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Toggle (Hidden on Desktop) -->
|
||||
<button class="mobile-menu-btn" aria-label="메뉴 열기" style="display: none;">
|
||||
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16">
|
||||
</path>
|
||||
<!-- Mobile Menu Toggle -->
|
||||
<button class="mobile-menu-btn" type="button" aria-label="메뉴 열기" aria-controls="mobile-drawer" aria-expanded="false">
|
||||
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Mobile Drawer -->
|
||||
@include('web.partials.mobile-drawer')
|
||||
|
||||
<!-- Mobile Drawer Overlay -->
|
||||
<div class="mobile-drawer-overlay" data-drawer-close hidden></div>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
97
resources/views/web/partials/mobile-drawer.blade.php
Normal file
97
resources/views/web/partials/mobile-drawer.blade.php
Normal file
@ -0,0 +1,97 @@
|
||||
@php
|
||||
$sections = config('web.drawer_sections', []);
|
||||
$isLoggedIn = auth()->check();
|
||||
$user = auth()->user();
|
||||
@endphp
|
||||
|
||||
{{-- Overlay --}}
|
||||
<div class="mobile-drawer-overlay" data-drawer-close hidden></div>
|
||||
|
||||
{{-- Drawer --}}
|
||||
<aside class="mobile-drawer" id="mobile-drawer" aria-hidden="true">
|
||||
<div class="mobile-drawer__head">
|
||||
<div class="mobile-drawer__title">PIN FOR YOU</div>
|
||||
<button class="mobile-drawer__close" type="button" aria-label="메뉴 닫기" data-drawer-close>✕</button>
|
||||
</div>
|
||||
|
||||
{{-- ✅ 트렌디한 “프로필 카드”: 로그인 전/후 UI 분기 --}}
|
||||
<div class="m-usercard">
|
||||
@if($isLoggedIn)
|
||||
<div class="m-usercard__row">
|
||||
<div class="m-avatar" aria-hidden="true">{{ mb_substr($user->name ?? 'U', 0, 1) }}</div>
|
||||
<div class="m-usercard__info">
|
||||
<div class="m-usercard__name">{{ $user->name ?? '회원' }}</div>
|
||||
<div class="m-usercard__meta">{{ $user->email ?? '' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-usercard__actions">
|
||||
<a class="m-pill" href="{{ route('web.mypage.info.index') }}">내 정보</a>
|
||||
{{-- <a class="m-pill m-pill--ghost" href="{{ route('web.auth.logout') }}">로그아웃</a> --}}
|
||||
</div>
|
||||
@else
|
||||
<div class="m-usercard__row">
|
||||
<div class="m-avatar m-avatar--ghost" aria-hidden="true">🔒</div>
|
||||
<div class="m-usercard__info">
|
||||
<div class="m-usercard__name">로그인이 필요해요</div>
|
||||
<div class="m-usercard__meta">내역 확인/1:1문의는 로그인 후 이용 가능</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-usercard__actions">
|
||||
<a class="m-pill" href="{{ route('web.auth.login') }}">로그인</a>
|
||||
<a class="m-pill m-pill--ghost" href="{{ route('web.auth.register') }}">회원가입</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- ✅ 메뉴 섹션(주메뉴/CS/정책/마이페이지) --}}
|
||||
<div class="mobile-drawer__body">
|
||||
@foreach($sections as $secKey => $sec)
|
||||
@php
|
||||
$title = $sec['title'] ?? '';
|
||||
$itemsKey = $sec['items'] ?? null;
|
||||
$rawItems = $itemsKey ? config('web.' . $itemsKey, []) : [];
|
||||
$items = collect($rawItems)->map(function($it){
|
||||
$url = '#';
|
||||
if (!empty($it['route'])) {
|
||||
try { $url = route($it['route']); } catch (\Throwable $e) { $url = '#'; }
|
||||
} elseif (!empty($it['url'])) {
|
||||
$url = $it['url'];
|
||||
}
|
||||
return [
|
||||
'label' => $it['label'] ?? '',
|
||||
'url' => $url,
|
||||
];
|
||||
})->values()->all();
|
||||
@endphp
|
||||
|
||||
{{-- 마이페이지는 “로그인 전”에 섹션 자체를 살짝 줄여 보여도 됨 --}}
|
||||
@if($secKey === 'mypage' && !$isLoggedIn)
|
||||
{{-- 로그인 전에는 마이페이지 섹션은 보여주되, 안내 문구를 둠 --}}
|
||||
<section class="m-navsec">
|
||||
<div class="m-navsec__title">{{ $title }}</div>
|
||||
<div class="m-navsec__hint">로그인 후 이용내역/교환내역을 확인할 수 있어요.</div>
|
||||
|
||||
<div class="m-grid">
|
||||
<a class="m-pill" href="{{ route('web.auth.login') }}">로그인</a>
|
||||
<a class="m-pill m-pill--ghost" href="{{ route('web.auth.register') }}">회원가입</a>
|
||||
</div>
|
||||
</section>
|
||||
@continue
|
||||
@endif
|
||||
|
||||
<section class="m-navsec">
|
||||
<div class="m-navsec__title">{{ $title }}</div>
|
||||
<div class="m-grid">
|
||||
@foreach($items as $it)
|
||||
<a class="m-tile" href="{{ $it['url'] }}">{{ $it['label'] }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- Footer CTA --}}
|
||||
<div class="mobile-drawer__foot">
|
||||
<a class="mobile-drawer__cta" href="{{ route('web.cs.qna.index') }}">1:1 문의하기</a>
|
||||
</div>
|
||||
</aside>
|
||||
@ -1,10 +1,6 @@
|
||||
<?php
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', function () {
|
||||
return view('web.home');
|
||||
})->name('web.home');
|
||||
|
||||
Route::view('/', 'web.home')->name('web.home');
|
||||
|
||||
Route::prefix('cs')->name('web.cs.')->group(function () {
|
||||
@ -29,4 +25,13 @@ Route::prefix('policy')->name('web.policy.')->group(function () {
|
||||
Route::view('/email', 'web.policy.email.index')->name('email.index');
|
||||
});
|
||||
|
||||
Route::get('/login', function () {
|
||||
return view('web.auth.login'); // 너가 만든 로그인 뷰로 변경
|
||||
})->name('web.auth.login');
|
||||
|
||||
Route::get('/register', function () {
|
||||
return view('web.auth.register'); // 회원가입 뷰로 변경
|
||||
})->name('web.auth.register');
|
||||
|
||||
|
||||
Route::fallback(fn () => abort(404));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user