Something went wrong. Try again.
Central platform for European atproto.<cc> country community websites atproto.eu
community atproto
Something went wrong. Try again.
2.6 kB · 52 lines
Astro
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253---import { useTranslations, type Lang } from '../i18n/ui';const { lang = 'en' } = Astro.props as { lang?: Lang };const t = useTranslations(lang);---
{/* A two-state segmented switch, not a single icon. Both light and dark are always visible; the active one is highlighted, so there is nothing to guess (a lone icon has to mean either the current state or the action, and people can't tell which). Clicking a side pins that theme. */}<div class="theme-toggle" role="group" aria-label={t('theme.toggle')}> <button class="theme-opt" type="button" data-theme-set="light" aria-label={t('theme.light')} title={t('theme.light')}> <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <circle cx="12" cy="12" r="4"></circle> <path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"></path> </svg> </button> <button class="theme-opt" type="button" data-theme-set="dark" aria-label={t('theme.dark')} title={t('theme.dark')}> <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"></path> </svg> </button></div>
<script is:inline> // Pin the theme on <html> (data-theme + color-scheme), persisted. The segmented control shows // both options; the active one is marked aria-pressed=true so the current state is unambiguous. (() => { const root = document.documentElement; const opts = Array.from(document.querySelectorAll('.theme-opt')); if (!opts.length) return; const systemDark = () => matchMedia('(prefers-color-scheme: dark)').matches; const current = () => root.dataset.theme || (systemDark() ? 'dark' : 'light'); const sync = () => { const now = current(); for (const b of opts) b.setAttribute('aria-pressed', String(b.dataset.themeSet === now)); }; sync(); for (const b of opts) { b.addEventListener('click', () => { const next = b.dataset.themeSet === 'dark' ? 'dark' : 'light'; root.dataset.theme = next; root.style.colorScheme = next; localStorage.setItem('theme', next); sync(); }); } // If the user hasn't pinned a theme, follow the system and keep the highlight in sync. matchMedia('(prefers-color-scheme: dark)').addEventListener?.('change', () => { if (!root.dataset.theme) sync(); }); })();</script>