Context
There is no theme state management in the app today. We need a provider/hook that owns the current theme, persists it, and avoids SSR flash.
Scope
- New file:
src/contexts/theme-context.tsx
- Exports:
ThemeProvider component, useTheme() hook, Theme type ('light' | 'dark' | 'system')
- On mount, read initial theme from
localStorage key vwc-theme; fall back to 'system' (resolves via matchMedia('(prefers-color-scheme: dark)'))
- On change: update
document.documentElement.classList (add/remove dark) and write to localStorage
- When
theme === 'system', subscribe to matchMedia changes so the UI flips live with OS
- Prevent SSR flash: add a small inline script in
_document.tsx that reads localStorage and sets the class before first paint
Acceptance Criteria
Depends on
#1 (Tailwind darkMode config)
Blocks
#3, #4
Context
There is no theme state management in the app today. We need a provider/hook that owns the current theme, persists it, and avoids SSR flash.
Scope
src/contexts/theme-context.tsxThemeProvidercomponent,useTheme()hook,Themetype ('light' | 'dark' | 'system')localStoragekeyvwc-theme; fall back to'system'(resolves viamatchMedia('(prefers-color-scheme: dark)'))document.documentElement.classList(add/removedark) and write tolocalStoragetheme === 'system', subscribe tomatchMediachanges so the UI flips live with OS_document.tsxthat readslocalStorageand sets the class before first paintAcceptance Criteria
useTheme()returns{ theme, setTheme, resolvedTheme }darkpersists across reloads'system'mode responds to live OS theme changes without a reloadDepends on
#1 (Tailwind darkMode config)
Blocks
#3, #4