Add sub-skills from ui-ux-pro-max-skill repo

Added:
- banner-design (new)
- brand (new)
- design-system (new)
- slides (new)
- ui-ux-pro-max data/scripts (from GitHub clone)
- ui-styling data/scripts (from GitHub clone)
This commit is contained in:
2026-04-16 18:33:32 +07:00
parent b26c8199a5
commit da5f964d9a
87 changed files with 18254 additions and 2 deletions

View File

@@ -0,0 +1,236 @@
# Component Specifications
Detailed specs for core components with states and variants.
## Button
### Variants
| Variant | Background | Text | Border | Use Case |
|---------|------------|------|--------|----------|
| default | primary | white | none | Primary actions |
| secondary | gray-100 | gray-900 | none | Secondary actions |
| outline | transparent | foreground | border | Tertiary actions |
| ghost | transparent | foreground | none | Subtle actions |
| link | transparent | primary | none | Navigation |
| destructive | red-600 | white | none | Dangerous actions |
### Sizes
| Size | Height | Padding X | Padding Y | Font Size | Icon Size |
|------|--------|-----------|-----------|-----------|-----------|
| sm | 32px | 12px | 6px | 14px | 16px |
| default | 40px | 16px | 8px | 14px | 18px |
| lg | 48px | 24px | 12px | 16px | 20px |
| icon | 40px | 0 | 0 | - | 18px |
### States
| State | Background | Text | Opacity | Cursor |
|-------|------------|------|---------|--------|
| default | token | token | 1 | pointer |
| hover | darker | token | 1 | pointer |
| active | darkest | token | 1 | pointer |
| focus | token | token | 1 | pointer |
| disabled | muted | muted-fg | 0.5 | not-allowed |
| loading | token | token | 0.7 | wait |
### Anatomy
```
┌─────────────────────────────────────┐
│ [icon] Label Text [icon] │
└─────────────────────────────────────┘
↑ ↑
leading icon trailing icon
```
---
## Input
### Variants
| Variant | Description |
|---------|-------------|
| default | Standard text input |
| textarea | Multi-line text |
| select | Dropdown selection |
| checkbox | Boolean toggle |
| radio | Single selection |
| switch | Toggle switch |
### Sizes
| Size | Height | Padding | Font Size |
|------|--------|---------|-----------|
| sm | 32px | 8px 12px | 14px |
| default | 40px | 8px 12px | 14px |
| lg | 48px | 12px 16px | 16px |
### States
| State | Border | Background | Ring |
|-------|--------|------------|------|
| default | gray-300 | white | none |
| hover | gray-400 | white | none |
| focus | primary | white | primary/20% |
| error | red-500 | white | red/20% |
| disabled | gray-200 | gray-100 | none |
### Anatomy
```
Label (optional)
┌─────────────────────────────────────┐
│ [icon] Placeholder/Value [action] │
└─────────────────────────────────────┘
Helper text or error message
```
---
## Card
### Variants
| Variant | Shadow | Border | Use Case |
|---------|--------|--------|----------|
| default | sm | 1px | Standard card |
| elevated | lg | none | Prominent content |
| outline | none | 1px | Subtle container |
| interactive | sm→md | 1px | Clickable card |
### Anatomy
```
┌─────────────────────────────────────┐
│ Card Header │
│ Title │
│ Description │
├─────────────────────────────────────┤
│ Card Content │
│ Main content area │
│ │
├─────────────────────────────────────┤
│ Card Footer │
│ Actions │
└─────────────────────────────────────┘
```
### Spacing
| Area | Padding |
|------|---------|
| header | 24px 24px 0 |
| content | 24px |
| footer | 0 24px 24px |
| gap | 16px |
---
## Badge
### Variants
| Variant | Background | Text |
|---------|------------|------|
| default | primary | white |
| secondary | gray-100 | gray-900 |
| outline | transparent | foreground |
| destructive | red-600 | white |
| success | green-600 | white |
| warning | yellow-500 | gray-900 |
### Sizes
| Size | Padding | Font Size | Height |
|------|---------|-----------|--------|
| sm | 4px 8px | 11px | 20px |
| default | 4px 10px | 12px | 24px |
| lg | 6px 12px | 14px | 28px |
---
## Alert
### Variants
| Variant | Icon | Background | Border |
|---------|------|------------|--------|
| default | info | gray-50 | gray-200 |
| destructive | alert | red-50 | red-200 |
| success | check | green-50 | green-200 |
| warning | warning | yellow-50 | yellow-200 |
### Anatomy
```
┌─────────────────────────────────────┐
│ [icon] Title [×]│
│ Description text │
└─────────────────────────────────────┘
```
---
## Dialog
### Sizes
| Size | Max Width | Use Case |
|------|-----------|----------|
| sm | 384px | Simple confirmations |
| default | 512px | Standard dialogs |
| lg | 640px | Complex forms |
| xl | 768px | Data-heavy dialogs |
| full | 100% - 32px | Full-screen on mobile |
### Anatomy
```
┌───────────────────────────────────────┐
│ Dialog Header [×]│
│ Title │
│ Description │
├───────────────────────────────────────┤
│ Dialog Content │
│ Scrollable if needed │
│ │
├───────────────────────────────────────┤
│ Dialog Footer │
│ [Cancel] [Confirm]│
└───────────────────────────────────────┘
```
---
## Table
### Row States
| State | Background | Use Case |
|-------|------------|----------|
| default | white | Normal row |
| hover | gray-50 | Mouse over |
| selected | primary/10% | Selected row |
| striped | gray-50/white | Alternating |
### Cell Alignment
| Content Type | Alignment |
|--------------|-----------|
| Text | Left |
| Numbers | Right |
| Status/Badge | Center |
| Actions | Right |
### Spacing
| Element | Value |
|---------|-------|
| cell padding | 12px 16px |
| header padding | 12px 16px |
| row height (compact) | 40px |
| row height (default) | 48px |
| row height (comfortable) | 56px |

View File

@@ -0,0 +1,214 @@
# Component Tokens
Component-specific tokens referencing semantic layer.
## Button Tokens
```css
:root {
/* Default (Primary) */
--button-bg: var(--color-primary);
--button-fg: var(--color-primary-foreground);
--button-hover-bg: var(--color-primary-hover);
--button-active-bg: var(--color-primary-active);
/* Secondary */
--button-secondary-bg: var(--color-secondary);
--button-secondary-fg: var(--color-secondary-foreground);
--button-secondary-hover-bg: var(--color-secondary-hover);
/* Outline */
--button-outline-border: var(--color-border);
--button-outline-fg: var(--color-foreground);
--button-outline-hover-bg: var(--color-accent);
/* Ghost */
--button-ghost-fg: var(--color-foreground);
--button-ghost-hover-bg: var(--color-accent);
/* Destructive */
--button-destructive-bg: var(--color-destructive);
--button-destructive-fg: var(--color-destructive-foreground);
--button-destructive-hover-bg: var(--color-destructive-hover);
/* Sizing */
--button-padding-x: var(--space-4);
--button-padding-y: var(--space-2);
--button-padding-x-sm: var(--space-3);
--button-padding-y-sm: var(--space-1-5);
--button-padding-x-lg: var(--space-6);
--button-padding-y-lg: var(--space-3);
/* Shape */
--button-radius: var(--radius-md);
--button-font-size: var(--font-size-sm);
--button-font-weight: var(--font-weight-medium);
}
```
## Input Tokens
```css
:root {
/* Background & Border */
--input-bg: var(--color-background);
--input-border: var(--color-input);
--input-fg: var(--color-foreground);
/* Placeholder */
--input-placeholder: var(--color-muted-foreground);
/* Focus */
--input-focus-border: var(--color-ring);
--input-focus-ring: var(--color-ring);
/* Error */
--input-error-border: var(--color-error);
--input-error-fg: var(--color-error);
/* Disabled */
--input-disabled-bg: var(--color-muted);
--input-disabled-fg: var(--color-muted-foreground);
/* Sizing */
--input-padding-x: var(--space-3);
--input-padding-y: var(--space-2);
--input-radius: var(--radius-md);
--input-font-size: var(--font-size-sm);
}
```
## Card Tokens
```css
:root {
/* Background & Border */
--card-bg: var(--color-card);
--card-fg: var(--color-card-foreground);
--card-border: var(--color-border);
/* Shadow */
--card-shadow: var(--shadow-default);
--card-shadow-hover: var(--shadow-md);
/* Spacing */
--card-padding: var(--space-6);
--card-padding-sm: var(--space-4);
--card-gap: var(--space-4);
/* Shape */
--card-radius: var(--radius-lg);
}
```
## Badge Tokens
```css
:root {
/* Default */
--badge-bg: var(--color-primary);
--badge-fg: var(--color-primary-foreground);
/* Secondary */
--badge-secondary-bg: var(--color-secondary);
--badge-secondary-fg: var(--color-secondary-foreground);
/* Outline */
--badge-outline-border: var(--color-border);
--badge-outline-fg: var(--color-foreground);
/* Destructive */
--badge-destructive-bg: var(--color-destructive);
--badge-destructive-fg: var(--color-destructive-foreground);
/* Sizing */
--badge-padding-x: var(--space-2-5);
--badge-padding-y: var(--space-0-5);
--badge-radius: var(--radius-full);
--badge-font-size: var(--font-size-xs);
}
```
## Alert Tokens
```css
:root {
/* Default */
--alert-bg: var(--color-background);
--alert-fg: var(--color-foreground);
--alert-border: var(--color-border);
/* Destructive */
--alert-destructive-bg: var(--color-destructive);
--alert-destructive-fg: var(--color-destructive-foreground);
/* Spacing */
--alert-padding: var(--space-4);
--alert-radius: var(--radius-lg);
}
```
## Dialog/Modal Tokens
```css
:root {
/* Overlay */
--dialog-overlay-bg: rgb(0 0 0 / 0.5);
/* Content */
--dialog-bg: var(--color-background);
--dialog-fg: var(--color-foreground);
--dialog-border: var(--color-border);
--dialog-shadow: var(--shadow-lg);
/* Spacing */
--dialog-padding: var(--space-6);
--dialog-radius: var(--radius-lg);
--dialog-max-width: 32rem;
}
```
## Table Tokens
```css
:root {
/* Header */
--table-header-bg: var(--color-muted);
--table-header-fg: var(--color-muted-foreground);
/* Body */
--table-row-bg: var(--color-background);
--table-row-hover-bg: var(--color-muted);
--table-row-fg: var(--color-foreground);
/* Border */
--table-border: var(--color-border);
/* Spacing */
--table-cell-padding-x: var(--space-4);
--table-cell-padding-y: var(--space-3);
}
```
## Usage Example
```css
.button {
background: var(--button-bg);
color: var(--button-fg);
padding: var(--button-padding-y) var(--button-padding-x);
border-radius: var(--button-radius);
font-size: var(--button-font-size);
font-weight: var(--button-font-weight);
transition: background var(--duration-fast);
}
.button:hover {
background: var(--button-hover-bg);
}
.button.secondary {
background: var(--button-secondary-bg);
color: var(--button-secondary-fg);
}
```

View File

@@ -0,0 +1,203 @@
# Primitive Tokens
Raw design values - foundation of the design system.
## Color Scales
### Gray Scale
```css
:root {
--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;
--color-gray-950: #030712;
}
```
### Primary Colors (Blue)
```css
:root {
--color-blue-50: #EFF6FF;
--color-blue-100: #DBEAFE;
--color-blue-200: #BFDBFE;
--color-blue-300: #93C5FD;
--color-blue-400: #60A5FA;
--color-blue-500: #3B82F6;
--color-blue-600: #2563EB;
--color-blue-700: #1D4ED8;
--color-blue-800: #1E40AF;
--color-blue-900: #1E3A8A;
}
```
### Status Colors
```css
:root {
/* Success - Green */
--color-green-500: #22C55E;
--color-green-600: #16A34A;
/* Warning - Yellow */
--color-yellow-500: #EAB308;
--color-yellow-600: #CA8A04;
/* Error - Red */
--color-red-500: #EF4444;
--color-red-600: #DC2626;
/* Info - Blue */
--color-info: var(--color-blue-500);
}
```
## Spacing Scale
4px base unit system.
```css
:root {
--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 */
}
```
## Typography Scale
```css
:root {
/* Font Sizes */
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--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 */
/* Line Heights */
--leading-none: 1;
--leading-tight: 1.25;
--leading-snug: 1.375;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--leading-loose: 2;
/* Font Weights */
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* Letter Spacing */
--tracking-tighter: -0.05em;
--tracking-tight: -0.025em;
--tracking-normal: 0;
--tracking-wide: 0.025em;
--tracking-wider: 0.05em;
}
```
## Border Radius
```css
:root {
--radius-none: 0;
--radius-sm: 0.125rem; /* 2px */
--radius-default: 0.25rem; /* 4px */
--radius-md: 0.375rem; /* 6px */
--radius-lg: 0.5rem; /* 8px */
--radius-xl: 0.75rem; /* 12px */
--radius-2xl: 1rem; /* 16px */
--radius-3xl: 1.5rem; /* 24px */
--radius-full: 9999px;
}
```
## Shadows
```css
:root {
--shadow-none: none;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-default: 0 1px 3px 0 rgb(0 0 0 / 0.1),
0 1px 2px -1px rgb(0 0 0 / 0.1);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1),
0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1),
0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1),
0 8px 10px -6px rgb(0 0 0 / 0.1);
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
}
```
## Motion / Duration
```css
:root {
--duration-75: 75ms;
--duration-100: 100ms;
--duration-150: 150ms;
--duration-200: 200ms;
--duration-300: 300ms;
--duration-500: 500ms;
--duration-700: 700ms;
--duration-1000: 1000ms;
/* Semantic durations */
--duration-fast: var(--duration-150);
--duration-normal: var(--duration-200);
--duration-slow: var(--duration-300);
}
```
## Z-Index Scale
```css
:root {
--z-auto: auto;
--z-0: 0;
--z-10: 10;
--z-20: 20;
--z-30: 30;
--z-40: 40;
--z-50: 50;
--z-dropdown: 1000;
--z-sticky: 1100;
--z-modal: 1200;
--z-popover: 1300;
--z-tooltip: 1400;
}
```

View File

@@ -0,0 +1,215 @@
# Semantic Tokens
Purpose-based aliases referencing primitive tokens.
## Color Semantics
### Background & Foreground
```css
:root {
/* Page background */
--color-background: var(--color-gray-50);
--color-foreground: var(--color-gray-900);
/* Card/surface background */
--color-card: white;
--color-card-foreground: var(--color-gray-900);
/* Popover/dropdown */
--color-popover: white;
--color-popover-foreground: var(--color-gray-900);
}
```
### Primary
```css
:root {
--color-primary: var(--color-blue-600);
--color-primary-hover: var(--color-blue-700);
--color-primary-active: var(--color-blue-800);
--color-primary-foreground: white;
}
```
### Secondary
```css
:root {
--color-secondary: var(--color-gray-100);
--color-secondary-hover: var(--color-gray-200);
--color-secondary-foreground: var(--color-gray-900);
}
```
### Muted
```css
:root {
--color-muted: var(--color-gray-100);
--color-muted-foreground: var(--color-gray-500);
}
```
### Accent
```css
:root {
--color-accent: var(--color-gray-100);
--color-accent-foreground: var(--color-gray-900);
}
```
### Destructive
```css
:root {
--color-destructive: var(--color-red-600);
--color-destructive-hover: var(--color-red-700);
--color-destructive-foreground: white;
}
```
### Status Colors
```css
:root {
--color-success: var(--color-green-600);
--color-success-foreground: white;
--color-warning: var(--color-yellow-500);
--color-warning-foreground: var(--color-gray-900);
--color-error: var(--color-red-600);
--color-error-foreground: white;
--color-info: var(--color-blue-500);
--color-info-foreground: white;
}
```
### Border & Ring
```css
:root {
--color-border: var(--color-gray-200);
--color-input: var(--color-gray-200);
--color-ring: var(--color-blue-500);
}
```
## Spacing Semantics
```css
:root {
/* Component internal spacing */
--spacing-component-xs: var(--space-1);
--spacing-component-sm: var(--space-2);
--spacing-component: var(--space-3);
--spacing-component-lg: var(--space-4);
/* Section spacing */
--spacing-section-sm: var(--space-8);
--spacing-section: var(--space-12);
--spacing-section-lg: var(--space-16);
/* Page margins */
--spacing-page-x: var(--space-4);
--spacing-page-y: var(--space-6);
}
```
## Typography Semantics
```css
:root {
/* Headings */
--font-heading: var(--font-size-2xl);
--font-heading-lg: var(--font-size-3xl);
--font-heading-xl: var(--font-size-4xl);
/* Body */
--font-body: var(--font-size-base);
--font-body-sm: var(--font-size-sm);
--font-body-lg: var(--font-size-lg);
/* Labels & Captions */
--font-label: var(--font-size-sm);
--font-caption: var(--font-size-xs);
}
```
## Interactive States
```css
:root {
/* Focus ring */
--ring-width: 2px;
--ring-offset: 2px;
--ring-color: var(--color-ring);
/* Opacity for disabled */
--opacity-disabled: 0.5;
/* Transitions */
--transition-colors: color, background-color, border-color;
--transition-transform: transform;
--transition-all: all;
}
```
## Dark Mode Overrides
```css
.dark {
--color-background: var(--color-gray-950);
--color-foreground: var(--color-gray-50);
--color-card: var(--color-gray-900);
--color-card-foreground: var(--color-gray-50);
--color-popover: var(--color-gray-900);
--color-popover-foreground: var(--color-gray-50);
--color-muted: var(--color-gray-800);
--color-muted-foreground: var(--color-gray-400);
--color-secondary: var(--color-gray-800);
--color-secondary-foreground: var(--color-gray-50);
--color-accent: var(--color-gray-800);
--color-accent-foreground: var(--color-gray-50);
--color-border: var(--color-gray-800);
--color-input: var(--color-gray-800);
}
```
## Usage Patterns
### Applying Semantic Tokens
```css
/* Good - uses semantic tokens */
.card {
background: var(--color-card);
color: var(--color-card-foreground);
border: 1px solid var(--color-border);
}
/* Bad - uses primitive tokens directly */
.card {
background: var(--color-gray-50);
color: var(--color-gray-900);
}
```
### Theme Switching
Semantic tokens enable instant theme switching:
```js
// Toggle dark mode
document.documentElement.classList.toggle('dark');
```

View File

@@ -0,0 +1,241 @@
# States and Variants
Component state definitions and variant patterns.
## Interactive States
### State Definitions
| State | Trigger | Visual Change |
|-------|---------|---------------|
| default | None | Base appearance |
| hover | Mouse over | Slight color shift |
| focus | Tab/click | Focus ring |
| active | Mouse down | Darkest color |
| disabled | disabled attr | Reduced opacity |
| loading | Async action | Spinner + opacity |
### State Priority
When multiple states apply, priority (highest to lowest):
1. disabled
2. loading
3. active
4. focus
5. hover
6. default
### State Transitions
```css
/* Standard transition for interactive elements */
.interactive {
transition-property: color, background-color, border-color, box-shadow;
transition-duration: var(--duration-fast);
transition-timing-function: ease-in-out;
}
```
| Transition | Duration | Easing |
|------------|----------|--------|
| Color changes | 150ms | ease-in-out |
| Background | 150ms | ease-in-out |
| Transform | 200ms | ease-out |
| Opacity | 150ms | ease |
| Shadow | 200ms | ease-out |
## Focus States
### Focus Ring Spec
```css
/* Standard focus ring */
.focusable:focus-visible {
outline: none;
box-shadow: 0 0 0 var(--ring-offset) var(--color-background),
0 0 0 calc(var(--ring-offset) + var(--ring-width)) var(--ring-color);
}
```
| Property | Value |
|----------|-------|
| Ring width | 2px |
| Ring offset | 2px |
| Ring color | primary (blue-500) |
| Offset color | background |
### Focus Within
```css
/* Container focus when child is focused */
.container:focus-within {
border-color: var(--color-ring);
}
```
## Disabled States
### Visual Treatment
```css
.disabled {
opacity: var(--opacity-disabled); /* 0.5 */
pointer-events: none;
cursor: not-allowed;
}
```
| Property | Disabled Value |
|----------|----------------|
| Opacity | 50% |
| Pointer events | none |
| Cursor | not-allowed |
| Background | muted |
| Color | muted-foreground |
### Accessibility
- Use `aria-disabled="true"` for semantic disabled
- Use `disabled` attribute for form elements
- Maintain sufficient contrast (3:1 minimum)
## Loading States
### Spinner Placement
| Component | Spinner Position |
|-----------|------------------|
| Button | Replace icon or center |
| Input | Trailing position |
| Card | Center overlay |
| Page | Center of viewport |
### Loading Treatment
```css
.loading {
position: relative;
pointer-events: none;
}
.loading::after {
content: '';
/* spinner styles */
}
.loading > * {
opacity: 0.7;
}
```
## Error States
### Visual Indicators
```css
.error {
border-color: var(--color-error);
color: var(--color-error);
}
.error:focus-visible {
box-shadow: 0 0 0 2px var(--color-background),
0 0 0 4px var(--color-error);
}
```
| Element | Error Treatment |
|---------|-----------------|
| Input border | red-500 |
| Input focus ring | red/20% |
| Helper text | red-600 |
| Icon | red-500 |
### Error Messages
- Position below input
- Use error color
- Include icon for accessibility
- Clear on valid input
## Variant Patterns
### Color Variants
```css
/* Pattern for color variants */
.component {
--component-bg: var(--color-primary);
--component-fg: var(--color-primary-foreground);
background: var(--component-bg);
color: var(--component-fg);
}
.component.secondary {
--component-bg: var(--color-secondary);
--component-fg: var(--color-secondary-foreground);
}
.component.destructive {
--component-bg: var(--color-destructive);
--component-fg: var(--color-destructive-foreground);
}
```
### Size Variants
```css
/* Pattern for size variants */
.component {
--component-height: 40px;
--component-padding: var(--space-4);
--component-font: var(--font-size-sm);
}
.component.sm {
--component-height: 32px;
--component-padding: var(--space-3);
--component-font: var(--font-size-xs);
}
.component.lg {
--component-height: 48px;
--component-padding: var(--space-6);
--component-font: var(--font-size-base);
}
```
## Accessibility Requirements
### Color Contrast
| Element | Minimum Ratio |
|---------|---------------|
| Normal text | 4.5:1 |
| Large text (18px+) | 3:1 |
| UI components | 3:1 |
| Focus indicator | 3:1 |
### State Indicators
- Never rely on color alone
- Use icons, text, or patterns
- Ensure focus is visible
- Provide loading announcements
### ARIA States
```html
<!-- Disabled -->
<button disabled aria-disabled="true">Submit</button>
<!-- Loading -->
<button aria-busy="true" aria-describedby="loading-text">
<span id="loading-text" class="sr-only">Loading...</span>
</button>
<!-- Error -->
<input aria-invalid="true" aria-describedby="error-msg">
<span id="error-msg" role="alert">Error message</span>
```

View File

@@ -0,0 +1,251 @@
# Tailwind Integration
Map design system tokens to Tailwind CSS configuration.
## CSS Variables Setup
### Base Layer
```css
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
/* Primitives */
--color-blue-600: 37 99 235; /* HSL: 217 91% 60% */
/* Semantic */
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--primary: 217 91% 60%;
--primary-foreground: 0 0% 100%;
--secondary: 220 14% 96%;
--secondary-foreground: 222 47% 11%;
--muted: 220 14% 96%;
--muted-foreground: 220 9% 46%;
--accent: 220 14% 96%;
--accent-foreground: 222 47% 11%;
--destructive: 0 84% 60%;
--destructive-foreground: 0 0% 100%;
--border: 220 13% 91%;
--input: 220 13% 91%;
--ring: 217 91% 60%;
--radius: 0.5rem;
}
.dark {
--background: 222 47% 4%;
--foreground: 210 40% 98%;
--primary: 217 91% 60%;
--primary-foreground: 0 0% 100%;
--secondary: 217 33% 17%;
--secondary-foreground: 210 40% 98%;
--muted: 217 33% 17%;
--muted-foreground: 215 20% 65%;
--accent: 217 33% 17%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62% 30%;
--destructive-foreground: 0 0% 100%;
--border: 217 33% 17%;
--input: 217 33% 17%;
--ring: 217 91% 60%;
}
}
```
## Tailwind Config
### tailwind.config.ts
```typescript
import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ['class'],
content: ['./src/**/*.{ts,tsx}'],
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
},
},
plugins: [],
}
export default config
```
## HSL Format Benefits
Using HSL without function allows opacity modifiers:
```tsx
// With HSL format (space-separated)
<div className="bg-primary/50"> // 50% opacity
<div className="text-primary/80"> // 80% opacity
// CSS output
background-color: hsl(217 91% 60% / 0.5);
```
## Component Classes
### Button Example
```css
@layer components {
.btn {
@apply inline-flex items-center justify-center
rounded-md font-medium
transition-colors
focus-visible:outline-none focus-visible:ring-2
focus-visible:ring-ring focus-visible:ring-offset-2
disabled:pointer-events-none disabled:opacity-50;
}
.btn-default {
@apply bg-primary text-primary-foreground
hover:bg-primary/90;
}
.btn-secondary {
@apply bg-secondary text-secondary-foreground
hover:bg-secondary/80;
}
.btn-outline {
@apply border border-input bg-background
hover:bg-accent hover:text-accent-foreground;
}
.btn-ghost {
@apply hover:bg-accent hover:text-accent-foreground;
}
.btn-destructive {
@apply bg-destructive text-destructive-foreground
hover:bg-destructive/90;
}
/* Sizes */
.btn-sm { @apply h-8 px-3 text-xs; }
.btn-md { @apply h-10 px-4 text-sm; }
.btn-lg { @apply h-12 px-6 text-base; }
}
```
## Spacing Integration
```typescript
// tailwind.config.ts
theme: {
extend: {
spacing: {
// Map to CSS variables if needed
'section': 'var(--spacing-section)',
'component': 'var(--spacing-component)',
}
}
}
```
## Animation Tokens
```typescript
// tailwind.config.ts
theme: {
extend: {
transitionDuration: {
fast: '150ms',
normal: '200ms',
slow: '300ms',
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
}
}
```
## Dark Mode Toggle
```typescript
// Toggle dark mode
function toggleDarkMode() {
document.documentElement.classList.toggle('dark')
}
// System preference
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark')
}
```
## shadcn/ui Alignment
This configuration aligns with shadcn/ui conventions:
- Same CSS variable naming
- Same HSL format
- Same color scale structure
- Compatible with `npx shadcn@latest add` commands
### Using with shadcn/ui
```bash
# Initialize (uses same token structure)
npx shadcn@latest init
# Add components (styled with these tokens)
npx shadcn@latest add button card input
```
Components will automatically use your design system tokens.

View File

@@ -0,0 +1,224 @@
# Token Architecture
Three-layer token system for scalable, themeable design systems.
## Layer Overview
```
┌─────────────────────────────────────────┐
│ Component Tokens │ Per-component overrides
│ --button-bg, --card-padding │
├─────────────────────────────────────────┤
│ Semantic Tokens │ Purpose-based aliases
│ --color-primary, --spacing-section │
├─────────────────────────────────────────┤
│ Primitive Tokens │ Raw design values
│ --color-blue-600, --space-4 │
└─────────────────────────────────────────┘
```
## Why Three Layers?
| Layer | Purpose | When to Change |
|-------|---------|----------------|
| Primitive | Base values (colors, sizes) | Rarely - foundational |
| Semantic | Meaning assignment | Theme switching |
| Component | Component customization | Per-component needs |
## Layer 1: Primitive Tokens
Raw design values without semantic meaning.
```css
:root {
/* Colors */
--color-gray-50: #F9FAFB;
--color-gray-900: #111827;
--color-blue-500: #3B82F6;
--color-blue-600: #2563EB;
/* Spacing (4px base) */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-4: 1rem; /* 16px */
--space-6: 1.5rem; /* 24px */
/* Typography */
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
/* Radius */
--radius-sm: 0.25rem;
--radius-default: 0.5rem;
--radius-lg: 0.75rem;
/* Shadows */
--shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
--shadow-default: 0 1px 3px rgb(0 0 0 / 0.1);
}
```
## Layer 2: Semantic Tokens
Purpose-based aliases that reference primitives.
```css
:root {
/* Background */
--color-background: var(--color-gray-50);
--color-foreground: var(--color-gray-900);
/* Primary */
--color-primary: var(--color-blue-600);
--color-primary-hover: var(--color-blue-700);
/* Secondary */
--color-secondary: var(--color-gray-100);
--color-secondary-foreground: var(--color-gray-900);
/* Muted */
--color-muted: var(--color-gray-100);
--color-muted-foreground: var(--color-gray-500);
/* Destructive */
--color-destructive: var(--color-red-600);
--color-destructive-foreground: white;
/* Spacing */
--spacing-component: var(--space-4);
--spacing-section: var(--space-6);
}
```
## Layer 3: Component Tokens
Component-specific tokens referencing semantic layer.
```css
:root {
/* Button */
--button-bg: var(--color-primary);
--button-fg: white;
--button-hover-bg: var(--color-primary-hover);
--button-padding-x: var(--space-4);
--button-padding-y: var(--space-2);
--button-radius: var(--radius-default);
/* Input */
--input-bg: var(--color-background);
--input-border: var(--color-gray-300);
--input-focus-ring: var(--color-primary);
--input-padding: var(--space-2) var(--space-3);
/* Card */
--card-bg: var(--color-background);
--card-border: var(--color-gray-200);
--card-padding: var(--space-4);
--card-radius: var(--radius-lg);
--card-shadow: var(--shadow-default);
}
```
## Dark Mode
Override semantic tokens for dark theme:
```css
.dark {
--color-background: var(--color-gray-900);
--color-foreground: var(--color-gray-50);
--color-muted: var(--color-gray-800);
--color-muted-foreground: var(--color-gray-400);
--color-secondary: var(--color-gray-800);
}
```
## Naming Convention
```
--{category}-{item}-{variant}-{state}
Examples:
--color-primary # category-item
--color-primary-hover # category-item-state
--button-bg-hover # component-property-state
--space-section-sm # category-semantic-variant
```
## Categories
| Category | Examples |
|----------|----------|
| color | primary, secondary, muted, destructive |
| space | 1, 2, 4, 8, section, component |
| font-size | xs, sm, base, lg, xl |
| radius | sm, default, lg, full |
| shadow | sm, default, lg |
| duration | fast, normal, slow |
## File Organization
```
tokens/
├── primitives.css # Raw values
├── semantic.css # Purpose aliases
├── components.css # Component tokens
└── index.css # Imports all
```
Or single file with layer comments:
```css
/* === PRIMITIVES === */
:root { ... }
/* === SEMANTIC === */
:root { ... }
/* === COMPONENTS === */
:root { ... }
/* === DARK MODE === */
.dark { ... }
```
## Migration from Flat Tokens
Before (flat):
```css
--button-primary-bg: #2563EB;
--button-secondary-bg: #F3F4F6;
```
After (three-layer):
```css
/* Primitive */
--color-blue-600: #2563EB;
--color-gray-100: #F3F4F6;
/* Semantic */
--color-primary: var(--color-blue-600);
--color-secondary: var(--color-gray-100);
/* Component */
--button-bg: var(--color-primary);
--button-secondary-bg: var(--color-secondary);
```
## W3C DTCG Alignment
Token JSON format (W3C Design Tokens Community Group):
```json
{
"color": {
"blue": {
"600": {
"$value": "#2563EB",
"$type": "color"
}
}
}
}
```