# Theming System Guide
## Overview
This application uses a unified theming system where **all colors are defined in ONE place** and consumed everywhere else through semantic CSS variables and Tailwind utilities.
## Single Source of Truth
All colors are defined in [`src/theme/tokens.ts`](./src/theme/tokens.ts):
```typescript
// Change brand colors here and see them propagate throughout the app
export const baseColors = {
gold: {
500: '#d99e34', // Primary brand color
// ... full scale
},
warmGray: { /* ... */ },
purple: { /* ... */ },
};
export const lightTokens = {
background: {
primary: baseColors.pure.white,
secondary: '#f8fafc',
// ... semantic names only
},
// ...
};
```
## How It Works
1. **Tokens** → CSS Variables → Tailwind Classes → Components
2. No hardcoded hex values or color classes allowed anywhere
3. Theme switching handled automatically via CSS variables
## Changing Colors
### To rebrand the entire application:
1. Edit colors in `src/theme/tokens.ts`
2. Save the file - Vite HMR will update everything automatically
3. That's it! 🎉
### Example: Change gold to blue:
```typescript
// src/theme/tokens.ts
export const baseColors = {
gold: {
50: '#eff6ff', // was: '#fefcf0'
100: '#dbeafe', // was: '#fdf7dc'
// ... continue with blue scale
500: '#3b82f6', // was: '#d99e34' - Primary brand color
// ... rest of blue scale
},
};
```
## Available Token Classes
### Background Colors
- `bg-primary` - Main background
- `bg-secondary` - Secondary background
- `bg-tertiary` - Tertiary background
- `bg-elevated` - Cards, modals
- `bg-overlay` - Modal overlays
### Text Colors
- `text-primary` - Primary text
- `text-secondary` - Secondary text
- `text-muted` - Muted text
- `text-inverse` - Inverse text (white on dark backgrounds)
- `text-disabled` - Disabled text
### Accent Colors
- `accent-primary-{50-900}` - Warm gray scale
- `accent-secondary-{50-900}` - Purple scale
- `accent-gold-{50-900}` - Gold/brand scale
- `accent-{color}-text` - Text color for each accent
### Semantic Colors
- `success-{bg|border|text|accent}` - Success states
- `warning-{bg|border|text|accent}` - Warning states
- `error-{bg|border|text|accent}` - Error states
- `info-{bg|border|text|accent}` - Info states
### Border Colors
- `border` - Default border (mapped to `border-default`)
- `border-muted` - Subtle borders
- `border-strong` - Emphasized borders
### Glass Effects
- `glass-bg` - Glass background
- `glass-border` - Glass border
- `glass-shadow` - Glass shadow
## Examples
### ✅ Correct Usage (Semantic tokens)
```tsx
```
### ❌ Wrong Usage (Hardcoded colors)
```tsx
```
## Component Patterns
### Button with token-based styling:
```tsx
```
### Card with glass effect:
```tsx
Card Title
Card content
```
## Theme Switching
The system automatically handles light/dark theme switching:
- Uses CSS variables that change based on `[data-theme="dark"]` or `.dark` classes
- No JavaScript required for color changes
- Blocking script in `index.html` prevents FOUC
## Validation & Linting
The system includes validation to prevent hardcoded colors from sneaking back in:
```bash
# Check for hardcoded colors
npm run validate:theme
# Lint will catch violations too
npm run lint
```
## Adding New Themes
To add a third theme (e.g., "high-contrast"):
1. Add tokens to `src/theme/tokens.ts`:
```typescript
export const highContrastTokens: ThemeTokens = {
background: {
primary: '#000000',
// ... high contrast values
},
// ...
};
```
2. Update CSS generation in `src/theme/cssVariables.ts`
3. Update theme context to support new theme
## No-FOUC Implementation
The theme is applied via a blocking script in `index.html` before React mounts:
```javascript
// Sets theme class before any content renders
document.documentElement.setAttribute('data-theme', theme);
```
## Contrast & Accessibility
All color combinations are validated for WCAG AA compliance using `src/utils/contrast.ts`. The utility now reads CSS variables directly, so contrast ratios are always accurate for the current theme.
## Migration from Hardcoded Colors
Common replacements when migrating existing components:
| Old (hardcoded) | New (semantic) |
|----------------|----------------|
| `text-slate-900` | `text-text-primary` |
| `text-slate-600` | `text-text-secondary` |
| `text-slate-400` | `text-text-muted` |
| `bg-white` | `bg-bg-primary` |
| `bg-slate-100` | `bg-bg-secondary` |
| `border-slate-200` | `border-border` |
| `text-white` | `text-text-inverse` |
## Benefits
✅ **Single source of truth** - Change colors in one file
✅ **Type safety** - TypeScript ensures valid tokens
✅ **No FOUC** - Theme applies before React renders
✅ **Automatic contrast** - WCAG compliance built-in
✅ **Lint protection** - Prevents hardcoded colors
✅ **Easy rebrand** - Update tokens, everything changes
✅ **Theme switching** - Seamless light/dark modes
## Architecture
```
src/theme/tokens.ts // Single source of truth
↓
src/theme/cssVariables.ts // Generate CSS variables
↓
src/styles/tokens.css // CSS variables output
↓
tailwind.config.js // Map to Tailwind classes
↓
Components // Use semantic classes
```
This architecture ensures that changing colors in `tokens.ts` propagates through the entire application automatically.