What to build
A React context component and hook that manage dark/light mode — reading system preference, persisting manual choices, and applying the .dark class to <html> so PandaCSS token conditions activate.
Set up:
ThemeProvider component: on mount reads localStorage for stored preference → falls back to window.matchMedia('prefers-color-scheme: dark') → defaults to light. Applies/removes .dark on <html>.
- Listens to system preference changes via
matchMedia event listener — applies in real time when no manual preference is stored.
useTheme() hook returning { theme, setTheme, toggleTheme }. setTheme writes to localStorage.
- Works in Next.js App Router, Next.js Pages, Vite, and TanStack Start (no server-only APIs called at import time).
- Dark mode toggle wired into Storybook toolbar using
ThemeProvider so Storybook previews match real app behavior.
Tests (Vitest + Testing Library):
- Correct class applied to
<html> on mount given: localStorage has stored preference, localStorage is empty + system prefers dark, localStorage is empty + system prefers light.
setTheme updates localStorage.
- System preference listener updates theme when no manual preference is stored.
Acceptance criteria
Blocked by
What to build
A React context component and hook that manage dark/light mode — reading system preference, persisting manual choices, and applying the
.darkclass to<html>so PandaCSS token conditions activate.Set up:
ThemeProvidercomponent: on mount readslocalStoragefor stored preference → falls back towindow.matchMedia('prefers-color-scheme: dark')→ defaults to light. Applies/removes.darkon<html>.matchMediaevent listener — applies in real time when no manual preference is stored.useTheme()hook returning{ theme, setTheme, toggleTheme }.setThemewrites tolocalStorage.ThemeProviderso Storybook previews match real app behavior.Tests (Vitest + Testing Library):
<html>on mount given: localStorage has stored preference, localStorage is empty + system prefers dark, localStorage is empty + system prefers light.setThemeupdates localStorage.Acceptance criteria
<ThemeProvider>applies.darkto<html>when system prefers dark and no localStorage preference existsuseTheme().toggleTheme()switches the class and persists the new preference tolocalStoragelocalStoragepreference over system preferenceBlocked by