Colors
Semantic color tokens adapt per palette. The 3-tier architecture maps primitives (blue-500) to semantic aliases (brand-primary) to component tokens (button-bg).
Neutral Scale
Brand Colors
Semantic Colors
Typography
Fluid type scale using clamp() for responsive sizing without breakpoints. Font families are foundation-locked: Inter for body, Inter for display headings, JetBrains Mono for code, Source Serif 4 for editorial callouts. These never change per palette.
Spacing & Grid
4px base grid. 13 spacing tokens from 4px to 80px.
Shadows & Elevation
5-level shadow system. Dark themes use glow-border technique (white rgba border + deeper black shadow) instead of invisible black shadows.
Motion
Duration tokens (fast/normal/slow/slower) + 4 easing curves. All motion respects prefers-reduced-motion.
Motion Choreography
Standardized animation presets combining duration + easing + transform. Every component follows these choreography patterns for consistent motion language.
Enter Transitions
Stagger Children
Interaction Feedback
/* Enter: fade + slide */
.enter { animation: fadeSlideUp 250ms cubic-bezier(0,0,0.2,1) forwards; }
@keyframes fadeSlideUp {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* Stagger children: 60ms per item */
.stagger > :nth-child(1) { animation-delay: 0ms; }
.stagger > :nth-child(2) { animation-delay: 60ms; }
.stagger > :nth-child(3) { animation-delay: 120ms; }
/* ... max 600ms total */
/* Interaction: hover-lift */
@media (hover: hover) { .interactive:hover { transform: translateY(-2px); } }
.interactive:active { transform: scale(0.98); }
/* Respect motion preferences */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}Modern CSS Transitions
Tokens for the View Transitions API and @starting-style rule. These modern CSS features enable smooth page transitions and entry animations for elements that transition from display: none.
View Transition Names
Assign named view transitions to key UI elements so the browser can animate them across page navigations or DOM state changes.
| Token | Value | Usage |
|---|---|---|
--transition-view-transition-name-header | uds-header | Persistent header across navigations |
--transition-view-transition-name-hero | uds-hero | Hero sections with cross-fade |
--transition-view-transition-name-card | uds-card | Card morph transitions |
--transition-view-transition-name-sidebar | uds-sidebar | Sidebar navigation persistence |
--transition-view-transition-name-modal | uds-modal | Modal dialog transitions |
@starting-style Tokens
The @starting-style rule defines the initial state for elements entering the DOM or transitioning from display: none. This replaces the need for JavaScript-triggered animation classes.
| Element | Opacity | Transform | Description |
|---|---|---|---|
| Modal | 0 | scale(0.95) | Scale-in entry for modal dialogs |
| Dropdown | 0 | translateY(-8px) | Slide-down entry for dropdowns |
| Toast | 0 | translateX(100%) | Slide-in from right for toasts |
| Popover | 0 | scale(0.9) translateY(-4px) | Combined scale and slide for popovers |
Transition Styles
Domain-specific easing curves recommended by the reasoning engine based on sector context.
Usage Examples
/* View Transitions API — cross-page element morphing */
.uds-header {
view-transition-name: var(--transition-view-transition-name-header);
}
.uds-card {
view-transition-name: var(--transition-view-transition-name-card);
}
/* Customize the transition animation */
::view-transition-old(uds-card) {
animation: var(--duration-fast) var(--ease-out) fade-out;
}
::view-transition-new(uds-card) {
animation: var(--duration-normal) var(--ease-out) fade-in;
}
/* @starting-style — entry animation from display:none */
.uds-modal {
opacity: 1;
transform: scale(1);
transition: opacity var(--duration-normal) var(--ease-out),
transform var(--duration-normal) var(--ease-out),
display var(--duration-normal) allow-discrete;
@starting-style {
opacity: 0;
transform: scale(0.95);
}
}
.uds-toast {
opacity: 1;
transform: translateX(0);
transition: opacity var(--duration-normal) var(--ease-out),
transform var(--duration-normal) var(--ease-out),
display var(--duration-normal) allow-discrete;
@starting-style {
opacity: 0;
transform: translateX(100%);
}
}
/* Sector-specific transition styles from reasoning engine */
/* Luxury: elegant curve */
.transition-elegant { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }
/* Finance: snappy curve */
.transition-snappy { transition-timing-function: cubic-bezier(0.2, 0, 0, 1); }
/* Healthcare: gentle curve */
.transition-gentle { transition-timing-function: cubic-bezier(0.4, 0, 0.6, 1); }
/* Data-dense: instant (no visible transition) */
.transition-instant { transition-timing-function: steps(1); }
/* Always respect reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
view-transition-name: none !important;
}
}Browser Support
Chrome 111+, Edge 111+, Opera 97+. Safari 18+. Firefox behind flag. Use document.startViewTransition() with progressive enhancement — non-supporting browsers simply skip the animation.
Chrome 117+, Edge 117+, Opera 103+, Safari 17.5+. Firefox 129+. Graceful degradation — without support, elements appear without entry animation. Pair with transition-behavior: allow-discrete for display transitions.
Layout & Scale
Foundation-level tokens for layout breakpoints, border radii, z-index layers, and opacity. These are governance-locked and never change per palette.
Border Radius Scale
Z-Index Layers
| Token | Value | Usage |
|---|---|---|
--z-dropdown | 100 | Dropdown menus, popovers |
--z-sticky | 200 | Sticky headers, toolbars |
--z-overlay | 300 | Overlay backdrops |
--z-modal | 400 | Modal dialogs |
--z-toast | 500 | Toast notifications |
--z-tooltip | 600 | Tooltips (above everything) |
--z-system | 9999 | Skip links, system-level UI |
Opacity Scale
Layout Breakpoints
| Token | Breakpoint | Container Max | Usage |
|---|---|---|---|
sm | 640px | 640px | Large phones |
md | 768px | 768px | Tablets |
lg | 1024px | 1024px | Small laptops |
xl | 1280px | 1200px | Desktops |
2xl | 1536px | 1400px | Wide screens |
Color Palettes
Each palette changes only color and shadow tokens. Typography, spacing, radii, and motion are locked to foundation values. Every palette ships with light + dark appearance modes, toggled via the sidebar button.
| Palette | Light bg | Brand Primary | Shape | Character |
|---|---|---|---|---|
| Minimal SaaS | #FFFFFF | #2563EB | Soft (6-12px) | Clean, conversion-focused |
| AI Futuristic | #0A0A0F | #00FF88 | Sharp (2-6px) | Dark-native, neon accents |
| Gradient Startup | #FFFFFF | #7C3AED | Round (8-16px) | Bold gradients, energetic |
| Corporate | #FFFFFF | #1A365D | Square (2-6px) | Muted, authoritative |
| Apple Minimal | #FFFFFF | #0071E3 | Smooth (8-14px) | Product-driven, restrained |
| Illustration | #FFFFFF | #C2410C | Friendly (10-18px) | Warm, playful, multi-color |
| Dashboard | #FFFFFF | #4F46E5 | Compact (4-8px) | Data viz, chart-friendly |
| Bold Lifestyle | #FFFFFF | #111111 | Brutalist (0px) | High-contrast, editorial |
| Minimal Corporate | #FDFCFB | #B45309 | Warm neutrals, editorial |
--color-*, --shadow-*, --radius-*, and --font-display tokens. Spacing, motion, z-index, opacity, and body typography remain foundation-locked.Palette Selection Rules
| Context | Default Palette | Rationale |
|---|---|---|
| Product UI (SaaS, apps) | Minimal SaaS | Highest readability, neutral brand presence, conversion-optimized |
| Marketing site | Gradient Startup | High visual energy for acquisition pages; default for all marketing surfaces |
| Internal dashboards | Dashboard | Chart-friendly 8-color scale, tabular number support |
| Developer tools / docs | AI Futuristic | Dark-native, monospace-friendly, low eye strain |
| Enterprise / regulated | Corporate | Conservative, authority-forward, stakeholder-safe |
Input
Form text input. WCAG 3:1 border contrast with dedicated border-input token.
Select
Toggle Switch
Tabs
Accessible tabbed interface with keyboard navigation.
Overview content. Tabs use role="tablist" and aria-selected for screen reader support.
Features content. Each tab panel is linked to its trigger via aria-controls and aria-labelledby.
Pricing content. Hidden panels use the hidden attribute for accessibility.
Accordion
Card
Content container with hover elevation. Used for features, pricing tiers, and grouped content.
Alert
Status banners for success, warning, error, and informational messages. 4px left border + semantic background.
Badge
Compact labels for status, categories, and counts. 4 semantic variants.
Avatar
User representation with initials, status indicators, and grouping. 5 sizes from 24px to 64px.
Avatar Group
Tooltip
Dropdown Menu
State Matrix
| State | Visual | Tokens | Behavior | Keyboard |
|---|---|---|---|---|
| Closed | Trigger button only | height: 44px, radius: --radius-md | Click or Enter/Space opens | Focus on trigger |
| Open | Menu panel below trigger, shadow-md | shadow: --shadow-md, z-index: --z-dropdown (100), radius: --radius-lg | Click outside or Escape closes | Arrow keys navigate items, Home/End jump |
| Item hover | bg: --color-bg-tertiary | padding: 8px 12px, transition: --duration-fast | Pointer indicates clickable | Visual focus ring matches hover |
| Item disabled | opacity: --opacity-disabled | opacity: --opacity-disabled (0.4), cursor: not-allowed | Click does nothing, skip in arrow nav | aria-disabled="true" |
| Submenu | Arrow icon on right edge | icon: --icon-sm (16px), offset: 4px | Hover/ArrowRight opens nested panel | ArrowLeft closes submenu |
| Loading items | Skeleton lines inside open panel | skeleton-count: 3, height: 36px each | Panel stays open, items replace skeleton | aria-busy="true" on menu |
Toast / Snackbar
State Matrix
| State | Visual | Tokens | Behavior | Timing |
|---|---|---|---|---|
| Entering | Slide up from bottom-right + fade in | z-index: --z-toast (500), shadow: --shadow-lg, radius: --radius-md | Adds to stack (max 3 visible) | Enter: --duration-normal ease-out |
| Visible (success) | Green left border, check icon | border-left: 4px --color-success, icon: --icon-md (20px) | Auto-dismiss countdown active | 5 seconds |
| Visible (error) | Red left border, alert icon | border-left: 4px --color-error, icon: --icon-md (20px) | Auto-dismiss countdown active | 8 seconds |
| Hovered | No visual change | cursor: default | Pause auto-dismiss timer | Timer resumes on mouse leave |
| Dismissed (user) | Fade out + slide right | transform: translateX(100%), opacity: 0 | Close button clicked | Exit: --duration-fast ease-in |
| Dismissed (auto) | Fade out + slide down | transform: translateY(8px), opacity: 0 | Timer expired | Exit: --duration-fast ease-in |
| Stacked (overflow) | Oldest toast removed | gap: --space-3 (12px) between toasts | When 4th toast arrives, remove oldest | Immediate removal of excess |
Pagination
Skeleton / Loading
Data Table
| Name | Status | Role | Last Active |
|---|---|---|---|
| Alice Johnson | Active | Admin | Just now |
| Bob Smith | Pending | Editor | 2h ago |
| Carol Davis | Inactive | Viewer | 3 days ago |
| David Lee | Active | Editor | 5m ago |
State Matrix
| State | Visual | Tokens | Behavior | ARIA |
|---|---|---|---|---|
| Loading (initial) | 3-5 skeleton rows, header visible | --skeleton-count: 4, row-height: 48px | No interaction on rows | aria-busy="true" on table |
| Loading (refresh) | Existing rows stay, subtle overlay | overlay: --opacity-muted (0.6) | Sorting/pagination disabled | aria-busy="true" |
| Empty | Illustration + heading + CTA centered | padding: --space-16, icon: --icon-xl (32px) | Primary action button focused | role="status" on message |
| Error | Inline alert above table + retry | border-left: 4px --color-error | Preserve stale data if available | role="alert" |
| Selected (single) | Row bg: --color-brand-muted | bg: --color-brand-muted, border-left: 2px --color-brand-primary | Checkbox checked, action bar visible | aria-selected="true" |
| Selected (bulk) | Count badge + bulk action bar | action-bar height: 48px, z-index: --z-sticky (200) | "Select all" applies to current page | Live region announces count |
| Sorted | Arrow icon in column header | icon: --icon-sm (16px), color: --color-text-primary | Re-fetches/re-sorts data | aria-sort="ascending|descending" |
Component Variant API
CVA-style variant contracts for every component. Variants compose as className--{variant}. Invalid combinations are explicitly disallowed.
Button
| Prop | Values | Default | CSS Class |
|---|---|---|---|
variant | primary · secondary · ghost · destructive | primary | .btn--{variant} |
size | sm · md · lg | md | .btn--{size} |
state | default · hover · active · disabled · loading | default | [disabled] · .btn--loading |
fullWidth | true · false | false | .btn--block |
iconOnly | true · false | false | .btn--icon |
Input
| Prop | Values | Default | CSS Class |
|---|---|---|---|
variant | default · error · success | default | .input--{variant} |
size | sm · md · lg | md | .input--{size} |
state | default · focus · disabled · readonly | default | [disabled] · [readonly] |
prefix | icon · text · none | none | .input-group |
Card
| Prop | Values | Default | CSS Class |
|---|---|---|---|
variant | elevated · outlined · filled | elevated | .card--{variant} |
padding | compact · default · spacious | default | .card--{padding} |
interactive | true · false | false | .card--interactive (adds hover-lift) |
Badge
| Prop | Values | Default | CSS Class |
|---|---|---|---|
variant | brand · success · error · neutral · warning | neutral | .badge--{variant} |
size | sm · md | md | .badge--{size} |
dot | true · false | false | .badge--dot (status indicator) |
Alert
| Prop | Values | Default | CSS Class |
|---|---|---|---|
variant | success · warning · error · info | info | .alert--{variant} |
dismissible | true · false | false | Adds close button |
icon | true · false | true | Hides leading icon |
Toast
| Prop | Values | Default | CSS Class |
|---|---|---|---|
variant | success · warning · error · info | info | .toast--{variant} |
position | top-right · top-center · bottom-right · bottom-center | bottom-right | JS positioning |
duration | 3000 · 5000 · 8000 · persistent | 5000 | JS auto-dismiss |
action | text · none | none | .toast-action |
Avatar
| Prop | Values | Default | CSS Class |
|---|---|---|---|
size | xs (24) · sm (32) · md (40) · lg (48) · xl (64) | md | .avatar--{size} |
status | online · busy · none | none | .avatar-status--{status} |
fallback | initials · icon · image | initials | Content-based |
group | true · false | false | .avatar-group |
Toggle
| Prop | Values | Default | CSS Class |
|---|---|---|---|
checked | true · false | false | .toggle.on |
disabled | true · false | false | [disabled] |
size | sm · md | md | .toggle--{size} |
Data Table
| Prop | Values | Default | CSS Class |
|---|---|---|---|
density | compact (36px) · default (48px) · comfortable (56px) | default | .data-table--{density} |
sortable | true · false | false | .data-table--sortable |
selectable | none · single · multi | none | .data-table--selectable |
striped | true · false | false | .data-table--striped |
stickyHeader | true · false | false | .data-table--sticky |
ghost + destructive on buttons) should be caught at lint-time. Use the data-variant attribute for framework-agnostic variant detection.Component Sandbox
Interactive playground — change props and see live updates. Theme inherits from palette selector above.
<button class="btn btn--md btn--primary">Button</button>
CSS Cascade Layers
All UDS token and component CSS is wrapped in @layer declarations for explicit cascade control. This prevents specificity conflicts between UDS styles and your application styles without resorting to !important.
Layer Hierarchy
UDS declares three ordered layers. Styles in later layers take precedence over earlier ones:
- uds.tokens — Design token custom properties (
:rootvariables, palette overrides) - uds.components — Component styles (
.btn,.card,.input, etc.) - uds.utilities — Utility classes and overrides
How It Works
The generated CSS output declares the layer order first, then places all tokens inside @layer uds.tokens:
@layer uds.tokens, uds.components, uds.utilities;
@layer uds.tokens {
:root {
--color-brand-primary: #2563eb;
--color-text-primary: #1a1a24;
--space-4: 16px;
/* ... all token custom properties */
}
}
Overriding UDS Styles
To override UDS styles cleanly, declare your own layer after the UDS layers. Styles in your layer automatically take precedence:
@layer uds.tokens, uds.components, uds.utilities, app;
@layer app {
/* Your styles here -- automatically override UDS without !important */
.custom-button {
background: var(--color-brand-primary);
border-radius: 999px;
}
.hero-section {
padding: var(--spacing-16);
}
}
Backward Compatibility
If your project does not use @layer, UDS styles still work correctly. Unlayered styles (styles not inside any @layer) always take precedence over layered styles in the CSS cascade. This means existing consumer stylesheets that do not use @layer will automatically override UDS defaults without any changes.
Browser Support
@layer is supported in all modern browsers since 2022: Chrome 99+, Firefox 97+, Safari 15.4+, and Edge 99+. For older browsers, the layer declarations are ignored and styles apply in normal source order.
Pattern: Forms
System-wide rules for form layout, validation, and submission. These rules apply to every form regardless of which components are used.
Single-column by default. Only use multi-column for short, related fields (city + state). Labels always above inputs, never floating or inline.
Inline errors below the field, shown on blur (not keystroke). Error text uses --color-error with role="alert". Never validate on focus.
Mark optional fields with "(optional)" suffix. Do not use asterisks. All fields are assumed required unless marked.
Primary button at bottom-left. Disable on submit, show inline spinner. Never navigate away on error. Preserve all entered data on failure.
Label-to-input gap: --spacing-1 (4px). Field-to-field gap: --spacing-5 (20px). Section gap: --spacing-8 (32px).
Below input, above error message. Uses --color-text-tertiary at --text-body-sm. Connected via aria-describedby.
Pattern: Data Tables
Rules for tabular data display, sorting, pagination, and state handling.
Compact: 40px. Default: 48px. Relaxed: 56px. Use compact for data-dense dashboards, relaxed for settings/admin.
Centered illustration + heading + action. Never show an empty <tbody>. Use "No [items] found" with a clear action ("Create first [item]").
Skeleton rows (3-5) matching column layout. Header stays visible. Never show a full-page spinner for table reloads.
Inline alert above table: "Unable to load data" + retry button. Preserve any existing rows if the error is a refresh failure.
Click header to cycle: unsorted → ascending → descending. Active sort column uses aria-sort. Only one column sorted at a time.
Checkbox in first column. "Select all" selects current page only. Show selection count + bulk action bar above table on selection.
Pattern: Feedback
Rules for alerts, toasts, error handling, and confirmations. These rules override any per-component defaults.
Max 2 lines of text. Use role="status" for info/success, role="alert" for error/warning. Never use alerts for marketing content. Never stack more than 1 inline alert per section.
Auto-dismiss after 5s (success) or 8s (error). Max 3 visible toasts, stacked bottom-right. Newest on top. Must include close button. Pause timer on hover.
Require explicit confirmation modal for irreversible actions (delete, publish, transfer). Primary button is destructive variant. Include "Type to confirm" for batch operations.
Show spinner after 300ms delay (avoid flicker). For >3s operations, show progress text. For >10s, show estimated time remaining. Never block the entire UI.
Always include: illustration or icon, heading, 1-line description, and a primary action button. Never show a blank page or container.
Every error message must include a next step: retry button, help link, or specific fix instruction. Never show raw error codes or stack traces.
Accessibility Audit
Automated WCAG 2.2 AA contrast verification covering 108 checks (9 palettes × 2 modes × 6 color pairs: text-primary/bg-primary, text-secondary/bg-primary, text-primary/bg-secondary, text-on-brand/brand-primary, brand-primary/bg-primary, text-tertiary/bg-primary). Last audited: March 2026.
| Palette | Light | Min Ratio | Dark | Min Ratio |
|---|---|---|---|---|
| Min SaaS | — | 0.0:1 | 4/4 | 4.5:1 |
| AI Futur. | 6/6 | 4.7:1 | 6/6 | 4.7:1 |
| Grad. Start. | — | 0.0:1 | 5/5 | 3.4:1 |
| Corporate | 1/1 | 15.4:1 | 5/5 | 4.2:1 |
| Apple Min. | 1/1 | 16.3:1 | 5/5 | 4.1:1 |
| Illustr. | 1/1 | 17.0:1 | 5/5 | 4.8:1 |
| Dashboard | 1/1 | 17.8:1 | 5/5 | 3.1:1 |
| Bold Life. | 1/1 | 17.3:1 | 6/6 | 3.7:1 |
| Min Corp. | 5/5 | 4.7:1 | 5/5 | 4.0:1 |
Checks Performed
4.5:1 required (AA)
4.5:1 required (AA)
4.5:1 required (AA)
4.5:1 required (AA)
3.0:1 required (AA large)
3.0:1 required (AA large)
Governance Requirements
4.5:1 text, 3:1 UI (SC 1.4.11). Verified across all 9 palettes × 2 modes.
Full tab order, arrow keys for composite widgets, Escape to dismiss.
ARIA roles, states, properties on every interactive component.
prefers-reduced-motion disables all animations globally.
44px minimum per WCAG 2.2 SC 2.5.8 on all interactive elements.
forced-colors media query ensures Windows High Contrast Mode support.