@@ -23,8 +23,9 @@ const t = (key: string) => {
2323
2424const isLanguageOpen = ref (false );
2525const isMenuOpen = ref (false );
26- const navbarBg = ref (' rgba(5, 7, 18, 0.72) ' );
26+ const isScrolled = ref (false );
2727const activeSection = ref (' ' );
28+ const theme = ref <' dark' | ' light' >(' dark' );
2829
2930const languageLabel = computed (() => t (` language.${props .lang } ` ));
3031
@@ -106,7 +107,32 @@ function onLogoClick(event: MouseEvent) {
106107}
107108
108109function updateNavbarBg() {
109- navbarBg .value = window .scrollY > 100 ? ' rgba(5, 7, 18, 0.82)' : ' rgba(5, 7, 18, 0.72)' ;
110+ isScrolled .value = window .scrollY > 100 ;
111+ }
112+
113+ function toggleTheme() {
114+ const w = window as any ;
115+ if (w .__rushdbTheme ) {
116+ w .__rushdbTheme .toggle ();
117+ }
118+ }
119+
120+ function syncThemeState() {
121+ const current = document .documentElement .getAttribute (' data-theme' ) || ' dark' ;
122+ theme .value = current as ' dark' | ' light' ;
123+ }
124+
125+ function onThemeChange() {
126+ syncThemeState ();
127+ }
128+
129+ function onStorageChange(e : StorageEvent ) {
130+ if (e .key !== ' rushdb-theme' ) return ;
131+ const w = window as any ;
132+ if (w .__rushdbTheme && e .newValue ) {
133+ w .__rushdbTheme .set (e .newValue , false );
134+ }
135+ syncThemeState ();
110136}
111137
112138function onKeydown(e : KeyboardEvent ) {
@@ -174,15 +200,20 @@ function setupNavHighlightObserver() {
174200}
175201
176202onMounted (() => {
203+ syncThemeState ();
177204 updateNavbarBg ();
178205 window .addEventListener (' scroll' , updateNavbarBg , { passive: true });
206+ window .addEventListener (' rushdb-theme-change' , onThemeChange );
207+ window .addEventListener (' storage' , onStorageChange );
179208 document .addEventListener (' keydown' , onKeydown );
180209 document .addEventListener (' click' , onDocumentClick );
181210 setupNavHighlightObserver ();
182211});
183212
184213onUnmounted (() => {
185214 window .removeEventListener (' scroll' , updateNavbarBg );
215+ window .removeEventListener (' rushdb-theme-change' , onThemeChange );
216+ window .removeEventListener (' storage' , onStorageChange );
186217 document .removeEventListener (' keydown' , onKeydown );
187218 document .removeEventListener (' click' , onDocumentClick );
188219 navHighlightObserver ?.disconnect ();
@@ -191,7 +222,7 @@ onUnmounted(() => {
191222 </script >
192223
193224<template >
194- <nav class =" navbar" :class =" { 'menu-open': isMenuOpen } " :style = " { background: navbarBg }" role =" navigation" :aria-label =" t('a11y.mainNavigation')" >
225+ <nav class =" navbar" :class =" { 'menu-open': isMenuOpen, 'is-scrolled': isScrolled }" role =" navigation" :aria-label =" t('a11y.mainNavigation')" >
195226 <div class =" nav-content" >
196227 <a :href =" homePath" class =" nav-logo" @click =" onLogoClick" >
197228 <img src =" /RushDB.png" alt =" RushDB Logo" class =" nav-logo-img" />
@@ -224,6 +255,20 @@ onUnmounted(() => {
224255 </li >
225256 </ul >
226257
258+ <button
259+ class =" theme-toggle"
260+ type =" button"
261+ :aria-label =" theme === 'dark' ? t('nav.switchToLight') : t('nav.switchToDark')"
262+ @click =" toggleTheme"
263+ >
264+ <svg v-if =" theme === 'dark'" class =" theme-toggle-icon" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" >
265+ <path d =" M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
266+ </svg >
267+ <svg v-else class =" theme-toggle-icon" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" >
268+ <path d =" M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
269+ </svg >
270+ </button >
271+
227272 <div class =" language-switcher" :class =" { show: isLanguageOpen }" >
228273 <div class =" language-trigger" @click =" toggleLanguageSwitcher" >{{ languageLabel }}</div >
229274 <div class =" language-dropdown" >
@@ -294,6 +339,19 @@ onUnmounted(() => {
294339 </ul >
295340
296341 <div class =" nav-mobile-actions" >
342+ <button
343+ class =" theme-toggle"
344+ type =" button"
345+ :aria-label =" theme === 'dark' ? t('nav.switchToLight') : t('nav.switchToDark')"
346+ @click =" toggleTheme"
347+ >
348+ <svg v-if =" theme === 'dark'" class =" theme-toggle-icon" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" >
349+ <path d =" M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
350+ </svg >
351+ <svg v-else class =" theme-toggle-icon" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" >
352+ <path d =" M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
353+ </svg >
354+ </button >
297355 <div class =" language-switcher mobile-lang" :class =" { show: isLanguageOpen }" >
298356 <div class =" language-trigger" @click =" toggleLanguageSwitcher" >{{ languageLabel }}</div >
299357 <div class =" language-dropdown" >
0 commit comments