Skip to content

Commit 3a30196

Browse files
committed
fix(ui): unify theme switching and mobile navigation
1 parent 76c6df9 commit 3a30196

3 files changed

Lines changed: 175 additions & 35 deletions

File tree

src/components/SpotlightHeader.astro

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,22 @@ const navigation = [
3030
<div class="flex items-center gap-2">
3131
<button
3232
type="button"
33-
id="theme-toggle"
33+
data-theme-toggle
3434
class="rounded-full border px-3 py-1.5 text-xs font-medium transition spotlight-header__button hover:bg-black/5 dark:hover:bg-white/10"
3535
style="border-color: var(--line); color: var(--text);"
3636
aria-label="切换主题"
3737
>
38-
主题
38+
<span data-theme-toggle-label>深色</span>
3939
</button>
4040

4141
<details class="relative md:hidden">
42-
<summary class="cursor-pointer list-none rounded-full border px-3 py-1.5 text-xs font-medium spotlight-header__button" style="border-color: var(--line); color: var(--text);">菜单</summary>
42+
<summary class="cursor-pointer list-none rounded-full border px-3 py-1.5 text-xs font-medium spotlight-header__button spotlight-header__menu-toggle" style="border-color: var(--line); color: var(--text);" aria-label="导航">
43+
<span class="spotlight-header__burger" aria-hidden="true">
44+
<span></span>
45+
<span></span>
46+
<span></span>
47+
</span>
48+
</summary>
4349
<nav class="absolute right-5 mt-2 min-w-36 rounded-xl border bg-[var(--surface)] p-2 text-sm shadow-sm spotlight-header__menu" style="border-color: var(--line);">
4450
{navigation.map((item) => (
4551
<a href={item.href} class="block rounded-lg px-3 py-2 muted hover:bg-black/5 hover:text-[var(--text)] dark:hover:bg-white/5 spotlight-header__menu-link" data-astro-prefetch>{item.name}</a>
@@ -84,23 +90,25 @@ const navigation = [
8490
.spotlight-header--inverse .spotlight-header__menu-link:hover {
8591
background: rgba(255, 255, 255, 0.05);
8692
}
87-
</style>
8893

89-
<script is:inline>
90-
(function () {
91-
if (
92-
localStorage.getItem('theme') === 'dark' ||
93-
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)
94-
) {
95-
document.documentElement.classList.add('dark');
96-
}
97-
})();
98-
</script>
94+
.spotlight-header__menu-toggle {
95+
display: inline-flex;
96+
align-items: center;
97+
justify-content: center;
98+
min-width: 2.5rem;
99+
min-height: 2.5rem;
100+
}
101+
102+
.spotlight-header__burger {
103+
display: inline-grid;
104+
gap: 0.22rem;
105+
}
99106

100-
<script>
101-
const toggle = document.getElementById('theme-toggle');
102-
toggle?.addEventListener('click', () => {
103-
document.documentElement.classList.toggle('dark');
104-
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light');
105-
});
106-
</script>
107+
.spotlight-header__burger span {
108+
display: block;
109+
width: 0.95rem;
110+
height: 1.5px;
111+
border-radius: 9999px;
112+
background: currentColor;
113+
}
114+
</style>

src/layouts/BaseLayout.astro

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ const websiteStructuredData = Astro.url.pathname === '/' ? generateWebsiteStruct
7474
<ClientRouter />
7575
<script is:inline>
7676
(function () {
77+
const storageKey = 'theme';
78+
const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
79+
const savedTheme = localStorage.getItem(storageKey);
80+
const theme = savedTheme || (darkQuery.matches ? 'dark' : 'light');
81+
document.documentElement.dataset.theme = theme;
82+
document.documentElement.classList.toggle('dark', theme === 'dark');
83+
7784
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
7885
if (!connection) return;
7986

@@ -97,6 +104,42 @@ const websiteStructuredData = Astro.url.pathname === '/' ? generateWebsiteStruct
97104

98105
<!-- Service Worker Registration -->
99106
<script>
107+
(() => {
108+
const storageKey = 'theme';
109+
const themeColorMeta = document.querySelector('meta[name="theme-color"]');
110+
111+
function updateThemeUi(theme) {
112+
document.documentElement.dataset.theme = theme;
113+
document.documentElement.classList.toggle('dark', theme === 'dark');
114+
localStorage.setItem(storageKey, theme);
115+
if (themeColorMeta) {
116+
themeColorMeta.setAttribute('content', theme === 'dark' ? '#080c12' : '#f97316');
117+
}
118+
document.querySelectorAll('[data-theme-toggle-label]').forEach((node) => {
119+
node.textContent = theme === 'dark' ? '浅色' : '深色';
120+
});
121+
}
122+
123+
function currentTheme() {
124+
return document.documentElement.classList.contains('dark') ? 'dark' : 'light';
125+
}
126+
127+
function bindThemeToggles() {
128+
document.querySelectorAll('[data-theme-toggle]').forEach((button) => {
129+
if (button.dataset.themeBound === 'true') return;
130+
button.dataset.themeBound = 'true';
131+
button.addEventListener('click', () => {
132+
updateThemeUi(currentTheme() === 'dark' ? 'light' : 'dark');
133+
});
134+
});
135+
136+
updateThemeUi(currentTheme());
137+
}
138+
139+
bindThemeToggles();
140+
document.addEventListener('astro:page-load', bindThemeToggles);
141+
})();
142+
100143
if ('serviceWorker' in navigator) {
101144
let refreshing = false;
102145

src/layouts/PhotographyLayout.astro

Lines changed: 103 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,33 @@ const navigation = [
7171
})}
7272
</nav>
7373

74-
<button
75-
type="button"
76-
id="photo-theme-toggle"
77-
class="photo-topbar__toggle"
78-
aria-label="切换主题"
79-
>
80-
主题
81-
</button>
74+
<div class="photo-topbar__actions">
75+
<button
76+
type="button"
77+
data-theme-toggle
78+
class="photo-topbar__toggle"
79+
aria-label="切换主题"
80+
>
81+
<span data-theme-toggle-label>浅色</span>
82+
</button>
83+
84+
<details class="photo-topbar__menu-shell">
85+
<summary class="photo-topbar__menu-toggle" aria-label="导航">
86+
<span class="photo-topbar__burger" aria-hidden="true">
87+
<span></span>
88+
<span></span>
89+
<span></span>
90+
</span>
91+
</summary>
92+
<nav class="photo-topbar__menu" aria-label="移动导航">
93+
{navigation.map((item) => (
94+
<a href={item.href} class={`photo-topbar__menu-link ${currentPath === item.href || (item.href === '/photography/' && currentPath.startsWith('/posts/')) ? 'is-active' : ''}`} data-astro-prefetch>
95+
{item.name}
96+
</a>
97+
))}
98+
</nav>
99+
</details>
100+
</div>
82101
</div>
83102
</header>
84103

@@ -340,6 +359,12 @@ const navigation = [
340359
color: rgba(248, 250, 252, 0.96);
341360
}
342361

362+
.photo-topbar__actions {
363+
display: flex;
364+
align-items: center;
365+
gap: 0.65rem;
366+
}
367+
343368
.photo-topbar__nav {
344369
display: none;
345370
align-items: center;
@@ -378,6 +403,68 @@ const navigation = [
378403
cursor: pointer;
379404
}
380405

406+
.photo-topbar__menu-shell {
407+
position: relative;
408+
}
409+
410+
.photo-topbar__menu-toggle {
411+
display: inline-flex;
412+
align-items: center;
413+
justify-content: center;
414+
min-width: 2.5rem;
415+
min-height: 2.5rem;
416+
border: 1px solid rgba(255, 255, 255, 0.12);
417+
border-radius: 9999px;
418+
background: transparent;
419+
color: rgba(248, 250, 252, 0.9);
420+
cursor: pointer;
421+
list-style: none;
422+
}
423+
424+
.photo-topbar__menu-toggle::-webkit-details-marker {
425+
display: none;
426+
}
427+
428+
.photo-topbar__burger {
429+
display: inline-grid;
430+
gap: 0.22rem;
431+
}
432+
433+
.photo-topbar__burger span {
434+
display: block;
435+
width: 0.95rem;
436+
height: 1.5px;
437+
border-radius: 9999px;
438+
background: currentColor;
439+
}
440+
441+
.photo-topbar__menu {
442+
position: absolute;
443+
right: 0;
444+
margin-top: 0.6rem;
445+
min-width: 9rem;
446+
padding: 0.45rem;
447+
border: 1px solid rgba(255, 255, 255, 0.08);
448+
border-radius: 1rem;
449+
background: rgba(12, 17, 24, 0.96);
450+
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.28);
451+
backdrop-filter: blur(14px);
452+
}
453+
454+
.photo-topbar__menu-link {
455+
display: block;
456+
padding: 0.65rem 0.85rem;
457+
border-radius: 0.8rem;
458+
color: rgba(226, 232, 240, 0.78);
459+
font-size: 0.92rem;
460+
}
461+
462+
.photo-topbar__menu-link.is-active,
463+
.photo-topbar__menu-link:hover {
464+
background: rgba(255, 255, 255, 0.06);
465+
color: rgba(248, 250, 252, 0.96);
466+
}
467+
381468
.photo-hero {
382469
position: relative;
383470
min-height: calc(100vh - 4.4rem);
@@ -976,6 +1063,10 @@ const navigation = [
9761063
display: flex;
9771064
}
9781065

1066+
.photo-topbar__menu-shell {
1067+
display: none;
1068+
}
1069+
9791070
.photo-hero__overlay--bottom {
9801071
grid-template-columns: minmax(0, 1.45fr) minmax(18rem, 0.72fr);
9811072
gap: 2rem;
@@ -1047,6 +1138,10 @@ const navigation = [
10471138
padding-inline: 0.9rem;
10481139
}
10491140

1141+
.photo-topbar__toggle {
1142+
padding-inline: 0.8rem;
1143+
}
1144+
10501145
.photo-hero,
10511146
.photo-hero__frame {
10521147
min-height: calc(100svh - 4.4rem);
@@ -1139,12 +1234,6 @@ const navigation = [
11391234
</style>
11401235

11411236
<script>
1142-
const toggle = document.getElementById('photo-theme-toggle');
1143-
toggle?.addEventListener('click', () => {
1144-
document.documentElement.classList.toggle('dark');
1145-
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light');
1146-
});
1147-
11481237
const galleryRoot = document.querySelector('.photo-page');
11491238
const photoRailLinks = Array.from(document.querySelectorAll('[data-photo-link]'));
11501239
const photoPanels = Array.from(document.querySelectorAll('[data-photo-panel]'));

0 commit comments

Comments
 (0)