-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Expand file tree
/
Copy pathThemeSwitcher.astro
More file actions
81 lines (71 loc) · 2.25 KB
/
ThemeSwitcher.astro
File metadata and controls
81 lines (71 loc) · 2.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
---
/**
* Theme Switcher Component
* Allows users to toggle between light and dark themes
* Persists theme preference to localStorage
*/
import './ThemeSwitcher.css';
import { BodyMd } from '@/components/primitives';
import { Icon } from 'astro-icon/components';
import { getLangFromUrl, useTranslations } from '@/i18n/utils';
interface Props {
showLabels?: boolean;
}
const { showLabels = true } = Astro.props;
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<button data-theme-toggle class="toggle" aria-label={t('theme.toggle')}>
<div class="toggle-icon">
<Icon
name="fluent:weather-moon-16-regular"
class="moon"
size="20"
color="var(--color-icon-primary)"
/>
<Icon
name="fluent:weather-sunny-16-regular"
class="sun"
size="20"
color="var(--color-icon-primary)"
/>
</div>
<BodyMd
vMargin={false}
class={`label-dark ${!showLabels ? 'sr-only' : ''}`}
aria-label={t('theme.switchToLight')}>{t('theme.switchToLight')}</BodyMd
>
<BodyMd
vMargin={false}
class={`label-light ${!showLabels ? 'sr-only' : ''}`}
aria-label={t('theme.switchToDark')}>{t('theme.switchToDark')}</BodyMd
>
</button>
<script>
// record frame requests
let rafHandle = 0;
const handleToggleClick = () => {
const root = document.documentElement;
const nextTheme = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
// Cancel any pending frame requests to prevent race conditions during rapid clicks
if (rafHandle) {
cancelAnimationFrame(rafHandle);
}
// Disable transitions
root.classList.add('disable-transitions');
// Apply theme
root.setAttribute('data-theme', nextTheme);
localStorage.setItem('theme', nextTheme);
// Wait for the browser to paint the new theme without transitions, then re-enable
// them. Double requestAnimationFrame ensures this happens after a full frame.
rafHandle = requestAnimationFrame(() => {
rafHandle = requestAnimationFrame(() => {
root.classList.remove('disable-transitions');
rafHandle = 0;
});
});
};
document.querySelectorAll('[data-theme-toggle]').forEach((button) => {
button.addEventListener('click', handleToggleClick);
});
</script>