Files
dss/docs/THEME_SYSTEM.md
Digital Production Factory 276ed71f31 Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
2025-12-09 18:45:48 -03:00

723 lines
17 KiB
Markdown

# Design System Theme System Guide
**Version:** 1.0.0
**Last Updated:** December 7, 2025
Complete guide to theming and customizing the design system.
---
## Table of Contents
1. [Theme Overview](#theme-overview)
2. [Light Mode (Default)](#light-mode-default)
3. [Dark Mode](#dark-mode)
4. [Custom Themes](#custom-themes)
5. [Theme Switching](#theme-switching)
6. [CSS Variable Customization](#css-variable-customization)
7. [Component Theming](#component-theming)
8. [Best Practices](#best-practices)
---
## Theme Overview
The design system includes:
- ✅ Light mode (default)
- ✅ Dark mode (CSS class-based)
- ✅ Full customization via CSS variables
- ✅ Per-component theme support
- ✅ Automatic contrast checking
### How Theming Works
Themes are implemented using CSS custom properties (variables) that automatically apply based on a class on the root element:
```html
<!-- Light mode (default) -->
<html>
<body>...</body>
</html>
<!-- Dark mode -->
<html class="dark">
<body>...</body>
</html>
```
---
## Light Mode (Default)
Light mode is the default theme with clean, bright colors optimized for daytime viewing.
### Color Palette (Light Mode)
| Token | Color | HEX | Usage |
|-------|-------|-----|-------|
| `--primary` | Blue | `#3b82f6` | Primary actions, focus |
| `--secondary` | Indigo | `#6366f1` | Secondary elements |
| `--success` | Green | `#10b981` | Success feedback |
| `--warning` | Amber | `#f59e0b` | Warning messages |
| `--destructive` | Red | `#ef4444` | Error/delete actions |
| `--info` | Cyan | `#0ea5e9` | Information |
| `--foreground` | Slate-900 | `#0f172a` | Primary text |
| `--muted-foreground` | Slate-600 | `#64748b` | Secondary text |
| `--background` | White | `#ffffff` | Page background |
| `--card` | Slate-50 | `#f8fafc` | Card backgrounds |
| `--card-foreground` | Slate-900 | `#1e293b` | Card text |
| `--border` | Slate-200 | `#e2e8f0` | Borders |
| `--input` | White | `#ffffff` | Input backgrounds |
| `--ring` | Blue | `#3b82f6` | Focus rings |
### Light Mode Appearance
- Clear distinction between elements
- High contrast text (4.5:1+)
- Subtle shadows for depth
- Neutral backgrounds
- Vibrant accent colors
### CSS
```css
:root {
/* Light mode variables are set by default */
--primary: #3b82f6;
--secondary: #6366f1;
--success: #10b981;
--warning: #f59e0b;
--destructive: #ef4444;
--info: #0ea5e9;
--foreground: #0f172a;
--muted-foreground: #64748b;
--background: #ffffff;
--card: #f8fafc;
--card-foreground: #1e293b;
--border: #e2e8f0;
--input: #ffffff;
--ring: #3b82f6;
}
```
---
## Dark Mode
Dark mode provides eye-friendly colors for low-light environments.
### Enabling Dark Mode
```javascript
// Add dark class to enable dark mode
document.documentElement.classList.add('dark');
```
### Color Palette (Dark Mode)
| Token | Color | HEX | Usage |
|-------|-------|-----|-------|
| `--primary` | Light Blue | `#60a5fa` | Primary actions |
| `--secondary` | Light Indigo | `#818cf8` | Secondary elements |
| `--success` | Light Green | `#34d399` | Success feedback |
| `--warning` | Light Amber | `#fbbf24` | Warning messages |
| `--destructive` | Light Red | `#f87171` | Error/delete actions |
| `--info` | Light Cyan | `#38bdf8` | Information |
| `--foreground` | Slate-50 | `#f1f5f9` | Primary text |
| `--muted-foreground` | Slate-400 | `#cbd5e1` | Secondary text |
| `--background` | Slate-950 | `#0f172a` | Page background |
| `--card` | Slate-900 | `#1e293b` | Card backgrounds |
| `--card-foreground` | Slate-50 | `#e2e8f0` | Card text |
| `--border` | Slate-800 | `#334155` | Borders |
| `--input` | Slate-900 | `#1f2937` | Input backgrounds |
| `--ring` | Light Blue | `#60a5fa` | Focus rings |
### Dark Mode Appearance
- Dark backgrounds reduce eye strain
- Brighter text for readability
- Adjusted accent colors for clarity
- Softer shadows
- Maintained contrast ratios
### CSS
```css
:root.dark {
--primary: #60a5fa;
--secondary: #818cf8;
--success: #34d399;
--warning: #fbbf24;
--destructive: #f87171;
--info: #38bdf8;
--foreground: #f1f5f9;
--muted-foreground: #cbd5e1;
--background: #0f172a;
--card: #1e293b;
--card-foreground: #e2e8f0;
--border: #334155;
--input: #1f2937;
--ring: #60a5fa;
}
```
### Component Support
All 9 components support dark mode:
- ✅ DsButton - All variants themed
- ✅ DsInput - Form fields themed
- ✅ DsCard - Background and text adjusted
- ✅ DsBadge - Color variants adjusted
- ✅ DsToast - Backgrounds and text adjusted
- ✅ DsWorkflow - Step colors adjusted
- ✅ DsNotificationCenter - Scrollbar styled
- ✅ DsActionBar - Background themed
- ✅ DsToastProvider - Inherits from toasts
---
## Custom Themes
Create custom themes by overriding CSS variables.
### Creating a Custom Theme
```css
/* custom-theme.css */
:root.custom-theme {
/* Custom brand colors */
--primary: #6d28d9; /* Deep purple */
--secondary: #d946ef; /* Magenta */
--success: #22c55e; /* Bright green */
--warning: #eab308; /* Bright yellow */
--destructive: #dc2626; /* Bright red */
/* Custom backgrounds */
--background: #f9f5ff; /* Lavender white */
--card: #f3e8ff; /* Light purple */
--card-foreground: #3f0f63; /* Dark purple text */
/* Custom text colors */
--foreground: #1f1337; /* Dark purple */
--muted-foreground: #6b5280; /* Muted purple */
/* Adjust other tokens as needed */
--border: #e9d5ff; /* Light purple border */
--input: #fafaf9; /* Warm white */
}
```
### Applying Custom Theme
```html
<html class="custom-theme">
<body>...</body>
</html>
```
```javascript
// Switch to custom theme
document.documentElement.classList.add('custom-theme');
// Remove other themes
document.documentElement.classList.remove('dark', 'light');
```
### Brand-Specific Theme Example
```css
/* tech-brand-theme.css - Modern tech company theme */
:root.tech-brand {
--primary: #0066ff; /* Vibrant blue */
--secondary: #00d4ff; /* Cyan */
--success: #00ff88; /* Neon green */
--warning: #ffaa00; /* Bright orange */
--destructive: #ff3366; /* Hot pink */
--foreground: #1a1a2e; /* Dark blue */
--background: #0f1419; /* Almost black */
--card: #161b22; /* Slightly lighter black */
--card-foreground: #ffffff; /* Pure white text */
--border: #30363d; /* Dark gray borders */
}
:root.tech-brand.dark {
/* Dark mode would be even more dark/vibrant */
--primary: #00d4ff; /* Swap primary/secondary in dark */
--secondary: #0066ff;
--success: #00ff88;
}
```
### Corporate Theme Example
```css
/* corporate-theme.css - Professional corporate theme */
:root.corporate {
--primary: #003366; /* Navy blue */
--secondary: #666666; /* Gray */
--success: #006600; /* Dark green */
--warning: #cc6600; /* Brown-orange */
--destructive: #990000; /* Dark red */
--foreground: #000000; /* Pure black */
--background: #ffffff; /* Pure white */
--card: #f5f5f5; /* Light gray */
--card-foreground: #333333; /* Dark gray text */
--border: #cccccc; /* Medium gray borders */
}
```
---
## Theme Switching
### Simple Theme Toggle
```html
<button id="theme-toggle">Toggle Dark Mode</button>
<script>
const button = document.getElementById('theme-toggle');
button.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
});
</script>
```
### Theme Switcher with Dropdown
```html
<select id="theme-selector">
<option value="">Light</option>
<option value="dark">Dark</option>
<option value="custom-theme">Custom Brand</option>
<option value="corporate">Corporate</option>
</select>
<script>
const selector = document.getElementById('theme-selector');
selector.addEventListener('change', (e) => {
const theme = e.target.value;
// Remove all theme classes
document.documentElement.classList.remove('dark', 'custom-theme', 'corporate');
// Add selected theme
if (theme) {
document.documentElement.classList.add(theme);
}
// Save preference
localStorage.setItem('preferred-theme', theme);
});
// Load saved preference
const saved = localStorage.getItem('preferred-theme');
if (saved) {
document.documentElement.classList.add(saved);
selector.value = saved;
}
</script>
```
### System Preference Detection
```javascript
// Detect system dark mode preference
function getSystemTheme() {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light';
}
// Apply system theme on page load
function applySystemTheme() {
const saved = localStorage.getItem('preferred-theme');
const theme = saved || getSystemTheme();
if (theme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}
applySystemTheme();
// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
if (!localStorage.getItem('preferred-theme')) {
// Only change if user hasn't set preference
if (e.matches) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}
});
```
### Theme Switcher Component
```html
<div class="theme-switcher">
<button id="light-theme" aria-label="Light theme">☀️</button>
<button id="dark-theme" aria-label="Dark theme">🌙</button>
<button id="auto-theme" aria-label="Auto theme">🔄</button>
</div>
<script>
const lightBtn = document.getElementById('light-theme');
const darkBtn = document.getElementById('dark-theme');
const autoBtn = document.getElementById('auto-theme');
lightBtn.addEventListener('click', () => {
document.documentElement.classList.remove('dark');
localStorage.setItem('preferred-theme', 'light');
});
darkBtn.addEventListener('click', () => {
document.documentElement.classList.add('dark');
localStorage.setItem('preferred-theme', 'dark');
});
autoBtn.addEventListener('click', () => {
localStorage.removeItem('preferred-theme');
applySystemTheme();
});
// Highlight current theme
function updateButtons() {
const isDark = document.documentElement.classList.contains('dark');
lightBtn.classList.toggle('active', !isDark);
darkBtn.classList.toggle('active', isDark);
}
updateButtons();
document.addEventListener('theme-change', updateButtons);
</script>
```
---
## CSS Variable Customization
### Override Global Variables
```css
/* Override for entire page */
:root {
--primary: #your-color;
--space-4: 1.25rem;
--duration-normal: 0.3s;
}
```
### Override for Specific Components
```css
/* Custom styling for one component */
.my-special-button {
--primary: #ff6b6b; /* Use different color */
background: var(--primary);
}
/* Custom styling for theme */
:root.my-theme {
--primary: #667eea;
--secondary: #764ba2;
}
```
### Using Variables in JavaScript
```javascript
// Get variable value
const primaryColor = getComputedStyle(document.documentElement)
.getPropertyValue('--primary')
.trim();
// Set variable value
document.documentElement.style.setProperty('--primary', '#ff0000');
// In component
class CustomComponent extends HTMLElement {
getPrimaryColor() {
return getComputedStyle(this).getPropertyValue('--primary').trim();
}
setPrimaryColor(color) {
this.style.setProperty('--primary', color);
}
}
```
---
## Component Theming
### Button Theming
```css
/* Light mode button colors */
.ds-btn[data-variant="primary"] {
background: var(--primary);
color: white;
}
.ds-btn[data-variant="success"] {
background: var(--success);
color: white;
}
/* Dark mode adjustments (automatic via tokens) */
:root.dark .ds-btn[data-variant="primary"] {
background: var(--primary); /* Already adjusted in tokens */
color: white;
}
```
### Input Theming
```css
/* Light mode input */
.ds-input {
background: var(--input);
border: 2px solid var(--border);
color: var(--foreground);
}
/* Dark mode input (automatic) */
:root.dark .ds-input {
background: var(--input); /* Dark background */
border-color: var(--border); /* Lighter border */
color: var(--foreground); /* Light text */
}
/* Focus state (works in both themes) */
.ds-input:focus {
border-color: var(--ring);
box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 20%, transparent);
}
```
### Card Theming
```css
/* Light mode card */
.ds-card {
background: var(--card);
color: var(--card-foreground);
border: 1px solid var(--border);
}
/* Dark mode card (automatic) */
:root.dark .ds-card {
background: var(--card); /* Dark background */
color: var(--card-foreground); /* Light text */
border-color: var(--border); /* Darker border */
}
```
### Toast Theming
```css
/* Light mode toast */
.ds-toast[data-type="success"] {
border-left: 4px solid var(--success);
background: linear-gradient(135deg,
color-mix(in oklch, var(--success) 5%, var(--card)) 0%,
var(--card) 100%);
}
/* Dark mode toast (automatic) */
:root.dark .ds-toast[data-type="success"] {
/* Uses dark theme tokens automatically */
border-left-color: var(--success);
background: linear-gradient(135deg,
color-mix(in oklch, var(--success) 5%, var(--card)) 0%,
var(--card) 100%);
}
```
---
## Best Practices
### 1. Always Use Tokens
```css
/* ✅ Good */
.my-button {
background: var(--primary);
color: var(--foreground);
padding: var(--space-4);
}
/* ❌ Bad */
.my-button {
background: #3b82f6;
color: #0f172a;
padding: 16px;
}
```
### 2. Test in Both Themes
```javascript
// Test dark mode
document.documentElement.classList.add('dark');
// Check colors, contrast, visibility
// Test light mode
document.documentElement.classList.remove('dark');
// Check colors, contrast, visibility
```
### 3. Maintain Contrast
Ensure all text meets WCAG 2.1 AA minimum (4.5:1 contrast ratio):
```javascript
// Validate contrast
function checkContrast(foreground, background) {
const fgLum = getRelativeLuminance(foreground);
const bgLum = getRelativeLuminance(background);
const ratio = (Math.max(fgLum, bgLum) + 0.05) / (Math.min(fgLum, bgLum) + 0.05);
return ratio >= 4.5; // WCAG AA
}
```
### 4. Respect User Preferences
```javascript
// Always check prefers-color-scheme
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
// Apply dark mode
} else {
// Apply light mode
}
// Listen for changes
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
// Update theme
});
```
### 5. Announce Theme Changes
```javascript
// Emit custom event when theme changes
const event = new CustomEvent('theme-change', {
detail: { theme: isDark ? 'dark' : 'light' }
});
document.dispatchEvent(event);
// Listen for theme changes
document.addEventListener('theme-change', (e) => {
console.log('Theme changed to:', e.detail.theme);
});
```
### 6. Handle Transitions Smoothly
```css
/* Smooth color transitions when switching themes */
* {
transition: background-color 0.2s ease, color 0.2s ease;
}
/* But avoid transitions on initial load */
*:not(:root):not(body) {
transition: none;
}
:root.theme-changing * {
transition: background-color 0.2s ease, color 0.2s ease;
}
```
### 7. Document Custom Themes
```markdown
# Custom Theme: TechBrand
## Colors
- Primary: #0066ff (Vibrant Blue)
- Secondary: #00d4ff (Cyan)
- Success: #00ff88 (Neon Green)
## Usage
```html
<html class="tech-brand">
<!-- Content -->
</html>
```
## Dark Mode
Automatically supports dark theme with enhanced contrast.
```
---
## Troubleshooting
### Issue: Theme not applying
**Solution:** Make sure CSS is loaded before HTML:
```javascript
// Load in this order:
import '@company/design-system/css/variants.css';
import '@company/design-system/css/components.css';
```
### Issue: Custom colors not working
**Solution:** Use CSS custom properties, not hardcoded values:
```css
/* ✅ Works */
--primary: #your-color;
/* ❌ Doesn't work */
background: #your-color;
```
### Issue: Dark mode colors don't match
**Solution:** Adjust tokens in dark mode CSS:
```css
:root.dark {
--primary: #lighter-color; /* Use lighter shade in dark mode */
}
```
### Issue: Contrast is too low
**Solution:** Check WCAG ratios and adjust colors:
```javascript
// Aim for 4.5:1 minimum (WCAG AA)
// 7:1+ for best accessibility (WCAG AAA)
```
---
## Summary
The design system theming system provides:
**Light & Dark modes** - Out of the box
**42 CSS variables** - Full customization
**Automatic component support** - All 9 components themed
**High contrast** - WCAG 2.1 AA compliant
**System preference detection** - Respects OS preference
**Persistence** - Save user theme preference
**Custom themes** - Create brand-specific themes
**Smooth transitions** - Animate theme changes
---
**For more help:** design-system@company.com