Chapter 3: Polish & Design
Dark Mode
Dark Mode
Remember the CSS variable color system we built in section 3? Here's the payoff. Because all our colors use variables, adding dark mode is straightforward — we define a second set of values.
Ask Claude Code to add dark mode
"Add dark mode to the app using Tailwind's class-based dark mode strategy. Define a dark theme color set in globals.css using our existing CSS variables. Create a theme toggle button in the header that switches between light and dark. Persist the choice in localStorage so it survives page reloads. Default to the system preference."
Tailwind supports two dark mode strategies: media (follows system preference) and class (toggled by a class on the html element). We use class because it gives users a manual toggle while still respecting system preference as the default.
The dark color values
Claude Code will add a .dark block to your CSS:
.dark {
--color-bg: 17 24 39;
--color-surface: 31 41 55;
--color-border: 55 65 81;
--color-text-primary: 243 244 246;
--color-text-secondary: 156 163 175;
--color-text-muted: 107 114 128;
--color-primary: 129 140 248;
--color-primary-hover: 99 102 241;
--color-primary-active: 79 70 229;
--color-success: 74 222 128;
--color-warning: 250 204 21;
--color-danger: 248 113 113;
}Notice that dark mode primaries are slightly lighter versions of the light mode colors. This is intentional — colors that look vibrant on a white background can look harsh on a dark one. Claude Code adjusts these automatically for good contrast.
The theme toggle
"Add a sun/moon icon toggle button in the header. Sun icon when in dark mode (click to switch to light), moon icon when in light mode (click to switch to dark). Animate the icon transition."
The toggle component needs to:
- Check localStorage on mount for a saved preference
- Fall back to system preference if no saved choice exists
- Add/remove the
darkclass on thehtmlelement - Save the choice to localStorage on toggle
- Prevent flash — apply the theme before the page renders
Preventing the dark mode flash
The trickiest part of dark mode is preventing a flash of the wrong theme on page load. Claude Code should add an inline script in the <head>:
<script dangerouslySetInnerHTML={{
__html: `
try {
const theme = localStorage.getItem('theme');
if (theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
} catch(e) {}
`
}} />Without this script, users with dark mode preference will see a flash of white before the page renders with dark colors. This inline script runs before React hydrates, preventing the flash.
Tailwind configuration
Make sure your Tailwind config has the class-based dark mode enabled:
// tailwind.config.ts
export default {
darkMode: 'class',
// ...
}Verifying dark mode
Check these things after dark mode is working:
- Background colors invert properly (white becomes dark gray, not pure black)
- Text is readable on dark backgrounds (check contrast)
- The primary accent color still looks good in both themes
- Toasts and overlays use the right theme colors
- Borders are visible but subtle in both themes
- The toggle persists through page reloads
"Review the dark mode implementation. Check every component for contrast issues. Make sure no hardcoded colors snuck in — everything should use our CSS variables."
Your app now adapts to user preference. This isn't just a nice-to-have — many developers (your likely audience for a todo app) spend hours in dark mode. It shows professionalism and care.