Wires the dark-mode plumbing: - New $lib/theme module: themePreference + resolvedTheme stores plus initTheme() / setThemePreference(). Owns localStorage persistence, <html data-theme=...> mirroring, and the matchMedia listener that re-resolves "system" when the OS preference flips. Storage-agnostic (no Supabase coupling) so the caller persists to public.users.theme separately. - Inline anti-FOUC script in app.html reads localStorage + matchMedia synchronously and writes data-theme before the first paint — refresh in dark mode no longer flashes white. - Tailwind switched to darkMode: ['selector', '[data-theme="dark"]'] so all existing `dark:` utilities resolve from the same attribute. - :root and :root[data-theme=light] share the light token set; dark tokens move into :root[data-theme=dark]. Tokens stay as RGB triplets (rgb(var(--token) / <alpha>)) per the repo gotcha — no hsl(). - ThemeToggle.svelte: three-way radiogroup (Light / Dark / System) with paraglide messages in EN + ES. Tests (TDD): 8 new unit specs in src/lib/theme.test.ts cover defaults, localStorage round-trip, data-theme reflection, system resolution against matchMedia, invalid-value fallback, and OS preference change propagation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
32 lines
1.2 KiB
TypeScript
32 lines
1.2 KiB
TypeScript
import type { Config } from 'tailwindcss';
|
|
|
|
export default {
|
|
content: ['./src/**/*.{html,js,svelte,ts}'],
|
|
// Fase 9.1: drive dark mode from <html data-theme="dark"> so the inline
|
|
// anti-FOUC script in app.html can set the attribute synchronously before
|
|
// the first paint. Existing `dark:` utilities keep working as-is.
|
|
darkMode: ['selector', '[data-theme="dark"]'],
|
|
theme: {
|
|
extend: {
|
|
fontFamily: {
|
|
sans: ['Inter', 'system-ui', 'sans-serif']
|
|
},
|
|
colors: {
|
|
// Semantic aliases — "The Monolith Editorial" surface hierarchy.
|
|
// Use background shifts (never 1px borders) to separate major UI areas.
|
|
background: 'rgb(var(--background) / <alpha-value>)',
|
|
'surface-container-low': 'rgb(var(--surface-container-low) / <alpha-value>)',
|
|
surface: 'rgb(var(--surface) / <alpha-value>)',
|
|
'surface-raised': 'rgb(var(--surface-raised) / <alpha-value>)',
|
|
// Text hierarchy
|
|
'text-primary': 'rgb(var(--text-primary) / <alpha-value>)',
|
|
'text-secondary': 'rgb(var(--text-secondary) / <alpha-value>)',
|
|
'text-muted': 'rgb(var(--text-muted) / <alpha-value>)',
|
|
// Only non-slate color allowed in the system
|
|
destructive: 'rgb(var(--destructive) / <alpha-value>)'
|
|
}
|
|
}
|
|
},
|
|
plugins: []
|
|
} satisfies Config;
|