SPB Git

spb/rareindex

Public
1.4 KB · 42 lines tsx
1'use client';23import { useEffect, useState } from 'react';4import { Moon, Sun } from 'lucide-react';56type Theme = 'light' | 'dark';78function applyTheme(t: Theme) {9  document.documentElement.setAttribute('data-theme', t);10  try {11    localStorage.setItem('ri-theme', t);12  } catch {13    /* ignore */14  }15}1617export function ThemeToggle({ className }: { className?: string }) {18  const [theme, setTheme] = useState<Theme>('light');19  useEffect(() => {20    const attr = document.documentElement.getAttribute('data-theme') as Theme | null;21    if (attr) setTheme(attr);22    else setTheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');23  }, []);24  return (25    <button26      type="button"27      aria-label={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}28      className={className ?? 'inline-flex h-8 w-8 items-center justify-center rounded-md text-muted hover:bg-inset hover:text-fg'}29      onClick={() => {30        const next: Theme = theme === 'dark' ? 'light' : 'dark';31        setTheme(next);32        applyTheme(next);33      }}34    >35      {theme === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}36    </button>37  );38}3940/** Inline script: apply the stored theme before paint to avoid flashes. */41export const THEME_SCRIPT = `(function(){try{var t=localStorage.getItem('ri-theme');if(t==='dark'||t==='light'){document.documentElement.setAttribute('data-theme',t);}}catch(e){}})();`;42
Files · d4cbf87a4d6431f74690ca22ffff043c6ca7da5d
Menu

Theme