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.
83.6%
3,728 shared elements against 729 raw primitives that have a shared equivalent
93.6%
19,290 token classes against 1,320 fixed colours (palette 70, hex 420, white/black 814, inline 16)
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.
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.
| Component | Example | Files | Elements | Replaces | Lint |
|---|---|---|---|---|---|
Avatar | JL JL | 3 | 10 | <span class="avatar">, <img> initials | documented only |
Badge | successwarningerrorinfo | 91 | 451 | rounded-full span with fixed colours | rule asks for it |
Breadcrumbs | 19 | 21 | <nav class="breadcrumb"> | documented only | |
Button | 242 | 1387 | <button>, <a class="btn"> | rule asks for it | |
Card | Card titleHeader, content, footer. Content | 81 | 324 | bespoke bordered panels | documented only |
CardContent | Card titleHeader, content, footer. Content | 10 | 48 | panel body | documented only |
CardDescription | Card titleHeader, content, footer. Content | 2 | 4 | panel sub-heading | documented only |
CardFooter | Card titleHeader, content, footer. Content | 3 | 5 | panel action row | documented only |
CardHeader | Card titleHeader, content, footer. Content | 9 | 37 | panel header rows | documented only |
CardTitle | Card titleHeader, content, footer. Content | 9 | 35 | panel heading | documented only |
CodeBlock | npm run lint:compliance | 20 | 86 | <pre><code> | documented only |
CollapsibleSection | Collapsed by default. | 2 | 4 | <details>/<summary> | documented only |
ColorSwatch | brand-primary | 3 | 33 | token preview squares | documented only |
DatePicker | 5 | 8 | <input type="date|datetime-local"> | rule asks for it | |
DeleteConfirmModal | 13 | 19 | window.confirm before a delete | documented only | |
Divider | 3 | 7 | <hr> | documented only | |
Dots | 3 | 7 | loading ellipsis | documented only | |
DropdownMenu | 1 | 1 | <div class="menu"> | documented only | |
DropdownMenuItem | 1 | 3 | <button class="menu-item"> | documented only | |
DropdownMenuLabel | 1 | 1 | <div class="menu-label"> | documented only | |
DropdownMenuSeparator | 1 | 1 | <div class="menu-separator"> | documented only | |
EmptyState | No traces yetSpans appear once a turn completes. | 6 | 7 | inline "No results yet" blocks | documented only |
IdChip | no example yet | 1 | 2 | β | documented only |
Input | 113 | 405 | <input> (text-like types), <textarea> | rule asks for it | |
Label | 5 | 5 | <label> | documented only | |
MetricCard | Traces 1,133+12 this week | 4 | 14 | StatCard duplicates, stat tiles | documented only |
Modal | 58 | 75 | <dialog>, bespoke fixed overlays | rule asks for it | |
MultiSelect | Steward | 3 | 3 | <select multiple> | rule asks for it |
OrderedList |
| 3 | 3 | <ol> | documented only |
Pagination | Page 1 of 3 | 3 | 3 | page-btn rows | documented only |
PrimaryTooltip | hover | 5 | 13 | brand-tinted tooltip | documented only |
ProgressBar | 62% | 5 | 6 | <div class="progress">, <progress> | documented only |
RadioGroup | 3 | 3 | <input type="radio"> groups | documented only | |
RadioGroupItem | 3 | 8 | <input type="radio"> | documented only | |
SecondaryTooltip | hover | 1 | 1 | secondary-tinted tooltip | documented only |
Select | 71 | 122 | <select> | rule asks for it | |
Spinner | 30 | 49 | any element with animate-spin | rule asks for it | |
StatusBadge | no example yet | 1 | 1 | β | documented only |
Tabs | Overview | 4 | 4 | TabNav, button rows with aria-selected | documented only |
Timeline | no example yet | 1 | 1 | β | documented only |
Tooltip | hover | 14 | 17 | title attributes, hover popovers | documented only |
Typography | Small body text, from the type scale. | 73 | 598 | headings with ad-hoc size classes | documented only |
Shown on your public profile.
That is not a valid address.
| Operation | Model | Tokens | Status |
|---|---|---|---|
| tool_call | qwen3:14b | 12,480 | success |
| research | qwen3:14b | 148,002 | success |
| vision | gemma3:27b | 3,120 | warning |
| consensus | deepseek-r1:32b | 96,410 | error |
Why shadows are absent
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.