@@ -62,23 +62,26 @@ const { locale } = Astro.props;
6262</div >
6363
6464<script >
65+ type ThemeMode = 'system' | 'light' | 'dark';
66+ type ThemeValue = 'light' | 'dark';
67+
6568 const root = document.documentElement;
6669 const modeKey = 'passion-blog-theme-mode';
6770 const legacyKey = 'passion-blog-theme';
68- const dropdown = /** @type {HTMLElement | null} */ ( document.querySelector('[data-theme-dropdown]') );
69- const trigger = /** @type {HTMLButtonElement | null} */ ( dropdown?.querySelector('[data-theme-trigger]')) ;
70- const menu = /** @type {HTMLElement | null} */ ( dropdown?.querySelector('[data-theme-menu]')) ;
71- const label = /** @type {HTMLElement | null} */ ( dropdown?.querySelector('[data-theme-label]')) ;
72- const caret = /** @type {HTMLElement | null} */ ( dropdown?.querySelector('[data-theme-caret]')) ;
73- const options = /** @type {HTMLButtonElement[]} */ ([... (dropdown?.querySelectorAll('[data-theme-option]') || []) ]);
71+ const dropdown = document.querySelector<HTMLElement> ('[data-theme-dropdown]');
72+ const trigger = dropdown?.querySelector<HTMLButtonElement> ('[data-theme-trigger]') ?? null ;
73+ const menu = dropdown?.querySelector<HTMLElement> ('[data-theme-menu]') ?? null ;
74+ const label = dropdown?.querySelector<HTMLElement> ('[data-theme-label]') ?? null ;
75+ const caret = dropdown?.querySelector<HTMLElement> ('[data-theme-caret]') ?? null ;
76+ const options = Array.from (dropdown?.querySelectorAll<HTMLButtonElement> ('[data-theme-option]') ?? [ ]);
7477 const media = window.matchMedia('(prefers-color-scheme: dark)');
7578
76- function resolveTheme(mode) {
79+ function resolveTheme(mode: ThemeMode): ThemeValue {
7780 if (mode === 'light' || mode === 'dark') return mode;
7881 return media.matches ? 'dark' : 'light';
7982 }
8083
81- function detectMode() {
84+ function detectMode(): ThemeMode {
8285 const saved = localStorage.getItem(modeKey);
8386 if (saved === 'system' || saved === 'light' || saved === 'dark') {
8487 return saved;
@@ -92,21 +95,21 @@ const { locale } = Astro.props;
9295 return 'system';
9396 }
9497
95- function apply(mode) {
98+ function apply(mode: ThemeMode ) {
9699 const theme = resolveTheme(mode);
97100 root.setAttribute('data-theme', theme);
98101 root.dataset.themeMode = mode;
99102 window.dispatchEvent(new CustomEvent('theme-change', { detail: theme }));
100103 }
101104
102- function setOpen(open) {
105+ function setOpen(open: boolean ) {
103106 if (!trigger || !menu || !caret) return;
104107 trigger.setAttribute('aria-expanded', open ? 'true' : 'false');
105108 menu.classList.toggle('hidden', !open);
106109 caret.textContent = open ? '▴' : '▾';
107110 }
108111
109- function syncOptionState(mode) {
112+ function syncOptionState(mode: ThemeMode ) {
110113 for (const option of options) {
111114 const selected = option.dataset.mode === mode;
112115 option.setAttribute('aria-checked', selected ? 'true' : 'false');
@@ -117,7 +120,7 @@ const { locale } = Astro.props;
117120 }
118121 }
119122
120- let activeMode = detectMode();
123+ let activeMode: ThemeMode = detectMode();
121124 apply(activeMode);
122125 if (label) {
123126 const selectedLabel = options.find((option) => option.dataset.mode === activeMode)?.dataset.label;
@@ -134,7 +137,7 @@ const { locale } = Astro.props;
134137
135138 options.forEach((option) => {
136139 option.addEventListener('click', () => {
137- const nextMode = option.dataset.mode;
140+ const nextMode = option.dataset.mode as ThemeMode | undefined ;
138141 if (!nextMode || !['system', 'light', 'dark'].includes(nextMode)) return;
139142
140143 activeMode = nextMode;
0 commit comments