Skip to content

Components

j3w1 edited this page Sep 8, 2026 · 2 revisions

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

The inventory

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.

Where one component lives

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.

Reading the contract

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.txt

Each 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.

States and the state matrix

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 is color.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 disabled or invalid appearances 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.

Keyboard and ARIA

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 workbench

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:

  1. Open a component — button is the easiest first look — and keep Profile on default.
  2. Step through every Variant and Initial state.
  3. 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.
  4. Try a Text fixture: a long label, an RTL string, Arabic or CJK text. This is where a design that assumed English breaks.
  5. 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.
  6. Use the contrast lab on a declared pair. Copy the result with Copy contrast result; it carries the context needed to discuss it.
  7. 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.

Recipes

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 a component

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

Clone this wiki locally