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
This commit is contained in:
722
docs/THEME_SYSTEM.md
Normal file
722
docs/THEME_SYSTEM.md
Normal file
@@ -0,0 +1,722 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user