Files
HolodoriCalc/src/routes/+layout.svelte
T
dal4segno 0bdade44e3
Publish GitHub Pages output / publish (push) Successful in 32s
fix: improve mobile navigation and calculator usability
2026-09-21 11:30:22 +09:00

150 lines
6.7 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/stores';
import AnalyticsConsent from '$lib/components/AnalyticsConsent.svelte';
import DomainMigrationNotice from '$lib/components/DomainMigrationNotice.svelte';
import { events } from '$lib/data/events.ts';
import { createInitialReleaseClock, synchronizeReleaseClock } from '$lib/data/release-clock.ts';
import { activeEventAt } from '$lib/engine/event-optimization.ts';
import '$lib/styles/layout-tokens.css';
let eventClock = createInitialReleaseClock($page.data?.serverNow);
let eventNow = eventClock.now();
let menuOpen = false;
$: if ($page.url.pathname) menuOpen = false;
$: showEventTab = Boolean(activeEventAt(events, eventNow));
onMount(() => {
let mounted = true;
void synchronizeReleaseClock().then((clock) => {
if (!mounted) return;
eventClock = clock;
eventNow = eventClock.now();
});
const timer = window.setInterval(() => eventNow = eventClock.now(), 1_000);
return () => {
mounted = false;
window.clearInterval(timer);
};
});
</script>
<DomainMigrationNotice />
<header class="site-nav">
<div class="nav-inner">
<a class="brand" href="/" aria-label="홀로도리 계산기 홈">홀로도리 계산기</a>
<button class="menu-toggle" type="button" aria-controls="primary-navigation" aria-expanded={menuOpen} on:click={() => menuOpen = !menuOpen}>{menuOpen ? '메뉴 닫기' : '전체 메뉴'}</button>
<nav id="primary-navigation" class:open={menuOpen} aria-label="주요 메뉴">
<a href="/cards" aria-current={$page.url.pathname.startsWith('/cards') ? 'page' : undefined}>카드 목록</a>
<a href="/analysis" aria-current={$page.url.pathname.startsWith('/analysis') ? 'page' : undefined}>배치 분석</a>
<a href="/rating" aria-current={$page.url.pathname.startsWith('/rating') ? 'page' : undefined}>리더 레이팅</a>
<a href="/combination" aria-current={$page.url.pathname.startsWith('/combination') ? 'page' : undefined}>조합 탐색</a>
{#if showEventTab}<a href="/event" aria-current={$page.url.pathname.startsWith('/event') ? 'page' : undefined}>이벤트</a>{/if}
<a href="/data" aria-current={$page.url.pathname.startsWith('/data') ? 'page' : undefined}>사용자 데이터</a>
<a href="/updates" aria-current={$page.url.pathname.startsWith('/updates') ? 'page' : undefined}>업데이트 내역</a>
<a href="/tools" aria-current={$page.url.pathname.startsWith('/tools') || $page.url.pathname === '/random' ? 'page' : undefined}>도구</a>
<a
class="feedback-link"
href="https://docs.google.com/forms/d/1z9c0c1crbCO6avXaYwrHZjhw2ib3zgAmW4ijJDTo4DM"
target="_blank"
rel="noreferrer"
aria-label="버그 제보 Google 폼을 새 탭에서 열기"
>버그 제보 <span aria-hidden="true">↗</span></a>
<a
class="feedback-link"
href="https://docs.google.com/forms/d/16NcTst7Rpa9XzqYIfPa4t9JF2X0CMfYzEbZrT8PLqco"
target="_blank"
rel="noreferrer"
aria-label="기능 건의 Google 폼을 새 탭에서 열기"
>기능 건의 <span aria-hidden="true">↗</span></a>
<a
class="report-link"
href="https://docs.google.com/forms/d/1bShzsxNN-iTk0uU3RfMfmLkYXYmUTmIWMmCtQmfl_Ik"
target="_blank"
rel="noreferrer"
aria-label="실측값 제보 Google 폼을 새 탭에서 열기"
>실측값 제보 <span aria-hidden="true">↗</span></a>
</nav>
</div>
</header>
<slot />
<AnalyticsConsent pagePath={`${$page.url.pathname}${$page.url.search}`} />
<style>
:global(*) { box-sizing: border-box; }
:global(body) { margin: 0; background: #101319; color: #e8ebf1; font-family: Pretendard, "Noto Sans KR", sans-serif; }
.site-nav {
position: sticky;
z-index: 20;
top: 0;
border-bottom: 1px solid rgb(255 255 255 / 8%);
background: rgb(16 19 25 / 88%);
backdrop-filter: blur(14px);
}
.nav-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
width: min(var(--page-max-width), calc(100% - var(--page-gutter)));
min-height: 62px;
margin: 0 auto;
}
.brand {
color: #f3f5f8;
font-size: 16px;
font-weight: 850;
letter-spacing: -.03em;
text-decoration: none;
}
.brand { flex-shrink: 0; }
.menu-toggle { display: none; }
nav { display: flex; min-width: 0; flex: 1; align-items: center; justify-content: safe flex-end; gap: 4px; overflow-x: auto; scrollbar-width: thin; }
nav::-webkit-scrollbar { display: none; }
nav a {
flex: 0 0 auto;
padding: 8px 12px;
border-radius: 9px;
color: #929cad;
font-size: 13px;
font-weight: 750;
text-decoration: none;
white-space: nowrap;
}
nav a:hover { color: #e2e6ed; background: rgb(255 255 255 / 5%); }
nav a[aria-current='page'] { color: #f1a1c3; background: rgb(239 139 182 / 12%); }
.feedback-link { border: 1px solid #9f5877; color: #edb8cf; background: linear-gradient(135deg, #482c39, #38232e); box-shadow: 0 0 12px rgb(175 91 128 / 10%); }
.feedback-link:hover { color: #f6d5e3; background: linear-gradient(135deg, #583444, #472a37); box-shadow: 0 0 16px rgb(175 91 128 / 18%); }
.report-link { flex: 0 0 auto; padding: 8px 12px; border: 1px solid #ef8bb6; border-radius: 9px; color: #21121a; background: linear-gradient(135deg, #f3a3c5, #e77ead); box-shadow: 0 0 18px rgb(239 139 182 / 18%); font-size: 13px; font-weight: 800; text-decoration: none; white-space: nowrap; }
.report-link:hover { color: #160d12; background: linear-gradient(135deg, #f8bad3, #ee94ba); box-shadow: 0 0 22px rgb(239 139 182 / 30%); }
.report-link span { margin-left: 2px; font-size: 11px; }
a:focus-visible { outline: 3px solid rgb(239 139 182 / 28%); outline-offset: 2px; }
@media (max-width: 1100px) {
.nav-inner { flex-wrap: wrap; padding-block: 5px; }
.menu-toggle { display: block; min-height: 44px; padding: 8px 14px; border: 1px solid #694256; border-radius: 9px; color: #f1a1c3; background: #29202a; font: inherit; font-size: 14px; cursor: pointer; }
.menu-toggle:focus-visible { outline: 3px solid #ef8bb6; outline-offset: 2px; }
nav { display: none; flex: 0 0 100%; grid-template-columns: repeat(2, minmax(0, 1fr)); max-height: 65dvh; overflow-y: auto; padding: 4px 2px 10px; }
nav.open { display: grid; }
nav a { display: flex; align-items: center; min-height: 44px; white-space: normal; }
}
@media (max-width: 560px) {
:global(:root) { --page-gutter: 24px; }
.nav-inner { width: calc(100% - 24px); min-height: 54px; }
.brand { font-size: 13px; }
nav { gap: 2px; }
nav a { padding: 7px 7px; font-size: 12px; }
.report-link { padding: 7px 8px; font-size: 12px; }
}
</style>