spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1'use client';2import { Moon, Sun } from 'lucide-react';3import { useEffect, useState } from 'react';4import { t } from '@/i18n';56export const THEME_KEY = 'ca-theme';78/** Applied before first paint by the inline script in layout.tsx; here we only toggle and persist. */9export function ThemeToggle({ className }: { className?: string }) {10 const [dark, setDark] = useState<boolean | null>(null);11 useEffect(() => {12 setDark(document.documentElement.classList.contains('dark'));13 const mq = window.matchMedia('(prefers-color-scheme: dark)');14 const onChange = () => {15 if (!localStorage.getItem(THEME_KEY)) {16 document.documentElement.classList.toggle('dark', mq.matches);17 setDark(mq.matches);18 }19 };20 mq.addEventListener('change', onChange);21 return () => mq.removeEventListener('change', onChange);22 }, []);23 const toggle = () => {24 const next = !document.documentElement.classList.contains('dark');25 document.documentElement.classList.toggle('dark', next);26 try {27 localStorage.setItem(THEME_KEY, next ? 'dark' : 'light');28 } catch {29 /* private mode */30 }31 setDark(next);32 };33 return (34 <button type="button" onClick={toggle} className={`tap grid place-items-center rounded-sm text-ink-2 hover:bg-surface-2 hover:text-ink ${className ?? ''}`} aria-label={t('theme.toggle')} aria-pressed={dark ?? undefined} title={dark ? t('theme.light') : t('theme.dark')}>35 {dark ? <Sun size={18} aria-hidden /> : <Moon size={18} aria-hidden />}36 </button>37 );38}3940/** Inline bootstrap: apply stored/system theme before paint (no flash). */41export const THEME_BOOT = `try{var k='${THEME_KEY}',s=localStorage.getItem(k),d=s?s==='dark':matchMedia('(prefers-color-scheme: dark)').matches;if(d)document.documentElement.classList.add('dark')}catch(e){}`;42