Compass component visual contracts (D3)
This document plus the CSS in design/components/ is the component visual
contract: the class vocabulary, the data-* variant modifiers, the states
each component renders, and the --cx-* semantic tokens it consumes. It is the
human-readable contract compass-ui implements against.
This side owns what renders. SolidJS props, reactivity, and
@compass/client wiring are compass-ui’s side of the seam and are not
specified here.
Conventions across the set:
- Class +
data-*variant modifiers, mirroring the existing codebase convention (.state-dot[data-state=…],data-status,data-priority). - Every interactive component renders six mandatory, uniform states: rest,
hover, active, selected, disabled, focus. Focus is always
:focus-visible→--cx-focus-ring; hover uses--cx-bg-hover; disabled is 45% opacity +cursor: default(never a color swap that breaks status semantics). - Three surface tiers: cards, rows, and list cells render on
--cx-bg-panel; selection is carried by--cx-bg-selected+ the accent left rule, never a raised background. - Every value is a
--cx-*semantic or scale token — no raw hex, no--rigel-*, no literal duration/easing/z-index (enforced bycx-token-gate).
Button
Section titled “Button”- Class:
.cx-btn - Variants:
data-variant="primary | ghost | danger",data-size="sm | md",data-selected(toggle/segmented). - States: rest / hover (
--cx-bg-hover) / active (--cx-bg-active) / selected (--cx-bg-selected+ accent border) / disabled (45%) / focus (--cx-focus-ring). - Tokens:
--cx-bg-panel,--cx-bg-hover,--cx-bg-active,--cx-bg-selected,--cx-accent,--cx-accent-hover,--cx-accent-muted,--cx-error,--cx-border,--cx-text,--cx-text-bright,--cx-bg,--cx-font-ui,--cx-text-xs/-sm,--cx-space-1/-2/-3,--cx-radius-md,--cx-motion-fast,--cx-ease-out,--cx-focus-ring.
Primary is an accent fill; ghost is borderless with a hover wash; danger uses
--cx-error.
Input / Search / Select / Composer
Section titled “Input / Search / Select / Composer”- Classes:
.cx-input,.cx-search,.cx-select,.cx-composer. - States: rest / hover / active (select) / selected (
data-selected) / disabled (45%) / focus. Focus swaps the border to--cx-border-focusand renders--cx-focus-ring. - Tokens:
--cx-bg-panel,--cx-bg-hover,--cx-bg-active,--cx-border,--cx-border-focus,--cx-accent,--cx-text,--cx-text-bright,--cx-text-faint,--cx-font-ui,--cx-text-sm,--cx-space-2/-3/-6/-8,--cx-radius-md,--cx-motion-fast,--cx-ease-out,--cx-focus-ring.
The composer is multi-line and grows to a 40vh cap; Enter / Shift-Enter is
compass-ui behaviour (this owns the visual box).
- Class:
.cx-card·data-selected,data-disabled. - States: rest / hover / active / selected (
--cx-bg-selected+ accent left rule) / disabled (45%) / focus. - Tokens:
--cx-bg-panel,--cx-bg-hover,--cx-bg-active,--cx-bg-selected,--cx-accent,--cx-border,--cx-text,--cx-text-bright,--cx-font-ui,--cx-text-sm,--cx-space-3,--cx-radius-md,--cx-motion-fast,--cx-ease-out,--cx-focus-ring.
Issue / PR / backlog rows on the panel tier; selection is the selection surface plus the accent left rule, never a raised background.
Badge / Pip / Chip
Section titled “Badge / Pip / Chip”- Classes:
.cx-badge·data-status,.cx-pip,.cx-chip. - Badge (display):
data-statusmaps CI (ci-pass | ci-fail | ci-pending) and review (review-approved | review-changes | review-pending) to--cx-ci-*/--cx-review-*color + border. - Pip (display): unread-count / attention indicator on
--cx-accent. - Chip (interactive): tracker chip with all six states.
- Tokens:
--cx-bg-panel,--cx-bg-hover,--cx-bg-active,--cx-bg-selected,--cx-accent,--cx-border,--cx-text,--cx-text-dim,--cx-text-bright,--cx-ci-pass/-fail/-pending,--cx-review-approved/-changes/-pending,--cx-bg,--cx-font-ui,--cx-text-xs,--cx-space-1/-2/-4/-5,--cx-radius-sm,--cx-motion-fast,--cx-ease-out,--cx-focus-ring.
Axis badge — .cx-axis-badge (RIG-2117 / RIG-2121, shipped variant)
Section titled “Axis badge — .cx-axis-badge (RIG-2117 / RIG-2121, shipped variant)”- Classes:
.cx-axis-badge·data-axis(ci | review) ·data-status·data-compact; contains.cx-axis-code(theCI/RVmono label) and a 9×9.glyphSVG. - What it is: the frozen Option B — a fixed 2-char axis code in the mono UI
face followed by a 9×9 1-bit pixel-art status glyph. The wrapper’s
data-axis+data-statusis the single source of truth: it setscolor, which paints both the code text and (viacurrentColor) the glyph fill. The inner glyph SVG carries NO owndata-status/data-verdict— it selects its shape off the wrapper via CSS descendant selectors. - Statuses: CI
success | pending | failure; reviewapproved | changes | commented. - Color routing:
[data-axis="ci"][data-status="success"]→--cx-ci-pass,pending→--cx-ci-pending,failure→--cx-ci-fail;[data-axis="review"][data-status="approved"]→--cx-review-approved,changes→--cx-review-changes,commented→--cx-review-pending. - Compact:
.cx-axis-badge[data-compact] .cx-axis-code { display: none; }— the glyph-only fallback for cramped surfaces (IssueCard); Bridge / Done rows show the code. - Geometry: each glyph is a 9×9 1-bit grid,
shape-rendering="crispEdges", one<rect width="1" height="1" fill="currentColor">per lit cell. - Tokens:
--cx-ci-pass/-pending/-fail,--cx-review-approved/-changes/-pending,--cx-font-ui,--cx-text-xs,--cx-space-1.
The six axis-badge glyphs (canonical 9×9 grids)
Section titled “The six axis-badge glyphs (canonical 9×9 grids)”# = lit cell, . = off; one CSS px per cell.
ci-success — check tick (reuses the state-dot done grid):
...................................#.......#.#.....#...#...#.....#.#.......#.....ci-pending — ellipsis (three 2×2 dots, “running…”):
....................................##.##.##.##.##.##............................ci-failure — full X cross:
#.......#.#.....#...#...#.....#.#.......#.......#.#.....#...#...#.....#.#.......#review-approved — a thick/bold check (the “extra-affirmed” tick):
..........................#.......####....##..##..##....####......##.............review-changes — delta (hollow triangle, the change mark):
.............#........#.......#.#......#.#.....#...#....#...#...#######..........review-commented — speech bubble with tail:
..........#######..#.....#..#.....#..#######....#.......#........................Back-port to rigel.build (follow-up, Q5)
Section titled “Back-port to rigel.build (follow-up, Q5)”The public rigel.build DS docs still show the earlier flat-dot badge form.
Porting this axis-badge variant (the code + 9×9 glyph grids above) to that
surface is tracked as a follow-up, out of scope for the Compass adoption
(RIG-2117 Q5). Until then, the grids here are canonical.
State dot
Section titled “State dot”- Class:
.cx-state-dot·data-state,data-alive="1"(working only). - States (agent process axis):
working | idle | waiting | done | paused | stopped | error | disconnected— the eightAgentStatevalues, keyed 1:1 against the frozen brand vocabulary. - Geometry: a 9×9 1-bit bitmap grid,
shape-rendering="crispEdges"(no anti-aliasing). Glyph geometry is fixed by the grids below;compass-uiemits identical inline SVG. - Color: per-state via
--cx-st-*(resolved throughcurrentColor). - Motion: only
workinganimates — the working pulse at--cx-pulse-periodin the working-state green--cx-st-working, never the purple phosphor--cx-pulse-color. The other seven are static, distinguished by glyph + color alone (CVD-safe, reduced-motion-safe). - Tokens:
--cx-st-working/-idle/-waiting/-done/-paused/-stopped/-error/ -disconnected,--cx-pulse-period,--cx-ease-out.
The eight frozen state glyphs (canonical 9×9 grids)
Section titled “The eight frozen state glyphs (canonical 9×9 grids)”# = lit cell, . = off. The dot box is 9px (one CSS px per grid cell —
razor-crisp per the brand 1-bit whole-cell rule); it sits within the ~12px
agent-row height. Each grid reads correctly at that size (the reason working
is a double-chevron, not a ring — brand state-icons.md).
Render size (Matt-ruled, RIG-2118): shipped as a 9px crisp dot box (one CSS px per grid cell) within the ~12px agent row. The earlier open question — 9px vs an 18px integer-multiple — is resolved: 9px is the intended shipped size.
working — double-chevron » (fast-forward); the ONLY animated state
(--cx-st-working):
.........#...#.....#...#.....#...#.....#...#...#...#...#...#...#...#.............idle — 3×3 block (--cx-st-idle):
..............................###......###......###..............................waiting — ? (--cx-st-waiting):
..####....#....#........#.......#.......#........#.................#.............done — check-tick (--cx-st-done):
...................................#.......#.#.....#...#...#.....#.#.......#.....paused — two bars (--cx-st-paused):
....................##.##....##.##....##.##....##.##....##.##....................stopped — hollow square outline (--cx-st-stopped):
....................#####....#...#....#...#....#...#....#####....................error — ! (--cx-st-error):
.............#........#........#........#........#.................#.............disconnected — broken square outline (--cx-st-disconnected):
....................##.##....#...#.............#...#....##.##....................Glyphs
Section titled “Glyphs”- Classes: none of its own — the
<Glyph name>primitive renders a bare inline SVG that the consuming control positions and colors. Adopted first by the right-sidebar activity bar (.r-tab .r-tab-icon[data-kind="glyph"]). - Geometry: an 11×11 1-bit bitmap grid,
shape-rendering="crispEdges"(no anti-aliasing); one<rect width="1" height="1">per lit cell, filled oncurrentColorso the consumer’s color flows through. 11×11 (not the state dot’s 9×9) gives a true center cell and enough cells for pictographs. - Accessibility: glyphs are decorative —
aria-hidden="true", norole="img"and noaria-label. A name-bearing label belongs on the consuming control, never on the glyph (this is the deliberate difference from the axis badge, whose glyph carries status meaning). - Names: the closed set is
status | files | vcs | pr— the four static activity-bar tabs. The set grows semantic names (never character names) as the chrome audit converts further sites.
The four canonical glyph grids (11×11)
Section titled “The four canonical glyph grids (11×11)”# = lit cell, . = off; one CSS px per cell. Coordinates are [x, y] with
x = column (0..10), y = row (0..10), origin top-left.
status — a status grid (replaces ▦): four cells in a 2×2 block:
............####.####..####.####..####.####..####.####.............####.####..####.####..####.####..####.####............files — a folder (replaces 🗀): a tab over a wider body:
.......................####.......####.......#########..#########..#########..#########..#########..#########............vcs — a version-control branch (replaces ⎇): a trunk with a branch
diverging to a top-right node:
............##....##...##...###...##..##.....#####......###........##.........##.........##.........##...................pr — two opposing horizontal arrows (replaces ⇄): right over left:
...................#...#########.........#.....................................#.........#########...#...................T5b glyph grids (RightSidebar + AgentView, 11×11)
Section titled “T5b glyph grids (RightSidebar + AgentView, 11×11)”cross — a bold ballot X (replaces ✗, the changes-requested verdict mark):
............##.....##..###...###...###.###.....#####.......###.......#####.....###.###...###...###..##.....##............close — a thin X close mark (replaces ✕, the pane/tab close affordance):
.#.......#..##.....##...##...##.....##.##.......###.........#.........###.......##.##.....##...##...##.....##..#.......#.neutral — a centered 3×3 dot (replaces •, the commented verdict mark):
.....................................###........###........###...........................................................split-right — a box divided by a vertical rule (replaces ⊞▏, split-right):
............#########..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..#########............split-down — a box divided by a horizontal rule (replaces ⊞▁, split-down):
............#########..#.......#..#.......#..#.......#..#########..#.......#..#.......#..#.......#..#########............T5a glyph grids (LeftSidebar + App, 11×11)
Section titled “T5a glyph grids (LeftSidebar + App, 11×11)”disclosure — a right-pointing triangle (replaces ▸, the closed disclosure
caret; CSS rotates it 90° open):
..............#..........##.........###........####.......#####......####.......###........##.........#..................disclosure-open — a down-pointing triangle (replaces ▼, the expanded
disclosure caret):
.......................#########...#######.....#####.......###.........#.................................................check — a check tick (replaces ✓, the Done view mark):
...............................#.........##........##...##...##.....##.##.......###.........#............................role — a filled diamond (replaces ◆, the non-worker role pip):
...........................#.........###.......#####.....#######.....#####.......###.........#...........................pin — a filled push-pin (replaces ★, the pinned agent state):
..............#####......#####......#####......#####......#####........#..........#..........#...........................pin-outline — a hollow push-pin (replaces ☆, the unpinned agent state):
..............#####......#...#......#...#......#...#......#####........#..........#..........#...........................subscribed — a filled disc (replaces ◉, subscribed / always-subscribed):
...............###.......#####.....#######....#######....#######....#######....#######.....#####.......###...............unsubscribed — a hollow ring (replaces ○, joined-not-subscribed):
...............###.......#####.....#######....##...##....##...##....##...##....#######.....#####.......###...............list — a stacked-rows list (replaces ▤, the Backlog view):
............#########..#.......#..#########..#.......#..#########..#.......#..#########..#.......#..#########............gear — a settings cog (replaces ⚙, the Settings view):
...............###........###.....#########..###...###..###...###..###...###..#########.....###........###...............logo — the Compass diamond mark (replaces ◇, the brand logo):
................#.........###.......##.##.....##...##...##.....##...##...##.....##.##.......###.........#................panel-left — a pane frame with the left region filled (replaces ▌, the
toggle-left-sidebar control):
............#########..#####...#..#####...#..#####...#..#####...#..#####...#..#####...#..#####...#..#########............panel-right — a pane frame with the right region filled (replaces ▐, the
toggle-right-sidebar control):
............#########..#...#####..#...#####..#...#####..#...#####..#...#####..#...#####..#...#####..#########............Chrome conversion audit (T5a + T5b)
Section titled “Chrome conversion audit (T5a + T5b)”Every chrome site converted from a character to a <Glyph>, with the
accessibility decision each one forced. <Glyph> is always aria-hidden, so a
name-bearing site is one where the character WAS the accessible name and the
name had to be moved onto a wrapper or control; a decorative site already had
one from adjacent text or an aria-label.
All sites below are decorative — the control or its neighbouring text already carries the name — except the two marked name-bearing, where the character WAS the name and it moved onto the wrapper.
| Site | Was | Glyph |
|---|---|---|
App brand mark |
◇ |
logo |
App view tab |
▦ |
status |
App sidebar toggles |
▌ ▐ |
panel-left panel-right |
LeftSidebar role pip |
◆ |
role |
LeftSidebar pin toggle |
★ ☆ |
pin pin-outline |
LeftSidebar folder caret |
▼ |
disclosure-open |
LeftSidebar browse/ws carets |
▸ |
disclosure |
LeftSidebar subscribe toggle |
◉ ○ |
subscribed unsubscribed |
LeftSidebar views |
▦ ▤ ✓ ⚙ |
status list check gear |
RightSidebar file row |
▸ |
disclosure |
RightSidebar repo/branch |
🗀 ⎇ |
files vcs |
RightSidebar dropdown carets |
▾ |
disclosure-open |
AgentView pane split |
⊞▏ ⊞▁ |
split-right split-down |
AgentView pane/tab close |
✕ |
close |
Name-bearing — the glyph is hidden, so the wrapper carries role="img" plus an
aria-label:
| Site | Was | Glyph | Announces |
|---|---|---|---|
LeftSidebar always-subscribed |
◉ |
subscribed |
Always subscribed |
RightSidebar verdicts |
✓ ✗ • |
check cross neutral |
verdict |
Kept as text, covered by the brand face — not conversions:
| Site | Char | Why |
|---|---|---|
RightSidebar file row |
· U+00B7 |
covered; a glyph would misalign |
| diff stats (3 sites) | − U+2212 |
in the cmap; pairs with ASCII + |
The two name-bearing rows are the hazard this table exists to catch: a bare
✓/✗ verdict mark reads as nothing once the glyph is hidden, so those sites
carry the verdict word on the wrapper. RightSidebar.prpane.test.tsx pins that.
- Class:
.cx-tabs·data-orientation="h | v", with.cx-tabitems (data-selected). - States (per tab): rest / hover / active / selected (accent underline for
h, accent left rule forv) / disabled (45%) / focus. - Tokens:
--cx-bg-hover,--cx-bg-active,--cx-accent,--cx-border,--cx-text,--cx-text-dim,--cx-text-bright,--cx-font-ui,--cx-text-sm,--cx-space-1/-2/-3,--cx-motion-fast,--cx-ease-out,--cx-focus-ring.
Topbar view-tabs, right-sidebar activity bar, workspace tab strip.
Panel / Pane
Section titled “Panel / Pane”- Classes:
.cx-panel,.cx-pane·data-focused. - States: a focused pane draws an accent 1px inner rule (the spatial-focus
marker); interactive children own their own
:focus-visiblering. - Tokens:
--cx-bg-panel,--cx-accent,--cx-border,--cx-text,--cx-font-ui,--cx-text-sm,--cx-radius-md,--cx-motion-base,--cx-ease-out.
The workspace’s two fixed panes (home channel · session trace). Focus is carried by the accent rule, not a raised background.
Tree row
Section titled “Tree row”- Class:
.cx-tree-row·data-depth(0–5),data-selected,data-disabled; children.cx-tree-caret,.cx-tree-pin. - States: rest / hover (row wash + pin reveal) / active / selected
(
--cx-bg-selected+ accent left rule) / disabled (45%) / focus. Height is 26px. - Tokens:
--cx-bg-hover,--cx-bg-active,--cx-bg-selected,--cx-accent,--cx-text,--cx-text-bright,--cx-text-faint,--cx-font-ui,--cx-text-sm,--cx-space-2/-3/-4/-5/-7/-8,--cx-motion-fast,--cx-ease-out,--cx-focus-ring.
Agent tree + channel/topic rows; caret, state dot, pin affordance.
Menu (Kobalte)
Section titled “Menu (Kobalte)”- Class:
.cx-menu, with.cx-menu-item(data-highlighted,data-selected,data-disabled). - States (per item): rest / hover (or Kobalte
data-highlighted) / active / selected / disabled (45%) / focus. The float is elev-2 on the panel surface; keyboard is WAI-ARIA via Kobalte, styled entirely by our classes. - Tokens:
--cx-bg-panel,--cx-bg-hover,--cx-bg-active,--cx-bg-selected,--cx-border,--cx-text,--cx-text-bright,--cx-font-ui,--cx-text-sm,--cx-space-1/-2/-8,--cx-radius-sm/-md,--cx-elev-2,--cx-z-overlay,--cx-motion-fast,--cx-ease-out,--cx-focus-ring.
Dialog (Kobalte)
Section titled “Dialog (Kobalte)”- Class:
.cx-dialog, with.cx-dialog-backdrop(scrim). - States: elev-3 float above the scrim (
--cx-scrim); focus-trap is Kobalte. Interactive children own their focus rings. - Tokens:
--cx-bg-panel,--cx-scrim,--cx-border-strong,--cx-text,--cx-font-ui,--cx-text-sm,--cx-space-4/-5,--cx-radius-lg,--cx-elev-3,--cx-z-modal.
Command palette (Kobalte combobox)
Section titled “Command palette (Kobalte combobox)”- Classes:
.cx-palette(the float container), with.cx-palette-input(query field),.cx-palette-backdrop(scrim + positioner),.cx-palette-list(results scroll region),.cx-palette-group(section header),.cx-palette-row(result row) ·data-selected,data-highlighted,data-disabled, and the row sub-parts.cx-palette-glyph,.cx-palette-title,.cx-palette-context,.cx-palette-shortcut. Plus.cx-palette-empty(no-results row) and.cx-palette-loading(loader host). - One surface, two modes, prefix-free. A single input over the command
registry with no mode sigil. Action mode fuzzy-searches registered
commands (
{ id, title, keywords, scope, shortcut?, run() }); scoped commands rank above global when their scope is active, and each shows its shortcut chip. Navigation mode — bare typing, no>prefix — matches both commands and destinations ({ id, title, kind, navigate(), score? },kind ∈ agent | channel | topic | issue | pr | view). Async destination providers rank by recency + fuzzy score; the weighting iscompass-ui’s and is not encoded here. - Result-row anatomy (left→right):
.cx-palette-glyph(a 9px 1-bit type glyph — the command icon or destinationkind) ·.cx-palette-title(the label, takes free space and truncates) ·.cx-palette-context(dim secondary text — scope / path / parent,--cx-text-dim) ·.cx-palette-shortcut(the right-aligned key-hint chip). - States: rest / hover (
--cx-bg-hover, shared by Kobaltedata-highlighted) / selected (--cx-bg-selected+ accent left rule) / disabled (45%) / focus (--cx-focus-ring)..cx-palette-groupis a dim uppercase label row (--cx-text-faint), never selectable. Empty state is a dim “no results” row (.cx-palette-empty); loading hosts the chase-light bar.cx-loader[data-topology="bar"](D9) under the input via.cx-palette-loading— the keyframe is T8, referenced not authored here. - Geometry: panel is elev-3 on
--cx-bg-panel, 560px wide (fluid to100%within the window), positioned in the top third of the viewport by the backdrop (horizontally centered,20vhtop offset — a reach-for-it surface, not a dead-center modal). Rows mirror the.cx-tree-rowdensity (26px height, same padding rhythm) so palette and tree read as one row system. - Host: a Kobalte combobox;
Cmd/Ctrl+Kopens it at--cx-z-palette. ARIA + keyboard traversal are Kobalte’s; this owns only the visual surface. - Tokens:
--cx-bg-panel,--cx-bg-hover,--cx-bg-selected,--cx-scrim,--cx-accent,--cx-border,--cx-border-strong,--cx-border-focus,--cx-text,--cx-text-bright,--cx-text-dim,--cx-text-faint,--cx-font-ui,--cx-text-xs/-sm/-lg,--cx-space-1/-2/-3/-4,--cx-radius-sm/-lg,--cx-elev-3,--cx-z-palette,--cx-motion-fast,--cx-ease-out,--cx-focus-ring.
Tooltip (Kobalte)
Section titled “Tooltip (Kobalte)”- Class:
.cx-tooltip. - Consumer:
CoachTip(components/CoachTip.tsx) — the shipped label+chord coaching tooltip adopted across the command-backed chrome (topbar Bridge tab, the four LeftSidebar view buttons, the two sidebar toggles).CoachTipContentrenders the control’s label, then its chord resolved from the keymap viashortcutFor(never hand-authored): a plus-chord reusesShortcutChip, a" then "leader sequence renders as plain text, and a command with no keymap row is label-only. - States: display surface (elev-1); open delay
--cx-tooltip-delay(400ms) is Kobalte’s timing prop. Never load-bearing — the same info is reachable elsewhere (a converted control keeps itsaria-keyshortcuts). - Tokens:
--cx-bg-raised,--cx-border,--cx-text,--cx-font-ui,--cx-text-xs,--cx-space-1/-2,--cx-radius-sm,--cx-elev-1,--cx-z-overlay.
- Class:
.cx-toast·data-kind="info | ok | warn | error". - States: display surface (elev-2), bottom-right stack at
--cx-z-toast;data-kindsets a status accent left rule without swapping the surface. A dismiss control is a.cx-btn. - Tokens:
--cx-bg-panel,--cx-border,--cx-border-strong,--cx-info,--cx-ok,--cx-warn,--cx-error,--cx-text,--cx-font-ui,--cx-text-sm,--cx-space-2/-3,--cx-radius-md,--cx-elev-2,--cx-z-toast.
Ask block
Section titled “Ask block”- Class:
.cx-ask·data-answered, with.cx-ask-optionsand.cx-btnoption buttons. - States: open (accent left rule pulling the eye) → answered (rule drops,
options dim to 45%, the chosen option’s
.cx-btn[data-selected]stays lit). Option buttons follow the.cx-btnstate contract. - Tokens:
--cx-bg-panel,--cx-accent,--cx-border,--cx-border-strong,--cx-text,--cx-text-dim,--cx-font-ui,--cx-text-sm,--cx-space-2/-3/-4,--cx-radius-md.
Markdown content
Section titled “Markdown content”- Class:
.cx-md— defined inbase.css(T2); NOT redefined here. Shiki via the--cx-ed-*editor theme (T7), GFM tables, mention chips. Listed for completeness of the D3 set.
Loader (spinner / bar)
Section titled “Loader (spinner / bar)”- Class:
.cx-loader·data-topology="spinner | bar", with.cx-loader-fill(bar). - Spinner: closed square loop, indeterminate work; the accent head travels the loop at the base cadence.
- Bar: open track, blue fill + fog head, determinate progress driven by
--cx-loader-value(0–1) thecompass-uiside sets; no indeterminate bar. - Non-purple loading palette. The one sanctioned purple spinner (the
brand-mark-in-motion moment) lives in the mark component (
mark*.css), not here — purple is never aliased into--cx-*. The full keyframe / boot-sequence spec is T8; this owns the class contract and consumes the D9 motion tokens. - Tokens:
--cx-accent,--cx-border,--cx-text,--cx-space-1/-4,--cx-radius-sm,--cx-motion-slow,--cx-motion-base,--cx-ease-out.
Scrollbar
Section titled “Scrollbar”- Global — defined in
base.css(T2); NOT redefined here. Thin (8px), thumb--cx-border-strong, track transparent. Listed for completeness of the D3 set.