Window gaps
Inner 14px, outer −2px; the bar sits above the gap and the border is 1px.
True-black canvas. Warm-dark panels. Rose text. Red focus. One monospace family. Square components. This page is the complete visual specification of the j3w1 identity; agents read the exports linked at the end instead.
version 1.1.0build 1faefcfprofiles default (approved) heritage-ansi (heritage) extended (proposed) sources j3w1/j3w1.github.io@ffa8122 j3w1/1w3j@9841315 github.com/j3w1/themeToken usage explorerRelease comparison
What the j3w1 theme is, where it came from, and the five rules that keep a port recognisable.
True-black canvas (#000000), near-black panels (#100c0c) and chrome (#090707), with dark-red raised layers, rose interface text (#e99499), near-white reading text (#f4eeee), dark-red selections (#531310), a bright red focus ring (#e53935), 1px square borders, zero radii, one monospace family for chrome, reading and code. Red marks interaction: focus, selection, active borders, primary actions. It is not a decoration and it is not applied to everything. Darkness is the canvas; readable content is the priority.
Five rules keep an implementation recognisable:
The palette began as a Manjaro i3 workstation preserved in j3w1/1w3j: a pywal
ramp pulled from a red wallpaper, then frozen and hand-tuned slot by slot in
config/Xresources while watching what ls, vim and the agnoster prompt
actually rendered. The i3 configuration supplied the rest of the idiom: 1px
pixel borders, 14/−2 gaps, tabbed layouts, a hidden top bar with Chinese
status labels, dunst toasts framed in #ae1914, dmenu selections in
#630f0d.
The contemporary reference is j3w1/j3w1.github.io, a working web
reinterpretation of that workstation. Its stylesheet named the roles this
specification adopts (--terminal, --surface-raised, --selection,
--focus, --prose, --muted …) and wrote the first contrast contract. Both
sources are pinned by revision in references/sources.json; after adoption
this repository is the authority and later changes in either source are
proposals, not automatic updates.
Clear organisation, responsive behaviour, strong keyboard and screen-reader support, precisely specified states, and controls that are pleasant to operate. It does not mean pill buttons, glass panels, gradients, neon glows, a Material-style redesign, or red on every surface. Sixteen concrete misreadings are rendered as labelled non-examples on the site.
The specification page’s generated literal-color previews have the narrow geometry, checkerboard and motion exception recorded in D-014; the component and consumer rules remain as written.
default — the approved everyday composition. Monochrome red/rose for
chrome, text, borders, interaction and actions; three bounded hues (amber,
green, blue) for status, diagnostics and diffs (D-001); monochrome syntax.heritage-ansi — the sixteen Xresources slots and the historical
assignments, exact, including the ones that fail the contrast floor. For
terminals, archives and fidelity; never for new interface text.extended — a proposed overlay that colours syntax, a full sixteen-slot
semantic terminal palette and chart series with the same three hues. Not
approved; the site labels it.Roles are color.<group>.<role> with a closed group list: surface, text,
border, interaction, action, status, code, diagnostic, diff,
terminal, chart, icon. Primitives are color.primitive.<family>.<step>
where the step rises with luminance. A CSS custom property is the path with
dots replaced by dashes: color.interaction.focus.ring →
--color-interaction-focus-ring. Roles alias primitives; primitives carry
provenance.
Surfaces, text ladder, borders, interaction states, focus, typography, spacing, density, elevation, motion, icons and layout, with the rules that govern each role.
Values live in tokens/; this document says what each role means and where it
may be used. Every rule below is normative for the default profile.
surface.desktop and surface.canvas use true black (#000000). Readable
content sits on the canvas or default (#100c0c) panels and cards, raised (#160b0b) for sticky headers,
menus and popovers, overlay for dialogs and drawers, sunken (#000000)
for reading and detail panes, input for form-control fills, chrome and
chrome-alt for window frames and bars. Layering is subtle by design; it may
not erase the boundary of a control, which is why controls also carry
border.control.
Never tint photographs, video, document content or user-defined data colours to put more red on screen.
| Role | Value | Contrast on canvas / default / raised | Use |
|---|---|---|---|
text.prose | #f4eeee | 18.31 / 16.96 / 15.83 | long-form reading, on-fill text |
text.bright | #ffa2a7 | 10.98 / 10.17 / 9.49 | headings and emphasis |
text.default | #e99499 | 9.16 / 8.48 / 7.92 | interface and body text |
text.muted | #bd787d | 6.15 / 5.69 / 5.31 | secondary text; the darkest colour permitted for chrome text |
text.subtle | #ad7175 | 5.40 / 5.00 / 4.66 | metadata, comments, line numbers |
text.disabled | #8a5559 | 3.52 / 3.26 / 3.05 | disabled controls only (exempt under WCAG 1.4.3; at least 3:1 by policy) |
text.accent | #e53935 | 4.97 / 4.60 / 4.30 | accent text on canvas and default surfaces only |
text.accent-strong | #f73f35 | 5.72 / 5.29 / 4.94 | accent text safe on every surface |
Ordinary text must reach 4.5:1 against its actual background in every state.
#a3676b and #7d1310 are never text roles in default, even when a
particular darker background improves their ratio; they
remain in service as border.control, icon.decorative, status.neutral.fill
and border.disabled. Metadata and comments that a person needs to read are
not decorative and use text.subtle, not a graphic colour.
The specification page’s generated literal-color previews have the narrow geometry, checkerboard and motion exception recorded in D-014; the component and consumer rules remain as written.
Every border is 1px (border.width.default); 2px (border.width.emphasis) is
reserved for the selected indicator bar, the sticky-header rule and the invalid
border. The quote rule is the one wider edge, at twice the emphasis width,
because it marks a block rather than bounding a control. border.divider (#2b0e0d) separates rows and sections;
border.default (#531310) is decorative and may never be the only visible
edge of a control; border.control (#a3676b, 4.71:1) is the boundary of
every form control at rest; border.active (#e53935) marks the focused or
active control; border.overlay (#e53935) frames every floating layer.
Styles: solid by default; dashed only for focus, drop targets and read-only
fields; dotted for hints; double for conflicts. Radius is 0 everywhere,
including checkboxes, switches, chips, avatars, badges and tooltips.
Every interactive component declares which of these apply and demonstrates every applicable one, including the combinations. Precedence when several apply: disabled > loading > invalid > selected > pressed > hover; focus-visible is always drawn on top and never suppressed by another state.
| State | Visual | Non-colour channel |
|---|---|---|
| default | rest tokens | — |
hover (@media (hover: hover) only) | background → interaction.hover.bg; links use it too with text.link-hover, menu items → interaction.hover.bg-strong | link underline thickens 1px → 2px; cursor |
| active / pressed | background → interaction.pressed.bg; border → border.active | border change; no translation |
| focus-visible | the ring (below) | the ring itself; never a glow or a colour shift alone |
| selected | fill interaction.selection.bg with interaction.selection.text; tabs and navigation use a 2px border.selected-indicator | aria-selected / aria-current; a check glyph in lists |
| selected + focus-visible | the selection fill and a ring in the fill’s on-fill colour (interaction.focus.ring-container, 7.48:1 on the selection) | both visible at once |
| selected, container inactive | interaction.selection.inactive-bg with interaction.selection.inactive-text; no ring | lightness drop between the two fills |
| disabled | text.disabled, border.disabled, interaction.disabled.bg; no hover or pressed styling; still focusable when aria-disabled | disabled / aria-disabled; cursor: not-allowed; never opacity |
| invalid | border → status.danger.border at 2px; message in status.danger.text with the ✕ glyph; aria-invalid; aria-describedby | border width 1 → 2px; glyph; message |
| invalid + focus-visible | the 2px danger border and the ring in interaction.focus.ring-container at focus.offset-invalid | double boundary |
| read-only | text.default; 1px dotted border.divider bottom; no fill; focusable; selectable | readonly; dotted edge |
| loading | size unchanged; a static ⋯ glyph replaces the leading icon; aria-busy | glyph; no animation under reduced motion |
| busy / indeterminate | static 45° stripes of action.primary.bg on interaction.pressed.bg; aria-valuetext | stripes |
| checked / mixed | fill action.primary.bg, glyph ✓ or – in action.primary.text | glyph |
| expanded / collapsed | SVG chevron rotated 0 / 90° within the motion budget | aria-expanded |
| current | 2px border.selected-indicator and text.bright | indicator bar |
| drop-target / dragging | dashed interaction.drop-target border; interaction.marquee fill | dashed pattern |
Rules that apply everywhere:
#e53935 measures 2.52:1 on action.primary.bg and 1.13:1 on status.danger.fill, which is why the ring recolours on those fills instead of relying on hue. It measures 3.38:1 on the interaction.selection.bg recessed by D-027, so a selected row keeps the ordinary control ring.outline: 1px dashed {color.interaction.focus.ring}; outline-offset: -2px. Focusable containers (panes, windows, dialogs, cards): outline: 2px solid {color.interaction.focus.ring-container}; outline-offset: -3px. On filled surfaces use the component’s explicit ring mapping: primary buttons and checked controls use interaction.focus.ring-container where declared; destructive buttons use their declared on-fill text role. The selection fill is the exception and takes the ordinary ring, on the measurement in rule 1. Keep the control or container geometry independently of the colour mapping. :focus:not(:focus-visible) draws nothing. Forced-colors mode uses Highlight. A host focus indicator is never removed without this replacement.✕ ! ✓ i ·), a border width, an underline pattern, or an ARIA state. Charts carry pattern fills.prefers-reduced-motion: reduce every duration is 0.01 ms, nothing slides or shimmers, and no behaviour waits on transitionend.Four button tones. primary is the only filled button at rest
(action.primary.bg #7d1310 with text.prose, 9.28:1); secondary is an
outline (border.control, text text.bright); tertiary is a ghost or icon
button (text.default, hover interaction.hover.bg); destructive is an
outline in action.destructive.text (#f73f35) that fills with
action.destructive.hover-bg (#dc282e) and #f9faf9 text (4.58:1) on
hover, and a filled variant for confirm dialogs. Destructive actions also
require a confirmation step or an undo affordance; the colour is not the
safeguard.
Five statuses: danger (#f73f35 text, #dc282e fill), warning
(#c9973f), success (#86a46f), info (#7e9ebb), neutral (#bd787d
text, #a3676b fill). Each has text, fill, on-fill, tint and border
roles, a mandatory glyph (✕ ! ✓ i ·) and a distinct underline or border
pattern (wavy, dashed, solid, dotted, double). The three extension hues are
desaturated and warm-shifted so they sit inside the identity, all between 6.5:1
and 7.6:1 on every surface, and are forbidden in surface, text,
border, interaction and action. Purple, cyan and magenta do not exist in
the theme.
Diagnostics: error = wavy #e53935 underline, #f73f35 text, #e53935
stripe, #2b0e0d line background; warning = wavy amber; info = dotted blue;
hint = dotted text.subtle; unused = text.muted with a dotted underline.
Diffs: added = #0f1a0e line background, green gutter mark and +,
word-level additions as a 2px green underline (no fill, so text contrast is
untouched); removed = #2b0e0d background, red gutter mark and −, word-level
removals underlined and struck through; modified = #1f1a0c background, amber
gutter mark and ~; conflicts = a double #f73f35 border. Textual markers and
existing status icons are always preserved.
Editor surfaces use code.bg (#0c0909), code.current-line (#1c0a09),
code.line-number (text.subtle, replacing the site’s #7d1310), caret
#e99499, selection #420f0c (muted text stays 4.71:1 inside it), search
matches #630f0d with the current match inverted (#ffa2a7 on #0c0909),
bracket match as a 1px #e53935 border, indent guides #531310, rendered
whitespace #7d1310 (decorative). Syntax in default is monochrome:
keywords #f73f35, strings #bd787d, comments #ad7175 italic, numbers and
constants #d4868b, functions #ffa2a7, variables and operators #e99499,
types #b37175, properties and headings #e53935, tags #f73f35,
attributes and escapes #d4868b; invalid text takes a wavy underline,
deprecated text a line-through. Languages inherit these roles; a
language-specific override needs a documented reason.
Terminal roles carry the sixteen slots exactly in every profile. Slots
1, 4, 6, 8, 9, 10, 12, 13, 14 and 15 do not reach 4.5:1 on the terminal
background (slot 15, #a3676b, measures 4.45:1; slot 0 is the background
itself); the theme documents this rather than normalising it, because
programs, not the theme, choose which slot means what. Inside an editor
selection (code.selection-bg, #420f0c) muted text stays at 4.71:1 but the
keyword, type, property and comment roles fall below 4.5:1; a selection is a
transient state and the limitation is recorded, not hidden. The extended overlay
proposes a semantic sixteen-slot palette that all reaches 4.5:1.
Links use strong red text.link (#f73f35) and a persistent underline, distinct
from the rose body and heading ladder (D-024). Hover uses near-white
text.link-hover on interaction.hover.bg (D-027; the strong fill read louder
than the link itself and stays with menu items). Current-page and selected
navigation use their explicit indicator and on-fill roles.
One family: SauceCodePro NFM (Source Code Pro through the Nerd Fonts build),
falling back to Source Code Pro, Cascadia Mono, Consolas, Liberation Mono,
DejaVu Sans Mono and monospace. The site records size-adjust metrics for
the fallbacks; no font file is distributed. Weights 400 and 700 plus italic.
Scale (size/line-height in px): ui-sm 12/16, ui-md 13/18, ui-lg 14/20,
reading 15/24, code 13/19, terminal 13/19 with −0.5px letter-spacing,
h1 20/28 bold, h2 16/24 bold, h3 13/18 bold in text.accent, caption
11/16 only with text.muted or lighter. Hosts keep the user’s chosen size;
tiny UI text is a reference-implementation metric, not a rule.
Spacing steps: 0, 1, 2, 4, 8, 12, 16, 24, 32, 48 px. Two density modes applied
with data-density on any subtree: compact (control 24px, row 28px, ui-sm
type, 8px horizontal padding, 2px gaps) matches the workstation; comfortable
(control 32px, row 36px, ui-md type, 12px padding, 4px gaps) suits everyday
applications. Both meet the 24×24 CSS-pixel target minimum; compact requires
4px between adjacent targets. Table rows are exempt and always take the
comfortable row height: a data row is content rather than chrome, and
compacting it costs scannability where it is needed most. Controls inside a
row still follow the page density. The site’s 3px i3 gap, 14/−2 window gaps and
28px bar are window-manager geometry, not theme tokens.
There are no shadows except shadow.floating (0 6px 22px rgb(0 0 0 / 55%))
on the i3 floating-window specimen. Floating layers are a 1px border.overlay
on surface.raised; modals add surface.backdrop (rgb(0 0 0 / 65%)).
Stacking: canvas 0 < raised 1 < popover 10 < drawer 20 < dialog 30 < toast 40 <
skip link 1000.
A popup opened by hovering follows the pointer: it sits 8px from the cursor, flips to the other side of it rather than crossing a viewport edge, keeps an 8px margin from every edge, and is repositioned on pointer move, scroll and resize. A popup with nothing to operate takes no pointer events, so it cannot stand between the cursor and what opened it; one that carries a control or a link stays reachable and holds while the pointer is inside it. Either closes when the pointer leaves both the trigger and the popup.
The same popup opened from the keyboard anchors to its trigger instead — below it, or above when there is no room below — because focus has no pointer to follow. This is a placement rule only: what opens a popup, and whether it is a tooltip, a preview or an inspector, is the component’s own contract.
SVG line icons on a 16px grid, 1.5px stroke, stroke: currentColor,
fill: none, sizes 12 / 16 / 20. No icon font is required; Nerd Font glyphs
appear only in terminal and heritage specimens. Icon-only controls carry
aria-label and a tooltip. icon.decorative (#a3676b) is for glyphs that
convey nothing on their own.
Reading measure 72ch, content column 680px, application layout 1440px. Breakpoints 320 (reflow floor), 600, 900, 1280. Components use container queries; sidebars collapse to drawers below 900px; data tables scroll horizontally with a sticky first column below 600px; the page never scrolls horizontally at 320px. Right-to-left mirrors breadcrumb separators, pagination, sidebar and drawer sides, wizard progression, affixes and tree indentation.
raised#160b0bapproveduse — Sticky headers, menus, popovers; D-027 recessed it so an open list reads as depth rather than a lighter panel.placeholder#a3676bapproveduse — Placeholder text, italic; D-027 darkened it to 4.71:1, just clear of the 4.5:1 floor.link#f73f35approveduse — Strong red link text, distinct from rose body text and headings; retain the underline.Measured ratios; ordinary text needs 4.5:1, disabled text is exempt but held to 3:1.
The four combinations every list, table and tab strip must show. Focus rings on fills take the on-fill text colour.
Each status has text, fill, tint and border roles and a mandatory glyph. The three extension hues (D-001) exist only here, in diagnostics, in diffs and in the proposed overlay.
The sixteen slots are exact in every profile; only the proposed extended overlay reassigns them. Use the page's profile control to switch specimens, or read the columns.
| Slot | default | heritage-ansi | extended |
|---|---|---|---|
ansi.0 | #0c0909 | #0c0909 | #0c0909proposed |
ansi.1 | #c81a1a | #c81a1a | #e53935proposed |
ansi.2 | #bd787d | #bd787d | #86a46fproposed |
ansi.3 | #d4868b | #d4868b | #c9973fproposed |
ansi.4 | #8c1212 | #8c1212 | #7e9ebbproposed |
ansi.5 | #f73f35 | #f73f35 | #b37175proposed |
ansi.6 | #9e474a | #9e474a | #bd787dproposed |
ansi.7 | #ffa2a7 | #ffa2a7 | #e99499proposed |
ansi.8 | #7d1310 | #7d1310 | #ad7175proposed |
ansi.9 | #ab1612 | #ab1612 | #f73f35proposed |
ansi.10 | #ad2721 | #ad2721 | #7fbf7aproposed |
ansi.11 | #b37175 | #b37175 | #d6a24bproposed |
ansi.12 | #871f19 | #871f19 | #9ab3c9proposed |
ansi.13 | #e82132 | #e82132 | #d4868bproposed |
ansi.14 | #e0292f | #e0292f | #ffa2a7proposed |
ansi.15 | #a3676b | #a3676b | #f4eeeeproposed |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.border.divider | #2b0e0dproposeduse-and-reportD-008 | #2b0e0dproposedhistorical-onlyD-008 | #2b0e0dproposedblockedD-008 | Hairline between rows and sections. |
color.border.default | #531310observeduse | #531310observedhistorical-only | #531310observedblocked | Decorative border (1.39:1; never the only edge of a control). |
color.border.strong | #9e231fobserveduse | #9e231fobservedhistorical-only | #9e231fobservedblocked | Emphasised 2px rule: sticky headers, section ends. |
color.border.control | #a3676bproposeduse-and-reportD-007 | #a3676bproposedhistorical-onlyD-007 | #a3676bproposedblockedD-007 | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
color.border.active | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Active or focused control border. |
color.border.overlay | #e53935proposeduse-and-reportD-008 | #ae1914heritagehistorical-only | #e53935proposedblockedD-008 | Every floating layer's 1px border. |
color.border.disabled | #7d1310observeduse | #7d1310observedhistorical-only | #7d1310observedblocked | Disabled control border (exempt). |
color.border.selected-indicator | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | 2px indicator bar on the selected tab or nav item. |
color.border.selected-indicator-inactive | #9e231fobserveduse | #9e231fobservedhistorical-only | #9e231fobservedblocked | Indicator bar when the container is not focused. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.interaction.focus.ring | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | 1px dashed focus ring on controls and rows. |
color.interaction.focus.ring-container | #ffa2a7observeduse | #ffa2a7observedhistorical-only | #ffa2a7observedblocked | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
color.interaction.selection.bg | #531310approveduse | #531310approvedhistorical-only | #531310approvedblocked | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
color.interaction.selection.text | #f4eeeeobserveduse | #f4eeeeobservedhistorical-only | #f4eeeeobservedblocked | Text on the selection. |
color.interaction.selection.inactive-bg | #420f0cobserveduse | #420f0cobservedhistorical-only | #420f0cobservedblocked | Selection while the container is not focused. |
color.interaction.selection.inactive-text | #e99499observeduse | #e99499observedhistorical-only | #e99499observedblocked | Text on the inactive selection (7.02:1). |
color.interaction.text-selection.bg | #911410observeduse | #911410observedhistorical-only | #911410observedblocked | ::selection background. |
color.interaction.text-selection.text | #f4eeeeobserveduse | #f4eeeeobservedhistorical-only | #f4eeeeobservedblocked | ::selection text. |
color.interaction.hover.bg | #1c0a09observeduse | #1c0a09observedhistorical-only | #1c0a09observedblocked | Hover on rows, ghost buttons, list items. |
color.interaction.hover.bg-strong | #630f0dobserveduse | #630f0dobservedhistorical-only | #630f0dobservedblocked | Hover on menu items; D-027 moved links to interaction.hover.bg. |
color.interaction.pressed.bg | #420f0cobserveduse | #420f0cobservedhistorical-only | #420f0cobservedblocked | Pressed fill. |
color.interaction.disabled.bg | #160b0bobserveduse | #160b0bobservedhistorical-only | #160b0bobservedblocked | Disabled control fill. |
color.interaction.marquee | rgb(145 20 16 / 12%)observeduse | rgb(145 20 16 / 12%)observedhistorical-only | rgb(145 20 16 / 12%)observedblocked | Drag marquee and ghost. |
color.interaction.drop-target | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Dashed drop-target border. |
color.interaction.scrollbar.track | #0c0909observeduse | #0c0909observedhistorical-only | #0c0909observedblocked | Scrollbar track. |
color.interaction.scrollbar.thumb | #420f0cobserveduse | #420f0cobservedhistorical-only | #420f0cobservedblocked | Scrollbar thumb. |
color.interaction.scrollbar.thumb-hover | #911410observeduse | #911410observedhistorical-only | #911410observedblocked | Scrollbar thumb on hover. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.action.primary.bg | #7d1310approveduse | #7d1310approvedhistorical-only | #7d1310approvedblocked | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
color.action.primary.text | #f4eeeeobserveduse | #f4eeeeobservedhistorical-only | #f4eeeeobservedblocked | Primary button text (9.28:1). |
color.action.primary.hover-bg | #911410observeduse | #911410observedhistorical-only | #911410observedblocked | Primary button hover (7.92:1 with prose). |
color.action.primary.pressed-bg | #630f0dobserveduse | #630f0dobservedhistorical-only | #630f0dobservedblocked | Primary button pressed. |
color.action.primary.border | #9e231fobserveduse | #9e231fobservedhistorical-only | #9e231fobservedblocked | Primary button border. |
color.action.secondary.bg | rgb(0 0 0 / 0%)observeduse | rgb(0 0 0 / 0%)observedhistorical-only | rgb(0 0 0 / 0%)observedblocked | Secondary button has no fill. |
color.action.secondary.text | #ffa2a7observeduse | #ffa2a7observedhistorical-only | #ffa2a7observedblocked | Secondary button text. |
color.action.secondary.border | #a3676bobserveduse | #a3676bobservedhistorical-only | #a3676bobservedblocked | Secondary button border (meets 3:1). |
color.action.secondary.hover-bg | #630f0dobserveduse | #630f0dobservedhistorical-only | #630f0dobservedblocked | Secondary button hover. |
color.action.secondary.pressed-bg | #420f0cobserveduse | #420f0cobservedhistorical-only | #420f0cobservedblocked | Secondary button pressed. |
color.action.tertiary.text | #e99499observeduse | #e99499observedhistorical-only | #e99499observedblocked | Ghost and icon button text. |
color.action.tertiary.hover-bg | #1c0a09observeduse | #1c0a09observedhistorical-only | #1c0a09observedblocked | Ghost and icon button hover. |
color.action.destructive.text | #f73f35observeduse | #f73f35observedhistorical-only | #f73f35observedblocked | Destructive button text at rest (outline style). |
color.action.destructive.border | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Destructive button border. |
color.action.destructive.hover-bg | #dc282eobserveduse | #dc282eobservedhistorical-only | #dc282eobservedblocked | Destructive button hover fill. |
color.action.destructive.hover-text | #f9faf9observeduse | #f9faf9observedhistorical-only | #f9faf9observedblocked | Destructive button text on hover (4.58:1). |
color.action.destructive.pressed-bg | #ae1914observeduse | #ae1914observedhistorical-only | #ae1914observedblocked | Destructive button pressed fill. |
color.action.destructive.filled-bg | #dc282eobserveduse | #dc282eobservedhistorical-only | #dc282eobservedblocked | Confirm-dialog destructive fill. |
color.action.destructive.filled-text | #f9faf9observeduse | #f9faf9observedhistorical-only | #f9faf9observedblocked | Text on the destructive fill. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.status.danger.text | #f73f35observeduse | #f73f35observedhistorical-only | #f73f35observedblocked | Danger text (5.40:1). |
color.status.danger.fill | #dc282eobserveduse | #dc282eobservedhistorical-only | #dc282eobservedblocked | Danger fill. |
color.status.danger.on-fill | #f9faf9observeduse | #f9faf9observedhistorical-only | #f9faf9observedblocked | Text on the danger fill (4.58:1). |
color.status.danger.tint | #2b0e0dobserveduse | #2b0e0dobservedhistorical-only | #2b0e0dobservedblocked | Danger background tint. |
color.status.danger.border | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Danger border, 2px on invalid inputs. |
color.status.warning.text | #c9973fapproveduse | #c9973fapprovedhistorical-only | #c9973fapprovedblocked | Warning text (7.54:1). |
color.status.warning.fill | #c9973fapproveduse | #c9973fapprovedhistorical-only | #c9973fapprovedblocked | Warning fill. |
color.status.warning.on-fill | #0c0909approveduse | #0c0909approvedhistorical-only | #0c0909approvedblocked | Text on the warning fill (7.54:1). |
color.status.warning.tint | #1f1a0capproveduse | #1f1a0capprovedhistorical-only | #1f1a0capprovedblocked | Warning background tint. |
color.status.warning.border | #c9973fapproveduse | #c9973fapprovedhistorical-only | #c9973fapprovedblocked | Warning border. |
color.status.success.text | #86a46fapproveduse | #86a46fapprovedhistorical-only | #86a46fapprovedblocked | Success text (7.13:1). |
color.status.success.fill | #86a46fapproveduse | #86a46fapprovedhistorical-only | #86a46fapprovedblocked | Success fill. |
color.status.success.on-fill | #0c0909approveduse | #0c0909approvedhistorical-only | #0c0909approvedblocked | Text on the success fill (7.13:1). |
color.status.success.tint | #0f1a0eapproveduse | #0f1a0eapprovedhistorical-only | #0f1a0eapprovedblocked | Success background tint. |
color.status.success.border | #86a46fapproveduse | #86a46fapprovedhistorical-only | #86a46fapprovedblocked | Success border. |
color.status.info.text | #7e9ebbapproveduse | #7e9ebbapprovedhistorical-only | #7e9ebbapprovedblocked | Info text (7.08:1). |
color.status.info.fill | #7e9ebbapproveduse | #7e9ebbapprovedhistorical-only | #7e9ebbapprovedblocked | Info fill. |
color.status.info.on-fill | #0c0909approveduse | #0c0909approvedhistorical-only | #0c0909approvedblocked | Text on the info fill (7.08:1). |
color.status.info.tint | #0f141capproveduse | #0f141capprovedhistorical-only | #0f141capprovedblocked | Info background tint. |
color.status.info.border | #7e9ebbapproveduse | #7e9ebbapprovedhistorical-only | #7e9ebbapprovedblocked | Info border. |
color.status.neutral.text | #bd787dobserveduse | #bd787dobservedhistorical-only | #bd787dobservedblocked | Neutral status text. |
color.status.neutral.fill | #a3676bobserveduse | #a3676bheritagehistorical-only | #a3676bobservedblocked | Neutral fill (dunst urgency_low). |
color.status.neutral.on-fill | #000000observeduse | #0c0909heritagedeprecatedhistorical-only | #000000observedblocked | Text on the neutral fill (4.71:1). |
color.status.neutral.tint | #241010observeduse | #241010observedhistorical-only | #241010observedblocked | Neutral background tint. |
color.status.neutral.border | #a3676bobserveduse | #a3676bobservedhistorical-only | #a3676bobservedblocked | Neutral border. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.code.bg | #0c0909observeduse | #0c0909observedhistorical-only | #0c0909observedblocked | Editor background. |
color.code.current-line | #1c0a09observeduse | #1c0a09observedhistorical-only | #1c0a09observedblocked | Current line background. |
color.code.gutter-bg | #0c0909observeduse | #0c0909observedhistorical-only | #0c0909observedblocked | Gutter background. |
color.code.line-number | #ad7175approveduse | #7d1310heritagedeprecatedhistorical-only | #ad7175approvedblocked | Line numbers (5.10:1; replaces the site's #7d1310). |
color.code.line-number-active | #e99499observeduse | #e99499observedhistorical-only | #e99499observedblocked | Line number of the current line. |
color.code.gutter-rule | #2b0e0dobserveduse | #2b0e0dobservedhistorical-only | #2b0e0dobservedblocked | Rule between gutter and text. |
color.code.caret | #e99499observeduse | #e99499observedhistorical-only | #e99499observedblocked | Caret (Xresources *cursorColor). |
color.code.selection-bg | #420f0cobserveduse | #420f0cobservedhistorical-only | #420f0cobservedblocked | Editor selection background (muted text stays 4.71:1). |
color.code.search-match-bg | #630f0dobserveduse | #630f0dobservedhistorical-only | #630f0dobservedblocked | Other search matches. |
color.code.search-current-bg | #ffa2a7observeduse | #ffa2a7observedhistorical-only | #ffa2a7observedblocked | Current search match background. |
color.code.search-current-text | #0c0909observeduse | #0c0909observedhistorical-only | #0c0909observedblocked | Current search match text. |
color.code.bracket-match | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Matching bracket 1px border. |
color.code.indent-guide | #531310observeduse | #531310observedhistorical-only | #531310observedblocked | Indent guides. |
color.code.whitespace | #7d1310observeduse | #7d1310observedhistorical-only | #7d1310observedblocked | Rendered whitespace (decorative). |
color.code.syntax.keyword | #f73f35observeduse | #f73f35observedhistorical-only | #f73f35observedblocked | Keywords (Xresources color5 lineage). |
color.code.syntax.string | #bd787dobserveduse | #bd787dobservedhistorical-only | #86a46fproposedblockedD-001 | Strings (color2 lineage). |
color.code.syntax.comment | #ad7175approveduse | #7d1310heritagedeprecatedhistorical-only | #ad7175approvedblocked | Comments, italic (color8 lineage, corrected for contrast). |
color.code.syntax.number | #d4868bobserveduse | #d4868bobservedhistorical-only | #c9973fproposedblockedD-001 | Numbers. |
color.code.syntax.constant | #d4868bobserveduse | #d4868bobservedhistorical-only | #c9973fproposedblockedD-001 | Constants and booleans. |
color.code.syntax.function | #ffa2a7observeduse | #ffa2a7heritagehistorical-only | #c9973fproposedblockedD-001 | Function names and calls (color7 lineage). |
color.code.syntax.variable | #e99499observeduse | #ab1612heritagedeprecatedhistorical-only | #e99499observedblocked | Variables and default text. |
color.code.syntax.type | #b37175observeduse | #b37175observedhistorical-only | #7e9ebbproposedblockedD-001 | Types. |
color.code.syntax.operator | #e99499observeduse | #ffa2a7heritagehistorical-only | #ffa2a7proposedblockedD-001 | Operators. |
color.code.syntax.punctuation | #e99499observeduse | #d4868bheritagehistorical-only | #b37175proposedblockedD-001 | Punctuation. |
color.code.syntax.property | #e53935observeduse | #e53935observedhistorical-only | #7e9ebbproposedblockedD-001 | Properties and keys (site .syntax-key). |
color.code.syntax.heading | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Markup headings (site .syntax-heading). |
color.code.syntax.tag | #f73f35observeduse | #f73f35observedhistorical-only | #f73f35observedblocked | Markup tags. |
color.code.syntax.attribute | #d4868bobserveduse | #d4868bobservedhistorical-only | #c9973fproposedblockedD-001 | Markup attributes. |
color.code.syntax.escape | #d4868bobserveduse | #d4868bobservedhistorical-only | #c9973fproposedblockedD-001 | Escape sequences and regex, bold. |
color.code.syntax.invalid | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Invalid text: wavy underline. |
color.code.syntax.deprecated | #ad7175observeduse | #ad7175observedhistorical-only | #ad7175observedblocked | Deprecated symbol: line-through. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.diagnostic.error.underline | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Error wavy underline. |
color.diagnostic.error.text | #f73f35observeduse | #f73f35observedhistorical-only | #f73f35observedblocked | Error message text. |
color.diagnostic.error.stripe | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Error stripe mark. |
color.diagnostic.error.bg | #2b0e0dobserveduse | #2b0e0dobservedhistorical-only | #2b0e0dobservedblocked | Error line background. |
color.diagnostic.warning.underline | #c9973fapproveduse | #c9973fapprovedhistorical-only | #c9973fapprovedblocked | Warning wavy underline. |
color.diagnostic.warning.text | #c9973fapproveduse | #c9973fapprovedhistorical-only | #c9973fapprovedblocked | Warning message text. |
color.diagnostic.warning.stripe | #c9973fapproveduse | #c9973fapprovedhistorical-only | #c9973fapprovedblocked | Warning stripe mark. |
color.diagnostic.info.underline | #7e9ebbapproveduse | #7e9ebbapprovedhistorical-only | #7e9ebbapprovedblocked | Info dotted underline. |
color.diagnostic.info.text | #7e9ebbapproveduse | #7e9ebbapprovedhistorical-only | #7e9ebbapprovedblocked | Info message text. |
color.diagnostic.hint.underline | #ad7175observeduse | #ad7175observedhistorical-only | #ad7175observedblocked | Hint dotted underline. |
color.diagnostic.unused.text | #bd787dobserveduse | #bd787dobservedhistorical-only | #bd787dobservedblocked | Unused symbol, dotted underline. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.diff.added.bg | #0f1a0eapproveduse | #0f1a0eapprovedhistorical-only | #0f1a0eapprovedblocked | Added line background. |
color.diff.added.gutter | #86a46fapproveduse | #86a46fapprovedhistorical-only | #86a46fapprovedblocked | Added gutter mark and +. |
color.diff.added.emphasis | #86a46fapproveduse | #86a46fapprovedhistorical-only | #86a46fapprovedblocked | Word-level addition: 2px underline. |
color.diff.removed.bg | #2b0e0dobserveduse | #2b0e0dobservedhistorical-only | #2b0e0dobservedblocked | Removed line background. |
color.diff.removed.gutter | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Removed gutter mark and −. |
color.diff.removed.emphasis | #e53935observeduse | #e53935observedhistorical-only | #e53935observedblocked | Word-level removal: underline and line-through. |
color.diff.modified.bg | #1f1a0capproveduse | #1f1a0capprovedhistorical-only | #1f1a0capprovedblocked | Modified line background. |
color.diff.modified.gutter | #c9973fapproveduse | #c9973fapprovedhistorical-only | #c9973fapprovedblocked | Modified gutter mark and ~. |
color.diff.conflict-border | #f73f35observeduse | #f73f35observedhistorical-only | #f73f35observedblocked | Conflict block: double 1px border. |
color.diff.header-bg | #241010observeduse | #241010observedhistorical-only | #241010observedblocked | Hunk header background. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.terminal.bg | #0c0909observeduse | #0c0909observedhistorical-only | #0c0909observedblocked | Terminal background. |
color.terminal.fg | #e99499observeduse | #e99499observedhistorical-only | #e99499observedblocked | Terminal foreground. |
color.terminal.cursor | #e99499observeduse | #e99499observedhistorical-only | #e99499observedblocked | Terminal cursor. |
color.terminal.selection-bg | #911410observeduse | #911410observedhistorical-only | #911410observedblocked | Terminal selection. |
color.terminal.selection-text | #f4eeeeobserveduse | #f4eeeeobservedhistorical-only | #f4eeeeobservedblocked | Terminal selection text. |
color.terminal.ansi.0 | #0c0909observeduse | #0c0909observedhistorical-only | #0c0909proposedblockedD-001 | ANSI slot 0, exact heritage value. |
color.terminal.ansi.1 | #c81a1aobserveduse | #c81a1aobservedhistorical-only | #e53935proposedblockedD-001 | ANSI slot 1, exact heritage value. |
color.terminal.ansi.2 | #bd787dobserveduse | #bd787dobservedhistorical-only | #86a46fproposedblockedD-001 | ANSI slot 2, exact heritage value. |
color.terminal.ansi.3 | #d4868bobserveduse | #d4868bobservedhistorical-only | #c9973fproposedblockedD-001 | ANSI slot 3, exact heritage value. |
color.terminal.ansi.4 | #8c1212observeduse | #8c1212observedhistorical-only | #7e9ebbproposedblockedD-001 | ANSI slot 4, exact heritage value. |
color.terminal.ansi.5 | #f73f35observeduse | #f73f35observedhistorical-only | #b37175proposedblockedD-001 | ANSI slot 5, exact heritage value. |
color.terminal.ansi.6 | #9e474aobserveduse | #9e474aobservedhistorical-only | #bd787dproposedblockedD-001 | ANSI slot 6, exact heritage value. |
color.terminal.ansi.7 | #ffa2a7observeduse | #ffa2a7observedhistorical-only | #e99499proposedblockedD-001 | ANSI slot 7, exact heritage value. |
color.terminal.ansi.8 | #7d1310observeduse | #7d1310observedhistorical-only | #ad7175proposedblockedD-001 | ANSI slot 8, exact heritage value. |
color.terminal.ansi.9 | #ab1612observeduse | #ab1612observedhistorical-only | #f73f35proposedblockedD-001 | ANSI slot 9, exact heritage value. |
color.terminal.ansi.10 | #ad2721observeduse | #ad2721observedhistorical-only | #7fbf7aproposedblockedD-001 | ANSI slot 10, exact heritage value. |
color.terminal.ansi.11 | #b37175observeduse | #b37175observedhistorical-only | #d6a24bproposedblockedD-001 | ANSI slot 11, exact heritage value. |
color.terminal.ansi.12 | #871f19observeduse | #871f19observedhistorical-only | #9ab3c9proposedblockedD-001 | ANSI slot 12, exact heritage value. |
color.terminal.ansi.13 | #e82132observeduse | #e82132observedhistorical-only | #d4868bproposedblockedD-001 | ANSI slot 13, exact heritage value. |
color.terminal.ansi.14 | #e0292fobserveduse | #e0292fobservedhistorical-only | #ffa2a7proposedblockedD-001 | ANSI slot 14, exact heritage value. |
color.terminal.ansi.15 | #a3676bobserveduse | #a3676bobservedhistorical-only | #f4eeeeproposedblockedD-001 | ANSI slot 15, exact heritage value. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.chart.series-1 | #ffa2a7observeduse | #ffa2a7observedhistorical-only | #ffa2a7observedblocked | First series. |
color.chart.series-2 | #f73f35observeduse | #f73f35observedhistorical-only | #86a46fproposedblockedD-001 | Second series. |
color.chart.series-3 | #bd787dobserveduse | #bd787dobservedhistorical-only | #c9973fproposedblockedD-001 | Third series. |
color.chart.series-4 | #a3676bobserveduse | #a3676bobservedhistorical-only | #7e9ebbproposedblockedD-001 | Fourth series (non-text, 4.45:1). |
color.chart.axis | #9e231fobserveduse | #9e231fobservedhistorical-only | #9e231fobservedblocked | Axis lines. |
color.chart.grid | #2b0e0dobserveduse | #2b0e0dobservedhistorical-only | #2b0e0dobservedblocked | Grid lines. |
color.chart.label | #bd787dobserveduse | #bd787dobservedhistorical-only | #bd787dobservedblocked | Axis labels. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.icon.default | #e99499observeduse | #e99499observedhistorical-only | #e99499observedblocked | Icons take the text colour (currentColor in CSS). |
color.icon.decorative | #a3676bobserveduse | #a3676bobservedhistorical-only | #a3676bobservedblocked | Decorative glyphs (3:1 as graphics, never text). |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
space.0 | 0pxobserveduse | 0pxobservedhistorical-only | 0pxobservedblocked | |
space.1 | 1pxobserveduse | 1pxobservedhistorical-only | 1pxobservedblocked | |
space.2 | 2pxobserveduse | 2pxobservedhistorical-only | 2pxobservedblocked | |
space.4 | 4pxobserveduse | 4pxobservedhistorical-only | 4pxobservedblocked | |
space.8 | 8pxobserveduse | 8pxobservedhistorical-only | 8pxobservedblocked | |
space.12 | 12pxobserveduse | 12pxobservedhistorical-only | 12pxobservedblocked | |
space.16 | 16pxobserveduse | 16pxobservedhistorical-only | 16pxobservedblocked | |
space.24 | 24pxobserveduse | 24pxobservedhistorical-only | 24pxobservedblocked | |
space.32 | 32pxobserveduse | 32pxobservedhistorical-only | 32pxobservedblocked | |
space.48 | 48pxobserveduse | 48pxobservedhistorical-only | 48pxobservedblocked |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
density.compact.control-height | 24pxobserveduse | 24pxobservedhistorical-only | 24pxobservedblocked | |
density.compact.row-height | 28pxobserveduse | 28pxobservedhistorical-only | 28pxobservedblocked | |
density.compact.control-padding-x | 8pxobserveduse | 8pxobservedhistorical-only | 8pxobservedblocked | |
density.compact.icon | 12pxobserveduse | 12pxobservedhistorical-only | 12pxobservedblocked | |
density.compact.gap | 2pxobserveduse | 2pxobservedhistorical-only | 2pxobservedblocked | |
density.comfortable.control-height | 32pxobserveduse | 32pxobservedhistorical-only | 32pxobservedblocked | |
density.comfortable.row-height | 36pxobserveduse | 36pxobservedhistorical-only | 36pxobservedblocked | |
density.comfortable.control-padding-x | 12pxobserveduse | 12pxobservedhistorical-only | 12pxobservedblocked | |
density.comfortable.icon | 16pxobserveduse | 16pxobservedhistorical-only | 16pxobservedblocked | |
density.comfortable.gap | 4pxobserveduse | 4pxobservedhistorical-only | 4pxobservedblocked |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
border.width.default | 1pxobserveduse | 1pxobservedhistorical-only | 1pxobservedblocked | Every border is 1px. |
border.width.emphasis | 2pxobserveduse | 2pxobservedhistorical-only | 2pxobservedblocked | Selected indicator, sticky header rule, invalid border. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
radius.none | 0pxobserveduse | 0pxobservedhistorical-only | 0pxobservedblocked | The only radius. Nothing is rounded. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
font.family.mono | "SauceCodePro NFM", "Source Code Pro", "Cascadia Mono", "Consolas", "Liberation Mono", "DejaVu Sans Mono", monospaceobserveduse | "SauceCodePro NFM", "Source Code Pro", "Cascadia Mono", "Consolas", "Liberation Mono", "DejaVu Sans Mono", monospaceobservedhistorical-only | "SauceCodePro NFM", "Source Code Pro", "Cascadia Mono", "Consolas", "Liberation Mono", "DejaVu Sans Mono", monospaceobservedblocked | One family for chrome, reading and code. Not bundled; see theme.json font. |
font.weight.regular | 400observeduse | 400observedhistorical-only | 400observedblocked | |
font.weight.bold | 700observeduse | 700observedhistorical-only | 700observedblocked | |
font.size.ui-sm | 12pxobserveduse | 12pxobservedhistorical-only | 12pxobservedblocked | |
font.size.ui-md | 13pxobserveduse | 13pxobservedhistorical-only | 13pxobservedblocked | |
font.size.ui-lg | 14pxobserveduse | 14pxobservedhistorical-only | 14pxobservedblocked | |
font.size.reading | 15pxobserveduse | 15pxobservedhistorical-only | 15pxobservedblocked | |
font.size.code | 13pxobserveduse | 13pxobservedhistorical-only | 13pxobservedblocked | |
font.size.terminal | 13pxobserveduse | 13pxobservedhistorical-only | 13pxobservedblocked | URxvt pixelsize 13. |
font.size.h1 | 20pxobserveduse | 20pxobservedhistorical-only | 20pxobservedblocked | |
font.size.h2 | 16pxobserveduse | 16pxobservedhistorical-only | 16pxobservedblocked | |
font.size.h3 | 13pxobserveduse | 13pxobservedhistorical-only | 13pxobservedblocked | |
font.size.caption | 11pxobserveduse | 11pxobservedhistorical-only | 11pxobservedblocked | Only with text.muted or lighter. |
font.line-height.ui-sm | 16pxobserveduse | 16pxobservedhistorical-only | 16pxobservedblocked | |
font.line-height.ui-md | 18pxobserveduse | 18pxobservedhistorical-only | 18pxobservedblocked | |
font.line-height.ui-lg | 20pxobserveduse | 20pxobservedhistorical-only | 20pxobservedblocked | |
font.line-height.reading | 24pxobserveduse | 24pxobservedhistorical-only | 24pxobservedblocked | |
font.line-height.code | 19pxobserveduse | 19pxobservedhistorical-only | 19pxobservedblocked | |
font.line-height.terminal | 19pxobserveduse | 19pxobservedhistorical-only | 19pxobservedblocked | |
font.line-height.h1 | 28pxobserveduse | 28pxobservedhistorical-only | 28pxobservedblocked | |
font.line-height.h2 | 24pxobserveduse | 24pxobservedhistorical-only | 24pxobservedblocked | |
font.line-height.h3 | 18pxobserveduse | 18pxobservedhistorical-only | 18pxobservedblocked | |
font.line-height.caption | 16pxobserveduse | 16pxobservedhistorical-only | 16pxobservedblocked | |
font.letter-spacing.terminal | -0.5pxobserveduse | -0.5pxobservedhistorical-only | -0.5pxobservedblocked | URxvt letterSpace -1 at pixelsize 13, as the site renders it. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
focus.ring | 1px dashed #e53935observeduse | 1px dashed #e53935observedhistorical-only | 1px dashed #e53935observedblocked | outline on controls and rows. |
focus.ring-container | 2px solid #ffa2a7observeduse | 2px solid #ffa2a7observedhistorical-only | 2px solid #ffa2a7observedblocked | outline on focusable containers. |
focus.offset | -2pxobserveduse | -2pxobservedhistorical-only | -2pxobservedblocked | outline-offset for controls. |
focus.offset-container | -3pxobserveduse | -3pxobservedhistorical-only | -3pxobservedblocked | outline-offset for containers. |
focus.offset-invalid | -4pxobserveduse | -4pxobservedhistorical-only | -4pxobservedblocked | outline-offset inside the 2px invalid border. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
motion.duration.fast | 80msobserveduse | 80msobservedhistorical-only | 80msobservedblocked | |
motion.duration.base | 120msobserveduse | 120msobservedhistorical-only | 120msobservedblocked | |
motion.duration.slow | 150msobserveduse | 150msobservedhistorical-only | 150msobservedblocked | The maximum. Reduced motion sets every duration to 0.01ms. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
shadow.floating | 0px 6px 22px 0px rgb(0 0 0 / 55%)observeduse | 0px 6px 22px 0px rgb(0 0 0 / 55%)observedhistorical-only | 0px 6px 22px 0px rgb(0 0 0 / 55%)observedblocked | The only shadow: the i3 floating window. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
layout.measure | 72observeduse | 72observedhistorical-only | 72observedblocked | Reading measure in ch. |
layout.content-max | 680pxobserveduse | 680pxobservedhistorical-only | 680pxobservedblocked | Reading column maximum. |
layout.app-max | 1440pxobserveduse | 1440pxobservedhistorical-only | 1440pxobservedblocked | Application layout maximum. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
breakpoint.xs | 320pxobserveduse | 320pxobservedhistorical-only | 320pxobservedblocked | Reflow floor (WCAG 1.4.10). |
breakpoint.sm | 600pxobserveduse | 600pxobservedhistorical-only | 600pxobservedblocked | |
breakpoint.md | 900pxobserveduse | 900pxobservedhistorical-only | 900pxobservedblocked | |
breakpoint.lg | 1280pxobserveduse | 1280pxobservedhistorical-only | 1280pxobservedblocked | The site's bar breaks below this. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
icon.size.sm | 12pxobserveduse | 12pxobservedhistorical-only | 12pxobservedblocked | |
icon.size.md | 16pxobserveduse | 16pxobservedhistorical-only | 16pxobservedblocked | |
icon.size.lg | 20pxobserveduse | 20pxobservedhistorical-only | 20pxobservedblocked | |
icon.stroke | 1.5pxobserveduse | 1.5pxobservedhistorical-only | 1.5pxobservedblocked | SVG line icons, currentColor, no fill. |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
z.canvas | 0observeduse | 0observedhistorical-only | 0observedblocked | |
z.raised | 1observeduse | 1observedhistorical-only | 1observedblocked | |
z.popover | 10observeduse | 10observedhistorical-only | 10observedblocked | |
z.drawer | 20observeduse | 20observedhistorical-only | 20observedblocked | |
z.dialog | 30observeduse | 30observedhistorical-only | 30observedblocked | |
z.toast | 40observeduse | 40observedhistorical-only | 40observedblocked | |
z.skip-link | 1000observeduse | 1000observedhistorical-only | 1000observedblocked |
| Token | default | heritage-ansi | extended | Use |
|---|---|---|---|---|
color.primitive.ink.0 | #000000observedblocked | #000000observedhistorical-only | #000000observedblocked | The desktop behind every window. |
color.primitive.ink.14 | #090707approvedblocked | #090707approvedhistorical-only | #090707approvedblocked | True Black / Rose surface primitive; D-023. |
color.primitive.ink.15 | #0a0707observedblocked | #0a0707observedhistorical-only | #0a0707observedblocked | Sunken reading and detail panes. |
color.primitive.ink.20 | #0c0909observedblocked | #0c0909observedhistorical-only | #0c0909observedblocked | Terminal and canvas background; Xresources color0 and *background. |
color.primitive.ink.25 | #100909observedblocked | #100909observedhistorical-only | #100909observedblocked | Window chrome and title bars. |
color.primitive.ink.28 | #100c0capprovedblocked | #100c0capprovedhistorical-only | #100c0capprovedblocked | True Black / Rose surface primitive; D-023. |
color.primitive.ink.30 | #160b0bobservedblocked | #160b0bobservedhistorical-only | #160b0bobservedblocked | Default panel surface. |
color.primitive.ink.40 | #1c0a09observedblocked | #1c0a09observedhistorical-only | #1c0a09observedblocked | Alternate chrome; hover rows; current line. |
color.primitive.ink.52 | #241010observedblocked | #241010observedhistorical-only | #241010observedblocked | Raised surface: sticky headers, menus, popovers. |
color.primitive.ink.60 | #2b0e0dobservedblocked | #2b0e0dobservedhistorical-only | #2b0e0dobservedblocked | Hairline divider; folds the site's six near-identical hairlines. |
color.primitive.red.50 | #420f0cobservedblocked | #420f0cobservedhistorical-only | #420f0cobservedblocked | Soft selection; inactive selection; scrollbar thumb. |
color.primitive.red.100 | #531310observedblocked | #531310observedhistorical-only | #531310observedblocked | Decorative hairline border. |
color.primitive.red.150 | #630f0dobservedblocked | #630f0dobservedhistorical-only | #630f0dobservedblocked | Dark selection; dmenu selected background. |
color.primitive.red.200 | #7d1310observedblocked | #7d1310observedhistorical-only | #7d1310observedblocked | Xresources color8. Never text. |
color.primitive.red.250 | #8c1212observedblocked | #8c1212observedhistorical-only | #8c1212observedblocked | Xresources color4; i3 focused child border. |
color.primitive.red.300 | #871f19observedblocked | #871f19observedhistorical-only | #871f19observedblocked | Xresources color12; the button fill. |
color.primitive.red.350 | #911410observedblocked | #911410observedhistorical-only | #911410observedblocked | Selection fill. |
color.primitive.red.400 | #9e231fobservedblocked | #9e231fobservedhistorical-only | #9e231fobservedblocked | Strong border; sticky header rule. |
color.primitive.red.450 | #ab1612observedblocked | #ab1612observedhistorical-only | #ab1612observedblocked | Xresources color9. |
color.primitive.red.500 | #ad2721observedblocked | #ad2721observedhistorical-only | #ad2721observedblocked | Xresources color10. |
color.primitive.red.550 | #ae1914observedblocked | #ae1914observedhistorical-only | #ae1914observedblocked | dunst frame and conky accent. |
color.primitive.red.650 | #c81a1aobservedblocked | #c81a1aobservedhistorical-only | #c81a1aobservedblocked | Xresources color1; i3 focused workspace background. |
color.primitive.red.750 | #dc282eobservedblocked | #dc282eobservedhistorical-only | #dc282eobservedblocked | dunst critical background; link underline. |
color.primitive.red.760 | #e0292fobservedblocked | #e0292fobservedhistorical-only | #e0292fobservedblocked | Xresources color14; i3 focused border. |
color.primitive.red.770 | #e82132observedblocked | #e82132observedhistorical-only | #e82132observedblocked | Xresources color13. |
color.primitive.red.800 | #e53935observedblocked | #e53935observedhistorical-only | #e53935observedblocked | Focus ring; active border; accent text on dark surfaces. |
color.primitive.red.850 | #f73f35observedblocked | #f73f35observedhistorical-only | #f73f35observedblocked | Xresources color5; strong accent; danger text. |
color.primitive.rose.300 | #8a5559approvedblocked | #8a5559approvedhistorical-only | #8a5559approvedblocked | Disabled text (3.33:1 on canvas; exempt, at least 3:1 by policy). |
color.primitive.rose.320 | #9e474aobservedblocked | #9e474aobservedhistorical-only | #9e474aobservedblocked | Xresources color6. |
color.primitive.rose.350 | #a3676bobservedblocked | #a3676bobservedhistorical-only | #a3676bobservedblocked | Xresources color15; 4.45:1 on canvas, so not ordinary text. |
color.primitive.rose.450 | #ad7175approvedblocked | #ad7175approvedhistorical-only | #ad7175approvedblocked | Subtle text: metadata, comments, line numbers (5.10:1). |
color.primitive.rose.550 | #b37175observedblocked | #b37175observedhistorical-only | #b37175observedblocked | Xresources color11. |
color.primitive.rose.600 | #bd787dobservedblocked | #bd787dobservedhistorical-only | #bd787dobservedblocked | Xresources color2; muted text, the floor for chrome text (5.81:1). |
color.primitive.rose.650 | #d4868bobservedblocked | #d4868bobservedhistorical-only | #d4868bobservedblocked | Xresources color3. |
color.primitive.rose.700 | #e99499observedblocked | #e99499observedhistorical-only | #e99499observedblocked | Xresources *foreground; interface text. |
color.primitive.rose.800 | #ffa2a7observedblocked | #ffa2a7observedhistorical-only | #ffa2a7observedblocked | Xresources color7; bright text, links, headings. |
color.primitive.paper.90 | #f4eeeeobservedblocked | #f4eeeeobservedhistorical-only | #f4eeeeobservedblocked | Prose and on-fill text. |
color.primitive.paper.100 | #f9faf9observedblocked | #f9faf9observedhistorical-only | #f9faf9observedblocked | Text on the critical toast. |
color.primitive.amber.500 | #c9973fapprovedblocked | #c9973fapprovedhistorical-only | #c9973fapprovedblocked | Warning text, border and fill (7.54:1 on canvas). |
color.primitive.amber.600 | #d6a24bproposedblocked | #d6a24bproposedhistorical-only | #d6a24bproposedblocked | Bright yellow slot in the extended terminal palette. |
color.primitive.amber.tint | #1f1a0capprovedblocked | #1f1a0capprovedhistorical-only | #1f1a0capprovedblocked | Warning and modified backgrounds. |
color.primitive.green.500 | #86a46fapprovedblocked | #86a46fapprovedhistorical-only | #86a46fapprovedblocked | Success text, border and fill (7.13:1 on canvas). |
color.primitive.green.600 | #7fbf7aobservedblocked | #7fbf7aobservedhistorical-only | #7fbf7aobservedblocked | The greeter's systemd OK; bright green slot in the extended terminal palette. |
color.primitive.green.tint | #0f1a0eapprovedblocked | #0f1a0eapprovedhistorical-only | #0f1a0eapprovedblocked | Added-line background. |
color.primitive.blue.500 | #7e9ebbapprovedblocked | #7e9ebbapprovedhistorical-only | #7e9ebbapprovedblocked | Info text, border and fill (7.08:1 on canvas). |
color.primitive.blue.600 | #9ab3c9proposedblocked | #9ab3c9proposedhistorical-only | #9ab3c9proposedblocked | Bright blue slot in the extended terminal palette. |
color.primitive.blue.tint | #0f141capprovedblocked | #0f141capprovedhistorical-only | #0f141capprovedblocked | Info background. |
color.primitive.alpha.transparent | rgb(0 0 0 / 0%)observedblocked | rgb(0 0 0 / 0%)observedhistorical-only | rgb(0 0 0 / 0%)observedblocked | No fill. |
color.primitive.alpha.black-55 | rgb(0 0 0 / 55%)observedblocked | rgb(0 0 0 / 55%)observedhistorical-only | rgb(0 0 0 / 55%)observedblocked | Floating-window shadow. |
color.primitive.alpha.black-65 | rgb(0 0 0 / 65%)observedblocked | rgb(0 0 0 / 65%)observedhistorical-only | rgb(0 0 0 / 65%)observedblocked | Modal backdrop; unifies the site's 62–72% backdrops. |
color.primitive.alpha.red-6 | rgb(145 20 16 / 6%)observedblocked | rgb(145 20 16 / 6%)observedhistorical-only | rgb(145 20 16 / 6%)observedblocked | Wallpaper stripe. |
color.primitive.alpha.red-12 | rgb(145 20 16 / 12%)observedblocked | rgb(145 20 16 / 12%)observedhistorical-only | rgb(145 20 16 / 12%)observedblocked | Drag marquee. |
color.primitive.ansi.0 | #0c0909observedblocked | #0c0909observedhistorical-only | #0c0909observedblocked | color0 |
color.primitive.ansi.1 | #c81a1aobservedblocked | #c81a1aobservedhistorical-only | #c81a1aobservedblocked | color1 |
color.primitive.ansi.2 | #bd787dobservedblocked | #bd787dobservedhistorical-only | #bd787dobservedblocked | color2 |
color.primitive.ansi.3 | #d4868bobservedblocked | #d4868bobservedhistorical-only | #d4868bobservedblocked | color3 |
color.primitive.ansi.4 | #8c1212observedblocked | #8c1212observedhistorical-only | #8c1212observedblocked | color4 |
color.primitive.ansi.5 | #f73f35observedblocked | #f73f35observedhistorical-only | #f73f35observedblocked | color5 |
color.primitive.ansi.6 | #9e474aobservedblocked | #9e474aobservedhistorical-only | #9e474aobservedblocked | color6 |
color.primitive.ansi.7 | #ffa2a7observedblocked | #ffa2a7observedhistorical-only | #ffa2a7observedblocked | color7 |
color.primitive.ansi.8 | #7d1310observedblocked | #7d1310observedhistorical-only | #7d1310observedblocked | color8 |
color.primitive.ansi.9 | #ab1612observedblocked | #ab1612observedhistorical-only | #ab1612observedblocked | color9 |
color.primitive.ansi.10 | #ad2721observedblocked | #ad2721observedhistorical-only | #ad2721observedblocked | color10 |
color.primitive.ansi.11 | #b37175observedblocked | #b37175observedhistorical-only | #b37175observedblocked | color11 |
color.primitive.ansi.12 | #871f19observedblocked | #871f19observedhistorical-only | #871f19observedblocked | color12 |
color.primitive.ansi.13 | #e82132observedblocked | #e82132observedhistorical-only | #e82132observedblocked | color13 |
color.primitive.ansi.14 | #e0292fobservedblocked | #e0292fobservedhistorical-only | #e0292fobservedblocked | color14 |
color.primitive.ansi.15 | #a3676bobservedblocked | #a3676bobservedhistorical-only | #a3676bobservedblocked | color15 |
Buttons, links, icon buttons, button groups, segmented controls, destructive treatment.
buttonThe native push button in the four action tones; primary is the only filled button at rest, destructive fills on hover and always needs a confirmation or an undo.
Report an issue with this component
View source and copy recipe · Download standalone example
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
root.text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
root.border | color.action.primary.borderobserveduse | #9e231f | Primary button border. |
root.bg-hover | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover (7.92:1 with prose). |
root.bg-active | color.action.primary.pressed-bgobserveduse | #630f0d | Primary button pressed. |
root.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
secondary.bg | color.action.secondary.bgobserveduse | rgb(0 0 0 / 0%) | Secondary button has no fill. |
secondary.text | color.action.secondary.textobserveduse | #ffa2a7 | Secondary button text. |
secondary.border | color.action.secondary.borderobserveduse | #a3676b | Secondary button border (meets 3:1). |
secondary.bg-hover | color.action.secondary.hover-bgobserveduse | #630f0d | Secondary button hover. |
secondary.bg-active | color.action.secondary.pressed-bgobserveduse | #420f0c | Secondary button pressed. |
secondary.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
tertiary.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
tertiary.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
tertiary.bg-active | color.interaction.pressed.bgobserveduse | #420f0c | Pressed fill. |
destructive.text | color.action.destructive.textobserveduse | #f73f35 | Destructive button text at rest (outline style). |
destructive.border | color.action.destructive.borderobserveduse | #e53935 | Destructive button border. |
destructive.bg-hover | color.action.destructive.hover-bgobserveduse | #dc282e | Destructive button hover fill. |
destructive.text-hover | color.action.destructive.hover-textobserveduse | #f9faf9 | Destructive button text on hover (4.58:1). |
destructive.bg-active | color.action.destructive.pressed-bgobserveduse | #ae1914 | Destructive button pressed fill. |
destructive.bg-filled | color.action.destructive.filled-bgobserveduse | #dc282e | Confirm-dialog destructive fill. |
destructive.text-filled | color.action.destructive.filled-textobserveduse | #f9faf9 | Text on the destructive fill. |
root.border-active | color.action.primary.borderobserveduse | #9e231f | Primary button border. |
secondary.border-active | color.border.activeobserveduse | #e53935 | Active or focused control border. |
tertiary.border-active | color.border.activeobserveduse | #e53935 | Active or focused control border. |
destructive.border-active | color.action.destructive.borderobserveduse | #e53935 | Destructive button border. |
root.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
loading.glyph | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Primary | Secondary | Tertiary | Destructive |
|---|---|---|---|---|
default | ||||
hover | ||||
focus-visible | ||||
active | ||||
disabled | ||||
loading |
The push button that triggers an action. Four tones and no more: primary
is the single filled button in a view, secondary is the outline for every
other action, tertiary is the ghost for low-emphasis and toolbar actions,
destructive is an outline in the danger tone that fills on hover. A
destructive button also requires a confirmation step or an undo affordance;
the colour is not the safeguard. Navigation is a link, not a button.
A native <button>; optional leading icon; the label. The loading glyph
occupies the icon slot so the size never changes. A filled destructive
modifier (.button-destructive-filled, color.action.destructive.filled-bg
with color.action.destructive.filled-text) exists only inside confirm
dialogs.
| State | Visual | Non-colour channel |
|---|---|---|
| default | primary: color.action.primary.bg fill, color.action.primary.text, 1px color.action.primary.border; secondary: 1px color.action.secondary.border, color.action.secondary.text; tertiary: color.action.tertiary.text, no border; destructive: 1px color.action.destructive.border, color.action.destructive.text | — |
| hover | primary → color.action.primary.hover-bg; secondary → color.action.secondary.hover-bg; tertiary → color.action.tertiary.hover-bg; destructive fills color.action.destructive.hover-bg with color.action.destructive.hover-text | cursor: pointer; hover-capable pointers only |
| focus-visible | ring 1px dashed at −2px: color.interaction.focus.ring-container on the primary fill, color.interaction.focus.ring on the outline tones, color.action.destructive.hover-text on the destructive hover fill | the ring |
| active | primary → color.action.primary.pressed-bg; secondary → color.action.secondary.pressed-bg; tertiary → color.interaction.pressed.bg; destructive → color.action.destructive.pressed-bg; primary keeps color.action.primary.border, secondary and tertiary use color.border.active, destructive keeps color.action.destructive.border (D-015) | border change on secondary and tertiary; no translation or scale |
| disabled | color.text.disabled on color.interaction.disabled.bg with 1px color.border.disabled, every tone alike; no hover or active styling | disabled; cursor: not-allowed; never opacity |
| loading | size unchanged; the static ⋯ glyph replaces the icon; the button keeps its tone and stays focusable but ignores activation | glyph; aria-busy |
Precedence: disabled > loading > active > hover; focus-visible is always drawn.
The primary edge stays unchanged when pressed (D-015). Its exact role is
root.border-active in the part map, just as root.border names the rest
edge. The default-tone state contrast map checks the text against the
pressed fill; it does not substitute color.border.active for this
unchanged edge. The existing primary-action contrast declaration identifies
the labelled button through its text, not through the fill’s boundary.
Native. Enter and Space activate; nothing else is bound. A loading button
stays in the tab order so focus is not lost mid-action, and the host ignores
activation until aria-busy clears.
The accessible name is the label text; an icon-only button is the
icon-button component. Never aria-disabled on a button that should be
skipped, and never disabled on a button whose reason a user needs to
discover: prefer keeping it enabled and explaining on activation. Contrast:
primary 9.28:1, secondary 10.37:1, tertiary 8.65:1, destructive 5.40:1 at
rest and 4.58:1 on the hover fill; every outline border ≥ 4.45:1; rings ≥
3:1 on every fill they sit on. The target is at least 24×24 CSS pixels in
compact density with 4px between neighbours.
Fill, border and outline express every state; no gradient, shadow or transform is used. Hosts without dashed outlines draw a solid 1px ring in the same colour and record the deviation. Hosts that cannot recolour the ring on a fill draw it outside the fill at +1px.
Rounded corners or pill shapes. Two filled buttons in one view. A red
filled destructive button at rest. A hover that only changes text colour.
Translating or scaling the button on press. A spinner inside the button.
Disabling with opacity. A <div> or <a> styled as a button. A gradient or
shadow to suggest depth.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus to and from the button in document order. |
| Enter / Space | Activates (native); Space activates on key up. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: icon-button link button-group segmented-control dialog
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
icon-buttonA square ghost button whose only content is a 16px line icon; it always carries an accessible name and, as a toggle, aria-pressed.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.action.secondary.bgobserveduse | rgb(0 0 0 / 0%) | Secondary button has no fill. |
root.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
root.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
root.bg-active | color.interaction.pressed.bgobserveduse | #420f0c | Pressed fill. |
root.border-active | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.bg-toggled | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
root.text-toggled | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
root.border-toggled | color.border.selected-indicatorobserveduse | #e53935 | 2px indicator bar on the selected tab or nav item. |
root.ring-toggled | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
icon.stroke | color.icon.defaultobserveduse | #e99499 | Icons take the text colour (currentColor in CSS). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Action | Toggle |
|---|---|---|
default | ||
hover | ||
focus-visible | ||
active | ||
disabled | ||
toggled | ||
toggled+focus-visible |
A button whose whole content is one icon: toolbar actions, close and clear affordances, and boolean toggles such as pin, mute or wrap. It is the tertiary tone constrained to a square. Any icon button needs an accessible name and a tooltip; if the label must be visible it is a button.
A native <button> sized to the density control height in both axes with a
single 16px inline SVG inside. The border is 1px and transparent at rest so
the active and toggled borders do not shift the layout.
| State | Visual | Non-colour channel |
|---|---|---|
| default | color.action.tertiary.text icon on nothing; transparent 1px border | — |
| hover | background → color.action.tertiary.hover-bg | cursor: pointer; hover-capable pointers only |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px | the ring |
| active | background → color.interaction.pressed.bg; border → color.border.active | border change; no translation |
| disabled | color.text.disabled on color.interaction.disabled.bg with 1px color.border.disabled; no hover | disabled; cursor: not-allowed |
| toggled | fill color.interaction.selection.bg with color.interaction.selection.text; border color.border.selected-indicator; the icon draws in its filled form | aria-pressed="true"; the filled icon shape; the border |
| toggled+focus-visible | the selection fill and a ring in color.interaction.focus.ring-container | both visible at once |
Precedence: disabled > toggled > active > hover; focus-visible is always drawn.
Native. The toggle variant flips aria-pressed on Enter and Space and does
not move focus. Inside a toolbar the host applies roving tabindex; the
button itself binds nothing.
aria-label (or aria-labelledby) names the button; the tooltip repeats
the same text and is not the name. The SVG is aria-hidden="true" and
focusable="false". A toggle uses aria-pressed, whose value is announced;
the name never changes with the state (“Mute”, not “Mute” / “Unmute”).
Contrast: icon 8.65:1 at rest, 12.47:1 on the selection fill; ring 4.69:1,
container ring 7.48:1 on the selection fill. Target ≥ 24×24 CSS pixels with
4px between neighbours in compact density.
Square, fill, border, outline; the icon is currentColor so it follows every state without its own colour. Hosts without inline SVG use their icon theme at 16px and record the substitution. Hosts without dashed outlines draw a solid 1px ring.
A circle. An icon button without an accessible name. A toggle that changes
its label instead of aria-pressed. A hover that recolours the icon alone.
An emoji or icon-font glyph in place of the SVG. A toggled state shown by
colour alone with a line icon. A shadow or scale on press.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus to and from the button. |
| Enter / Space | Activates; the toggle variant flips aria-pressed. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: button toolbar tooltip segmented-control
Stress fixtures: FX-320 FX-ZOOM-200 FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
linkThe native anchor for navigation; always underlined in the link-underline role, hover fills with the strong hover surface, the current page carries a 2px indicator bar.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.text | color.text.linkapproveduse | #f73f35 | Strong red link text, distinct from rose body text and headings; retain the underline. |
root.underline | color.text.link-underlineobserveduse | #dc282e | Link underline. |
root.text-hover | color.text.link-hoverobserveduse | #f4eeee | Link text on hover, over interaction.hover.bg-strong. |
root.bg-hover | color.interaction.hover.bg-strongobserveduse | #630f0d | Hover on menu items; D-027 moved links to interaction.hover.bg. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.text-visited | color.text.linkapproveduse | #f73f35 | Strong red link text, distinct from rose body text and headings; retain the underline. |
root.text-current | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
root.indicator | color.border.selected-indicatorobserveduse | #e53935 | 2px indicator bar on the selected tab or nav item. |
icon.stroke | color.icon.defaultobserveduse | #e99499 | Icons take the text colour (currentColor in CSS). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Inline | Standalone | Current |
|---|---|---|---|
default | Every value on this page comes from the token tables, which the exports embed by tag. | ||
hover | |||
focus-visible | |||
visited | |||
current |
Navigation to another document, section or resource. A link never performs
an action on the current page; that is a button. Links are recognisable by
the persistent underline as well as strong red color.text.link, distinct
from rose body text and headings (D-024).
A native <a href>. Inline: text plus the 1px underline. Standalone: the
same with a trailing arrow icon and a little horizontal padding so the hover
fill has room. Current: aria-current="page", brighter text and a 2px bar
replacing the underline.
| State | Visual | Non-colour channel |
|---|---|---|
| default | color.text.link; underline 1px color.text.link-underline offset 2px | the underline |
| hover | background → color.interaction.hover.bg-strong; text → color.text.link-hover; underline 2px in the text colour | underline thickens 1px → 2px; cursor: pointer |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px; no change to the underline | the ring |
| visited | color.text.link; underline unchanged | the underline continues to mark navigation; browsing history does not reduce its visibility |
| current | text → color.text.bright; the underline becomes a 2px color.border.selected-indicator bottom border; no hover fill | aria-current="page"; the bar’s 2px weight |
Precedence: current > hover; focus-visible is always drawn.
Native. Enter follows the link; Space never does. Skip links and in-page
links move focus to their target, which needs tabindex="-1" when it is not
focusable.
The link text describes the destination on its own (“Release notes”, never
“here”). Same destination, same text. aria-current="page" marks the
current document in navigation and nothing else. Links that open a new
window say so in text. Link text meets 4.5:1 on canvas, panel and raised
surfaces; the underline meets 3:1. The generated contrast report records exact
ratios for the current profile, including hover text and focus. Inline targets are exempt
from the 24×24 minimum; standalone links reach it through their line box.
Colour, underline thickness, background and outline; the arrow is an inline
SVG in currentColor. Hosts that cannot thicken an underline invert the link on
hover instead and record it. Hosts without aria-current expose the current
link as selected.
A link without an underline at rest. Blue or purple links. A hover that only recolours the text. A link that opens a dialog or submits a form. An underline drawn with a border on an inline link. A visited colour that fails 4.5:1. A button styled to look like a link. Text such as “click here”.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus to and from the link. |
| Enter | Follows the link (native); Space scrolls the page, never activates. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: button breadcrumbs sidebar-nav skip-link pagination
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
button-groupRelated independent native actions with individual tab stops.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
dependency.role-color-interaction-disabled-bg | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
dependency.role-color-text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-4 | space.4observeduse | 4px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | |
focus-visible | |
disabled |
Group related independent actions without turning them into an exclusive choice. Each button keeps its native type, accessible name and tab stop. Application effects belong to the host.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Named content on the default surface. | Native text and structure. |
| focus-visible | Dashed focus ring. | Native keyboard focus position. |
| disabled | Disabled semantic foreground, background and border at full opacity. | Native disabled state blocks activation. |
Use role=group with a visible or accessible label. Buttons retain distinct names and native disabled behavior. A button group does not add arrow-key navigation or selection semantics.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Visit each enabled button. |
| Enter / Space | Activate the focused native button. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
segmented-controlA short exclusive choice implemented with labelled native radios.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-border-width-emphasis | border.width.emphasisobserveduse | 2px | Selected indicator, sticky header rule, invalid border. |
dependency.role-color-action-primary-bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
dependency.role-color-action-primary-hover-bg | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover (7.92:1 with prose). |
dependency.role-color-action-primary-text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
dependency.role-color-border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
dependency.role-color-interaction-disabled-bg | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
dependency.role-color-interaction-focus-ring-container | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
dependency.role-color-interaction-hover-bg | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
dependency.role-color-interaction-selection-bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
dependency.role-color-interaction-selection-text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
dependency.role-color-status-danger-border | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
dependency.role-color-status-danger-text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
dependency.role-color-text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-focus-offset | focus.offsetobserveduse | -2px | outline-offset for controls. |
dependency.role-focus-ring | focus.ringobserveduse | 1px dashed #e53935 | outline on controls and rows. |
dependency.role-font-line-height-ui-md | font.line-height.ui-mdobserveduse | 18px | |
dependency.role-font-line-height-ui-sm | font.line-height.ui-smobserveduse | 16px | |
dependency.role-font-size-ui-md | font.size.ui-mdobserveduse | 13px | |
dependency.role-font-size-ui-sm | font.size.ui-smobserveduse | 12px | |
dependency.role-icon-size-md | icon.size.mdobserveduse | 16px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-16 | space.16observeduse | 16px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-24 | space.24observeduse | 24px | |
dependency.role-space-4 | space.4observeduse | 4px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | |
checked | |
focus-visible | |
disabled |
Choose one value from a short labelled set using native radios. This changes a value; use tabs for associated content panels. Native names, required checks, disabled fieldsets and form reset are preserved.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Named content on the default surface. | Native text and structure. |
| checked | Selected fill and a check indicator. | Native checked state. |
| focus-visible | Dashed focus ring. | Native keyboard focus position. |
| disabled | Disabled semantic foreground, background and border at full opacity. | Native disabled state blocks activation. |
Keep native radios and labels. A checked value has both a visible native indicator and its programmatic checked state. Do not hide all radios from assistive technology or put role=tab on the labels.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Enter the native radio group once and leave it. |
| Arrow keys / Space | Use native radio selection and wrapping. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
Text, password, number, search, textarea, select, checkbox, radio, switch, range, fieldsets, labels, help text, validation.
fieldThe label, control, help and message wrapper that every form control sits in; it fixes the stacking order, the required mark, the description links and the invalid message.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
label.required-mark | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
control.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
control.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
control.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
control.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | Letters, digits and hyphens; shown in the bar. |
required | |
invalid | |
disabled |
The wrapper that gives every control the same label, description and error treatment. Controls specify their box; the field specifies what sits above and below it and how they are linked. Every form control in this theme is rendered inside a field or a fieldset.
Label above the control, always stacked; the control slot; help text
below in color.text.muted; the validation message, which takes the help
slot visually while help stays in the DOM and aria-describedby lists both.
The required mark is present in the markup and shown only when the control
is required.
| State | Visual | Non-colour channel |
|---|---|---|
| default | label color.text.bright; help color.text.muted; control as its own component | — |
| required | * after the label in color.status.danger.text plus visually hidden “required” | required attribute; the mark |
| invalid | the control’s 2px color.status.danger.border; message with the ✕ glyph in color.status.danger.text; help visually hidden | border width 1 → 2px; glyph; aria-invalid |
| disabled | label and control text color.text.disabled; control color.interaction.disabled.bg with 1px color.border.disabled; help unchanged | disabled; cursor: not-allowed |
Precedence: disabled > invalid; required is shown in every state.
The field adds no behaviour. Clicking the label focuses the control
(native for). Focus order inside the field is the control’s own.
Every control has a programmatic label; placeholder text is never the
label. Help and message are linked with aria-describedby in that order so
the description is read before the error. aria-invalid="true" is set only
after interaction or submit. The message keeps its glyph so the state is not
colour-only. Contrast: label 10.10:1, help 5.66:1, message 5.26:1 on the
panel surface; disabled label 3.24:1 (exempt).
Plain block layout; the only pseudo-element is nothing, the mark is real
markup. Hosts without aria-describedby put help and message in the
control’s tooltip or accessible description and record it.
A floating label inside the control. A label to the left of the control. Placeholder text as the only label. An error shown by a red border alone. A message that replaces the help text in the DOM. A required mark that is only a colour change. An asterisk with no text alternative. Two controls in one field.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reaches the control; the label is not a tab stop. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: text-field textarea select checkbox radio-group fieldset number-field search-field
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-DENSITY FX-STATE-MATRIX
text-fieldSingle-line free-text entry, covering every native input type that renders as a line box; search, number and password are variants, not separate components.
Report an issue with this component
View source and copy recipe · Download standalone example
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-focus | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.ring-invalid | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
root.border-readonly | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
input.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
input.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
input.placeholder | color.text.placeholderapproveduse | #a3676b | Placeholder text, italic; D-027 darkened it to 4.71:1, just clear of the 4.5:1 floor. |
input.caret | color.code.caretobserveduse | #e99499 | Caret (Xresources *cursorColor). |
input.selection-bg | color.interaction.text-selection.bgobserveduse | #911410 | ::selection background. |
input.selection-text | color.interaction.text-selection.textobserveduse | #f4eeee | ::selection text. |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
label.required-mark | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
affix.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
affix.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
action.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
action.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
loading.glyph | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Text | Password with reveal | Search with clear | Number with stepper | With prefix and suffix |
|---|---|---|---|---|---|
default | Shown on your profile and in the window title. | Twelve characters or more; spaces allowed. | Escape clears the query. | Up and Down step by one; Shift steps by ten. | Host only; the path is fixed. |
hover | |||||
focus-visible | |||||
placeholder-shown | |||||
filled | |||||
required | |||||
invalid | |||||
invalid+focus-visible | |||||
invalid+hover | |||||
disabled | |||||
disabled+filled | |||||
read-only | |||||
read-only+focus-visible | |||||
loading |
Single-line free-text entry. Every native type that renders as a line box
(text, password, search, number, email, url, tel) is this
component; date and time inputs are the date-picker and time-picker
components because their popups are host surfaces.
Label above the control; the control box; optional prefix and suffix inside
the box separated by a color.border.divider rule; the input; optional
trailing actions (clear, reveal, stepper); help text below; the validation
message, which takes the help slot visually while help stays in the DOM and
aria-describedby lists both.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.control on color.surface.input; text color.text.default | — |
| hover | background → color.interaction.hover.bg; border unchanged | cursor: text |
| focus-visible | border → color.border.active; ring 1px dashed color.interaction.focus.ring at −2px | the ring |
| placeholder-shown | placeholder in color.text.placeholder, italic | italic |
| filled | as default with a value | — |
| required | * after the label in color.status.danger.text plus visually hidden “required” | required attribute; the mark |
| invalid | border 2px color.status.danger.border; message with the ✕ glyph | border width 1 → 2px; glyph; aria-invalid |
| invalid+focus-visible | the 2px danger border and a 1px dashed ring in color.interaction.focus.ring-container at −4px (the control form, recoloured; never the 2px container form) | double boundary |
| invalid+hover | as invalid with the hover background | cursor |
| disabled | text color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg; no hover; the label and help keep their colours | disabled; cursor: not-allowed |
| disabled+filled | as disabled with a value | disabled |
| read-only | no box: 1px dotted color.border.divider bottom edge on color.surface.canvas; text color.text.default; focusable and selectable | readonly; dotted edge |
| read-only+focus-visible | the dotted color.border.divider edge unchanged, plus the dashed ring at −2px | the ring |
| loading | size unchanged; a static ⋯ glyph in color.text.muted inside the box after the input and before any trailing action; input remains editable | glyph; aria-busy |
Precedence when several apply: disabled > loading > invalid > hover; focus-visible is always drawn.
Focus order is label (not focusable) → input → clear or reveal → stepper
buttons. The clear button is type="button" with aria-label="Clear <label>";
the reveal button toggles type between password and text and its own
aria-pressed. Escape in the search variant clears a non-empty value.
A programmatic label is required (<label for>). Help and message are linked
through aria-describedby in that order. aria-invalid="true" is set only
after the user interacted with the field or submitted the form, never on first
paint. The message is announced on submit through the form’s live region, not
on every keystroke. Contrast: text 8.65:1 on the input surface, placeholder
5.81:1, disabled text 3.33:1 (exempt), control border 4.45:1 (≥ 3:1), invalid
border 4.69:1, danger message 5.40:1. The box is at least 24×24 CSS pixels in
compact density.
Everything is expressible with border, background and outline; the only pseudo-element is the required mark. Toolkits without dashed outlines draw a solid 1px ring in the same colour and record the deviation. Toolkits without a read-only style treat read-only as default text and record it.
Rounded corners. A floating or animated label. An underline-only field with no box. A glow instead of the dashed ring. Placeholder text in red. Disabling by opacity. Using colour alone for the invalid state. A hover that changes the text colour.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus in and out; affix actions (clear, reveal, stepper) are separate tab stops after the input. |
| Escape | Clears the value in the search variant when it is non-empty; otherwise nothing. |
| Enter | Submits the owning form (native). |
| Up / Down | Number variant steps by the step attribute; Shift multiplies by ten. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: textarea select combobox search-field number-field field
Stress fixtures: FX-LONG FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
textareaMulti-line free-text entry in the same box as the text field; resizes vertically only and can show a character count.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-focus | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.ring-invalid | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
root.border-readonly | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
root.bg-readonly | color.surface.canvasapproveduse | #000000 | Page and terminal background. |
input.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
input.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
input.placeholder | color.text.placeholderapproveduse | #a3676b | Placeholder text, italic; D-027 darkened it to 4.71:1, just clear of the 4.5:1 floor. |
input.caret | color.code.caretobserveduse | #e99499 | Caret (Xresources *cursorColor). |
input.selection-bg | color.interaction.text-selection.bgobserveduse | #911410 | ::selection background. |
input.selection-text | color.interaction.text-selection.textobserveduse | #f4eeee | ::selection text. |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
label.required-mark | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
count.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
count.text-over | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | With character count |
|---|---|---|
default | Plain text; one change per line. | 46 / 200 Counts characters, not bytes. |
hover | ||
focus-visible | ||
placeholder-shown | ||
filled | ||
required | ||
invalid | ||
invalid+focus-visible | ||
disabled | ||
read-only |
Free text longer than a line: descriptions, messages, notes. It shares the text field’s box, states and message so a form reads as one system. Code goes in the code-editor component, not a textarea.
Label above; the box with the native <textarea> inside, at least three
rows tall, resizable downwards only; the optional count; help; the
validation message that takes the help slot visually while help stays in the
DOM.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.control on color.surface.input; text color.text.default | — |
| hover | background → color.interaction.hover.bg; border unchanged | cursor: text |
| focus-visible | border → color.border.active; ring 1px dashed color.interaction.focus.ring at −2px | the ring |
| placeholder-shown | placeholder in color.text.placeholder, italic | italic |
| filled | as default with a value | — |
| required | * after the label in color.status.danger.text plus visually hidden “required” | required; the mark |
| invalid | border 2px color.status.danger.border; message with the ✕ glyph; a count past its limit turns color.status.danger.text with the glyph | border width 1 → 2px; glyph; aria-invalid |
| invalid+focus-visible | the 2px danger border and the ring in color.interaction.focus.ring-container at −4px | double boundary |
| disabled | text color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg; no resize handle | disabled; cursor: not-allowed |
| read-only | no box: 1px dotted color.border.divider bottom edge on color.surface.canvas; no resize handle; selectable | readonly; dotted edge |
Precedence: disabled > invalid > hover; focus-visible is always drawn.
Native editing. Enter inserts a line break and never submits. Tab leaves the control; a host that needs literal tabs provides a separate command and documents it. The count is not focusable.
A programmatic label is required. Help, count and message are linked
through aria-describedby in that order. The count is aria-live="polite"
and throttled so it does not announce every keystroke; the limit is stated
in the help text so it is known before typing. aria-invalid="true" only
after interaction or submit. Contrast as the text field: text 8.65:1,
placeholder 5.81:1, control border 4.45:1, invalid border 4.69:1, message
5.40:1.
Border, background, outline and resize: vertical. Hosts without a resize
handle grow the box with content up to a maximum and record it. Hosts
without dashed outlines draw a solid 1px ring.
Horizontal or free resizing. A box narrower than its label column. A count that only changes colour past the limit. A glow instead of the dashed ring. Rounded corners. An auto-growing box with no maximum. Tab inserting a character. Placeholder text as the label. Disabling by opacity.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus in and out; Tab never inserts a tab character. |
| Enter | Inserts a line break; never submits. |
| Ctrl+Enter / Cmd+Enter | May submit the owning form when the host binds it; announced in the help text. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: text-field field code-editor
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-OVERFLOW FX-STATE-MATRIX
selectA themed single-choice combobox or multiple-choice list backed by a native select for forms and the no-JavaScript fallback.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-focus | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.ring-invalid | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
control.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
control.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
chevron.stroke | color.icon.defaultobserveduse | #e99499 | Icons take the text colour (currentColor in CSS). |
option.bg | color.surface.raisedapproveduse | #160b0b | Sticky headers, menus, popovers; D-027 recessed it so an open list reads as depth rather than a lighter panel. |
option.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
option.bg-selected | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
option.text-selected | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
group.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
label.required-mark | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Single | With option groups | Multiple |
|---|---|---|---|
default | New windows open here. | Fallbacks use recorded size-adjust metrics. | Click or Space toggles an option; Shift+arrows selects a range. The native fallback uses Ctrl/Command+click. |
hover | |||
focus-visible | |||
required | |||
invalid | |||
invalid+focus-visible | |||
disabled |
Choosing one option, or several, from a fixed list. D-025 requires theme-owned choice surfaces in web applications. The native select remains the form-value and constraint source. A searchable or asynchronous list is the separate combobox component.
Label above; a theme-owned trigger and listbox for single choices, or an
always-visible multiselectable listbox. Each selected option has a check mark
and the canonical selection fill. The hidden native <select> retains its
name, options, current values and defaults. Help and validation are linked
to the visible control. Without JavaScript the original native control is visible.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.control on color.surface.input; value color.text.default; chevron color.icon.default | the chevron |
| hover | background → color.interaction.hover.bg; border unchanged | cursor: default; hover-capable pointers only |
| focus-visible | border → color.border.active; ring 1px dashed color.interaction.focus.ring at −2px | the ring |
| required | * after the label in color.status.danger.text plus visually hidden “required” | required; the mark |
| invalid | border 2px color.status.danger.border; message with the ✕ glyph | border width 1 → 2px; glyph; aria-invalid |
| invalid+focus-visible | the 2px danger border and the ring in color.interaction.focus.ring-container at −4px | double boundary |
| disabled | text and chevron color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg | disabled; cursor: not-allowed |
Inside the theme-owned list, a selected option is
color.interaction.selection.bg with color.interaction.selection.text and
group labels are color.text.muted on color.surface.raised. Precedence:
disabled > invalid > hover; focus-visible is always drawn.
The single control follows the select-only combobox pattern: arrows open and move the active option, Home/End reach the ends, typing finds an option, and Enter/Space commits. Escape and Tab close without committing. Multiple choices use arrows, Space or click to toggle, Shift with arrows for a range, and Ctrl/Command+A to select or clear all enabled choices. No modifier is required for ordinary multiple selection. The native fallback uses host keyboard behavior.
A programmatic label is required; the first option is a real choice or a
placeholder option that is disabled and selected with text such as
“Choose a workspace”, never an empty string. aria-invalid="true" only after
interaction or submit. Help and message through aria-describedby. The
themed popup uses the declared contrast roles. The visible control receives
the label, required/invalid state and descriptions. A failed native constraint
focuses that visible control and exposes a theme-owned error. Disabled options,
optgroups and fieldsets remain unavailable. Current values and defaults survive
reconnection, programmatic updates and form reset. Automated evidence records
its actual environments; it does not imply a manual screen-reader pass.
Use the packaged behavior and complete copy closure for web implementations. The supported enhancement can apply the same renderer to existing selects without moving them or duplicating form values. Native platforms keep the mappings listed above; no-JavaScript pages expose the native fallback.
An unthemed browser popup presented as a fully themed implementation. A background-image chevron in a literal colour. Rounded corners. A select whose first option is blank. A select that triggers navigation on change. Invalid shown by colour alone. A glow instead of the ring. Disabling by opacity.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus in and out. |
| Space / Alt+Down | Opens the themed popup without committing a choice. |
| Up / Down | Opens the single list or moves its active option; Enter or Space commits. In the multiple list, moves the active option. |
| Typing | Moves to an enabled option starting with the typed characters. |
| Space / click; Shift+Up / Shift+Down; Ctrl+Command+A | Toggles a multiple option; extends a range; selects or clears all enabled options. |
| Escape / Tab | Closes the single popup without committing the active option. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: combobox multiselect text-field field menu
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
checkboxThe native checkbox drawn as a 16px square that fills with the primary action colour and a ✓ or – glyph; single, or a fieldset group of independent choices.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
box.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
box.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
box.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
box.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
box.bg-checked | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
box.bg-checked-hover | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover (7.92:1 with prose). |
box.glyph | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
box.ring-checked | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
box.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
box.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
box.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
box.glyph-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
text.default | color.text.defaultobserveduse | #e99499 | Interface and body text. |
text.disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
legend.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
legend.required-mark | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Single | Group |
|---|---|---|
default | ||
hover | ||
focus-visible | ||
checked | ||
mixed | ||
checked+focus-visible | ||
checked+disabled | ||
disabled | ||
invalid | ||
required |
An independent yes/no choice, or several of them under one legend. A setting that takes effect immediately is a switch; one of several exclusive options is a radio group. A parent checkbox over a partly selected set shows the mixed state.
The box is the native input itself with appearance: none, so it keeps
native focus, form and keyboard behaviour. The ✓ and – glyphs are inline
SVGs stacked over it in the same 16px cell. The option row is a <label>,
at least 24px tall, which makes the text part of the target. A group is a
<fieldset> with a <legend>.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 16px box, 1px color.border.control on color.surface.input; text color.text.default | — |
| hover | box background → color.interaction.hover.bg; checked box → color.action.primary.hover-bg | cursor: pointer; hover-capable pointers only |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px around the box | the ring |
| checked | box fills color.action.primary.bg; ✓ in color.action.primary.text; border stays color.border.control | the ✓ glyph; checked |
| mixed | box fills color.action.primary.bg; – in color.action.primary.text | the – glyph; indeterminate / aria-checked="mixed" |
| checked+focus-visible | the fill and a ring in color.interaction.focus.ring-container | glyph and ring |
| checked+disabled | box color.interaction.disabled.bg with 1px color.border.disabled; ✓ and text in color.text.disabled | glyph; disabled; cursor: not-allowed |
| disabled | box color.interaction.disabled.bg with 1px color.border.disabled; text color.text.disabled | disabled; cursor: not-allowed |
| invalid | box border 2px color.status.danger.border; message with the ✕ glyph | border width 1 → 2px; glyph; aria-invalid |
| required | * after the text or legend in color.status.danger.text plus visually hidden “required” | required; the mark |
Precedence: disabled > invalid > checked > hover; focus-visible is always drawn.
Native. Space toggles; Enter does nothing. Every checkbox in a group is a separate tab stop, unlike radios. A mixed parent becomes checked on the first Space and unchecked on the second, never mixed again by keyboard.
The text is the label through the wrapping <label>; a group has its
legend read before each option. indeterminate is a DOM property with no
attribute, so the host mirrors it to data-state-mixed for styling and the
state reads as “mixed” natively. A required group marks the legend, not
every option, and validates on submit. Contrast: text 8.43:1, glyph 9.28:1
on the fill, box edge 4.45:1 on the input surface and 4.33:1 against the
panel when checked, ring 4.69:1, container ring 5.57:1 on the fill.
A square with a border and a fill plus two glyphs. Hosts that draw their own indicator set it square, 16px, and use the same fill and glyph colours; a host without a mixed indicator draws the dash itself or records the gap.
Rounded or circular boxes. A tick drawn with a border trick or an icon
font. A checked state shown by border colour alone with no glyph. A box
smaller than 16px or a row shorter than 24px. The mixed state used as a
third value. A checkbox that saves on change. Disabling by opacity. A div
with role="checkbox".
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves through every checkbox; each is its own tab stop. |
| Space | Toggles; a mixed box becomes checked. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: radio-group switch fieldset field list
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
radio-groupA fieldset of native radios for one exclusive choice; each radio is a 16px square that fills with the primary action colour and an inner square when checked.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
box.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
box.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
box.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
box.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
box.bg-checked | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
box.bg-checked-hover | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover (7.92:1 with prose). |
box.dot | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
box.ring-checked | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
box.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
box.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
box.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
box.dot-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
text.default | color.text.defaultobserveduse | #e99499 | Interface and body text. |
text.disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
legend.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
legend.required-mark | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Vertical | Horizontal |
|---|---|---|
default | ||
hover | ||
focus-visible | ||
checked | ||
checked+focus-visible | ||
disabled | ||
invalid | ||
required |
One choice out of a few visible options. Below two options a checkbox or switch is used; above roughly six, a select. The radios are square, as every control in this theme is, and are told apart from checkboxes by the inner square instead of the ✓ glyph and by arrow-key movement instead of separate tab stops.
A <fieldset> whose <legend> asks the question; a list of <label> rows
each holding the native input, drawn as a 16px square with appearance: none, and its text; the validation message at the end. The inner square is
a real element stacked over the input in the same cell.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 16px square, 1px color.border.control on color.surface.input; text color.text.default | — |
| hover | box background → color.interaction.hover.bg; a checked box → color.action.primary.hover-bg | cursor: pointer; hover-capable pointers only |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px around the box | the ring |
| checked | box fills color.action.primary.bg; an 8px inner square in color.action.primary.text; border stays color.border.control | the inner square; checked |
| checked+focus-visible | the fill and a ring in color.interaction.focus.ring-container | inner square and ring |
| disabled | boxes color.interaction.disabled.bg with 1px color.border.disabled; inner square and text color.text.disabled | disabled; cursor: not-allowed |
| invalid | every box border 2px color.status.danger.border; message with the ✕ glyph | border width 1 → 2px; glyph; aria-invalid |
| required | * after the legend in color.status.danger.text plus visually hidden “required” | required; the mark |
Precedence: disabled > invalid > checked > hover; focus-visible is always drawn.
Native. The group is one tab stop; the arrow keys move both focus and the check, so moving through a group changes the value. That is why a radio group never triggers navigation or a request on change; the value is read on submit or through an explicit action.
The legend is read before each option and is the group’s name. Options
share a name. The message is linked with aria-describedby on the
fieldset and aria-invalid is set on the inputs after submit. A group with
no default checked radio must have a required mark or a “none” option, so
the empty state is intentional. Contrast: text 8.43:1, inner square 9.28:1
on the fill, box edge 4.45:1 on the input surface and 4.33:1 against the
panel when checked, ring 4.69:1, container ring 5.57:1.
Square, border, fill and one inner square; no pseudo-element is needed. Hosts that insist on a circular radio indicator record the deviation rather than replacing the control with a custom widget.
Circular radios. A checked state shown by border colour alone with no inner
square. Radios that submit or navigate on change. A group with no legend or
with the legend rendered as a label on one option. Separate tab stops per
radio. One radio on its own. Disabling by opacity. A div with
role="radiogroup" built from spans.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Enters the group on the checked radio, or the first when none is checked; leaves it entirely. |
| Up / Down / Left / Right | Moves the check to the previous or next radio and selects it (native); wraps at the ends. |
| Space | Checks the focused radio when none is checked. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: checkbox switch segmented-control fieldset field select
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
switchAn on/off control that takes effect immediately; a button with role switch and aria-checked, drawn as a square-cornered track with a square thumb that fills with the primary colour when on.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
track.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
track.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
track.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
track.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
track.bg-checked | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
track.bg-checked-hover | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover (7.92:1 with prose). |
track.ring-checked | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
track.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
track.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
thumb.bg | color.text.defaultobserveduse | #e99499 | Interface and body text. |
thumb.bg-checked | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
thumb.bg-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
name.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
state.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
name.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | With labels |
|---|---|---|
default |
Follow system theme
| |
hover | ||
focus-visible | ||
checked | ||
checked+focus-visible | ||
disabled | ||
checked+disabled |
A setting that turns on or off the moment it is toggled: notifications, wrap lines, follow system theme. A choice that is only applied on submit is a checkbox. The switch is a button because it acts; it is not a form field.
A <button role="switch">; inside it the track, a 32×16px box with square
corners, and the thumb, a 10px square that sits at the start when off and
at the end when on. The with-labels variant adds a visible name before the
button and an “Off” / “On” word after the track.
| State | Visual | Non-colour channel |
|---|---|---|
| default | track 1px color.border.control on color.surface.input; thumb color.text.default at the start | thumb position |
| hover | track background → color.interaction.hover.bg; on track → color.action.primary.hover-bg | cursor: pointer; hover-capable pointers only |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the track | the ring |
| checked | track fills color.action.primary.bg; thumb color.action.primary.text moves to the end; border stays color.border.control; the word reads “On” | aria-checked="true"; thumb position; the word |
| checked+focus-visible | the fill and a ring in color.interaction.focus.ring-container on the track | position and ring |
| disabled | track color.interaction.disabled.bg with 1px color.border.disabled; thumb and name color.text.disabled | disabled; cursor: not-allowed |
| checked+disabled | as disabled with the thumb at the end | disabled; aria-checked="true"; thumb position |
The thumb moves without animation by default; a host may transition
transform for at most 120ms under prefers-reduced-motion: no-preference. Precedence: disabled > checked > hover; focus-visible is
always drawn.
Space and Enter toggle. Nothing else is bound; there is no arrow-key movement between switches. Focus never moves as a result of toggling.
role="switch" on a native button gives the correct name, role and value
with the button’s own keyboard handling; only aria-checked is scripted.
The name states what is on (“Wrap long lines”), never the current value;
the “Off” / “On” word is aria-hidden because assistive technology reads
aria-checked. The state is also carried by thumb position and, in the
with-labels variant, the word. Contrast: thumb 8.65:1 on the off track,
9.28:1 on the on track; track edge 4.45:1; ring 4.69:1 off, 5.57:1 on.
Two nested rectangles, a border and a fill; the thumb position is layout, not a transform, so it survives hosts with no animation. Hosts with a native switch use it, square it, and map the two fills.
A pill-shaped track or a round thumb. A switch that only takes effect on submit. A checkbox input styled as a switch. Colour as the only difference between on and off. A green on colour. A label that reads “On” as the name. Disabling by opacity. A sliding animation longer than 120ms or one that runs under reduced motion.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus to and from the switch. |
| Space / Enter | Toggles aria-checked and applies the change at once. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: checkbox radio-group icon-button field
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
fieldsetThe native fieldset that groups related fields under a legend inside a 1px divider border; disabling it disables everything inside.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
legend.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
body.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
legend.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
field.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
field.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
field.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
field.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
field.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | Pixels between windows. Negative values overlap the screen edge. |
disabled |
Grouping fields that belong together: an address, a set of credentials, the options of one feature. The legend names the group for sighted users and assistive technology alike, and disabling the fieldset disables every control inside it in one step. Checkbox and radio groups use their own components, which are fieldsets too.
The native <fieldset> with a 1px color.border.divider border and 16px
padding; the <legend> in color.text.bright sitting in the top border;
the fields stacked inside. The border is a section rule, decorative by
design; the legend and the spacing carry the grouping.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.divider border; legend color.text.bright; fields as their own components | the border and the legend |
| disabled | legend and labels color.text.disabled; every control inside takes its own disabled treatment (color.interaction.disabled.bg, color.border.disabled); the border unchanged | disabled on the fieldset; cursor: not-allowed on the controls |
None of its own. Disabled fieldsets remove their controls from the tab order natively, so a disabled group is skipped rather than walked.
The legend is the accessible name of the group and is announced before the first field inside; it says what the group is, not what to do. A group never nests deeper than two levels. Disabling a group hides its controls from keyboard users, so a disabled group states why in text before it or keeps its controls enabled. Contrast: legend 10.10:1, labels 8.43:1, help 5.66:1 on the panel surface.
A border and a heading. Hosts without a legend-in-border draw the heading above the box and record it. Disabled propagation is native on the web and in GTK and Qt; a host without it disables each control.
A fieldset with no legend or with a legend hidden visually. A card used
instead of a fieldset for form controls. Rounded corners. A 2px or
border.control border on the group, which would read as a control. A
legend in accent red. Disabling only the legend. Nesting three levels deep.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves through the fields inside in document order; the legend is not a tab stop. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: field checkbox radio-group text-field card
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-DENSITY FX-STATE-MATRIX
number-fieldThe text-field box around a native number input with right-aligned tabular digits, an optional unit affix and two square stepper buttons that replace the host spinner.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-focus | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.ring-invalid | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
root.border-readonly | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
root.bg-readonly | color.surface.canvasapproveduse | #000000 | Page and terminal background. |
input.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
input.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
input.caret | color.code.caretobserveduse | #e99499 | Caret (Xresources *cursorColor). |
unit.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
unit.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
action.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
action.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
action.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | With unit |
|---|---|---|
default | Between 1 and 20. | Between 0 and 64 pixels; Up and Down step by one. |
hover | ||
focus-visible | ||
invalid | ||
invalid+focus-visible | ||
disabled | ||
read-only |
Entering a bounded number: a gap in pixels, a timeout in milliseconds, a
count. The theme replaces the host spinner, which is tiny and inconsistent,
with two square buttons that are real tab stops. Free-form numeric text such
as a version or an identifier is a text field with inputmode.
Label above; the box; the input with digits aligned to the end; the optional unit; the stepper with + over −, each button half the control height and at least 24px wide; help stating the range; the validation message.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.control on color.surface.input; digits color.text.default; unit color.text.muted; stepper glyphs color.action.tertiary.text | — |
| hover | background → color.interaction.hover.bg; a hovered stepper button → color.action.tertiary.hover-bg | cursor: text on the input, pointer on the buttons |
| focus-visible | border → color.border.active; ring 1px dashed color.interaction.focus.ring at −2px on the box, or on the focused stepper button | the ring |
| invalid | border 2px color.status.danger.border; message with the ✕ glyph | border width 1 → 2px; glyph; aria-invalid |
| invalid+focus-visible | the 2px danger border and the ring in color.interaction.focus.ring-container at −4px | double boundary |
| disabled | text, unit and glyphs color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg; buttons disabled | disabled; cursor: not-allowed |
| read-only | no box: 1px dotted color.border.divider bottom edge on color.surface.canvas; the stepper is hidden | readonly; dotted edge; no buttons |
Precedence: disabled > invalid > hover; focus-visible is always drawn.
Native arrow stepping inside the input; the stepper buttons are ordinary
buttons after it in the tab order and call stepUp() / stepDown().
Typing a value outside the range is allowed and reported as invalid on
blur or submit, never clamped silently.
A programmatic label is required. The unit is aria-hidden and repeated in
the label or help (“Inner gap (px)”), so it is never lost. Stepper buttons
are named “Increase
The text field’s box plus two buttons; the host spinner is hidden with the
engine’s pseudo-elements and appearance: textfield. Hosts whose spin
buttons cannot be replaced style them square in the same colours and record
it.
The host’s default spinner. Round or overlapping stepper buttons. A value clamped silently on blur. Left-aligned digits. A unit typed into the value. A number field for phone numbers, postcodes or identifiers. Invalid shown by colour alone. Disabling by opacity.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Input, then the increase button, then the decrease button. |
| Up / Down | Steps by the step attribute (native); Shift+Up / Shift+Down step by ten where the host binds it. |
| Page Up / Page Down | Steps by ten times the step where the host binds it. |
| Home / End | Jumps to min or max where the host binds it. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: text-field field range search-field
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
search-fieldThe text-field box around a native search input with a magnifier, a clear button that appears with a value, an optional shortcut hint, a loading glyph and a no-results status line.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-focus | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
input.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
input.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
input.placeholder | color.text.placeholderapproveduse | #a3676b | Placeholder text, italic; D-027 darkened it to 4.71:1, just clear of the 4.5:1 floor. |
input.caret | color.code.caretobserveduse | #e99499 | Caret (Xresources *cursorColor). |
icon.stroke | color.icon.defaultobserveduse | #e99499 | Icons take the text colour (currentColor in CSS). |
loading.glyph | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
clear.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
clear.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
kbd.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
kbd.border | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
status.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | With shortcut hint |
|---|---|---|
default | Matches names and paths; Escape clears. No results for “site.css”. |
/
Press / anywhere to search. No results for “focus.ring”. |
hover | ||
focus-visible | ||
placeholder-shown | ||
filled | ||
loading | ||
disabled | ||
no-results |
Filtering a list or table or searching a site. It is the text field’s box with the parts a search needs: an icon that says what the box is for, a clear button, a place for the shortcut that reaches it, a loading glyph while results arrive and a status line when nothing matches. A search that offers suggestions as you type is the combobox component.
Label above (or visually hidden); the box with the magnifier, the input, the clear button and the optional hint; help; the status line. The loading glyph takes the magnifier’s slot so the box never changes size.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.control on color.surface.input; magnifier color.icon.default; text color.text.default | the magnifier |
| hover | background → color.interaction.hover.bg; the clear button → color.action.tertiary.hover-bg when hovered | cursor: text |
| focus-visible | border → color.border.active; ring 1px dashed color.interaction.focus.ring at −2px; the hint hides | the ring |
| placeholder-shown | placeholder in color.text.placeholder, italic; no clear button | italic; the clear button is absent |
| filled | as default with a value; the clear button is present | the clear button |
| loading | the static ⋯ glyph in color.text.muted replaces the magnifier; the input stays editable | glyph; aria-busy |
| disabled | text and icon color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg; no clear button | disabled; cursor: not-allowed |
| no-results | the status line “No results for …” in color.text.muted below the help; the box unchanged | the status text; role="status" |
Precedence: disabled > loading > hover; focus-visible is always drawn. A search field is never invalid; an empty result is a status, not an error.
Escape clears a non-empty value and keeps focus, which is native for
type="search" in most engines and scripted where it is not. The clear
button is a separate tab stop only while it is visible. The shortcut in the
hint is bound by the host and never captured while another field has focus.
A programmatic label is required even when hidden. The landmark
role="search" lets users jump to it. The status line is role="status"
so “No results” is announced once, after the query settles, and the result
count is announced there too (“12 results”). The magnifier and hint are
aria-hidden; the clear button is named “Clear
The text field plus icons and a status line. Hosts hide their own cancel button; hosts that cannot draw the hint omit it and keep the shortcut in the tooltip.
Rounded or pill-shaped search boxes. A magnifier button that submits instead of an icon. A spinner while loading. Results announced on every keystroke. “No results” in red or with the ✕ glyph. A clear button that is always visible. A search field without a label. Disabling by opacity.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Input, then the clear button when it is visible. |
| Escape | Clears a non-empty value and keeps focus; with an empty value, nothing. |
| Enter | Runs the search when the host does not search as you type. |
| / or Ctrl+K | Focuses the field from anywhere on the page when the host binds the shortcut shown in the hint. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: text-field combobox command-palette toolbar empty-state kbd
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
rangeA bounded native numeric slider with visible output.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-action-primary-bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
dependency.role-color-border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
dependency.role-color-interaction-disabled-bg | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
dependency.role-color-text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | |
focus-visible | |
disabled |
Set a numeric value with a native range input and a visible output. The input retains min, max, step, disabled, name/value submission and form reset. Platform track geometry is a native rendering boundary; the semantic accent and focus roles remain explicit.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Named content on the default surface. | Native text and structure. |
| focus-visible | Dashed focus ring. | Native keyboard focus position. |
| disabled | Disabled semantic foreground, background and border at full opacity. | Native disabled state blocks activation. |
Retain the input’s accessible name and native value semantics. The visible output does not replace its label. Put units in the label or help text. No value changes solely from receiving focus.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reach and leave the slider. |
| Arrow keys / Home / End | Use native stepping, minimum and maximum behavior. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
Combobox, multiselect, date and time, file selection, repeaters, wizard, bounded form-builder specimen.
comboboxA text input that filters a listbox of suggestions; the APG combobox with list autocomplete, rendered with the list open.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-focus | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.ring-invalid | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
input.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
input.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
input.placeholder | color.text.placeholderapproveduse | #a3676b | Placeholder text, italic; D-027 darkened it to 4.71:1, just clear of the 4.5:1 floor. |
input.caret | color.code.caretobserveduse | #e99499 | Caret (Xresources *cursorColor). |
toggle.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
toggle.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
listbox.bg | color.surface.raisedapproveduse | #160b0b | Sticky headers, menus, popovers; D-027 recessed it so an open list reads as depth rather than a lighter panel. |
listbox.border | color.border.overlayproposeduse-and-reportD-008 | #e53935 | Every floating layer's 1px border. |
option.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
option.bg-hover | color.interaction.hover.bg-strongobserveduse | #630f0d | Hover on menu items; D-027 moved links to interaction.hover.bg. |
option.text-hover | color.text.link-hoverobserveduse | #f4eeee | Link text on hover, over interaction.hover.bg-strong. |
option.bg-selected | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
option.text-selected | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
option.ring-selected | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
group.text | color.text.subtleapproveduse | #ad7175 | Metadata, comments, line numbers. |
group.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
empty.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Flat list | Grouped options | No results |
|---|---|---|---|
default |
Down opens the list; Enter accepts. |
Recent hosts first. |
Escape clears the filter. |
hover | |||
focus-visible | |||
open | |||
closed | |||
selected | |||
invalid | |||
invalid+focus-visible | |||
disabled | |||
no-results |
A text input whose value is completed from a list of suggestions. It is the right control when the set is large or open-ended (fonts, hosts, tags); a closed set of a dozen values is the select component. The demonstration renders the list open so every option state is visible without a script.
Label above the box; the box with the input and an optional toggle; the
listbox directly below, on color.surface.raised behind a 1px
color.border.overlay; options in color.text.default, an optional group
heading in color.text.subtle above a color.border.divider rule; the empty
message in color.text.muted; help and validation message below, as in the
text field.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.control on color.surface.input; list on color.surface.raised with a 1px color.border.overlay | — |
| hover | box background → color.interaction.hover.bg; the hovered option → color.interaction.hover.bg-strong with color.text.link-hover | cursor: text on the box, default on the list |
| focus-visible | box border → color.border.active; ring 1px dashed color.interaction.focus.ring at −2px | the ring |
| open | list visible below the box; toggle glyph rotated 180° | aria-expanded="true"; the toggle glyph |
| closed | list absent; the box unchanged | aria-expanded="false"; nothing below the box |
| selected | the active option filled color.interaction.selection.bg with color.interaction.selection.text and a 1px dashed ring in color.interaction.focus.ring-container | aria-selected="true"; aria-activedescendant; the ring |
| invalid | box border 2px color.status.danger.border; message with the ✕ glyph | border width 1 → 2px; glyph; aria-invalid |
| invalid+focus-visible | the 2px danger border and the ring in color.interaction.focus.ring-container at −4px | double boundary |
| disabled | text color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg; list never opens | disabled; cursor: not-allowed |
| no-results | the list holds only the empty row in color.text.muted, italic | aria-disabled row; italic; a polite announcement of zero matches |
Precedence when several apply: disabled > invalid > selected > hover; focus-visible is always drawn on the box, and the selection ring on the option.
DOM focus never leaves the input. Down and Up open the list and move the
active option, which the input reports through aria-activedescendant; Enter
accepts it; Escape closes the list and, when the list is already closed,
clears the value. Alt+Down and Alt+Up open and close without changing the
active option. Home and End move the caret. Tab closes the list and keeps
whatever is typed. Typing filters the list on every keystroke; the number of
matches is announced through a polite live region, not by re-reading the list.
The input carries role="combobox", aria-autocomplete="list",
aria-expanded, aria-controls pointing at the listbox and, while an option
is active, aria-activedescendant. The listbox has an accessible name
(aria-label or aria-labelledby the field label); options are role="option"
with aria-selected on the active one only; groups are role="group" with
aria-labelledby their heading. The empty row is a disabled option so the
list is never empty; the zero-match count is announced through the same polite
live region as every other count. Contrast: option text 8.43:1 on the
raised surface, selection text 12.47:1 on the selection fill, the selection ring
7.48:1 on the fill, the list border 4.57:1 on the raised surface, group
headings 4.96:1.
Everything is expressible with background, border and outline; the toggle
rotation is a transform inside the motion budget. Toolkits with a native
completion popup use it and map the option fill to the selection roles; the
list border is the popover frame. Toolkits without aria-activedescendant
move real focus to the option and record the deviation.
A select restyled as a combobox. Moving DOM focus into the list on Down. A list that floats over the page with a shadow or a rounded frame. Highlighting the active option with a colour change of the text alone. A no-results state that silently shows the full list. Opening on hover. A list that slides or fades in for longer than 150 ms.
| Key | Action |
|---|---|
| Down / Up | Opens the list if closed; moves the active option, wrapping at either end; the input keeps DOM focus. |
| Enter | Accepts the active option into the input and closes the list. |
| Escape | Closes the list; a second Escape clears the input. |
| Alt+Down / Alt+Up | Opens or closes the list without moving the active option. |
| Home / End | Moves the caret inside the input, never the active option. |
| Tab | Leaves the control; an open list closes and the typed text stays. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: text-field select multiselect search-field menu command-palette
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX
date-pickerA themed date editor and calendar, alone or as a from/to pair, backed by native form values and constraints.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-focus | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.ring-invalid | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
root.border-readonly | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
root.bg-readonly | color.surface.canvasapproveduse | #000000 | Page and terminal background. |
input.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
input.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
input.caret | color.code.caretobserveduse | #e99499 | Caret (Xresources *cursorColor). |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
legend.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
separator.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
calendar.bg | color.surface.raisedapproveduse | #160b0b | Sticky headers, menus, popovers; D-027 recessed it so an open list reads as depth rather than a lighter panel. |
calendar.border | color.border.overlayproposeduse-and-reportD-008 | #e53935 | Every floating layer's 1px border. |
calendar.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
calendar.selected-bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
calendar.selected-text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Single date | From / to |
|---|---|---|
default | Within 2026. | Inclusive; at most 92 days. |
hover | ||
focus-visible | ||
invalid | ||
invalid+focus-visible | ||
disabled | ||
read-only |
Choose a date by explicit text entry or a themed calendar. A range is two separately labeled date controls. D-025 requires theme-owned web controls rather than browser popup styling.
A label, editor, calendar trigger, format hint and validation message. The native date input remains the successful form control and owns its value, defaults and constraints. It is hidden only after enhancement.
| State | Treatment |
|---|---|
| default | Input surface, default text and control border. |
| hover | The declared hover fill. |
| focus-visible | Active border and the canonical focus ring. |
| invalid | Danger border and a linked message. |
| invalid+focus-visible | Danger border plus the container focus ring. |
| disabled | Disabled colors; editor and calendar actions unavailable. |
| read-only | Readable canvas text and bottom edge; calendar action unavailable. |
Use the declared state tokens: default input surface and control border; hover fill; the canonical focus ring; danger border and message for invalid input; disabled colors and unavailable actions. Read-only values remain readable and cannot open a picker or change through step buttons. The date read-only treatment retains the canvas surface and unobtrusive bottom edge.
Alt+Down opens the calendar. Arrows move by a day or week, Home/End reach week boundaries, and PageUp/PageDown change months. Enter/Space chooses a focused date. Escape closes and restores editor focus. Tab leaves normally; the calendar is a nonmodal popup.
The visible editor receives the original label, descriptions, required state and error state. Format hints are always available. Native min, max, step and required constraints remain authoritative. Invalid entry focuses the visible editor and exposes a theme-owned message. Calendar selection is a fill; keyboard focus is a separate ring. Disabled dates cannot be selected. The range validates that its end is not before its start. Form reset restores the native defaults.
Automated evidence records its actual browser, states and limitations; it is not a manual screen-reader acceptance.
The package and complete copy distribution include the renderer. The public control enhancement applies the same behavior to existing native markup. The no-JavaScript fallback and non-web platform mappings remain native.
An unthemed host popup presented as fully themed. A second named input that duplicates form values. Discarding native constraints, labels or reset behavior. Literal colors, rounded corners, or a selected fill with no distinguishable focus boundary.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves through the editor, calendar trigger and other controls. |
| Alt+Down | Opens the calendar from the editor. |
| Arrows / Home / End | Moves among dates; Home and End reach the beginning and end of the week. |
| PageUp / PageDown | Moves to the previous or next month. |
| Enter / Space | Chooses a focused calendar date or activates a month button. |
| Escape | Closes the calendar and returns focus to the editor. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: text-field time-picker field fieldset
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
time-pickerA themed time editor with explicit step buttons, backed by native time values and constraints.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-focus | color.border.activeobserveduse | #e53935 | Active or focused control border. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
input.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
input.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
input.caret | color.code.caretobserveduse | #e99499 | Caret (Xresources *cursorColor). |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Time |
|---|---|
default | Between 06:00 and 22:00, in five-minute steps. |
hover | |
focus-visible | |
invalid | |
disabled |
Enter a time explicitly or adjust it using themed step buttons. D-025 requires theme-owned web controls rather than browser popup styling.
A label, editor, increase/decrease buttons, format hint and validation message. The native time input remains the successful form control and owns its value, defaults and constraints. It is hidden only after enhancement.
| State | Treatment |
|---|---|
| default | Input surface, default text and control border. |
| hover | The declared hover fill. |
| focus-visible | Active border and the canonical focus ring. |
| invalid | Danger border and a linked message. |
| disabled | Disabled colors; editor and step actions unavailable. |
Use the declared state tokens: default input surface and control border; hover fill; the canonical focus ring; danger border and message for invalid input; disabled colors and unavailable actions. Read-only values remain readable and cannot open a picker or change through step buttons. The date read-only treatment retains the canvas surface and unobtrusive bottom edge.
Type HH:MM or HH:MM:SS in the editor. Tab reaches the named step buttons; Enter/Space activates them. Each step uses the native input step attribute. With step=“any”, use direct entry and the step buttons remain unavailable.
The visible editor receives the original label, descriptions, required state and error state. Format hints are always available. Native min, max, step and required constraints remain authoritative. Invalid entry focuses the visible editor and exposes a theme-owned message. No host clock popup or browser segment selection is used in the enhanced control. Form reset restores the native defaults.
Automated evidence records its actual browser, states and limitations; it is not a manual screen-reader acceptance.
The package and complete copy distribution include the renderer. The public control enhancement applies the same behavior to existing native markup. The no-JavaScript fallback and non-web platform mappings remain native.
An unthemed host popup presented as fully themed. A second named input that duplicates form values. Discarding native constraints, labels or reset behavior. Literal colors, rounded corners, or a selected fill with no distinguishable focus boundary.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves between the time editor, step buttons and other controls. |
| Typing | Edits HH:MM or HH:MM:SS text; native min, max and step validate it. |
| Enter / Space on a step button | Increases or decreases by the native step. With step=any, buttons are disabled. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: text-field date-picker number-field field
Stress fixtures: FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
file-inputA native file input behind a label styled as a secondary button, optionally inside a drop zone, with a list of chosen files and per-file errors.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
button.bg | color.action.secondary.bgobserveduse | rgb(0 0 0 / 0%) | Secondary button has no fill. |
button.text | color.action.secondary.textobserveduse | #ffa2a7 | Secondary button text. |
button.border | color.action.secondary.borderobserveduse | #a3676b | Secondary button border (meets 3:1). |
button.bg-hover | color.action.secondary.hover-bgobserveduse | #630f0d | Secondary button hover. |
button.bg-pressed | color.action.secondary.pressed-bgobserveduse | #420f0c | Secondary button pressed. |
button.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
button.border-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
button.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
button.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
button.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
zone.border | color.interaction.drop-targetobserveduse | #e53935 | Dashed drop-target border. |
zone.bg-drop-target | color.interaction.marqueeobserveduse | rgb(145 20 16 / 12%) | Drag marquee and ghost. |
zone.hint | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
status.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
list.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
list.divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
list.size | color.text.subtleapproveduse | #ad7175 | Metadata, comments, line numbers. |
list.error | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
remove.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
remove.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
progress.track | color.interaction.pressed.bgobserveduse | #420f0c | Pressed fill. |
progress.stripe | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
help.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Button | Drop zone | With chosen files |
|---|---|---|---|
default | Up to five files, 10 MB each. | PNG or WebP, 4 MB each. |
Up to five files, 10 MB each. |
hover | |||
focus-visible | |||
drop-target | |||
invalid | |||
disabled | |||
loading |
Choosing one or more files from the host. The native input keeps the host chooser, its keyboard behaviour and its security model; the theme replaces only the input’s own rendering with a button-styled label, adds an optional drop zone, and lists what was chosen with any per-file error.
The field label; the root row (or the drop zone) holding the button-styled
label and the status text; the hidden-but-focusable native input; an optional
list of chosen files, each with name, size in color.text.subtle and a
remove button, one of which may carry the ✕ glyph and an error in
color.status.danger.text; a static striped progress bar while uploading;
help and validation message below.
| State | Visual | Non-colour channel |
|---|---|---|
| default | button: 1px color.action.secondary.border outline, text color.action.secondary.text; zone: 1px dashed color.interaction.drop-target | — |
| hover | button background → color.action.secondary.hover-bg; border unchanged | cursor: pointer |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the button (the input has focus) | the ring |
| drop-target | root fill color.interaction.marquee; border 1px dashed color.interaction.drop-target | dashed pattern; the hint text changes to “Release to add” |
| invalid | button border 2px color.status.danger.border; message with the ✕ glyph; a failed row shows its own ✕ | border width 1 → 2px; glyphs; aria-invalid |
| disabled | button text color.text.disabled, border color.border.disabled, background color.interaction.disabled.bg; the zone’s dashes take color.border.disabled; no hover | disabled; cursor: not-allowed |
| loading | a static bar of 45° stripes, color.action.primary.bg on color.interaction.pressed.bg, below the row; the button stays enabled | aria-busy; the status text reads “Uploading n files”; no animation |
Precedence when several apply: disabled > loading > invalid > drop-target > hover; focus-visible is always drawn.
Tab reaches the native input, which is drawn as the button; Enter or Space opens the host chooser; Escape in the chooser returns to the input. Each remove button in the list is its own tab stop after the input, and Delete or Backspace on it acts like Enter. Dropping is a pointer gesture only and never the sole way to add a file.
The native input is hidden with the sr-only pattern, never display: none
or visibility: hidden, so it keeps focus and the host chooser. It is named
by both labels (“Attachments” and “Choose files”), and aria-describedby
lists help and message. The status text and the list are announced through a
polite live region after a choice; per-file errors are in the row text, not
only in colour. The drop zone has no role and no tabindex; aria-busy on
the root marks uploading. Contrast: button text 10.37:1 on the canvas and
6.84:1 on the hover fill, button border 4.45:1, drop-target border 4.69:1 on
the canvas and 4.52:1 on the marquee fill, status and help 5.81:1, sizes
5.10:1, danger text 5.40:1.
The button is the secondary button of the actions family; the zone is a dashed border and a translucent fill; the stripes are a repeating gradient (or a hatched brush). Toolkits without drag-and-drop omit the zone and keep the button; toolkits without a translucent fill composite the marquee over the canvas and record the value.
A hidden input with display: none and a button that clicks it by script. A
drop zone that is the only way to add files. A rounded, dotted, animated
“pulsing” zone. A spinner while uploading. A progress bar that animates its
stripes. Errors shown by a red file name alone. Disabling by opacity.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reaches the native input (drawn as the button), then each remove button in the list. |
| Enter / Space | Opens the host file chooser from the input. |
| Delete / Backspace | On a focused remove button, same as activating it. |
| Escape | Closes the host chooser (host behaviour). |
Component JSON ·Implementation brief ·Build a task kit ·
Related: button text-field progress list field
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX
wizardA multi-step form with a stepper header, the current panel and back/next actions; native buttons with aria-current="step".
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
steps.divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
step.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
step.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
step.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
step.text-current | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
step.indicator-current | color.border.selected-indicatorobserveduse | #e53935 | 2px indicator bar on the selected tab or nav item. |
step.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
step.marker-complete | color.status.success.textapproveduse | #86a46f | Success text (7.13:1). |
step.marker-invalid | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
step.indicator-invalid | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
panel.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
panel.border | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
title.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
text.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
message.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
next.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
next.text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
next.bg-hover | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover (7.92:1 with prose). |
next.bg-pressed | color.action.primary.pressed-bgobserveduse | #630f0d | Primary button pressed. |
next.border | color.action.primary.borderobserveduse | #9e231f | Primary button border. |
next.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
back.text | color.action.secondary.textobserveduse | #ffa2a7 | Secondary button text. |
back.border | color.action.secondary.borderobserveduse | #a3676b | Secondary button border (meets 3:1). |
back.bg-hover | color.action.secondary.hover-bgobserveduse | #630f0d | Secondary button hover. |
busy.glyph | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Numbered steps | Glyph markers |
|---|---|---|
default | Step 2 of 3: TokensChoose the profile the tokens resolve against. The extended overlay can be added later. | Step 3 of 4: BindingsKeep the existing hjkl bindings or replace them with the imported set. |
hover | ||
focus-visible | ||
current | ||
disabled | ||
invalid | ||
busy |
A form too long for one screen, split into ordered steps that are validated one at a time. The stepper shows where the person is and which steps are complete; the panel holds the current step; Back and Next move through the sequence. Steps are not tabs: a future step is unreachable until the steps before it pass.
The stepper, an ordered list of step buttons over a color.border.divider
rule; each step’s marker and name; the panel on color.surface.default with
its “Step n of m” heading in color.text.bright; the actions row with Back
(secondary) and Next (primary, color.action.primary.bg with
color.action.primary.text).
| State | Visual | Non-colour channel |
|---|---|---|
| default | steps in color.text.default; complete steps carry the ✓ in color.status.success.text | the ✓ glyph; the numbers |
| hover | step background → color.interaction.hover.bg; Next → color.action.primary.hover-bg; Back → color.action.secondary.hover-bg | cursor: pointer |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px; on Next’s fill the ring takes color.interaction.focus.ring-container | the ring |
| current | 2px color.border.selected-indicator under the step; name in color.text.bright | aria-current="step"; the indicator bar; the heading reads “Step n of m” |
| disabled | a future step in color.text.disabled; no hover | disabled; cursor: not-allowed; skipped by Tab |
| invalid | the step’s marker becomes ✕ in color.status.danger.text; its indicator takes color.status.danger.border; the panel shows the message | glyph; message text; aria-invalid on the failing fields |
| busy | Next shows the static ⋯ glyph before its label and does not respond; size unchanged | aria-busy on the wizard; the glyph; cursor: progress |
Precedence when several apply: disabled > busy > invalid > current > hover; focus-visible is always drawn.
Tab walks the enabled step buttons in order, then the panel’s fields, then Back and Next. Enter or Space on a reachable step goes to it; on Next it validates the panel, and on failure moves focus to the first invalid field, on success advances and moves focus to the new panel heading. Back never validates. There is no arrow-key navigation between steps, because the stepper is a list of buttons, not a tablist.
The wizard is a region named by aria-label; the stepper is an <ol> so
count and position are announced; the current step carries
aria-current="step"; complete steps append visually hidden ”, complete”;
unreachable steps are disabled, not aria-disabled, because they cannot be
activated. The panel is a <section> named by its heading, which receives
focus after each step change. Validation failures are announced through the
form’s live region and the ✕ marker; aria-busy marks the wizard while Next
is in progress. Contrast: step text 8.65:1, current name 10.37:1, complete
marker 7.13:1, indicator 4.69:1, Next text 9.28:1 on its fill, ring 5.57:1 on
the primary fill, future step text 3.33:1 (exempt).
Everything is text, a 2px bottom border and the button tones; no icon font (the markers are text glyphs). Toolkits with a native assistant use its page list and map the indicator to the current page; toolkits with none draw the stepper as a row of buttons and record any deviation in the indicator width.
Tabs restyled as steps. A progress bar in place of the stepper. Circles with connecting lines that need a rounded radius. Future steps that look enabled and reject the click. A spinner on Next. Colour-only completion marks. Validation that blocks Back. Animated slides between panels.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reaches each enabled step button, the panel's fields, then Back and Next; future steps are disabled and skipped. |
| Enter / Space | On a step button, goes to that step if it is reachable; on Next, validates the panel and advances; on Back, returns without validating. |
| Escape | Nothing; leaving a wizard is an explicit Cancel or the host's navigation. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: button fieldset tabs breadcrumbs progress admin-form
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
form-builderA bounded reference editor for five canonical field kinds, with keyboard ordering, local preview and explicit JSON definition import and export.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
status.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Reference definition |
|---|---|
default | Draft reference · 3 fields · default profile
The interactive builder provides native add, edit, move and remove controls, canonical previews, and explicit JSON transfer. This static specimen does not submit data. |
empty | |
invalid | |
disabled |
Build a bounded reference form from text, textarea, select, checkbox and radio fields in the interactive builder. D-021 remains proposed. This draft component does not promote new tokens, roles, profiles or native ports.
Each field has a stable ID, label, help, required flag and, for select/radio, stable option IDs and labels. Add and remove fields, edit definitions and move them with explicit Move up / Move down buttons. Saving an edit validates the complete proposed definition before replacing the current one.
The preview uses the same canonical field renderer and validation model as the validation-recovery composition. Changing a definition clears preview values and errors. Merely moving focus or typing a preview value does not change the definition. Preview state is separate from schema state.
The empty state displays an explanation and an add action. An invalid import or definition edit reports a corrective message and leaves the current definition intact. Unavailable movement buttons are disabled at the list boundaries. The default state shows the ordered definition and preview. The static specimen represents the definition; interactive execution evidence is recorded separately.
All operations use native controls. No drag operation is required. Move actions keep focus on the moved field’s controls. Removing a field focuses the next remaining field or the add control. Import and reset return to the add control. Preview submit focuses its error summary; links target stable field control IDs.
Use the canonical label, help and error associations, required markers and radio fieldset/legend semantics. Stable IDs survive JSON round trips and field reordering. Imported strings are text, never markup. No manual screen-reader pass is claimed by test presence.
Definition schema version 1 pins theme name, version, default profile and constituent component content digest. Accept at most 64 KiB, 20 fields, 20 options per choice field, 120-character labels and 400-character help. Preview text is limited to 2000 characters. Unknown keys, duplicate IDs, unsupported kinds, invalid choices and mismatched theme identity are rejected atomically. An old content identity requires an explicit migration outside this tool.
Everything stays in memory. Only explicit Download definition creates a file, containing the schema and no entered values. Reset clears the definition and preview. There is no autosave, network submission, schema execution, arbitrary HTML/CSS/JavaScript or imported validation expression.
A production backend, conditional logic engine, drag-only editor, arbitrary code generator, persistent account form, or native application port.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reach add, field editor, ordering, removal, preview and transfer controls in document order. |
| Enter / Space | Activate the focused native button. Move up and Move down preserve stable identity and focus. |
| Arrow keys | Use native select and radio choices in the preview. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: admin-form text-field textarea select checkbox radio-group button
Stress fixtures: FX-360 FX-ZOOM-200 FX-DENSITY
multiselectA searchable native checkbox group that preserves all selected form values.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-border-width-emphasis | border.width.emphasisobserveduse | 2px | Selected indicator, sticky header rule, invalid border. |
dependency.role-color-action-primary-bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
dependency.role-color-action-primary-hover-bg | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover (7.92:1 with prose). |
dependency.role-color-action-primary-text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
dependency.role-color-border-active | color.border.activeobserveduse | #e53935 | Active or focused control border. |
dependency.role-color-border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
dependency.role-color-interaction-disabled-bg | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
dependency.role-color-interaction-focus-ring-container | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
dependency.role-color-interaction-hover-bg | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
dependency.role-color-status-danger-border | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
dependency.role-color-status-danger-text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
dependency.role-color-text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-focus-offset | focus.offsetobserveduse | -2px | outline-offset for controls. |
dependency.role-focus-ring | focus.ringobserveduse | 1px dashed #e53935 | outline on controls and rows. |
dependency.role-font-line-height-ui-md | font.line-height.ui-mdobserveduse | 18px | |
dependency.role-font-line-height-ui-sm | font.line-height.ui-smobserveduse | 16px | |
dependency.role-font-size-ui-md | font.size.ui-mdobserveduse | 13px | |
dependency.role-font-size-ui-sm | font.size.ui-smobserveduse | 12px | |
dependency.role-icon-size-md | icon.size.mdobserveduse | 16px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-24 | space.24observeduse | 24px | |
dependency.role-space-4 | space.4observeduse | 4px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | 3 topics; 1 selected |
checked | |
focus-visible | |
disabled | |
no-results |
Select zero or more named values using a labelled native checkbox list. Local search filters visible choices without clearing selections. Successful checkboxes retain repeated native form keys. Clear selection is an explicit action.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Named content on the default surface. | Native text and structure. |
| checked | Selected fill and a check indicator. | Native checked state. |
| focus-visible | Dashed focus ring. | Native keyboard focus position. |
| disabled | Disabled semantic foreground, background and border at full opacity. | Native disabled state blocks activation. |
| no-results | Muted result count. | Zero-results text; existing selected values remain selected. |
Keep labels and native checkbox semantics. Hidden choices leave the focus order but retain checked form values. Announce matching and selected counts, including zero. Required selection policy belongs to the host; marking every checkbox required would require every option.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Visit search, clear and each visible enabled checkbox. |
| Space | Toggle the focused checkbox. |
| Escape | Clear a non-empty query without clearing selection. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
repeaterRepeat native field rows with stable identities and keyboard ordering.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
dependency.role-color-interaction-disabled-bg | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
dependency.role-color-text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-24 | space.24observeduse | 24px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | No contacts added. |
empty | |
focus-visible | |
disabled |
Repeat native fields from trusted inert HTML. Add, remove and reorder preserve row identities. The host supplies fields and handles submission; the component does not execute a schema or provide persistence. A max attribute bounds the number of rows; the supplied example is limited to ten.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Named content on the default surface. | Native text and structure. |
| empty | A muted empty message. | Explicit text and retained Add action. |
| focus-visible | Dashed focus ring. | Native keyboard focus position. |
| disabled | Disabled semantic foreground, background and border at full opacity. | Native disabled state blocks activation. |
Namespace IDs and label/ARIA references for each row. Row identities do not change during reordering. Actions have names and disabled boundary states. Removing a focused row restores focus to a surviving row or Add; announce each explicit operation.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Visit fields and row actions in source order. |
| Enter / Space | Add, remove or move a row. Focus follows the affected row or nearest survivor. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
Cards, lists, badges, chips, avatars, descriptions, tables, timelines, simple charts.
cardA bordered panel that groups a title, body and metadata; a link card makes the whole panel one link, a selectable card is an option with the selection fill.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.border | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.border-hover | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
title.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
body.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
meta.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
selected.bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
selected.text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
selected.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | Link card | Selectable |
|---|---|---|---|
default | Window gapsInner 14px, outer −2px; the bar sits above the gap and the border is 1px. |
Tiling
Windows fill the screen without overlap; the gap is the only ornament.
Floating
Windows overlap and carry the one shadow the theme permits.
| |
hover | |||
focus-visible | |||
selected | |||
selected+focus-visible |
Groups a small unit of content — a title, a line or two, a metadata line — so it reads as one thing. A card is static by default; it becomes a link when the whole unit leads somewhere, and an option when the user picks among several units. A card is not a dialog, not a form and not a table row.
A 1px color.border.default edge on color.surface.default, no shadow, no
radius. Inside, a title in color.text.bright, body text in
color.text.default, metadata in color.text.muted. Padding is 12px in
comfortable and 8px in compact density. A link card is one anchor around
the same content; a selectable card is an option inside a listbox group.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.default on color.surface.default | — |
| hover | link and selectable cards only: background → color.interaction.hover.bg; border → color.border.control | cursor: pointer; the border lightens from decorative to control |
| focus-visible | container ring 2px solid color.interaction.focus.ring-container at −3px | the ring |
| selected | fill color.interaction.selection.bg with color.interaction.selection.text on every line | aria-selected="true"; the fill’s on-fill text |
| selected+focus-visible | the fill and the 2px ring in color.interaction.focus.ring-container | both visible at once |
A static card has no hover and no focus: nothing happens on a pointer and it is not in the tab order.
A link card is one tab stop and Enter follows it; controls inside a link card are not allowed (a link cannot contain a button). A selectable group is a listbox with roving tabindex: Tab lands on the selected card, the arrow keys move and select, Space toggles in a multi-select group. A static card that contains controls contributes those controls to the tab order and nothing else.
A static card is an article with a heading when it stands alone, or a
section inside a list. A link card names itself from its content and the
title comes first so it is read first. Selectable cards are role="option"
inside role="listbox" with aria-label; aria-selected on each option.
Contrast: title 10.10:1, body 8.43:1, metadata 5.66:1 on the card; on the
selection fill every line switches to the on-fill text (12.47:1) because muted
text would fall to 4.19:1. The ring is 10.10:1 on the card and 7.48:1 on the
fill.
A bordered box with a background and an outline. Toolkits that add a shadow to their frames remove it. Hosts without container outlines draw the ring as a 2px inner border and record it.
A drop shadow or elevation. Rounded corners. A hover lift or scale. A static
card with a hover fill. A card whose only clickable part is an invisible
overlay. A link card containing buttons. A selected card shown by a border
colour alone without the fill and aria-selected. Muted metadata left on the
selection fill.
| Key | Action |
|---|---|
| Tab | Reaches a link card as one stop; reaches a selectable group at its selected card (roving tabindex). |
| Enter | Follows a link card. |
| Up / Down / Left / Right | Moves between selectable cards in the group and selects the focused one. |
| Space | Toggles the focused card in a multi-select group. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: list link button table
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
listA selectable list of rows (an APG listbox) in single or multiple selection; the selected row is the selection fill with a check glyph and an optional metadata column.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
row.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
row.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
row.divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
row.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
row.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
selected.bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
selected.text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
selected.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
selected.inactive-bg | color.interaction.selection.inactive-bgobserveduse | #420f0c | Selection while the container is not focused. |
selected.inactive-text | color.interaction.selection.inactive-textobserveduse | #e99499 | Text on the inactive selection (7.02:1). |
check.glyph | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
meta.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Single | Multi | With metadata |
|---|---|---|---|
default |
|
|
|
hover | |||
focus-visible | |||
selected | |||
selected+focus-visible | |||
selected+container-inactive | |||
disabled |
Lets the user choose one or several rows from a short, visible set. A list is a listbox: it holds a selection, it does not navigate. Rows that lead somewhere are sidebar-nav; rows with columns are a table; a long set that needs filtering is a combobox.
A ul with role="listbox" framed by 1px color.border.control on
color.surface.default. Each option is one row: a check gutter, the label in
color.text.default, and in the metadata variant a trailing column in
color.text.muted. Rows are separated by 1px color.border.divider. A
selected row is filled with color.interaction.selection.bg; every part of
it, including the metadata and the ✓ glyph, takes
color.interaction.selection.text.
| State | Visual | Non-colour channel |
|---|---|---|
| default | text color.text.default; no fill; the check gutter empty | — |
| hover | background → color.interaction.hover.bg | cursor: pointer |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the option | the ring |
| selected | fill color.interaction.selection.bg; text and glyph color.interaction.selection.text | aria-selected="true"; the ✓ glyph |
| selected+focus-visible | the fill and the ring in color.interaction.focus.ring-container | both visible at once |
| selected+container-inactive | fill color.interaction.selection.inactive-bg with color.interaction.selection.inactive-text; no ring | lightness drop between the two fills; the glyph stays |
| disabled | text color.text.disabled; no fill; no hover; still reachable by arrow keys | aria-disabled="true"; cursor: not-allowed |
Precedence: disabled > selected > hover; focus-visible is always drawn.
Roving tabindex: the selected option (or the first) has tabindex="0", the
rest -1, so the list costs one tab stop. Up and Down move; in single
selection the selection follows focus, in multiple selection Space toggles
and Shift with the arrows extends. Home and End jump; Ctrl+A selects all in a
multi-select list; typing jumps to the next matching label. Disabled options
are reachable and announced, never skipped silently.
aria-label or aria-labelledby on the listbox; aria-multiselectable
when several may be chosen; aria-selected on every option, including
"false". The ✓ glyph is aria-hidden; aria-selected is the state.
Metadata is inside the option so it is read with the label. Contrast: text
8.43:1, metadata 5.66:1, selected text 12.47:1 on the fill, inactive selection
7.02:1, ring 4.57:1 on the surface and 7.48:1 on the fill, frame 4.33:1. Rows
are at least 24 CSS pixels tall with the check gutter as the leading target
area.
Rows with a background, a glyph gutter and an outline; nothing else. Hosts
with a native list view take the fill, the inactive fill and the divider
colour and keep their keyboard model. The inactive fill applies when the
list’s window or pane loses focus, which a web page detects through
:focus-within on the pane.
A selected row marked by a border or a check alone without the fill. Rounded row highlights. Muted metadata left on the selection fill. A list where every row is a tab stop. A list that navigates on selection. Disabled rows removed from the arrow-key order. Hover that changes the text colour. A list without a frame on the canvas.
| Key | Action |
|---|---|
| Tab | Enters the list at the selected option, or the first option when nothing is selected; a second Tab leaves the list. |
| Down / Up | Moves focus to the next or previous option; in single selection the selection follows focus. |
| Home / End | Moves focus to the first or last option. |
| Space | Toggles the focused option in a multi-select list. |
| Shift+Down / Shift+Up | Extends the selection by one in a multi-select list. |
| Ctrl+A | Selects every option in a multi-select list. |
| A-Z | Moves focus to the next option starting with the typed character. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: card select combobox tree table
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-DENSITY FX-STATE-MATRIX
badgeA small square label for a status or a count; every status carries its glyph and its fill, the outline form uses the status border, and an empty badge is a dot with a name.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
danger.bg | color.status.danger.fillobserveduse | #dc282e | Danger fill. |
danger.text | color.status.danger.on-fillobserveduse | #f9faf9 | Text on the danger fill (4.58:1). |
danger.border | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
danger.outline-text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
warning.bg | color.status.warning.fillapproveduse | #c9973f | Warning fill. |
warning.text | color.status.warning.on-fillapproveduse | #0c0909 | Text on the warning fill (7.54:1). |
warning.border | color.status.warning.borderapproveduse | #c9973f | Warning border. |
warning.outline-text | color.status.warning.textapproveduse | #c9973f | Warning text (7.54:1). |
success.bg | color.status.success.fillapproveduse | #86a46f | Success fill. |
success.text | color.status.success.on-fillapproveduse | #0c0909 | Text on the success fill (7.13:1). |
success.border | color.status.success.borderapproveduse | #86a46f | Success border. |
success.outline-text | color.status.success.textapproveduse | #86a46f | Success text (7.13:1). |
info.bg | color.status.info.fillapproveduse | #7e9ebb | Info fill. |
info.text | color.status.info.on-fillapproveduse | #0c0909 | Text on the info fill (7.08:1). |
info.border | color.status.info.borderapproveduse | #7e9ebb | Info border. |
info.outline-text | color.status.info.textapproveduse | #7e9ebb | Info text (7.08:1). |
neutral.bg | color.status.neutral.fillobserveduse | #a3676b | Neutral fill (dunst urgency_low). |
neutral.text | color.status.neutral.on-fillobserveduse | #000000 | Text on the neutral fill (4.71:1). |
neutral.border | color.status.neutral.borderobserveduse | #a3676b | Neutral border. |
neutral.outline-text | color.status.neutral.textobserveduse | #bd787d | Neutral status text. |
count.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
count.text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Status | Count | Outline |
|---|---|---|---|
default | Failed
Drifted
Verified
Proposed
Deferred | 12 unread | Failed
Drifted
Verified
Proposed
Deferred |
empty |
Names a status or shows a count next to the thing it describes. A badge is read-only: it never opens, toggles or removes anything. An interactive label is a chip; a message with a status is an alert.
An inline span in caption type with 2px by 8px padding and square corners.
Status badges fill with color.status.<status>.fill and use the matching
on-fill text, preceded by the status glyph: ✕ danger, ! warning, ✓ success,
i info, · neutral. Outline badges have no fill, the text colour and a 1px
border in the same status. The count badge is a number on
color.action.primary.bg in color.action.primary.text with a visually
hidden unit. An empty badge is an 8px square dot in the status fill with a
name.
| State | Visual | Non-colour channel |
|---|---|---|
| default | fill, glyph and text for the status; or the outline form | the glyph; the text |
| empty | an 8px square dot in the status fill; the glyph and text visually hidden | role="img" with aria-label, or the hidden text; the dot’s position beside its subject |
A badge has no hover, focus, pressed or disabled state because it is not a control.
None. A badge is never focusable. When a badge summarises something the user can act on, the action lives in the neighbouring control and the badge is part of that control’s name or description.
The glyph is aria-hidden and the text carries the meaning, so a screen
reader hears “Failed”, not “cross Failed”. Count badges include their unit
as visually hidden text (“12 unread”). An empty badge, a dot, carries
role="img" and aria-label or wraps hidden text; a dot with no name is
decoration and must be aria-hidden. Contrast: on-fill text 4.58:1 (danger),
7.54:1 (warning), 7.13:1 (success), 7.08:1 (info), 4.71:1 (neutral); outline
text 5.26:1 to 7.34:1; borders and dots at least 4.04:1 on the panel surface.
Badges are not targets, so no minimum size applies beyond legibility at 11px
caption type.
A filled or outlined span with a glyph. Toolkits without background colours on labels use the outline form. Hosts without the glyph characters use the nearest available (x, !, check mark, i, bullet) and record it.
Rounded or pill badges. A badge that is clickable. A status shown by colour without its glyph. A count badge that hides its unit from assistive technology. Purple, cyan or magenta statuses. A badge in body type. A badge whose text wraps. A dot without a name.
Component JSON ·Implementation brief ·Build a task kit ·
Related: chip alert table list
Stress fixtures: FX-I18N FX-RTL FX-ZOOM-200 FX-320 FX-RM FX-HC FX-STATE-MATRIX
chipA square bordered token for a filter, a removable value or a static tag; a pressed filter chip takes the selection fill and a check glyph.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
root.bg-pressed | color.interaction.pressed.bgobserveduse | #420f0c | Pressed fill. |
root.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
root.text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
root.border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
root.bg-disabled | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
selected.bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
selected.text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
selected.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
remove.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
remove.text-hover | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
remove.bg-hover | color.interaction.hover.bg-strongobserveduse | #630f0d | Hover on menu items; D-027 moved links to interaction.hover.bg. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Filter | Removable | Static |
|---|---|---|---|
default | tabs | R1 | |
hover | |||
focus-visible | |||
selected | |||
selected+focus-visible | |||
disabled |
Represents one value the user can toggle or remove: a filter in a filterable table, a chosen item in a multiselect, a tag on a record. A chip that only informs is a badge; a chip that runs an action is a button.
A square control-height box with 1px color.border.control on
color.surface.default and text in color.text.default. A filter chip is a
button with a check gutter that shows ✓ while pressed. A removable chip is a
span holding the label and a × button in color.text.muted. A static chip is
a span with nothing else. When selected, the chip fills with
color.interaction.selection.bg and its text becomes
color.interaction.selection.text.
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.control on color.surface.default; text color.text.default | — |
| hover | background → color.interaction.hover.bg; the remove button → color.interaction.hover.bg-strong with color.text.bright | cursor: pointer |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the chip or on the remove button | the ring |
| selected | fill color.interaction.selection.bg; text and glyph color.interaction.selection.text; the border takes the fill colour | aria-pressed="true"; the ✓ glyph |
| selected+focus-visible | the fill and the ring in color.interaction.focus.ring-container | both visible at once |
| disabled | text color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg; no hover | disabled; cursor: not-allowed |
Pressing a filter chip uses color.interaction.pressed.bg for the duration
of the press only. Precedence: disabled > selected > hover; focus-visible is
always drawn.
A filter chip is one tab stop that toggles on Space or Enter. A removable chip contributes only its remove button to the tab order; the label is not focusable. A static chip is never focusable. Inside a token field the field owns Backspace and the arrow keys.
Filter chips expose aria-pressed and keep the same label in both states;
the ✓ glyph is aria-hidden. The remove button is named “Remove compact density with 4px between neighbours.
A bordered box with a background and an outline; the remove control is an ordinary small button. Toolkits with a native toggle button take the fill for the checked state. Hosts without a glyph draw the check as a 2px inset rule at the inline start and record it.
Pill or rounded chips. A selected chip shown by a border colour alone
without the fill and aria-pressed. A removable chip whose × has no name.
A chip that is a link. A static tag that reacts to hover. Disabling by
opacity. A chip whose label clips inside the border. A chip in caption
type (that is a badge).
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reaches a filter chip, or the remove button of a removable chip; static chips are skipped. |
| Space / Enter | Toggles a filter chip; activates the remove button. |
| Backspace / Delete | In a token field, removes the chip before the caret (the field's contract, not the chip's). |
Component JSON ·Implementation brief ·Build a task kit ·
Related: badge button multiselect combobox toolbar
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-STATE-MATRIX
tableA native data table with a header rule, row dividers, hover and selection fills, sortable headers with a glyph, and static empty and loading rows.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
header.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
header.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
header.rule | color.border.strongobserveduse | #9e231f | Emphasised 2px rule: sticky headers, section ends. |
header.bg-sticky | color.surface.raisedapproveduse | #160b0b | Sticky headers, menus, popovers; D-027 recessed it so an open list reads as depth rather than a lighter panel. |
sort.glyph | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
sort.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
row.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
row.divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
row.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
selected.bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
selected.text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
selected.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
selected.inactive-bg | color.interaction.selection.inactive-bgobserveduse | #420f0c | Selection while the container is not focused. |
selected.inactive-text | color.interaction.selection.inactive-textobserveduse | #e99499 | Text on the inactive selection (7.02:1). |
caption.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
empty.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
loading.glyph | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | Sticky header | Numeric | With caption | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
default |
|
|
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hover | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected+focus-visible | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected+container-inactive | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sorted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
empty | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
loading | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Shows records as rows and attributes as columns so they can be compared down a column. The table is for reading; a table with selection checkboxes, resizable columns and a toolbar is the data-table. A single column of rows is a list.
A collapsed native table on color.surface.default with no outer border.
The header row is color.text.bright above a 1px color.border.strong rule
(2px when the header is sticky, on color.surface.raised); sortable headers
wrap their label in a button and show ▲ or ▼ when sorted. Body rows are the
row height, separated by 1px color.border.divider, text in
color.text.default; numeric cells are end-aligned with tabular numbers.
An optional caption in color.text.muted sits above. Two special rows,
hidden unless needed, span every column: the empty message and the loading
glyph.
| State | Visual | Non-colour channel |
|---|---|---|
| default | rows on color.surface.default; header rule 1px color.border.strong | — |
| hover | row background → color.interaction.hover.bg | cursor: default; only on hover-capable pointers |
| selected | row fill color.interaction.selection.bg; every cell color.interaction.selection.text | aria-selected="true" on the row |
| selected+focus-visible | the fill and a ring in color.interaction.focus.ring-container around the row | both visible at once |
| selected+container-inactive | row fill color.interaction.selection.inactive-bg with color.interaction.selection.inactive-text; no ring | lightness drop between the two fills |
| sorted | the sorted header shows ▲ (ascending) or ▼ (descending) in color.text.bright | aria-sort; the glyph |
| empty | the body is one full-width cell in color.text.muted: “No rows match.” | the message text |
| loading | the body is one full-width cell with the static ⋯ glyph in color.text.muted; the header stays | aria-busy="true"; the glyph; no animation |
Precedence: loading > empty; selected > hover; focus-visible is always drawn.
A plain table has no row focus: Tab reaches the sort buttons in the header and any control inside a cell. Enter or Space on a sort button cycles ascending, descending. Tables whose rows are targets (row links, row selection) are the data-table and add row focus there.
caption or aria-labelledby names the table; th scope="col" on every
header and scope="row" on a row header column. aria-sort on the sorted
th, none or absent elsewhere; the glyph is aria-hidden. The empty and
loading rows are real rows so the table stays a table; aria-busy="true"
on the table while loading and a live region for the outcome. Contrast:
body text 8.43:1, header 10.10:1, caption and empty message 5.66:1, selected
text 12.47:1 on the fill, inactive 7.02:1, ring 7.48:1 on the fill, sort ring
4.57:1. Rows are at least 24 CSS pixels tall; sort buttons fill the header
cell.
Row backgrounds, one border under the header, one between rows, an outline on a selected row. Toolkits with a native table view take the rule, the divider and the two fills. Hosts that cannot draw an outline on a row draw the ring as an inset 1px border and record it.
Zebra striping. A rounded or bordered table frame. A selected row marked by
a left bar (that is navigation, not selection). Header text in caption size
or uppercase. Sorting shown by colour alone without aria-sort and a glyph.
An animated spinner while loading. An empty state that removes the header.
Text truncated with no way to read the full value. A hover fill on a
touch device.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves between the sort buttons and any controls inside cells; rows themselves are not tab stops in a plain table. |
| Enter / Space | Toggles the sort of the focused header between ascending and descending. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: data-table list pagination badge filterable-table
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-DENSITY FX-OVERFLOW FX-STATE-MATRIX
data-tableA native table with controls: a selection column with a mixed select-all, sortable headers, row focus with the ring on the fill, and static resize handles.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
header.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
header.rule | color.border.strongobserveduse | #9e231f | Emphasised 2px rule: sticky headers, section ends. |
sort.glyph | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
row.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
row.divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
row.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
row.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
selected.bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
selected.text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
selected.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
checkbox.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
checkbox.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
checkbox.checked-bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
checkbox.glyph | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
resize.handle | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
resize.handle-hover | color.border.activeobserveduse | #e53935 | Active or focused control border. |
empty.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
loading.glyph | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | Selectable | Resizable | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
default |
|
|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hover | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
focus-visible | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected+focus-visible | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sorted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mixed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
empty | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
loading | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A table the user works in: selects rows, sorts, resizes columns. The plain table is for reading; this component adds the controls and the keyboard model. It stays a native table so its cells keep their header associations; hosts that need cell-level navigation promote it to a grid and record the change.
The table component’s structure with the same rule, dividers and fills,
plus: a selection column whose header holds a select-all with
aria-checked true, false or mixed, drawn on color.action.primary.bg with
the ✓ or – glyph in color.action.primary.text; native checkboxes in each
row on color.surface.input with a 1px color.border.control boundary;
rows that can take focus; and in the resizable variant a 1px handle in
color.border.default at the inline end of each header that turns
color.border.active under the pointer.
| State | Visual | Non-colour channel |
|---|---|---|
| default | rows on color.surface.default; header rule 1px color.border.strong | — |
| hover | row background → color.interaction.hover.bg; a resize handle → color.border.active | cursor: default on rows, col-resize on handles |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px around the focused row or control | the ring |
| selected | row fill color.interaction.selection.bg; every cell color.interaction.selection.text; the row checkbox checked | aria-selected="true"; the checked box |
| selected+focus-visible | the fill and the ring in color.interaction.focus.ring-container | both visible at once |
| sorted | ▲ or ▼ in color.text.bright on the sorted header | aria-sort; the glyph |
| mixed | the select-all on color.action.primary.bg with the – glyph | aria-checked="mixed"; the glyph |
| empty | one full-width cell in color.text.muted: “No rows match.”; the header and controls stay | the message text |
| loading | one full-width cell with the static ⋯ glyph; the header stays | aria-busy="true"; the glyph; no animation |
Precedence: loading > empty; selected > hover; focus-visible is always drawn. Selection is a fill; focus is a ring; they never borrow each other’s form.
Header controls first: the select-all, then each sort button. Rows use a roving tabindex so the body costs one tab stop; Up and Down move, Space toggles, Shift extends, Ctrl+A selects all. Controls inside the focused row are reached with Tab and left with Shift+Tab. Column widths are set from a header menu or by dragging the handle; the handle itself is never a tab stop.
Every row checkbox is labelled by the row’s first data cell
(aria-labelledby); the select-all is named “Select all rows” and exposes
aria-checked="mixed" when only some are selected. aria-selected on the
row mirrors its checkbox. Sorted headers carry aria-sort; the table
carries aria-busy while loading; the count of selected rows is announced
through a live region owned by the surrounding toolbar. Contrast: text
8.43:1, header 10.10:1, selected text 12.47:1 on the fill, ring 4.57:1 on the
surface and 7.48:1 on the fill, checkbox boundary 4.33:1, check glyph 9.28:1
on the primary fill. Checkboxes and handles are at least 24 CSS pixels tall
in compact density.
The table component’s drawing plus a checkbox column and a header checkbox; the handle is a 1px rule. Toolkits with native table views map row selection to their selection model and the mixed state to their tristate checkbox. Hosts without row focus fall back to the checkbox as the only row control and record it.
A grid role on a table that is not navigated cell by cell. Zebra stripes.
A select-all that is a native checkbox with a script-only indeterminate
state and no aria-checked. Rows selected by clicking anywhere with no
checkbox alternative. A selected row shown by a border alone. A resize
handle that is a tab stop. An animated loading skeleton. Rounded checkboxes.
Text truncated with no full-value affordance.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves through the select-all, the sort buttons, then the rows (one row is the tab stop; roving tabindex) and any control inside the focused row. |
| Down / Up | Moves row focus; in a single-select table the selection follows focus. |
| Space | Toggles the focused row's selection (the same as its checkbox). |
| Shift+Down / Shift+Up | Extends the selection by one row. |
| Ctrl+A | Selects every row; the select-all reflects it. |
| Enter / Space | On a sort button, cycles the sort; on the select-all, toggles between all and none. |
| Left / Right | Reserved for cell navigation in hosts that promote the table to a grid; not used here. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: table checkbox pagination toolbar filterable-table
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-DENSITY FX-OVERFLOW FX-TOUCH FX-STATE-MATRIX
description-listSemantic terms and values that wrap into a readable single column.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-border-divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-4 | space.4observeduse | 4px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default |
|
stacked |
Present named facts as semantic terms and descriptions. Terms remain adjacent to their values in source order. A narrow view stacks the term above its value.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Two aligned columns. | Native term and description relationships. |
| stacked | One column at narrow widths. | Each term immediately precedes its value. |
Use dt and dd rather than a grid of unrelated paragraphs. Long values wrap without clipping. Preserve meaningful link text and do not make every value keyboard focusable.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Visit links in values. Plain facts are not tab stops. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
avatarA square entity image with a named text fallback.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-font-weight-bold | font.weight.boldobserveduse | 700 | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-32 | space.32observeduse | 32px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | |
loading | |
error |
Identify a person or entity with an optional caller-owned image and text fallback. The component ships no image service or assets. Failed images restore the fallback and retain the full accessible name.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Initials or loaded image. | Full name on the image container. |
| loading | Fallback remains visible. | No loss of the full accessible name while loading. |
| error | Fallback remains visible. | Image failure does not remove the entity name. |
Use one full accessible name, avoiding duplicate announcements from image alt and fallback text. Initials alone do not adequately identify a person. A decorative avatar beside an equivalent visible name may be aria-hidden.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab | No additional tab stop. An enclosing link remains native. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
timelineChronological events with explicit dates, descriptions and current state.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-border-width-emphasis | border.width.emphasisobserveduse | 2px | Selected indicator, sticky header rule, invalid border. |
dependency.role-color-border-active | color.border.activeobserveduse | #e53935 | Active or focused control border. |
dependency.role-color-border-divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-4 | space.4observeduse | 4px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default |
|
current |
Read events as an ordered list with explicit time and status text. The caller supplies order; the component does not reorder unknown dates. Lines support the sequence without becoming the only status channel.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Divider along the event sequence. | Ordered list and visible time. |
| current | Emphasized start border. | aria-current and explicit Current text. |
Use real time values and visible dates. Identify the current event with aria-current and text. Avoid a color-only dot or an order that changes between visual presentation and reading order.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reach event links and actions. The sequence is not focusable. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
chartA one-series numeric chart with an always-visible complete data table.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-chart-axis | color.chart.axisobserveduse | #9e231f | Axis lines. |
dependency.role-color-chart-grid | color.chart.gridobserveduse | #2b0e0d | Grid lines. |
dependency.role-color-chart-label | color.chart.labelobserveduse | #bd787d | Axis labels. |
series.primary | color.chart.series-2observeduse | #f73f35 | Second series. |
header.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
row.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | ||||||||
|---|---|---|---|---|---|---|---|---|---|
default |
No data available. | ||||||||
empty | |||||||||
loading |
Show one bounded numeric series with its complete textual data table. Labels and exact values carry the meaning without color. The API accepts finite non-negative values; it rejects invalid data rather than silently inventing missing numbers.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Red primary series (color.chart.series-2), with canonical axis and label roles. | Complete labelled data table. |
| empty | Plot and rows are absent. | Explicit No data available text. |
| loading | Existing data remains readable under a busy indicator. | aria-busy with named loading text from the host. |
Keep the table visible without JavaScript. Do not replace it with tooltips or color-only legends. The single series uses the approved red chart role color.chart.series-2, as requested by the owner for graphs; foreground typography retains its rose roles. Each SVG bar has a text label. A loading status must distinguish old data from a completed refresh.
Column headers use color.text.bright; body row labels and numeric values use
color.text.default. Row-header semantics remain native th scope="row" even
though their typography matches body values. Do not apply the column-header color
to every th indiscriminately.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab | Visit any scrollable data region. Points do not add unnecessary tab stops. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
kbdA literal key or short keyboard sequence rendered as native kbd text.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-border-width-emphasis | border.width.emphasisobserveduse | 2px | Selected indicator, sticky header rule, invalid border. |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-4 | space.4observeduse | 4px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | Ctrl + K |
Show readable key names using native kbd text. This documents a shortcut; it does not bind one. State the platform when shortcuts differ.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Named content on the default surface. | Native text and structure. |
Write readable names such as Ctrl, Shift and Escape. Keep sequence punctuation as text. A shortcut hint does not replace an action’s accessible name.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| None | The keycap is documentation, not a button. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
Alerts, toasts, progress, loading, skeletons, tooltips, popovers, dialogs, drawers, empty and error states.
alertAn inline status message with a mandatory glyph on the status tint behind a 1px status border; role alert for danger, status otherwise.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
danger.bg | color.status.danger.tintobserveduse | #2b0e0d | Danger background tint. |
danger.border | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
danger.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
warning.bg | color.status.warning.tintapproveduse | #1f1a0c | Warning background tint. |
warning.border | color.status.warning.borderapproveduse | #c9973f | Warning border. |
warning.text | color.status.warning.textapproveduse | #c9973f | Warning text (7.54:1). |
success.bg | color.status.success.tintapproveduse | #0f1a0e | Success background tint. |
success.border | color.status.success.borderapproveduse | #86a46f | Success border. |
success.text | color.status.success.textapproveduse | #86a46f | Success text (7.13:1). |
info.bg | color.status.info.tintapproveduse | #0f141c | Info background tint. |
info.border | color.status.info.borderapproveduse | #7e9ebb | Info border. |
info.text | color.status.info.textapproveduse | #7e9ebb | Info text (7.08:1). |
neutral.bg | color.status.neutral.tintobserveduse | #241010 | Neutral background tint. |
neutral.border | color.status.neutral.borderobserveduse | #a3676b | Neutral border. |
neutral.text | color.status.neutral.textobserveduse | #bd787d | Neutral status text. |
body.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
close.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
close.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
close.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Danger | Warning | Success | Info | Neutral | Dismissible |
|---|---|---|---|---|---|---|
default |
Build failed
Two token references do not resolve in the default profile. |
Unsaved changes
The draft is kept locally until you publish. |
Saved
The settings apply to new windows. |
Fonts
The reference font is not distributed; a metric-compatible fallback is used. |
Read only
This profile is approved; open a decision to change it. |
New version
0.2.0 adds the extended overlay. |
hover | ||||||
focus-visible |
A status message that belongs to the page or form around it: a failed submit, a saved draft, a deprecation notice. It stays in flow, is never timed, and is announced once when it appears. Transient confirmations are toasts; blocking questions are dialogs.
The block on the status tint behind a 1px status border; the glyph in the
status text colour; the title in the same colour and the message in
color.text.default; an optional close button at the end. The five statuses
use their own tint, border and text roles: color.status.danger.tint,
color.status.warning.tint, color.status.success.tint,
color.status.info.tint and color.status.neutral.tint behind
color.status.danger.border, color.status.warning.border,
color.status.success.border, color.status.info.border and
color.status.neutral.border.
| State | Visual | Non-colour channel |
|---|---|---|
| default | status tint, 1px status border, glyph and title in the status text colour | the glyph ✕ ! ✓ i · |
| hover | close button background → color.action.tertiary.hover-bg; the alert itself does not react | cursor: pointer on the button only |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the close button | the ring |
The alert has no disabled, selected or loading state; a message that is no longer true is removed, not greyed.
Only the close button is in the tab order. Enter or Space dismisses and returns focus to where it was, or to the next focusable element after the alert when that element is gone. Escape does nothing, so a page-level Escape handler is never swallowed.
Danger uses role="alert" (assertive) because it needs attention now; the
other four use role="status" (polite). The container exists before its
text is set, so the live region fires; text is never injected together with
the container. The glyph is aria-hidden and the title states the status in
words (“Build failed”, “Saved”), so nothing depends on colour or on the glyph
alone. The close button is aria-label="Dismiss" and at least 24×24 CSS
pixels. Contrast: status text on its tint from 4.88:1 (danger) to 6.59:1
(warning, info); body text at least 7.56:1 on every tint; borders from 4.07:1
(neutral) to 6.59:1.
Everything is a tint, a 1px border and text; the glyph is a text character, not an icon font. Toolkits with a native info bar map its type to the five statuses and keep the glyph in the text; toolkits without a tint draw the border only and record the deviation.
A coloured left stripe with no border. A rounded, shadowed card. A banner
that slides in or auto-dismisses. Colour-only status without the glyph and a
worded title. Using role="alert" for success messages. A close button that
is an icon with no accessible name. Fixed-position alerts covering content.
| Key | Action |
|---|---|
| Tab | Reaches the close button when present; the alert itself is never focusable. |
| Enter / Space | Dismisses; focus returns to the element that had it, or to the next focusable element after the alert. |
| Escape | Nothing; alerts are not dialogs. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: toast badge dialog empty-state error-state
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
toastA brief notification in a fixed corner stack; canvas surface behind a 1px overlay border, status tints for the four polite statuses and the danger fill for critical ones.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.canvasapproveduse | #000000 | Page and terminal background. |
root.border | color.border.overlayproposeduse-and-reportD-008 | #e53935 | Every floating layer's 1px border. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
glyph.text | color.status.info.textapproveduse | #7e9ebb | Info text (7.08:1). |
warning.bg | color.status.warning.tintapproveduse | #1f1a0c | Warning background tint. |
warning.border | color.status.warning.borderapproveduse | #c9973f | Warning border. |
warning.text | color.status.warning.textapproveduse | #c9973f | Warning text (7.54:1). |
success.bg | color.status.success.tintapproveduse | #0f1a0e | Success background tint. |
success.border | color.status.success.borderapproveduse | #86a46f | Success border. |
success.text | color.status.success.textapproveduse | #86a46f | Success text (7.13:1). |
info.bg | color.status.info.tintapproveduse | #0f141c | Info background tint. |
info.border | color.status.info.borderapproveduse | #7e9ebb | Info border. |
info.text | color.status.info.textapproveduse | #7e9ebb | Info text (7.08:1). |
neutral.bg | color.status.neutral.tintobserveduse | #241010 | Neutral background tint. |
neutral.border | color.status.neutral.borderobserveduse | #a3676b | Neutral border. |
neutral.text | color.status.neutral.textobserveduse | #bd787d | Neutral status text. |
danger.bg | color.status.danger.fillobserveduse | #dc282e | Danger fill. |
danger.text | color.status.danger.on-fillobserveduse | #f9faf9 | Text on the danger fill (4.58:1). |
danger.button-bg-hover | color.action.destructive.pressed-bgobserveduse | #ae1914 | Destructive button pressed fill. |
action.text | color.text.linkapproveduse | #f73f35 | Strong red link text, distinct from rose body text and headings; retain the underline. |
action.bg-hover | color.interaction.hover.bg-strongobserveduse | #630f0d | Hover on menu items; D-027 moved links to interaction.hover.bg. |
action.text-hover | color.text.link-hoverobserveduse | #f4eeee | Link text on hover, over interaction.hover.bg-strong. |
close.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
close.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
close.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
count.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
stack.z | z.toastobserveduse | 40 |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Plain | Five statuses stacked | With action |
|---|---|---|---|
default | Settings saved. | Connection lost. Changes are queued. Battery at 10%. Screenshot copied. Workspace 3 moved to the left output. Layout locked. | Window closed. |
hover | |||
focus-visible | |||
stacked | |||
reduced-motion |
A short confirmation or notice that does not belong to any one element on the page: “Saved”, “Copied”, “Connection lost”. It appears in a fixed corner stack, is announced politely, and leaves on its own after a pause or when dismissed. Anything that needs a decision is a dialog; anything tied to a form is an alert.
The stack region; each toast on color.surface.canvas behind a 1px
color.border.overlay, or on a status tint with its status border; the glyph
and the text; an optional action; the Dismiss button; the “+n” count. The
danger toast is the historical critical notification: color.status.danger.fill
with color.status.danger.on-fill for the text, the glyph and both buttons.
| State | Visual | Non-colour channel |
|---|---|---|
| default | canvas surface, 1px overlay border, glyph in the status text colour | the glyph; the worded text |
| hover | action → color.interaction.hover.bg-strong with color.text.link-hover; Dismiss → color.action.tertiary.hover-bg; the timeout pauses | cursor: pointer; underline thickens on the action |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the focused button; on the danger fill the ring takes color.status.danger.on-fill; the timeout pauses | the ring |
| stacked | toasts sit in one column with a 4px gap, newest at the end; the last visible toast shows “+n” in color.text.muted for queued ones | the count text; at most three visible |
| reduced-motion | no transition; the toast is simply present or absent | prefers-reduced-motion; nothing moves |
The toast has no disabled, selected or busy state.
Toasts never take focus when they appear. A host shortcut (F6 on the web reference) moves focus into the region; Tab then walks each toast’s action and Dismiss; Escape dismisses the focused toast. While any toast has focus or the pointer, its timeout is paused.
The region is named “Notifications”; each toast is role="status" so it is
announced politely, and danger toasts are role="alert". The text states the
outcome in words and the glyph is aria-hidden. Every toast has Dismiss and
the timeout is at least 8 seconds, extended for toasts with an action; an
action is never the only way to reach what it does. Contrast: text 8.65:1 on
the canvas, the overlay border 4.69:1, status text on its tint from 5.31:1
(neutral) to 6.59:1, on-fill text 4.58:1 on the danger fill, which is itself
4.14:1 against the canvas.
A fixed region, tints, 1px borders and text glyphs. Hosts with a notification daemon send the text and urgency instead of drawing a toast; the danger fill maps to the critical urgency. Toolkits with a native toast overlay use it and record that the danger fill is not available.
A toast that slides in from an edge. A rounded, shadowed pill. A toast with no Dismiss that vanishes after two seconds. A toast that takes focus on appearance. Stacking more than three with no count. A form error shown as a toast. Colour-only status without the glyph and worded text.
| Key | Action |
|---|---|
| F6 / a host shortcut | Moves focus into the notification region; toasts never steal focus when they appear. |
| Tab / Shift+Tab | Within the region, reaches the action and then Dismiss of each toast in order. |
| Enter / Space | Activates the action or dismisses. |
| Escape | Dismisses the focused toast and returns focus to the region, then to the page. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: alert dialog progress badge
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX
progressA native progress element with a label and value; determinate fills with the primary colour, indeterminate shows static 45° stripes, never an animation.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
track.bg | color.interaction.pressed.bgobserveduse | #420f0c | Pressed fill. |
track.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
value.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
value.bg-error | color.status.danger.fillobserveduse | #dc282e | Danger fill. |
stripe.a | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
stripe.b | color.interaction.pressed.bgobserveduse | #420f0c | Pressed fill. |
label.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
value.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
detail.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
error.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Determinate | Indeterminate | With detail line |
|---|---|---|---|
default | Writing exports/components/date-picker.json | ||
busy | |||
error | |||
reduced-motion |
Shows how far a known task has gone, or that an unbounded one is running. It reports; it never blocks. A task that can be cancelled has a button beside the bar. A short wait with no bar is the loading indicator.
The header row with the label in color.text.bright and the value in
color.text.default; the bar, a native progress element whose track is
color.interaction.pressed.bg behind a 1px color.border.control and whose
fill is color.action.primary.bg; an optional detail line in
color.text.muted.
| State | Visual | Non-colour channel |
|---|---|---|
| default | fill color.action.primary.bg on the track; the value text | the native value; the percentage text |
| busy | the whole track shows static 45° stripes of color.action.primary.bg on color.interaction.pressed.bg; no value text | aria-busy; aria-valuetext; the stripe pattern; no animation |
| error | fill → color.status.danger.fill; the label gains the ✕ glyph in color.status.danger.text; the detail line says what failed | glyph; worded detail |
| reduced-motion | identical to default and busy: nothing animates in any state | prefers-reduced-motion |
Indeterminate work is the busy state; the determinate variant enters it while the total is unknown and leaves it once known.
None. The progress element is not focusable and receives no key. A cancel button next to the bar is an ordinary button with its own tab stop.
The label is a real <label for> on the progress element, so its name is
programmatic; the value is in the native value and repeated in text.
Indeterminate bars set aria-valuetext (“Preparing”), and the container sets
aria-busy for the duration. Completion and failure are announced through a
polite live region, not by the bar. Contrast: label 10.37:1, value 8.65:1,
detail 5.81:1, track boundary 4.45:1, error fill 3.36:1 against the track.
The primary fill against the track measures 1.51:1 and is waived because the
value is stated in text; a host that must meet 3:1 graphically raises the
track to color.surface.canvas.
A track, a fill and a hatched pattern. Toolkits with a native bar map the fill to the primary colour and disable pulse animation; toolkits without gradients draw the stripes with a hatched brush or alternating characters.
A spinner. Animated or “barber-pole” stripes. A shimmering skeleton in place of a bar. A circular ring. A rounded pill-shaped bar. Colour-only failure without the glyph and text. A bar with no label. A bar that blocks input.
| Key | Action |
|---|---|
| Tab | Nothing; progress is not focusable and never traps a person. |
| Escape | Nothing; cancelling is a separate button next to the bar. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: loading-indicator file-input wizard toast
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
loading-indicatorA static ⋯ glyph with a worded label inside an aria-busy container; inline next to text or as a centred block. There is no spinner.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
glyph.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
label.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
block.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
block.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Inline | Block |
|---|---|---|
default | Loading fonts |
Loading the component list
|
reduced-motion |
Marks the moment between a request and its result when there is nothing to measure: a list that is being fetched, a field that is being validated. The theme has no spinner; the same static ⋯ glyph that a loading control shows appears here beside a worded label, and the container says it is busy.
The busy container; the glyph in color.text.muted; the label in
color.text.default. Inline, the two follow the text they qualify; as a
block, they sit centred on color.surface.default inside a
color.border.divider edge where the content will appear.
| State | Visual | Non-colour channel |
|---|---|---|
| default | the static ⋯ and the label | aria-busy="true"; the glyph; the worded label |
| reduced-motion | identical: there is nothing to reduce | prefers-reduced-motion |
The indicator has no hover, focus or disabled state; it is not a control.
None. The indicator is never focusable. When loading replaces content that had focus, focus moves to the container’s heading or to the indicator’s parent region, never to the indicator itself, and returns to the result when it arrives.
The container is role="status" with aria-busy="true" while loading, so
assistive technology can wait for the subtree to settle and announce the
label once. The glyph is aria-hidden; the label is a real sentence with the
object named (“Loading fonts”) and is visible in both variants; a host that
must hide it inline keeps it visually hidden, never absent. Because nothing moves, there is no
prefers-reduced-motion branch to get wrong. Contrast: label 8.65:1 on the
canvas and 8.43:1 on the panel surface; glyph 5.81:1 and 5.66:1.
Text only. Toolkits with a spinner widget do not use it; the glyph and label are a plain label. Hosts that insist on an activity indicator record the deviation and keep it under the 150 ms motion budget with a reduced-motion branch.
A spinner or rotating ring. Animated dots. A shimmering skeleton. A full-page overlay that blocks input for a fetch. A glyph with no label. An indicator that takes focus. A dimmed container at reduced opacity.
| Key | Action |
|---|---|
| Tab | Nothing; the indicator is not focusable and never traps focus; the content it replaces keeps its tab stops when it returns. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: progress skeleton empty-state toast
Stress fixtures: FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
tooltipA short description of a control shown on hover and focus; role tooltip linked by aria-describedby, a 1px overlay border on the raised surface, no arrow.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
popup.bg | color.surface.raisedapproveduse | #160b0b | Sticky headers, menus, popovers; D-027 recessed it so an open list reads as depth rather than a lighter panel. |
popup.border | color.border.overlayproposeduse-and-reportD-008 | #e53935 | Every floating layer's 1px border. |
popup.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
shortcut.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
shortcut.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
trigger.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
trigger.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
trigger.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
popup.z | z.popoverobserveduse | 10 |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Description | With shortcut |
|---|---|---|
default | Reload the window and keep the layout.
| Split the focused container horizontally Mod+h
|
hover | ||
focus-visible | ||
open | ||
closed |
A few words that explain a control without being its name: what an icon button does, which shortcut opens it, why it is disabled. It is a description only. Anything a person must read to use the control belongs in the control or its label, and anything interactive is a popover.
The trigger and the popup. The popup is color.surface.raised behind a 1px
color.border.overlay, text in color.text.default, optional shortcut in a
kbd with color.text.bright inside a 1px color.border.control; no arrow,
no shadow, no radius. The demonstration renders it in flow below the trigger.
| State | Visual | Non-colour channel |
|---|---|---|
| default | trigger at rest; the popup shown for the demonstration | aria-describedby on the trigger |
| hover | trigger background → color.action.tertiary.hover-bg; the popup appears after 300 ms | cursor: pointer; the popup itself |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the trigger; the popup appears at once | the ring; the popup |
| open | the popup visible, 1px color.border.overlay on color.surface.raised | the popup is present in the accessibility tree |
| closed | the popup absent; the trigger unchanged | hidden; the description is still read from the DOM |
The popup itself has no hover, pressed or focus state: it is not interactive.
Focus on the trigger shows the tooltip without delay; moving focus away hides it. Escape hides it and does nothing else, so a dialog behind the trigger does not close. The popup never receives focus and contains no focusable element.
The trigger points at the popup with aria-describedby, so the description
is available whether or not the popup is shown; the popup is role="tooltip".
It opens on hover and on focus, stays open while the pointer is over the
trigger or the popup (WCAG 1.4.13: hoverable, dismissible, persistent), and
closes on Escape. The trigger has its own accessible name; the tooltip never
supplies it. Touch has no hover: the description is available through a
long-press or is shown inline. Contrast: text 8.43:1 on the raised surface,
shortcut 10.10:1, border 4.57:1.
A box with a border and text; placement is the host’s positioning primitive. Toolkits with a native tooltip use it with the tokens for border and background and record any radius they cannot remove.
A rounded bubble with an arrow. A tooltip with a shadow or a fade longer than 150 ms. A tooltip as the only accessible name of an icon button. A tooltip with a link or a button inside. One that opens on focus but not on hover, or on hover with no delay. A title attribute. A tooltip that follows the pointer.
| Key | Action |
|---|---|
| Tab | Focusing the trigger shows the tooltip; leaving it hides the tooltip. |
| Escape | Hides the tooltip without moving focus; the trigger's own Escape behaviour is not triggered. |
| Enter / Space | Act on the trigger as usual; the tooltip hides on activation. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: icon-button popover kbd toolbar menu
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX
dialogA native dialog on the overlay surface behind a 1px overlay border and the backdrop; modal when shown, with trapped focus and Escape to close.
Report an issue with this component
View source and copy recipe · Download standalone example — visual-only; modal behavior is host-owned.
| Part | Role | Value | Use |
|---|---|---|---|
backdrop.bg | color.surface.backdropproposeduse-and-reportD-008 | rgb(0 0 0 / 65%) | Behind modal dialogs. |
root.bg | color.surface.overlayobserveduse | #241010 | Dialogs and drawers. |
root.border | color.border.overlayproposeduse-and-reportD-008 | #e53935 | Every floating layer's 1px border. |
root.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.z | z.dialogobserveduse | 30 | |
title.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
text.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
close.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
close.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
close.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
button.text | color.action.secondary.textobserveduse | #ffa2a7 | Secondary button text. |
button.border | color.action.secondary.borderobserveduse | #a3676b | Secondary button border (meets 3:1). |
button.bg-hover | color.action.secondary.hover-bgobserveduse | #630f0d | Secondary button hover. |
primary.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
primary.text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
primary.bg-hover | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover (7.92:1 with prose). |
destructive.bg | color.action.destructive.filled-bgobserveduse | #dc282e | Confirm-dialog destructive fill. |
destructive.text | color.action.destructive.filled-textobserveduse | #f9faf9 | Text on the destructive fill. |
destructive.bg-hover | color.action.destructive.hover-bgobserveduse | #dc282e | Destructive button hover fill. |
field.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
field.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
field.label | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
note.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Confirm | Alert dialog | Form |
|---|---|---|---|
default | |||
open | |||
closed | |||
focus-trapped | |||
hover | |||
focus-visible | |||
reduced-motion |
A question or a small task that must be answered before the page continues:
confirm a destructive action, name a new profile, sign in again. It takes
the whole screen’s attention, so it is short, has a clear title, and always
offers a way out. The demonstration renders the dialog open in flow, with a
wrapper standing in for the backdrop; a host always uses showModal().
The backdrop, color.surface.backdrop over the page; the dialog on
color.surface.overlay behind a 1px color.border.overlay; the header with
the title in color.text.bright and Close; the body in color.text.default;
the actions row. Confirmation semantics do not prescribe action tone (D-015):
ordinary confirmations use primary. A destructive confirmation uses the filled destructive button,
color.action.destructive.filled-bg with color.action.destructive.filled-text;
the form dialog holds a labelled text field on color.surface.input.
| State | Visual | Non-colour channel |
|---|---|---|
| default | the dialog open on its backdrop | open; the page behind is inert |
| open | as default; opacity 0 → 1 within 150 ms on capable hosts | open; focus inside the dialog |
| closed | nothing: the dialog and its backdrop are absent | no open; focus back on the opener |
| focus-trapped | the dialog itself carries the container ring, 2px solid color.interaction.focus.ring-container at −3px, when it holds focus; the note says Tab cycles | Tab never leaves; the note text |
| hover | Close background → color.action.tertiary.hover-bg; other buttons take their tone’s hover | cursor: pointer |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the focused control; primary uses color.interaction.focus.ring-container, destructive uses color.action.destructive.filled-text (D-015) | the ring |
| reduced-motion | no opacity transition; the dialog is simply present or absent | prefers-reduced-motion |
A dialog is never disabled; a busy dialog disables its confirm button and shows the ⋯ glyph on it.
Tab and Shift+Tab cycle inside the dialog; nothing behind it is reachable. Escape closes and cancels, and focus returns to the opener. Enter submits a form dialog; elsewhere it only activates the focused button, so a destructive confirm is never triggered by Enter on the dialog itself. On open, focus goes to the first focusable element, or to the dialog when the first element is destructive; on close, back to the opener.
The dialog is named by its title through aria-labelledby and described by
its first paragraph through aria-describedby. Confirmations use
role="alertdialog". showModal() makes the rest of the page inert and
supplies the top layer; the theme never re-implements the trap on a
non-modal dialog. The backdrop is decorative and not a click target for
cancel unless Close is also present. Contrast: text 7.92:1 and title 9.49:1
on the overlay surface, the overlay border 4.30:1, secondary buttons 6.84:1
on hover, destructive confirm text 4.58:1 on its fill, the container ring
9.49:1.
A box, a border, a dimmed backdrop and the button tones. Toolkits with a native modal window use it with the overlay border on the frame and record that the backdrop is the host’s. Toolkits without a top layer draw the backdrop as a full-window overlay under the dialog.
A glowing or shadowed card. Rounded corners. A dialog that slides or scales
in. <dialog> shown with show() and a hand-written focus trap. A dialog
with no title, or with the title only in the accessible name. A destructive
confirm as the default Enter action. A backdrop that closes the dialog with
no Close button. A dialog stacked on a dialog.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Cycles through the dialog's focusable elements and never leaves it while open. |
| Escape | Closes the dialog (cancel) and returns focus to the element that opened it. |
| Enter | In a form dialog, submits; otherwise activates the focused button only. |
| Space | Activates the focused button. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: drawer alert toast button text-field popover
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX
drawerA full-height modal panel anchored to the start or end edge; a native dialog on the overlay surface with a 1px overlay border on its inner edge.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
backdrop.bg | color.surface.backdropproposeduse-and-reportD-008 | rgb(0 0 0 / 65%) | Behind modal dialogs. |
root.bg | color.surface.overlayobserveduse | #241010 | Dialogs and drawers. |
root.border | color.border.overlayproposeduse-and-reportD-008 | #e53935 | Every floating layer's 1px border. |
root.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.z | z.drawerobserveduse | 20 | |
title.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
text.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
body.divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
item.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
item.bg-hover | color.interaction.hover.bg-strongobserveduse | #630f0d | Hover on menu items; D-027 moved links to interaction.hover.bg. |
item.text-hover | color.text.link-hoverobserveduse | #f4eeee | Link text on hover, over interaction.hover.bg-strong. |
close.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
close.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
close.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
note.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Start edge | End edge |
|---|---|---|
default | ||
open | ||
closed | ||
focus-trapped | ||
reduced-motion |
A panel that opens from the side to hold navigation or a task on narrow screens, or a secondary form on wide ones. It is a modal dialog with a different shape: full height, anchored to the start or end edge, with a single visible border on the edge that faces the page. Below 900px the sidebar collapses into a drawer; above it, a drawer holds tasks that need more room than a dialog.
The backdrop, color.surface.backdrop over the page; the panel on
color.surface.overlay with a 1px color.border.overlay on its inner edge;
the header with the title in color.text.bright and Close; the body, rows
divided by color.border.divider. The demonstration renders the panel inside
a stage of fixed height standing in for the viewport.
| State | Visual | Non-colour channel |
|---|---|---|
| default | the panel open at its edge on the backdrop | open; the page behind is inert |
| open | as default; opacity 0 → 1 within 150 ms on capable hosts; never a slide | open; focus inside the panel |
| closed | nothing: the panel and the backdrop are absent | no open; focus back on the opener |
| focus-trapped | the panel carries the container ring, 2px solid color.interaction.focus.ring-container at −3px, when it holds focus; the note says Tab cycles | Tab never leaves; the note text |
| reduced-motion | no opacity transition; the panel is simply present or absent | prefers-reduced-motion |
Items inside the drawer take the states of the list or navigation they belong to; Close takes the tertiary button’s hover and ring.
Tab and Shift+Tab cycle inside the panel; the page behind is inert. Escape closes and returns focus to the opener. Enter or Space on a navigation item navigates and closes. On open, focus goes to the first focusable element, which is the first item or Close.
The panel is named by its title through aria-labelledby. showModal()
supplies the top layer and inertness; the theme never re-implements the trap
on a non-modal element or on a plain aside. The backdrop is decorative; a
click on it may close the drawer only because Close is also present. Both
edges use logical properties, so the panel and its border mirror in RTL with
no extra rules. Contrast: text 7.92:1 and title 9.49:1 on the overlay
surface, the border 4.30:1, the ring 4.30:1, hovered items 11.41:1 on the
strong hover fill.
A full-height box with one border and a dimmed backdrop. Toolkits without a top layer draw the backdrop as a full-window overlay under the panel; toolkits with a native sidebar keep it non-modal and record that it is a sidebar, not a drawer.
A panel that slides in from the edge. A drawer with a shadow or a rounded inner corner. A drawer that is a plain aside with a hand-written focus trap. A bottom sheet with a drag handle. A drawer with no Close button. A drawer that stays open under a dialog. A translucent panel over page content.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Cycles inside the drawer and never leaves it while open. |
| Escape | Closes the drawer and returns focus to the element that opened it. |
| Enter / Space | Activates the focused item or button; a navigation item closes the drawer after navigating. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: dialog sidebar-nav menu toolbar
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX
empty-stateWhat a container shows when it has nothing to list; a glyph, a heading in the bright text colour, one sentence in the muted colour and an optional secondary action.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
glyph.text | color.icon.decorativeobserveduse | #a3676b | Decorative glyphs (3:1 as graphics, never text). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
text.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
action.text | color.action.secondary.textobserveduse | #ffa2a7 | Secondary button text. |
action.border | color.action.secondary.borderobserveduse | #a3676b | Secondary button border (meets 3:1). |
action.bg-hover | color.action.secondary.hover-bgobserveduse | #630f0d | Secondary button hover. |
action.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | With action | Without action |
|---|---|---|
default | No components matchThree filters are active; none of the 67 components passes all of them. | No notificationsNew ones appear here and in the corner of the screen. |
A region with nothing in it says so, and says what would fill it. It replaces a blank pane, an empty table body or a list with no rows after a filter, and it offers the one action that changes that when there is one. A failure is the error state; a wait is the loading indicator.
The block on color.surface.default inside a 1px color.border.divider,
content centred; the glyph in color.icon.decorative; the heading in
color.text.bright; the sentence in color.text.muted; the optional
secondary action in color.action.secondary.text with a 1px
color.action.secondary.border.
| State | Visual | Non-colour channel |
|---|---|---|
| default | glyph, heading and sentence centred; the action, when present, below them | the worded heading; the action is a real button |
The block has no states of its own; the action takes the secondary button’s hover and ring.
Only the action is in the tab order. When a filter empties a list that had focus inside it, focus moves to the heading of the region, not into the empty state, so the person hears where they are.
The heading is a real heading at the level the region uses, so the empty
state appears in the outline; the sentence follows it in the reading order.
The glyph is aria-hidden. When content becomes empty after an action
(filter, delete), a polite live region announces the heading text. The
action is never the only route: filters can also be cleared where they were
set. Contrast: heading 10.10:1, sentence 5.66:1, action text 10.10:1 and its
border 4.33:1 on the panel surface.
Text and one optional button. Toolkits with a status page use it with the glyph as text; toolkits with only a placeholder string add the action as a separate button below the view.
An illustration or mascot. A primary (filled) call to action. A blank pane. A greyed-out table header with no message. A message that only says “No data”. A heading in the accent red. An empty state that appears while loading. Centred text in a rounded card with a shadow.
| Key | Action |
|---|---|
| Tab | Reaches the action when present; the block itself is never focusable. |
| Enter / Space | Activates the action. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: error-state loading-indicator list table data-table button
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX
skeletonStatic layout placeholders with a named loading status.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-border-divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
dependency.role-color-surface-raised | color.surface.raisedapproveduse | #160b0b | Sticky headers, menus, popovers; D-027 recessed it so an open list reads as depth rather than a lighter panel. |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-16 | space.16observeduse | 16px | |
dependency.role-space-2 | space.2observeduse | 2px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | Loading project details… |
loading | |
reduced-motion |
Reserve approximate layout space while a named region loads. Decorative blocks are hidden from accessibility APIs. A separate status describes loading. The skeleton is static and never shimmers.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Static decorative blocks. | Named loading status. |
| loading | Raised surface blocks; no opacity fade. | aria-busy on the region. |
| reduced-motion | Same static presentation. | No information depends on movement. |
Apply aria-busy to the region being updated and provide a concise status. Decorative blocks are aria-hidden. Do not remove the focused control merely to show a skeleton. Replacement content must keep a sensible reading order.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab | No new tab stops or focus capture. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
popoverA non-modal supporting panel with light dismissal and ordinary tab order.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-border-overlay | color.border.overlayproposeduse-and-reportD-008 | #e53935 | Every floating layer's 1px border. |
dependency.role-color-surface-overlay | color.surface.overlayobserveduse | #241010 | Dialogs and drawers. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-16 | space.16observeduse | 16px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | This workspace uses synthetic local data. |
open | |
closed | |
focus-visible |
Disclose a non-modal supporting panel near its trigger. Interactive content follows ordinary tab order. Escape and outside activation dismiss; focus is not trapped. Use the native Popover API when available with an equivalent documented fallback.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Named trigger. | Button semantics and accessible name. |
| open | Square overlay surface and border. | aria-expanded=true; panel is reachable. |
| closed | Panel hidden. | aria-expanded=false; hidden controls leave focus order. |
| focus-visible | Dashed focus ring. | Native keyboard focus. |
Keep essential instructions outside collapsed panels. Use a visible label or aria-label for the panel. Do not add aria-modal or a focus trap. Native automatic dismissal and the fallback both synchronize expanded state.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Enter / Space | Toggle from the trigger. |
| Tab / Shift+Tab | Move through ordinary controls without trapping focus. |
| Escape | Close and restore the trigger. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
error-stateA specific failure explanation with an explicit recovery request.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-border-width-emphasis | border.width.emphasisobserveduse | 2px | Selected indicator, sticky header rule, invalid border. |
dependency.role-color-border-disabled | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
dependency.role-color-interaction-disabled-bg | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
dependency.role-color-status-danger-border | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
dependency.role-color-status-danger-text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
dependency.role-color-text-disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | ✕ Project list unavailableThe demonstration request failed. Retry to load the local example records. |
error | |
loading | |
disabled | |
focus-visible |
Explain a failed operation and offer a bounded recovery action. Retrying emits a request; the component does not claim that a service recovered. The host updates content when the actual operation completes.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Danger border and text heading. | Specific written explanation and error glyph. |
| error | Same explicit failure treatment. | Failure text remains available. |
| loading | Busy status accompanies the existing explanation. | aria-busy and host-provided pending text. |
| disabled | Disabled roles at full opacity. | Native retry button is disabled. |
| focus-visible | Dashed ring. | Native keyboard focus. |
Do not rely only on color or a glyph. Give the retry action a specific name. Announce a newly occurring error in an appropriate host status region, without repeatedly interrupting reading. Keep focus unless recovery requires an explicit next step.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reach the recovery action. |
| Enter / Space | Request a retry without moving focus. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
Editor syntax, gutters, caret, selections, search, diagnostics, diffs, terminal text and the historical ANSI slots.
code-editorThe editor surface, gutter, caret, current line, selection, bracket match and monochrome syntax roles that every code-editing or code-viewing host maps onto.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.code.bgobserveduse | #0c0909 | Editor background. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
gutter.bg | color.code.gutter-bgobserveduse | #0c0909 | Gutter background. |
gutter.rule | color.code.gutter-ruleobserveduse | #2b0e0d | Rule between gutter and text. |
gutter.bg-read-only | color.surface.sunkenapproveduse | #000000 | Reading and detail panes set below the canvas. |
line-number.text | color.code.line-numberapproveduse | #ad7175 | Line numbers (5.10:1; replaces the site's #7d1310). |
line-number.text-active | color.code.line-number-activeobserveduse | #e99499 | Line number of the current line. |
line.bg-current | color.code.current-lineobserveduse | #1c0a09 | Current line background. |
caret.bg | color.code.caretobserveduse | #e99499 | Caret (Xresources *cursorColor). |
selection.bg | color.code.selection-bgobserveduse | #420f0c | Editor selection background (muted text stays 4.71:1). |
selection.bg-inactive | color.interaction.selection.inactive-bgobserveduse | #420f0c | Selection while the container is not focused. |
selection.text-inactive | color.interaction.selection.inactive-textobserveduse | #e99499 | Text on the inactive selection (7.02:1). |
bracket.outline | color.code.bracket-matchobserveduse | #e53935 | Matching bracket 1px border. |
indent.guide | color.code.indent-guideobserveduse | #531310 | Indent guides. |
whitespace.text | color.code.whitespaceobserveduse | #7d1310 | Rendered whitespace (decorative). |
fold.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
fold.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
syntax.keyword | color.code.syntax.keywordobserveduse | #f73f35 | Keywords (Xresources color5 lineage). |
syntax.string | color.code.syntax.stringobserveduse | #bd787d | Strings (color2 lineage). |
syntax.comment | color.code.syntax.commentapproveduse | #ad7175 | Comments, italic (color8 lineage, corrected for contrast). |
syntax.number | color.code.syntax.numberobserveduse | #d4868b | Numbers. |
syntax.constant | color.code.syntax.constantobserveduse | #d4868b | Constants and booleans. |
syntax.function | color.code.syntax.functionobserveduse | #ffa2a7 | Function names and calls (color7 lineage). |
syntax.variable | color.code.syntax.variableobserveduse | #e99499 | Variables and default text. |
syntax.type | color.code.syntax.typeobserveduse | #b37175 | Types. |
syntax.operator | color.code.syntax.operatorobserveduse | #e99499 | Operators. |
syntax.punctuation | color.code.syntax.punctuationobserveduse | #e99499 | Punctuation. |
syntax.property | color.code.syntax.propertyobserveduse | #e53935 | Properties and keys (site .syntax-key). |
syntax.heading | color.code.syntax.headingobserveduse | #e53935 | Markup headings (site .syntax-heading). |
syntax.tag | color.code.syntax.tagobserveduse | #f73f35 | Markup tags. |
syntax.attribute | color.code.syntax.attributeobserveduse | #d4868b | Markup attributes. |
syntax.escape | color.code.syntax.escapeobserveduse | #d4868b | Escape sequences and regex, bold. |
syntax.invalid | color.code.syntax.invalidobserveduse | #e53935 | Invalid text: wavy underline. |
syntax.deprecated | color.code.syntax.deprecatedobserveduse | #ad7175 | Deprecated symbol: line-through. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | JavaScript | Markup | Folded region |
|---|---|---|---|
default | // Compose the window title of the focused container. import { Container } from "./tree.js"; const MAX_LENGTH = 48; export function title(container) { const name = container.name ?? "untitled…"; let label = `${name} (${container.children.length})`; if (label.length > MAX_LENGTH) { label = label.slice(0, MAX_LENGTH) + "…"; } return escape(label); /* escape() is deprecated */ } const broken = 0x1G; | <!doctype html> <!-- window frame specimen --> <html lang="en"> <head> <title>j3w1 — theme</title> </head> <body class="frame" data-density="compact"> <h1>Window frame</h1> <marquee>obsolete</marquee> <p>Focus is a ring; selection is a fill.</p> </body> </html>< | export function title(container) { const name = container.name ?? "untitled"; if (label.length > MAX_LENGTH) { ⋯ } return escape(label); /* escape() is deprecated */ } |
focus-visible | |||
selected | |||
selected+container-inactive | |||
current | |||
read-only |
The reference rendering of source code: the surface, gutter, caret, current line, selection, bracket match, indent guides, rendered whitespace, fold markers and the seventeen monochrome syntax roles. It is the contract a real editor’s theme API is mapped onto; this component never edits.
Root .code-editor; parts .code-editor-<part>: -pre (the <pre>),
-line, -ln (line number), -text, -indent, -ws (rendered
whitespace), -caret, -sel (selection range), -bracket, -fold, and
the gutter fold glyph -fold-gutter. Syntax spans are .code-editor-<role>:
-kw keyword, -str string, -cm comment, -num number, -const
constant, -fn function, -var variable, -type type, -op operator,
-punct punctuation, -prop property, -heading heading, -tag tag,
-attr attribute, -esc escape, -invalid invalid, -deprecated
deprecated. Line numbers are color.code.line-number on
color.code.gutter-bg, separated from the text by a 1px
color.code.gutter-rule; indent guides are 1px color.code.indent-guide;
rendered whitespace is color.code.whitespace. Every colour is a role
variable, so the profile switch on the specimen recolours the sample without
touching the markup.
| State | Visual | Non-colour channel |
|---|---|---|
| default | code on color.code.bg; comments italic; invalid text wavy-underlined in color.code.syntax.invalid; deprecated text struck through | italic, wavy underline, line-through |
| focus-visible | container ring 2px color.interaction.focus.ring-container at −3px; a 1px color.code.caret bar; the caret line filled color.code.current-line across gutter and text; its number in color.code.line-number-active | the ring; the caret bar |
| selected | the range filled color.code.selection-bg; syntax colours kept | the range is announced through the host’s selection; the fill spans line ends |
| selected+container-inactive | the same range in color.interaction.selection.inactive-bg with color.interaction.selection.inactive-text; no caret, no ring | lightness drop between the two fills |
| current | both characters of the matched pair outlined 1px solid color.code.bracket-match | the outline is a box, not a colour change |
| read-only | gutter drops to color.surface.sunken behind the same line numbers; the gutter rule becomes dotted; no caret is drawn | aria-readonly; dotted rule; missing caret |
Precedence: selection is drawn under the text, the current line under the selection, the bracket outline and the caret above everything. The caret never blinks in the reference; a host that blinks it keeps the user’s cursor-blink setting and stops under reduced motion.
The host editor owns every binding. The theme states only what it draws in response: the caret and current line while focused, the selection fill for the host’s selection, the outline for the host’s bracket match. The reference markup is one Tab stop and, being read-only, consumes no key. A host that captures Tab for indentation must offer an escape route (Escape, then Tab) and announce it; the theme does not provide one.
Syntax colour is decoration: no meaning is carried by colour alone. Invalid
text has the wavy underline, deprecated text the line-through, comments the
italic. Selection is a fill and focus is a ring, drawn together and never
substituted for one another. Every syntax role reaches 4.5:1 on
color.code.bg (the lowest is color.code.syntax.property at 4.69:1) and
color.text.default reaches 7.02:1 inside the selection fill. Comment,
type, keyword and property text drop below 4.5:1 inside
color.code.selection-bg (3.81–4.38:1); this is a recorded limitation of the
selection value and is not to be fixed by recolouring the tokens locally. Line
numbers are metadata people read and use color.text.subtle, not a graphic
colour; indent guides, rendered whitespace and the gutter rule are decorative.
In forced-colours mode the caret, selection and bracket outline take the
system Highlight, and the syntax spans keep their text styles.
A real editor is a host surface: CodeMirror, Monaco, GtkSourceView,
KSyntaxHighlighting, an .icls scheme or a terminal editor each expose a
theme API, and the port maps the roles here onto that API without touching
caret behaviour, keyboard handling or selection semantics. Where a host has no
bracket-match outline it may use the bold weight and records the deviation;
where a host cannot draw a 1px caret it uses its own caret in
color.code.caret. The heritage-ansi and extended profiles change only
role values; the mapping does not change.
Rainbow syntax in the default profile (the three extension hues are
proposed only in extended). A blinking or animated caret in the reference.
A selection that recolours the text to white and loses the syntax roles. A
current-line highlight brighter than the selection. A bracket match drawn as
a background fill, which reads as a selection. Line numbers in the decorative
#7d1310. A glow, gradient or rounded corner on the editor box. Removing the
host’s focus indicator without drawing the container ring.
| Key | Action |
|---|---|
| (host) | The host editor owns every key; the theme changes no binding, no caret behaviour and no selection model. |
| Tab | Reaches the editor as one stop in the reference; whether Tab inserts or moves focus is the host's rule and is announced by the host. |
| Escape | In hosts that trap Tab, Escape must release focus to the next stop; the theme does not provide it. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: diagnostics diff-view terminal
Stress fixtures: FX-STATE-MATRIX FX-320 FX-ZOOM-200 FX-RM FX-HC FX-LONG FX-RTL
diagnosticsInline problem markers in code (underline patterns, an error line and an overview stripe) and the problems panel that lists them as a listbox, each severity carrying its glyph.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
code.bg | color.code.bgobserveduse | #0c0909 | Editor background. |
code.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
code.line-number | color.code.line-numberapproveduse | #ad7175 | Line numbers (5.10:1; replaces the site's #7d1310). |
code.gutter-rule | color.code.gutter-ruleobserveduse | #2b0e0d | Rule between gutter and text. |
code.selection-bg | color.code.selection-bgobserveduse | #420f0c | Editor selection background (muted text stays 4.71:1). |
error.underline | color.diagnostic.error.underlineobserveduse | #e53935 | Error wavy underline. |
error.text | color.diagnostic.error.textobserveduse | #f73f35 | Error message text. |
error.stripe | color.diagnostic.error.stripeobserveduse | #e53935 | Error stripe mark. |
error.line-bg | color.diagnostic.error.bgobserveduse | #2b0e0d | Error line background. |
warning.underline | color.diagnostic.warning.underlineapproveduse | #c9973f | Warning wavy underline. |
warning.text | color.diagnostic.warning.textapproveduse | #c9973f | Warning message text. |
warning.stripe | color.diagnostic.warning.stripeapproveduse | #c9973f | Warning stripe mark. |
info.underline | color.diagnostic.info.underlineapproveduse | #7e9ebb | Info dotted underline. |
info.text | color.diagnostic.info.textapproveduse | #7e9ebb | Info message text. |
hint.underline | color.diagnostic.hint.underlineobserveduse | #ad7175 | Hint dotted underline. |
hint.text | color.text.subtleapproveduse | #ad7175 | Metadata, comments, line numbers. |
unused.text | color.diagnostic.unused.textobserveduse | #bd787d | Unused symbol, dotted underline. |
deprecated.text | color.code.syntax.deprecatedobserveduse | #ad7175 | Deprecated symbol: line-through. |
row.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
row.location | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
row.divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
row.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
row.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
row.bg-selected | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
row.text-selected | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
row.ring-selected | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
header.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
status.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
empty.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
empty.glyph | color.status.success.textapproveduse | #86a46f | Success text (7.13:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Inline markers | Problems panel |
|---|---|---|
default | ✕ Language server stopped; markers may be stale. import { readFile } from "node:fs/promises"; const title = document.title; function render(frame) { const count = frame.children.length; escape(title); return frmae.title + count; } render(document.body) == null; No problems in title.js |
Problems
✕ 1 error
! 1 warning
i 1 info
· 3 hints
✕ Language server stopped; the list may be stale.
No problems |
hover | ||
focus-visible | ||
selected | ||
selected+focus-visible | ||
error | ||
empty |
How problems appear on code and in the list that indexes them. Six kinds are distinguished by pattern and glyph as well as colour: error, warning, info, hint, unused and deprecated. The panel is the keyboard route to every marker.
Root .diagnostics with the variant class .diagnostics-inline or
.diagnostics-panel. Inline parts: .diagnostics-code (the <pre>),
-line, -ln, -text, -mark (the stripe cell), and the ranges
.diagnostics-error, -warning, -info, -hint, -unused, -deprecated;
.diagnostics-line-error marks the row filled color.diagnostic.error.bg;
.diagnostics-target is the range that belongs to the selected row. Panel
parts: -header, -title, -count, -status, -list, -row, -glyph
(with -glyph-error and so on), -message, -loc, -rule, -empty.
Patterns: error and warning wavy, info and hint dotted, unused dotted in
color.diagnostic.unused.text, deprecated line-through in
color.code.syntax.deprecated. Glyphs: ✕ error in
color.diagnostic.error.text, ! warning in color.diagnostic.warning.text,
i info in color.diagnostic.info.text, · hint in color.text.subtle.
The stripe mark repeats the glyph in the stripe colour
(color.diagnostic.error.stripe, color.diagnostic.warning.stripe).
| State | Visual | Non-colour channel |
|---|---|---|
| default | ranges decorated as above on color.code.bg; the error row filled color.diagnostic.error.bg; panel rows on color.surface.default with 1px color.border.divider between them | underline pattern; glyph |
| hover | panel row background → color.interaction.hover.bg; inline ranges unchanged (the hover tooltip is a host surface) | cursor: default on rows |
| focus-visible | the active row: 1px dashed color.interaction.focus.ring at −2px; inline box: the container ring 2px color.interaction.focus.ring-container at −3px | the ring |
| selected | row filled color.interaction.selection.bg, text and glyph color.interaction.selection.text; the matching inline range filled color.code.selection-bg under its underline | aria-selected; the glyph stays; the fill spans the row |
| selected+focus-visible | the selection fill and a 1px dashed ring in color.interaction.focus.ring-container | both at once |
| error | the provider failed: a role=status line with ✕ in color.status.danger.text above the stale rows; the inline stripe shows the same ✕ at its top | glyph; live announcement |
| empty | no problems: rows and stripe marks hidden; ✓ No problems in color.text.muted with the glyph in color.status.success.text; inline decorations removed | glyph; the absence of decoration |
Precedence: selected > hover; focus-visible is drawn on top. A selected row recolours its glyph to the on-selection text so the shape, not the hue, carries the severity.
The panel follows the APG listbox pattern with aria-activedescendant:
the list is one Tab stop, Up and Down move the active row, Home and End jump,
type-ahead matches message text, Enter opens the location. Selection follows
focus. Inline markers add no keys; next-problem and previous-problem
navigation and the hover card are host bindings and host surfaces.
Never colour-only: every severity has its underline pattern in code and its
glyph in the panel, and the glyph is part of the option’s accessible name
(“Error: frmae is not defined, title.js line 6”). The error row background
is an addition to the wavy underline, not a replacement. Contrast:
color.diagnostic.error.text reaches 4.88:1 on the error row and 5.26:1 on
the panel; every underline reaches 3:1 on its actual background including
the selected range (the error underline measures 3.81:1 on the code
selection fill); color.diagnostic.unused.text reaches 5.81:1
so unused code is still readable. aria-selected and aria-activedescendant
carry the state; the status line is role=status, announced once. In
forced-colours mode the underline patterns persist because they are
text-decoration, not backgrounds.
Underline styles are native in Pango, Qt, JetBrains and most terminal emulators; where wavy is unavailable the port uses a plain underline plus the glyph and records the deviation. Hosts that draw their own overview ruler map the stripe colours; hosts without one omit the stripe and keep the glyphs. The listbox maps to any single-selection list widget.
Red-only errors without the ✕ glyph or the wavy pattern. A wavy underline
for every severity, which leaves the pattern meaningless. Purple or cyan for
info and hint. A hover on a panel row that changes only the text colour. A
selected row whose glyph keeps its severity colour on the red fill (2.47:1).
A panel row with a rounded highlight or a left accent bar instead of the fill.
Removing the error line background to “calm” the editor.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus to and from the list as one stop; the active row is restored. |
| Down / Up | Moves the active row; selection follows focus in this single-select listbox. |
| Home / End | First or last row. |
| Enter | Opens the location in the editor and moves focus there. |
| Type-ahead | Jumps to the next row whose message starts with the typed characters. |
| (host) | Inline markers have no keyboard of their own; navigation between problems (F8-style) is the host's binding. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: code-editor diff-view terminal
Stress fixtures: FX-STATE-MATRIX FX-320 FX-ZOOM-200 FX-RM FX-HC FX-LONG FX-I18N
diff-viewUnified and split diffs as native tables; added, removed and modified rows keep their textual gutter marker, word-level changes are underlines, conflicts are double borders.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.code.bgobserveduse | #0c0909 | Editor background. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
line-number.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
line-number.rule | color.code.gutter-ruleobserveduse | #2b0e0d | Rule between gutter and text. |
row.divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
added.bg | color.diff.added.bgapproveduse | #0f1a0e | Added line background. |
added.gutter | color.diff.added.gutterapproveduse | #86a46f | Added gutter mark and +. |
added.emphasis | color.diff.added.emphasisapproveduse | #86a46f | Word-level addition: 2px underline. |
removed.bg | color.diff.removed.bgobserveduse | #2b0e0d | Removed line background. |
removed.gutter | color.diff.removed.gutterobserveduse | #e53935 | Removed gutter mark and −. |
removed.emphasis | color.diff.removed.emphasisobserveduse | #e53935 | Word-level removal: underline and line-through. |
modified.bg | color.diff.modified.bgapproveduse | #1f1a0c | Modified line background. |
modified.gutter | color.diff.modified.gutterapproveduse | #c9973f | Modified gutter mark and ~. |
hunk.bg | color.diff.header-bgobserveduse | #241010 | Hunk header background. |
hunk.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
hunk.context | color.text.defaultobserveduse | #e99499 | Interface and body text. |
conflict.border | color.diff.conflict-borderobserveduse | #f73f35 | Conflict block: double 1px border. |
conflict.marker | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
fold.text | color.text.linkapproveduse | #f73f35 | Strong red link text, distinct from rose body text and headings; retain the underline. |
fold.bg-hover | color.interaction.hover.bg-strongobserveduse | #630f0d | Hover on menu items; D-027 moved links to interaction.hover.bg. |
row.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
row.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
row.bg-selected | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
row.text-selected | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
row.ring-selected | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Unified | Split | Word-level | Merge conflict | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
default |
|
|
|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hover | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
focus-visible | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expanded | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
collapsed | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
How changed text is shown: line rows with a fill and a textual marker,
word-level changes as underlines that leave text contrast untouched, hunk
headers, collapsible unchanged regions, and merge conflicts in a double
border. The +, − and ~ markers are content, present in every state and
every profile.
Root .diff-view (the table) with the variant class .diff-view-unified,
-split, -word-level or -conflict. Row classes: .diff-view-row,
-row-context, -row-added, -row-removed, -row-modified, -row-hunk,
-row-fold, -row-conflict with -row-conflict-start, -row-conflict-mid
and -row-conflict-end for the marker lines. Cells: .diff-view-ln,
-marker, -text, -fold-cell, and -range for the @@ span; the split
variant adds -side-old and -side-new on cells and, on a -row-pair,
-cell-removed and -cell-added per side (an empty -cell-blank faces an
added line); the conflict variant adds -row-ours and -row-theirs. Word-level spans: .diff-view-word-added (2px underline in
color.diff.added.emphasis) and .diff-view-word-removed (underline plus
line-through in color.diff.removed.emphasis). The fold button is
.diff-view-fold with an inline SVG chevron .diff-view-chevron and two
labels, -fold-collapsed and -fold-expanded; the folded rows are a
<tbody class="diff-view-folded" hidden>. Line numbers
use color.text.muted rather than color.code.line-number because the
modified fill would drop the subtle step to 4.45:1.
| State | Visual | Non-colour channel |
|---|---|---|
| default | added rows color.diff.added.bg with + in color.diff.added.gutter; removed color.diff.removed.bg with − in color.diff.removed.gutter; modified color.diff.modified.bg with ~ in color.diff.modified.gutter; hunk rows color.diff.header-bg; conflicts in a 3px double color.diff.conflict-border | the markers; the double border |
| hover | the row’s number and marker cells → color.interaction.hover.bg; the text cell keeps its fill | cursor: default; only on hover-capable pointers |
| selected | the whole row filled color.interaction.selection.bg with color.interaction.selection.text; the marker stays | aria-current (or :target for a permalinked line); the marker glyph |
| focus-visible | 1px dashed color.interaction.focus.ring at −2px around the row; on a selected row the ring is color.interaction.focus.ring-container | the ring |
| expanded | the folded <tbody> is shown; the chevron points down; the button reads “Hide 12 unchanged lines” | aria-expanded="true"; the rows appear |
| collapsed | the folded <tbody> is hidden; the chevron points to the inline end; ”… 12 unchanged lines” | aria-expanded="false"; the ellipsis |
Precedence: selected > hover; focus-visible is always drawn. A selected row loses its diff fill but never its marker, which is why the marker is content.
A plain table has no row focus. The official web distribution places it in a named, focusable scroll region so keyboard users can inspect wide lines without page overflow. The region uses the canonical container focus ring. Within the table, the fold buttons are the only stops and toggle with Enter or Space, the chevron turning within the motion budget. Hosts that expose the diff as a grid own row navigation and draw the ring on the focused row; the theme adds no binding of its own.
The caption names both files. Every changed row carries its marker in a cell
of its own, so a screen reader announces + or − before the line and a
monochrome print keeps the distinction. Word-level changes use
text-decoration (underline, or underline and line-through), never a fill,
so the row text stays at 7.56:1 or better on every diff fill. The − marker
measures 4.24:1 on the removed fill: it is bold, sits on its own fill and is
a glyph, so it is held to the 3:1 graphics floor and recorded here. Folded
hunks use aria-expanded and aria-controls on a real button; the hidden
rows are hidden, not visually clipped, so they are not read while
collapsed. Conflict markers are the literal <<<<<<<, ======= and
>>>>>>> lines in color.text.bright on color.diff.header-bg, inside the
double border.
A table with cell backgrounds, borders and text decoration is expressible in
every toolkit. Hosts without double borders draw two 1px lines and record it;
hosts without line-through keep the underline and the − marker. The three
extension hues are confined to the diff group here and change with the
profile; the markers do not.
Red and green fills with no +/− marker. Word-level changes drawn as a
bright fill that pushes the text below 4.5:1. A removed line rendered in
text.disabled or struck through as a whole. Collapsed hunks that are only
visually clipped and still read out. Conflict blocks in purple. Hover that
recolours the code text. Round-cornered hunk cards floating on the page. A
selected row that drops the marker because “the colour says it”.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves between the fold buttons and any row the host makes focusable; rows are not stops in a plain table. |
| Enter / Space | On a fold button toggles the hidden hunk; focus stays on the button. |
| Down / Up | In grid hosts, moves the selected row; the theme draws the ring on whichever row the host focuses. |
| (host) | Next-change and previous-change navigation, staging and comment affordances are host bindings. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: code-editor diagnostics terminal
Stress fixtures: FX-STATE-MATRIX FX-320 FX-ZOOM-200 FX-RM FX-HC FX-LONG FX-OVERFLOW
terminalTerminal text on the terminal surface with the sixteen heritage ANSI slots carried exactly, the cursor, the selection and the six text attributes; a transcript, the slot grid and the attributes.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.terminal.bgobserveduse | #0c0909 | Terminal background. |
root.text | color.terminal.fgobserveduse | #e99499 | Terminal foreground. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
root.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
cursor.bg | color.terminal.cursorobserveduse | #e99499 | Terminal cursor. |
cursor.text | color.terminal.bgobserveduse | #0c0909 | Terminal background. |
selection.bg | color.terminal.selection-bgobserveduse | #911410 | Terminal selection. |
selection.text | color.terminal.selection-textobserveduse | #f4eeee | Terminal selection text. |
current.bg | color.code.current-lineobserveduse | #1c0a09 | Current line background. |
prompt.bg | color.terminal.ansi.4observeduse | #8c1212 | ANSI slot 4, exact heritage value. |
prompt.text | color.terminal.selection-textobserveduse | #f4eeee | Terminal selection text. |
prompt.branch-bg | color.terminal.ansi.3observeduse | #d4868b | ANSI slot 3, exact heritage value. |
prompt.branch-text | color.terminal.bgobserveduse | #0c0909 | Terminal background. |
dim.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
inverse.bg | color.terminal.fgobserveduse | #e99499 | Terminal foreground. |
inverse.text | color.terminal.bgobserveduse | #0c0909 | Terminal background. |
label.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
fail.glyph | color.terminal.fgobserveduse | #e99499 | Terminal foreground. |
slot.ansi-0 | color.terminal.ansi.0observeduse | #0c0909 | ANSI slot 0, exact heritage value. |
slot.ansi-1 | color.terminal.ansi.1observeduse | #c81a1a | ANSI slot 1, exact heritage value. |
slot.ansi-2 | color.terminal.ansi.2observeduse | #bd787d | ANSI slot 2, exact heritage value. |
slot.ansi-3 | color.terminal.ansi.3observeduse | #d4868b | ANSI slot 3, exact heritage value. |
slot.ansi-4 | color.terminal.ansi.4observeduse | #8c1212 | ANSI slot 4, exact heritage value. |
slot.ansi-5 | color.terminal.ansi.5observeduse | #f73f35 | ANSI slot 5, exact heritage value. |
slot.ansi-6 | color.terminal.ansi.6observeduse | #9e474a | ANSI slot 6, exact heritage value. |
slot.ansi-7 | color.terminal.ansi.7observeduse | #ffa2a7 | ANSI slot 7, exact heritage value. |
slot.ansi-8 | color.terminal.ansi.8observeduse | #7d1310 | ANSI slot 8, exact heritage value. |
slot.ansi-9 | color.terminal.ansi.9observeduse | #ab1612 | ANSI slot 9, exact heritage value. |
slot.ansi-10 | color.terminal.ansi.10observeduse | #ad2721 | ANSI slot 10, exact heritage value. |
slot.ansi-11 | color.terminal.ansi.11observeduse | #b37175 | ANSI slot 11, exact heritage value. |
slot.ansi-12 | color.terminal.ansi.12observeduse | #871f19 | ANSI slot 12, exact heritage value. |
slot.ansi-13 | color.terminal.ansi.13observeduse | #e82132 | ANSI slot 13, exact heritage value. |
slot.ansi-14 | color.terminal.ansi.14observeduse | #e0292f | ANSI slot 14, exact heritage value. |
slot.ansi-15 | color.terminal.ansi.15observeduse | #a3676b | ANSI slot 15, exact heritage value. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Transcript | ANSI grid | Attributes |
|---|---|---|---|
default | ~/theme main ls -l ports total 5 drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 alacritty drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 gtk -rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 README.md -rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 verify.sh lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 xresources -> ../tokens/ -rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 evidence.tar.gz ~/theme main cat ports/gtk/port.json cat: ports/gtk/port.json: No such file or directory ~/theme main npm run validate | slot normal bold background 0 ✕ 1 ✕ 2 normal bold 3 normal bold 4 ✕ 5 normal bold 6 ✕ 7 normal bold 8 ✕ 9 ✕ 10 ✕ 11 normal bold 12 ✕ 13 ✕ 14 ✕ 15 ✕ ✕ fails 4.5:1 on the terminal background; the extended profile reassigns the slots | bold Warm-black surfaces, rose text. dim Warm-black surfaces, rose text. italic Warm-black surfaces, rose text. underline Warm-black surfaces, rose text. inverse Warm-black surfaces, rose text. strike Warm-black surfaces, rose text. selected Warm-black surfaces, rose text. |
focus-visible | |||
selected | |||
busy | |||
current |
The reference for terminal text: the surface, the default foreground, the
cursor, the selection, the six attributes and the sixteen slots the
workstation froze in Xresources. The slots are the origin of the theme and
are carried exactly; what a program does with a slot is that program’s
choice, and the theme documents where a choice fails rather than quietly
fixing it.
Root .terminal (the <pre>) with a variant class .terminal-transcript,
.terminal-ansi-grid or .terminal-attributes. Parts .terminal-<part>:
-line, -prompt with -prompt-path, -prompt-branch and -prompt-arrow,
-cursor, -sel, -label, -fail (the ✕), and the attributes -bold,
-dim, -italic, -underline, -inverse, -strike. Slot classes are
.terminal-fg-0 … .terminal-fg-15 and .terminal-bg-0 … .terminal-bg-15,
each a single role variable color.terminal.ansi.0 … color.terminal.ansi.15,
so the profile switch on the specimen recolours the grid without touching
the markup. The foreground is color.terminal.fg on color.terminal.bg;
the prompt path segment is color.terminal.selection-text on
color.terminal.ansi.4 and the branch segment color.terminal.bg on
color.terminal.ansi.3, the two agnoster backgrounds the workstation used.
| State | Visual | Non-colour channel |
|---|---|---|
| default | text on color.terminal.bg; the cursor cell hollow, a 1px color.terminal.cursor outline; no ring | the hollow cursor |
| focus-visible | container ring 2px color.interaction.focus.ring-container at −3px; the cursor solid color.terminal.cursor with color.terminal.bg text | the ring; the cursor fills |
| selected | the run filled color.terminal.selection-bg with color.terminal.selection-text; slot colours inside the run give way to the selection text | the fill spans whole cells; the emulator exposes the selection |
| busy | a command is running: the cursor is a solid static block at the end of the output; the transcript is aria-busy | aria-busy; the cursor shape; no blink |
| current | the cursor line filled color.code.current-line across its width; the cursor as in focus | the row fill; the cursor cell |
The cursor never blinks in the reference. A host keeps the user’s own blink setting and stops blinking under reduced motion; the theme never turns it on.
The emulator and the shell own every key, including Tab, Escape and the selection modifiers. The reference transcript is a single focus stop that consumes nothing. The theme draws the ring when the host reports focus, the fill when the host reports a selection, and the block cursor where the host puts it.
The transcript is role=log so new output is announced politely; the grid
and the attribute sheet are plain <pre> elements with a label. The sixteen
slots are exact in every profile: slots 2, 3, 5, 7 and 11 reach 4.5:1 on the
terminal background; slots 1, 4, 6, 8, 9, 10, 12, 13 and 14 do not
(1.86:1–4.43:1), and the grid marks each of them with a ✕ after its number
so the failure is visible, not implied. Two more are marked on the same
evidence: slot 15 measures 4.45:1 and slot 0 is the background itself. The
extended overlay reassigns the slots to a semantic sixteen that all reach
4.5:1; the site’s profile control shows the grid under it. The background
row is legible only with a foreground the program chooses: with the default
foreground only slots 0 and 8 pass, which is why the row is shown and not
declared as a contract. Two escape families bypass the slots entirely and
are outside the theme: bold-as-bright, which substitutes slots 8–15 for 0–7
and must be off for the slots to hold, and 256-colour or 24-bit sequences,
which carry their own values. Attributes are attributes: dim uses
color.text.muted (5.81:1) rather than a slot, inverse swaps foreground and
background (8.65:1), and bold, italic, underline and strikethrough are text
styles a screen reader can expose.
Every emulator with a sixteen-slot palette can hold these values byte for
byte; the port’s evidence records whether bold-as-bright is off and whether
the cursor and selection keys exist. Programs that pick slot 4 or 12 for
directories inherit the documented failure; a port may recommend LS_COLORS
or a prompt configuration but never changes the slots to compensate. The
cursor shape (block, bar, underline) is the user’s setting; the theme only
supplies its colour.
Normalising the heritage slots so every one reaches 4.5:1 and calling it
heritage-ansi. A blinking cursor in the reference. A cursor that is a
glowing bar. Bold rendered as bright, which silently swaps the slots. A
purple or cyan slot borrowed from another scheme. Wrapping the transcript at
narrow widths and breaking the prompt segments. A selection drawn as a ring
instead of a fill. Rounded prompt segments or a gradient prompt.
| Key | Action |
|---|---|
| (host) | The emulator and the shell own every key; the theme changes no binding and no cursor behaviour. |
| Tab | Reaches the transcript as one focus stop; inside a live emulator Tab belongs to the shell. |
| Shift+Arrows / mouse | Selection is the emulator's; the theme draws it in selection-bg and selection-text. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: code-editor diagnostics diff-view
Stress fixtures: FX-STATE-MATRIX FX-320 FX-ZOOM-200 FX-RM FX-HC FX-LONG FX-OVERFLOW
editor-searchLiteral search and match navigation over escaped read-only source text.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-code-search-current-bg | color.code.search-current-bgobserveduse | #ffa2a7 | Current search match background. |
dependency.role-color-code-search-current-text | color.code.search-current-textobserveduse | #0c0909 | Current search match text. |
dependency.role-color-code-search-match-bg | color.code.search-match-bgobserveduse | #630f0d | Other search matches. |
dependency.role-color-interaction-focus-ring-container | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
dependency.role-color-surface-code | color.surface.codeapproveduse | #000000 | Editor background. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default |
|---|---|
default | Enter text to search. const theme = 'j3w1';
const mode = 'dark';
export { theme, mode }; |
filled | |
no-results | |
focus-visible |
Find literal text in a read-only code sample. Search, previous and next expose exact match counts and the current match. The component does not execute regexes, mutate source text or provide a code editor backend.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Plain escaped source. | Readable preformatted text. |
| filled | Match fill; current match has its own marker. | Exact current and total counts. |
| no-results | No highlighted matches. | Explicit zero-results status. |
| focus-visible | Dashed control ring. | Native keyboard focus. |
Render source as text, never as trusted HTML from the consumer. Highlighted matches must retain the exact source text for copying. Announce counts without moving input focus. Match navigation scrolls the current mark into view, and reduced motion requires no animation.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Reach query and match navigation. |
| Enter / Shift+Enter | Move to next or previous match. |
| Escape | Clear the query and retain focus. |
Component JSON ·Implementation brief ·Build a task kit ·
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
Settings panel, administrative form, filterable table, i3-inspired window frame; file browser, compact dashboard and form builder later.
settings-panelA two-column settings screen composed from the sidebar-nav, fieldset, switch, select, radio-group, text-field, checkbox and button components; below 600px the sidebar becomes a tab strip.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.border | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
nav.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
nav.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
nav.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
nav.current-text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
nav.current-indicator | color.border.selected-indicatorobserveduse | #e53935 | 2px indicator bar on the selected tab or nav item. |
nav.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
title.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
lead.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
group.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
group.legend | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
control.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
control.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
control.checked-bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
control.checked-glyph | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
invalid.border | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
invalid.message | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
footer.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
apply.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
apply.text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
apply.busy-glyph | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
reset.text | color.action.secondary.textobserveduse | #ffa2a7 | Secondary button text. |
reset.border | color.action.secondary.borderobserveduse | #a3676b | Secondary button border (meets 3:1). |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Two columns | Narrow (360px) |
|---|---|---|
default | AppearanceBar, gaps and labels of the workstation. Changes apply to new windows.
Hide the bar
Smart gaps
Shown only where no window is. Pixels between windows; the outer gap follows. | AppearanceBar, gaps and labels of the workstation. Changes apply to new windows.
Hide the bar
Smart gaps
Shown only where no window is. Pixels between windows; the outer gap follows. |
focus-visible | ||
invalid | ||
busy |
A settings screen that proves the form components compose without new tokens: a section list on the left, one fieldset of preferences on the right, and a footer of actions. It exists to show the pieces together under every state and at every width, not to prescribe a product.
The region landmark; the sidebar-nav with the open section marked by
aria-current and the 2px color.border.selected-indicator; the form
landmark with its h2 in color.text.bright and lead in color.text.muted;
one fieldset with a color.border.divider edge holding two switches (Hide
the bar, Smart gaps), a wallpaper select, the bar-label radio-group (中文 /
English), the inner-gap text-field with a px suffix and the bar-module
checkbox group; a footer rule and the Apply (primary) and Reset (secondary)
buttons. Every part is the component’s own markup and classes.
| State | Visual | Non-colour channel |
|---|---|---|
| default | rest tokens of every component on color.surface.default; the open section carries the indicator bar | aria-current |
| focus-visible | the dashed color.interaction.focus.ring on the control holding focus (shown on the first switch); the other controls stay at rest | the ring |
| invalid | the inner-gap field takes the 2px color.status.danger.border and shows its ✕ message; the other controls stay at rest | border width 1 → 2px; glyph; aria-invalid |
| busy | Apply shows the static ⋯ glyph in color.action.primary.text; the form carries aria-busy; nothing shrinks or animates | glyph; aria-busy; cursor: progress |
The composed components keep their own hover, checked, disabled and read-only treatments; this specimen declares only the states the screen adds to them.
Landmarks first: a screen reader’s landmark list offers the region, the navigation and the form by name. Tab walks the section links, then every control in reading order, then Apply and Reset. The radio-group and the select move with arrows inside one stop; the numeric field steps with Up and Down; switches and checkboxes toggle with Space. Nothing traps focus and nothing changes on hover alone.
The region is named by the h2 through aria-labelledby; the nav has its own
label so two navigation landmarks on a page stay distinguishable; the open
section is aria-current="page". The form is named by the same h2 and
carries aria-busy="true" while Apply is in progress, so the pending state
is announced without a live region. Every control has a programmatic label;
every message is linked by aria-describedby. Contrast: title 10.10:1 and
lead 5.66:1 on the panel surface, control boundaries 4.33:1, the indicator
bar 4.57:1, the danger border 4.69:1 on the input surface.
Three landmarks, native controls and a container query; nothing here needs a script. A host without container queries collapses the sidebar at the 600px viewport breakpoint. The 56rem reference width, the sidebar width and the two-column grid belong to this specimen and are not rules; the specimen proves composition, not a product. Toolkits port each contained component through its own mapping and place them with their native layout.
A rounded, shadowed settings card. A Material-style app bar above the form. Toggle switches with rounded pills or animated thumbs. Section links drawn as filled tabs instead of the indicator bar. A red form background to show that something is invalid. Applying while the controls are dimmed with opacity. A spinner replacing the Apply label.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Walks the section links, then the controls in reading order, then Apply and Reset; nothing inside is a composite widget, so every control is its own stop. |
| Enter / Space | Follows a section link; toggles a switch or checkbox; opens the select. |
| Arrow keys | Move within the radio-group and the select's options; step the numeric field. |
| Escape | Nothing; the panel is not a dialog and nothing is dismissed. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: sidebar-nav fieldset switch select radio-group text-field checkbox button tabs
Stress fixtures: FX-STATE-MATRIX FX-320 FX-360 FX-ZOOM-200 FX-RM FX-HC FX-I18N FX-RTL FX-DENSITY
admin-formA create-or-edit record screen composed from breadcrumbs, a wizard step list, alerts, six field components and a button row; the review step is a table with an Edit button per row.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.border | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
crumbs.link | color.text.linkapproveduse | #f73f35 | Strong red link text, distinct from rose body text and headings; retain the underline. |
crumbs.current | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
title.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
step.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
step.current-text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
step.current-indicator | color.border.selected-indicatorobserveduse | #e53935 | 2px indicator bar on the selected tab or nav item. |
step.complete | color.status.success.textapproveduse | #86a46f | Success text (7.13:1). |
step.invalid | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
notice.tint | color.status.info.tintapproveduse | #0f141c | Info background tint. |
notice.border | color.status.info.borderapproveduse | #7e9ebb | Info border. |
notice.text | color.status.info.textapproveduse | #7e9ebb | Info text (7.08:1). |
summary.tint | color.status.danger.tintobserveduse | #2b0e0d | Danger background tint. |
summary.border | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
summary.text | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
field.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
field.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
field.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
field.label | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
field.help | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
field.affix | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
invalid.border | color.status.danger.borderobserveduse | #e53935 | Danger border, 2px on invalid inputs. |
invalid.message | color.status.danger.textobserveduse | #f73f35 | Danger text (5.40:1). |
disabled.bg | color.interaction.disabled.bgobserveduse | #160b0b | Disabled control fill. |
disabled.text | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
disabled.border | color.border.disabledobserveduse | #7d1310 | Disabled control border (exempt). |
review.key | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
review.header-rule | color.border.strongobserveduse | #9e231f | Emphasised 2px rule: sticky headers, section ends. |
review.row-rule | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
footer.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
continue.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
continue.text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
continue.busy-glyph | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
back.text | color.action.secondary.textobserveduse | #ffa2a7 | Secondary button text. |
back.border | color.action.secondary.borderobserveduse | #a3676b | Secondary button border (meets 3:1). |
draft.text | color.action.tertiary.textobserveduse | #e99499 | Ghost and icon button text. |
draft.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Identity step | Review step | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
default | New workstation
Draft
Nothing is created until Review; Save draft keeps the record locally.
Two fields need attention
The name and the hostname are already in use; choose different ones. Shown in the bar and the records list. Letters, digits and hyphens; the domain is fixed. Decides the default workspace layout. Today if left unchanged. One .pub file; ed25519 preferred. 31 / 200 Plain text; shown on the record page. | New workstation
Review
Edit returns to the step that owns the value; Save creates the record.
Two fields need attention
The name and the hostname are already in use; edit the Identity step.
| ||||||||||||||||||||||||
invalid | ||||||||||||||||||||||||||
busy | ||||||||||||||||||||||||||
disabled |
A record screen that proves the advanced form components compose with the navigation and feedback components: a trail, a heading, a step list, a notice, a grid of fields and a row of actions, then the same screen at its review step. It exists to show validation, saving and inertness across a whole form, not to prescribe a product.
The region landmark; the breadcrumbs (Records / Workstations / New); the h2
in color.text.bright; the wizard step list with the current step on the
2px color.border.selected-indicator; an info alert on
color.status.info.tint; the form landmark holding the name and hostname
text-fields, the role select, the commissioned-on date-picker, the SSH-key
file-input, the notes textarea with its count and the enable checkbox; the
footer rule and the Back, Save draft and Continue buttons. The review
variant replaces the fields with a table whose row headers are the field
names in color.text.muted and whose last column is a tertiary Edit button.
Every part is the component’s own markup and classes.
| State | Visual | Non-colour channel |
|---|---|---|
| default | rest tokens of every component on color.surface.default; the current step carries the indicator bar, future steps are disabled | aria-current="step" |
| invalid | the name and hostname fields take the 2px color.status.danger.border and show their ✕ messages; the current step’s marker becomes ✕; a danger alert on color.status.danger.tint summarising the two fields replaces the notice; the other fields stay at rest | border width 1 → 2px; glyphs; aria-invalid; role="alert" |
| busy | Continue shows the static ⋯ glyph in color.action.primary.text; the form carries aria-busy; nothing shrinks or animates | glyph; aria-busy; cursor: progress |
| disabled | every control takes its component’s disabled treatment (color.text.disabled, color.border.disabled, color.interaction.disabled.bg); labels and review keys dim; no hover anywhere | disabled on the wrapping fieldset; cursor: not-allowed; never opacity |
The composed components keep their own hover, focus-visible, required, placeholder-shown and read-only treatments; this specimen declares only the states the whole form adds to them.
Landmarks first: the region, the Breadcrumb navigation and the form are offered by name. Tab walks the trail, the reachable steps, the fields in reading order and the three buttons; the file-input’s hidden native input is the stop and its visible button label is drawn with the ring. The select and the date field move with arrows inside one stop. In the review variant the Edit buttons are ordinary stops inside the table. Nothing traps focus.
The region and the form are both named by the h2. Complete steps read ”, complete”
through a visually hidden suffix; future steps are disabled, not hidden.
The notice is role="status" and the invalid summary is role="alert", so
the summary is announced once on submit while each field’s message stays
linked by aria-describedby; aria-invalid is set only after submit. While
saving the form carries aria-busy and, once inert, sits inside a disabled
fieldset so every control reports its state natively. The review table has
a caption, column headers and row headers; each Edit button names its field
through a visually hidden suffix. Contrast: notice title 6.5:1 or better on
its tint, messages 5.26:1 on the panel surface, the invalid boundary 4.69:1
on the input surface, disabled text 3.24:1 (exempt).
Landmarks, native controls, one container query and three root hooks
(data-state-invalid, data-state-busy, data-state-disabled); no script
is needed to render any state. Hosts without container queries stack the
footer at the 600px viewport breakpoint. The 56rem reference width and the
two-column field grid are layout of this specimen and not rules; the
specimen proves composition, not a product. Toolkits port each contained
component through its own mapping.
A rounded card with a drop shadow around the form. A Material stepper with circular numbered badges and a connecting line. A red banner across the whole form instead of the summary alert plus field messages. Fields whose borders glow red. A spinner replacing the Continue label. Saving while the fields fade to half opacity. Placing the actions in a sticky footer bar with a shadow.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Breadcrumb links, the reachable step buttons, then every field in reading order, then Back, Save draft and Continue; the file-input's native input is the stop and its button label is drawn with the ring. |
| Enter | Follows a breadcrumb, activates a step or a button; in a text field submits the form in a host. |
| Space | Toggles the checkbox; opens the file chooser from the file-input. |
| Arrow keys | Move within the select and the date field's segments. |
| Escape | Nothing; the screen is not a dialog. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: breadcrumbs wizard alert text-field select date-picker file-input textarea checkbox button table
Stress fixtures: FX-STATE-MATRIX FX-LONG FX-320 FX-360 FX-ZOOM-200 FX-RM FX-HC FX-I18N FX-RTL FX-DENSITY
filterable-tableA list screen composed from a toolbar with search and filter, removable chips, a selectable sortable data-table with status badges, a count and pagination; with empty and loading variants.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.border | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
toolbar.bg | color.surface.chromeapproveduse | #090707 | Window chrome, title bars, bars. |
toolbar.border | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
toolbar.separator | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
search.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
search.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
search.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
search.placeholder | color.text.placeholderapproveduse | #a3676b | Placeholder text, italic; D-027 darkened it to 4.71:1, just clear of the 4.5:1 floor. |
search.icon | color.icon.defaultobserveduse | #e99499 | Icons take the text colour (currentColor in CSS). |
filter.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
filter.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
new.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
new.text | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
chip.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
chip.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
chip.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
chip.remove | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
header.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
header.rule | color.border.strongobserveduse | #9e231f | Emphasised 2px rule: sticky headers, section ends. |
row.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
row.rule | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
row.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
row.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
row.selected-bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
row.selected-text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
row.ring-on-selection | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
check.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
check.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
check.checked-bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
check.glyph | color.action.primary.textobserveduse | #f4eeee | Primary button text (9.28:1). |
badge.tested-fill | color.status.success.fillapproveduse | #86a46f | Success fill. |
badge.tested-text | color.status.success.on-fillapproveduse | #0c0909 | Text on the success fill (7.13:1). |
badge.demonstrated-fill | color.status.info.fillapproveduse | #7e9ebb | Info fill. |
badge.demonstrated-text | color.status.info.on-fillapproveduse | #0c0909 | Text on the info fill (7.08:1). |
empty.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
empty.heading | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
empty.glyph | color.icon.decorativeobserveduse | #a3676b | Decorative glyphs (3:1 as graphics, never text). |
count.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
pagination.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
pagination.current | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
pagination.disabled | color.text.disabledapproveduse | #8a5559 | Disabled control text. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Eight of 43 | Nothing matches | Loading | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
default |
priority: R1
maturity: stable
|
priority: R1
maturity: stable
search: marquee
No components matchThree filters are active; none of the 43 components passes all of them. |
priority: R1
maturity: stable
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hover | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
focus-visible | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
selected | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
no-results | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A list screen that proves the navigation, display and feedback components compose around one data-table: find, filter, select, page. It exists to show the states of a whole list screen, including nothing matching and still loading, not to prescribe a product.
The region landmark; the toolbar on color.surface.chrome with the
search-field, the Filter menu button, the Columns toggle, a separator and the
primary New button; a row of removable chips (priority: R1, maturity:
stable); the scroller around the data-table, whose Name header carries
aria-sort, whose rows carry aria-selected and a checkbox, and whose last
column is a status badge (✓ Tested on color.status.success.fill, i
Demonstrated on color.status.info.fill); the count in color.text.muted
and the compact pagination. The empty variant adds the empty-state block;
the loading variant marks the table aria-busy. Every part is the
component’s own markup and classes.
| State | Visual | Non-colour channel |
|---|---|---|
| default | rest tokens of every component; Name sorted ascending with its ▲ glyph; Prev disabled | aria-sort; the glyph |
| hover | the pointed row takes color.interaction.hover.bg; the toolbar controls and chips keep their own hover on their own pointer events only | cursor |
| focus-visible | the dashed color.interaction.focus.ring on the row holding focus (shown on the third row); the toolbar and chips stay at rest | the ring |
| selected | two rows take color.interaction.selection.bg with color.interaction.selection.text and a checked box; the select-all reports mixed with the – glyph | aria-selected; ✓ and – glyphs; aria-checked="mixed" |
| no-results | the body is the “No components match” row, the count reads 0 of 43, the search field’s status line announces it; the chips stay so the cause is visible | text; role="status" |
The composed components keep their own pressed, sorted, disabled, loading and open treatments; this specimen declares only the states the whole screen adds to them.
Landmarks first: the region, the toolbar’s search landmark and the pagination navigation are offered by name. The toolbar is one stop with arrow movement between the search field, Filter, Columns and New (the toolbar contract); Escape clears the search. Each chip’s Remove button is its own stop. The table follows the data-table contract: the first row is the stop, arrows move between rows, Space on a row’s checkbox toggles it, Enter on a header sorts. Next is the last stop; a disabled Prev is not focusable.
The search field’s visible label is replaced by a visually hidden one so the
toolbar stays one row; its status line is visually hidden too and announces
“No components match” through role="status". The count is a second status
region and changes once per filter. Rows are named by their Name cell
through aria-labelledby on the row checkbox; the select-all is a
checkbox-role button whose mixed state carries the – glyph. Badges carry
their glyphs so status survives without colour. Contrast: toolbar text
8.60:1 on the chrome surface, boundaries 4.42:1, headers 10.10:1, badges
7.08:1 and 7.13:1 on their fills, the ring 7.48:1 on the selection fill.
Native table semantics, a toolbar, a menu button and a status region; the
roving tabindex and the menu need the host’s script, every state renders
without one from the root hooks data-state-selected and
data-state-no-results and the table’s own aria-busy. Hosts without
container queries wrap the toolbar at the 600px viewport breakpoint. The
56rem width and the 44rem table minimum are layout of this specimen and not
rules; the specimen proves composition, not a product.
A rounded search pill with a magnifier inside a shadowed toolbar. Filter chips drawn as pills with a coloured fill per category. Zebra-striped rows. A selected row shown by a coloured left bar and no fill. A blue Material checkbox. A skeleton shimmer while loading. An infinite scroll with no count and no pages.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Into the toolbar (the search input is its stop), then each chip's Remove button, then the table (the first row is its stop), then Next. |
| Arrow keys | Inside the toolbar move between the search field, Filter, Columns and New; inside the table move between rows; Home and End reach the first and last row. |
| Enter / Space | Open the Filter menu; toggle Columns; activate New or a chip's Remove; on a header sort by it; on a row's checkbox toggle its selection. |
| Escape | Clears a non-empty search; closes the Filter menu and returns focus to its button. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: toolbar search-field menu icon-button button chip data-table badge empty-state pagination
Stress fixtures: FX-STATE-MATRIX FX-320 FX-360 FX-ZOOM-200 FX-RM FX-HC FX-I18N FX-RTL FX-OVERFLOW FX-DENSITY
i3-window-frameThe workstation frame; an i3 bar with workspaces and Chinese i3status labels above the terminal and code-editor components as tiled, tabbed or floating windows behind i3 title bars.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.desktopobserveduse | #000000 | Behind every window. |
root.border | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
bar.bg | color.surface.chromeapproveduse | #090707 | Window chrome, title bars, bars. |
bar.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
bar.rule | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
workspace.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
workspace.selected-bg | color.interaction.selection.bgapproveduse | #531310 | Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection. |
workspace.selected-text | color.interaction.selection.textobserveduse | #f4eeee | Text on the selection. |
workspace.bg-hover | color.interaction.hover.bgobserveduse | #1c0a09 | Hover on rows, ghost buttons, list items. |
workspace.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
workspace.ring-on-selection | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
workspace.urgent-bg | color.status.danger.fillobserveduse | #dc282e | Danger fill. |
workspace.urgent-text | color.status.danger.on-fillobserveduse | #f9faf9 | Text on the danger fill (4.58:1). |
status.separator | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
status.glyph | color.icon.decorativeobserveduse | #a3676b | Decorative glyphs (3:1 as graphics, never text). |
clock.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
area.bg | color.surface.desktopobserveduse | #000000 | Behind every window. |
window.bg | color.surface.canvasapproveduse | #000000 | Page and terminal background. |
window.border | color.border.defaultobserveduse | #531310 | Decorative border (1.39:1; never the only edge of a control). |
window.border-focused | color.border.activeobserveduse | #e53935 | Active or focused control border. |
window.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
title.bg | color.surface.chromeapproveduse | #090707 | Window chrome, title bars, bars. |
title.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
title.bg-focused | color.surface.chrome-altobserveduse | #1c0a09 | Alternate chrome. |
title.text-focused | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
tab.indicator | color.border.selected-indicatorobserveduse | #e53935 | 2px indicator bar on the selected tab or nav item. |
tab.indicator-inactive | color.border.selected-indicator-inactiveobserveduse | #9e231f | Indicator bar when the container is not focused. |
floating.border | color.border.overlayproposeduse-and-reportD-008 | #e53935 | Every floating layer's 1px border. |
floating.shadow | shadow.floatingobserveduse | 0px 6px 22px 0px rgb(0 0 0 / 55%) | The only shadow: the i3 floating window. |
terminal.bg | color.terminal.bgobserveduse | #0c0909 | Terminal background. |
terminal.text | color.terminal.fgobserveduse | #e99499 | Terminal foreground. |
editor.bg | color.code.bgobserveduse | #0c0909 | Editor background. |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Tiled | Tabbed | Floating |
|---|---|---|---|
default | ~/theme — zsh
~/theme main ls -l ports total 5 drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 alacritty drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 gtk -rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 README.md -rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 verify.sh lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 xresources -> ../tokens/ -rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 evidence.tar.gz ~/theme main cat ports/gtk/port.json cat: ports/gtk/port.json: No such file or directory ~/theme main npm run validate title.js — editor
// Compose the window title of the focused container. import { Container } from "./tree.js"; const MAX_LENGTH = 48; export function title(container) { const name = container.name ?? "untitled…"; let label = `${name} (${container.children.length})`; if (label.length > MAX_LENGTH) { label = label.slice(0, MAX_LENGTH) + "…"; } return escape(label); /* escape() is deprecated */ } const broken = 0x1G; | ~/theme main ls -l ports total 5 drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 alacritty drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 gtk -rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 README.md -rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 verify.sh lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 xresources -> ../tokens/ -rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 evidence.tar.gz ~/theme main cat ports/gtk/port.json cat: ports/gtk/port.json: No such file or directory ~/theme main npm run validate // Compose the window title of the focused container. import { Container } from "./tree.js"; const MAX_LENGTH = 48; export function title(container) { const name = container.name ?? "untitled…"; let label = `${name} (${container.children.length})`; if (label.length > MAX_LENGTH) { label = label.slice(0, MAX_LENGTH) + "…"; } return escape(label); /* escape() is deprecated */ } const broken = 0x1G; | ~/theme — zsh
~/theme main ls -l ports total 5 drwxr-xr-x 2 j3w1 j3w1 4096 Sep 6 09:12 alacritty drwx------ 2 j3w1 j3w1 4096 Sep 6 09:12 gtk -rw-r--r-- 1 j3w1 j3w1 1180 Sep 6 09:12 README.md -rwxr-xr-x 1 j3w1 j3w1 512 Sep 6 09:12 verify.sh lrwxrwxrwx 1 j3w1 j3w1 11 Sep 6 09:12 xresources -> ../tokens/ -rw-r--r-- 1 j3w1 j3w1 20480 Sep 6 09:12 evidence.tar.gz ~/theme main cat ports/gtk/port.json cat: ports/gtk/port.json: No such file or directory ~/theme main npm run validate title.js — editor
// Compose the window title of the focused container. import { Container } from "./tree.js"; const MAX_LENGTH = 48; export function title(container) { const name = container.name ?? "untitled…"; let label = `${name} (${container.children.length})`; if (label.length > MAX_LENGTH) { label = label.slice(0, MAX_LENGTH) + "…"; } return escape(label); /* escape() is deprecated */ } const broken = 0x1G; |
focus-visible | |||
selected | |||
container-inactive | |||
error |
The frame the palette was tuned in: an i3 bar, tiled windows with 1px pixel borders, 14/−2 gaps, a tabbed container and one floating window. It proves that the terminal and code-editor components sit inside window chrome drawn from the same roles, and it records the workstation’s geometry as reference metrics. It is a specimen of composition, not a desktop.
The region on color.surface.desktop; the 28px bar on
color.surface.chrome with the workspace tablist (the visible workspace on
color.interaction.selection.bg) and the i3status group in Chinese with
its clock in color.text.bright; the workspace grid with the reference
gaps; each window a focusable group behind a 1px color.border.default
frame, color.border.active when focused, with a 23px title bar in
color.text.muted on color.surface.chrome that turns color.text.bright
on color.surface.chrome-alt when focused; the terminal or code-editor as
the client with its own boundary given up to the frame. Tabbed: the titles
form a tablist whose visible tab carries the 2px
color.border.selected-indicator. Floating: the editor sits above the
terminal behind color.border.overlay with shadow.floating, the only
shadow in the theme.
| State | Visual | Non-colour channel |
|---|---|---|
| default | the terminal window focused: color.border.active frame and the bright title; the editor at rest; workspace 1 filled | the title text; aria-selected |
| focus-visible | the 2px color.interaction.focus.ring-container ring at −3px inside the focused window or container; the client draws no second ring | the ring |
| selected | workspace 2 takes the selection fill with color.interaction.selection.text; workspace 1 drops to rest | aria-selected |
| container-inactive | no window has focus: every frame color.border.default, every title color.text.muted on color.surface.chrome, the tab indicator color.border.selected-indicator-inactive, no caret | lightness drop; no caret |
| error | workspace 3 is urgent: color.status.danger.fill with color.status.danger.on-fill and a trailing ! | the ! mark; “urgent” read to assistive technology |
The clients keep their own states (the terminal’s busy cursor, the editor’s selection and read-only gutter); this specimen declares only the states the frame adds around them.
The workspace tablist is one stop with arrow movement; Enter or Space shows a workspace. Each window is a focusable group so the frame itself can hold focus, as i3 focuses a container; Tab then enters the client, whose own contract applies (the terminal log, the read-only editor textbox). In the tabbed variant the tab list is one stop and only the visible panel’s client follows. Mod+h j k l and Mod+1..9 belong to the window manager and are documented, not reimplemented. Nothing traps focus.
The region names its workspace and layout. The bar is a group holding a
named tablist and a status group in lang="zh", whose Nerd Font glyphs
are aria-hidden so only the labels are read. Every window is a named
group; the focused one says so in its name and the floating one too.
Urgency carries the ! mark and a visually hidden “urgent”. Contrast:
workspace names 8.60:1 and the clock 10.31:1 on the bar, focused titles
10.01:1, rest titles 5.77:1, the focused frame 4.97:1 and the ring 10.37:1
on the window surface, the urgent fill 4.12:1 against the bar with 4.58:1
text on it.
Groups, a tablist and the clients’ own roles; every state renders from the root hooks without a script. The i3 geometry — bar 28px, titles 23px, gaps 14px inner and −2px outer — is reproduced with private variables on this specimen’s root only. Those numbers are reference metrics of the workstation, recorded so a port of the i3 configuration matches it; they are not tokens and not a rule for any other application, which keeps its own window manager, bar and gaps. The specimen proves composition, not a product; only i3 and sway ports draw this frame.
A window with rounded corners or a title bar gradient. A focused window that glows or blurs its neighbours. A shadow under a tiled window; the only shadow is the floating one. Gaps of 14/−2 forced into an unrelated application’s layout. A workspace indicator drawn as a red underline instead of the selection fill. Traffic-light close buttons in the title bar. A translucent bar.
| Key | Action |
|---|---|
| Tab / Shift+Tab | The workspace tablist (its selected tab is the stop), then each window group, then the client inside it; in the tabbed variant the tab list, then the visible panel's client. |
| Arrow keys | Move between workspaces in the bar and between tabs in a tabbed container; inside a client the client's own contract applies. |
| Enter / Space | Shows the workspace or tab under focus. |
| Mod+h j k l, Mod+1..9 | The window manager's own bindings move focus and switch workspaces on the workstation; the specimen documents them and does not reimplement them. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: terminal code-editor tabs
Stress fixtures: FX-STATE-MATRIX FX-320 FX-360 FX-ZOOM-200 FX-RM FX-HC FX-I18N FX-RTL FX-DENSITY
file-browserLocation navigation and a filterable native file table with bounded host actions.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-border-width-emphasis | border.width.emphasisobserveduse | 2px | Selected indicator, sticky header rule, invalid border. |
dependency.role-color-border-divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
dependency.role-color-border-selected-indicator | color.border.selected-indicatorobserveduse | #e53935 | 2px indicator bar on the selected tab or nav item. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-16 | space.16observeduse | 16px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
default |
2 files | |||||||||
selected | ||||||||||
empty | ||||||||||
no-results | ||||||||||
focus-visible |
Compose hierarchical location navigation with a filterable file table. Opening an entry emits its stable identity. The caller owns file access, upload, deletion and permissions. The supplied names and sizes are synthetic local examples.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Locations and native file rows. | Explicit names, kinds and sizes. |
| selected | Selected location follows the tree contract. | aria-selected plus location filter. |
| empty | No file rows. | Explicit zero-files text. |
| no-results | Filtered rows are hidden. | Matching count and retained search query. |
| focus-visible | Dashed control outline. | Native focus and tree roving focus. |
Keep the complete nested tree behavior and semantic table headers. Filtering does not move focus. Announce counts, and keep the search field available after zero results. File actions must not imply access beyond the host’s actual permissions.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Move among tree navigation, search and row actions. |
| Arrow keys | Use the tree contract inside navigation. |
| Enter / Space | Request opening the chosen file through an event. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: tree table search-field
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
compact-dashboardNamed operational metrics, a complete chart and an actionable native records table.
Report an issue with this component
| Part | Role | Value | Use |
|---|---|---|---|
root.bg | color.surface.defaultapproveduse | #100c0c | Panels and cards. |
root.text | color.text.defaultobserveduse | #e99499 | Interface and body text. |
root.border | color.border.controlproposeduse-and-reportD-007 | #a3676b | Form-control boundary (4.45:1, meets the 3:1 non-text minimum). |
heading.text | color.text.brightobserveduse | #ffa2a7 | Headings and emphasis. Links use color.text.link. |
secondary.text | color.text.mutedobserveduse | #bd787d | Secondary text; the darkest colour permitted for chrome text. |
control.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
control.focus | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
dependency.role-border-width-default | border.width.defaultobserveduse | 1px | Every border is 1px. |
dependency.role-color-border-divider | color.border.dividerproposeduse-and-reportD-008 | #2b0e0d | Hairline between rows and sections. |
dependency.role-color-surface-chrome | color.surface.chromeapproveduse | #090707 | Window chrome, title bars, bars. |
dependency.role-density-comfortable-control-height | density.comfortable.control-heightobserveduse | 32px | |
dependency.role-density-compact-control-height | density.compact.control-heightobserveduse | 24px | |
dependency.role-font-size-h2 | font.size.h2observeduse | 16px | |
dependency.role-radius-none | radius.noneobserveduse | 0px | The only radius. Nothing is rounded. |
dependency.role-space-12 | space.12observeduse | 12px | |
dependency.role-space-2 | space.2observeduse | 2px | |
dependency.role-space-8 | space.8observeduse | 8px |
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Default | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
default | Delivery overviewSynthetic local fixture
No projects available. | ||||||||||||
loading | |||||||||||||
empty | |||||||||||||
focus-visible |
Compose a small operational overview from named metrics, a complete chart and a records table. All figures are caller data. Synthetic fixtures are labelled; the component does not fetch data or infer business health.
| State | Presentation | Non-colour channel |
|---|---|---|
| default | Square metric panels and readable tables. | Metric names, exact values and status text. |
| loading | Busy boundary with existing content retained. | aria-busy and host-provided loading status. |
| empty | Explicit absence of records. | Empty message; metric zero differs from unknown. |
| focus-visible | Dashed action outline. | Native keyboard focus. |
Keep metrics as term/value pairs, tables labelled and all chart data visible. Do not use a color-only health score. Busy status does not manufacture a completed refresh. The host decides how unknown metrics are described, rather than silently displaying zero.
Wrap labels and actions without changing source order. Use logical spacing in RTL. Preserve native target sizes at both densities; only bounded data regions may scroll horizontally. Use equivalent native semantics and approved roles; record any unsupported behavior as a mapping deviation. The host owns application data, persistence, permissions and services.
Unlabelled controls, color-only status, drag-only operations, hidden required instructions, stolen focus or an invented service success.
| Key | Action |
|---|---|
| Tab / Shift+Tab | Visit record actions and any scrollable data region. |
| Enter / Space | Request the chosen record through an event. |
Component JSON ·Implementation brief ·Build a task kit ·
Related: chart description-list table
Stress fixtures: FX-320 FX-360 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-TOUCH FX-DENSITY FX-STATE-MATRIX
Every fixture id below is referenced from component specifications; a component that lists a fixture must survive it.
FX-LONG Long textA 120-character label, a 40-character unbroken token, three lines of help. Labels wrap; the box never grows past its container; nothing is clipped.
The value Benachrichtigungseinstellungen_v2_final_final must not widen the box. Help text runs to three lines on a narrow frame and stays readable; it wraps at the container edge rather than clipping.
FX-320 320 and 360 pixel framesThe reflow floor (WCAG 1.4.10). No horizontal scrolling inside the frame; the label stacks above; help and message wrap.
Shown on your profile.
Shown on your profile.
FX-I18N English, Spanish, Chinese, ArabicNot translations of the site: fixtures for width, CJK fallback faces and mirrored affixes. The Arabic block sets dir="rtl".
Choose how the workstation notifies you.
Elige cómo te avisa la estación de trabajo.
选择工作站如何通知您。
اختر كيف تنبّهك محطة العمل.
FX-RTL Right-to-leftLogical properties mirror prefixes, suffixes, indicators and stepper order without a second stylesheet.
المضيف فقط؛ المسار ثابت.
FX-RM Reduced motionUnder prefers-reduced-motion every duration is 0.01ms; toasts and dialogs appear in place, loading glyphs are static. The page's own browser test emulates it.
FX-HC Forced coloursUnder forced-colors: active the rings use Highlight and borders survive because they are real borders. Emulate it in the browser's rendering settings.
FX-TOUCH Coarse pointerNo hover styles; targets at least 24×24 CSS pixels with 4px between them in compact density.
FX-DENSITY Compact and comfortableThe same control at both densities, side by side. Both meet the 24×24 target minimum.
24px control.
32px control.
FX-OVERFLOW OverflowFifty rows and twelve columns scroll inside their own container; the page never scrolls horizontally.
| # | col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 | col9 | col10 | col11 | col12 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 |
| 3 | 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 |
| 4 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 |
| 5 | 5 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 |
| 6 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 54 | 60 | 66 | 72 |
| 7 | 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 | 77 | 84 |
| 8 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 88 | 96 |
| 9 | 9 | 18 | 27 | 36 | 45 | 54 | 63 | 72 | 81 | 90 | 99 | 108 |
| 10 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 | 110 | 120 |
| 11 | 11 | 22 | 33 | 44 | 55 | 66 | 77 | 88 | 99 | 110 | 121 | 132 |
| 12 | 12 | 24 | 36 | 48 | 60 | 72 | 84 | 96 | 108 | 120 | 132 | 144 |
| 13 | 13 | 26 | 39 | 52 | 65 | 78 | 91 | 104 | 117 | 130 | 143 | 156 |
| 14 | 14 | 28 | 42 | 56 | 70 | 84 | 98 | 112 | 126 | 140 | 154 | 168 |
| 15 | 15 | 30 | 45 | 60 | 75 | 90 | 105 | 120 | 135 | 150 | 165 | 180 |
| 16 | 16 | 32 | 48 | 64 | 80 | 96 | 112 | 128 | 144 | 160 | 176 | 192 |
| 17 | 17 | 34 | 51 | 68 | 85 | 102 | 119 | 136 | 153 | 170 | 187 | 204 |
| 18 | 18 | 36 | 54 | 72 | 90 | 108 | 126 | 144 | 162 | 180 | 198 | 216 |
| 19 | 19 | 38 | 57 | 76 | 95 | 114 | 133 | 152 | 171 | 190 | 209 | 228 |
| 20 | 20 | 40 | 60 | 80 | 100 | 120 | 140 | 160 | 180 | 200 | 220 | 240 |
| 21 | 21 | 42 | 63 | 84 | 105 | 126 | 147 | 168 | 189 | 210 | 231 | 252 |
| 22 | 22 | 44 | 66 | 88 | 110 | 132 | 154 | 176 | 198 | 220 | 242 | 264 |
| 23 | 23 | 46 | 69 | 92 | 115 | 138 | 161 | 184 | 207 | 230 | 253 | 276 |
| 24 | 24 | 48 | 72 | 96 | 120 | 144 | 168 | 192 | 216 | 240 | 264 | 288 |
| 25 | 25 | 50 | 75 | 100 | 125 | 150 | 175 | 200 | 225 | 250 | 275 | 300 |
| 26 | 26 | 52 | 78 | 104 | 130 | 156 | 182 | 208 | 234 | 260 | 286 | 312 |
| 27 | 27 | 54 | 81 | 108 | 135 | 162 | 189 | 216 | 243 | 270 | 297 | 324 |
| 28 | 28 | 56 | 84 | 112 | 140 | 168 | 196 | 224 | 252 | 280 | 308 | 336 |
| 29 | 29 | 58 | 87 | 116 | 145 | 174 | 203 | 232 | 261 | 290 | 319 | 348 |
| 30 | 30 | 60 | 90 | 120 | 150 | 180 | 210 | 240 | 270 | 300 | 330 | 360 |
| 31 | 31 | 62 | 93 | 124 | 155 | 186 | 217 | 248 | 279 | 310 | 341 | 372 |
| 32 | 32 | 64 | 96 | 128 | 160 | 192 | 224 | 256 | 288 | 320 | 352 | 384 |
| 33 | 33 | 66 | 99 | 132 | 165 | 198 | 231 | 264 | 297 | 330 | 363 | 396 |
| 34 | 34 | 68 | 102 | 136 | 170 | 204 | 238 | 272 | 306 | 340 | 374 | 408 |
| 35 | 35 | 70 | 105 | 140 | 175 | 210 | 245 | 280 | 315 | 350 | 385 | 420 |
| 36 | 36 | 72 | 108 | 144 | 180 | 216 | 252 | 288 | 324 | 360 | 396 | 432 |
| 37 | 37 | 74 | 111 | 148 | 185 | 222 | 259 | 296 | 333 | 370 | 407 | 444 |
| 38 | 38 | 76 | 114 | 152 | 190 | 228 | 266 | 304 | 342 | 380 | 418 | 456 |
| 39 | 39 | 78 | 117 | 156 | 195 | 234 | 273 | 312 | 351 | 390 | 429 | 468 |
| 40 | 40 | 80 | 120 | 160 | 200 | 240 | 280 | 320 | 360 | 400 | 440 | 480 |
| 41 | 41 | 82 | 123 | 164 | 205 | 246 | 287 | 328 | 369 | 410 | 451 | 492 |
| 42 | 42 | 84 | 126 | 168 | 210 | 252 | 294 | 336 | 378 | 420 | 462 | 504 |
| 43 | 43 | 86 | 129 | 172 | 215 | 258 | 301 | 344 | 387 | 430 | 473 | 516 |
| 44 | 44 | 88 | 132 | 176 | 220 | 264 | 308 | 352 | 396 | 440 | 484 | 528 |
| 45 | 45 | 90 | 135 | 180 | 225 | 270 | 315 | 360 | 405 | 450 | 495 | 540 |
| 46 | 46 | 92 | 138 | 184 | 230 | 276 | 322 | 368 | 414 | 460 | 506 | 552 |
| 47 | 47 | 94 | 141 | 188 | 235 | 282 | 329 | 376 | 423 | 470 | 517 | 564 |
| 48 | 48 | 96 | 144 | 192 | 240 | 288 | 336 | 384 | 432 | 480 | 528 | 576 |
| 49 | 49 | 98 | 147 | 196 | 245 | 294 | 343 | 392 | 441 | 490 | 539 | 588 |
| 50 | 50 | 100 | 150 | 200 | 250 | 300 | 350 | 400 | 450 | 500 | 550 | 600 |
Sixteen misreadings of the theme, drawn on purpose. The pictures are decorative; the rule under each one is normative.
Radius is 0 everywhere; buttons, chips, inputs and badges are square.
Surfaces are opaque warm blacks; no backdrop-filter, no translucent cards over content.
Fills are flat tokens; no red-to-black buttons, gradient headers or gradient borders.
Focus is a 1px dashed ring at −2px; never a box-shadow halo, and elevation is a border, not a drop shadow.
Red marks interaction only: focus, selection, active borders, primary actions. Panels, body text and sidebars stay warm black and rose.
No floating action buttons, ripples, floating labels or tonal elevation stacks. Modern means precise, not Material.
There is no light profile. White panels with dark text are a different theme.
Syntax stays monochrome rose/red in the default profile; only the proposed extended overlay adds three hues, and never purple, cyan or magenta.
Every status carries a glyph (✕ ! ✓ i ·) and a pattern; a coloured dot alone tells a colour-blind reader nothing.
One monospace family for chrome, reading and code; Inter or system-ui is another identity.
The site's --inactive measures 1.86:1 on the canvas. It is a disabled border and rendered whitespace, never line numbers, comments or any text.
Focus is a ring and selection is a fill. A focused row painted #531310, or a #e53935 ring on a #531310 row (2.15:1), hides where the keyboard is.
Slot 4 (#8c1212) is a terminal colour at 2.09:1. Directory names in a file browser use the text roles, not the historical slots.
A zero-radius system has square avatars and no circular spinner; loading is a static ⋯ glyph.
#531310 is decorative (1.39:1). A form control needs color.border.control (#a3676b, 4.45:1) as its boundary.
opacity: .5 leaks the background and breaks contrast accounting; disabled uses text.disabled, border.disabled and the disabled fill.
A local reference workflow preserves values through validation, review and an explicitly simulated save.
The validation and recovery pattern composes admin-form, text-field, textarea, select, checkbox, alert, button and table. It is a reference composition under proposed D-020, not a new approved profile or a production persistence system. Existing component rules remain normative. Its compact JSON contract records the constituent component content digests, fields, fixtures, transitions and limits.
Enter values and submit to validate. Errors appear together in a focused summary with links to the affected fields. Preserve every entered value, including unrelated notes and choices. Correct fields and submit again to review. Editing does not announce errors on each keystroke or silently remove the last submitted error summary. Review offers an explicit return to editing.
Confirming the review enters busy, with the form controls disabled and an explicit pending message. The separate Complete simulation action produces success. A submit click alone is never success. Busy, disabled, invalid and success have distinct meaning. Reset clears in-memory values and errors.
The example reserves ws-07 for Name and Hostname and requires a lowercase hostname made of letters, digits and hyphen-separated segments. These are named local fixtures from admin-form, not server uniqueness checks. No request, account, storage or database is involved. The page retains static normal, invalid, busy and success examples without JavaScript.
Automated protocols cover keyboard submission, summary navigation, correction, preservation, review, busy and success, along with narrow layout and no-JS content. Test implementation is not an execution pass. Read the published verification report for actual runs; no manual screen-reader pass is claimed. The submit-time feedback follows the GOV.UK validation pattern.
A one-way, pinned Variables API bridge with explicit diffs and external ownership receipts.
This independently authored bridge maps sRGB colors to COLOR variables and scalar px spacing/radius to FLOAT variables. Aliases remain native variable aliases. Token paths are stable identities; slash-separated names live in the j3w1/theme/default/v1 collection. Alias primitives are dependencies with empty property scopes, not newly approved application roles. Semantic scopes are specific to text, fills, strokes, gaps and corner radius. Eligibility and pending decision IDs travel with each definition.
Only the approved default profile is imported. Heritage and proposed profiles are reported, never converted into extra modes. Typography, shadows, composite borders and other unsupported types are reported without flattening. Figma does not natively import this JSON: the adapter uses the Variables Plugin API. Install the font families named in the pinned typography tokens independently; this bridge neither imports fonts nor distributes binaries.
In a verified checkout, run:
node scripts/figma-bridge.mjs --ref FULL_COMMIT_OR_RELEASE_TAG > payload.json
The default sample selects color.surface.default, color.text.default, space.4
and radius.none, plus their two alias dependencies. Use --roles with an
explicit comma-separated role selection to change the sample. The command
reads Git objects at the resolved full revision and records source and payload
digests, mapping version, unsupported items and excluded profiles. It never
reads moving branch bytes or substitutes the working tree.
Use the Figma connector’s use_figma Variables API route in a new design draft
or an existing collection for which you retain the current receipt. Read the
connector’s current skill/API instructions first. The actual adapter is
exports/figma/importer.mjs; pin its download to
the same reviewed delivery revision as these instructions.
Load the module’s function in the connector script (remove only the ES-module
export keyword), then return the awaited call:
return await importFigmaVariables(figma, {
payload, receipt: null, documentKey, action: "dry-run"
});
Supply the locally generated payload and exact target file key as explicit
inputs. Inspect the returned diff. Dry-run performs reads only. Invoke again
with action: "apply" only for the reviewed change. Preserve the entire
returned receipt outside the document and repository. Subsequent dry-runs and
applies must supply that receipt and the same document key. An exact re-import
is a no-op. Updates show before/after definitions; omitted owned variables are
retained and reported, never silently removed. These receipts are trusted
local ownership records, not importable third-party configuration.
This reviewed route is bounded to eight variables including dependencies per namespace, one collection and one default mode, fitting an incremental connector operation. Larger payloads fail before mutation. The connector’s atomic script execution is required for apply/rollback; this module is not a standalone plugin transaction manager. No team publishing permission or paid plan capability is assumed. Permission, mode and API errors remain failures.
Use action: "rollback" with the current receipt to undo its most recent
apply. Existing variables regain their exact recorded values and metadata;
new variables are removed in reverse dependency order. The result returns the
previous receipt. A newly created collection is removed only when it contains
no unrelated variables. Preserve the returned previous receipt for another
rollback or subsequent import.
Rollback also refuses to remove a variable referenced by a retained variable in any local collection. Inspect any canvas bindings you added after import before requesting rollback; this adapter does not audit every canvas node.
Before any mutation the adapter compares all owned IDs, values, descriptions, scopes, collection identity and mode against the receipt. Missing, detached or manually edited variables stop apply and rollback. It never adopts a matching name without a receipt. Resolve the conflict deliberately in Figma or create a separate draft; do not discard ownership records to force an overwrite.
A real six-variable sample was imported through the connector in a new design draft with Starter/Full access and Plugin API 1.0.0. Actual alias IDs matched; spacing and radius matched exactly. Color channels matched the exact float32 representation of the source channels, with no tolerance. Exact re-import changed no IDs. A separately previewed spacing addition was applied and rolled back. A deliberate manual edit was rejected without overwriting the edit, then the test edit was explicitly restored. Private receipts remain outside public artifacts. The issue evidence records the exact candidate and source revision.
Source protocols also cover detached IDs, value updates, initial rollback, unrelated variables, invalid payloads and extra modes using an explicit API fixture. Those are fixture results, not additional account capability claims. No full component kit, native port, library publication, manual design review, or automatic Figma-to-token synchronization is claimed. D-019 remains proposed.
Contrast targets, focus and keyboard contracts, reduced motion, forced colours, and what an automated pass does and does not prove.
WCAG 2.2 criteria are used as measurable design targets for the roles this theme defines. A green contrast report and a clean automated scan of the site are evidence that the specified colours and the reference markup meet those targets. They are not a conformance claim for any application that adopts the theme: keyboard operation, focus management, labels, reflow and manual review remain the consumer’s responsibility, and the theme’s own limitations are listed at the end of this document.
text.disabled #8a5559, 3.52:1 on the true-black canvas).4.4996 reads 4.50 and fails.#a3676b as text, #7d1310 anywhere as text)
are flagged $deprecated in the heritage-ansi profile and are never
presented as ordinary passes.exports/contrast.md lists every declared pair: the global pairs in
spec/contrast.json and every stateTokens and contrast[] entry of every
component. Waived pairs carry their reason; a waiver is only valid for
decorative graphics that are never the sole boundary of a control.
Focus is never lost and never invisible. Controls and rows show
focus.ring (1px dashed #e53935, offset −2px); focusable containers show
focus.ring-container (2px solid #ffa2a7, offset −3px). On fills, use the
component’s explicit ring role (D-015): primary and checked controls use
focus.ring-container where declared; destructive fills use their on-fill
text role. Colour mapping does not change ring geometry.
:focus without :focus-visible draws nothing,
which keeps pointer clicks quiet without hiding keyboard focus. A component
that removes a host’s default indicator must draw this one. State matrices on
the site render every declared combination including selected+focus-visible
and invalid+focus-visible, because those are where rings disappear in
practice.
Native controls keep native keyboard behaviour. Custom widgets follow the WAI
ARIA Authoring Practices pattern cited in their frontmatter (aria.apg):
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. The site’s own controls (search, filters,
inspector, copy) are reachable and operable with the keyboard, and the no-JS
build keeps every normative section readable.
Durations are at most 150ms and apply to opacity, transform and background
only. Under prefers-reduced-motion: reduce every duration is 0.01ms: toasts,
dialogs and drawers appear in place, indeterminate progress shows static
stripes, loading glyphs do not spin, skeletons do not shimmer, tab indicators
do not slide. No behaviour is sequenced on transitionend.
The specification page’s generated literal-color previews have the narrow geometry, checkerboard and motion exception recorded in D-014; the component and consumer rules remain as written.
Under forced-colors: active rings use Highlight, selections use
Highlight / HighlightText, borders survive (they are real borders, not
box-shadows), and pattern fills keep charts and diffs legible.
Components reflow at 320 CSS pixels without horizontal page scrolling, at 200%
browser zoom and at 200% text-only zoom. Rings and borders stay 1px / 2px CSS.
Targets are at least 24×24 CSS pixels with 4px spacing in compact density.
Long labels wrap or truncate with a visible affordance; the site’s stress
fixtures (English, Spanish, Chinese, Arabic with dir="rtl") exercise this.
Every status has a glyph; every state has a border, pattern or ARIA change;
diffs keep + − ~ markers; charts carry pattern fills and a table fallback.
The heritage-ansi terminal palette has reduced hue differentiation by
design, and the specification documents it as a limitation rather than fixing
it silently.
text.disabled at 3.52:1 on canvas is below the ordinary-text floor by design and
relies on the WCAG exemption for inactive controls.extended overlay
proposes a corrected set.code.selection-bg. Selection is transient; the
limitation is recorded in the code-editor specification.The coverage ledger describes source facts: specified, demonstrated and test
implemented. exports/coverage.json schemaVersion 1 adds testImplemented and
retains tested as a compatibility alias with its original file-existence
meaning. Component JSON coverage and family totals use the same definitions.
No source coverage Boolean proves execution or blanket WCAG conformance.
The linked static verification report supplies actual per-test outcomes and per-component summaries. Evidence schemaVersion 1 records automated or manual method, category, component, explicitly covered states/variants, browser/version, OS, viewport, profile, density, JavaScript setting and a run or protocol reference. Empty state/variant lists mean unspecified, never all. The environment’s profile and density describe the initial configuration; tests that switch them say so in their scope. Browser records from one engine never imply another engine. Skipped, failed, not run and not applicable are separate outcomes; every non-pass has a reason. Manual screen-reader and keyboard claims require protocol records. The existing automated suite targets Chromium; other engines remain not run.
Freshness compares both the tested artifact digest and a conservative source fingerprint covering tracked contract sources, schemas, generators, site code, tests, agent guidance, CI configuration and package lockfile. All such changes invalidate the current claim; unrelated source changes may therefore invalidate more evidence than strictly necessary. A stale record retains its actual outcome and supporting reference but does not establish a current pass.
A build first publishes a static not-run report and a subject manifest. The
browser reporter checks that source and specimen assets still match that subject
before publishing execution evidence. Report generation writes only under
dist/verification/ and verifies all specimen bytes remain unchanged. Report
presentation gets a separate static/browser gate and never certifies itself.
Run timestamps and revisions live only in this post-test evidence, outside the
committed deterministic exports and their digests, avoiding circular hashes.
Local workflow: build, run the browser suite, npm run verification:report,
npm run verification:check, then npm run test:verification. A local run names
its local execution and digests; it is not represented as hosted CI evidence.
For a manual protocol, provide a schema-valid evidence document to
node scripts/verification-report.mjs <repo-relative-evidence.json> with its
actual tested digests, browser/environment and a protocol: reference containing
the protocol identifier and recorded observations. Retain the protocol with the
evidence; do not copy private artifacts into this public repository.
954 declared pairs measured on resolved sRGB values with the WCAG 2.x formula; 17 waived (decorative only), 0 failing. A pass is a design target for the named role, not an application-wide conformance claim.
| Pair | State | Kind | Foreground | Background | Ratio | Min | Result |
|---|---|---|---|---|---|---|---|
| default text on canvas | — | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| default text on default surface | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| default text on raised | — | text | color.text.default #e99499 | color.surface.raised #160b0b | 8.43 | 4.5 | pass |
| bright text on canvas | — | text | color.text.bright #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| prose on canvas | — | text | color.text.prose #f4eeee | color.surface.canvas #000000 | 18.31 | 4.5 | pass |
| prose on sunken | — | text | color.text.prose #f4eeee | color.surface.sunken #000000 | 18.31 | 4.5 | pass |
| muted text on canvas | — | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| muted text on raised | — | text | color.text.muted #bd787d | color.surface.raised #160b0b | 5.66 | 4.5 | pass |
| subtle text on canvas | — | text | color.text.subtle #ad7175 | color.surface.canvas #000000 | 5.40 | 4.5 | pass |
| subtle text on raised | — | text | color.text.subtle #ad7175 | color.surface.raised #160b0b | 4.96 | 4.5 | pass |
| disabled text on canvas (exempt; house floor 3:1) | — | ui | color.text.disabled #8a5559 | color.surface.canvas #000000 | 3.52 | 3 | pass |
| placeholder on input | — | text | color.text.placeholder #a3676b | color.surface.input #000000 | 4.71 | 4.5 | pass |
| accent text on canvas | — | text | color.text.accent #e53935 | color.surface.canvas #000000 | 4.97 | 4.5 | pass |
| accent text on default surface | — | text | color.text.accent #e53935 | color.surface.default #100c0c | 4.60 | 4.5 | pass |
| strong accent on raised | — | text | color.text.accent-strong #f73f35 | color.surface.raised #160b0b | 5.26 | 4.5 | pass |
| link on canvas | — | text | color.text.link #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| link hover text on strong hover fill | — | text | color.text.link-hover #f4eeee | color.interaction.hover.bg-strong #630f0d | 11.41 | 4.5 | pass |
| selection text on selection | — | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| inactive selection text on inactive selection | — | text | color.interaction.selection.inactive-text #e99499 | color.interaction.selection.inactive-bg #420f0c | 7.02 | 4.5 | pass |
| primary action text on fill | — | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| primary action text on hover fill | — | text | color.action.primary.text #f4eeee | color.action.primary.hover-bg #911410 | 7.92 | 4.5 | pass |
| secondary action text on canvas | — | text | color.action.secondary.text #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| destructive text on canvas | — | text | color.action.destructive.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| destructive hover text on hover fill | — | text | color.action.destructive.hover-text #f9faf9 | color.action.destructive.hover-bg #dc282e | 4.58 | 4.5 | pass |
| danger text on canvas | — | text | color.status.danger.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| danger text on danger tint | — | text | color.status.danger.text #f73f35 | color.status.danger.tint #2b0e0d | 4.88 | 4.5 | pass |
| danger on-fill text | — | text | color.status.danger.on-fill #f9faf9 | color.status.danger.fill #dc282e | 4.58 | 4.5 | pass |
| warning text on canvas | — | text | color.status.warning.text #c9973f | color.surface.canvas #000000 | 7.98 | 4.5 | pass |
| warning text on warning tint | — | text | color.status.warning.text #c9973f | color.status.warning.tint #1f1a0c | 6.59 | 4.5 | pass |
| warning on-fill text | — | text | color.status.warning.on-fill #0c0909 | color.status.warning.fill #c9973f | 7.54 | 4.5 | pass |
| success text on canvas | — | text | color.status.success.text #86a46f | color.surface.canvas #000000 | 7.55 | 4.5 | pass |
| success text on success tint | — | text | color.status.success.text #86a46f | color.status.success.tint #0f1a0e | 6.43 | 4.5 | pass |
| success on-fill text | — | text | color.status.success.on-fill #0c0909 | color.status.success.fill #86a46f | 7.13 | 4.5 | pass |
| info text on canvas | — | text | color.status.info.text #7e9ebb | color.surface.canvas #000000 | 7.50 | 4.5 | pass |
| info text on info tint | — | text | color.status.info.text #7e9ebb | color.status.info.tint #0f141c | 6.59 | 4.5 | pass |
| info on-fill text | — | text | color.status.info.on-fill #0c0909 | color.status.info.fill #7e9ebb | 7.08 | 4.5 | pass |
| neutral on-fill text | — | text | color.status.neutral.on-fill #000000 | color.status.neutral.fill #a3676b | 4.71 | 4.5 | pass |
| focus ring on canvas | — | ui | color.interaction.focus.ring #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| focus ring on default surface | — | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| container focus ring on selection | — | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| control border on input | — | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| control border on default surface | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| active border on input | — | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| decorative border on canvas (decorative; not a control edge) | — | ui | color.border.default #531310 | color.surface.canvas #000000 | 1.47 | 3 | waived: decorative hairline; never the only boundary of a control (spec/foundations.md) |
| strong rule on canvas (decorative) | — | ui | color.border.strong #9e231f | color.surface.canvas #000000 | 2.71 | 3 | waived: 2px decorative rule paired with a surface change; never the only boundary of a control |
| line numbers on code background | — | text | color.code.line-number #ad7175 | color.code.bg #0c0909 | 5.10 | 4.5 | pass |
| caret on current line | — | ui | color.code.caret #e99499 | color.code.current-line #1c0a09 | 8.35 | 3 | pass |
| comment on code background | — | text | color.code.syntax.comment #ad7175 | color.code.bg #0c0909 | 5.10 | 4.5 | pass |
| string on code background | — | text | color.code.syntax.string #bd787d | color.code.bg #0c0909 | 5.81 | 4.5 | pass |
| keyword on code background | — | text | color.code.syntax.keyword #f73f35 | color.code.bg #0c0909 | 5.40 | 4.5 | pass |
| type on code background | — | text | color.code.syntax.type #b37175 | color.code.bg #0c0909 | 5.23 | 4.5 | pass |
| muted text on editor selection | — | text | color.text.muted #bd787d | color.code.selection-bg #420f0c | 4.71 | 4.5 | pass |
| current search match text | — | text | color.code.search-current-text #0c0909 | color.code.search-current-bg #ffa2a7 | 10.37 | 4.5 | pass |
| error text on canvas | — | text | color.diagnostic.error.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| warning diagnostic text on canvas | — | text | color.diagnostic.warning.text #c9973f | color.surface.canvas #000000 | 7.98 | 4.5 | pass |
| info diagnostic text on canvas | — | text | color.diagnostic.info.text #7e9ebb | color.surface.canvas #000000 | 7.50 | 4.5 | pass |
| default text on added-line background | — | text | color.text.default #e99499 | color.diff.added.bg #0f1a0e | 7.80 | 4.5 | pass |
| default text on removed-line background | — | text | color.text.default #e99499 | color.diff.removed.bg #2b0e0d | 7.82 | 4.5 | pass |
| default text on modified-line background | — | text | color.text.default #e99499 | color.diff.modified.bg #1f1a0c | 7.56 | 4.5 | pass |
| terminal foreground | — | text | color.terminal.fg #e99499 | color.terminal.bg #0c0909 | 8.65 | 4.5 | pass |
| chart label on canvas | — | text | color.chart.label #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| chart series 4 on canvas (graphic) | — | ui | color.chart.series-4 #a3676b | color.surface.canvas #000000 | 4.71 | 3 | pass |
| decorative icon on canvas (graphic) | — | ui | color.icon.decorative #a3676b | color.surface.canvas #000000 | 4.71 | 3 | pass |
| admin-form default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| admin-form default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| admin-form invalid text | invalid | text | color.status.danger.text #f73f35 | color.status.danger.tint #2b0e0d | 4.88 | 4.5 | pass |
| admin-form invalid border | invalid | ui | color.status.danger.border #e53935 | color.status.danger.tint #2b0e0d | 4.24 | 3 | pass |
| admin-form busy text | busy | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| title, labels and the current step on the panel surface | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| help, affix and review keys on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| breadcrumb links on the panel surface | — | text | color.text.link #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| complete-step check | — | text | color.status.success.text #86a46f | color.surface.default #100c0c | 6.99 | 4.5 | pass |
| notice title on its tint | — | text | color.status.info.text #7e9ebb | color.status.info.tint #0f141c | 6.59 | 4.5 | pass |
| notice text on its tint | — | text | color.text.default #e99499 | color.status.info.tint #0f141c | 8.06 | 4.5 | pass |
| summary text on the danger tint | invalid | text | color.text.default #e99499 | color.status.danger.tint #2b0e0d | 7.82 | 4.5 | pass |
| field messages on the panel surface | invalid | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| invalid field boundary | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| current-step indicator bar | — | ui | color.border.selected-indicator #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| Back button and file-input button boundaries | — | ui | color.action.secondary.border #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| disabled control text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled labels and review keys (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| disabled boundaries (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| review header rule (decorative) | — | ui | color.border.strong #9e231f | color.surface.default #100c0c | 2.51 | 1 | pass |
| panel edge (decorative) | — | ui | color.border.default #531310 | color.surface.default #100c0c | 1.36 | 1 | pass |
| step, row and footer rules (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| alert default text | default | text | color.status.danger.text #f73f35 | color.status.danger.tint #2b0e0d | 4.88 | 4.5 | pass |
| alert default border | default | ui | color.status.danger.border #e53935 | color.status.danger.tint #2b0e0d | 4.24 | 3 | pass |
| alert hover text | hover | text | color.action.tertiary.text #e99499 | color.action.tertiary.hover-bg #1c0a09 | 8.35 | 4.5 | pass |
| alert focus-visible text | focus-visible | text | color.action.tertiary.text #e99499 | color.status.info.tint #0f141c | 8.06 | 4.5 | pass |
| alert focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.status.info.tint #0f141c | 4.37 | 3 | pass |
| body text on the danger tint | — | text | color.text.default #e99499 | color.status.danger.tint #2b0e0d | 7.82 | 4.5 | pass |
| warning glyph and title on the warning tint | — | text | color.status.warning.text #c9973f | color.status.warning.tint #1f1a0c | 6.59 | 4.5 | pass |
| warning border on the warning tint | — | ui | color.status.warning.border #c9973f | color.status.warning.tint #1f1a0c | 6.59 | 3 | pass |
| body text on the warning tint | — | text | color.text.default #e99499 | color.status.warning.tint #1f1a0c | 7.56 | 4.5 | pass |
| success glyph and title on the success tint | — | text | color.status.success.text #86a46f | color.status.success.tint #0f1a0e | 6.43 | 4.5 | pass |
| success border on the success tint | — | ui | color.status.success.border #86a46f | color.status.success.tint #0f1a0e | 6.43 | 3 | pass |
| body text on the success tint | — | text | color.text.default #e99499 | color.status.success.tint #0f1a0e | 7.80 | 4.5 | pass |
| info glyph and title on the info tint | — | text | color.status.info.text #7e9ebb | color.status.info.tint #0f141c | 6.59 | 4.5 | pass |
| info border on the info tint | — | ui | color.status.info.border #7e9ebb | color.status.info.tint #0f141c | 6.59 | 3 | pass |
| body text on the info tint | — | text | color.text.default #e99499 | color.status.info.tint #0f141c | 8.06 | 4.5 | pass |
| neutral glyph and title on the neutral tint | — | text | color.status.neutral.text #bd787d | color.status.neutral.tint #241010 | 5.31 | 4.5 | pass |
| neutral border on the neutral tint | — | ui | color.status.neutral.border #a3676b | color.status.neutral.tint #241010 | 4.07 | 3 | pass |
| body text on the neutral tint | — | text | color.text.default #e99499 | color.status.neutral.tint #241010 | 7.92 | 4.5 | pass |
| close button on the info tint | — | text | color.action.tertiary.text #e99499 | color.status.info.tint #0f141c | 8.06 | 4.5 | pass |
| ring on the info tint | focus-visible | ui | color.interaction.focus.ring #e53935 | color.status.info.tint #0f141c | 4.37 | 3 | pass |
| avatar default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| avatar default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| badge default text | default | text | color.status.danger.on-fill #f9faf9 | color.status.danger.fill #dc282e | 4.58 | 4.5 | pass |
| badge empty text | empty | text | color.status.danger.on-fill #f9faf9 | color.status.danger.fill #dc282e | 4.58 | 4.5 | pass |
| warning badge | — | text | color.status.warning.on-fill #0c0909 | color.status.warning.fill #c9973f | 7.54 | 4.5 | pass |
| success badge | — | text | color.status.success.on-fill #0c0909 | color.status.success.fill #86a46f | 7.13 | 4.5 | pass |
| info badge | — | text | color.status.info.on-fill #0c0909 | color.status.info.fill #7e9ebb | 7.08 | 4.5 | pass |
| neutral badge | — | text | color.status.neutral.on-fill #000000 | color.status.neutral.fill #a3676b | 4.71 | 4.5 | pass |
| count badge | — | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| danger outline text | — | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| warning outline text | — | text | color.status.warning.text #c9973f | color.surface.default #100c0c | 7.39 | 4.5 | pass |
| success outline text | — | text | color.status.success.text #86a46f | color.surface.default #100c0c | 6.99 | 4.5 | pass |
| info outline text | — | text | color.status.info.text #7e9ebb | color.surface.default #100c0c | 6.94 | 4.5 | pass |
| neutral outline text | — | text | color.status.neutral.text #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| danger outline border | — | ui | color.status.danger.border #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| warning outline border | — | ui | color.status.warning.border #c9973f | color.surface.default #100c0c | 7.39 | 3 | pass |
| success outline border | — | ui | color.status.success.border #86a46f | color.surface.default #100c0c | 6.99 | 3 | pass |
| info outline border | — | ui | color.status.info.border #7e9ebb | color.surface.default #100c0c | 6.94 | 3 | pass |
| neutral outline border | — | ui | color.status.neutral.border #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| danger dot on the panel surface | empty | ui | color.status.danger.fill #dc282e | color.surface.default #100c0c | 4.06 | 3 | pass |
| warning dot on the panel surface | empty | ui | color.status.warning.fill #c9973f | color.surface.default #100c0c | 7.39 | 3 | pass |
| success dot on the panel surface | empty | ui | color.status.success.fill #86a46f | color.surface.default #100c0c | 6.99 | 3 | pass |
| info dot on the panel surface | empty | ui | color.status.info.fill #7e9ebb | color.surface.default #100c0c | 6.94 | 3 | pass |
| neutral dot on the panel surface | empty | ui | color.status.neutral.fill #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| breadcrumbs default text | default | text | color.text.link #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| breadcrumbs hover text | hover | text | color.text.link-hover #f4eeee | color.interaction.hover.bg-strong #630f0d | 11.41 | 4.5 | pass |
| breadcrumbs focus-visible text | focus-visible | text | color.text.link #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| breadcrumbs focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| breadcrumbs current text | current | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| breadcrumbs truncated text | truncated | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| separator on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| link underline | — | ui | color.text.link-underline #dc282e | color.surface.default #100c0c | 4.06 | 3 | pass |
| button-group default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| button-group default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| button default text | default | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| button hover text | hover | text | color.action.primary.text #f4eeee | color.action.primary.hover-bg #911410 | 7.92 | 4.5 | pass |
| button focus-visible text | focus-visible | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| button focus-visible outline | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 3 | pass |
| button active text | active | text | color.action.primary.text #f4eeee | color.action.primary.pressed-bg #630f0d | 11.41 | 4.5 | pass |
| button loading text | loading | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| primary fill against the panel (decorative; the label identifies the button) | — | ui | color.action.primary.bg #7d1310 | color.surface.default #100c0c | 1.83 | 3 | waived: the fill is not the boundary of the control; the label text at 9.28:1 identifies it, and hover, active and focus each add a second channel |
| secondary text at rest | — | text | color.action.secondary.text #ffa2a7 | color.action.secondary.bg #000000 | 10.98 | 4.5 | pass |
| secondary border at rest | — | ui | color.action.secondary.border #a3676b | color.action.secondary.bg #000000 | 4.71 | 3 | pass |
| secondary text on the hover fill | hover | text | color.action.secondary.text #ffa2a7 | color.action.secondary.hover-bg #630f0d | 6.84 | 4.5 | pass |
| secondary border against the panel while hovered (the fill inside it measures 2.94:1) | hover | ui | color.action.secondary.border #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| ring on the secondary hover fill | hover | ui | color.interaction.focus.ring #e53935 | color.action.secondary.hover-bg #630f0d | 3.10 | 3 | pass |
| secondary text while pressed | active | text | color.action.secondary.text #ffa2a7 | color.action.secondary.pressed-bg #420f0c | 8.41 | 4.5 | pass |
| tertiary text at rest | — | text | color.action.tertiary.text #e99499 | color.action.secondary.bg #000000 | 9.16 | 4.5 | pass |
| tertiary text on the hover fill | hover | text | color.action.tertiary.text #e99499 | color.action.tertiary.hover-bg #1c0a09 | 8.35 | 4.5 | pass |
| tertiary text while pressed | active | text | color.action.tertiary.text #e99499 | color.interaction.pressed.bg #420f0c | 7.02 | 4.5 | pass |
| active border on the pressed fill | active | ui | color.border.active #e53935 | color.interaction.pressed.bg #420f0c | 3.81 | 3 | pass |
| destructive text at rest | — | text | color.action.destructive.text #f73f35 | color.action.secondary.bg #000000 | 5.72 | 4.5 | pass |
| destructive border at rest | — | ui | color.action.destructive.border #e53935 | color.action.secondary.bg #000000 | 4.97 | 3 | pass |
| destructive text on the hover fill | hover | text | color.action.destructive.hover-text #f9faf9 | color.action.destructive.hover-bg #dc282e | 4.58 | 4.5 | pass |
| ring on the destructive hover fill (the ring takes the on-fill colour) | hover | ui | color.action.destructive.hover-text #f9faf9 | color.action.destructive.hover-bg #dc282e | 4.58 | 3 | pass |
| destructive text while pressed | active | text | color.action.destructive.hover-text #f9faf9 | color.action.destructive.pressed-bg #ae1914 | 6.81 | 4.5 | pass |
| confirm-dialog filled destructive | — | text | color.action.destructive.filled-text #f9faf9 | color.action.destructive.filled-bg #dc282e | 4.58 | 4.5 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| card default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| card hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| card hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| card focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| card focus-visible outline | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.default #100c0c | 10.17 | 3 | pass |
| card selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| card selected+focus-visible text | selected+focus-visible | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| card selected+focus-visible outline | selected+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| title on the card | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| metadata on the card | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| title on the hover fill | hover | text | color.text.bright #ffa2a7 | color.interaction.hover.bg #1c0a09 | 10.01 | 4.5 | pass |
| metadata on the hover fill | hover | text | color.text.muted #bd787d | color.interaction.hover.bg #1c0a09 | 5.60 | 4.5 | pass |
| card edge (decorative; the card is not a control at rest) | — | ui | color.border.default #531310 | color.surface.default #100c0c | 1.36 | 1 | pass |
| chart default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| chart default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| checkbox default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| checkbox hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| checkbox focus-visible border | focus-visible | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| checkbox focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| checkbox checked text | checked | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| checkbox mixed text | mixed | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| checkbox checked+focus-visible text | checked+focus-visible | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| checkbox checked+focus-visible outline | checked+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 3 | pass |
| checkbox invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| checkbox required border | required | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| option text on the panel surface | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| legend on the panel surface | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| checked box edge against the panel (the border stays border.control) | checked | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| glyph on the hovered checked fill | hover | text | color.action.primary.text #f4eeee | color.action.primary.hover-bg #911410 | 7.92 | 4.5 | pass |
| validation message on the panel surface | — | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| disabled option text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| disabled glyph (exempt; house floor 3:1) | checked+disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| chip default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| chip default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| chip hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| chip hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| chip focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| chip focus-visible border | focus-visible | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| chip focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| chip selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| chip selected+focus-visible text | selected+focus-visible | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| chip selected+focus-visible outline | selected+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| remove glyph at rest | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| disabled chip text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled chip border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| code-editor default text | default | text | color.code.syntax.variable #e99499 | color.code.bg #0c0909 | 8.65 | 4.5 | pass |
| code-editor default border | default | ui | color.border.control #a3676b | color.code.bg #0c0909 | 4.45 | 3 | pass |
| code-editor focus-visible text | focus-visible | text | color.code.syntax.variable #e99499 | color.code.current-line #1c0a09 | 8.35 | 4.5 | pass |
| code-editor focus-visible outline | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.code.current-line #1c0a09 | 10.01 | 3 | pass |
| code-editor selected text | selected | text | color.code.syntax.variable #e99499 | color.code.selection-bg #420f0c | 7.02 | 4.5 | pass |
| code-editor selected+container-inactive text | selected+container-inactive | text | color.interaction.selection.inactive-text #e99499 | color.interaction.selection.inactive-bg #420f0c | 7.02 | 4.5 | pass |
| code-editor current text | current | text | color.code.syntax.variable #e99499 | color.code.bg #0c0909 | 8.65 | 4.5 | pass |
| code-editor current border | current | ui | color.code.bracket-match #e53935 | color.code.bg #0c0909 | 4.69 | 3 | pass |
| code-editor read-only text | read-only | text | color.code.line-number #ad7175 | color.surface.sunken #000000 | 5.40 | 4.5 | pass |
| caret on the current line | focus-visible | ui | color.code.caret #e99499 | color.code.current-line #1c0a09 | 8.35 | 3 | pass |
| bracket-match outline on the editor surface | current | ui | color.code.bracket-match #e53935 | color.code.bg #0c0909 | 4.69 | 3 | pass |
| bracket-match outline on the current line | current | ui | color.code.bracket-match #e53935 | color.code.current-line #1c0a09 | 4.53 | 3 | pass |
| line numbers in the gutter | — | text | color.code.line-number #ad7175 | color.code.gutter-bg #0c0909 | 5.10 | 4.5 | pass |
| active line number | — | text | color.code.line-number-active #e99499 | color.code.current-line #1c0a09 | 8.35 | 4.5 | pass |
| keyword | — | text | color.code.syntax.keyword #f73f35 | color.code.bg #0c0909 | 5.40 | 4.5 | pass |
| string | — | text | color.code.syntax.string #bd787d | color.code.bg #0c0909 | 5.81 | 4.5 | pass |
| comment | — | text | color.code.syntax.comment #ad7175 | color.code.bg #0c0909 | 5.10 | 4.5 | pass |
| number and constant | — | text | color.code.syntax.number #d4868b | color.code.bg #0c0909 | 7.16 | 4.5 | pass |
| function | — | text | color.code.syntax.function #ffa2a7 | color.code.bg #0c0909 | 10.37 | 4.5 | pass |
| type | — | text | color.code.syntax.type #b37175 | color.code.bg #0c0909 | 5.23 | 4.5 | pass |
| property, heading and invalid text | — | text | color.code.syntax.property #e53935 | color.code.bg #0c0909 | 4.69 | 4.5 | pass |
| tag | — | text | color.code.syntax.tag #f73f35 | color.code.bg #0c0909 | 5.40 | 4.5 | pass |
| attribute and escape | — | text | color.code.syntax.attribute #d4868b | color.code.bg #0c0909 | 7.16 | 4.5 | pass |
| deprecated text | — | text | color.code.syntax.deprecated #ad7175 | color.code.bg #0c0909 | 5.10 | 4.5 | pass |
| muted string text inside the selection | selected | text | color.code.syntax.string #bd787d | color.code.selection-bg #420f0c | 4.71 | 4.5 | pass |
| fold marker text | — | text | color.text.muted #bd787d | color.code.bg #0c0909 | 5.81 | 4.5 | pass |
| indent guide (decorative) | — | ui | color.code.indent-guide #531310 | color.code.bg #0c0909 | 1.39 | 1 | pass |
| rendered whitespace (decorative) | — | ui | color.code.whitespace #7d1310 | color.code.bg #0c0909 | 1.86 | 1 | pass |
| gutter rule (decorative) | — | ui | color.code.gutter-rule #2b0e0d | color.code.bg #0c0909 | 1.11 | 1 | pass |
| combobox default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| combobox default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| combobox hover text | hover | text | color.text.link-hover #f4eeee | color.interaction.hover.bg-strong #630f0d | 11.41 | 4.5 | pass |
| combobox focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| combobox focus-visible border | focus-visible | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| combobox focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| combobox open text | open | text | color.text.default #e99499 | color.surface.raised #160b0b | 8.43 | 4.5 | pass |
| combobox open border | open | ui | color.border.overlay #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| combobox selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| combobox selected outline | selected | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| combobox invalid text | invalid | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| combobox invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| combobox invalid+focus-visible text | invalid+focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| combobox invalid+focus-visible border | invalid+focus-visible | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| combobox invalid+focus-visible outline | invalid+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.input #000000 | 10.98 | 3 | pass |
| combobox no-results text | no-results | text | color.text.muted #bd787d | color.surface.raised #160b0b | 5.66 | 4.5 | pass |
| combobox no-results border | no-results | ui | color.border.overlay #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| group heading on the listbox | — | text | color.text.subtle #ad7175 | color.surface.raised #160b0b | 4.96 | 4.5 | pass |
| validation message on the canvas | — | text | color.status.danger.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| help text on the canvas | — | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| group separator (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.raised #160b0b | 1.08 | 1 | pass |
| command-palette default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| command-palette default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| compact-dashboard default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| compact-dashboard default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| data-table default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| data-table hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| data-table focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| data-table focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| data-table selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| data-table selected+focus-visible text | selected+focus-visible | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| data-table selected+focus-visible outline | selected+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| data-table sorted text | sorted | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| data-table mixed text | mixed | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| data-table empty text | empty | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| data-table loading text | loading | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| header text | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| checkbox boundary | — | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| checked fill against the row (the glyph and the boundary carry the state) | mixed | ui | color.action.primary.bg #7d1310 | color.surface.default #100c0c | 1.83 | 1 | pass |
| header rule (decorative) | — | ui | color.border.strong #9e231f | color.surface.default #100c0c | 2.51 | 1 | pass |
| row divider (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| resize handle at rest (decorative; keyboard resizing is a header action) | — | ui | color.border.default #531310 | color.surface.default #100c0c | 1.36 | 1 | pass |
| date-picker default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| date-picker default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| date-picker hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| date-picker hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| date-picker focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| date-picker focus-visible border | focus-visible | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| date-picker focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| date-picker invalid text | invalid | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| date-picker invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| date-picker invalid+focus-visible text | invalid+focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| date-picker invalid+focus-visible border | invalid+focus-visible | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| date-picker invalid+focus-visible outline | invalid+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.input #000000 | 10.98 | 3 | pass |
| date-picker read-only text | read-only | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| validation message on the canvas | — | text | color.status.danger.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| help text and the range separator on the canvas | — | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| read-only bottom edge (decorative; the field has no box) | read-only | ui | color.border.divider #2b0e0d | color.surface.canvas #000000 | 1.17 | 1 | pass |
| calendar text | — | text | color.text.default #e99499 | color.surface.raised #160b0b | 8.43 | 4.5 | pass |
| selected date | — | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| description-list default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| description-list default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| diagnostics default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| diagnostics default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| diagnostics hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| diagnostics focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| diagnostics focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| diagnostics selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| diagnostics selected+focus-visible text | selected+focus-visible | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| diagnostics selected+focus-visible outline | selected+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| diagnostics error text | error | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| diagnostics empty text | empty | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| code text | — | text | color.text.default #e99499 | color.code.bg #0c0909 | 8.65 | 4.5 | pass |
| code text on the error line | — | text | color.text.default #e99499 | color.diagnostic.error.bg #2b0e0d | 7.82 | 4.5 | pass |
| error glyph on the error line | — | text | color.diagnostic.error.text #f73f35 | color.diagnostic.error.bg #2b0e0d | 4.88 | 4.5 | pass |
| error underline | — | ui | color.diagnostic.error.underline #e53935 | color.code.bg #0c0909 | 4.69 | 3 | pass |
| error underline on the error line | — | ui | color.diagnostic.error.underline #e53935 | color.diagnostic.error.bg #2b0e0d | 4.24 | 3 | pass |
| warning underline inside the selected range | selected | ui | color.diagnostic.warning.underline #c9973f | color.code.selection-bg #420f0c | 6.12 | 3 | pass |
| warning underline | — | ui | color.diagnostic.warning.underline #c9973f | color.code.bg #0c0909 | 7.54 | 3 | pass |
| info underline | — | ui | color.diagnostic.info.underline #7e9ebb | color.code.bg #0c0909 | 7.08 | 3 | pass |
| hint underline | — | ui | color.diagnostic.hint.underline #ad7175 | color.code.bg #0c0909 | 5.10 | 3 | pass |
| unused text | — | text | color.diagnostic.unused.text #bd787d | color.code.bg #0c0909 | 5.81 | 4.5 | pass |
| deprecated text | — | text | color.code.syntax.deprecated #ad7175 | color.code.bg #0c0909 | 5.10 | 4.5 | pass |
| error stripe mark | — | ui | color.diagnostic.error.stripe #e53935 | color.code.bg #0c0909 | 4.69 | 3 | pass |
| warning stripe mark | — | ui | color.diagnostic.warning.stripe #c9973f | color.code.bg #0c0909 | 7.54 | 3 | pass |
| info stripe mark | — | ui | color.diagnostic.info.text #7e9ebb | color.code.bg #0c0909 | 7.08 | 3 | pass |
| error glyph and count in the panel | — | text | color.diagnostic.error.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| warning glyph and count in the panel | — | text | color.diagnostic.warning.text #c9973f | color.surface.default #100c0c | 7.39 | 4.5 | pass |
| info glyph and count in the panel | — | text | color.diagnostic.info.text #7e9ebb | color.surface.default #100c0c | 6.94 | 4.5 | pass |
| hint glyph in the panel | — | text | color.text.subtle #ad7175 | color.surface.default #100c0c | 5.00 | 4.5 | pass |
| location and rule code | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| row text on hover | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| error glyph on hover | hover | text | color.diagnostic.error.text #f73f35 | color.interaction.hover.bg #1c0a09 | 5.21 | 4.5 | pass |
| the ✓ of the empty message | empty | text | color.status.success.text #86a46f | color.surface.default #100c0c | 6.99 | 4.5 | pass |
| dialog default text | default | text | color.text.default #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| dialog default border | default | ui | color.border.overlay #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| dialog open text | open | text | color.text.default #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| dialog open border | open | ui | color.border.overlay #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| dialog focus-trapped text | focus-trapped | text | color.text.default #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| dialog focus-trapped border | focus-trapped | ui | color.border.overlay #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| dialog focus-trapped outline | focus-trapped | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.overlay #241010 | 9.49 | 3 | pass |
| dialog hover text | hover | text | color.action.tertiary.text #e99499 | color.action.tertiary.hover-bg #1c0a09 | 8.35 | 4.5 | pass |
| dialog focus-visible text | focus-visible | text | color.action.tertiary.text #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| dialog focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| dialog reduced-motion text | reduced-motion | text | color.text.default #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| dialog reduced-motion border | reduced-motion | ui | color.border.overlay #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| title on the overlay surface | — | text | color.text.bright #ffa2a7 | color.surface.overlay #241010 | 9.49 | 4.5 | pass |
| note on the overlay surface | — | text | color.text.muted #bd787d | color.surface.overlay #241010 | 5.31 | 4.5 | pass |
| secondary button text on the overlay surface | — | text | color.action.secondary.text #ffa2a7 | color.surface.overlay #241010 | 9.49 | 4.5 | pass |
| secondary button boundary on the overlay surface | — | ui | color.action.secondary.border #a3676b | color.surface.overlay #241010 | 4.07 | 3 | pass |
| primary button text | — | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| destructive confirm text on its fill | — | text | color.action.destructive.filled-text #f9faf9 | color.action.destructive.filled-bg #dc282e | 4.58 | 4.5 | pass |
| destructive fill against the overlay surface | — | ui | color.action.destructive.filled-bg #dc282e | color.surface.overlay #241010 | 3.79 | 3 | pass |
| ring on the primary fill | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 3 | pass |
| ring on the destructive fill | focus-visible | ui | color.action.destructive.filled-text #f9faf9 | color.action.destructive.filled-bg #dc282e | 4.58 | 3 | pass |
| form field text | — | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| form field boundary on the overlay surface | — | ui | color.border.control #a3676b | color.surface.overlay #241010 | 4.07 | 3 | pass |
| diff-view default text | default | text | color.text.default #e99499 | color.code.bg #0c0909 | 8.65 | 4.5 | pass |
| diff-view default border | default | ui | color.border.control #a3676b | color.code.bg #0c0909 | 4.45 | 3 | pass |
| diff-view hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| diff-view selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| diff-view focus-visible text | focus-visible | text | color.text.default #e99499 | color.code.bg #0c0909 | 8.65 | 4.5 | pass |
| diff-view focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.code.bg #0c0909 | 4.69 | 3 | pass |
| diff-view expanded text | expanded | text | color.text.link #f73f35 | color.diff.header-bg #241010 | 4.94 | 4.5 | pass |
| diff-view collapsed text | collapsed | text | color.text.link #f73f35 | color.diff.header-bg #241010 | 4.94 | 4.5 | pass |
| text on an added row | — | text | color.text.default #e99499 | color.diff.added.bg #0f1a0e | 7.80 | 4.5 | pass |
| text on a removed row | — | text | color.text.default #e99499 | color.diff.removed.bg #2b0e0d | 7.82 | 4.5 | pass |
| text on a modified row | — | text | color.text.default #e99499 | color.diff.modified.bg #1f1a0c | 7.56 | 4.5 | pass |
| hunk context on the header | — | text | color.text.default #e99499 | color.diff.header-bg #241010 | 7.92 | 4.5 | pass |
| hunk range on the header | — | text | color.text.muted #bd787d | color.diff.header-bg #241010 | 5.31 | 4.5 | pass |
| line numbers on a context row | — | text | color.text.muted #bd787d | color.code.bg #0c0909 | 5.81 | 4.5 | pass |
| line numbers on an added row | — | text | color.text.muted #bd787d | color.diff.added.bg #0f1a0e | 5.23 | 4.5 | pass |
| line numbers on a removed row | — | text | color.text.muted #bd787d | color.diff.removed.bg #2b0e0d | 5.25 | 4.5 | pass |
| line numbers on a modified row | — | text | color.text.muted #bd787d | color.diff.modified.bg #1f1a0c | 5.07 | 4.5 | pass |
| the + marker | — | text | color.diff.added.gutter #86a46f | color.diff.added.bg #0f1a0e | 6.43 | 4.5 | pass |
| the − marker (bold, 4.24:1; glyph plus fill) | — | ui | color.diff.removed.gutter #e53935 | color.diff.removed.bg #2b0e0d | 4.24 | 3 | pass |
| the ~ marker | — | text | color.diff.modified.gutter #c9973f | color.diff.modified.bg #1f1a0c | 6.59 | 4.5 | pass |
| word-level added underline | — | ui | color.diff.added.emphasis #86a46f | color.diff.added.bg #0f1a0e | 6.43 | 3 | pass |
| word-level removed underline and strike | — | ui | color.diff.removed.emphasis #e53935 | color.diff.removed.bg #2b0e0d | 4.24 | 3 | pass |
| conflict double border | — | ui | color.diff.conflict-border #f73f35 | color.code.bg #0c0909 | 5.40 | 3 | pass |
| conflict marker lines | — | text | color.text.bright #ffa2a7 | color.diff.header-bg #241010 | 9.49 | 4.5 | pass |
| ring on an added row | focus-visible | ui | color.interaction.focus.ring #e53935 | color.diff.added.bg #0f1a0e | 4.23 | 3 | pass |
| ring on a removed row | focus-visible | ui | color.interaction.focus.ring #e53935 | color.diff.removed.bg #2b0e0d | 4.24 | 3 | pass |
| ring on a modified row | focus-visible | ui | color.interaction.focus.ring #e53935 | color.diff.modified.bg #1f1a0c | 4.10 | 3 | pass |
| ring on the selected row | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| fold button text | — | text | color.text.link #f73f35 | color.diff.header-bg #241010 | 4.94 | 4.5 | pass |
| disclosure default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| disclosure default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| drawer default text | default | text | color.text.default #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| drawer default border | default | ui | color.border.overlay #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| drawer open text | open | text | color.text.default #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| drawer open border | open | ui | color.border.overlay #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| drawer focus-trapped text | focus-trapped | text | color.text.default #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| drawer focus-trapped border | focus-trapped | ui | color.border.overlay #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| drawer focus-trapped outline | focus-trapped | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.overlay #241010 | 9.49 | 3 | pass |
| drawer reduced-motion text | reduced-motion | text | color.text.default #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| drawer reduced-motion border | reduced-motion | ui | color.border.overlay #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| title on the overlay surface | — | text | color.text.bright #ffa2a7 | color.surface.overlay #241010 | 9.49 | 4.5 | pass |
| note on the overlay surface | — | text | color.text.muted #bd787d | color.surface.overlay #241010 | 5.31 | 4.5 | pass |
| close button on the overlay surface | — | text | color.action.tertiary.text #e99499 | color.surface.overlay #241010 | 7.92 | 4.5 | pass |
| ring on the overlay surface | — | ui | color.interaction.focus.ring #e53935 | color.surface.overlay #241010 | 4.30 | 3 | pass |
| hovered item | — | text | color.text.link-hover #f4eeee | color.interaction.hover.bg-strong #630f0d | 11.41 | 4.5 | pass |
| row dividers (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.overlay #241010 | 1.01 | 1 | pass |
| editor-search default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| editor-search default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| empty-state default text | default | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| heading on the panel surface | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| action text on the panel surface | — | text | color.action.secondary.text #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| action boundary on the panel surface | — | ui | color.action.secondary.border #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| glyph (decorative) | — | ui | color.icon.decorative #a3676b | color.surface.default #100c0c | 4.36 | 1 | pass |
| container edge (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.canvas #000000 | 1.17 | 1 | pass |
| error-state default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| error-state default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| field default text | default | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| field required text | required | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| field invalid text | invalid | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| help text on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| help text on the canvas | — | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| disabled label (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| disabled control text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| fieldset default text | default | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| group border (decorative; the legend and spacing carry the grouping) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| field labels inside the group | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| help text inside the group | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| disabled legend and labels (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| disabled field text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled field border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| file-browser default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| file-browser default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| file-input default text | default | text | color.action.secondary.text #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| file-input default border | default | ui | color.action.secondary.border #a3676b | color.surface.canvas #000000 | 4.71 | 3 | pass |
| file-input hover text | hover | text | color.action.secondary.text #ffa2a7 | color.action.secondary.hover-bg #630f0d | 6.84 | 4.5 | pass |
| file-input focus-visible text | focus-visible | text | color.action.secondary.text #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| file-input focus-visible border | focus-visible | ui | color.action.secondary.border #a3676b | color.surface.canvas #000000 | 4.71 | 3 | pass |
| file-input focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| file-input drop-target text | drop-target | text | color.text.default #e99499 | color.interaction.marquee #110202 | 8.84 | 4.5 | pass |
| file-input drop-target border | drop-target | ui | color.interaction.drop-target #e53935 | color.interaction.marquee #110202 | 4.80 | 3 | pass |
| file-input invalid text | invalid | text | color.action.secondary.text #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| file-input invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| hovered button boundary against the canvas outside it | hover | ui | color.action.secondary.border #a3676b | color.surface.canvas #000000 | 4.71 | 3 | pass |
| drop-zone dashed border at rest | — | ui | color.interaction.drop-target #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| status, hint and help text on the canvas | — | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| file sizes on the canvas | — | text | color.text.subtle #ad7175 | color.surface.canvas #000000 | 5.40 | 4.5 | pass |
| per-file error and validation message on the canvas | — | text | color.status.danger.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| disabled button text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| progress stripes (decorative pattern) | loading | ui | color.action.primary.bg #7d1310 | color.interaction.pressed.bg #420f0c | 1.51 | 1 | pass |
| filterable-table default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| filterable-table hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| filterable-table focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| filterable-table focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| filterable-table selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| filterable-table no-results text | no-results | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| toolbar controls on the chrome surface | — | text | color.text.default #e99499 | color.surface.chrome #090707 | 8.77 | 4.5 | pass |
| search box and filter button boundaries on the chrome surface | — | ui | color.border.control #a3676b | color.surface.chrome #090707 | 4.51 | 3 | pass |
| column headers, empty-state heading and current page | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| count, chip remove glyph and empty-state text | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Tested badge | — | text | color.status.success.on-fill #0c0909 | color.status.success.fill #86a46f | 7.13 | 4.5 | pass |
| Demonstrated badge | — | text | color.status.info.on-fill #0c0909 | color.status.info.fill #7e9ebb | 7.08 | 4.5 | pass |
| New button and checked boxes | — | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| ring on a selected row | selected | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| chip and checkbox boundaries | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| disabled Prev link (exempt; house floor 3:1) | — | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| header rule (decorative) | — | ui | color.border.strong #9e231f | color.surface.default #100c0c | 2.51 | 1 | pass |
| panel edge and toolbar separator (decorative) | — | ui | color.border.default #531310 | color.surface.default #100c0c | 1.36 | 1 | pass |
| row, toolbar and footer rules (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| form-builder default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| form-builder default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Editor text | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Definition status | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| i3-window-frame default text | default | text | color.text.muted #bd787d | color.surface.chrome #090707 | 5.88 | 4.5 | pass |
| i3-window-frame focus-visible text | focus-visible | text | color.text.bright #ffa2a7 | color.surface.chrome-alt #1c0a09 | 10.01 | 4.5 | pass |
| i3-window-frame focus-visible outline | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.chrome-alt #1c0a09 | 10.01 | 3 | pass |
| i3-window-frame selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| i3-window-frame container-inactive text | container-inactive | text | color.text.muted #bd787d | color.surface.chrome #090707 | 5.88 | 4.5 | pass |
| i3-window-frame error text | error | text | color.status.danger.on-fill #f9faf9 | color.status.danger.fill #dc282e | 4.58 | 4.5 | pass |
| workspace names and status text on the bar | — | text | color.text.default #e99499 | color.surface.chrome #090707 | 8.77 | 4.5 | pass |
| clock on the bar | — | text | color.text.bright #ffa2a7 | color.surface.chrome #090707 | 10.50 | 4.5 | pass |
| focused title | — | text | color.text.bright #ffa2a7 | color.surface.chrome-alt #1c0a09 | 10.01 | 4.5 | pass |
| focused window frame on the desktop | — | ui | color.border.active #e53935 | color.surface.desktop #000000 | 4.97 | 3 | pass |
| floating window frame on the desktop | — | ui | color.border.overlay #e53935 | color.surface.desktop #000000 | 4.97 | 3 | pass |
| container ring on the window surface | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.canvas #000000 | 10.98 | 3 | pass |
| ring on a workspace or tab | — | ui | color.interaction.focus.ring #e53935 | color.surface.chrome #090707 | 4.75 | 3 | pass |
| visible-tab indicator on the title row | — | ui | color.border.selected-indicator #e53935 | color.surface.chrome #090707 | 4.75 | 3 | pass |
| urgent fill against the bar | error | ui | color.status.danger.fill #dc282e | color.surface.chrome #090707 | 4.20 | 3 | pass |
| visible-workspace fill against the bar | — | ui | color.interaction.selection.bg #531310 | color.surface.chrome #090707 | 1.41 | 1 | pass |
| inactive tab indicator | container-inactive | ui | color.border.selected-indicator-inactive #9e231f | color.surface.chrome #090707 | 2.60 | 1 | pass |
| unfocused window frames (decorative) | — | ui | color.border.default #531310 | color.surface.desktop #000000 | 1.47 | 1 | pass |
| status block separators (decorative) | — | ui | color.text.muted #bd787d | color.surface.chrome #090707 | 5.88 | 1 | pass |
| status glyphs (decorative) | — | ui | color.icon.decorative #a3676b | color.surface.chrome #090707 | 4.51 | 1 | pass |
| icon-button default text | default | text | color.action.tertiary.text #e99499 | color.action.secondary.bg #000000 | 9.16 | 4.5 | pass |
| icon-button hover text | hover | text | color.action.tertiary.text #e99499 | color.action.tertiary.hover-bg #1c0a09 | 8.35 | 4.5 | pass |
| icon-button focus-visible text | focus-visible | text | color.action.tertiary.text #e99499 | color.action.secondary.bg #000000 | 9.16 | 4.5 | pass |
| icon-button focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.action.secondary.bg #000000 | 4.97 | 3 | pass |
| icon-button active text | active | text | color.action.tertiary.text #e99499 | color.interaction.pressed.bg #420f0c | 7.02 | 4.5 | pass |
| icon-button active border | active | ui | color.border.active #e53935 | color.interaction.pressed.bg #420f0c | 3.81 | 3 | pass |
| icon-button toggled text | toggled | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| icon-button toggled+focus-visible text | toggled+focus-visible | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| icon-button toggled+focus-visible outline | toggled+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| toggled border against the panel | toggled | ui | color.border.selected-indicator #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| toggled fill against the panel (decorative) | toggled | ui | color.interaction.selection.bg #531310 | color.surface.default #100c0c | 1.36 | 3 | waived: aria-pressed, the filled icon form and the 1px selected-indicator border carry the state; the fill is not the boundary |
| disabled icon (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| kbd default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| kbd default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| link default text | default | text | color.text.link #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| link hover text | hover | text | color.text.link-hover #f4eeee | color.interaction.hover.bg-strong #630f0d | 11.41 | 4.5 | pass |
| link focus-visible text | focus-visible | text | color.text.link #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| link focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| link visited text | visited | text | color.text.link #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| link current text | current | text | color.text.bright #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| link current border | current | ui | color.border.selected-indicator #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| underline on the canvas | — | ui | color.text.link-underline #dc282e | color.surface.canvas #000000 | 4.39 | 3 | pass |
| underline on the panel | — | ui | color.text.link-underline #dc282e | color.surface.default #100c0c | 4.06 | 3 | pass |
| link text on the panel | — | text | color.text.link #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| link text on raised surfaces | — | text | color.text.link #f73f35 | color.surface.raised #160b0b | 5.26 | 4.5 | pass |
| ring on the hover fill | hover | ui | color.interaction.focus.ring #e53935 | color.interaction.hover.bg-strong #630f0d | 3.10 | 3 | pass |
| list default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| list default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| list hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| list focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| list focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| list selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| list selected+focus-visible text | selected+focus-visible | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| list selected+focus-visible outline | selected+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| list selected+container-inactive text | selected+container-inactive | text | color.interaction.selection.inactive-text #e99499 | color.interaction.selection.inactive-bg #420f0c | 7.02 | 4.5 | pass |
| metadata column on the list surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| metadata column on the hover fill | hover | text | color.text.muted #bd787d | color.interaction.hover.bg #1c0a09 | 5.60 | 4.5 | pass |
| disabled option text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| row divider (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| loading-indicator default text | default | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| loading-indicator reduced-motion text | reduced-motion | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| glyph on the canvas | — | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| block label on the panel surface | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| block glyph on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| block edge (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.canvas #000000 | 1.17 | 1 | pass |
| menu default text | default | text | color.text.default #e99499 | color.surface.raised #160b0b | 8.43 | 4.5 | pass |
| menu default border | default | ui | color.border.overlay #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| menu hover text | hover | text | color.text.link-hover #f4eeee | color.interaction.hover.bg-strong #630f0d | 11.41 | 4.5 | pass |
| menu focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| menu focus-visible border | focus-visible | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| menu focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| menu open text | open | text | color.text.default #e99499 | color.interaction.pressed.bg #420f0c | 7.02 | 4.5 | pass |
| menu open border | open | ui | color.border.active #e53935 | color.interaction.pressed.bg #420f0c | 3.81 | 3 | pass |
| menu closed text | closed | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| menu closed border | closed | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| menu checked text | checked | text | color.text.bright #ffa2a7 | color.surface.raised #160b0b | 10.10 | 4.5 | pass |
| menu selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| menu selected outline | selected | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| shortcut hint on the raised surface | — | text | color.text.muted #bd787d | color.surface.raised #160b0b | 5.66 | 4.5 | pass |
| disabled item text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.raised #160b0b | 3.24 | 3 | pass |
| item separator (decorative; role separator carries the grouping) | — | ui | color.border.divider #2b0e0d | color.surface.raised #160b0b | 1.08 | 1 | pass |
| submenu chevron (decorative; aria-haspopup carries the meaning) | — | ui | color.icon.decorative #a3676b | color.surface.raised #160b0b | 4.33 | 1 | pass |
| multiselect default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| multiselect default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| number-field default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| number-field default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| number-field hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| number-field hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| number-field focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| number-field focus-visible border | focus-visible | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| number-field focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| number-field invalid text | invalid | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| number-field invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| number-field invalid+focus-visible text | invalid+focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| number-field invalid+focus-visible border | invalid+focus-visible | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| number-field invalid+focus-visible outline | invalid+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.input #000000 | 10.98 | 3 | pass |
| number-field read-only text | read-only | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| unit affix on the input surface | — | text | color.text.muted #bd787d | color.surface.input #000000 | 6.15 | 4.5 | pass |
| stepper glyphs on the input surface | — | text | color.action.tertiary.text #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| stepper glyphs on their hover fill | hover | text | color.action.tertiary.text #e99499 | color.action.tertiary.hover-bg #1c0a09 | 8.35 | 4.5 | pass |
| validation message on the panel surface | — | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| help text on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| affix and stepper dividers (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.input #000000 | 1.17 | 1 | pass |
| read-only bottom edge (decorative; the field has no box) | read-only | ui | color.border.divider #2b0e0d | color.surface.canvas #000000 | 1.17 | 1 | pass |
| pagination default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| pagination hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| pagination focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| pagination focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| pagination current text | current | text | color.text.on-selection #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| ring on the current page when focused | current | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| disabled previous/next (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| gap ellipsis on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| popover default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| popover default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| progress default text | default | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| progress default border | default | ui | color.border.control #a3676b | color.surface.canvas #000000 | 4.71 | 3 | pass |
| progress error text | error | text | color.status.danger.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| progress error border | error | ui | color.status.danger.border #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| label on the canvas | — | text | color.text.bright #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| detail line on the canvas | — | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| value fill against the track | — | ui | color.action.primary.bg #7d1310 | color.interaction.pressed.bg #420f0c | 1.51 | 3 | waived: the fill measures 1.51:1 against the track; the value is carried by the visible percentage text and the native value, and the track boundary (border.control, 4.45:1) marks the full extent |
| stripes (decorative pattern) | busy | ui | color.action.primary.bg #7d1310 | color.interaction.pressed.bg #420f0c | 1.51 | 1 | pass |
| error fill against the track | error | ui | color.status.danger.fill #dc282e | color.interaction.pressed.bg #420f0c | 3.36 | 3 | pass |
| radio-group default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| radio-group hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| radio-group focus-visible border | focus-visible | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| radio-group focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| radio-group checked text | checked | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| radio-group checked+focus-visible text | checked+focus-visible | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| radio-group checked+focus-visible outline | checked+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 3 | pass |
| radio-group invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| radio-group required border | required | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| option text on the panel surface | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| legend on the panel surface | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| checked radio edge against the panel (the border stays border.control) | checked | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| inner square on the hovered checked fill | hover | ui | color.action.primary.text #f4eeee | color.action.primary.hover-bg #911410 | 7.92 | 3 | pass |
| validation message on the panel surface | — | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| disabled option text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| disabled inner square (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| range default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| range default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| repeater default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| repeater default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| search-field default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| search-field default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| search-field hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| search-field hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| search-field focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| search-field focus-visible border | focus-visible | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| search-field focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| search-field placeholder-shown text | placeholder-shown | text | color.text.placeholder #a3676b | color.surface.input #000000 | 4.71 | 4.5 | pass |
| search-field placeholder-shown border | placeholder-shown | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| search-field loading text | loading | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| search-field loading border | loading | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| search-field no-results text | no-results | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| search-field no-results border | no-results | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| magnifier on the input surface | — | ui | color.icon.default #e99499 | color.surface.input #000000 | 9.16 | 3 | pass |
| loading glyph and kbd hint on the input surface | — | text | color.text.muted #bd787d | color.surface.input #000000 | 6.15 | 4.5 | pass |
| clear glyph on the input surface | — | text | color.action.tertiary.text #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| clear glyph on its hover fill | hover | text | color.action.tertiary.text #e99499 | color.action.tertiary.hover-bg #1c0a09 | 8.35 | 4.5 | pass |
| help and no-results status on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| kbd hint edge (decorative) | — | ui | color.border.default #531310 | color.surface.input #000000 | 1.47 | 1 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| segmented-control default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| segmented-control default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| select default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| select default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| select hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| select hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| select focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| select focus-visible border | focus-visible | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| select focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| select required text | required | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| select required border | required | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| select invalid text | invalid | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| select invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| select invalid+focus-visible text | invalid+focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| select invalid+focus-visible border | invalid+focus-visible | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| select invalid+focus-visible outline | invalid+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.input #000000 | 10.98 | 3 | pass |
| chevron on the input surface | — | ui | color.icon.default #e99499 | color.surface.input #000000 | 9.16 | 3 | pass |
| option text in the themed list | — | text | color.text.default #e99499 | color.surface.raised #160b0b | 8.43 | 4.5 | pass |
| group label in the themed list | — | text | color.text.muted #bd787d | color.surface.raised #160b0b | 5.66 | 4.5 | pass |
| selected option | — | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| validation message on the panel surface | — | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| help text on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| settings-panel default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| settings-panel focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| settings-panel focus-visible border | focus-visible | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| settings-panel focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| settings-panel invalid text | invalid | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| settings-panel invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| settings-panel busy text | busy | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| title, legend and the current section on the panel surface | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| lead and help text on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| validation message on the panel surface | invalid | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| current-section indicator bar | — | ui | color.border.selected-indicator #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| control boundaries on the panel surface | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Reset button boundary | — | ui | color.action.secondary.border #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| panel edge (decorative) | — | ui | color.border.default #531310 | color.surface.default #100c0c | 1.36 | 1 | pass |
| sidebar, group and footer rules (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| sidebar-nav default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| sidebar-nav hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| sidebar-nav focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| sidebar-nav focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| sidebar-nav current text | current | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| sidebar-nav current border | current | ui | color.border.selected-indicator #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| sidebar-nav selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| sidebar-nav selected+container-inactive text | selected+container-inactive | text | color.interaction.selection.inactive-text #e99499 | color.interaction.selection.inactive-bg #420f0c | 7.02 | 4.5 | pass |
| sidebar-nav expanded text | expanded | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| sidebar-nav collapsed text | collapsed | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| ring on a selected item when focused | selected | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| group chevron (decorative; aria-expanded carries the state) | — | ui | color.icon.decorative #a3676b | color.surface.default #100c0c | 4.36 | 1 | pass |
| rail edge (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| skeleton default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| skeleton default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| skip-link default text | default | text | color.text.bright #ffa2a7 | color.surface.raised #160b0b | 10.10 | 4.5 | pass |
| skip-link default border | default | ui | color.border.overlay #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| skip-link focus-visible text | focus-visible | text | color.text.bright #ffa2a7 | color.surface.raised #160b0b | 10.10 | 4.5 | pass |
| skip-link focus-visible border | focus-visible | ui | color.border.overlay #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| skip-link focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| ring on the raised surface | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| switch default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| switch hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| switch focus-visible border | focus-visible | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| switch focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| switch checked text | checked | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| switch checked+focus-visible text | checked+focus-visible | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| switch checked+focus-visible outline | checked+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 3 | pass |
| thumb on the off track | — | ui | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 3 | pass |
| thumb on the hovered off track | hover | ui | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 3 | pass |
| thumb on the hovered on track | hover | ui | color.action.primary.text #f4eeee | color.action.primary.hover-bg #911410 | 7.92 | 3 | pass |
| on track edge against the panel (the border stays border.control) | checked | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| visible name on the panel surface | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Off / On word on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| disabled thumb (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled name (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| table default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| table hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| table selected text | selected | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| table selected+focus-visible text | selected+focus-visible | text | color.interaction.selection.text #f4eeee | color.interaction.selection.bg #531310 | 12.47 | 4.5 | pass |
| table selected+focus-visible outline | selected+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.interaction.selection.bg #531310 | 7.48 | 3 | pass |
| table selected+container-inactive text | selected+container-inactive | text | color.interaction.selection.inactive-text #e99499 | color.interaction.selection.inactive-bg #420f0c | 7.02 | 4.5 | pass |
| table sorted text | sorted | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| table empty text | empty | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| table loading text | loading | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| header text | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| sticky header text on the raised surface | — | text | color.text.bright #ffa2a7 | color.surface.raised #160b0b | 10.10 | 4.5 | pass |
| caption | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| ring on a sort button | — | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| header rule (decorative; thead carries the structure) | — | ui | color.border.strong #9e231f | color.surface.default #100c0c | 2.51 | 1 | pass |
| row divider (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| tabs default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| tabs hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| tabs focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| tabs focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| tabs selected text | selected | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| tabs selected border | selected | ui | color.border.selected-indicator #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| tabs selected+focus-visible text | selected+focus-visible | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| tabs selected+focus-visible border | selected+focus-visible | ui | color.border.selected-indicator #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| tabs selected+focus-visible outline | selected+focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.default #100c0c | 4.60 | 3 | pass |
| tabs selected+container-inactive text | selected+container-inactive | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| inactive indicator (decorative; the selected tab keeps aria-selected and bright text) | selected+container-inactive | ui | color.border.selected-indicator-inactive #9e231f | color.surface.default #100c0c | 2.51 | 1 | pass |
| disabled tab text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.default #100c0c | 3.26 | 3 | pass |
| tablist bottom rule (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.default #100c0c | 1.08 | 1 | pass |
| terminal default text | default | text | color.terminal.fg #e99499 | color.terminal.bg #0c0909 | 8.65 | 4.5 | pass |
| terminal default border | default | ui | color.border.control #a3676b | color.terminal.bg #0c0909 | 4.45 | 3 | pass |
| terminal focus-visible text | focus-visible | text | color.terminal.fg #e99499 | color.terminal.bg #0c0909 | 8.65 | 4.5 | pass |
| terminal focus-visible outline | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.terminal.bg #0c0909 | 10.37 | 3 | pass |
| terminal selected text | selected | text | color.terminal.selection-text #f4eeee | color.terminal.selection-bg #911410 | 7.92 | 4.5 | pass |
| terminal busy text | busy | text | color.terminal.bg #0c0909 | color.terminal.cursor #e99499 | 8.65 | 4.5 | pass |
| terminal current text | current | text | color.terminal.fg #e99499 | color.code.current-line #1c0a09 | 8.35 | 4.5 | pass |
| hollow cursor outline at rest | — | ui | color.terminal.cursor #e99499 | color.terminal.bg #0c0909 | 8.65 | 3 | pass |
| cursor on the cursor line | current | ui | color.terminal.cursor #e99499 | color.code.current-line #1c0a09 | 8.35 | 3 | pass |
| prompt segment text on slot 4 | — | text | color.terminal.selection-text #f4eeee | color.terminal.ansi.4 #8c1212 | 8.28 | 4.5 | pass |
| branch segment text on slot 3 | — | text | color.terminal.bg #0c0909 | color.terminal.ansi.3 #d4868b | 7.16 | 4.5 | pass |
| prompt arrow (the segment's own colour, decorative) | — | ui | color.terminal.ansi.4 #8c1212 | color.terminal.bg #0c0909 | 2.09 | 1 | pass |
| dim text and grid labels | — | text | color.text.muted #bd787d | color.terminal.bg #0c0909 | 5.81 | 4.5 | pass |
| inverse text | — | text | color.terminal.bg #0c0909 | color.terminal.fg #e99499 | 8.65 | 4.5 | pass |
| slot 2 | — | text | color.terminal.ansi.2 #bd787d | color.terminal.bg #0c0909 | 5.81 | 4.5 | pass |
| slot 3 | — | text | color.terminal.ansi.3 #d4868b | color.terminal.bg #0c0909 | 7.16 | 4.5 | pass |
| slot 5 | — | text | color.terminal.ansi.5 #f73f35 | color.terminal.bg #0c0909 | 5.40 | 4.5 | pass |
| slot 7 | — | text | color.terminal.ansi.7 #ffa2a7 | color.terminal.bg #0c0909 | 10.37 | 4.5 | pass |
| slot 11 | — | text | color.terminal.ansi.11 #b37175 | color.terminal.bg #0c0909 | 5.23 | 4.5 | pass |
| slot 1 (3.42:1) | — | text | color.terminal.ansi.1 #c81a1a | color.terminal.bg #0c0909 | 3.42 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 4 (2.09:1) | — | text | color.terminal.ansi.4 #8c1212 | color.terminal.bg #0c0909 | 2.09 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 6 (3.26:1) | — | text | color.terminal.ansi.6 #9e474a | color.terminal.bg #0c0909 | 3.26 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 8 (1.86:1) | — | text | color.terminal.ansi.8 #7d1310 | color.terminal.bg #0c0909 | 1.86 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 9 (2.70:1) | — | text | color.terminal.ansi.9 #ab1612 | color.terminal.bg #0c0909 | 2.70 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 10 (2.92:1) | — | text | color.terminal.ansi.10 #ad2721 | color.terminal.bg #0c0909 | 2.92 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 12 (2.12:1) | — | text | color.terminal.ansi.12 #871f19 | color.terminal.bg #0c0909 | 2.12 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 13 (4.43:1) | — | text | color.terminal.ansi.13 #e82132 | color.terminal.bg #0c0909 | 4.43 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 14 (4.28:1) | — | text | color.terminal.ansi.14 #e0292f | color.terminal.bg #0c0909 | 4.28 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 15 (4.45:1; not in the foundations list) | — | text | color.terminal.ansi.15 #a3676b | color.terminal.bg #0c0909 | 4.45 | 4.5 | waived: heritage slot; documented as failing, programs choose slot semantics |
| slot 0 (1.00:1; it is the background) | — | text | color.terminal.ansi.0 #0c0909 | color.terminal.bg #0c0909 | 1.00 | 4.5 | waived: heritage slot; slot 0 is the terminal background and is never a foreground on it |
| text-field default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| text-field default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| text-field hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| text-field hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| text-field focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| text-field focus-visible border | focus-visible | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| text-field focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| text-field placeholder-shown text | placeholder-shown | text | color.text.placeholder #a3676b | color.surface.input #000000 | 4.71 | 4.5 | pass |
| text-field placeholder-shown border | placeholder-shown | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| text-field invalid text | invalid | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| text-field invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| text-field invalid+focus-visible text | invalid+focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| text-field invalid+focus-visible border | invalid+focus-visible | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| text-field invalid+focus-visible outline | invalid+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.input #000000 | 10.98 | 3 | pass |
| text-field read-only text | read-only | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| validation message on the panel surface | — | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| help text on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| read-only bottom edge (decorative; the field has no box) | read-only | ui | color.border.divider #2b0e0d | color.surface.canvas #000000 | 1.17 | 1 | pass |
| textarea default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| textarea default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| textarea hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| textarea hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| textarea focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| textarea focus-visible border | focus-visible | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| textarea focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| textarea placeholder-shown text | placeholder-shown | text | color.text.placeholder #a3676b | color.surface.input #000000 | 4.71 | 4.5 | pass |
| textarea placeholder-shown border | placeholder-shown | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| textarea invalid text | invalid | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| textarea invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| textarea invalid+focus-visible text | invalid+focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| textarea invalid+focus-visible border | invalid+focus-visible | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| textarea invalid+focus-visible outline | invalid+focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.input #000000 | 10.98 | 3 | pass |
| textarea read-only text | read-only | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| validation message on the panel surface | — | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| help and count on the panel surface | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| read-only bottom edge (decorative; the field has no box) | read-only | ui | color.border.divider #2b0e0d | color.surface.canvas #000000 | 1.17 | 1 | pass |
| time-picker default text | default | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| time-picker default border | default | ui | color.border.control #a3676b | color.surface.input #000000 | 4.71 | 3 | pass |
| time-picker hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| time-picker hover border | hover | ui | color.border.control #a3676b | color.interaction.hover.bg #1c0a09 | 4.29 | 3 | pass |
| time-picker focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| time-picker focus-visible border | focus-visible | ui | color.border.active #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| time-picker focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| time-picker invalid text | invalid | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| time-picker invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.input #000000 | 4.97 | 3 | pass |
| validation message on the canvas | — | text | color.status.danger.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| help text on the canvas | — | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| disabled text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.interaction.disabled.bg #160b0b | 3.24 | 3 | pass |
| disabled border (exempt) | disabled | ui | color.border.disabled #7d1310 | color.interaction.disabled.bg #160b0b | 1.82 | 1 | pass |
| timeline default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| timeline default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| toast default text | default | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| toast default border | default | ui | color.border.overlay #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| toast hover text | hover | text | color.text.link-hover #f4eeee | color.interaction.hover.bg-strong #630f0d | 11.41 | 4.5 | pass |
| toast focus-visible text | focus-visible | text | color.action.tertiary.text #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| toast focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| toast stacked text | stacked | text | color.text.muted #bd787d | color.surface.canvas #000000 | 6.15 | 4.5 | pass |
| toast stacked border | stacked | ui | color.border.overlay #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| toast reduced-motion text | reduced-motion | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| toast reduced-motion border | reduced-motion | ui | color.border.overlay #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| glyph on the plain toast | — | text | color.status.info.text #7e9ebb | color.surface.canvas #000000 | 7.50 | 4.5 | pass |
| action text on the plain toast | — | text | color.text.link #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| warning glyph on its tint | — | text | color.status.warning.text #c9973f | color.status.warning.tint #1f1a0c | 6.59 | 4.5 | pass |
| warning border on its tint | — | ui | color.status.warning.border #c9973f | color.status.warning.tint #1f1a0c | 6.59 | 3 | pass |
| text on the warning tint | — | text | color.text.default #e99499 | color.status.warning.tint #1f1a0c | 7.56 | 4.5 | pass |
| success glyph on its tint | — | text | color.status.success.text #86a46f | color.status.success.tint #0f1a0e | 6.43 | 4.5 | pass |
| success border on its tint | — | ui | color.status.success.border #86a46f | color.status.success.tint #0f1a0e | 6.43 | 3 | pass |
| text on the success tint | — | text | color.text.default #e99499 | color.status.success.tint #0f1a0e | 7.80 | 4.5 | pass |
| info glyph on its tint | — | text | color.status.info.text #7e9ebb | color.status.info.tint #0f141c | 6.59 | 4.5 | pass |
| info border on its tint | — | ui | color.status.info.border #7e9ebb | color.status.info.tint #0f141c | 6.59 | 3 | pass |
| text on the info tint | — | text | color.text.default #e99499 | color.status.info.tint #0f141c | 8.06 | 4.5 | pass |
| neutral glyph on its tint | — | text | color.status.neutral.text #bd787d | color.status.neutral.tint #241010 | 5.31 | 4.5 | pass |
| neutral border on its tint | — | ui | color.status.neutral.border #a3676b | color.status.neutral.tint #241010 | 4.07 | 3 | pass |
| text on the neutral tint | — | text | color.text.default #e99499 | color.status.neutral.tint #241010 | 7.92 | 4.5 | pass |
| text, glyph and buttons on the danger fill | — | text | color.status.danger.on-fill #f9faf9 | color.status.danger.fill #dc282e | 4.58 | 4.5 | pass |
| the danger fill is its own boundary against the canvas | — | ui | color.status.danger.fill #dc282e | color.surface.canvas #000000 | 4.39 | 3 | pass |
| danger border on the danger fill (decorative) | — | ui | color.status.danger.border #e53935 | color.status.danger.fill #dc282e | 1.13 | 1 | pass |
| toolbar default text | default | text | color.text.default #e99499 | color.surface.chrome #090707 | 8.77 | 4.5 | pass |
| toolbar hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| toolbar focus-visible text | focus-visible | text | color.text.default #e99499 | color.surface.chrome #090707 | 8.77 | 4.5 | pass |
| toolbar focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.chrome #090707 | 4.75 | 3 | pass |
| toolbar toggled text | toggled | text | color.text.bright #ffa2a7 | color.interaction.pressed.bg #420f0c | 8.41 | 4.5 | pass |
| toolbar toggled border | toggled | ui | color.border.active #e53935 | color.interaction.pressed.bg #420f0c | 3.81 | 3 | pass |
| disabled button text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.chrome #090707 | 3.37 | 3 | pass |
| separator (decorative; role separator carries the grouping) | — | ui | color.border.default #531310 | color.surface.chrome #090707 | 1.41 | 1 | pass |
| toolbar bottom edge (decorative) | — | ui | color.border.divider #2b0e0d | color.surface.chrome #090707 | 1.12 | 1 | pass |
| tooltip default text | default | text | color.text.default #e99499 | color.surface.raised #160b0b | 8.43 | 4.5 | pass |
| tooltip default border | default | ui | color.border.overlay #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| tooltip hover text | hover | text | color.action.tertiary.text #e99499 | color.action.tertiary.hover-bg #1c0a09 | 8.35 | 4.5 | pass |
| tooltip focus-visible text | focus-visible | text | color.action.tertiary.text #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| tooltip focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| tooltip open text | open | text | color.text.default #e99499 | color.surface.raised #160b0b | 8.43 | 4.5 | pass |
| tooltip open border | open | ui | color.border.overlay #e53935 | color.surface.raised #160b0b | 4.57 | 3 | pass |
| shortcut on the tooltip | — | text | color.text.bright #ffa2a7 | color.surface.raised #160b0b | 10.10 | 4.5 | pass |
| shortcut key border on the tooltip | — | ui | color.border.control #a3676b | color.surface.raised #160b0b | 4.33 | 3 | pass |
| trigger text on the canvas | — | text | color.action.tertiary.text #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| tree default text | default | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| tree default border | default | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| Primary content | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| Supporting content | — | text | color.text.muted #bd787d | color.surface.default #100c0c | 5.69 | 4.5 | pass |
| Control boundary | — | ui | color.border.control #a3676b | color.surface.default #100c0c | 4.36 | 3 | pass |
| wizard default text | default | text | color.text.default #e99499 | color.surface.canvas #000000 | 9.16 | 4.5 | pass |
| wizard hover text | hover | text | color.text.default #e99499 | color.interaction.hover.bg #1c0a09 | 8.35 | 4.5 | pass |
| wizard focus-visible text | focus-visible | text | color.text.bright #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| wizard focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| wizard current text | current | text | color.text.bright #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| wizard current border | current | ui | color.border.selected-indicator #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| wizard invalid text | invalid | text | color.status.danger.text #f73f35 | color.surface.canvas #000000 | 5.72 | 4.5 | pass |
| wizard invalid border | invalid | ui | color.status.danger.border #e53935 | color.surface.canvas #000000 | 4.97 | 3 | pass |
| wizard busy text | busy | text | color.action.primary.text #f4eeee | color.action.primary.bg #7d1310 | 9.28 | 4.5 | pass |
| complete marker on the canvas | — | text | color.status.success.text #86a46f | color.surface.canvas #000000 | 7.55 | 4.5 | pass |
| panel title on the panel surface | — | text | color.text.bright #ffa2a7 | color.surface.default #100c0c | 10.17 | 4.5 | pass |
| panel text on the panel surface | — | text | color.text.default #e99499 | color.surface.default #100c0c | 8.48 | 4.5 | pass |
| step message on the panel surface | — | text | color.status.danger.text #f73f35 | color.surface.default #100c0c | 5.29 | 4.5 | pass |
| panel edge (decorative) | — | ui | color.border.default #531310 | color.surface.canvas #000000 | 1.47 | 1 | pass |
| back button text | — | text | color.action.secondary.text #ffa2a7 | color.surface.canvas #000000 | 10.98 | 4.5 | pass |
| back button boundary | — | ui | color.action.secondary.border #a3676b | color.surface.canvas #000000 | 4.71 | 3 | pass |
| next button boundary | — | ui | color.action.primary.border #9e231f | color.surface.canvas #000000 | 2.71 | 3 | waived: the primary button is identified by its text at 9.28:1 on its fill and by its position; the fill measures 1.86:1 and its border 2.56:1 against the canvas, a documented property of the actions family, not of the wizard |
| ring on the next button's fill | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 3 | pass |
| future step text (exempt; house floor 3:1) | disabled | ui | color.text.disabled #8a5559 | color.surface.canvas #000000 | 3.52 | 3 | pass |
How the theme travels to other applications and frameworks, what a port must reproduce, what it may declare unsupported, and how support is catalogued truthfully.
A port themes only what the host documents as customisable. Editor colouring, application chrome, terminal colours and plugin surfaces are separate scopes; a file that changes one does not claim the others. A port never patches binaries, injects unsupported hacks, overwrites a user’s whole settings file or replaces keybindings, credentials or unrelated preferences. It installs under a unique name beside the host’s defaults and documents how to roll back.
Every required role is mapped, explicitly inherited from the host, marked unsupported with an explanation, or intentionally out of scope. A percentage of coverage is meaningful only with its denominator.
| Host capability | Fallback |
|---|---|
| No dashed outlines (many native toolkits) | solid 1px ring in focus.ring; record the deviation |
| No separate inactive selection | selection fill everywhere; record it |
| No read-only styling hook | treat as default text with a dotted bottom edge if borders are available, else as default |
| No pattern fills (charts) | keep the glyph or label channel; never colour alone |
| Sixteen-colour terminal | heritage-ansi for fidelity or the extended slots when approved; document bold-as-bright and whether 256/24-bit colours bypass the slots |
| Fixed radii in the host | document; do not fight the toolkit |
| Host forbids removing its focus indicator | keep the host’s indicator; never draw two |
The site’s exports/tokens.css exposes every role as a custom property.
Consume roles (--color-text-default), never primitives. A framework
integration maps roles onto the framework’s theme keys and lists what it
cannot express; agents/consume.md gives the reading order and the deviation
report format, and theme.lock.json pins the revision.
A port lives at ports/<slug>/ with port.json, mapping.json, src/,
dist/ and evidence/, created from templates/port/ only when work begins.
Statuses are experimental, verified and deprecated; reference
implementations and roadmap candidates are not statuses. verified requires a
real import into the recorded application version with matching evidence and a
token digest equal to the current default profile. A successful XML or JSON
parse is a structural pass, not verification. Never hand-edit a generated hex;
fix the mapping or the approved token and regenerate.
For JetBrains, an editor scheme (.icls) and a UI theme plugin are separate
capabilities; the scheme alone does not recolour toolbars or dialogs. For
Notepad++, a style-theme XML, the dark-mode chrome setting and User Defined
Languages are three mechanisms. Consult the vendor documentation for the
tested version before mapping.
j3w1/j3w1.github.io is the flagship web implementation and the source of the
observed values. It is not an upstream that this repository scrapes at build
time, and there is no automatic synchronisation in either direction. A later
integration pins a release of this theme in that site through a separate
reviewed change and records its own site-specific values (window-manager
geometry, wallpaper gradients) as such.
Explore capabilities and semantic mappings
No native ports are published yet. Historical implementations are catalogued under References and are not supported downloads.
Specified: spec/components/<id>.md validates. Demonstrated: <id>.demo.html exists with a fragment for every declared variant and renders on the site. Test implemented: tests/browser/components/<id>.spec.js exists; does not establish a pass. Computed from the sources, never declared.
Execution results are separate: verification summaries and full matrix. Missing browser or manual evidence is not run.
| Family | Total | Specified | Demonstrated | Test implemented |
|---|---|---|---|---|
| composed | 6 | 6 | 6 | 0 |
| feedback | 11 | 11 | 11 | 1 |
| display | 11 | 11 | 11 | 1 |
| navigation | 10 | 10 | 10 | 2 |
| actions | 5 | 5 | 5 | 2 |
| forms-basic | 11 | 11 | 11 | 4 |
| developer | 5 | 5 | 5 | 0 |
| forms-advanced | 8 | 8 | 8 | 1 |
| Component | Family | Priority | Specified | Demonstrated | Test implemented |
|---|---|---|---|---|---|
admin-form · evidence | composed | R1 | yes | yes | — |
alert · evidence | feedback | R1 | yes | yes | — |
avatar · evidence | display | R2 | yes | yes | — |
badge · evidence | display | R1 | yes | yes | — |
breadcrumbs · evidence | navigation | R1 | yes | yes | — |
button-group · evidence | actions | R2 | yes | yes | — |
button · evidence | actions | R1 | yes | yes | yes |
card · evidence | display | R1 | yes | yes | — |
chart · evidence | display | R2 | yes | yes | — |
checkbox · evidence | forms-basic | R1 | yes | yes | yes |
chip · evidence | display | R1 | yes | yes | — |
code-editor · evidence | developer | R1 | yes | yes | — |
combobox · evidence | forms-advanced | R1 | yes | yes | — |
command-palette · evidence | navigation | R2 | yes | yes | — |
compact-dashboard · evidence | composed | R2 | yes | yes | — |
data-table · evidence | display | R1 | yes | yes | — |
date-picker · evidence | forms-advanced | R1 | yes | yes | — |
description-list · evidence | display | R2 | yes | yes | — |
diagnostics · evidence | developer | R1 | yes | yes | — |
dialog · evidence | feedback | R1 | yes | yes | yes |
diff-view · evidence | developer | R1 | yes | yes | — |
disclosure · evidence | navigation | R2 | yes | yes | — |
drawer · evidence | feedback | R1 | yes | yes | — |
editor-search · evidence | developer | R2 | yes | yes | — |
empty-state · evidence | feedback | R1 | yes | yes | — |
error-state · evidence | feedback | R2 | yes | yes | — |
field · evidence | forms-basic | R1 | yes | yes | — |
fieldset · evidence | forms-basic | R1 | yes | yes | — |
file-browser · evidence | composed | R2 | yes | yes | — |
file-input · evidence | forms-advanced | R1 | yes | yes | — |
filterable-table · evidence | composed | R1 | yes | yes | — |
form-builder · evidence | forms-advanced | L | yes | yes | yes |
i3-window-frame · evidence | composed | R1 | yes | yes | — |
icon-button · evidence | actions | R1 | yes | yes | — |
kbd · evidence | display | R2 | yes | yes | — |
link · evidence | actions | R1 | yes | yes | yes |
list · evidence | display | R1 | yes | yes | — |
loading-indicator · evidence | feedback | R1 | yes | yes | — |
menu · evidence | navigation | R1 | yes | yes | yes |
multiselect · evidence | forms-advanced | R2 | yes | yes | — |
number-field · evidence | forms-basic | R1 | yes | yes | — |
pagination · evidence | navigation | R1 | yes | yes | — |
popover · evidence | feedback | R2 | yes | yes | — |
progress · evidence | feedback | R1 | yes | yes | — |
radio-group · evidence | forms-basic | R1 | yes | yes | yes |
range · evidence | forms-basic | R2 | yes | yes | — |
repeater · evidence | forms-advanced | R2 | yes | yes | — |
search-field · evidence | forms-basic | R1 | yes | yes | — |
segmented-control · evidence | actions | R2 | yes | yes | — |
select · evidence | forms-basic | R1 | yes | yes | yes |
settings-panel · evidence | composed | R1 | yes | yes | — |
sidebar-nav · evidence | navigation | R1 | yes | yes | — |
skeleton · evidence | feedback | R2 | yes | yes | — |
skip-link · evidence | navigation | R1 | yes | yes | — |
switch · evidence | forms-basic | R1 | yes | yes | — |
table · evidence | display | R1 | yes | yes | yes |
tabs · evidence | navigation | R1 | yes | yes | yes |
terminal · evidence | developer | R1 | yes | yes | — |
text-field · evidence | forms-basic | R1 | yes | yes | yes |
textarea · evidence | forms-basic | R1 | yes | yes | — |
time-picker · evidence | forms-advanced | R1 | yes | yes | — |
timeline · evidence | display | R2 | yes | yes | — |
toast · evidence | feedback | R1 | yes | yes | — |
toolbar · evidence | navigation | R1 | yes | yes | — |
tooltip · evidence | feedback | R1 | yes | yes | — |
tree · evidence | navigation | R2 | yes | yes | — |
wizard · evidence | forms-advanced | R1 | yes | yes | — |
Where the values came from and what each reference does not prove. Nothing here is a supported download.
| Reference | Kind | Where | What it does not prove |
|---|---|---|---|
| j3w1.github.io site.css — the flagship web implementation | reference-implementation | j3w1/j3w1.github.io@ffa8122 assets/css/site.css | The :root block (lines 78–136) is the observed source of every role value. It will become a pinned consumer of this theme through a separate reviewed change; there is no live import. |
| j3w1.github.io accessibility contract (contrast floor, reduced motion, focus) | reference-implementation | j3w1/j3w1.github.io@ffa8122 docs/wm-accessibility.md | Section 9 measured the palette and restricted --quiet and --inactive; this specification extends those rules (D-003). |
| Xresources — the canonical sixteen-slot palette with the author's slot annotations | historical | j3w1/1w3j@9841315 config/Xresources | Exact values are preserved as color.primitive.ansi.*; the annotations seed the syntax roles. A 2020–21 X11 configuration proves nothing about current desktop installations. |
| i3-gaps configuration — borders, gaps, bar and client colours | historical | j3w1/1w3j@9841315 config/i3/config | Source of the 1px borders, 14/−2 gaps and the focused/urgent colour classes. Window-manager geometry is reference metrics, not a cross-application rule. |
| i3status colours — good, bad and degraded inside the same hue | historical | j3w1/1w3j@9841315 config/i3/i3status.conf | A historical status semantic (#FFA2A7 / #A3676B / #C81A1A). Superseded for new work by D-001. |
| dunst notification colours | historical | j3w1/1w3j@9841315 config/config/dunst/dunstrc | Origin of the toast roles and the critical pair #DC282E / #F9FAF9 (4.58:1). |
| dmenu colours | historical | j3w1/1w3j@9841315 config/dmenurc | Origin of the dark selection #630F0D. |
| gedit GtkSourceView scheme '1w3j' — a Solarized structure filled with reds | historical | j3w1/1w3j@9841315 config/config/gedit/styles/1w3j.xml | An early editor port by the owner. Not a supported download; values predate the token set. |
| IntelliJ colour scheme '1w3j theme' (2020–21, Material Darker base, red chrome and console) | historical | j3w1/1w3j@9841315 config/intellij/config/colors/1w3j-theme.icls | Only chrome, console, gutter and selection were recoloured; syntax stayed stock. Evidence that a JetBrains port has precedent, not a port. |
| tmux status colours (pywal-cache variant) | historical | j3w1/1w3j@9841315 config/tmux.conf.local | Uses the non-canonical #0C0A09 / #D09899 variant; catalogued to explain why those values are not tokens. |
| pywal-generated ramp (scripts/colors.sh) | historical | j3w1/1w3j@9841315 scripts/colors.sh | The machine-generated predecessor of the hand-tuned Xresources palette. Not normative. |
Do not read this page. Pin a tag or commit and fetch the exports at that revision; the site is confirmation only.
This document is for people and agents applying the theme to another project.
The contributor workflow for this repository is in AGENTS.md and is not
repeated here.
You are consuming design data and optional official implementations. The consumer project’s own instructions (its
AGENTS.md, CLAUDE.md, security, architecture and operational rules) always
take precedence over anything in this repository.
For compatible web apps, prefer the exact @j3w1/ui package. Complete copy
bundles are also supported. Read packages/ui/dist/index.json at your immutable
revision, then selected contracts/<id>.json and examples/<id>.json files.
The installed CLI prepares a bounded task:
j3w1-ui kit --components text-field,button,dialog --framework vue --mode package --out ./j3w1-task
Choose copy for complete source ownership or mapping for an incompatible
host. Native hosts require mapping mode. Kits include canonical contracts,
dependency closure and shared accessibility/identity rules; they do not declare
your application verified. Follow docs/ui-consumption.md for exact installation,
framework binding and runtime checks. npm publication is an owner release action;
use the release tarball and its published integrity metadata before publication.
Keep the existing lock and deviation workflow below for canonical mappings.
<rev> is a release tag (v0.1.0) or a full 40-character commit SHA. Never
main, never a branch name, never “latest”. Resolve a tag to its commit before
reading anything, and use the same <rev> in every URL below. Mixing
revisions is a contract violation.
GET https://api.github.com/repos/j3w1/theme/git/ref/tags/<tag> → object.sha
git ls-remote https://github.com/j3w1/theme refs/tags/<tag>
https://raw.githubusercontent.com/j3w1/theme/<rev>/theme.json
Take version, profiles, exports.canonicalForAgents and exports.digests.
https://raw.githubusercontent.com/j3w1/theme/<rev>/agents/consume.md
If the copy you are reading came from a different revision, fetch it again.
Use the pinned approved default profile. Pending roles in that profile use-and-report their decision IDs; this does not approve them. Proposed profiles are preview-only and blocked for delivery. Heritage profiles are historical-only. Deprecated or heritage roles are blocked for new approved-profile mappings. Consume roles within their documented scope, never primitives. Release numbering does not approve profiles or tokens.
The policy block is generated. Pending decision IDs are available in each
resolved token’s eligibility.decisionIds, including alias dependencies.
Report those IDs alongside any actual deviations; using an authorized pending
value is a disclosure, not an invented substitution. No replacement is implied
by a blocked action. A profile preview does not authorize delivered work.
https://raw.githubusercontent.com/j3w1/theme/<rev>/exports/theme.compact.md
https://raw.githubusercontent.com/j3w1/theme/<rev>/exports/components/<id>.json one per component you implement
https://raw.githubusercontent.com/j3w1/theme/<rev>/exports/tokens.resolved.json only for aliases the component JSON does not inline
https://raw.githubusercontent.com/j3w1/theme/<rev>/exports/digests.json to record what you used
Optional: exports/theme.full.md for the whole specification;
exports/tokens.css for a ready-made custom-property sheet. The website
https://j3w1.github.io/theme/ is for visual confirmation only. It is not a
source of values, and screenshots never override tokens.
Name the integration kind: css-vars, vuetify, tailwind,
jetbrains-icls, gtk-css, terminal-16 or other. List which specified
surfaces the target can express natively and which it cannot (for example a
framework with no read-only field style, or a sixteen-colour terminal that
cannot carry hover). Anything the target cannot express becomes a documented
deviation in step 7, never an invented approximation.
keyboard or aria section requires it and the
host allows it.outline: none without drawing the specified ring.Check computed values against the component JSON for every declared state.
Write the deviation report (below) into the pull request or commit body and
write theme.lock.json (below) at the consumer’s chosen path.
## j3w1/theme deviation report
theme: j3w1-theme <version> @ <commit> (<tag or "commit">) profile=<profile>
integration: <kind> (<host framework and version>)
implemented: <component-id> (<n>/<m> states); ...
unsupported surfaces:
- <spec surface or host surface> — <why the target cannot express it>
deviations:
| component | state / property | spec value | applied | reason | kind |
| --- | --- | --- | --- | --- | --- |
host rules that took precedence: <list or "none">
lock: <path to theme.lock.json>
kind is one of unsupported (the target cannot express it), substituted
(the nearest native equivalent, with a value taken from the token set, never
invented), omitted (the requester excluded it) or host-rule (a consumer
instruction won).
Minimal, committed next to the integration code, validated by
schemas/json/theme.lock.schema.json. Not a package manager: no graph, no
install, no auto-update. Updating the theme means changing ref, revision,
version and exports, re-running the consumer’s own checks and re-emitting
the deviations.
{
"schemaVersion": 1,
"theme": "j3w1-theme",
"version": "0.1.0",
"ref": "v0.1.0",
"revision": "0123456789abcdef0123456789abcdef01234567",
"profile": "default",
"integration": { "id": "my-app-css-vars", "version": "1", "kind": "css-vars" },
"resolvedAt": "2026-10-01T12:00:00Z",
"exports": {
"exports/theme.compact.md": "sha256-…",
"exports/tokens.resolved.json": "sha256-…",
"exports/components/text-field.json": "sha256-…"
},
"components": ["text-field"],
"deviations": [
{
"component": "text-field",
"target": "read-only",
"kind": "unsupported",
"specValue": "color.border.divider dotted bottom edge",
"applied": null,
"reason": "The host renders read-only as disabled; no separate style hook."
}
]
}
revision is always the full commit even when ref is a tag; exports keys
are the repository-relative paths listed in exports/digests.json for exactly
the files you read; deviations may be empty but must be present.
Once a consumer project points its own agent instructions at this document and records a pinned revision, the everyday request can be:
Implement this interface using j3w1/theme’s approved default profile.
Without that setup, say it in full:
Implement this interface using
j3w1/theme. Resolve and record a theme revision, followagents/consume.md, use the approved default profile, and read the relevant component specifications. Preserve this project’s behaviour and framework; report unsupported mappings and deviations.
Numbered design and governance decisions. A token or rule is approved only through an accepted entry here.
Statuses: proposed (awaiting the owner), accepted, rejected, superseded.
A token moves from proposed to approved only by an accepted decision referenced
in its $extensions["io.github.j3w1.theme"].approval.decision. Agents may open
proposed entries; only the owner changes a status.
| # | Title | Status | Date | Decided by |
|---|---|---|---|---|
| D-000 | Responsibility split: tokens, spec, ports, evidence | accepted | 2026-09-05 | owner (design package) |
| D-001 | Controlled three-hue extension for status, diagnostics and diffs | accepted | 2026-09-06 | owner |
| D-002 | v0.1.0 ships the full R1 component inventory | accepted | 2026-09-06 | owner |
| D-003 | Text-role corrections with new in-hue values | accepted | 2026-09-06 | owner |
| D-004 | Public repository; MIT code, CC BY 4.0 prose | accepted | 2026-09-06 | owner |
| D-005 | Default profile id is default | proposed | 2026-09-06 | — |
| D-006 | Focus-ring rule, including invalid + focus | proposed | 2026-09-06 | — |
| D-007 | border.control uses #A3676B | proposed | 2026-09-06 | — |
| D-008 | Fold site hairlines into border.divider; surface.sunken; unified overlay border | proposed | 2026-09-06 | — |
| D-009 | Density modes compact and comfortable as tokens | proposed | 2026-09-06 | — |
| D-010 | The site deploys from main | proposed | 2026-09-06 | — |
| D-011 | Reference screenshots are cropped before publication | proposed | 2026-09-06 | — |
| D-012 | Exports are committed and drift-checked | proposed | 2026-09-06 | — |
| D-015 | Clarify component mappings exposed by consumption acceptance | proposed | 2026-09-07 | — |
| D-016 | Pinned release comparison report and historical rendering | proposed | 2026-09-08 | — |
| D-017 | Private framework parity evidence | proposed | 2026-09-08 | — | | D-018 | Declared port capabilities and fresh import evidence | proposed | 2026-09-08 | — | | D-019 | One-way namespaced Figma Variables bridge | proposed | 2026-09-08 | — | | D-020 | Bounded local validation and recovery workflow | proposed | 2026-09-08 | — | | D-021 | Local form composition with versioned data | proposed | 2026-09-08 | — | | D-022 | Local visual selection before agent implementation distribution | proposed | 2026-09-08 | — | | D-023 | True Black / Rose canonical foundation | accepted | 2026-09-08 | owner (explicit selection of candidate I) | | D-024 | Distinguish links from body text and headings | accepted | 2026-09-08 | owner (explicit preview correction) | | D-025 | Theme interactive controls throughout the portal and demo | accepted | 2026-09-09 | owner (explicit native-control replacement request) | | D-026 | Primary action fill darkens off heritage ANSI 12 | accepted | 2026-09-11 | owner (explicit acceptance after visual review) | | D-027 | Recede the interactive chrome | accepted | 2026-09-11 | owner (explicit acceptance after visual review) | | D-028 | Merge gates and the deployment gate are different suites | accepted | 2026-09-11 | owner (explicit verification-split instruction) |
Status: accepted · 2026-09-05 (adopted from the design package ARCHITECTURE.md).
Decision. Token files own literal values and aliases. The specification owns meaning, permitted uses and constraints. Port mappings own native keys. Evidence records own what was actually tested. README, the site, and pictures summarise these authorities and never replace them. A disagreement between spec and tokens is a defect to resolve, not permission to choose the convenient answer. Images never override token values. Historical source is evidence of origin, not an override of the adopted accessible design.
Status: accepted · 2026-09-06.
Context. The observed identity is monochrome red/rose. Diagnostics, diffs and status information need differentiation that lightness alone cannot give (a 200-line diff cannot be scanned by glyph). The owner’s own PhpStorm scheme already uses conventional hues for these surfaces.
Decision. The default profile adds exactly three hues, desaturated and
warm-shifted: amber #C9973F (warning, modified), green #86A46F (success,
added), blue #7E9EBB (info), with dark tints #1F1A0C, #0F1A0E, #0F141C.
They are permitted only in the status, diagnostic, diff and (in the
extended overlay) code, terminal and chart groups. They are forbidden in
surface, text, border, interaction and action. Purple, cyan and magenta
are not added. Every status role also carries a mandatory non-colour channel
(glyph, underline pattern or border style). Syntax highlighting in default
stays monochrome; the extended overlay profile remains proposed.
Consequences. Measured contrast on canvas / surface / raised: amber 7.54 / 7.34 / 6.90, green 7.13 / 6.95 / 6.53, blue 7.08 / 6.90 / 6.48.
Status: accepted · 2026-09-06.
Decision. The first public release contains every component marked R1 in
the coverage inventory (about 47 across all nine families) plus four composed
specimens (settings panel, administrative form, filterable table, i3 window
frame), each specified and demonstrated; ten core components are also
tested (button, link, text-field, select, checkbox, radio-group, tabs, dialog,
menu, table). R2 items and the form-builder specimen are deferred to v0.2.
Status: accepted · 2026-09-06.
Context. #A3676B (the site’s --quiet) measures 4.45:1 on #0C0909 and
#7D1310 (--inactive) 1.86:1; the site already restricts them but still uses
#7D1310 for line numbers. The site’s suggested replacement #A8403A measures
only 3.27:1.
Decision. text.subtle = #AD7175 (5.10 / 4.96 / 4.67) for metadata,
comments and line numbers. text.disabled = #8A5559 (3.33 / 3.24 / 3.05;
exempt under WCAG 1.4.3, at least 3:1 by house policy). #A3676B remains in
service as border.control, icon.decorative, status.neutral.fill and
chart.series.4 (non-text, at least 3:1). #7D1310 remains only as
border.disabled, code.whitespace and minor grid lines, never as text. The
heritage-ansi profile keeps the original assignments flagged $deprecated.
Status: accepted · 2026-09-06.
Decision. The repository is public. Code, tokens, schemas, scripts, tests,
site source and generated JSON/CSS exports are MIT. Specification prose,
specimens and generated Markdown exports are CC BY 4.0. Screenshots under
references/ are all rights reserved, reference only. No fonts, no vendor
template material, no private business data are ever committed; CI scans for
them. See LICENSE.md.
Status: proposed.
Decision. The approved everyday composition is the profile default. A
later rename would be a MAJOR version change.
Status: proposed.
Decision. Controls and rows: outline: 1px dashed {color.interaction.focus.ring}
(#E53935) with outline-offset: -2px. Focusable containers (panes, windows,
dialogs, cards): outline: 2px solid {color.interaction.focus.ring-container}
(#FFA2A7) with outline-offset: -3px. On any filled surface (selection,
action fills, status fills) the ring takes that surface’s on-fill text colour,
because #E53935 on #911410 measures 2.15:1. For invalid + focus, where the
2px danger border is already red, the ring switches to the container colour
#FFA2A7 at outline-offset: -4px. :focus:not(:focus-visible) draws nothing.
In forced-colors mode the ring uses Highlight. Selection is always a fill and
focus is always a ring; neither borrows the other’s form.
Current clarification (D-015). The owner-directed component mappings below clarify the generic on-fill wording above. Primary buttons and checked controls retain their explicitly declared container-colour ring; destructive fills retain their declared on-fill text ring. Geometry and contrast floors are unchanged.
Status: proposed.
Decision. Form-control boundaries must reach 3:1 against their surface
(WCAG 1.4.11). #531310 (1.39) and #9E231F (2.56) both fail, so
border.control = #A3676B (4.45 / 4.33 / 4.07). The alternative #C81A1A
(3.42) is rejected as too loud for every input at rest.
Status: proposed.
Decision. The site’s six near-identical hairline hexes (#290D0C,
#24100F, #321110, #38100F, #190B0B, #2B0E0D) fold into one
border.divider = #2B0E0D. #0A0707 becomes surface.sunken. Every
floating layer (dialog, drawer, menu, popover, toast) uses a 1px
border.overlay = #E53935; the historical dunst frame #AE1914 survives only
in heritage-ansi.
Status: proposed.
Decision. Two density modes are tokens, applied with data-density on any
subtree. compact matches the site: control height 24px, row height 28px, UI
type 12/16, icon 16, list gap 2. comfortable: control 32px, row 36px, UI
type 13/18, icon 16, list gap 4. Both meet the 24×24 CSS-pixel target minimum;
compact requires 4px between adjacent targets.
Status: proposed.
Decision. The site deploys from main through GitHub Actions after the
full pipeline passes. The page header prints the version and build commit and
links to the latest tag; consumers pin tags or commits regardless.
Status: proposed.
Context. The four PhpStorm screenshots show a private project name in the window title bar.
Decision. Before any screenshot is committed under references/, the title
bar is cropped (or the name redacted) and the redaction is disclosed in the
provenance record. If the owner prefers, the screenshots stay out of the public
repository and are described in references/catalogue.json only.
Status: proposed.
Decision. exports/, site/src/styles/tokens.generated.css, the JSON
schemas under schemas/json/ and the README marker blocks are generated and
committed; npm run check fails CI on drift or orphan files. Outputs contain no
timestamps and no commit hashes so two runs are byte-identical. dist/ is never
committed.
Status: proposed (formal log status remains owner-controlled).
Context. Issue #4 found conflicting instructions to use and to prohibit pending roles in the approved default profile. Alternatives were to block pending roles until individually approved, or to use pinned values with disclosure.
Owner direction, 2026-09-07. During Phase 1 planning the owner selected “Use and report” and “Block new use” for deprecated roles, then authorized implementation. That explicit direction governs consumption without approving pending token values or changing D-005 through D-012.
Policy. An approved default profile’s pending roles may be used at a pinned revision with disclosure of their pending decision IDs, including dependencies. Proposed profiles remain preview-only and blocked for delivery. Heritage profiles are historical-only. Deprecated or heritage roles cannot be newly mapped in an approved profile; no substitute is implied. Ordinary observed/approved roles remain usable within their documented scope; primitives remain inspection-only. Release version, profile status, role status, deprecation and eligibility are separate facts. The generator emits the policy and eligibility metadata.
Status: proposed (formal log status remains owner-controlled).
Context. Issue #1 requests circular previews and a transparency checkerboard, which conflict with the general square geometry and no-gradient rules. Keeping square previews would remove the requested circle-to-square interaction.
Owner direction, 2026-09-07. The owner explicitly approved a narrow preview exception and authorized implementation. It is presentation of literal data, not a new palette, semantic role or component geometry rule.
Exception. Every visible CSS hex literal on the spec page receives one
automatically generated preview. Preserve the literal exactly. The preview is
16px including its theme-derived 1px boundary, circular at rest, and scales to
17.4px with radius zero when CSS :hover matches. The fast duration token and
ease-out govern transform and border-radius; reduced motion removes animation.
Alpha is composited over a checkerboard made from existing theme tokens.
Only this preview permits rounded geometry, patterned gradient backgrounds and
border-radius animation. Text, copying, keyboard order and no-JS access remain
intact. The parsed literal is data and never inferred from a token or screenshot.
Syntax specimens (literal data, not additional theme roles): #f00, #f008,
#ff0000, #ff000080, #000000, #ffffff. These examples also exercise
copy fidelity and transparent, dark and light previews in the browser suite.
The j3w1 specification’s inline color circles provide a pointer-following exact-value preview for actual mouse and pen pointer events. The popup follows the pointer’s position inside the circle with an 8px gap, flips when necessary and stays inside an 8px viewport gutter. It disappears immediately when the pointer leaves that circle, on an outside press, Escape, or loss of window focus. After page or nested-container scrolling, recheck the element under the last pointer position: keep the popup only if the same circle remains there. The popup cannot capture pointer input or keep itself open when hovered. It is a supplementary, non-interactive visual preview; token tables and the keyboard-accessible token inspector retain the persistent information and links. Long previews are bounded by the viewport. No new keyboard stop is introduced. Pointer tracking has no animation or easing, including under reduced motion. This owner-directed behavior extends the D-014 preview exception only; it does not change the general tooltip or focus contract.
Compatibility clarification. CSS :hover controls the circle-to-square
morph directly. Mouse and pen pointer events control the popup; touch and
unknown pointer types do not open it. Neither behavior is gated by hover
or any-hover media features: browsers can report no hover capability while
still delivering mouse input. Reduced motion continues to remove animation,
and circles and their literal text remain visible without JavaScript.
Status: proposed. The owner directed application of this resolution on 2026-09-07; its implementation preserves existing component appearance. The formal decision status remains owner-managed.
Context. Phase 2’s independently reviewed composed acceptance preserved three contradictions: ordinary confirmation versus destructive alert-dialog tone, the primary button’s pressed-border metadata versus its prose/CSS, and the global on-fill focus wording versus explicit component ring mappings. Phase 3 controls must not present an arbitrary interpretation as canonical.
Owner-directed resolution. Preserve current component appearance and all token
values. Ordinary confirmation uses the primary action tone; destructive tone
belongs to destructive actions, independent of the confirmation’s semantic
role. The primary button retains color.action.primary.border when pressed;
secondary and tertiary use color.border.active, and destructive retains its
declared destructive border. Correct the generic/default metadata accordingly.
Focusable controls retain their documented component ring mappings: primary
and checked/selected fills use color.interaction.focus.ring-container where
declared; destructive fills use their on-fill text role. Clarify the global
wording to refer to these explicit mappings, retaining ring geometry,
forced-colors behavior and every contrast floor. No profile or pending-token
approval is implied.
Alternatives. Enforce the generic prose everywhere, changing currently rendered pressed borders and focus colors; or retain the contradictions and explicitly mark affected workbench configurations unresolved. Neither is silently selected. Historical acceptance reports remain historical evidence.
Status: proposed. Issue #2 authorizes derived release diagnostics; formal decision status and all theme approvals remain owner-managed.
Context. Release comparison needs a versioned report without putting run metadata into deterministic canonical exports or interpreting missing historical fields using today’s rules.
Implementation scope. Schema version 1 records both immutable revisions,
profile and input digests, separate semantic change categories, explicit
dependency evidence, and unsupported historical inputs. Reports and historical
specimen renderings are build artifacts under dist/releases/; the canonical
exports retain their existing contract. Historical data is read from Git, never
executed as a build or imported as a module. The current shared specimen
renderer presents each side’s maintained markup and styles with a documented
renderer version and matched conditions. This is a reconstructed visual
reference, not evidence that an old site’s JavaScript or an external port ran.
Actual browser captures remain run evidence with recorded environment and
artifact hashes. Missing evidence stays not run. Renames require an explicit
mapping naming the two revisions; equal values do not establish a rename.
Alternatives. A raw file diff loses semantic categories and dependency evidence; executing historical applications expands the trust boundary and confounds renderer changes with theme changes. Neither is required for this bounded comparison. No design values, eligibility rules or verification statuses are changed by the report format.
Status: proposed. Issue #3 authorizes an independently authored private harness; formal approval statuses and the existing license boundaries are unchanged.
The harness consumes an operator-supplied licensed target and an explicit license review. It copies only declared template inputs into a new private fixture directory, uses synthetic examples and records the exact theme, template, framework, dependency and fixture identities. The original target and its application behavior remain outside this experiment’s write scope. Browser captures, copied vendor inputs and detailed results remain private. Missing prerequisites produce not-run output. A successful diagnostic run is not a verified port or a claim of complete visual parity.
Alternatives are a public vendor copy (incompatible with the current license boundary) or a generic framework mock presented as real template evidence (insufficient provenance). Neither is used.
Status: proposed. Issue #14 authorizes a derived mapping explorer; it does not create a port, change host capabilities or approve a profile.
Optional capabilities.json metadata refines each existing mapped/unmapped
role into mapped, inherited, unsupported, out-of-scope or not-implemented,
with an explicit surface and reason. Legacy unmapped reasons remain
unclassified; the generator never guesses a more specific state. Import
verification is separate from mapping state and declared manifest status.
Verified output requires a recorded real-import protocol bound to current
target metadata, canonical tokens, mappings, capabilities and artifact bytes.
Relevant changes make evidence stale; failed or missing checks cannot pass.
The same generated catalogue supplies summaries, drill-downs and task kits.
Private parity records are never automatically imported into the catalogue.
Alternatives are a hand-maintained support matrix or treating a successful parse as an application import. Both lose the evidence boundary.
Status: proposed. Issue #15 authorizes this bounded adapter; approval and eligibility statuses remain those of the canonical source.
An explicit pinned revision supplies colors and scalar spacing/radius values. Aliases retain their dependency structure; dependency-only primitives are hidden from property pickers. Unsupported types and non-approved profiles are reported. One collection and one mode avoid assuming paid mode capabilities. Dry-run is read-only, apply is explicit, and external import receipts identify owned IDs and their last observed values. Conflicts and manual edits stop an update or rollback. The bridge does not store ownership in unsupported plugin data, overwrite unrelated variables, or write values back to the repository.
Alternatives are manual duplicate maintenance, a paid-only REST workflow, or silently flattening aliases/composites. None meets the one-way evidence scope.
Status: proposed. Issue #16 authorizes one executable pattern using existing admin-form, field, alert and button rules, without adding palette roles.
The workflow has editing, invalid, review, busy and success stages. Validation runs on explicit submission, preserves values, and presents linked errors. Typing does not rewrite live announcements. Review can return to a field; confirm enters busy, and a separate local simulation-completion event enters success. No request is made. The existing workstation example’s reserved name and hostname are explicitly local fixture conflicts, not business policy. The pattern contract pins constituent component content digests and reports automated evidence separately from manual screen-reader acceptance.
Alternatives are a color-only invalid snapshot, validation on every keystroke, or treating a submit click as a successful save. They do not teach recovery.
Status: proposed. Issue #19 authorizes the deferred form-builder demonstration.
Five canonical field kinds can be added, edited, removed and reordered with buttons. Definitions, preview values and validation state are separate. JSON imports have closed fields, bounded size/counts and unique stable IDs; failed imports leave the current form unchanged. Content identity pins the supplying theme and component contracts. Downloads contain definitions, never entered values. The builder reuses D-020’s field rendering and validation behavior. It remains a draft reference component, not a backend platform or native port.
Alternatives are arbitrary HTML/schema execution, a conditional workflow engine, and a second visual field implementation. They exceed this task.
Status: proposed. Issue #37 and the owner’s implementation instruction authorize three local candidate systems before canonicalization. Formal design approval remains owner-managed.
The primary outcome is agent consumption of installable or copyable components
and compositions with complete dependencies and pinned machine contracts. The
future package, documentation portal and Vue application are consumers of the
same canonical design language. A local review harness first compares exactly
three explicit token overlays with Current, using shared specimen rendering and
an original Vue composition. Overlays, generated reports and review builds stay
ignored and are never public profiles or delivery artifacts. The existing alias
resolver and unrounded contrast engine evaluate candidates without changing
canonical values, historical ANSI slots or waivers. No approval is inferred from
numerical or browser evidence. The owner must select a concrete candidate before
foundation freeze, mass productization or deployment. See
docs/phase6-visual-review.md for commands, boundaries and continuation.
Alternatives are productizing the old palette before visual selection, publishing unapproved preview profiles, or offering incomplete appearance-only snippets as consumable components. None satisfies the authorized sequence and consumption goal.
Status: accepted. The owner explicitly selected the reviewed candidate I: “use the True Black / Rose theme and make it permanent for j3w1/theme”, and authorized continuing the remaining Phase 6 implementation.
The approved change is confined to seven surface assignments: canvas, sunken,
input, code and terminal use the existing pure black primitive; chrome uses
#090707; default panels use #100c0c. Add the two selected values as
color.primitive.ink.14 and color.primitive.ink.28, preserving the luminance
ordering and all historical primitives. Keep every other color assignment,
including rose/white foreground, red actions, focus, borders, selections,
status and chart roles, exactly as reviewed. Raised/overlay surfaces remain
#241010; alternate chrome remains #1c0a09. Preserve the complete historical
profile by explicitly retaining its previous surface assignments.
The rejected A/B/C, revised D/E/F and final G/H/I reviews are historical local evidence. Their temporary inputs are not public profiles. Selection approves these specific surface values and assignments, not unrelated pending decisions, new colors or accessibility waivers. This is the foundation for the Phase 6 package, copy distributions, static portal and Vue showcase. Existing consumers retain their immutable pins; the new surface contract ships in version 1.0.0, following the repository’s major-version rule for approved value changes.
Superseded in part, and only in part. Three later accepted decisions each
replace one or more of the role assignments above, and leave everything else
here standing: D-024 replaces text.link; D-026 replaces
action.primary.bg; D-027 replaces surface.raised,
interaction.selection.bg and text.placeholder. Every other value and rule
recorded in this decision — the seven surface assignments, the two new
primitives, the luminance ordering, the remaining foreground, red action,
focus, border, selection, status and chart roles, overlay #241010, alternate
chrome #1c0a09, and the retained historical profile — stands as accepted.
This decision keeps its accepted status; it is not superseded as a whole.
Status: accepted. During the implementation preview the owner requested links
that are more distinguishable from normal text, using different colors. This
is an explicit correction to the foreground-role retention in D-023, limited
to links. Map color.text.link to the existing strong red primitive #f73f35,
retain the persistent red underline, and retain near-white hover text on the
strong dark-red hover fill. Visited links keep the link color and underline;
current-page indicators and selected navigation retain their explicit on-fill
roles. Body text and heading colors remain the True Black / Rose hierarchy.
The alternative of bright-rose links still shares the heading color and gives too little separation in the reviewed dashboard. Blue/purple links would introduce an unrelated hue. No new primitive, status hue, contrast exception or focus rule is introduced. Preserve the old link assignment in the historical profile. This refinement ships with the same unreleased 1.0.0 major change.
Status: accepted. After reviewing native blue selections in the work board and
agent-kit picker, the owner explicitly instructed: “replace mostly all native
components like <select> we must use all j3w1 themed”. The owner also requested a
properly proportioned command-search field. This records that authorization.
The maintained select implementation now renders its single-choice popup and multiple-choice list in theme-owned DOM using the existing selection, surface, text, border and focus roles. The native select remains the form-value and constraint source and the no-JavaScript fallback. This supersedes the select specification’s prohibition on replacing the host popup for web implementations. Native ports retain their host-specific mappings. The custom interface follows the select-only combobox and multiple listbox keyboard patterns, preserves labels, disabled options/fieldsets, native input/change events, defaults and form reset, and documents its actual tested scope.
Provide the same supported enhancement for existing application controls, use it throughout the portal and Vue demo, and include it in consumption documentation and copy closures. Theme ordinary input, button, checkbox, radio, range and file control chrome as well. Browser-owned system dialogs and explicit no-JavaScript fallbacks remain native; they are not styled by unsupported platform hacks.
Date inputs use a theme-owned text editor and calendar; time inputs use explicit text entry and theme-owned step buttons. Their original native inputs retain constraints and form values. This supersedes the earlier date/time component specifications’ host-popup descriptions for enhanced web implementations.
The alternative of advisory option CSS leaves the demonstrated blue host selection unchanged. One-off Vue/portal dropdowns duplicate behavior and leave agents with inconsistent components. New hues, focus rules, accessibility waivers and license changes are not authorized by this correction. The additive enhancement and corrected component behavior ship in 1.1.0; existing 1.0.0 pins remain immutable. The command field fills its dialog width below its label.
Status: accepted. The owner asked for a slightly darker fill on primary
actions such as the portal’s Explore components button, and accepted this
entry as implemented after reviewing it.
color.action.primary.bg aliased color.primitive.red.300, which is also
color.primitive.ansi.12 and color.terminal.ansi.12. schemas/roles.mjs
fixes every color.primitive.ansi.<i> to its heritage value byte for byte in
every profile, so darkening that primitive is not available: it would fail
validation and silently repaint the terminal’s bright red.
The role therefore aliases color.primitive.red.200 (#7d1310), a value
already in the palette, rather than minting a new colour. Label contrast rises
from 8.17:1 to 9.28:1 and the focus ring over the fill from 4.90:1 to 5.57:1;
both were already passing. The state ladder stays ordered — pressed #630f0d
below the fill, hover #911410 above it.
The declared pair primary fill against the panel is a recorded decorative
waiver. It measured 2.07:1 before this change and measures 1.83:1 after it,
further from 3:1. It is listed as decorative because the label identifies the
button; this decision does not change that reasoning, and no waiver is added
or widened.
This supersedes part of D-023, and only this part. action.primary.bg
(#871f19) was one of the assignments that decision kept “exactly as
reviewed” under its red actions clause; this entry replaces that single role
and nothing else in D-023. The primitive is untouched, so
color.primitive.red.300, color.primitive.ansi.12 and
color.terminal.ansi.12 all still carry the heritage value.
Alternatives: mint a new primitive at the exact requested value, which adds a
colour to the palette for a difference of about one step and no measurable
gain; or leave the fill and darken only the portal button, which would put a
value in a stylesheet that is not in tokens/, against the confirmation-only
rule in theme.json.
Status: accepted. The owner found the open list, its selected row, the placeholder text and the link hover fill all louder than the content they sit against, and asked for a darker treatment across the set, then accepted this entry as implemented after reviewing it.
Four roles move, all onto values already in the palette; no colour is minted.
surface.raised takes ink.30 (#160b0b) instead of ink.52. An open list
now reads as depth rather than as a lighter panel, and stays distinct from
surface.default (#100c0c).interaction.selection.bg takes red.100 (#531310) instead of red.350.
Text on the fill improves from 7.92:1 to 12.47:1.text.placeholder takes rose.350 (#a3676b) instead of rose.600,
measuring 4.71:1 on surface.input — darker, and still clear of the 4.5:1
floor without a waiver.interaction.hover.bg rather than interaction.hover.bg-strong.
Menu items keep the strong fill, so the two remain distinguishable.The selection fill was never what carried selection: at #911410 it measured
2.00:1 against the raised surface, below the 3:1 non-text minimum, and the
recessed value measures less again. Selection is carried by the 2px
border.selected-indicator and the check glyph, which is what
spec/foundations.md requires when a state would otherwise be colour-only.
Both are unchanged. The full report still shows 954 pairs, 17 waived and none
failing; no waiver is added or widened.
This supersedes part of D-023, and only this part. surface.raised
(#241010) and interaction.selection.bg (#911410) were among the values
the owner selected in that accepted decision, and text.placeholder
(#bd787d) fell under the same decision’s instruction to keep the foreground
roles exactly as reviewed. This entry replaces those three role assignments
and nothing else in D-023: every other surface, foreground, focus, border,
selection, status and chart role it approved stands as accepted, as does its
historical-profile retention. tests/foundation-selection.test.js guards the
set by name and now records the recessed values; every other surface in it is
still the D-023 selection, untouched.
The darker fill also settles the ring on a selected row. D-006 recolours the
ring on filled surfaces because #e53935 measured 2.15:1 on the old
#911410; on #531310 it measures 3.38:1, past the 3:1 minimum, so a
selected row now takes the ordinary 1px dashed control ring instead of the 2px
solid container ring. That is the only fill this reaches: the ring still
measures 2.52:1 on action.primary.bg and 1.13:1 on status.danger.fill, and
both keep their recoloured ring. spec/foundations.md carried the stale 2.15:1
figure as its stated reason and has been corrected; D-006’s own record is left
as written, since it describes what was true when it was opened.
Alternatives: keep the fills and darken only the components the owner pointed at, which would put presentation in a stylesheet rather than in the roles and leave selection meaning two different things; or introduce new primitives at intermediate values, which adds colours to the palette for differences the existing steps already cover.
Status: accepted. The owner instructed that the exhaustive verification move off the pull-request critical path: merges gate on a few minutes of checks, the full matrix gates deployment, and a local full run stays recommended rather than mandatory.
Context. Accepting D-026 and D-027 changed prose, token metadata and generated exports. It cost twenty-nine and a half minutes of continuous integration before it could merge. Measured on that run: sources zero point six minutes, build zero point seven, packed consumers three point three, and the browser matrix twenty-four point nine. The browser job was eighty-four per cent of the wait, and the repository is public, so the runners are free and the whole cost is latency for whoever is waiting.
Decision. A pull request merges on validate, build and a new smoke
job. The packed-consumer and browser suites run on main before deployment
and on demand through the workflow dispatch. The full local loop is
npm run test:all; it is available and recommended before pushing, and it is
not mandatory. Nothing verifies that it was run.
The full matrix is a deployment gate. It always was: a browser failure has
never prevented a merge from being correct, it prevents the site from being
published, and deploy still depends on it. Requiring it before merge treated
a publication gate as a correctness gate and charged every change for the
difference. What the merge gate keeps is what a source test cannot see: that
the built site loads under its base path, renders from the tokens, and takes a
keyboard.
smoke is a named subset and reports as one. It runs the desktop project
only, over the page, portal, button and text-field specs, and it reports with
the plain list reporter rather than the evidence reporter. That last point is
the rule, not an optimisation: the evidence reporter records unselected tests
as not run, so a filtered run that reached it would publish an evidence matrix
that looked complete and was not. A passing smoke is a signal that the build
is sound. It is never a coverage claim, and the published matrix continues to
come only from the full suite.
The packed-consumer suite moves with the browser suite and cannot be split
from it. The browser job downloads the consumer evidence artifact and
republishes it against the deployed package, and
scripts/ui-verification-report.mjs requires the complete protocol on all
three engines because a filtered run must not pass as a full one. Both are on
the deployment path together.
No mandatory local gate. A receipt produced by the machine that ran the
tests proves only that something wrote a receipt. It cannot distinguish a real
run from a forgotten one that was filled in afterwards, so it would have
caught carelessness and nothing else, at the price of machinery that looked
like proof. Local verification is a practice here, recorded in AGENTS.md,
not a gate.
Alternatives: shard the full suite across runners, which keeps complete coverage before merge at about six minutes and costs nothing on a public repository, but is bounded below by the largest single spec file and gives each shard its own partial evidence file to merge; or keep the packed consumers on the merge path for three more minutes, which buys cross-engine coverage of the distributed package rather than of this site. Sharding is the one worth revisiting if complete pre-merge coverage ever matters more than the minutes.