Repository navigation
Components
Read a component's contract, check your implementation against it, and know what the maintained example does and does not prove.
Contents: The inventory · Where a component lives · Reading the contract · States · Keyboard and ARIA · The workbench · Recipes · Adding one
48 components, every one specified and demonstrated; 11 have browser tests implemented. The inventory declares nine families; eight of them carry components, since foundations is the token layer rather than a set of widgets.
| Family | Components |
|---|---|
| actions | button, icon-button, link |
| forms-basic | field, fieldset, text-field, textarea, number-field, search-field, select, checkbox, radio-group, switch |
| forms-advanced | combobox, date-picker, time-picker, file-input, wizard, form-builder |
| navigation | breadcrumbs, menu, pagination, sidebar-nav, skip-link, tabs, toolbar |
| display | badge, card, chip, data-table, list, table |
| feedback | alert, dialog, drawer, empty-state, loading-indicator, progress, toast, tooltip |
| developer | code-editor, diagnostics, diff-view, terminal |
| composed | admin-form, filterable-table, i3-window-frame, settings-panel |
The 11 with tests implemented: button, checkbox, dialog, form-builder, link, menu, radio-group, select, table, tabs, text-field. Remember what that phrase means — a test exists. Whether it ran and passed is a separate question with a separate answer in the verification report.
The live ledger is generated into the README coverage table and exports/coverage.json.
Take text-field. Five files, each owning a different thing:
| File | Owns |
|---|---|
spec/components/text-field.md |
The contract: frontmatter with variants, states, token map, contrast pairs, anatomy, keyboard, portability; then the prose that explains the intent |
spec/components/text-field.demo.html |
The maintained demo fragment every rendered specimen is built from |
site/src/styles/components/text-field.css |
The reference CSS — token variables only, no literal colours |
exports/components/text-field.json |
The generated machine contract, with every role resolved to a value and an eligibility |
exports/components/text-field.brief.txt |
The same thing as plain text, for a small context budget |
The .md and the demo are the sources; the two exports are generated. Never edit an export.
exports/components/<id>.json is the file to implement against. The fields that matter:
| Field | What you do with it |
|---|---|
variants |
The supported versions. Implement the ones your task names; do not invent a fifth tone. |
states |
Every state the component must handle. Your implementation is measured per state. |
tokens |
The map from anatomy part to role, with the resolved value, status and eligibility inlined |
stateTokens |
The declared foreground / background / outline per state — what a contrast check measures |
contrast |
Every declared pair with its floor, kind (text or ui), state and any waiver with its reason |
anatomy |
The named parts and what each one is |
keyboard |
The key-by-key contract |
aria |
The pattern, and the WAI-ARIA Authoring Practices link it follows |
fixtures |
The stress cases the component is expected to survive |
coverage |
specified, demonstrated, testImplemented, state count |
release, sourceDigest, anchors
|
Which revision this contract came from, and where its human page is |
The plain-text brief carries the same token map in one screen:
curl -fsSL https://raw.githubusercontent.com/j3w1/theme/v0.1.0/exports/components/text-field.brief.txtEach token line shows the part, the resolved value, the role it aliases, and the eligibility action — use or use-and-report. If any line says use-and-report, its decision ID has to appear in your deviation report.
The states are where implementations fail, so the specification is explicit about combinations, not just the resting appearance. The site renders a state matrix for every declared combination — including selected + focus-visible and invalid + focus-visible, because those are exactly where focus rings disappear in practice.
Two rules to hold on to:
-
The ring is drawn on top of whatever fill is showing, in the ring role that fill declares. On a filled control that is usually
color.interaction.focus.ring-container; on an outline control it iscolor.interaction.focus.ring; on a destructive fill it is the on-fill text colour. The geometry — 1px dashed, offset −2px, or −4px inside a 2px invalid border — does not change with the colour. -
A forced visual state is not behaviour. The matrix renders
disabledorinvalidappearances by attribute so they can be inspected side by side. A specimen that looks disabled is not evidence that any application implements disabling, and a specimen that looks invalid is not evidence of validation. Those are separate claims with separate evidence.
Native controls keep native keyboard behaviour — that is why the button specification says <button type="button"> and not a div with role="button". Custom widgets follow the APG pattern named in their aria block: roving tabindex in toolbars, tabs, trees and listboxes; aria-activedescendant in comboboxes; focus trapping and Escape in dialogs and drawers; arrow keys in radio groups and menus.
Positive tabindex is banned. Every icon-only control has an accessible name. Focus is never lost and never invisible.
The component workbench is the confirmation tool: live controls for button, text field, checkbox, tabs and dialog, plus table and sidebar-navigation previews. A useful pass:
- Open a component — button is the easiest first look — and keep Profile on
default. - Step through every Variant and Initial state.
- Set a narrow Viewport preset (360px), then turn on the comparison frame and set it to 1280px. The two frames can differ in density, direction and text fixture. Compare wrapping and spacing.
- Try a Text fixture: a long label, an RTL string, Arabic or CJK text. This is where a design that assumed English breaks.
- Use Inspect anatomy part and read Anatomy and measurements. It separates the declared role, the resolved value and the browser's actual measurement, and it reports mismatches without correcting the specimen — a mismatch is a finding, not a rendering artefact to ignore.
- Use the contrast lab on a declared pair. Copy the result with Copy contrast result; it carries the context needed to discuss it.
- Replay a motion transition if the component declares one. Your system's reduced-motion setting takes priority — barely any movement may be the correct outcome.
Share this configuration produces a link that reproduces the starting configuration and identifies the build and source digest it came from. Review Review structured payload before you copy: entered label and help text are excluded unless you explicitly include them. Nothing is written to browser storage. If the build a shared link names is no longer available, the tool says so rather than silently rendering something else — keep a short written description alongside any link that matters.
The workbench, like the whole site, is confirmation. It does not certify accessibility, and it does not replace a keyboard or screen-reader pass.
The recipe viewer packages button, text-field and dialog as standalone HTML and CSS generated directly from the maintained demo and stylesheet — usable without Astro or a build step. Details, file order and the instance-prefix rule are in Web integration.
The dialog recipe deserves its own warning: it is an open visual reference. Modal lifecycle, close actions, focus trapping and focus return are host responsibilities, and no behaviour module is bundled.
Adding one is allowed without a decision entry as long as every token reference resolves to an already-approved role. Start from templates/component/, write the .md and the demo fragment, add the reference CSS with token variables only, then run the check loop. A new role, a new colour, or a change to the focus, selection or contrast rules needs a decision first. See Contributing.
Next: Forms · Accessibility · Verification
Wiki home · Agent workflow · Portal · Vue demo · v1.1.0 release
This handbook explains consumption of v1.1.0. The pinned repository's tokens, specification, implementation contracts and evidence remain authoritative. The live site may advance; keep your application's pin explicit. Preserve the material's license notices.