Theme System
Complete guide to the Green Goods CSS variables-based theming system.
Overview
The Green Goods admin dashboard uses a CSS variables-based theme system that provides:
Instant theme switching with no visual flash
Semantic color tokens that automatically adapt to light/dark modes
Single source of truth for all color definitions
Type-safe theme API with React hooks
Architecture
Core Components
Theme CSS (
packages/shared/src/styles/theme.css)Defines all CSS variables
Maps semantic tokens to base colors
Handles light/dark mode switching
Theme Controller (
packages/shared/src/theme/index.ts)Pure JavaScript API for theme management
Handles localStorage persistence
Manages system preference detection
React Hook (
packages/shared/src/hooks/app/useTheme.ts)React integration for theme state
Provides reactive theme updates
Cleanup on unmount
Tailwind Integration (
packages/admin/tailwind.config.ts)Maps CSS variables to Tailwind utilities
Enables
bg-*,text-*,border-*classesSupports opacity modifiers
Theme System
Data Attribute Approach
The system uses data-theme attributes instead of class-based dark mode:
Why data-theme:
More semantic than
.darkclassExplicit theme state in markup
Easy to extend (high-contrast, etc.)
Clean selector syntax in CSS
Pre-Hydration Script
Prevents flash of wrong theme on page load:
Color Token System
Semantic Background Tokens
bg-white-0
White (#FFFFFF)
Black (#000000)
Primary backgrounds
bg-weak-50
Gray 50
Gray 900
Subtle backgrounds
bg-soft-200
Gray 200
Gray 700
Soft backgrounds
bg-sub-300
Gray 300
Gray 600
Medium backgrounds
bg-surface-800
Gray 100
Gray 800
Surface backgrounds
bg-strong-950
Black (#000000)
White (#FFFFFF)
Strong backgrounds
Semantic Text Tokens
text-strong-950
Gray 950
Gray 50
Primary text
text-sub-600
Gray 600
Gray 400
Secondary text
text-soft-400
Gray 400
Gray 500
Tertiary text
text-disabled-300
Gray 300
Gray 600
Disabled text
Semantic Stroke Tokens
stroke-strong-950
Gray 950
Gray 50
Strong borders
stroke-sub-300
Gray 300
Gray 600
Medium borders
stroke-soft-200
Gray 200
Gray 700
Soft borders
State Color Tokens
Each state has 4 variants:
Success (Green)
success-dark
success-base
success-light
success-lighter
Error (Red)
error-dark
error-base
error-light
error-lighter
Warning (Orange)
warning-dark
warning-base
warning-light
warning-lighter
Information (Blue)
information-dark
information-base
information-light
information-lighter
Usage guidelines:
-darker: Text on light backgrounds-base: Icons, borders, primary state color-light: Borders, accents-lighter: Backgrounds
Usage
JavaScript/TypeScript
React Hook
Tailwind CSS
Raw CSS
Common Patterns
Card Component
Button Variants
Form Input
Status Badge
Alert Banners
Migration Guide
Migrating Existing Components
Before:
After:
Common Replacements
bg-white dark:bg-gray-800
bg-bg-white
bg-gray-50 dark:bg-gray-900
bg-bg-weak
bg-gray-100
bg-bg-weak
text-gray-900 dark:text-gray-100
text-text-strong
text-gray-600 dark:text-gray-400
text-text-sub
text-gray-500
text-text-soft
border-gray-200 dark:border-gray-700
border-stroke-soft
border-gray-300
border-stroke-sub
Brand Colors vs Semantic Tokens
Keep as brand colors:
Primary CTAs:
bg-green-600 hover:bg-green-700Active tab indicators:
border-green-500 text-green-600Links:
text-green-600 hover:text-green-900
Convert to semantic tokens:
Status badges:
bg-success-lighter text-success-darkError states:
bg-error-lighter text-error-darkForm validation:
border-error-base focus:ring-error-lighterRemove buttons:
text-error-base hover:bg-error-lighter
Testing Checklist
When adding new components:
Troubleshooting
Colors Not Changing
Problem: Classes don't change colors between themes.
Solution: Ensure CSS variables are defined in theme.css and mapped in tailwind.config.ts.
Theme Flash on Load
Problem: Wrong theme briefly shows on page load.
Solution: Verify pre-hydration script is in index.html before React loads.
Opacity Modifiers Not Working
Problem: bg-text-strong/50 doesn't work.
Solution: Ensure colors use rgb(var(--color) / <alpha-value>) format in Tailwind config.
State Colors Not Available
Problem: bg-success-lighter class not found.
Solution: Add state color tokens to tailwind.config.ts:
Best Practices
Always use semantic tokens for neutral colors (gray scale)
Use state tokens for status indicators and alerts
Keep brand colors for primary actions and navigation
Test both themes during development
Avoid hardcoded colors in component styles
Use opacity modifiers for subtle variations
Document custom patterns in this guide
Maintain accessibility contrast ratios in both themes
References
Shared theme CSS:
packages/shared/src/styles/theme.cssTheme controller:
packages/shared/src/theme/index.tsReact hook:
packages/shared/src/hooks/app/useTheme.tsAdmin Tailwind config:
packages/admin/tailwind.config.tsImplementation doc:
/CSS_VARIABLES_THEME_IMPLEMENTATION.md
Last updated