Component Suite

Every primitive, driven by tokens. Change the theme and nothing below should need to know. The compliance panel says how much of the app already agrees.

Compliance
How much of app/ and components/ goes through this suite. Measured on 2026-09-28 over 567 files (2 exempt, this page among them).
+2.7 pts since baseline 2026-09-27gate passes
Shared-component usage

83.6%

3,728 shared elements against 729 raw primitives that have a shared equivalent

+2.7
vs baseline 80.9%
Palette compliance

93.6%

19,290 token classes against 1,320 fixed colours (palette 70, hex 420, white/black 814, inline 16)

+0.3
vs baseline 93.3%
Baseline

80.9%

Recorded 2026-09-27. Committed in eslint-rules/compliance-baseline.json and only ever raised: npm run lint:compliance:gate fails below it.

83.6%
Baseline history2026-09-27 Β· 80.9% / 93.3%

Usage = shared elements Γ· (shared + raw primitives the suite can replace). Palette = token colour classes Γ· (tokens + fixed colours). The rule behind it is local/no-raw-primitive; the law it enforces is .agent/laws/law-frontend-styling.md. How to raise the baseline or allow-list a file: docs/frontend-design-system.md.

Mode
Theme Audit
Real computed contrast, every theme Γ— both modes Γ— 11 critical pairs. WCAG AA is 4.5:1. Runs entirely in this browser.
Component index
Every export of @sovereignty/ui, where it is used, and the raw primitive it replaces. 42 components, 0 never rendered, 3 without an example yet.
rule asks for itdocumented only
ComponentExampleFilesElementsReplacesLint
Avatar
JL
JL
310<span class="avatar">, <img> initialsdocumented only
Badge
successwarningerrorinfo
91451rounded-full span with fixed coloursrule asks for it
Breadcrumbs1921<nav class="breadcrumb">documented only
Button
2421387<button>, <a class="btn">rule asks for it
Card

Card title

Header, content, footer.

Content
81324bespoke bordered panelsdocumented only
CardContent

Card title

Header, content, footer.

Content
1048panel bodydocumented only
CardDescription

Card title

Header, content, footer.

Content
24panel sub-headingdocumented only
CardFooter

Card title

Header, content, footer.

Content
35panel action rowdocumented only
CardHeader

Card title

Header, content, footer.

Content
937panel header rowsdocumented only
CardTitle

Card title

Header, content, footer.

Content
935panel headingdocumented only
CodeBlock
npm run lint:compliance
2086<pre><code>documented only
CollapsibleSection

Collapsed by default.

24<details>/<summary>documented only
ColorSwatch
brand-primary
333token preview squaresdocumented only
DatePicker
58<input type="date|datetime-local">rule asks for it
DeleteConfirmModal1319window.confirm before a deletedocumented only
Divider

37<hr>documented only
Dots37loading ellipsisdocumented only
DropdownMenu
11<div class="menu">documented only
DropdownMenuItem
13<button class="menu-item">documented only
DropdownMenuLabel
11<div class="menu-label">documented only
DropdownMenuSeparator
11<div class="menu-separator">documented only
EmptyState

No traces yet

Spans appear once a turn completes.

67inline "No results yet" blocksdocumented only
IdChipno example yet12β€”documented only
Input
113405<input> (text-like types), <textarea>rule asks for it
Label55<label>documented only
MetricCard
Traces

1,133

+12
this week
414StatCard duplicates, stat tilesdocumented only
Modal5875<dialog>, bespoke fixed overlaysrule asks for it
MultiSelect
Steward
33<select multiple>rule asks for it
OrderedList
  • Lint
  • Score
  • Gate
33<ol>documented only
Pagination
Page 1 of 3
33page-btn rowsdocumented only
PrimaryTooltiphover513brand-tinted tooltipdocumented only
ProgressBar
62%
56<div class="progress">, <progress>documented only
RadioGroup
33<input type="radio"> groupsdocumented only
RadioGroupItem
38<input type="radio">documented only
SecondaryTooltiphover11secondary-tinted tooltipdocumented only
Select
71122<select>rule asks for it
Spinner
3049any element with animate-spinrule asks for it
StatusBadgeno example yet11β€”documented only
Tabs
Overview
44TabNav, button rows with aria-selecteddocumented only
Timelineno example yet11β€”documented only
Tooltiphover1417title attributes, hover popoversdocumented only
TypographySmall body text, from the type scale.73598headings with ad-hoc size classesdocumented only
Buttons β€” the .btn classes
Text on the filled variants is derived from the fill, not hard-coded white. Rendered through <Button unstyled>, the passthrough for a bespoke look; the component's own variants are in the index above.
Form controls
Text fields, selects and textareas are the suite's Input and Select. Checkbox, radio and switch have no component yet, so the .checkbox/.radio/.switch classes are shown bare.

Shown on your public profile.

That is not a valid address.

Badges, dots and chips
The direct replacement for bg-green-100 text-green-800.
successwarningerrorinfoneutralbrand
successwarningerrorinfobrand
successwarningerrorinfoneutral
laws Γ—skills Γ—
Alerts
●
success
Body copy takes the page foreground β€” a paragraph of coloured text is harder to read, and the fill already carries the meaning.
●
warning
Body copy takes the page foreground β€” a paragraph of coloured text is harder to read, and the fill already carries the meaning.
●
error
Body copy takes the page foreground β€” a paragraph of coloured text is harder to read, and the fill already carries the meaning.
●
info
Body copy takes the page foreground β€” a paragraph of coloured text is harder to read, and the fill already carries the meaning.
●
neutral
Body copy takes the page foreground β€” a paragraph of coloured text is harder to read, and the fill already carries the meaning.
Table
OperationModelTokensStatus
tool_callqwen3:14b12,480success
researchqwen3:14b148,002success
visiongemma3:27b3,120warning
consensusdeepseek-r1:32b96,410error
Tabs, breadcrumb and pagination
Progress, spinner and stats
Documents indexed
1,133
+12
Vectors cached
1,133
0
Failed embeds
0
βˆ’3
Avatars, menu and accordion
JLJLJLABC
Why shadows are absent
The stylesheet strips box-shadow globally, so depth is carried by the surface ladder instead. An elevation token expressed as a shadow would be a token that can never render.
Surface ladder
Nesting decides the colour. Nothing here names a token.

Each .layer takes whichever of the two ladder values its parent is not. Move a block, or wrap it in another, and it repaints itself β€” there is nothing to sweep.

layer 1 β€” surface
layer 2 β€” recessed
layer 3 β€” surface again
layer 4 β€” recessed again
layer 5 β€” forced elevated (escape hatch)
States and dividers
Empty
β—‡
No traces yet
Spans appear here once a chat turn completes.

β–²
Could not reach the model
Recall fell back to keyword-only, which is the previous behaviour.