Polish & Launch
Light / Dark / System Theme Switcher
Add a theme switcher that lets visitors pick light mode, dark mode, or follow their system preference — with the choice saved to localStorage so it persists across visits.
0/5 filled
Dark mode colors
Built something? Show it off in the Airo Builder Lab
Your Prompt
0/5 filled
Add a light / dark / system theme switcher to my site.
Switcher placement: Header (next to navigation)
Switcher style: Icons (Sun / Moon / Monitor)
Dark mode colors:
Background: #1A1A2E
Text: #E8E8E8
Accent: [same as light mode]
Implementation requirements:
1. Create a ThemeContext (src/context/ThemeContext.tsx) with three options: 'light' | 'dark' | 'system'.
- 'system' reads window.matchMedia('(prefers-color-scheme: dark)') and updates if the OS preference changes (add a listener).
- Save the user's choice to localStorage under the key "theme-preference".
- On mount, read localStorage first; fall back to 'system' if nothing is saved.
- Apply the active theme by setting both class="dark" and data-theme="dark" on <html> when dark mode is active.
2. Define dark mode CSS variables under [data-theme="dark"] in globals.css. Do NOT use Tailwind's dark: prefix — use CSS variables only. Map the dark mode colors above to the existing CSS variable names (--c-bg, --c-heading, --c-body, --c-accent, etc.).
3. Build the switcher UI at the specified placement using the specified style:
- Icons: a pill with Sun / Moon / Monitor icons from lucide-react; active icon has accent background
- Text labels: three buttons labeled Light / Dark / System
- Dropdown: a <select> with the three options
- Each option must have aria-pressed (for buttons) or be a proper <option> (for select)
- The switcher must be keyboard accessible
4. Avoid a flash of wrong theme on page load: read localStorage and apply the class to <html> before React hydrates (add a small inline <script> in the HTML head, or handle it in the ThemeContext initializer using a synchronous read).
5. Wrap the app in <ThemeProvider> in main.tsx or App.tsx.
Unfilled fields show as [brackets] — fill them in or leave as-is.