Design language
Components & patterns
Structure, size, states and signature motion for every component; the console dashboard pattern and chart rules; the accessibility checklist.
Component spec
| Component | Structure and size | States | Signature motion |
|---|---|---|---|
| Button | Primary filled (brand) / tonal (container fill + ink text) / text (brand text, no fill) / danger; height 36 (sm 32, lg 44); padding 16; r-sm; icons 16 | Hover layer (primary → brand-hover); press 0.97; disabled 0.4; loading keeps the width | Press |
| Icon button | 36 square (follows --control); r-sm or round; always has a tooltip | Same as button | Press |
| Input / select | Filled container, no border, r-sm, height --control; leading icon 16; label above, help text below (caption) | Hover container-high; focus: surface + 2px brand ring; invalid: 2px danger ring | Focus beam |
| Segmented | container pill track, padding 3; selected thumb is surface + level 2; items --control − 6 high | Hover text turns ink; press 0.96; ← / → switch | The thumb slides (snappy) |
| Chip | Height 32, r-full; unselected container | Selected: brand-tint + on-brand-tint + check | Refract |
| Switch | Track 36 × 22, thumb 16; on = brand track | The thumb stretches 4px while pressed; disabled 0.4 | Bouncy thumb |
| Checkbox / radio | 18 square r-xs / 18 round; unselected has a 1.5px ink-3 inner stroke (the only control stroke allowed) | Selected: brand fill grows from the center and the check draws; press 0.88 | Refract (from the center) |
| Tabs | Height 40; 2px brand indicator; for secondary switching inside content (page-level views use segmented) | Selected text in ink | Indicator: leading edge snappy, trailing edge smooth with a 60ms delay |
| Card | Slab: surface + r-lg + elev-1 + highlight; padding --pad; header: icon tile + title + text button on the right | Clickable cards lift with specular light on hover and press to 0.99 | Lift, specular |
| Metric | Icon tile (domain color) + label (label, ink-2) + number (metric) + change badge + mini area chart (along the bottom, 40 high) | Numbers flash tint when data refreshes | Count-up, disperse in |
| List table | No frame, no column lines; header 32 (caption, ink-3, no fill); rows --row; inset separator lines (starting at the first column's content); numbers right-aligned and tabular; first column has an icon tile or rank | Row hover: container fill + r-md (hides neighbouring separators); selected brand-tint | New rows light up |
| Grouped list | Groups of surface + r-lg; rows 52; icon tile 32 on the left, value and › on the right; inset separators | Row hover container | — |
| Sidebar | Table background, no right border; width 256 / rail 72; nav items 36, r-sm; group names overline; quota card on surface at the bottom | Current item: surface + elev-1 + brand icon + 600; hover layer | Width animates smoothly on collapse |
| Top bar | Height 60 (compact 56); left: menu button (< 1024) + company / site switcher pills (container); right: bell (count badge) + avatar | Transparent at rest; frosted + elev-1 once scrolled | Fade |
| Dialog | Overlay level: surface + r-lg (r-xl top corners as a compact bottom sheet) + elev-4; width 480 / 640; padding 24; --scrim behind | Focus trapped; Esc closes | Rises from just below the center (gentle) |
| Drawer | 480 on the right (details) / 304 on the left (navigation); overlay level | Same as dialog | Slides in from the edge (smooth) |
| Menu / dropdown panel | Float level: surface + r-md + elev-3 + hairline; padding 6; options 36 high, r-sm | Option hover / keyboard highlight container; selected: brand text + check | Opens from the trigger (snappy) |
| Toast | Inverse surface inverse, r-md, elev-3, at least 48 high; 22 round status icon on the left; action button on the right; bottom center, ≤ 440 wide | Hover pauses the timer | Bouncy arrival |
| Tooltip | Inverse surface, r-xs, caption, max width 260 | 500ms delay / immediate for a neighbour within 300ms | Fade in + 2px |
| Banner | Domain or status tint fill, r-lg, icon tile on the left, action on the right; on phones the action wraps below the text and the close button stays top-right | Dismissible | Height expands (smooth) |
| Badge | Height 22, r-full, tint fill + on-tint text + 12 icon or 6px dot | — | Count changes bounce |
| Empty state | 64 icon tile + title + explanation + primary action | — | Disperse in |
| Skeleton | container fill, r-xs; shaped like the final content | Static under reduced motion | Sweep |
| Progress | Height 8, r-full, container track, brand fill; a spectrum bar when there are several segments | Indeterminate: a 40% block moving back and forth | Fill grows smoothly |
| Spectrum bar | See "Spectrum bar" in Color & material | Hovering a segment shows its value | Segments unfold from the left (disperse) |
Pattern: dashboard
The console's /t/{tenant} page follows this structure; the "Pattern" section of the demo is fully interactive.
| Region | Content | Components |
|---|---|---|
| Shell | Sidebar (the current item, Dashboard, is a lit slab) + transparent top bar (company and site switcher pills, bell, avatar) | Sidebar, top bar |
| Header | Page title "Dashboard" + the description "Overall performance of sites, content, search and AI visibility" + the reporting period; on the right a site dropdown + segmented "Last 7 days / Last 30 days / Last 90 days / Custom" | Dropdown, segmented |
| Metric row | Articles published (content), search clicks (search), AI recommendation rate (ai), distribution reach (distribution); each with an icon tile, change badge and mini area chart | Metric cards (disperse in, count-up) |
| Main chart, 8 cols | "Search clicks and AI mentions", a two-series area chart; AI mentions use the right axis (values differ by > 10×) | Chart |
| Side card, 4 cols | "Traffic sources" as a spectrum bar + legend (search engines / AI engines / social / cluster sites / direct) | Spectrum bar |
| AI card | AI-generated GEO suggestions with a dispersion edge on top | Card + dispersion edge |
| List cards | Top keywords, top articles, cluster sites | List tables |
| Phone | Header actions wrap; metric cards in 2 columns; charts and lists in one column; top bar 56 | — |
Chart rules: no frame around the plot area; horizontal reference lines only (separator, dashed); line width 2.5 with round caps; areas fade vertically (24% → 0); legends are a small dot + text; hover tooltips are float-level cards. When two series differ by more than 10×, put the second on a right axis (tick labels in the series color, legend marked "right axis"); with three or more series, never use two axes; split the chart or normalise instead.
Accessibility checklist
- Text contrast ≥ 4.5:1; text ≥ 24px, icons and control boundaries ≥ 3:1; checked in both themes (
pnpm tokens:check) - Color is never the only carrier of meaning: statuses have text and an icon; spectrum bar segments have legend text
- Everything works with the keyboard; tab order matches visual order; there is a "Skip to main content" link
- Focus rings appear only on
:focus-visible, 2px brand, visible on every surface - Dialogs and drawers trap focus, close on Esc and return focus to their trigger
- Icon-only buttons have an
aria-labeland a tooltip - Form errors are linked with
aria-describedby; after submit, focus moves to the first error - Segmented controls use
radiogroup/radio; tabs usetablist/tab; switches useswitch; checkboxes usecheckbox - The toast region is
aria-live="polite"; toasts with actions stay ≥ 6s and pause on hover - Touch targets ≥ 44 × 44, pointer targets ≥ 32 × 32, spacing ≥ 8
- Respect
prefers-reduced-motion; offer a "Reduce motion" switch - Never rely on hover to convey information (always visible on touch)
- Works at 200% zoom; long Chinese and German strings don't overflow
- Image thumbnails use
brightness(0.92)in the dark theme