Design System
Tokens, components, and patterns for the Cosmo pipeline orchestration tool. Built for power users: dense, warm, purposeful.
Color
Warm cream surfaces with deep ink and orange accent. All tokens are OKLCH for perceptually uniform mixing. Neutrals are tinted toward the orange hue at ~78° to create subconscious cohesion.
Surfaces
Ink scale
Accent — Orange
Status
Build phase
Cool slate hue (228°) — machine processing stages in the pipeline bar: Build Queued and In Build.
Token reference
| Token | Swatch | Value | Use |
|---|---|---|---|
| --build-600 | oklch(44% 0.12 228) | Build phase hover / emphasis | |
| --build-500 | oklch(53% 0.14 228) | In Build pipeline segment | |
| --build-100 | oklch(92% 0.028 228) | Build Queued pipeline segment, build tinted surfaces | |
| --bg-raised | oklch(99.2% 0.005 78) | Cards, inputs, raised surfaces | |
| --bg-panel | cream-50 | Sidebar, panels | |
| --bg-app | cream-100 | App canvas, main background | |
| --bg-sunken | cream-200 | Inactive tabs, count chips, sunken wells | |
| --line | oklch(89% 0.022 78) | Default borders, dividers | |
| --line-strong | oklch(84% 0.028 78) | Emphasized borders, table headers | |
| --ink | ink-900 | Primary text | |
| --ink-soft | ink-500 | Secondary text, nav labels | |
| --ink-muted | ink-400 | Metadata, placeholders, labels | |
| --accent | orange-500 | Primary accent, active states, CTA | |
| --accent-dark | orange-600 | Hover state, links, text on accent-wash | |
| --accent-soft | orange-100 | Focus rings, tinted surfaces | |
| --accent-wash | orange-50 | Active nav bg, today column bg |
Typography
Three roles. Barlow for display and headings — condensed, sturdy, confident. Red Hat Text for all UI text and body — humanist grotesque, clear and warm at 12–14px. Rubik for metadata, labels, counts, and IDs — compact geometric, no monospacing.
Type scale
Font roles
| Token | Family | Use |
|---|---|---|
| --font-display | Barlow | Page titles, section heads, card titles, KPI values, day numbers |
| --font-ui | Red Hat Text | Body copy, nav items, buttons, inputs, table cells, all interactive UI |
| --font-mono | Rubik | IDs, counts, dates, status tags, pipeline labels, metadata — compact data contexts |
Spacing
4pt base scale. Token names are step-based, not pixel-based, so a redesign doesn't require renaming. Use gap for siblings, padding for internal breathing room.
Elevation
Shadows are warm-tinted (same hue as ink), never cold gray. They signal hierarchy — raised surfaces use xs/sm; floating panels use md; drawers and modals use lg.
| Token | Use |
|---|---|
| --shadow-xs | Cards, chip hover states |
| --shadow-sm | Playable cards, detail panels |
| --shadow-md | Cmd-K, floating filters, popover |
| --shadow-lg | Drawers, overlays, deep panels |
Buttons
Use primary (ink-900) for one main action per context. Accent (orange) only for the single most critical action in the view — rarely more than once per page. Secondary and ghost carry everything else.
Variants
Sizes
Disabled state
| Class | Description |
|---|---|
| .btn | Base — always combine with a size and a variant modifier |
| .btn-sm / .btn-md / .btn-lg | Size — 5 / 7 / 10px vertical padding |
| .btn-primary | ink-900 fill; one per context maximum |
| .btn-accent | Orange fill; use sparingly, one per view at most |
| .btn-secondary | bg-raised with border; general secondary actions |
| .btn-ghost | Transparent; low-hierarchy inline actions |
| .btn-danger | Destructive — red text, red bg on hover |
| .btn-link | Text-only, accent-dark color, zero horizontal padding |
Tags & Badges
Status tags use semantic color pairs (bg + text). Tier badges are always ink-900/cream — they convey rank, not health. Phase tags use subtle neutral styling since the phase label already carries the meaning.
Status tags
Tier badges
Count & phase
| Class | Description |
|---|---|
| .tag | Base status pill — mono uppercase, always add a variant |
| .tag-on-track / .tag-at-risk / .tag-behind / .tag-no-start / .tag-delivered | Semantic color pairs (bg + text) |
| .tier-badge | 18×18 ink-900 square badge for T1 / T2 / T3 / R labels |
| .tier-badge.lg | 24×24 variant for detail views and cards |
| .count-badge | Numeric count chip, bg-sunken |
| .count-badge.accent | Accent-colored count for active or important numbers |
| .phase-tag | Pipeline phase label — neutral pill with border |
Inputs
All form controls share the same border token and focus ring (accent + accent-soft). Keep labels short and above the field. Hint text is optional — only include when something non-obvious needs explaining.
Text input
Select & checkboxes
| Class | Description |
|---|---|
| .field | Flex-column wrapper for label + input + hint |
| .field-label / .field-hint | Above-field label (550 weight) and optional clarifying hint |
| .input | Text input — bg-raised, accent focus ring |
| .input.input-error | Error state — red border and focus shadow |
| .select | Dropdown — same sizing as .input, custom chevron via background-image |
| .checkbox-row | Flex-row wrapper for checkbox + label |
| .checkbox | Native checkbox styled with accent-color |
Playable card
One entity, three representations. The same playable appears as a full two-row card in the backlog, a compact single-row card in the weekly grid, and a flat table row in Pulse views. All three share tier-badge, name, PLY-ID, and game name — only density changes.
Full card — backlog
Two-row card used in the backlog panel, pools panel, and search results. Row 1: tier badge, playable name, PLY-ID. Row 2: game name, concept chip (NV/NC). Add .selected when linked to the open detail panel.
Grid card — schedule
Single-row compact variant for weekly board day cells. Row 2 is hidden. The .ply-status badge anchors right in row 1 — its label is the pipeline stage, its age span shows elapsed time.
Table row — Pulse
Flat full-width row for Pulse and status views. Same tier badge, name, PLY-ID, and game name — laid out as a horizontal row with state-colored backgrounds. Uses the .task-row component family documented in Patterns · 05.
Click popover
260px floating card shown when clicking a grid card. Anchored to the card in JS. Pop-tier-badge color is set inline from the card's status.
| Class | Description |
|---|---|
| .ply-card | Full two-row playable card — backlog, pools, search results |
| .ply-card.selected | Accent border + wash — card linked to the open detail panel |
| .ply-card--grid | Compact variant: single row, row 2 hidden, tighter padding — use in schedule day cells |
| .ply-card-r1 / .ply-card-r2 | Row 1 (badge + name + ID) and Row 2 (game + concept chip) |
| .ply-card-name | Playable name — truncates, takes flex space |
| .ply-card-id | PLY-XXX mono label — flex-shrink 0, always visible |
| .ply-card-game | Game name in row 2 — truncates |
| .ply-card-concept | NV / NC concept chip — mono, muted, row 2 right |
| .ply-status | Status+time badge anchored right in grid row 1 — default (muted), .ok, .warn, .err |
| .ply-status-age | Elapsed time appended inside .ply-status — e.g. "4H", "2D" |
| .task-row (+ states) | Pulse / table row representation — see Patterns · 05 |
| .popover / .pop-* | Click popover on grid cards — see Components · 04 (popover class table in components.css) |
Sidebar
The app shell's primary navigation panel. Fixed to full viewport height. Collapses from 212px to a 48px icon rail — toggle state persists in localStorage across pages.
Expanded & collapsed states
.collapsed to the aside. State saved to localStorage.title on each nav item provides a tooltip. Active state preserved..shell uses var(--sidebar-w, 212px) as its first column. JS sets the property; CSS transitions it.| Class | Description |
|---|---|
| .sidebar | Full-height flex column, bg-panel, right border |
| .sidebar.collapsed | Modifier — collapses to 48px icon rail |
| .sidebar-brand | Top row: brand text + toggle button, min-height 62px |
| .sidebar-brand-text | Wraps .brand-name and .brand-sub; hidden in collapsed state |
| .sidebar-toggle | 24px ghost button; swaps chevron via .toggle-icon-collapse / .toggle-icon-expand |
| .sidebar-nav | Nav item list; centers to icon-only when collapsed |
| .sidebar-footer | User identity row; hides name and role when collapsed |
Icons
Icons come from Lucide — a MIT-licensed stroke icon library. The library is embedded locally at design-system/lib/lucide.min.js and updated via npm run update-icons in data/.
Navigation icons
Usage
| Usage | Description |
|---|---|
| <i data-lucide="icon-name"></i> | Replaced at runtime by lucide.createIcons(). Add CSS width/height and stroke properties to size and color. |
| .nav-icon | 14×14 with stroke-width: 1.8. Inherits opacity from parent nav-item state. |
| npm run update-icons | Run from data/ to pull latest Lucide and copy the UMD bundle to lib/lucide.min.js. |
Data patterns
KPI tiles, progress bars, segmented bars, and load bars form the observability layer of Cosmo. All use Barlow for numeric values (tight tracking, no ambiguity) and Rubik for labels.
KPI tiles
Progress bars
Tier breakdown bar (.seg-bar)
Use .seg-bar only for tier T1/T2/T3 breakdowns.
For pipeline stage_status use
Status Distribution Bar.
Load bars (auditor capacity)
| Class | Description |
|---|---|
| .kpi / .kpi.sm | KPI tile — flex column with label, value, delta. .sm is compact height |
| .kpi.accent | Tints .kpi-value to the accent color |
| .kpi-label / .kpi-value / .kpi-delta | Mono label / display numeral / mono trend indicator |
| .kpi-delta.down / .flat | Red or muted delta; default is green |
| .progress-wrap / .progress-track / .progress-fill | Labeled progress bar with track + fill |
| .progress-fill.green / .amber / .red | Status color variants for progress |
| .seg-bar / .seg-bar.compact | Tier breakdown bar (tier T1/T2/T3) — 28px or 14px height. For pipeline stage_status use the Status Distribution Bar instead. |
| .load-bar-fill.low / .mid / .high / .over | Capacity states — green / accent / amber / red |
Status distribution bar
Horizontal bar showing the breakdown of playables by pipeline stage_status. Each segment is proportional to its count. Supports click-to-filter (active segment emphasized, others dimmed), a clear-filter affordance, per-segment responsive label collapse, and label-only hover tooltips. Canonical JS pattern lives in screens/prototypes/air.html.
Live demo
Click a segment to activate the filter (others dim). Click again or use × clear to reset. Drag the bottom-right corner to resize — labels collapse to icon-only when there isn't room.
Behavior notes
- Filtering:
filterByStatus(key)— clicking a segment toggles it active. Active =.is-active; others =.is-dimmed. Clicking the active segment again clears the filter. - Clear affordance:
.sdb-filter-hint.visiblereveals the hint + clear button. Show/hide via JS when filter is active. - Responsive collapse:
evaluatePbWide(bar)measures each segment; adds.is-widewhen wide enough to show the label alongside the icon. Wire to aResizeObserveron the bar element. - Tooltip:
showPbTooltip(seg)readsdata-tipfrom the segment and positions a body-attached.sdb-tooltipabove it. Label only — no meaning copy. - Background color: set inline via JS from
STATUS_CFG[n].cssVar(e.g.style="background:var(--amber-500)").
| Class | Description |
|---|---|
| .sdb | Bar container — flex row, 22px height, radius-full, overflow hidden. Segments flex inside. |
| .sdb-header | Row above the bar: label left, filter hint right. |
| .sdb-label | Mono uppercase label ("Pipeline"). |
| .sdb-filter-hint | Hidden by default. Add .visible when a filter is active. |
| .sdb-clear-btn | Inline button inside the filter hint. Calls clearPipelineFilter(). |
| .sdb-seg | Individual segment. Width set inline as % of total. Background set inline from STATUS_CFG. |
| .sdb-seg.is-active | This segment is the current filter target — brightened + ink outline. |
| .sdb-seg.is-dimmed | Another segment is active — this one is muted (opacity 0.18). |
| .sdb-seg.is-wide | Segment is wide enough to show its label. Added/removed by evaluatePbWide(). |
| .sdb-seg.is-light | Segment has a dark background — icon and label use cream-100 instead of ink-700. |
| .sdb-seg-icon | Lucide icon wrapper. SVG is 10×10px stroke-width 2. |
| .sdb-seg-label | Mono label. Hidden unless .is-wide is present. |
| .sdb-tooltip | Body-attached fixed tooltip. JS positions it above the hovered segment. Add .is-visible to show. |
| .dist-bar / .dist-seg | Legacy aliases — same visual output. Prototypes should migrate to .sdb / .sdb-seg over time. |
Charts
All charts use Recharts (React) styled with Cosmo design tokens. Area charts track trends, stacked bars compare across dimensions, donut and gauge show proportions and capacity. Color: green = on-track, amber = at-risk, red = behind, accent = highlight.
| Chart | Recharts components used |
|---|---|
| Delivery Trend | AreaChart + Area (fill gradient) + Line (target dashed) + XAxis + YAxis + Tooltip + CartesianGrid |
| Status by Game | BarChart + Bar × 3 (stacked, On Track / At Risk / Behind) + XAxis + YAxis + Tooltip + Legend |
| Status Donut | PieChart + Pie (innerRadius 60, outerRadius 90) + Cell per segment + Legend |
| Capacity Gauge | PieChart + Pie (startAngle=180, endAngle=0, 2 cells) + center label via foreignObject |
| Auditor Workload | BarChart (layout=vertical) + Bar × 2 (active + remaining) + XAxis + YAxis (type=category) + Tooltip |
| Color palette | green-500 (on-track), amber-600 (at-risk), red-500 (behind), orange-500 (accent/delivered), ink-300 (grid), ink-400 (axis text) |
Topbar
The topbar anchors page identity (title left) and controls (filters + week switch right). The date range uses mono type to signal it's a coordinate, not a headline.
| Class | Description |
|---|---|
| .date-label | Mono uppercase date range label — use in topbar right |
| .week-switch | Rounded pill container for prev / current / next navigation |
| .week-btn / .week-btn.active | Week nav button; active gets ink-900 fill |
Feedback
Inline alerts use the same semantic color pairs as status tags. Empty states are functional — they tell the user what to do, not just that nothing is here.
Inline alerts
Empty state
| Class | Description |
|---|---|
| .alert | Inline contextual alert — flex row, always add a semantic variant |
| .alert-info / .alert-success / .alert-warning / .alert-danger | Semantic color variants |
| .alert-body / .alert-title / .alert-desc | Content wrapper, bold heading, softer description |
| .empty-state | Centered flex column for zero-data states — always include an action |
| .empty-icon / .empty-title / .empty-desc | 40px icon box, heading, instructional copy (max ~30ch) |
| .status-tag + variants | Inline status with dot — same semantic variants as .tag |
| .status-dot | 5px circle, color: currentColor — inherits from .status-tag variant |
| .popover | Fixed 260px overlay panel — prefer over modals for detail views |
| .toast | Fixed bottom-center notification — 0.2s opacity + translate transition |
Entity cards
Three entity types — Playable, Game, and Auditor — each represented at two scales: a compact task row for dense lists, and a collapsible entity card for grouped views. All states shown.
Playable task row
Compact row used in Dispatch backlog, Pulse status view, and Delivery table. Height 44px. States communicate urgency without adding visual weight.
Game entity card
Groups all tasks for a game. Expandable — collapsed shows summary stats, expanded shows individual task rows. Square avatar with partner badge.
Auditor entity card
Groups tasks by auditor. Shows capacity bar inline in the header. Circle avatar (person). Used in TL Dispatch and TL Pulse.
| Class | Description |
|---|---|
| .task-list | Container for task rows — bordered, rounded, clips overflow |
| .task-row | 44px flex row — default state for assigned/in-progress tasks |
| .task-row.needs-review | Amber wash — task waiting for sign-off (pending-pg or pending-tl) |
| .task-row.stalled | Light amber wash — no movement 2+ days |
| .task-row.failed | Red wash — build failed or critical error |
| .task-row.selected | Accent wash — row linked to open detail panel |
| .task-row-type / -name / -title / -sub / -meta / -actions | Internal layout slots — type chip | name block | meta badges | hover actions |
| .task-row-id / .task-row-game | Mono sub-labels in the name block second row |
| .entity-card | Collapsible group card — add .collapsed to collapse the body |
| .entity-card.selected | Accent border + accent-wash header — linked to open detail panel |
| .entity-card-head / -body | Always-visible header / hideable content area |
| .entity-av / .entity-av-sq / .entity-av-badge / .entity-av-circle | Avatar wrapper, square (games), partner badge, circle (people) |
| .entity-name / .entity-sub | Primary label and secondary line in entity header |
| .entity-stats / .entity-stat / .entity-stat-n / .entity-stat-l | Stat cluster — number + label, add .warn/.danger/.good to color the number |
| .entity-chevron | Collapse/expand indicator — rotates -90° when .collapsed on card |
| .entity-detail-btn | Panel-open icon button in card header — use stopPropagation to not trigger collapse |
| .cap-bar / .cap-bar-track / .cap-bar-fill | Capacity bar — auditor load indicator. Add .ok / .warn / .over to fill |
Detail panel
Docked right panel (320px) that slides open when an entity row or card is clicked. Three entity types — Playable, Game, Auditor — each with their own head, actions, and body sections. One panel at a time.
Three entity types
Panels are rendered side-by-side here for comparison. In the product, only one is visible at a time — docked to the right edge of the main content area.
| Class | Description |
|---|---|
| .detail-panel | 320px flex column, docked right — add .hidden to collapse (width → 0) |
| .dp-head | Panel header — relative positioned to allow absolute .dp-close button |
| .dp-close | Absolute top-right × button — always present |
| .dp-type | Mono uppercase label — e.g. "Task · PLY-018" or "Game" |
| .dp-title-row / .dp-title / .dp-sub | Title row (icon + name), subtitle line below |
| .dp-body | Scrollable flex column — gap 20px between .dp-section items |
| .dp-section / .dp-section-label | Named section with 9px uppercase mono label |
| .dp-actions / .dp-action | Action button row — variants: .primary / .secondary / .ghost |
| .dp-meta / .dp-meta-key / .dp-meta-val | 2-column key/value grid — auto-width keys, 1fr values |
| .dp-preview / .dp-preview-play | 110px playable thumbnail with centered play button — provide a background gradient |
| .dp-timeline / .dp-tl-item / .dp-tl-dot / .dp-tl-event / .dp-tl-when | Vertical timeline — dot colors: .green / .amber / .red |
| .dp-pipeline / .dp-pipe-box / .dp-pipe-n / .dp-pipe-l | Pipeline stats boxes — add .warn/.danger to box for border color, .warn/.danger/.good to number for text color |