mirror of
https://github.com/sern-handler/website
synced 2026-06-16 12:52:20 +00:00
78 lines
2.5 KiB
Plaintext
78 lines
2.5 KiB
Plaintext
---
|
||
import Select from './Select.astro';
|
||
import type { Props } from '../props';
|
||
|
||
const { labels } = Astro.props;
|
||
---
|
||
|
||
<starlight-theme-select>
|
||
{/* TODO: Can we give this select a width that works well for each language’s strings? */}
|
||
<Select
|
||
icon="laptop"
|
||
label={labels['themeSelect.accessibleLabel']}
|
||
value="auto"
|
||
options={[
|
||
{ label: labels['themeSelect.dark'], selected: false, value: 'dark' },
|
||
{ label: labels['themeSelect.light'], selected: false, value: 'light' },
|
||
{ label: labels['themeSelect.auto'], selected: true, value: 'auto' },
|
||
]}
|
||
width="6.25em"
|
||
/>
|
||
</starlight-theme-select>
|
||
|
||
{/* Inlined to avoid FOUC. Uses global scope from `ThemeProvider.astro` */}
|
||
<script is:inline>
|
||
StarlightThemeProvider.updatePickers();
|
||
</script>
|
||
|
||
<script>
|
||
type Theme = 'auto' | 'dark' | 'light';
|
||
|
||
/** Key in `localStorage` to store color theme preference at. */
|
||
const storageKey = 'starlight-theme';
|
||
|
||
/** Get a typesafe theme string from any JS value (unknown values are coerced to `'auto'`). */
|
||
const parseTheme = (theme: unknown): Theme =>
|
||
theme === 'auto' || theme === 'dark' || theme === 'light' ? theme : 'auto';
|
||
|
||
/** Load the user’s preference from `localStorage`. */
|
||
const loadTheme = (): Theme =>
|
||
parseTheme(typeof localStorage !== 'undefined' && localStorage.getItem(storageKey));
|
||
|
||
/** Store the user’s preference in `localStorage`. */
|
||
function storeTheme(theme: Theme): void {
|
||
if (typeof localStorage !== 'undefined') {
|
||
localStorage.setItem(storageKey, theme === 'light' || theme === 'dark' ? theme : '');
|
||
}
|
||
}
|
||
|
||
/** Get the preferred system color scheme. */
|
||
const getPreferredColorScheme = (): Theme =>
|
||
matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
||
|
||
/** Update select menu UI, document theme, and local storage state. */
|
||
function onThemeChange(theme: Theme): void {
|
||
StarlightThemeProvider.updatePickers(theme);
|
||
document.documentElement.dataset.theme = theme === 'auto' ? getPreferredColorScheme() : theme;
|
||
storeTheme(theme);
|
||
}
|
||
|
||
// React to changes in system color scheme.
|
||
matchMedia(`(prefers-color-scheme: light)`).addEventListener('change', () => {
|
||
if (loadTheme() === 'auto') onThemeChange('auto');
|
||
});
|
||
|
||
class StarlightThemeSelect extends HTMLElement {
|
||
constructor() {
|
||
super();
|
||
onThemeChange(loadTheme());
|
||
this.querySelector('select')?.addEventListener('change', (e) => {
|
||
if (e.currentTarget instanceof HTMLSelectElement) {
|
||
onThemeChange(parseTheme(e.currentTarget.value));
|
||
}
|
||
});
|
||
}
|
||
}
|
||
customElements.define('starlight-theme-select', StarlightThemeSelect);
|
||
</script>
|