Chapter 3: Polish & Design
Building a Color System
Building a Color System
A cohesive color system is the foundation of good design. Instead of picking random colors as you go, we'll set up a system of CSS variables that keeps everything consistent.
You might wonder why we're using CSS variables instead of Tailwind's built-in color classes. The payoff comes in section 11 (Dark Mode) — because every component references the same variables, adding dark mode means defining ONE new block of values. The entire app rethemes automatically. For now, trust the process.
Ask Claude Code to build it
"Set up a color system for the todo app using CSS custom properties in globals.css. I want a light theme with indigo as the primary accent color. Define variables for: background, surface, border, text (primary, secondary, muted), primary color (with hover and active variants), and status colors for success, warning, and danger. Then update the Tailwind config to use these variables."
Why CSS variables instead of hardcoded Tailwind classes like bg-indigo-500? Because in section 11, when we add dark mode, we'll define a second set of values under .dark and every component updates automatically. With hardcoded classes, you'd need to add dark: variants to every single element. CSS variables make dark mode a 10-minute job instead of an hour-long hunt.
What Claude Code creates
Claude Code will add something like this to your globals.css:
@layer base {
:root {
--color-bg: 255 255 255;
--color-surface: 249 250 251;
--color-border: 229 231 235;
--color-text-primary: 17 24 39;
--color-text-secondary: 107 114 128;
--color-text-muted: 156 163 175;
--color-primary: 99 102 241;
--color-primary-hover: 79 70 229;
--color-primary-active: 67 56 202;
--color-success: 34 197 94;
--color-warning: 234 179 8;
--color-danger: 239 68 68;
}
}Notice the RGB values without rgb() wrapping. This is a Tailwind convention that lets you use opacity modifiers like bg-primary/50. Claude Code knows this pattern.
Understanding the color hierarchy
Our system has three layers:
- Background colors —
bgis the page background,surfaceis for cards and elevated elements - Text colors —
primaryfor headings,secondaryfor body text,mutedfor labels and placeholders - Accent colors —
primaryfor buttons and links, status colors for feedback
Tailwind configuration
Claude Code will also update your tailwind.config.ts to map these variables to Tailwind classes:
theme: {
extend: {
colors: {
bg: 'rgb(var(--color-bg) / <alpha-value>)',
surface: 'rgb(var(--color-surface) / <alpha-value>)',
border: 'rgb(var(--color-border) / <alpha-value>)',
'text-primary': 'rgb(var(--color-text-primary) / <alpha-value>)',
'text-secondary': 'rgb(var(--color-text-secondary) / <alpha-value>)',
primary: 'rgb(var(--color-primary) / <alpha-value>)',
'primary-hover': 'rgb(var(--color-primary-hover) / <alpha-value>)',
},
},
},Now you can use classes like bg-surface, text-text-secondary, and bg-primary throughout your app — and they'll all update automatically when we add dark mode.
Apply the base colors
"Apply the new color system to the app layout. Set the page background to bg, card backgrounds to surface, and update all text to use the text color variables."
If you've been using hardcoded Tailwind colors like bg-gray-100 or text-gray-600, ask Claude Code to replace them all with the new design tokens. This keeps your color system consistent and makes dark mode much easier later.
Check your browser. The app should look similar to before but now uses a unified color system. The real payoff comes when we add dark mode — one change to the variables, and the entire app transforms.