
Theming Components
- 61 installs
- 426 repo stars
- Updated December 11, 2025
- ancoleman/ai-design-components
Theming-components is a Claude skill that provides a design-token system and theming framework for consistent, customizable UI styling with light/dark, RTL and accessibility support.
About
This skill provides a design-token system and theming framework for consistent, customizable UI styling. Developers use it when theming components, implementing light/dark mode, creating brand styles, or supporting RTL languages. It covers a full token taxonomy, theme switching, multi-platform export and accessibility.
- 7-category token taxonomy (color, type, spacing, shadows, borders, motion, z-index)
- Light/dark and custom brand theme switching via CSS variables
- RTL support with CSS logical properties and WCAG contrast
Theming Components by the numbers
- 61 all-time installs (skills.sh)
- Ranked #1,207 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
theming-components capabilities & compatibility
- Capabilities
- design tokens · theming · dark mode · rtl support · accessibility styling
- Use cases
- ui design · frontend · web design
- Pricing
- Free
What theming-components says it does
Provides design token system and theming framework for consistent, customizable UI styling across all components.
Design tokens are the **single source of truth** for all visual design decisions.
npx skills add https://github.com/ancoleman/ai-design-components --skill theming-componentsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 61 |
|---|---|
| repo stars | ★ 426 |
| Last updated | December 11, 2025 |
| Repository | ancoleman/ai-design-components ↗ |
What it does
Provide a design-token and theming system for consistent, customizable styling with light/dark, RTL and a11y support.
Who is it for?
Establishing a design-token foundation with theme switching and brand customization.
Skip if: Backend services with no UI styling layer.
When should I use this skill?
Theming components, implementing light/dark mode, creating brand styles, or supporting RTL languages.
What you get
A token-driven theming system enabling brand customization, theme switching and RTL support.
- Design-token taxonomy
- Light/dark and brand themes
- RTL and accessibility styling
By the numbers
- 7 core token categories
- 3-tier color hierarchy (primitive, semantic, component)
- 4px base spacing scale
Files
Design Tokens & Theming System
Comprehensive design token system providing the foundational styling architecture for all component skills, enabling brand customization, theme switching, RTL support, and consistent visual design.
Overview
Design tokens are the single source of truth for all visual design decisions. This skill provides:
1. Complete Token Taxonomy: 7 core categories (color, typography, spacing, borders, shadows, motion, z-index) 2. Theme Switching: Light/dark mode, high-contrast, custom brand themes 3. RTL/i18n Support: CSS logical properties for automatic right-to-left language support 4. Multi-Platform Export: CSS variables, SCSS, iOS Swift, Android XML, JavaScript 5. Component Integration: Skill chaining architecture for consistent styling across all components
Critical Architectural Principle:
Component Skills (Behavior + Structure) → Use tokens for ALL visual styling
Design Tokens (Styling Variables) → Define colors, spacing, typography
Theme Files (Token Overrides) → Light, dark, brand-specific values---
Quick Start
Using Tokens in Components
Step 1: Reference tokens in your component:
.button {
background-color: var(--button-bg-primary);
color: var(--button-text-primary);
padding-inline: var(--button-padding-inline);
padding-block: var(--button-padding-block);
border-radius: var(--button-border-radius);
transition: var(--transition-fast);
}Step 2: Themes automatically apply:
<!-- Light theme -->
<html data-theme="light">
<button class="button">Primary Button</button>
</html>
<!-- Dark theme (same component, different appearance) -->
<html data-theme="dark">
<button class="button">Primary Button</button>
</html>No code changes needed - theme switching is automatic!
Basic Theme Switching
function setTheme(themeName) {
document.documentElement.setAttribute('data-theme', themeName);
localStorage.setItem('theme', themeName);
}
function toggleTheme() {
const current = document.documentElement.getAttribute('data-theme');
setTheme(current === 'dark' ? 'light' : 'dark');
}
// Load saved theme on page load
setTheme(localStorage.getItem('theme') || 'light');---
Token Taxonomy (7 Core Categories)
1. Color Tokens
3-tier hierarchy: Primitive → Semantic → Component
/* Primitive (9-shade scales) */
--color-blue-500: #3B82F6;
/* Semantic (purpose-based) */
--color-primary: var(--color-blue-500);
--color-success: var(--color-green-500);
--color-error: var(--color-red-500);
/* Component-specific */
--button-bg-primary: var(--color-primary);Complete color system: See references/color-system.md
2. Spacing Tokens
4px base scale:
--space-1: 4px; --space-2: 8px; --space-4: 16px;
--space-6: 24px; --space-8: 32px; --space-12: 48px;
/* Semantic */
--spacing-sm: var(--space-2); /* 8px */
--spacing-md: var(--space-4); /* 16px */
--spacing-lg: var(--space-6); /* 24px */3. Typography Tokens
--font-sans: 'Inter', -apple-system, sans-serif;
--font-mono: 'Fira Code', monospace;
--font-size-sm: 14px;
--font-size-base: 16px;
--font-size-lg: 18px;
--font-weight-normal: 400;
--font-weight-semibold: 600;
--font-weight-bold: 700;4. Border & Radius Tokens
--border-width-thin: 1px;
--border-width-medium: 2px;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;5. Shadow Tokens
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.07);
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.12);
--shadow-focus-primary: 0 0 0 3px rgba(59, 130, 246, 0.3);6. Motion Tokens
--duration-fast: 150ms;
--duration-normal: 200ms;
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--transition-fast: all var(--duration-fast) var(--ease-out);Reduced motion support:
@media (prefers-reduced-motion: reduce) {
:root { --transition-fast: none; }
}7. Z-Index Tokens
--z-dropdown: 1000;
--z-modal-backdrop: 1040;
--z-modal: 1050;
--z-tooltip: 1070;---
Theme Architecture
Light/Dark Themes
/* themes/light.css */
:root {
--color-primary: #3B82F6;
--color-background: #FFFFFF;
--color-text-primary: #1F2937;
}
/* themes/dark.css */
:root[data-theme="dark"] {
--color-primary: #60A5FA;
--color-background: #111827;
--color-text-primary: #F9FAFB;
}Custom Brand Theme
:root[data-theme="my-brand"] {
--color-primary: #FF6B35;
--font-sans: 'Poppins', sans-serif;
--radius-md: 12px;
}Complete theme guide: See references/theme-switching.md
---
CSS Logical Properties (RTL Support)
Use logical properties for automatic RTL language support:
| Physical (Avoid) | Logical (Use) |
|---|---|
margin-left | margin-inline-start |
padding-right | padding-inline-end |
text-align: left | text-align: start |
/* Correct - auto-flips in RTL */
.button {
padding-inline: var(--button-padding-inline);
margin-inline-start: var(--spacing-sm);
}Complete RTL guide: See references/logical-properties.md
---
Component Integration
All component skills use this naming convention:
--{component}-{property}-{variant?}-{state?}Examples:
--button-bg-primary
--button-bg-primary-hover
--input-border-color-focus
--chart-color-1Components use tokens for ALL styling:
.button {
background-color: var(--button-bg-primary);
border-radius: var(--button-border-radius);
}Theme changes automatically update all components.
Complete integration guide: See references/component-integration.md
---
Accessibility
WCAG 2.1 AA Compliance
- Normal text: 4.5:1 contrast minimum
- Large text (18px+): 3:1 minimum
- UI components: 3:1 minimum
High-Contrast Theme
:root[data-theme="high-contrast"] {
--color-primary: #0000FF;
--color-text-primary: #000000;
/* 7:1 contrast (WCAG AAA) */
}Reduced Motion
@media (prefers-reduced-motion: reduce) {
:root {
--duration-fast: 0ms;
--transition-fast: none;
}
}Complete accessibility guide: See references/accessibility-tokens.md
---
Platform Exports (Style Dictionary)
Transform tokens to any platform:
JSON Tokens → Style Dictionary → CSS Variables
→ iOS Swift
→ Android XML
→ JavaScriptnpm run build-tokensComplete setup guide: See references/style-dictionary-setup.md
---
W3C Token Format
{
"color": {
"primary": {
"$value": "#3B82F6",
"$type": "color"
}
}
}---
Scripts
# Generate color scale from base color
python scripts/generate_color_scale.py --base "#3B82F6"
# Validate token structure
python scripts/validate_tokens.py
# Check WCAG contrast ratios
python scripts/validate_contrast.py
# Build all platforms
npm run build-tokens---
References
Core Systems:
references/color-system.md- Complete color scales and semanticsreferences/typography-system.md- Type scales and fontsreferences/spacing-system.md- Spacing scale and rhythm
Implementation:
references/theme-switching.md- Light/dark mode, custom themesreferences/component-integration.md- How skills use tokensreferences/logical-properties.md- RTL support patterns
Tools & Accessibility:
references/style-dictionary-setup.md- Multi-platform buildreferences/accessibility-tokens.md- WCAG compliance
---
Key Takeaways
1. Design tokens are the foundation - All visual styling flows from tokens 2. 3-level hierarchy - Primitive → Semantic → Component tokens 3. 7 core categories - Color, spacing, typography, borders, shadows, motion, z-index 4. Theme switching built-in - Light, dark, high-contrast, custom brands 5. RTL support automatic - CSS logical properties enable right-to-left languages 6. Accessibility first - WCAG compliance, reduced motion, high contrast 7. Referenced by all skills - Every component skill uses design tokens
---
Progressive disclosure: This SKILL.md provides overview and quick start. Detailed documentation in references/ directory.
Skill chaining architecture: See SKILL_CHAINING_ARCHITECTURE.md
/**
* Style Dictionary Configuration
* Transforms W3C design tokens to multiple platform formats
*
* Supports: CSS, SCSS, JavaScript, TypeScript, iOS Swift, Android XML
*/
import StyleDictionary from 'style-dictionary';
export default {
// Source token files
source: [
'tokens/global/**/*.json',
'tokens/themes/light.json', // Default theme
'tokens/components/**/*.json'
],
// Platform outputs
platforms: {
// CSS Custom Properties (Web)
css: {
transformGroup: 'css',
buildPath: 'build/css/',
files: [
{
destination: 'variables.css',
format: 'css/variables',
options: {
outputReferences: true, // Use CSS var() references
showFileHeader: true
}
}
]
},
// CSS - Dark Theme
'css-dark': {
transformGroup: 'css',
buildPath: 'build/css/',
source: [
'tokens/global/**/*.json',
'tokens/themes/light.json',
'tokens/themes/dark.json', // Dark theme overrides
'tokens/components/**/*.json'
],
files: [
{
destination: 'variables-dark.css',
format: 'css/variables',
options: {
outputReferences: true,
showFileHeader: true,
selector: ':root[data-theme="dark"]' // Dark theme selector
}
}
]
},
// CSS - High Contrast Theme
'css-high-contrast': {
transformGroup: 'css',
buildPath: 'build/css/',
source: [
'tokens/global/**/*.json',
'tokens/themes/light.json',
'tokens/themes/high-contrast.json',
'tokens/components/**/*.json'
],
files: [
{
destination: 'variables-high-contrast.css',
format: 'css/variables',
options: {
outputReferences: true,
showFileHeader: true,
selector: ':root[data-theme="high-contrast"]'
}
}
]
},
// SCSS Variables
scss: {
transformGroup: 'scss',
buildPath: 'build/scss/',
files: [
{
destination: '_variables.scss',
format: 'scss/variables',
options: {
outputReferences: true,
showFileHeader: true
}
}
]
},
// JavaScript/TypeScript ES6
js: {
transformGroup: 'js',
buildPath: 'build/js/',
files: [
{
destination: 'tokens.js',
format: 'javascript/es6',
options: {
showFileHeader: true
}
},
{
destination: 'tokens.d.ts',
format: 'typescript/es6-declarations',
options: {
showFileHeader: true
}
}
]
},
// iOS Swift
ios: {
transformGroup: 'ios-swift',
buildPath: 'build/ios/',
files: [
{
destination: 'DesignTokens.swift',
format: 'ios-swift/class.swift',
options: {
className: 'DesignTokens',
showFileHeader: true
}
}
]
},
// Android XML
android: {
transformGroup: 'android',
buildPath: 'build/android/res/values/',
files: [
{
destination: 'colors.xml',
format: 'android/colors',
filter: {
type: 'color'
}
},
{
destination: 'dimens.xml',
format: 'android/dimens',
filter: {
type: 'dimension'
}
},
{
destination: 'font_dimens.xml',
format: 'android/fontDimens',
filter: {
type: 'fontSize'
}
}
]
}
}
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Design Tokens - Theme Switcher Demo</title>
<!-- Prevent FOUC (Flash of Unstyled Content) -->
<script>
(function() {
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
})();
</script>
<style>
/* Import generated token CSS */
/* In production, link to build/css/variables.css and variables-dark.css */
/* Light theme (default) */
:root {
--color-primary: #3B82F6;
--color-bg-primary: #FFFFFF;
--color-text-primary: #1F2937;
--color-border: #E5E7EB;
--spacing-md: 16px;
--spacing-lg: 24px;
--radius-md: 8px;
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.1);
--transition-fast: all 150ms ease-out;
}
/* Dark theme */
:root[data-theme="dark"] {
--color-primary: #60A5FA;
--color-bg-primary: #111827;
--color-text-primary: #F9FAFB;
--color-border: #374151;
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.5);
}
/* Base styles */
body {
margin: 0;
padding: var(--spacing-lg);
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
transition: var(--transition-fast);
}
.container {
max-width: 800px;
margin: 0 auto;
}
.card {
background-color: var(--color-bg-primary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding-inline: var(--spacing-lg);
padding-block: var(--spacing-md);
margin-block-end: var(--spacing-md);
box-shadow: var(--shadow-md);
transition: var(--transition-fast);
}
.button {
background-color: var(--color-primary);
color: white;
padding-inline: var(--spacing-lg);
padding-block: var(--spacing-md);
border: none;
border-radius: var(--radius-md);
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: var(--transition-fast);
}
.button:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.theme-selector {
display: flex;
gap: var(--spacing-md);
margin-block-end: var(--spacing-lg);
}
.theme-button {
padding-inline: var(--spacing-md);
padding-block: var(--spacing-md);
border: 2px solid var(--color-border);
border-radius: var(--radius-md);
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
cursor: pointer;
transition: var(--transition-fast);
font-size: 14px;
}
.theme-button.active {
background-color: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.theme-button:hover {
border-color: var(--color-primary);
}
</style>
</head>
<body>
<div class="container">
<h1>Design Tokens - Theme Switcher Demo</h1>
<!-- Theme selector -->
<div class="theme-selector">
<button class="theme-button" data-theme="light">
☀️ Light
</button>
<button class="theme-button" data-theme="dark">
🌙 Dark
</button>
<button class="theme-button" data-theme="high-contrast">
⚡ High Contrast
</button>
</div>
<!-- Demo cards -->
<div class="card">
<h2>Welcome to Design Tokens</h2>
<p>
This demo shows how design tokens enable automatic theme switching.
All visual styling comes from CSS custom properties (design tokens).
</p>
<p>
Try switching themes above - notice how all components update instantly
without any JavaScript manipulation of styles.
</p>
</div>
<div class="card">
<h3>How It Works</h3>
<ol>
<li>Set <code>data-theme</code> attribute on <code><html></code></li>
<li>CSS overrides token values for that theme</li>
<li>All components referencing tokens update automatically</li>
</ol>
<button class="button">Example Button</button>
</div>
<div class="card">
<h3>Benefits</h3>
<ul>
<li>✅ Zero code changes for new themes</li>
<li>✅ Consistent visual design</li>
<li>✅ Easy brand customization</li>
<li>✅ Automatic RTL support (with logical properties)</li>
<li>✅ Accessibility built-in (WCAG 2.1 AA)</li>
</ul>
</div>
</div>
<script>
// Theme switching logic
function setTheme(themeName) {
// Set attribute on <html>
document.documentElement.setAttribute('data-theme', themeName);
// Save preference
localStorage.setItem('theme', themeName);
// Update active button
document.querySelectorAll('.theme-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.theme === themeName);
});
}
// Initialize
const currentTheme = localStorage.getItem('theme') || 'light';
setTheme(currentTheme);
// Add click handlers
document.querySelectorAll('.theme-button').forEach(btn => {
btn.addEventListener('click', () => {
setTheme(btn.dataset.theme);
});
});
// Listen to system preference changes (optional)
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
setTheme(e.matches ? 'dark' : 'light');
}
});
</script>
</body>
</html>
/**
* React Theme Provider
*
* Provides theme context to entire application with:
* - Light/dark mode support
* - Custom theme support
* - LocalStorage persistence
* - System preference detection
* - FOUC prevention
*/
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
export type Theme = 'light' | 'dark' | 'high-contrast' | string;
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
toggleTheme: () => void;
availableThemes: { value: Theme; label: string; icon: string }[];
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
const AVAILABLE_THEMES = [
{ value: 'light' as Theme, label: 'Light', icon: '☀️' },
{ value: 'dark' as Theme, label: 'Dark', icon: '🌙' },
{ value: 'high-contrast' as Theme, label: 'High Contrast', icon: '⚡' }
];
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setThemeState] = useState<Theme>('light');
const [isInitialized, setIsInitialized] = useState(false);
// Initialize theme on mount
useEffect(() => {
const initTheme = () => {
// 1. Check saved preference
const saved = localStorage.getItem('theme');
if (saved) {
setThemeState(saved);
return;
}
// 2. Check system preference
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const prefersHighContrast = window.matchMedia('(prefers-contrast: high)').matches;
if (prefersHighContrast) {
setThemeState('high-contrast');
} else if (prefersDark) {
setThemeState('dark');
} else {
setThemeState('light');
}
};
initTheme();
setIsInitialized(true);
}, []);
// Apply theme when it changes
useEffect(() => {
if (!isInitialized) return;
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme, isInitialized]);
// Listen to system preference changes
useEffect(() => {
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
const contrastQuery = window.matchMedia('(prefers-contrast: high)');
const handleChange = () => {
// Only auto-switch if user hasn't set manual preference
if (!localStorage.getItem('theme')) {
if (contrastQuery.matches) {
setThemeState('high-contrast');
} else if (darkModeQuery.matches) {
setThemeState('dark');
} else {
setThemeState('light');
}
}
};
darkModeQuery.addEventListener('change', handleChange);
contrastQuery.addEventListener('change', handleChange);
return () => {
darkModeQuery.removeEventListener('change', handleChange);
contrastQuery.removeEventListener('change', handleChange);
};
}, []);
const setTheme = (newTheme: Theme) => {
setThemeState(newTheme);
};
const toggleTheme = () => {
setThemeState(prev => prev === 'dark' ? 'light' : 'dark');
};
return (
<ThemeContext.Provider value={{ theme, setTheme, toggleTheme, availableThemes: AVAILABLE_THEMES }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
/**
* Theme Toggle Component
*
* Simple button to toggle between light and dark themes
*/
import { useTheme } from './ThemeProvider';
export function ThemeToggle() {
const { theme, toggleTheme } = useTheme();
return (
<button
onClick={toggleTheme}
style={{
backgroundColor: 'var(--button-bg-secondary)',
color: 'var(--button-text-secondary)',
paddingInline: 'var(--button-padding-inline)',
paddingBlock: 'var(--button-padding-block)',
borderRadius: 'var(--button-border-radius)',
border: '1px solid var(--color-border)',
fontSize: 'var(--font-size-base)',
fontWeight: 'var(--font-weight-medium)',
cursor: 'pointer',
transition: 'var(--transition-fast)',
display: 'flex',
alignItems: 'center',
gap: 'var(--spacing-xs)'
}}
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} theme`}
>
<span style={{ fontSize: '20px' }}>
{theme === 'light' ? '🌙' : '☀️'}
</span>
<span>
{theme === 'light' ? 'Dark Mode' : 'Light Mode'}
</span>
</button>
);
}
/**
* Theme Selector with Multiple Options
*/
export function ThemeSelector() {
const { theme, setTheme, availableThemes } = useTheme();
return (
<div style={{ display: 'flex', gap: 'var(--spacing-sm)' }}>
{availableThemes.map(t => (
<button
key={t.value}
onClick={() => setTheme(t.value)}
style={{
backgroundColor: theme === t.value ? 'var(--button-bg-primary)' : 'var(--button-bg-secondary)',
color: theme === t.value ? 'var(--button-text-primary)' : 'var(--button-text-secondary)',
paddingInline: 'var(--spacing-md)',
paddingBlock: 'var(--spacing-sm)',
borderRadius: 'var(--button-border-radius)',
border: theme === t.value ? 'none' : '1px solid var(--color-border)',
fontSize: 'var(--font-size-sm)',
fontWeight: 'var(--font-weight-medium)',
cursor: 'pointer',
transition: 'var(--transition-fast)'
}}
aria-label={`Switch to ${t.label} theme`}
aria-pressed={theme === t.value}
>
<span style={{ marginInlineEnd: 'var(--spacing-xs)' }}>{t.icon}</span>
{t.label}
</button>
))}
</div>
);
}
/**
* Token Usage Examples
*
* Demonstrates how to properly use design tokens in components
* Shows both inline styles and CSS approaches
*/
import React from 'react';
/**
* Example 1: Button Component using inline styles
*/
export function TokenButton({ variant = 'primary', children, ...props }) {
return (
<button
style={{
// ✅ Use component tokens
backgroundColor: `var(--button-bg-${variant})`,
color: `var(--button-text-${variant})`,
// ✅ Use logical properties for RTL
paddingInline: 'var(--button-padding-inline)',
paddingBlock: 'var(--button-padding-block)',
// ✅ Reference other tokens
borderRadius: 'var(--button-border-radius)',
fontSize: 'var(--button-font-size)',
fontWeight: 'var(--button-font-weight)',
border: 'none',
cursor: 'pointer',
transition: 'var(--transition-fast)',
}}
{...props}
>
{children}
</button>
);
}
/**
* Example 2: Card Component with all token categories
*/
export function TokenCard({ children }) {
return (
<div
style={{
// Colors
backgroundColor: 'var(--color-bg-primary)',
color: 'var(--color-text-primary)',
border: '1px solid var(--color-border)',
// Spacing (logical properties)
paddingInline: 'var(--spacing-lg)',
paddingBlock: 'var(--spacing-md)',
marginBlockEnd: 'var(--spacing-md)',
// Borders
borderRadius: 'var(--radius-lg)',
// Shadows
boxShadow: 'var(--shadow-md)',
// Typography
fontFamily: 'var(--font-sans)',
fontSize: 'var(--font-size-base)',
lineHeight: 'var(--line-height-normal)',
// Motion
transition: 'var(--transition-normal)',
}}
>
{children}
</div>
);
}
/**
* Example 3: Alert Component with semantic tokens
*/
export function TokenAlert({ type = 'info', children }) {
return (
<div
role="alert"
style={{
backgroundColor: `var(--color-${type}-bg)`,
color: `var(--color-${type})`,
borderInlineStart: `4px solid var(--color-${type})`,
paddingInline: 'var(--spacing-md)',
paddingBlock: 'var(--spacing-sm)',
borderRadius: 'var(--radius-md)',
fontSize: 'var(--font-size-sm)',
marginBlockEnd: 'var(--spacing-md)',
}}
>
{children}
</div>
);
}
/**
* Example 4: Token Reference Demo
* Shows all token categories in use
*/
export function TokenShowcase() {
return (
<div style={{ padding: 'var(--spacing-xl)' }}>
<h1 style={{
fontSize: 'var(--font-size-4xl)',
fontWeight: 'var(--font-weight-bold)',
color: 'var(--color-text-primary)',
marginBlockEnd: 'var(--spacing-lg)'
}}>
Design Token Showcase
</h1>
{/* Colors */}
<section style={{ marginBlockEnd: 'var(--spacing-xl)' }}>
<h2 style={{
fontSize: 'var(--font-size-2xl)',
fontWeight: 'var(--font-weight-semibold)',
marginBlockEnd: 'var(--spacing-md)'
}}>
Colors
</h2>
<div style={{ display: 'flex', gap: 'var(--spacing-md)', flexWrap: 'wrap' }}>
<div style={{
width: '100px',
height: '100px',
backgroundColor: 'var(--color-primary)',
borderRadius: 'var(--radius-md)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'var(--color-text-inverse)',
fontSize: 'var(--font-size-sm)'
}}>
Primary
</div>
<div style={{
width: '100px',
height: '100px',
backgroundColor: 'var(--color-success)',
borderRadius: 'var(--radius-md)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'var(--color-text-inverse)',
fontSize: 'var(--font-size-sm)'
}}>
Success
</div>
<div style={{
width: '100px',
height: '100px',
backgroundColor: 'var(--color-warning)',
borderRadius: 'var(--radius-md)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'var(--color-text-inverse)',
fontSize: 'var(--font-size-sm)'
}}>
Warning
</div>
<div style={{
width: '100px',
height: '100px',
backgroundColor: 'var(--color-error)',
borderRadius: 'var(--radius-md)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'var(--color-text-inverse)',
fontSize: 'var(--font-size-sm)'
}}>
Error
</div>
</div>
</section>
{/* Spacing */}
<section style={{ marginBlockEnd: 'var(--spacing-xl)' }}>
<h2 style={{
fontSize: 'var(--font-size-2xl)',
fontWeight: 'var(--font-weight-semibold)',
marginBlockEnd: 'var(--spacing-md)'
}}>
Spacing Scale
</h2>
<div style={{ display: 'flex', alignItems: 'flex-end', gap: 'var(--spacing-xs)' }}>
{['xs', 'sm', 'md', 'lg', 'xl', '2xl'].map(size => (
<div key={size} style={{
width: `var(--spacing-${size})`,
height: `var(--spacing-${size})`,
backgroundColor: 'var(--color-primary)',
borderRadius: 'var(--radius-sm)'
}} />
))}
</div>
</section>
{/* Typography */}
<section style={{ marginBlockEnd: 'var(--spacing-xl)' }}>
<h2 style={{
fontSize: 'var(--font-size-2xl)',
fontWeight: 'var(--font-weight-semibold)',
marginBlockEnd: 'var(--spacing-md)'
}}>
Typography Scale
</h2>
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-sm)' }}>
<p style={{ fontSize: 'var(--font-size-xs)' }}>Extra Small (12px)</p>
<p style={{ fontSize: 'var(--font-size-sm)' }}>Small (14px)</p>
<p style={{ fontSize: 'var(--font-size-base)' }}>Base (16px)</p>
<p style={{ fontSize: 'var(--font-size-lg)' }}>Large (18px)</p>
<p style={{ fontSize: 'var(--font-size-xl)' }}>Extra Large (20px)</p>
</div>
</section>
{/* Shadows */}
<section style={{ marginBlockEnd: 'var(--spacing-xl)' }}>
<h2 style={{
fontSize: 'var(--font-size-2xl)',
fontWeight: 'var(--font-weight-semibold)',
marginBlockEnd: 'var(--spacing-md)'
}}>
Elevation Shadows
</h2>
<div style={{ display: 'flex', gap: 'var(--spacing-md)', flexWrap: 'wrap' }}>
{['sm', 'md', 'lg', 'xl'].map(size => (
<div
key={size}
style={{
width: '120px',
height: '80px',
backgroundColor: 'var(--color-bg-primary)',
borderRadius: 'var(--radius-md)',
boxShadow: `var(--shadow-${size})`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 'var(--font-size-sm)',
color: 'var(--color-text-secondary)'
}}
>
{size}
</div>
))}
</div>
</section>
{/* Border Radius */}
<section>
<h2 style={{
fontSize: 'var(--font-size-2xl)',
fontWeight: 'var(--font-weight-semibold)',
marginBlockEnd: 'var(--spacing-md)'
}}>
Border Radius
</h2>
<div style={{ display: 'flex', gap: 'var(--spacing-md)', flexWrap: 'wrap' }}>
{['sm', 'md', 'lg', 'xl', 'full'].map(size => (
<div
key={size}
style={{
width: size === 'full' ? '80px' : '80px',
height: '80px',
backgroundColor: 'var(--color-primary)',
borderRadius: `var(--radius-${size})`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 'var(--font-size-sm)',
color: 'var(--color-text-inverse)'
}}
>
{size}
</div>
))}
</div>
</section>
</div>
);
}
skill: "theming-components"
version: "1.0"
domain: "frontend"
# Base outputs required for all theming implementations
base_outputs:
- path: "tokens/"
must_contain: ["global/", "themes/", "components/"]
reason: "Design token directory structure (global → themes → components)"
- path: "tokens/global/"
must_contain: ["colors.json", "spacing.json", "typography.json"]
reason: "Core primitive design tokens"
- path: "build/"
must_contain: []
reason: "Platform-specific token outputs (CSS, SCSS, JS, iOS, Android)"
- path: "config.js"
must_contain: ["StyleDictionary", "source:", "platforms:"]
reason: "Style Dictionary configuration for token transformation"
# Conditional outputs based on configuration
conditional_outputs:
maturity:
starter:
- path: "tokens/global/colors.json"
must_contain: ["$value", "$type"]
reason: "W3C design token format for color primitives"
- path: "tokens/themes/light.json"
must_contain: ["color"]
reason: "Light theme token overrides"
- path: "build/css/variables.css"
must_contain: [":root", "--color-", "var(--"]
reason: "CSS custom properties output"
- path: "examples/theme-switcher.html"
must_contain: ["data-theme", "setTheme"]
reason: "Basic light/dark mode toggle"
intermediate:
- path: "tokens/global/"
must_contain: ["colors.json", "spacing.json", "typography.json", "borders.json", "shadows.json"]
reason: "Complete 5-category token taxonomy"
- path: "tokens/themes/"
must_contain: ["light.json", "dark.json"]
reason: "Light and dark theme configurations"
- path: "build/css/"
must_contain: ["variables.css", "variables-dark.css"]
reason: "Multi-theme CSS outputs with theme selectors"
- path: "examples/ThemeProvider.tsx"
must_contain: ["createContext", "localStorage", "prefers-color-scheme"]
reason: "React theme provider with persistence and system preference"
- path: "tokens/components/"
must_contain: ["button.json"]
reason: "Component-specific design tokens"
- path: "scripts/validate_tokens.py"
must_contain: ["json", "schema"]
reason: "Token structure validation script"
advanced:
- path: "tokens/global/"
must_contain: ["colors.json", "spacing.json", "typography.json", "borders.json", "shadows.json", "motion.json", "z-index.json"]
reason: "Complete 7-category token taxonomy"
- path: "tokens/themes/"
must_contain: ["light.json", "dark.json", "high-contrast.json"]
reason: "Full theme set including high-contrast accessibility"
- path: "tokens/languages/"
must_contain: ["ar.json"]
reason: "RTL language support configuration"
- path: "build/"
must_contain: ["css/", "scss/", "js/", "ios/", "android/"]
reason: "Multi-platform token exports (web, mobile)"
- path: "scripts/generate_color_scale.py"
must_contain: ["colorsys", "def generate_scale"]
reason: "Color scale generation from base colors"
- path: "scripts/validate_contrast.py"
must_contain: ["WCAG", "contrast_ratio", "4.5"]
reason: "WCAG 2.1 contrast ratio validation"
- path: "scripts/validate_logical_properties.py"
must_contain: ["margin-inline", "padding-inline", "text-align: start"]
reason: "CSS logical property validation for RTL support"
- path: "examples/TokenUsageExample.tsx"
must_contain: ["var(--", "semantic token", "component token"]
reason: "Comprehensive token usage patterns (primitive/semantic/component)"
frontend_framework:
react:
- path: "examples/ThemeProvider.tsx"
must_contain: ["createContext", "useContext", "useState"]
reason: "React Context-based theme provider"
- path: "examples/ThemeToggle.tsx"
must_contain: ["useTheme", "toggleTheme"]
reason: "React theme toggle component"
- path: "build/js/tokens.d.ts"
must_contain: ["export", "declare"]
reason: "TypeScript type definitions for tokens"
vue:
- path: "examples/ThemeProvider.vue"
must_contain: ["provide", "inject", "ref"]
reason: "Vue Composition API theme provider"
- path: "examples/ThemeToggle.vue"
must_contain: ["<template>", "useTheme"]
reason: "Vue theme toggle component"
svelte:
- path: "examples/themeStore.ts"
must_contain: ["writable", "subscribe"]
reason: "Svelte store for theme management"
- path: "examples/ThemeToggle.svelte"
must_contain: ["<script>", "$theme"]
reason: "Svelte theme toggle component"
vanilla:
- path: "examples/theme-switcher.html"
must_contain: ["data-theme", "localStorage", "setAttribute"]
reason: "Vanilla JS theme implementation"
styling:
tailwind:
- path: "tailwind.config.js"
must_contain: ["theme:", "extend:", "colors:"]
reason: "Tailwind configuration with token integration"
- path: "build/js/tokens.js"
must_contain: ["export", "const"]
reason: "JavaScript tokens for Tailwind config consumption"
- path: "examples/tailwind-theme-example.tsx"
must_contain: ["className", "dark:"]
reason: "Tailwind with design token integration"
css_modules:
- path: "examples/Button.module.css"
must_contain: [".button", "var(--button-"]
reason: "CSS Modules using design tokens"
- path: "build/css/variables.css"
must_contain: [":root", "--"]
reason: "CSS custom properties for CSS Modules"
styled_components:
- path: "examples/StyledButton.tsx"
must_contain: ["styled.", "props.theme"]
reason: "Styled-components with theme prop"
- path: "examples/theme.ts"
must_contain: ["export const lightTheme", "export const darkTheme"]
reason: "Theme objects for styled-components ThemeProvider"
scss:
- path: "build/scss/_variables.scss"
must_contain: ["$color-", "$spacing-"]
reason: "SCSS variables from design tokens"
- path: "examples/styles.scss"
must_contain: ["@import", "$color-primary"]
reason: "SCSS consuming token variables"
state_management:
context:
- path: "examples/ThemeProvider.tsx"
must_contain: ["createContext", "ThemeContext.Provider"]
reason: "React Context for theme state"
zustand:
- path: "examples/useThemeStore.ts"
must_contain: ["create", "persist"]
reason: "Zustand store with persistence for theme state"
redux:
- path: "examples/themeSlice.ts"
must_contain: ["createSlice", "setTheme"]
reason: "Redux slice for theme management"
pinia:
- path: "examples/themeStore.ts"
must_contain: ["defineStore", "pinia"]
reason: "Pinia store for Vue theme state"
# Scaffolding files that should be created as starting points
scaffolding:
- path: "tokens/global/colors.json"
reason: "Initialize primitive color tokens (9-shade scales)"
- path: "tokens/global/spacing.json"
reason: "Initialize spacing scale (4px base)"
- path: "tokens/global/typography.json"
reason: "Initialize typography tokens (fonts, sizes, weights)"
- path: "tokens/global/borders.json"
reason: "Initialize border width and radius tokens"
- path: "tokens/global/shadows.json"
reason: "Initialize shadow token definitions"
- path: "tokens/global/motion.json"
reason: "Initialize animation duration and easing tokens"
- path: "tokens/global/z-index.json"
reason: "Initialize z-index layering system"
- path: "tokens/themes/light.json"
reason: "Initialize light theme (default)"
- path: "tokens/themes/dark.json"
reason: "Initialize dark theme configuration"
- path: "tokens/themes/high-contrast.json"
reason: "Initialize high-contrast accessibility theme"
- path: "tokens/components/.gitkeep"
reason: "Initialize component tokens directory"
- path: "config.js"
reason: "Style Dictionary build configuration"
- path: "package.json"
reason: "Node.js dependencies (style-dictionary)"
- path: "build/.gitkeep"
reason: "Initialize build output directory"
- path: ".gitignore"
reason: "Ignore node_modules and build artifacts"
- path: "README.md"
reason: "Document design token system and usage"
# Metadata
metadata:
primary_blueprints: ["dashboard", "frontend"]
contributes_to:
- "Design token system (primitives, semantic, component tokens)"
- "Theme switching (light, dark, high-contrast, custom brands)"
- "RTL/i18n support (CSS logical properties)"
- "Accessibility (WCAG 2.1 AA/AAA compliance)"
- "Multi-platform export (CSS, SCSS, iOS, Android, JavaScript)"
common_patterns:
- "3-tier token hierarchy (primitive → semantic → component)"
- "W3C design token format ($value, $type)"
- "CSS custom properties with var() references"
- "Theme switching via data-theme attribute"
- "System preference detection (prefers-color-scheme)"
- "LocalStorage persistence for theme preference"
- "CSS logical properties for RTL support"
- "Reduced motion support (@media prefers-reduced-motion)"
- "WCAG contrast validation (4.5:1 normal text, 3:1 large text)"
integration_points:
all_component_skills: "All component skills reference design tokens for styling"
forms: "Form inputs use button/input component tokens"
data_viz: "Charts use semantic color tokens for consistent visualization"
dashboards: "Dashboard layout uses spacing and z-index tokens"
navigation: "Nav components use semantic color and spacing tokens"
feedback: "Alerts/toasts use semantic color tokens (success, error, warning)"
typical_directory_structure: |
project/
├── tokens/ # Source design tokens
│ ├── global/ # Primitive tokens
│ │ ├── colors.json # Color scales (blue-50 to blue-900)
│ │ ├── spacing.json # Spacing scale (4px base)
│ │ ├── typography.json # Font families, sizes, weights
│ │ ├── borders.json # Border widths, radius
│ │ ├── shadows.json # Box shadow definitions
│ │ ├── motion.json # Animation durations, easings
│ │ └── z-index.json # Layering system
│ ├── themes/ # Theme overrides
│ │ ├── light.json # Light theme (default)
│ │ ├── dark.json # Dark theme
│ │ └── high-contrast.json # Accessibility theme
│ ├── components/ # Component-specific tokens
│ │ ├── button.json # Button tokens
│ │ ├── input.json # Input tokens
│ │ └── chart.json # Chart color tokens
│ └── languages/ # RTL/i18n configs
│ └── ar.json # Arabic (RTL) overrides
├── build/ # Generated platform outputs
│ ├── css/
│ │ ├── variables.css # Light theme CSS
│ │ ├── variables-dark.css # Dark theme CSS
│ │ └── variables-high-contrast.css
│ ├── scss/
│ │ └── _variables.scss # SCSS variables
│ ├── js/
│ │ ├── tokens.js # ES6 module
│ │ └── tokens.d.ts # TypeScript definitions
│ ├── ios/
│ │ └── DesignTokens.swift # Swift class
│ └── android/
│ └── colors.xml # Android resources
├── scripts/ # Token utilities
│ ├── generate_color_scale.py # Generate 9-shade scales
│ ├── validate_tokens.py # Structure validation
│ ├── validate_contrast.py # WCAG compliance
│ └── validate_logical_properties.py # RTL validation
├── examples/ # Usage examples
│ ├── ThemeProvider.tsx # React theme provider
│ ├── ThemeToggle.tsx # Theme switcher UI
│ ├── TokenUsageExample.tsx # Token hierarchy demo
│ └── theme-switcher.html # Vanilla JS example
├── references/ # Documentation
│ ├── color-system.md # Color token details
│ ├── theme-switching.md # Theme implementation guide
│ ├── logical-properties.md # RTL support patterns
│ ├── accessibility-tokens.md # WCAG compliance guide
│ └── component-integration.md # How skills use tokens
├── config.js # Style Dictionary config
├── package.json # Node dependencies
└── README.md # Project overview
tools_required:
- name: "Style Dictionary"
version: "^4.0.0"
purpose: "Transform design tokens to platform formats"
install: "npm install style-dictionary"
- name: "Python 3.x"
purpose: "Run validation and generation scripts"
packages: ["colorsys (built-in)"]
validation_checks:
- "W3C design token format ($value, $type) in all token files"
- "Primitive tokens exist (colors.json, spacing.json, typography.json)"
- "Light and dark themes defined"
- "CSS custom properties use kebab-case naming"
- "Component tokens reference semantic tokens (not primitives)"
- "WCAG 2.1 AA contrast ratios met (4.5:1 normal, 3:1 large)"
- "CSS logical properties used (margin-inline vs margin-left)"
- "Reduced motion support (@media prefers-reduced-motion)"
- "Theme switcher persists to localStorage"
- "System preference detection (prefers-color-scheme)"
anti_patterns:
- name: "Components use primitive tokens directly"
avoid: "--color-blue-500 in button"
use: "--button-bg-primary referencing --color-primary"
- name: "Physical CSS properties"
avoid: "margin-left, padding-right, text-align: left"
use: "margin-inline-start, padding-inline-end, text-align: start"
- name: "Hardcoded colors in components"
avoid: "color: #3B82F6"
use: "color: var(--button-text-primary)"
- name: "No theme switching support"
avoid: "Single hardcoded color set"
use: "CSS custom properties with data-theme attribute"
- name: "Missing accessibility themes"
avoid: "Only light and dark themes"
use: "Include high-contrast theme for WCAG AAA"
{
"name": "@ai-design-components/design-tokens",
"version": "1.0.0",
"description": "Design token system for ai-design-components - foundational styling layer",
"type": "module",
"scripts": {
"build": "style-dictionary build --config config.js",
"build:watch": "style-dictionary build --config config.js --watch",
"clean": "rm -rf build/",
"validate": "python scripts/validate_tokens.py",
"validate:contrast": "python scripts/validate_contrast.py",
"validate:rtl": "python scripts/validate_logical_properties.py",
"generate:palette": "python scripts/generate_color_scale.py"
},
"keywords": [
"design-tokens",
"design-system",
"theming",
"css-variables",
"style-dictionary",
"w3c",
"accessibility",
"rtl",
"i18n"
],
"author": "Anton Coleman",
"license": "MIT",
"dependencies": {
"style-dictionary": "^4.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0"
},
"files": [
"build/",
"tokens/",
"config.js",
"SKILL.md",
"README.md"
]
}
Design Tokens & Theming System
Foundational styling layer for the ai-design-components skill library
Version: 1.0.0 | Status: ✅ Complete | W3C Compliant
---
Overview
The design-tokens skill provides the foundational styling architecture for all component skills in the ai-design-components library. It enables:
- 🎨 Theme Switching: Light/dark mode, high-contrast, custom brand themes
- 🌍 RTL/i18n Support: CSS logical properties for automatic right-to-left language support
- ♿ Accessibility: WCAG 2.1 AA compliant color combinations, high-contrast themes, reduced motion
- 🚀 Multi-Platform: Export to CSS, SCSS, iOS Swift, Android XML, JavaScript/TypeScript
- 🔗 Skill Chaining: Component skills reference tokens for consistent, themeable styling
---
Quick Start
1. Build Tokens
# Install dependencies
npm install
# Build tokens for all platforms
npm run build
# Watch mode (auto-rebuild on changes)
npm run build:watchGenerates:
build/css/variables.css- CSS custom properties (light theme)build/css/variables-dark.css- Dark theme overridesbuild/css/variables-high-contrast.css- High-contrast themebuild/scss/_variables.scss- SCSS variablesbuild/js/tokens.js- JavaScript/TypeScript tokensbuild/ios/DesignTokens.swift- iOS Swift tokens
---
2. Use in Your Project
HTML:
<link rel="stylesheet" href="build/css/variables.css">
<link rel="stylesheet" href="build/css/variables-dark.css">CSS:
.button {
background-color: var(--button-bg-primary);
color: var(--button-text-primary);
padding-inline: var(--button-padding-inline);
border-radius: var(--button-border-radius);
}JavaScript (theme switching):
document.documentElement.setAttribute('data-theme', 'dark');---
Token Taxonomy
7 Core Categories:
1. Color (tokens/global/colors.json)
- 9-shade palettes: gray, blue, purple, green, yellow, red, orange
- Semantic colors: primary, success, warning, error, text, backgrounds, borders
2. Spacing (tokens/global/spacing.json)
- 4px base scale (0, 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, 32)
- Semantic spacing: xs, sm, md, lg, xl, 2xl, 3xl
3. Typography (tokens/global/typography.json)
- Font families: sans, serif, mono
- Type scale: xs → 7xl (12px → 72px)
- Font weights: thin → black (100 → 900)
- Line heights: tight, normal, relaxed
4. Borders (tokens/global/borders.json)
- Border widths: thin, medium, thick
- Border radius: sm → full (4px → 9999px)
5. Shadows (tokens/global/shadows.json)
- Elevation: xs → 2xl
- Focus rings (colored)
- Inner shadows
6. Motion (tokens/global/motion.json)
- Durations: instant → slower (100ms → 700ms)
- Easing: linear, in, out, in-out, bounce
7. Z-Index (tokens/global/z-index.json)
- Layering: base → notification (0 → 1080)
---
Token Hierarchy
3-Tier Architecture:
Primitive Tokens (Foundation)
↓ referenced by
Semantic Tokens (Purpose)
↓ referenced by
Component Tokens (Specific)
↓ used by
Component Skills (forms, data-viz, tables, etc.)Example:
--color-blue-500 (Primitive)
↓
--color-primary (Semantic)
↓
--button-bg-primary (Component)
↓
<button> in forms skill---
Themes
3 Built-in Themes:
1. Light (tokens/themes/light.json) - Default theme 2. Dark (tokens/themes/dark.json) - Dark mode overrides 3. High-Contrast (tokens/themes/high-contrast.json) - WCAG AAA (7:1 contrast)
Custom Themes: Create your own by overriding token values:
:root[data-theme="my-brand"] {
--color-primary: #FF6B35;
--font-sans: 'Poppins', sans-serif;
--radius-md: 12px;
}---
CSS Logical Properties (RTL Support)
All tokens use logical properties for automatic RTL support:
/* ✅ CORRECT - Auto-flips in RTL */
--button-padding-inline: 24px;
--icon-margin-inline-end: 4px;
/* ❌ WRONG - Won't flip in RTL */
--button-padding-left: 24px;
--icon-margin-right: 4px;Supported languages: Arabic, Hebrew, Persian, Urdu (RTL), plus all LTR languages
---
Component Integration
All component skills reference design tokens:
Button (from forms skill):
.button {
background-color: var(--button-bg-primary);
padding-inline: var(--button-padding-inline);
border-radius: var(--button-border-radius);
}Chart (from data-viz skill):
<Line stroke="var(--chart-color-1)" />Complete integration guide: SKILL_CHAINING_ARCHITECTURE.md
---
File Structure
design-tokens/
├── SKILL.md # Main skill file (878 lines)
├── SKILL_CHAINING_ARCHITECTURE.md # Integration guide for component skills
├── init.md # Master plan (1845 lines)
├── README.md # This file
├── config.js # Style Dictionary configuration
├── package.json # Dependencies
│
├── tokens/ # W3C format source tokens
│ ├── global/
│ │ ├── colors.json # Color primitives (9-shade palettes)
│ │ ├── spacing.json # Spacing scale (4px base)
│ │ ├── typography.json # Fonts, sizes, weights, line heights
│ │ ├── borders.json # Border widths and radii
│ │ ├── shadows.json # Elevation shadows
│ │ ├── motion.json # Animation durations and easing
│ │ └── z-index.json # Layering system
│ ├── themes/
│ │ ├── light.json # Light theme (semantic mappings)
│ │ ├── dark.json # Dark theme overrides
│ │ └── high-contrast.json # High-contrast theme (WCAG AAA)
│ ├── components/
│ │ ├── button.json # Button component tokens
│ │ ├── input.json # Input component tokens
│ │ └── chart.json # Chart component tokens (data-viz)
│ └── languages/
│ ├── ar.json # Arabic overrides
│ └── ja.json # Japanese overrides
│
├── build/ # Generated output (from Style Dictionary)
│ ├── css/
│ │ ├── variables.css # Light theme CSS variables
│ │ ├── variables-dark.css # Dark theme CSS variables
│ │ └── variables-high-contrast.css # High-contrast CSS variables
│ ├── scss/
│ │ └── _variables.scss # SCSS variables
│ ├── js/
│ │ ├── tokens.js # JavaScript ES6 tokens
│ │ └── tokens.d.ts # TypeScript declarations
│ └── ios/
│ └── DesignTokens.swift # iOS Swift tokens
│
├── scripts/ # Token-free execution scripts
│ ├── generate_color_scale.py # Generate 9-shade palette
│ ├── validate_tokens.py # Validate W3C format
│ ├── validate_contrast.py # Check WCAG compliance
│ └── validate_logical_properties.py # Verify RTL support
│
├── references/ # Progressive disclosure docs
│ ├── component-integration.md # How components use tokens
│ ├── theme-switching.md # Theme implementation guide
│ ├── logical-properties.md # CSS logical properties reference
│ ├── accessibility-tokens.md # WCAG compliance guide
│ └── style-dictionary-setup.md # Build system documentation
│
└── examples/ # Working code examples
├── ThemeProvider.tsx # React theme context
├── ThemeToggle.tsx # Theme toggle component
├── TokenUsageExample.tsx # Token usage patterns
└── theme-switcher.html # Vanilla JS demo---
Usage Examples
React with Theme Provider
// App.tsx
import { ThemeProvider } from './design-tokens/examples/ThemeProvider';
import { ThemeToggle } from './design-tokens/examples/ThemeToggle';
function App() {
return (
<ThemeProvider>
<ThemeToggle />
<YourComponents />
</ThemeProvider>
);
}Vanilla JavaScript
// Set theme
function setTheme(themeName) {
document.documentElement.setAttribute('data-theme', themeName);
localStorage.setItem('theme', themeName);
}
// Toggle light/dark
function toggleTheme() {
const current = document.documentElement.getAttribute('data-theme');
setTheme(current === 'dark' ? 'light' : 'dark');
}Component with Tokens
.my-component {
/* Use component tokens */
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
/* Use logical properties for RTL */
padding-inline: var(--spacing-md);
padding-block: var(--spacing-sm);
/* Reference other categories */
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
transition: var(--transition-fast);
}---
Scripts & Validation
Generate Color Scale
# Create 9-shade palette from base color
python scripts/generate_color_scale.py \
--base "#FF6B35" \
--name "brand-orange" \
--output tokens/global/colors-custom.jsonValidate Tokens
# Validate W3C format and naming
npm run validate
# Check WCAG color contrast
npm run validate:contrast
# Verify RTL compatibility
npm run validate:rtl---
W3C Compliance
Specification: Design Tokens Community Group 2025.10
Required properties:
$value- Token value$type- Token type (color, dimension, fontSize, etc.)
Optional properties:
$description- Human-readable description$extensions- Custom metadata
Token references:
{
"color": {
"primary": {
"$value": "{color.blue.500}",
"$type": "color"
}
}
}---
Accessibility Features
✅ WCAG 2.1 AA Compliant
- All text/background combinations meet 4.5:1 contrast
- UI components meet 3:1 contrast
- Validated with
scripts/validate_contrast.py
✅ High-Contrast Theme
- 7:1 contrast ratio (WCAG AAA)
- Auto-applies via
prefers-contrast: highmedia query
✅ Reduced Motion
- Honors
prefers-reduced-motion: reduce - Disables all animations when requested
✅ Colorblind-Safe
- Chart colors use IBM/Paul Tol palettes
- No red/green reliance
---
Component Token Naming
Convention:
--{component}-{property}-{variant?}-{state?}Examples:
--button-bg-primary--button-bg-primary-hover--input-border-color-focus--chart-color-1
Complete naming guide: SKILL_CHAINING_ARCHITECTURE.md
---
Integration with Other Skills
This skill is referenced by:
- ✅
data-viz- Chart color palettes, axis colors, tooltips - ✅
forms- Button, input, select, checkbox styling - 🚧
tables- Table borders, row colors, headers - 🚧
dashboards- Layout spacing, card styling - 🚧 All other component skills
How to integrate: See SKILL_CHAINING_ARCHITECTURE.md for complete integration architecture.
---
Testing
Visual Testing
# Open theme switcher demo
open examples/theme-switcher.htmlTest checklist:
- [ ] Light theme displays correctly
- [ ] Dark theme displays correctly
- [ ] High-contrast theme displays correctly
- [ ] Theme persists after reload
- [ ] No FOUC (flash of unstyled content)
- [ ] RTL mode works (
<html dir="rtl">)
Automated Testing
# Validate token structure
npm run validate
# Check color contrast (WCAG)
npm run validate:contrast
# Verify RTL compatibility
npm run validate:rtl---
Key Files
Documentation:
SKILL.md- Main skill file (progressive disclosure)SKILL_CHAINING_ARCHITECTURE.md- Component integration guideinit.md- Complete master planreferences/- Detailed documentation
Source Tokens (W3C format):
tokens/global/- Primitive tokens (colors, spacing, etc.)tokens/themes/- Theme overrides (light, dark, high-contrast)tokens/components/- Component-specific tokenstokens/languages/- Language-specific overrides
Build System:
config.js- Style Dictionary configurationpackage.json- Build scriptsbuild/- Generated output
Scripts:
scripts/generate_color_scale.py- Generate color palettesscripts/validate_tokens.py- W3C format validationscripts/validate_contrast.py- WCAG compliance checkerscripts/validate_logical_properties.py- RTL verification
Examples:
examples/ThemeProvider.tsx- React theme contextexamples/ThemeToggle.tsx- Theme switcher componentexamples/TokenUsageExample.tsx- Usage patternsexamples/theme-switcher.html- Vanilla JS demo
---
Architecture Principles
Separation of Concerns
Component Skills = Behavior + Structure (NO visual styling)
Design Tokens = Visual Styling Variables
Themes = Token Value OverridesResult: Components are infinitely customizable without code changes
Progressive Disclosure
SKILL.md (878 lines) → Quick start, overview, references
↓
references/ → Detailed documentation
↓
scripts/ → Executable utilities (token-free)Token-Free Scripts
Scripts execute without loading into context (zero token cost):
python scripts/generate_color_scale.py # 0 tokens
python scripts/validate_contrast.py # 0 tokens---
Resources
Specifications:
Tools:
- Style Dictionary - Token transformation
- Tokens Studio - Figma plugin (optional)
Testing:
- WebAIM Contrast Checker
- Sim Daltonism - Colorblind simulator
---
License
MIT - Part of ai-design-components
---
Contributing
See parent repository for contribution guidelines.
Skill development follows: ../skill_best_practice.md (Anthropic's official Skills guide)
---
Changelog
v1.0.0 (November 13, 2025)
- ✅ Complete W3C-compliant token system
- ✅ 7 token categories (color, spacing, typography, borders, shadows, motion, z-index)
- ✅ 3 built-in themes (light, dark, high-contrast)
- ✅ CSS logical properties for RTL support
- ✅ Multi-platform exports (CSS, SCSS, iOS, Android, JS)
- ✅ WCAG 2.1 AA compliant
- ✅ Skill chaining architecture
- ✅ Token generation and validation scripts
- ✅ Complete documentation and examples
---
Built following Anthropic's Skills best practices
Progressive disclosure | Token-efficient | W3C compliant | Production-ready
Accessibility Token Reference
WCAG 2.1 compliant design tokens for accessible interfaces
Table of Contents
- WCAG Contrast Requirements
- Color Contrast Validation
- Pre-Validated Color Pairs
- Validation Script
- High-Contrast Theme
- Reduced Motion
- Focus Indicators
- Color Blindness Considerations
- Colorblind-Safe Palettes
- Don't Rely on Color Alone
- Accessibility Checklist
- Token Design
- Implementation
- Testing Tools
- Resources
---
WCAG Contrast Requirements
WCAG 2.1 Level AA:
- Normal text (< 18px): 4.5:1 minimum
- Large text (≥ 18px or 14px bold): 3:1 minimum
- UI components: 3:1 minimum
WCAG 2.1 Level AAA:
- Normal text: 7:1 minimum
- Large text: 4.5:1 minimum
---
Color Contrast Validation
Pre-Validated Color Pairs
Light Theme (AA Compliant):
/* Text on backgrounds */
--color-text-primary: #111827; /* gray-900 */
--color-bg-primary: #FFFFFF;
/* Ratio: 17.7:1 ✅ (AAA) */
--color-text-secondary: #4B5563; /* gray-600 */
--color-bg-primary: #FFFFFF;
/* Ratio: 7.1:1 ✅ (AAA) */
--color-text-tertiary: #9CA3AF; /* gray-400 */
--color-bg-primary: #FFFFFF;
/* Ratio: 3.2:1 ✅ (AA large text only) */Dark Theme (AA Compliant):
--color-text-primary: #F9FAFB; /* gray-50 */
--color-bg-primary: #111827; /* gray-900 */
/* Ratio: 17.7:1 ✅ (AAA) */
--color-text-secondary: #D1D5DB; /* gray-300 */
--color-bg-primary: #111827;
/* Ratio: 9.8:1 ✅ (AAA) */Validation Script
# Check all color combinations
python scripts/validate_contrast.py---
High-Contrast Theme
For users with visual impairments:
:root[data-theme="high-contrast"] {
/* Pure colors */
--color-primary: #0000FF; /* Pure blue */
--color-text-primary: #000000; /* Pure black */
--color-background: #FFFFFF; /* Pure white */
--color-border: #000000; /* Black borders */
/* 7:1 minimum contrast (AAA) */
--color-text-secondary: #333333;
/* Stronger shadows */
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.5);
/* Thicker borders */
--border-width-thin: 2px;
}Auto-apply via system preference:
@media (prefers-contrast: high) {
:root {
/* Apply high-contrast token values */
}
}---
Reduced Motion
Honor user's motion preference:
@media (prefers-reduced-motion: reduce) {
:root {
/* Disable all animations */
--duration-instant: 0ms;
--duration-fast: 0ms;
--duration-normal: 0ms;
--duration-slow: 0ms;
/* Remove transitions */
--transition-fast: none;
--transition-normal: none;
--transition-slow: none;
/* Disable animations */
--ease-in: linear;
--ease-out: linear;
}
}JavaScript detection:
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReducedMotion) {
console.log('Reduced motion enabled');
}---
Focus Indicators
Visible focus states for keyboard navigation:
:root {
/* Focus ring tokens */
--shadow-focus-primary: 0 0 0 3px rgba(59, 130, 246, 0.3);
--shadow-focus-error: 0 0 0 3px rgba(239, 68, 68, 0.3);
--shadow-focus-success: 0 0 0 3px rgba(34, 197, 94, 0.3);
/* High-contrast focus (thicker, darker) */
--shadow-focus-high-contrast: 0 0 0 4px rgba(0, 0, 255, 0.6);
}
/* Apply to interactive elements */
.button:focus-visible {
outline: none;
box-shadow: var(--shadow-focus-primary);
}
.input:focus-visible {
outline: none;
box-shadow: var(--shadow-focus-primary);
}
/* High-contrast override */
:root[data-theme="high-contrast"] .button:focus-visible {
box-shadow: var(--shadow-focus-high-contrast);
}Never remove focus indicators:
/* ❌ WRONG - Removes keyboard navigation indicator */
button:focus {
outline: none;
}
/* ✅ CORRECT - Custom focus indicator */
button:focus-visible {
outline: none;
box-shadow: var(--shadow-focus-primary);
}---
Color Blindness Considerations
Colorblind-Safe Palettes
Built into chart tokens (tokens/components/chart.json):
/* IBM Colorblind-Safe Palette */
--chart-color-1: #648FFF; /* Blue */
--chart-color-2: #785EF0; /* Purple */
--chart-color-3: #DC267F; /* Magenta */
--chart-color-4: #FE6100; /* Orange */
--chart-color-5: #FFB000; /* Yellow */Avoid red/green combinations:
- 8% of males have red-green colorblindness
- Use blue/orange, purple/yellow instead
Don't Rely on Color Alone
/* ❌ Color only */
.status-success {
color: var(--color-success);
}
/* ✅ Color + icon/pattern */
.status-success::before {
content: '✓ ';
color: var(--color-success);
}---
Accessibility Checklist
Token Design
- [ ] Text/background combinations meet 4.5:1 (AA)
- [ ] UI components meet 3:1 contrast
- [ ] High-contrast theme provides 7:1 (AAA)
- [ ] Focus indicators are clearly visible
- [ ] Reduced motion preference honored
Implementation
- [ ] Use semantic color tokens (not primitives)
- [ ] Provide focus indicators on all interactive elements
- [ ] Don't rely on color alone (use icons/patterns)
- [ ] Test with colorblind simulators
- [ ] Test keyboard navigation
Testing Tools
- Chrome DevTools: Contrast ratio checker, color vision deficiency simulator
- Firefox: Accessibility inspector
- WAVE: Web accessibility evaluation tool
- axe DevTools: Automated accessibility testing
---
Resources
- WCAG 2.1: https://www.w3.org/WAI/WCAG21/quickref/
- Contrast Checker: https://webaim.org/resources/contrastchecker/
- Color Blind Simulator: Sim Daltonism (Mac), Color Oracle (cross-platform)
---
Validation script: scripts/validate_contrast.py
Color System
Complete color token reference for the design system.
Table of Contents
- Color Scale Structure
- Semantic Color Tokens
- Dark Theme Overrides
- Accessibility Requirements
- Contrast Ratios
- Validated Combinations
- Colorblind-Safe Considerations
Color Scale Structure
Each color has a 9-shade scale from lightest (50) to darkest (900):
/* Blue Scale */
--color-blue-50: #EFF6FF;
--color-blue-100: #DBEAFE;
--color-blue-200: #BFDBFE;
--color-blue-300: #93C5FD;
--color-blue-400: #60A5FA;
--color-blue-500: #3B82F6; /* Base */
--color-blue-600: #2563EB;
--color-blue-700: #1D4ED8;
--color-blue-800: #1E40AF;
--color-blue-900: #1E3A8A;
/* Gray Scale */
--color-gray-50: #F9FAFB;
--color-gray-100: #F3F4F6;
--color-gray-200: #E5E7EB;
--color-gray-300: #D1D5DB;
--color-gray-400: #9CA3AF;
--color-gray-500: #6B7280;
--color-gray-600: #4B5563;
--color-gray-700: #374151;
--color-gray-800: #1F2937;
--color-gray-900: #111827;
/* Additional Scales: red, green, yellow, purple, pink, indigo, teal, orange */Semantic Color Tokens
Map primitive colors to semantic meaning:
/* Brand Colors */
--color-primary: var(--color-blue-500);
--color-primary-light: var(--color-blue-400);
--color-primary-dark: var(--color-blue-600);
--color-secondary: var(--color-purple-500);
/* Feedback Colors */
--color-success: var(--color-green-500);
--color-success-bg: var(--color-green-50);
--color-success-border: var(--color-green-200);
--color-warning: var(--color-yellow-500);
--color-warning-bg: var(--color-yellow-50);
--color-warning-border: var(--color-yellow-200);
--color-error: var(--color-red-500);
--color-error-bg: var(--color-red-50);
--color-error-border: var(--color-red-200);
--color-info: var(--color-blue-500);
--color-info-bg: var(--color-blue-50);
--color-info-border: var(--color-blue-200);
/* Text Colors */
--color-text-primary: var(--color-gray-900);
--color-text-secondary: var(--color-gray-600);
--color-text-tertiary: var(--color-gray-400);
--color-text-inverse: var(--color-white);
/* Background Colors */
--color-bg-primary: var(--color-white);
--color-bg-secondary: var(--color-gray-50);
--color-bg-tertiary: var(--color-gray-100);
/* Border Colors */
--color-border-default: var(--color-gray-200);
--color-border-hover: var(--color-gray-300);
--color-border-focus: var(--color-primary);Dark Theme Overrides
:root[data-theme="dark"] {
/* Invert the scale direction */
--color-text-primary: var(--color-gray-50);
--color-text-secondary: var(--color-gray-300);
--color-bg-primary: var(--color-gray-900);
--color-bg-secondary: var(--color-gray-800);
--color-bg-tertiary: var(--color-gray-700);
--color-border-default: var(--color-gray-700);
--color-border-hover: var(--color-gray-600);
/* Adjust primary for better contrast on dark */
--color-primary: var(--color-blue-400);
/* Feedback colors stay similar but adjust backgrounds */
--color-success-bg: var(--color-green-900);
--color-error-bg: var(--color-red-900);
}Accessibility Requirements
Contrast Ratios
| Usage | Minimum Ratio | WCAG Level |
|---|---|---|
| Normal text | 4.5:1 | AA |
| Large text (18px+) | 3:1 | AA |
| UI components | 3:1 | AA |
| Enhanced (AAA) | 7:1 | AAA |
Validated Combinations
These combinations meet WCAG AA:
| Background | Text | Ratio |
|---|---|---|
| white | gray-900 | 15.1:1 |
| white | gray-600 | 5.7:1 |
| gray-900 | white | 15.1:1 |
| blue-500 | white | 4.5:1 |
Colorblind-Safe Considerations
Avoid relying solely on:
- Red vs Green (deuteranopia, protanopia)
- Blue vs Purple (tritanopia)
Always pair color with:
- Icons or patterns
- Text labels
- Shape differences
Component Integration Guide
How component skills consume design tokens for themeable styling
Table of Contents
- Quick Reference
- Integration Pattern
- Step 1: Define Component Tokens
- Step 2: Use Tokens in Component
- Step 3: Theme Switching Works Automatically
- Complete Examples
- Button Component (forms skill)
- Chart Component (data-viz skill)
- Integration Checklist
- ✅ Design Phase
- ✅ Implementation Phase
- ✅ Documentation Phase
- ✅ Testing Phase
- Common Patterns
- Pattern 1: Variants
- Pattern 2: States
- Pattern 3: Size Variants
- Complete Skill Documentation Template
- Anti-Patterns to Avoid
- ❌ Hardcoded Values
- ❌ Physical Properties
- ❌ Direct Primitive References
- Resources
This guide shows component skills how to properly use design tokens for all visual styling.
---
Quick Reference
Component Token Naming:
--{component}-{property}-{variant?}-{state?}Examples:
--button-bg-primary--button-bg-primary-hover--input-border-color-focus--chart-color-1
---
Integration Pattern
Step 1: Define Component Tokens
File: tokens/components/{component}.json
{
"$schema": "https://design-tokens.org/community-group/format/1.0.0",
"button": {
"bg": {
"primary": {
"$value": "{semantic.color.primary}",
"$type": "color"
},
"primary-hover": {
"$value": "{semantic.color.primary-hover}",
"$type": "color"
}
},
"padding": {
"inline": {
"$value": "{semantic.spacing.lg}",
"$type": "dimension"
}
}
}
}Step 2: Use Tokens in Component
React/TSX:
function Button({ variant = 'primary', children }) {
return (
<button
style={{
backgroundColor: `var(--button-bg-${variant})`,
color: `var(--button-text-${variant})`,
paddingInline: 'var(--button-padding-inline)',
borderRadius: 'var(--button-border-radius)',
}}
>
{children}
</button>
);
}CSS:
.button {
background-color: var(--button-bg-primary);
color: var(--button-text-primary);
padding-inline: var(--button-padding-inline);
padding-block: var(--button-padding-block);
border-radius: var(--button-border-radius);
transition: var(--transition-fast);
}
.button:hover {
background-color: var(--button-bg-primary-hover);
}Step 3: Theme Switching Works Automatically
No code changes needed - themes override token values:
setTheme('dark'); // All buttons become dark-themed
setTheme('brand'); // All buttons use brand colors---
Complete Examples
Button Component (forms skill)
Tokens (tokens/components/button.json):
{
"button": {
"bg": {
"primary": { "$value": "{semantic.color.primary}", "$type": "color" },
"primary-hover": { "$value": "{semantic.color.primary-hover}", "$type": "color" },
"secondary": { "$value": "{semantic.color.bg-secondary}", "$type": "color" },
"disabled": { "$value": "{semantic.color.disabled}", "$type": "color" }
},
"text": {
"primary": { "$value": "{semantic.color.text-inverse}", "$type": "color" },
"secondary": { "$value": "{semantic.color.text-primary}", "$type": "color" }
},
"padding": {
"inline": { "$value": "{semantic.spacing.lg}", "$type": "dimension" },
"block": { "$value": "{semantic.spacing.sm}", "$type": "dimension" }
},
"border-radius": { "$value": "{semantic.radius.md}", "$type": "dimension" }
}
}Implementation:
.button {
background-color: var(--button-bg-primary);
color: var(--button-text-primary);
padding-inline: var(--button-padding-inline);
padding-block: var(--button-padding-block);
border-radius: var(--button-border-radius);
font-size: var(--button-font-size);
font-weight: var(--button-font-weight);
border: none;
cursor: pointer;
transition: var(--transition-fast);
}
.button:hover {
background-color: var(--button-bg-primary-hover);
}
.button--secondary {
background-color: var(--button-bg-secondary);
color: var(--button-text-secondary);
}
.button:disabled {
background-color: var(--button-bg-disabled);
cursor: not-allowed;
}---
Chart Component (data-viz skill)
Tokens (tokens/components/chart.json):
{
"chart": {
"color": {
"1": { "$value": "#648FFF", "$type": "color" },
"2": { "$value": "#785EF0", "$type": "color" },
"3": { "$value": "#DC267F", "$type": "color" }
},
"axis": {
"color": { "$value": "{semantic.color.border}", "$type": "color" }
},
"grid": {
"color": { "$value": "{semantic.color.bg-tertiary}", "$type": "color" }
},
"tooltip": {
"bg": { "$value": "{semantic.color.bg-inverse}", "$type": "color" }
}
}
}Implementation (Recharts):
import { LineChart, Line, XAxis, YAxis } from 'recharts';
function SalesChart({ data }) {
return (
<LineChart data={data}>
<XAxis stroke="var(--chart-axis-color)" />
<YAxis stroke="var(--chart-axis-color)" />
<Line
type="monotone"
dataKey="sales"
stroke="var(--chart-color-1)"
strokeWidth={2}
/>
</LineChart>
);
}---
Integration Checklist
When creating a component skill:
✅ Design Phase
- [ ] List all visual styling properties
- [ ] Map to token categories (color, spacing, etc.)
- [ ] Define component-specific tokens
- [ ] Use logical property names (inline/block)
✅ Implementation Phase
- [ ] Use CSS custom properties (
var(--token-name)) - [ ] Use logical properties (
padding-inline, NOTpadding-left) - [ ] NO hardcoded values
- [ ] Reference component tokens (not primitives)
✅ Documentation Phase
- [ ] Add "Styling & Theming" section to SKILL.md
- [ ] List component tokens used
- [ ] Provide custom theming example
- [ ] Reference design-tokens skill
✅ Testing Phase
- [ ] Test light theme
- [ ] Test dark theme
- [ ] Test RTL mode (
<html dir="rtl">) - [ ] Verify theme switching works
---
Common Patterns
Pattern 1: Variants
/* Base button */
.button {
background-color: var(--button-bg-primary);
}
/* Variants via data attributes */
.button[data-variant="secondary"] {
background-color: var(--button-bg-secondary);
}
.button[data-variant="danger"] {
background-color: var(--button-bg-danger);
}Pattern 2: States
.input {
border-color: var(--input-border-color);
}
.input:hover {
border-color: var(--input-border-color-hover);
}
.input:focus {
border-color: var(--input-border-color-focus);
box-shadow: var(--input-shadow-focus);
}
.input[aria-invalid="true"] {
border-color: var(--input-border-color-error);
}Pattern 3: Size Variants
.button--sm {
height: var(--button-height-sm);
font-size: var(--button-font-size-sm);
}
.button--md {
height: var(--button-height-md);
font-size: var(--button-font-size-md);
}
.button--lg {
height: var(--button-height-lg);
font-size: var(--button-font-size-lg);
}---
Complete Skill Documentation Template
Add this to your component skill's SKILL.md:
## Styling & Theming
This component uses design tokens from the **design-tokens** skill for all visual styling.
### Component Tokens
See `design-tokens/tokens/components/{component}.json` for complete list.
**Primary Tokens:**
- `--{component}-bg-primary` - Primary background color
- `--{component}-text-primary` - Primary text color
- `--{component}-padding-inline` - Horizontal padding (RTL-aware)
- `--{component}-border-radius` - Corner radius
### Custom Theming
Override these tokens in your theme file:
\```css
:root[data-theme="custom"] {
--{component}-bg-primary: #FF6B35;
--{component}-border-radius: 20px;
}
\```
### Theme Support
- ✅ Light mode
- ✅ Dark mode
- ✅ High contrast
- ✅ Custom brand themes
- ✅ RTL languages
See `design-tokens/` skill for complete theming documentation.---
Anti-Patterns to Avoid
❌ Hardcoded Values
/* WRONG - No theming support */
.button {
background-color: #3B82F6; /* ❌ Hardcoded */
padding: 12px 24px; /* ❌ Hardcoded */
}❌ Physical Properties
/* WRONG - Won't flip in RTL */
.button {
padding-left: var(--button-padding); /* ❌ Physical property */
margin-right: 8px; /* ❌ Won't flip */
}❌ Direct Primitive References
/* WRONG - Skip semantic layer */
.button {
background-color: var(--color-blue-500); /* ❌ Use semantic tokens */
}Correct:
/* ✅ CORRECT */
.button {
background-color: var(--button-bg-primary); /* ✅ Component token */
padding-inline: var(--button-padding-inline); /* ✅ Logical property */
}---
Resources
- SKILL_CHAINING_ARCHITECTURE.md - Complete integration architecture
- tokens/components/ - Example component token files
- examples/ - Working code examples
- SKILL.md - Main design-tokens skill documentation
---
For complete skill chaining architecture, see: SKILL_CHAINING_ARCHITECTURE.md
CSS Logical Properties Reference
Complete guide to CSS logical properties for RTL/i18n support
Table of Contents
- Why Logical Properties?
- Core Concepts
- Complete Property Mapping
- Margin
- Padding
- Borders
- Positioning
- Text Alignment
- Sizing
- Token Examples
- ✅ CORRECT - Logical Properties
- ❌ WRONG - Physical Properties
- Browser Support
- Testing RTL
- Set Document Direction
- Visual Testing Checklist
- Browser DevTools
- Common Patterns
- Pattern 1: Card with Start Border
- Pattern 2: Icon Before Text
- Pattern 3: Dropdown Menu
- Resources
---
Why Logical Properties?
Traditional approach (broken in RTL):
.button {
margin-left: 16px; /* ❌ Always left, even in RTL */
padding-right: 24px; /* ❌ Always right, even in RTL */
}Modern approach (RTL-aware):
.button {
margin-inline-start: 16px; /* ✅ Left in LTR, right in RTL */
padding-inline-end: 24px; /* ✅ Right in LTR, left in RTL */
}---
Core Concepts
Inline Axis = Direction of text flow
- LTR (English): Horizontal (left → right)
- RTL (Arabic): Horizontal (right → left)
- Vertical (Japanese): Vertical (top → bottom)
Block Axis = Direction of block stacking
- Horizontal languages: Vertical (top → bottom)
Start = Beginning of flow (left in LTR, right in RTL) End = End of flow (right in LTR, left in RTL)
---
Complete Property Mapping
Margin
| Physical | Logical | Auto-Flips? |
|---|---|---|
margin-left | margin-inline-start | ✅ Yes |
margin-right | margin-inline-end | ✅ Yes |
margin-top | margin-block-start | ❌ No |
margin-bottom | margin-block-end | ❌ No |
Shorthands:
margin-inline: 16px; /* Both start and end */
margin-inline: 8px 16px; /* Start, end */
margin-block: 12px; /* Both start and end */Padding
| Physical | Logical | Auto-Flips? |
|---|---|---|
padding-left | padding-inline-start | ✅ Yes |
padding-right | padding-inline-end | ✅ Yes |
padding-top | padding-block-start | ❌ No |
padding-bottom | padding-block-end | ❌ No |
Shorthands:
padding-inline: 24px; /* Both start and end */
padding-block: 12px; /* Both start and end */Borders
| Physical | Logical | Auto-Flips? |
|---|---|---|
border-left | border-inline-start | ✅ Yes |
border-right | border-inline-end | ✅ Yes |
border-top | border-block-start | ❌ No |
border-bottom | border-block-end | ❌ No |
Properties:
border-inline-start: 1px solid #ccc;
border-inline-end-color: red;
border-inline-end-width: 2px;
border-inline-start-style: dashed;Positioning
| Physical | Logical | Auto-Flips? |
|---|---|---|
left | inset-inline-start | ✅ Yes |
right | inset-inline-end | ✅ Yes |
top | inset-block-start | ❌ No |
bottom | inset-block-end | ❌ No |
Usage:
.dropdown {
position: absolute;
inset-inline-start: 0; /* Left in LTR, right in RTL */
inset-block-start: 100%; /* Below element */
}Text Alignment
| Physical | Logical | Auto-Flips? |
|---|---|---|
text-align: left | text-align: start | ✅ Yes |
text-align: right | text-align: end | ✅ Yes |
text-align: center | text-align: center | ❌ No (same) |
Sizing
| Physical | Logical |
|---|---|
width | inline-size |
height | block-size |
min-width | min-inline-size |
max-width | max-inline-size |
Example:
.container {
inline-size: 100%; /* Width in horizontal, height in vertical */
max-inline-size: 1200px;
block-size: auto; /* Height in horizontal */
}---
Token Examples
✅ CORRECT - Logical Properties
:root {
/* Spacing - inline/block */
--button-padding-inline: 24px;
--button-padding-block: 12px;
--card-margin-inline-start: 16px;
--icon-margin-inline-end: 4px;
/* Borders - inline/block */
--alert-border-inline-start-width: 4px;
/* Positioning - inset */
--dropdown-inset-inline-start: 0;
--tooltip-inset-block-start: 100%;
}
/* Usage */
.button {
padding-inline: var(--button-padding-inline);
padding-block: var(--button-padding-block);
margin-inline-start: var(--card-margin-inline-start);
}❌ WRONG - Physical Properties
:root {
/* ❌ Don't use physical directions */
--button-padding-left: 24px;
--button-padding-right: 24px;
--card-margin-left: 16px;
--icon-margin-right: 4px;
}---
Browser Support
Excellent support (2025):
- Chrome/Edge: Since version 69 (2018)
- Firefox: Since version 41 (2015)
- Safari: Since version 12.1 (2019)
Coverage: >95% of global users
Fallback (if needed for very old browsers):
/* Fallback */
margin-left: 16px;
/* Modern (overrides) */
margin-inline-start: 16px;---
Testing RTL
Set Document Direction
<!-- LTR (default) -->
<html dir="ltr" lang="en">
<!-- RTL (Arabic, Hebrew) -->
<html dir="rtl" lang="ar">Visual Testing Checklist
When switching to RTL, verify:
- [ ] Text alignment flips (left → right)
- [ ] Padding/margins flip
- [ ] Borders flip (left border → right border)
- [ ] Icons flip position
- [ ] Navigation order reverses
- [ ] Forms align correctly
- [ ] Tooltips position correctly
Browser DevTools
Chrome: 1. Open DevTools 2. Elements panel 3. Edit <html> tag 4. Change dir="ltr" to dir="rtl"
Firefox: 1. Right-click page 2. Inspect Element 3. Edit HTML attribute
---
Common Patterns
Pattern 1: Card with Start Border
.card {
border-inline-start: 4px solid var(--color-primary);
padding-inline: var(--spacing-md);
padding-block: var(--spacing-sm);
}
/* LTR: Left border, padding-left/right */
/* RTL: Right border, padding-right/left */Pattern 2: Icon Before Text
.button-icon {
margin-inline-end: var(--spacing-xs);
}
/* LTR: Icon on left, margin-right */
/* RTL: Icon on right, margin-left */Pattern 3: Dropdown Menu
.dropdown {
position: absolute;
inset-inline-start: 0;
inset-block-start: 100%;
}
/* LTR: Aligns to left edge */
/* RTL: Aligns to right edge */---
Resources
- W3C Spec: https://www.w3.org/TR/css-logical-1/
- MDN Guide: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values
- Can I Use: https://caniuse.com/css-logical-props
---
Always use logical properties in design tokens for automatic RTL support!
Spacing System
Complete spacing token reference for the design system.
Table of Contents
- Base Scale
- Semantic Spacing
- Component Spacing
- Buttons
- Form Inputs
- Cards
- Layout
- Logical Properties
- Usage Guidelines
- Consistent Rhythm
- Touch Targets
- Visual Hierarchy
Base Scale
4px base unit with consistent multipliers:
/* Primitive Scale */
--space-0: 0;
--space-px: 1px;
--space-0-5: 0.125rem; /* 2px */
--space-1: 0.25rem; /* 4px */
--space-1-5: 0.375rem; /* 6px */
--space-2: 0.5rem; /* 8px */
--space-2-5: 0.625rem; /* 10px */
--space-3: 0.75rem; /* 12px */
--space-3-5: 0.875rem; /* 14px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-7: 1.75rem; /* 28px */
--space-8: 2rem; /* 32px */
--space-9: 2.25rem; /* 36px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-14: 3.5rem; /* 56px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
--space-24: 6rem; /* 96px */
--space-32: 8rem; /* 128px */Semantic Spacing
/* T-shirt sizing for general use */
--spacing-xs: var(--space-1); /* 4px */
--spacing-sm: var(--space-2); /* 8px */
--spacing-md: var(--space-4); /* 16px */
--spacing-lg: var(--space-6); /* 24px */
--spacing-xl: var(--space-8); /* 32px */
--spacing-2xl: var(--space-12); /* 48px */
--spacing-3xl: var(--space-16); /* 64px */Component Spacing
Buttons
--button-padding-xs: var(--space-1) var(--space-2);
--button-padding-sm: var(--space-1-5) var(--space-3);
--button-padding-md: var(--space-2) var(--space-4);
--button-padding-lg: var(--space-2-5) var(--space-5);
--button-padding-xl: var(--space-3) var(--space-6);
--button-gap: var(--space-2); /* Between icon and text */Form Inputs
--input-padding-x: var(--space-3);
--input-padding-y: var(--space-2);
--input-gap: var(--space-2); /* Between label and input */
--field-gap: var(--space-4); /* Between form fields */Cards
--card-padding: var(--space-4);
--card-padding-lg: var(--space-6);
--card-gap: var(--space-3); /* Between card elements */Layout
--container-padding-x: var(--space-4);
--container-padding-x-lg: var(--space-8);
--section-gap: var(--space-16); /* Between page sections */
--stack-gap: var(--space-4); /* Vertical stack of elements */
--inline-gap: var(--space-2); /* Horizontal inline elements */
--grid-gap: var(--space-4);
--grid-gap-lg: var(--space-6);Logical Properties
Use logical properties for RTL support:
/* Instead of padding-left/padding-right */
padding-inline: var(--spacing-md);
padding-inline-start: var(--spacing-sm);
padding-inline-end: var(--spacing-lg);
/* Instead of padding-top/padding-bottom */
padding-block: var(--spacing-md);
padding-block-start: var(--spacing-sm);
padding-block-end: var(--spacing-lg);
/* Instead of margin-left/margin-right */
margin-inline: var(--spacing-md);
margin-inline-start: var(--spacing-sm);
margin-inline-end: var(--spacing-lg);Usage Guidelines
Consistent Rhythm
- Use the scale consistently
- Don't use arbitrary values (e.g., 13px, 17px)
- Prefer semantic tokens over primitive values
Touch Targets
- Minimum 44x44px for touch interfaces
- 8px minimum spacing between targets
Visual Hierarchy
- Larger spacing = higher importance separation
- Group related items with smaller spacing
- Separate sections with larger spacing
Style Dictionary Setup Guide
Transform W3C design tokens to multiple platform formats
Table of Contents
- What is Style Dictionary?
- Installation
- Configuration
- Building Tokens
- Output Examples
- Input (W3C JSON)
- Output: CSS Variables
- Output: SCSS
- Output: JavaScript
- Output: iOS Swift
- Output: Android XML
- Token References
- Multi-Theme Build
- Package.json Scripts
- Usage in Projects
- CSS
- SCSS
- JavaScript/TypeScript
- Resources
---
What is Style Dictionary?
Style Dictionary transforms design tokens from JSON to platform-specific formats:
JSON Tokens → Style Dictionary → CSS Variables
→ SCSS Variables
→ iOS Swift
→ Android XML
→ JavaScriptIndustry standard - Used by Amazon, Adobe, Salesforce, IBM
---
Installation
npm install --save-dev style-dictionary---
Configuration
File: config.js
import StyleDictionary from 'style-dictionary';
export default {
// Source token files
source: [
'tokens/global/**/*.json',
'tokens/themes/light.json',
'tokens/components/**/*.json'
],
// Platform outputs
platforms: {
// CSS Custom Properties
css: {
transformGroup: 'css',
buildPath: 'build/css/',
files: [{
destination: 'variables.css',
format: 'css/variables',
options: {
outputReferences: true, // Use var() for references
showFileHeader: true
}
}]
},
// Dark theme (separate file)
'css-dark': {
transformGroup: 'css',
buildPath: 'build/css/',
source: [
'tokens/global/**/*.json',
'tokens/themes/dark.json',
'tokens/components/**/*.json'
],
files: [{
destination: 'variables-dark.css',
format: 'css/variables',
options: {
outputReferences: true,
selector: ':root[data-theme="dark"]'
}
}]
},
// SCSS Variables
scss: {
transformGroup: 'scss',
buildPath: 'build/scss/',
files: [{
destination: '_variables.scss',
format: 'scss/variables'
}]
},
// JavaScript/TypeScript
js: {
transformGroup: 'js',
buildPath: 'build/js/',
files: [{
destination: 'tokens.js',
format: 'javascript/es6'
}]
}
}
};---
Building Tokens
# Build all platforms
npm run build
# or
style-dictionary build --config config.js
# Watch mode (auto-rebuild on changes)
npm run build:watch
# or
style-dictionary build --config config.js --watch---
Output Examples
Input (W3C JSON)
{
"color": {
"primary": {
"$value": "#3B82F6",
"$type": "color"
}
},
"spacing": {
"md": {
"$value": "16px",
"$type": "dimension"
}
}
}Output: CSS Variables
/* build/css/variables.css */
:root {
--color-primary: #3B82F6;
--spacing-md: 16px;
}Output: SCSS
/* build/scss/_variables.scss */
$color-primary: #3B82F6;
$spacing-md: 16px;Output: JavaScript
// build/js/tokens.js
export const color = {
primary: '#3B82F6'
};
export const spacing = {
md: '16px'
};Output: iOS Swift
// build/ios/DesignTokens.swift
public class DesignTokens {
public static let colorPrimary = UIColor(hex: "#3B82F6")
public static let spacingMd = CGFloat(16)
}Output: Android XML
<!-- build/android/res/values/colors.xml -->
<resources>
<color name="color_primary">#3B82F6</color>
</resources>
<!-- build/android/res/values/dimens.xml -->
<resources>
<dimen name="spacing_md">16dp</dimen>
</resources>---
Token References
W3C format supports references:
{
"color": {
"blue": {
"500": {
"$value": "#3B82F6",
"$type": "color"
}
},
"primary": {
"$value": "{color.blue.500}",
"$type": "color"
}
}
}CSS output with `outputReferences: true`:
:root {
--color-blue-500: #3B82F6;
--color-primary: var(--color-blue-500); /* ✅ Reference preserved */
}Why this matters:
- Changing
--color-blue-500updates--color-primaryautomatically - Enables dynamic theming
---
Multi-Theme Build
Build separate CSS files for each theme:
// config.js
export default {
platforms: {
'css-light': {
transformGroup: 'css',
buildPath: 'build/css/',
source: ['tokens/global/**/*.json', 'tokens/themes/light.json'],
files: [{
destination: 'variables-light.css',
format: 'css/variables',
options: { selector: ':root' }
}]
},
'css-dark': {
transformGroup: 'css',
buildPath: 'build/css/',
source: ['tokens/global/**/*.json', 'tokens/themes/dark.json'],
files: [{
destination: 'variables-dark.css',
format: 'css/variables',
options: { selector: ':root[data-theme="dark"]' }
}]
}
}
};---
Package.json Scripts
{
"scripts": {
"build": "style-dictionary build --config config.js",
"build:watch": "style-dictionary build --config config.js --watch",
"clean": "rm -rf build/",
"validate": "python scripts/validate_tokens.py",
"validate:contrast": "python scripts/validate_contrast.py"
},
"devDependencies": {
"style-dictionary": "^4.0.0"
}
}---
Usage in Projects
CSS
<!-- Load generated CSS -->
<link rel="stylesheet" href="build/css/variables.css">
<link rel="stylesheet" href="build/css/variables-dark.css">/* Use in your styles */
.button {
background-color: var(--button-bg-primary);
padding: var(--spacing-md);
}SCSS
// Import generated variables
@import 'build/scss/variables';
.button {
background-color: $button-bg-primary;
padding: $spacing-md;
}JavaScript/TypeScript
import { color, spacing } from './build/js/tokens';
const buttonStyles = {
backgroundColor: color.primary,
padding: spacing.md
};---
Resources
- Style Dictionary Docs: https://styledictionary.com
- GitHub: https://github.com/amzn/style-dictionary
- Context7:
/amzn/style-dictionary
---
Configuration file: config.js in skill root
Theme Switching Guide
Complete guide to implementing light/dark mode and custom brand themes
Table of Contents
- How Theme Switching Works
- 1. Define Themes with Token Overrides
- 2. Set Theme Attribute
- 3. All Components Update Automatically
- Implementation Patterns
- Vanilla JavaScript
- React Theme Provider
- Creating Custom Themes
- Brand Theme Example
- System Preference Detection
- Respect User's OS Preference
- Multi-Theme Selector
- Theme Persistence
- LocalStorage (Basic)
- Cookie (Server-Side Rendering)
- Avoiding Flash of Unstyled Content (FOUC)
- Problem
- Solution 1: Inline Script (Fastest)
- Solution 2: Server-Side Rendering
- Theme Transition Animation
- Testing Themes
- Manual Testing Checklist
- Automated Testing
---
How Theme Switching Works
1. Define Themes with Token Overrides
Light theme (base, default):
:root {
--color-primary: #3B82F6;
--color-background: #FFFFFF;
--color-text-primary: #1F2937;
}Dark theme (overrides):
:root[data-theme="dark"] {
--color-primary: #60A5FA;
--color-background: #111827;
--color-text-primary: #F9FAFB;
}2. Set Theme Attribute
document.documentElement.setAttribute('data-theme', 'dark');3. All Components Update Automatically
No code changes needed - CSS variables cascade:
.button {
background-color: var(--button-bg-primary);
/* Gets #3B82F6 in light, #60A5FA in dark */
}---
Implementation Patterns
Vanilla JavaScript
// theme-switcher.js
function setTheme(themeName) {
// Set attribute on <html>
document.documentElement.setAttribute('data-theme', themeName);
// Save preference
localStorage.setItem('theme', themeName);
// Update UI (optional)
updateThemeIcon(themeName);
}
function getTheme() {
// Check saved preference
const saved = localStorage.getItem('theme');
if (saved) return saved;
// Check system preference
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
return 'light'; // Default
}
function toggleTheme() {
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
setTheme(next);
}
// Initialize on page load
window.addEventListener('DOMContentLoaded', () => {
const theme = getTheme();
setTheme(theme);
});
// Listen to system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
setTheme(e.matches ? 'dark' : 'light');
}
});---
React Theme Provider
// ThemeContext.tsx
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
type Theme = 'light' | 'dark' | 'high-contrast' | string;
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType>({
theme: 'light',
setTheme: () => {},
toggleTheme: () => {},
});
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setThemeState] = useState<Theme>('light');
// Load saved theme on mount
useEffect(() => {
const saved = localStorage.getItem('theme');
if (saved) {
setThemeState(saved);
} else {
// Use system preference
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
setThemeState(prefersDark ? 'dark' : 'light');
}
}, []);
// Apply theme when it changes
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme]);
const setTheme = (newTheme: Theme) => {
setThemeState(newTheme);
};
const toggleTheme = () => {
setThemeState(prev => prev === 'dark' ? 'light' : 'dark');
};
return (
<ThemeContext.Provider value={{ theme, setTheme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export const useTheme = () => useContext(ThemeContext);Usage:
// App.tsx
import { ThemeProvider } from './ThemeContext';
function App() {
return (
<ThemeProvider>
<YourApp />
</ThemeProvider>
);
}
// ThemeToggle.tsx
import { useTheme } from './ThemeContext';
function ThemeToggle() {
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
{theme === 'light' ? '🌙 Dark Mode' : '☀️ Light Mode'}
</button>
);
}---
Creating Custom Themes
Brand Theme Example
/* themes/acme-brand.css */
:root[data-theme="acme"] {
/* Brand colors */
--color-primary: #FF6B35; /* Acme orange */
--color-secondary: #004E89; /* Acme blue */
--color-accent: #FFA62B;
/* Brand typography */
--font-sans: 'Poppins', sans-serif;
--font-weight-bold: 700;
/* Brand spacing (more generous) */
--spacing-md: 20px;
/* Brand borders (more rounded) */
--radius-md: 12px;
/* Brand shadows (softer) */
--shadow-md: 0 6px 12px rgba(0, 0, 0, 0.08);
}Apply theme:
setTheme('acme');---
System Preference Detection
Respect User's OS Preference
/* Automatically apply dark theme if user prefers */
@media (prefers-color-scheme: dark) {
:root {
/* Dark theme tokens */
}
}JavaScript with manual override:
function getInitialTheme() {
// 1. Check manual preference
const saved = localStorage.getItem('theme');
if (saved) return saved;
// 2. Check system preference
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
// 3. Default
return 'light';
}Listen to system changes:
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
// Only auto-switch if user hasn't set manual preference
if (!localStorage.getItem('theme')) {
setTheme(e.matches ? 'dark' : 'light');
}
});---
Multi-Theme Selector
Allow users to choose from multiple themes:
function ThemeSelector() {
const { theme, setTheme } = useTheme();
const themes = [
{ value: 'light', label: '☀️ Light', icon: '☀️' },
{ value: 'dark', label: '🌙 Dark', icon: '🌙' },
{ value: 'high-contrast', label: '⚡ High Contrast', icon: '⚡' },
{ value: 'acme', label: '🎨 Acme Brand', icon: '🎨' }
];
return (
<select
value={theme}
onChange={(e) => setTheme(e.target.value)}
style={{
padding: 'var(--spacing-sm)',
borderRadius: 'var(--radius-md)',
border: '1px solid var(--color-border)'
}}
>
{themes.map(t => (
<option key={t.value} value={t.value}>
{t.label}
</option>
))}
</select>
);
}---
Theme Persistence
LocalStorage (Basic)
// Save
localStorage.setItem('theme', 'dark');
// Load
const theme = localStorage.getItem('theme') || 'light';
// Clear
localStorage.removeItem('theme');Cookie (Server-Side Rendering)
// Set cookie
document.cookie = `theme=dark; path=/; max-age=31536000`; // 1 year
// Read cookie
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
const theme = getCookie('theme') || 'light';---
Avoiding Flash of Unstyled Content (FOUC)
Problem
Page loads with default theme, then JavaScript switches to saved theme = visual flash
Solution 1: Inline Script (Fastest)
<!DOCTYPE html>
<html>
<head>
<script>
// Execute BEFORE any CSS loads
(function() {
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
})();
</script>
<link rel="stylesheet" href="styles.css">
</head>
<body>...</body>
</html>Solution 2: Server-Side Rendering
// Next.js _document.js
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html>
<Head />
<body>
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
})();
`
}}
/>
<Main />
<NextScript />
</body>
</Html>
);
}---
Theme Transition Animation
Smooth theme switches:
:root {
/* Transition all color changes */
--theme-transition: background-color 200ms ease-in-out,
color 200ms ease-in-out,
border-color 200ms ease-in-out;
}
* {
transition: var(--theme-transition);
}
/* Disable during theme switch to prevent animation */
.theme-transitioning * {
transition: none !important;
}JavaScript:
function setTheme(themeName) {
// Add class to prevent animations
document.documentElement.classList.add('theme-transitioning');
// Set theme
document.documentElement.setAttribute('data-theme', themeName);
// Remove class after a frame
requestAnimationFrame(() => {
requestAnimationFrame(() => {
document.documentElement.classList.remove('theme-transitioning');
});
});
localStorage.setItem('theme', themeName);
}---
Testing Themes
Manual Testing Checklist
- [ ] Light theme displays correctly
- [ ] Dark theme displays correctly
- [ ] High-contrast theme meets WCAG AAA
- [ ] Custom brand themes apply correctly
- [ ] Theme persists after page reload
- [ ] System preference detection works
- [ ] No FOUC (flash of unstyled content)
- [ ] Smooth transitions between themes
- [ ] All components update correctly
Automated Testing
// theme.test.js
describe('Theme switching', () => {
it('applies light theme by default', () => {
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
});
it('switches to dark theme', () => {
setTheme('dark');
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
});
it('persists theme preference', () => {
setTheme('dark');
expect(localStorage.getItem('theme')).toBe('dark');
});
});---
Complete theme switching implementation in: examples/theme-switcher.tsx
Typography System
Complete typography token reference for the design system.
Table of Contents
- Font Families
- Type Scale
- Font Weights
- Line Heights
- Letter Spacing
- Semantic Typography Tokens
- Headings
- Body Text
- UI Text
- Accessibility Guidelines
- Minimum Sizes
- Readability
Font Families
/* Primary font stack */
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, sans-serif;
/* Monospace for code */
--font-mono: 'Fira Code', 'JetBrains Mono', 'SF Mono', Monaco,
'Cascadia Code', Consolas, monospace;
/* Serif for editorial/marketing (optional) */
--font-serif: 'Georgia', 'Times New Roman', serif;Type Scale
Based on 1.25 ratio (Major Third) with 16px base:
/* Size Scale */
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px - Base */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */
--font-size-5xl: 3rem; /* 48px */
--font-size-6xl: 3.75rem; /* 60px */Font Weights
--font-weight-thin: 100;
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-extrabold: 800;
--font-weight-black: 900;Line Heights
--line-height-none: 1;
--line-height-tight: 1.25;
--line-height-snug: 1.375;
--line-height-normal: 1.5; /* Default for body text */
--line-height-relaxed: 1.625;
--line-height-loose: 2;Letter Spacing
--letter-spacing-tighter: -0.05em;
--letter-spacing-tight: -0.025em;
--letter-spacing-normal: 0;
--letter-spacing-wide: 0.025em;
--letter-spacing-wider: 0.05em;
--letter-spacing-widest: 0.1em;Semantic Typography Tokens
Headings
--heading-1-size: var(--font-size-4xl);
--heading-1-weight: var(--font-weight-bold);
--heading-1-line-height: var(--line-height-tight);
--heading-1-letter-spacing: var(--letter-spacing-tight);
--heading-2-size: var(--font-size-3xl);
--heading-2-weight: var(--font-weight-semibold);
--heading-2-line-height: var(--line-height-tight);
--heading-3-size: var(--font-size-2xl);
--heading-3-weight: var(--font-weight-semibold);
--heading-3-line-height: var(--line-height-snug);
--heading-4-size: var(--font-size-xl);
--heading-4-weight: var(--font-weight-medium);
--heading-4-line-height: var(--line-height-snug);Body Text
--body-size: var(--font-size-base);
--body-weight: var(--font-weight-normal);
--body-line-height: var(--line-height-normal);
--body-sm-size: var(--font-size-sm);
--body-lg-size: var(--font-size-lg);UI Text
--label-size: var(--font-size-sm);
--label-weight: var(--font-weight-medium);
--caption-size: var(--font-size-xs);
--caption-weight: var(--font-weight-normal);
--button-size: var(--font-size-sm);
--button-weight: var(--font-weight-medium);Accessibility Guidelines
Minimum Sizes
- Body text: 16px minimum (14px for secondary)
- Interactive elements: 14px minimum
- Captions/labels: 12px minimum
Readability
- Line length: 45-75 characters optimal
- Paragraph spacing: 1.5x line height
- Use relative units (rem) not pixels for scaling
#!/usr/bin/env python3
"""
Generate a 9-shade color scale from a base color.
Usage:
python generate_color_scale.py --base "#3B82F6" --name "blue"
Outputs W3C format JSON for use in tokens/global/colors.json
"""
import argparse
import json
import colorsys
def hex_to_hsl(hex_color):
"""Convert hex color to HSL."""
hex_color = hex_color.lstrip('#')
r, g, b = tuple(int(hex_color[i:i+2], 16) / 255.0 for i in (0, 2, 4))
h, l, s = colorsys.rgb_to_hls(r, g, b)
return h, s, l
def hsl_to_hex(h, s, l):
"""Convert HSL to hex color."""
r, g, b = colorsys.hls_to_rgb(h, l, s)
return '#{:02X}{:02X}{:02X}'.format(
int(r * 255),
int(g * 255),
int(b * 255)
)
def generate_scale(base_hex, name):
"""
Generate 9-shade color scale.
Shades:
- 50: Lightest (backgrounds)
- 100-400: Light shades
- 500: Base color (provided)
- 600-900: Dark shades
"""
h, s, l = hex_to_hsl(base_hex)
# Lightness adjustments for each shade
# 50 is lightest, 500 is base, 900 is darkest
lightness_map = {
50: 0.96,
100: 0.92,
200: 0.84,
300: 0.72,
400: 0.60,
500: l, # Base lightness
600: l - 0.10,
700: l - 0.20,
800: l - 0.28,
900: l - 0.35
}
scale = {}
for shade, target_lightness in lightness_map.items():
# Clamp lightness between 0 and 1
clamped_lightness = max(0, min(1, target_lightness))
hex_color = hsl_to_hex(h, s, clamped_lightness)
scale[str(shade)] = {
"$value": hex_color,
"$type": "color"
}
# Add description for key shades
if shade == 50:
scale[str(shade)]["$description"] = f"Lightest {name} - backgrounds"
elif shade == 500:
scale[str(shade)]["$description"] = f"Base {name}"
elif shade == 900:
scale[str(shade)]["$description"] = f"Darkest {name}"
return scale
def main():
parser = argparse.ArgumentParser(
description='Generate a 9-shade color scale from a base color'
)
parser.add_argument(
'--base',
required=True,
help='Base color in hex format (e.g., #3B82F6)'
)
parser.add_argument(
'--name',
required=True,
help='Color name (e.g., blue, red, brand-primary)'
)
parser.add_argument(
'--output',
help='Output JSON file path (optional, defaults to stdout)'
)
args = parser.parse_args()
# Generate scale
scale = generate_scale(args.base, args.name)
# Wrap in W3C format
output = {
"$schema": "https://design-tokens.org/community-group/format/1.0.0",
"color": {
args.name: scale
}
}
# Output
json_str = json.dumps(output, indent=2)
if args.output:
with open(args.output, 'w') as f:
f.write(json_str)
print(f"✅ Color scale '{args.name}' generated at {args.output}")
else:
print(json_str)
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""
Validate color contrast ratios for WCAG compliance.
Checks:
- Text/background combinations meet WCAG 2.1 AA (4.5:1 normal text, 3:1 large text)
- UI component contrast meets 3:1 minimum
- Warns about AAA failures (7:1)
"""
import json
import sys
from pathlib import Path
def hex_to_rgb(hex_color):
"""Convert hex color to RGB tuple."""
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
def relative_luminance(rgb):
"""Calculate relative luminance (WCAG formula)."""
r, g, b = [x / 255.0 for x in rgb]
# Convert to sRGB
def to_srgb(c):
if c <= 0.03928:
return c / 12.92
else:
return ((c + 0.055) / 1.055) ** 2.4
r_srgb, g_srgb, b_srgb = to_srgb(r), to_srgb(g), to_srgb(b)
# Calculate luminance
return 0.2126 * r_srgb + 0.7152 * g_srgb + 0.0722 * b_srgb
def contrast_ratio(color1, color2):
"""Calculate contrast ratio between two colors."""
l1 = relative_luminance(hex_to_rgb(color1))
l2 = relative_luminance(hex_to_rgb(color2))
lighter = max(l1, l2)
darker = min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
def load_theme_colors(tokens_dir):
"""Load color tokens from light theme."""
theme_file = tokens_dir / 'themes' / 'light.json'
if not theme_file.exists():
print(f"❌ Theme file not found: {theme_file}")
return None
with open(theme_file, 'r') as f:
theme = json.load(f)
return theme.get('semantic', {}).get('color', {})
def validate_wcag_compliance():
"""Validate WCAG color contrast compliance."""
print("🔍 Validating color contrast (WCAG 2.1)...")
script_dir = Path(__file__).parent
tokens_dir = script_dir.parent / 'tokens'
colors = load_theme_colors(tokens_dir)
if not colors:
sys.exit(1)
errors = []
warnings = []
# Define text/background pairs to check
checks = [
# (text_token, bg_token, min_ratio, description)
('text-primary', 'bg-primary', 4.5, 'Primary text on primary bg'),
('text-secondary', 'bg-primary', 4.5, 'Secondary text on primary bg'),
('text-primary', 'bg-secondary', 4.5, 'Primary text on secondary bg'),
('text-inverse', 'bg-inverse', 4.5, 'Inverse text on inverse bg'),
('primary', 'bg-primary', 3.0, 'Primary color contrast (UI component)'),
('error', 'bg-primary', 3.0, 'Error color contrast'),
('success', 'bg-primary', 3.0, 'Success color contrast'),
('warning', 'bg-primary', 3.0, 'Warning color contrast'),
]
print("\n" + "=" * 70)
print("WCAG CONTRAST VALIDATION")
print("=" * 70)
for text_key, bg_key, min_ratio, description in checks:
text_token = colors.get(text_key, {})
bg_token = colors.get(bg_key, {})
if not text_token or not bg_token:
warnings.append(f"⚠️ Missing token: {text_key} or {bg_key}")
continue
# Extract color values (handle references)
text_color = text_token.get('$value', '')
bg_color = bg_token.get('$value', '')
# Skip if color is a reference (starts with {)
if text_color.startswith('{') or bg_color.startswith('{'):
warnings.append(f"⚠️ Skipping reference: {description}")
continue
# Skip non-hex colors
if not text_color.startswith('#') or not bg_color.startswith('#'):
continue
try:
ratio = contrast_ratio(text_color, bg_color)
# Check against minimum ratio
status = "✅" if ratio >= min_ratio else "❌"
aaa_status = "AAA ✨" if ratio >= 7.0 else "AA" if ratio >= 4.5 else "FAIL"
print(f"{status} {description}")
print(f" {text_key} ({text_color}) / {bg_key} ({bg_color})")
print(f" Ratio: {ratio:.2f}:1 ({aaa_status})")
if ratio < min_ratio:
errors.append(
f"❌ {description}: {ratio:.2f}:1 (required: {min_ratio}:1)"
)
elif ratio < 7.0 and min_ratio >= 4.5:
warnings.append(
f"⚠️ {description}: {ratio:.2f}:1 (AAA requires 7:1)"
)
except Exception as e:
warnings.append(f"⚠️ Error checking {description}: {e}")
# Report
print("\n" + "=" * 70)
print("SUMMARY")
print("=" * 70)
if errors:
print(f"\n❌ ERRORS ({len(errors)}):")
for error in errors:
print(f" {error}")
if warnings:
print(f"\n⚠️ WARNINGS ({len(warnings)}):")
for warning in warnings:
print(f" {warning}")
if not errors:
print("\n✅ All checked combinations meet WCAG 2.1 AA!")
print(f"\n📊 Checked {len(checks)} color combinations")
print("=" * 70)
sys.exit(0 if not errors else 1)
if __name__ == '__main__':
validate_wcag_compliance()
#!/usr/bin/env python3
"""
Validate that tokens use CSS logical properties for RTL support.
Checks:
- Token names use 'inline' instead of 'left/right'
- Token names use 'block' instead of 'top/bottom'
- No physical directional properties in token names
"""
import json
import sys
from pathlib import Path
def validate_logical_properties():
"""Validate CSS logical property usage in token names."""
print("🔍 Validating CSS logical properties (RTL support)...")
script_dir = Path(__file__).parent
tokens_dir = script_dir.parent / 'tokens'
errors = []
warnings = []
# Physical properties to avoid
physical_terms = {
'left', 'right', 'top', 'bottom',
'horizontal', 'vertical'
}
# Logical properties (acceptable)
logical_terms = {
'inline', 'block', 'inline-start', 'inline-end',
'block-start', 'block-end', 'inset'
}
def check_token_names(node, path=[]):
"""Recursively check token names."""
if not isinstance(node, dict):
return
for key, value in node.items():
if key.startswith('$'): # Skip metadata
continue
current_path = path + [key]
key_lower = key.lower()
# Check for physical properties
for physical in physical_terms:
if physical in key_lower:
# Exception: Allow 'inset-inline-start' even though it contains 'start'
if any(logical in key_lower for logical in logical_terms):
continue
errors.append(
f"❌ Token uses physical property '{physical}': {'.'.join(current_path)}"
)
break
# Recurse
check_token_names(value, current_path)
# Check all token files
token_files_checked = 0
for json_file in tokens_dir.rglob('*.json'):
try:
with open(json_file, 'r') as f:
tokens = json.load(f)
check_token_names(tokens)
token_files_checked += 1
except json.JSONDecodeError as e:
warnings.append(f"⚠️ Invalid JSON in {json_file}: {e}")
# Report
print("\n" + "=" * 70)
print("RTL VALIDATION REPORT")
print("=" * 70)
if errors:
print(f"\n❌ ERRORS ({len(errors)}):")
for error in errors:
print(f" {error}")
print("\n💡 Tip: Use logical properties for RTL support:")
print(" ✅ Use: inline, block, inline-start, inline-end")
print(" ❌ Avoid: left, right, top, bottom")
if warnings:
print(f"\n⚠️ WARNINGS ({len(warnings)}):")
for warning in warnings:
print(f" {warning}")
if not errors and not warnings:
print("\n✅ All tokens use CSS logical properties! RTL-ready.")
print(f"\n📊 Checked {token_files_checked} token files")
print("=" * 70)
sys.exit(0 if not errors else 1)
if __name__ == '__main__':
validate_logical_properties()
{
"$schema": "https://www.designtokens.org/tr/2025.10/",
"$description": "Button component tokens - for forms skill",
"button": {
"bg": {
"primary": {
"$value": "{semantic.color.primary}",
"$type": "color",
"$description": "Primary button background"
},
"primary-hover": {
"$value": "{semantic.color.primary-hover}",
"$type": "color"
},
"primary-active": {
"$value": "{semantic.color.primary-active}",
"$type": "color"
},
"secondary": {
"$value": "{semantic.color.bg-secondary}",
"$type": "color"
},
"secondary-hover": {
"$value": "{semantic.color.bg-tertiary}",
"$type": "color"
},
"tertiary": {
"$value": "transparent",
"$type": "color"
},
"disabled": {
"$value": "{semantic.color.disabled}",
"$type": "color"
},
"danger": {
"$value": "{semantic.color.error}",
"$type": "color"
},
"danger-hover": {
"$value": "{color.red.600}",
"$type": "color"
}
},
"text": {
"primary": {
"$value": "{semantic.color.text-inverse}",
"$type": "color"
},
"secondary": {
"$value": "{semantic.color.text-primary}",
"$type": "color"
},
"tertiary": {
"$value": "{semantic.color.primary}",
"$type": "color"
},
"disabled": {
"$value": "{semantic.color.text-disabled}",
"$type": "color"
}
},
"padding": {
"inline": {
"$value": "{semantic.spacing.lg}",
"$type": "dimension",
"$description": "Horizontal padding - uses logical property"
},
"block": {
"$value": "{semantic.spacing.sm}",
"$type": "dimension",
"$description": "Vertical padding - uses logical property"
}
},
"border": {
"radius": {
"$value": "{semantic.radius.md}",
"$type": "dimension"
},
"width": {
"$value": "{border.width.thin}",
"$type": "dimension"
},
"color-secondary": {
"$value": "{semantic.color.border}",
"$type": "color"
}
},
"font": {
"size": {
"$value": "{font.size.base}",
"$type": "fontSize"
},
"weight": {
"$value": "{font.weight.medium}",
"$type": "fontWeight"
}
},
"height": {
"sm": {
"$value": "2rem",
"$type": "dimension",
"$description": "32px - small button"
},
"md": {
"$value": "2.5rem",
"$type": "dimension",
"$description": "40px - medium button (default)"
},
"lg": {
"$value": "3rem",
"$type": "dimension",
"$description": "48px - large button"
}
}
}
}
Related skills
FAQ
What token categories does it define?
Seven core categories: color, typography, spacing, borders, shadows, motion and z-index.
How does theme switching work?
Components reference CSS custom properties and themes override those variables via a data-theme attribute, so switching needs no component code changes.