j3w1 UI Theme Spec
Identity, role tables, component index, accessibility and portability rules. Full state matrices, contrast pairs and decisions have dedicated static pages.
Source revision 9afef8564142e41458748895d16b4df161a67d66
Identity
What the j3w1 theme is, where it came from, and the five rules that keep a port recognisable.
True-black canvas, editor and terminal interiors (#000000), neutral near-black panels and raised layers, rose interface text and terminal/agent prose, and distinct syntax and status hues (D-041). Red marks identity, focus, selection and danger; ordinary application content and terminal prose use rose; white is reserved for document headings, exceptional highlights and contrast-critical roles. Keep 1px square borders, zero radii and one monospace family for chrome, reading and code. Darkness is the canvas; readable content is the priority.
Five rules keep an implementation recognisable:
- Preserve the true-black base, rose application and terminal text hierarchy and restrained red identity. Use semantic colours to distinguish code, additions, warnings and information.
- Use the role assigned to a token, never the token whose colour happens to look closest.
- Keep content, selection, keyboard focus, diagnostics and changed text distinguishable from one another. Selection is a fill; focus is a ring.
- Nothing is rounded, blurred, glowing or gradient-filled. Elevation is a 1px border and, for modals, a backdrop.
- Preserve the host application’s behaviour, keyboard handling and accessibility settings. Geometry and typography limits of a host are documented deviations, not reasons to rewrite it.
Where it came from
The palette began on a Manjaro i3 workstation (j3w1/1w3j): a pywal ramp
from a red wallpaper, frozen and tuned slot by slot in config/Xresources.
The i3 configuration gave the rest: 1px borders, 14/−2 gaps, tabbed layouts,
dunst toasts framed in #ae1914, dmenu selections in #630f0d. Its web
version, j3w1/j3w1.github.io, named the roles this specification uses and
wrote the first contrast contract. Both are pinned in
references/sources.json. This repository is the authority: later changes in
either source are proposals, not automatic updates.
What “modern” means here
Clear structure, responsive layout, strong keyboard and screen-reader support, exact states, pleasant controls. Not pill buttons, glass panels, gradients, neon glows, a Material-style redesign, or red on every surface; the site draws such misreadings as labelled non-examples.
One exception (D-014): the generated preview beside each visible CSS hex literal on the spec page. It adds no palette, role or geometry rule, and text, copying, keyboard order and no-JS access stay intact.
- A 16px circle with its theme-derived 1px boundary; alpha shows over a checkerboard of theme tokens.
- On CSS
:hoverit becomes a 17.4px square (fast duration, ease-out on transform and border-radius; none under reduced motion). - Mouse and pen pointers also open a non-interactive popup with the exact
value; touch and unknown pointers do not. CSS
:hoveralone drives both, never thehoverorany-hovermedia features, which can report no hover despite a mouse. - The popup follows the pointer inside the circle at an 8px gap, flips when needed and keeps an 8px viewport gutter. Tracking has no animation.
- It closes when the pointer leaves the circle, on an outside press, Escape, loss of window focus, and on page or nested scrolling unless the circle is still under the last pointer position. It never captures input, holds itself open or adds a keyboard stop; token tables and the token inspector keep the persistent information.
Profiles
default— the approved everyday composition: true-black interiors, neutral application layers, rose application text and terminal/agent prose, red interaction accents, and semantic green, amber, blue, violet and cyan for coding and terminal content (D-041).code.syntax.*is coloured by default;code.hued.*remains the compatible semantic alias family for ports that already consume it.heritage-ansi— the sixteen Xresources slots and their historical assignments, exact, including those that fail the contrast floor. For archives and fidelity, never new interface text.extended— the earlier proposed, unapproved overlay for syntax, terminal and chart colours. D-041 updates the default directly; it does not promote this profile.
Naming
Roles are color.<group>.<role>, from a closed group list: surface, text,
border, interaction, action, status, code, diagnostic, diff,
terminal, chart, icon. Primitives are color.primitive.<family>.<step>,
the step rising with luminance; roles alias primitives, and primitives carry
provenance. A CSS custom property is the path with dashes for dots:
color.interaction.focus.ring → --color-interaction-focus-ring.
Foundations
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 is normative for the default profile.
Surfaces
surface.desktop and surface.canvas are true black (#000000). Readable
content sits on the canvas or on:
default(#0d0707) for panels and cards;raised(#140b0b) for sticky headers, menus and popovers;overlay(#1c1010) for dialogs and drawers;accent(#3d0c0a) for the user’s own content set apart from its neighbours, such as outgoing chat messages; secondary text on it usestext.muted, nevertext.subtle. Red links cap its brightness: at this hue it stays at or below#3f0c0a(D-035);sunken(#000000) for reading and detail panes;inputfor form-control fills;- true-black
chromeand warm rose-blackchrome-altfor window frames and bars.
D-041 keeps ordinary application layers warm rose-black, without a cool blue cast; surface.accent remains a
bounded red message surface, not a tint for every panel.
The layers differ only slightly, so controls also carry border.control.
Never tint photos, video, document content or user-defined data colours.
Text ladder
| Role | Use |
|---|---|
text.prose | long-form reading; aliases rose text.default |
text.heading | white document/content titles and H1–H6 |
text.highlight | deliberate exceptional white highlights |
text.bright | brighter rose UI labels |
text.default | rose interface and body text |
text.muted | secondary text; the darkest colour permitted for ordinary chrome text |
text.subtle | metadata, comments, line numbers |
text.disabled | disabled controls only (exempt under WCAG 1.4.3; at least 3:1 by policy) |
text.accent | restrained red accent text on canvas and default surfaces |
text.accent-strong | red emphasis suitable for raised surfaces |
text.link | underlined links; hover uses text.link-hover on interaction.hover.bg |
D-041 uses rose for ordinary application text and terminal/agent prose
(text.default, #e99499); terminal prose and prompts use bright rose
(terminal.fg, #ffa2a7) for clearer small text. White is limited to document
headings, exceptional highlights and contrast-critical roles. Exact values
and ratios per surface are in the token and contrast reports. Metadata and
comments people need to read use text.subtle, never a decorative border or
historical ANSI primitive.
Borders
Every border is 1px (border.width.default). 2px (border.width.emphasis) is
only for the selected indicator bar, the sticky-header rule and the invalid
border. The quote rule, which marks a block rather than bounding a control, is
the one wider edge, at twice the emphasis width.
border.divider(#482b2b) separates rows and sections.border.default(#482b2b) is decorative, never the only visible edge of a control.border.control(#c81a1a) is the edge of every form control at rest.border.active(#e53935) marks the focused or active control.border.overlay(#c81a1a) 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.
Interaction states
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 the ordinary ring (rule 1; D-027) | both visible at once |
| text selected, host keeps the text colour | translucent interaction.text-selection.tint (D-036), which also suits hosts that paint the fill over their content; where the host can recolour selected text, interaction.text-selection.bg with interaction.text-selection.text | the host’s selection handles |
| 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 (rule 5) |
| 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 focus.ring-contrast 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:
- Selection is a fill; focus is a ring. Neither borrows the other’s form. The ring must reach 3:1 on whatever it sits over:
#e53935falls below it onaction.primary.bgandstatus.danger.fill, so the ring recolours on those fills; it meets it oninteraction.selection.bg(D-027), so a selected row keeps the ordinary control ring. - Focus ring (D-006; component mapping clarification D-015). Controls and rows:
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 usefocus.ring-contrast(the control ring ininteraction.focus.ring-container, D-040) 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 usesHighlight. A host focus indicator is never removed without this replacement. - Never colour-only. Every state and every status carries a second channel: a glyph (
✕ ! ✓ i ·), a border width, an underline pattern, or an ARIA state. Charts carry pattern fills. - Hover changes fills and borders, never text colour alone, and only on hover-capable pointers.
- Disabled never uses opacity. Opacity leaks the background and breaks contrast accounting.
- Contrast floor. Text ≥ 4.5:1 and control boundaries, rings and meaningful graphics ≥ 3:1 against the actual background of the state, measured after compositing translucent colours.
- Motion. Durations are 80 / 120 / 150 ms at most, on opacity, transform and background only; under
prefers-reduced-motion: reduceevery duration is 0.01 ms, nothing slides or shimmers, and no behaviour waits ontransitionend.
Actions
Four button tones:
primary, the only filled button at rest:action.primary.bg(#7d1310) withaction.primary.text(bright rose; D-041).secondary, an outline:border.control, texttext.bright.tertiary, a ghost or icon button:text.default, hoverinteraction.hover.bg.destructive, an outline inaction.destructive.text(#f73f35) that fills on hover withaction.destructive.hover-bg(#dc282e) and#f9faf9text. A filled variant exists for confirm dialogs.
Destructive actions also need a confirmation step or an undo; the colour is not the safeguard.
Status, diagnostics and diffs (D-001)
D-041 updates the palette while preserving the D-001 semantic roles.
Five statuses: red danger, amber warning, green success, blue info,
and grey neutral. Each has text, fill, on-fill, tint and border
roles, a required glyph (✕ ! ✓ i ·) and its own underline or border pattern
(wavy, dashed, solid, dotted, double). Use the explicit on-fill role on a
status fill, not ordinary body text. Coding and terminal roles also have
violet and cyan; these distinguish syntax and named terminal colours rather
than tinting ordinary application chrome. Every use follows its role and
measures contrast on the actual surface.
Diagnostics:
- error: wavy
#e53935underline,#f73f35text,#e53935stripe,#2b0e0dline background; - warning: wavy amber;
- info: dotted blue;
- hint: dotted
text.subtle; - unused:
text.mutedwith a dotted underline.
Diffs:
- added:
#0f1a0eline background, green gutter mark and+; word-level additions as a 2px green underline (no fill, so text contrast is untouched); - removed:
#2b0e0dbackground, red gutter mark and−; word-level removals underlined and struck through; - modified:
#1f1a0cbackground, amber gutter mark and~; - conflicts: a double
#f73f35border.
Always keep textual markers and existing status icons.
Code and terminal
The surface and interior roles remain distinct in scope, but D-041 makes
surface.code, surface.terminal, code.bg, code.gutter-bg, terminal.bg
and default ANSI 0 all true black (#000000). This replaces the earlier
#0c0909 interior assignment without changing historical ANSI primitives.
Editor surfaces:
code.current-lineuses the neutral default layer;code.line-numberuses the readable subtle tier; active numbers and the caret step up to the normal and bright text tiers;code.selection-bgis a subdued red selection fill;code.search-match-bgmarks matches, with the current match inverted bycode.search-current-bgandcode.search-current-text;code.bracket-matchis a 1px accent border;code.indent-guideis a neutral separator andcode.whitespaceis a decorative neutral mark.
Syntax in default is semantic: violet keywords, green strings, amber
numbers/constants/attributes/escapes, blue functions, cyan types/properties,
coral tags/invalid text, rose variables/operators, muted punctuation and
subtle italic comments. Headings use the bright text tier. The existing
code.hued.* roles resolve to the corresponding semantic syntax assignments;
ports need no separate palette. Invalid text takes a wavy underline and
deprecated text a line-through. Languages inherit these roles; a
language-specific override needs a written reason. Selection and diff fills
must be checked with the syntax foregrounds they retain.
Terminal slots:
heritage-ansicarries the sixteenXresourcesslots exactly.color.primitive.ansi.*remains byte-for-byte historical; the heritage profile explicitly restores its historical slot and background assignments.defaultuses the semantic sixteen (D-041): black, red, green, amber, blue, violet, cyan and rose, followed by their bright variants. Slots 7 and 15 both use bright rose#ffa2a7for readable terminal text. Slot 0 is the true-black background; slots 1–15, including neutral bright black, target at least 4.5:1 on it. Slots 2 and 10 remain green so programs using ANSI additions can be read correctly.extendedremains a proposed overlay, not an approved alternative needed to obtain coloured syntax or terminal slots.
Programs choose the slots and may emit 256-colour or true-colour values that
bypass them. A theme cannot repair host dimming or independently recolour
text that shares a host theme key with a background. Port guides record
those limits; Claude Code’s shared word-highlight and edit-summary keys are
one example. Their dedicated diff.added.shared-word and
diff.removed.shared-word roles balance summary-on-black and ordinary
rose-on-highlight at about 3:1. These roles apply only where a host combines
those two uses; they do not replace standard diff emphasis or relax the
ordinary-text contrast floor. Syntax foregrounds require separate host review.
Typography
One family: SauceCodePro NFM (Source Code Pro in 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.heading, caption
11/16 only with text.muted or lighter. D-037: display 32/40 for
page heroes and metric figures and title 24/32 for page titles above h1;
components keep h1–h3. Hosts keep the size the user chose; tiny UI text is
a metric of the reference implementation, not a rule.
Spacing and density (D-009)
Spacing steps: 0, 1, 2, 4, 8, 12, 16, 24, 32, 48 px. compact is the default
density (D-038): it applies wherever no data-density is set, in components,
the site and copied bundles. Set data-density="comfortable" on any subtree to
opt in to the larger mode:
| Mode | Control | Row | Type | Horizontal padding | Gaps | Use |
|---|---|---|---|---|---|---|
compact | 24px | 28px | ui-sm | 8px | 2px | matches the workstation |
comfortable | 32px | 36px | ui-md | 12px | 4px | everyday applications |
Both meet the 24×24 CSS-pixel target minimum; compact needs 4px between
neighbouring targets. Table rows always use the comfortable row height (a data
row is content, and compact rows are harder to scan); controls inside a row
follow the page density. The site’s 3px i3 gap, 14/−2 window gaps and 28px bar
are window-manager geometry, not theme tokens.
Elevation and layering
No shadows, except shadow.floating (0 6px 22px rgb(0 0 0 / 55%)) on the i3
floating-window specimen. A floating layer is a 1px border.overlay on
surface.raised; a modal adds 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 cursor’s other side instead of crossing a viewport edge, and keeps 8px from every edge.
- It moves again on pointer move, scroll and resize.
- A popup with nothing to operate takes no pointer events; one with a control or link stays reachable and open while the pointer is inside it.
- Either kind closes when the pointer leaves both the trigger and the popup; the reachable kind waits 300 ms first, so the pointer can cross between them, and stays while keyboard focus is on its trigger or inside it.
Opened from the keyboard, the same popup anchors to its trigger: below it, or above when there is no room below. This is a placement rule only; what opens a popup and what kind it is belong to the component’s contract.
Icons
SVG line icons on a 16px grid, 1.5px stroke, stroke: currentColor,
fill: none, sizes 12 / 16 / 20. No icon font is needed; Nerd Font glyphs
appear only in terminal and heritage specimens. Icon-only controls carry
aria-label and a tooltip. icon.decorative (#986f72) is for glyphs that
mean nothing on their own.
Layout
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.
Typography semantics (D-033)
All content titles and H1–H6 use text.heading; size, weight and spacing carry
the hierarchy. Chrome and window titles use text.default or text.bright;
body and long-form prose use text.default and text.prose. Bold inherits the
current semantic colour, including inside headings, muted text, links and
selections, and never requests white by itself. D-041 keeps ordinary application text and brighter UI labels rose,
with white reserved for document headings, exceptional highlights and
contrast-critical roles. Terminal and agent prose uses rose terminal.fg; the semantic hierarchy
and inherited bold rule remain. Selection and on-fill roles are explicit and
must be measured against their actual backgrounds. The historical D-033
migration audit is docs/rose-typography-migration.md.
Tokens
Eligibility: use within the role's scope; use-and-report records its pending decision; blocked excludes delivery; historical-only preserves heritage; preview-only excludes delivery. Approval and consumption eligibility stay separate.
All profile values, primitives and documented usage →
Surfaces
Text
Text ladder on each surface
Measured ratios; ordinary text needs 4.5:1, disabled text is exempt but held to 3:1.
Selection is a fill; focus is a ring
The four combinations every list, table and tab strip must show. Focus rings on fills take the on-fill text colour.
Borders and radii
Status set
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, in the opt-in code.hued syntax roles (D-030) and in the proposed overlay.
Terminal slots by profile
heritage-ansi carries the sixteen Xresources slots exactly; the approved default profile carries the readable heritage sixteen (D-029; slot 6 coral, D-032); 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 | #000000 | #0c0909 | #0c0909proposed |
ansi.1 | #f07178 | #c81a1a | #e53935proposed |
ansi.2 | #82d9a0 | #bd787d | #86a46fproposed |
ansi.3 | #e5c07b | #d4868b | #c9973fproposed |
ansi.4 | #82b4ff | #8c1212 | #7e9ebbproposed |
ansi.5 | #c7a0e8 | #f73f35 | #b37175proposed |
ansi.6 | #78d4d4 | #9e474a | #bd787dproposed |
ansi.7 | #ffa2a7 | #ffa2a7 | #e99499proposed |
ansi.8 | #bd787d | #7d1310 | #ad7175proposed |
ansi.9 | #ff9095 | #ab1612 | #f73f35proposed |
ansi.10 | #a2edb8 | #ad2721 | #7fbf7aproposed |
ansi.11 | #f2d49b | #b37175 | #d6a24bproposed |
ansi.12 | #a4caff | #871f19 | #9ab3c9proposed |
ansi.13 | #dec0f4 | #e82132 | #d4868bproposed |
ansi.14 | #a0e7e7 | #e0292f | #ffa2a7proposed |
ansi.15 | #ffa2a7 | #a3676b | #f4eeeeproposed |
Borders
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.border.divider | --color-border-divider | #482b2b | approveduse |
color.border.default | --color-border-default | #482b2b | approveduse |
color.border.strong | --color-border-strong | #9e231f | observeduse |
color.border.control | --color-border-control | #c81a1a | approveduse |
color.border.active | --color-border-active | #e53935 | observeduse |
color.border.overlay | --color-border-overlay | #c81a1a | approveduse |
color.border.disabled | --color-border-disabled | #482b2b | approveduse |
color.border.selected-indicator | --color-border-selected-indicator | #e53935 | observeduse |
color.border.selected-indicator-inactive | --color-border-selected-indicator-inactive | #9e231f | observeduse |
Interaction
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.interaction.focus.ring | --color-interaction-focus-ring | #e53935 | observeduse |
color.interaction.focus.ring-container | --color-interaction-focus-ring-container | #ffa2a7 | observeduse |
color.interaction.selection.bg | --color-interaction-selection-bg | #531310 | approveduse |
color.interaction.selection.text | --color-interaction-selection-text | #f4eeee | observeduse |
color.interaction.selection.inactive-bg | --color-interaction-selection-inactive-bg | #420f0c | observeduse |
color.interaction.selection.inactive-text | --color-interaction-selection-inactive-text | #e99499 | approveduse |
color.interaction.text-selection.bg | --color-interaction-text-selection-bg | #420f0c | observeduse |
color.interaction.text-selection.text | --color-interaction-text-selection-text | #f4eeee | observeduse |
color.interaction.text-selection.tint | --color-interaction-text-selection-tint | rgb(145 20 16 / 50%) | approveduse |
color.interaction.hover.bg | --color-interaction-hover-bg | #1c1010 | approveduse |
color.interaction.hover.bg-strong | --color-interaction-hover-bg-strong | #1c1010 | approveduse |
color.interaction.pressed.bg | --color-interaction-pressed-bg | #420f0c | observeduse |
color.interaction.disabled.bg | --color-interaction-disabled-bg | #0d0707 | approveduse |
color.interaction.marquee | --color-interaction-marquee | rgb(145 20 16 / 12%) | observeduse |
color.interaction.drop-target | --color-interaction-drop-target | #e53935 | observeduse |
color.interaction.scrollbar.track | --color-interaction-scrollbar-track | #000000 | approveduse |
color.interaction.scrollbar.thumb | --color-interaction-scrollbar-thumb | #986f72 | approveduse |
color.interaction.scrollbar.thumb-hover | --color-interaction-scrollbar-thumb-hover | #d4868b | approveduse |
Actions
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.action.primary.bg | --color-action-primary-bg | #7d1310 | approveduse |
color.action.primary.text | --color-action-primary-text | #ffa2a7 | approveduse |
color.action.primary.hover-bg | --color-action-primary-hover-bg | #911410 | observeduse |
color.action.primary.pressed-bg | --color-action-primary-pressed-bg | #630f0d | observeduse |
color.action.primary.border | --color-action-primary-border | #9e231f | observeduse |
color.action.secondary.bg | --color-action-secondary-bg | rgb(0 0 0 / 0%) | observeduse |
color.action.secondary.text | --color-action-secondary-text | #ffa2a7 | approveduse |
color.action.secondary.border | --color-action-secondary-border | #986f72 | approveduse |
color.action.secondary.hover-bg | --color-action-secondary-hover-bg | #630f0d | observeduse |
color.action.secondary.pressed-bg | --color-action-secondary-pressed-bg | #420f0c | observeduse |
color.action.tertiary.text | --color-action-tertiary-text | #e99499 | approveduse |
color.action.tertiary.hover-bg | --color-action-tertiary-hover-bg | #1c0a09 | observeduse |
color.action.destructive.text | --color-action-destructive-text | #f07178 | observeduse |
color.action.destructive.border | --color-action-destructive-border | #e53935 | observeduse |
color.action.destructive.hover-bg | --color-action-destructive-hover-bg | #dc282e | observeduse |
color.action.destructive.hover-text | --color-action-destructive-hover-text | #f9faf9 | observeduse |
color.action.destructive.pressed-bg | --color-action-destructive-pressed-bg | #ae1914 | observeduse |
color.action.destructive.filled-bg | --color-action-destructive-filled-bg | #dc282e | observeduse |
color.action.destructive.filled-text | --color-action-destructive-filled-text | #f9faf9 | observeduse |
Status
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.status.danger.text | --color-status-danger-text | #f07178 | observeduse |
color.status.danger.fill | --color-status-danger-fill | #dc282e | observeduse |
color.status.danger.on-fill | --color-status-danger-on-fill | #f9faf9 | observeduse |
color.status.danger.tint | --color-status-danger-tint | #2b0e0d | observeduse |
color.status.danger.border | --color-status-danger-border | #e53935 | observeduse |
color.status.warning.text | --color-status-warning-text | #e5c07b | approveduse |
color.status.warning.fill | --color-status-warning-fill | #e5c07b | approveduse |
color.status.warning.on-fill | --color-status-warning-on-fill | #000000 | approveduse |
color.status.warning.tint | --color-status-warning-tint | #1f1a0c | approveduse |
color.status.warning.border | --color-status-warning-border | #e5c07b | approveduse |
color.status.success.text | --color-status-success-text | #82d9a0 | approveduse |
color.status.success.fill | --color-status-success-fill | #82d9a0 | approveduse |
color.status.success.on-fill | --color-status-success-on-fill | #000000 | approveduse |
color.status.success.tint | --color-status-success-tint | #0f1a0e | approveduse |
color.status.success.border | --color-status-success-border | #82d9a0 | approveduse |
color.status.info.text | --color-status-info-text | #82b4ff | approveduse |
color.status.info.fill | --color-status-info-fill | #82b4ff | approveduse |
color.status.info.on-fill | --color-status-info-on-fill | #000000 | approveduse |
color.status.info.tint | --color-status-info-tint | #0f141c | approveduse |
color.status.info.border | --color-status-info-border | #82b4ff | approveduse |
color.status.neutral.text | --color-status-neutral-text | #d4868b | approveduse |
color.status.neutral.fill | --color-status-neutral-fill | #986f72 | approveduse |
color.status.neutral.on-fill | --color-status-neutral-on-fill | #000000 | observeduse |
color.status.neutral.tint | --color-status-neutral-tint | #140b0b | approveduse |
color.status.neutral.border | --color-status-neutral-border | #986f72 | approveduse |
Code
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.code.bg | --color-code-bg | #000000 | approveduse |
color.code.current-line | --color-code-current-line | #0d0707 | approveduse |
color.code.gutter-bg | --color-code-gutter-bg | #000000 | approveduse |
color.code.line-number | --color-code-line-number | #bd787d | approveduse |
color.code.line-number-active | --color-code-line-number-active | #e99499 | approveduse |
color.code.gutter-rule | --color-code-gutter-rule | #482b2b | approveduse |
color.code.caret | --color-code-caret | #ffa2a7 | approveduse |
color.code.selection-bg | --color-code-selection-bg | #420f0c | observeduse |
color.code.search-match-bg | --color-code-search-match-bg | #630f0d | observeduse |
color.code.search-current-bg | --color-code-search-current-bg | #ffa2a7 | observeduse |
color.code.search-current-text | --color-code-search-current-text | #000000 | approveduse |
color.code.bracket-match | --color-code-bracket-match | #e53935 | observeduse |
color.code.indent-guide | --color-code-indent-guide | #482b2b | approveduse |
color.code.whitespace | --color-code-whitespace | #986f72 | approveduse |
color.code.syntax.keyword | --color-code-syntax-keyword | #c7a0e8 | approveduse |
color.code.syntax.string | --color-code-syntax-string | #82d9a0 | approveduse |
color.code.syntax.comment | --color-code-syntax-comment | #bd787d | approveduse |
color.code.syntax.number | --color-code-syntax-number | #e5c07b | approveduse |
color.code.syntax.constant | --color-code-syntax-constant | #e5c07b | approveduse |
color.code.syntax.function | --color-code-syntax-function | #82b4ff | approveduse |
color.code.syntax.variable | --color-code-syntax-variable | #e99499 | approveduse |
color.code.syntax.type | --color-code-syntax-type | #78d4d4 | approveduse |
color.code.syntax.operator | --color-code-syntax-operator | #e99499 | approveduse |
color.code.syntax.punctuation | --color-code-syntax-punctuation | #d4868b | approveduse |
color.code.syntax.property | --color-code-syntax-property | #78d4d4 | approveduse |
color.code.syntax.heading | --color-code-syntax-heading | #f4eeee | approveduse |
color.code.syntax.tag | --color-code-syntax-tag | #f07178 | approveduse |
color.code.syntax.attribute | --color-code-syntax-attribute | #e5c07b | approveduse |
color.code.syntax.escape | --color-code-syntax-escape | #e5c07b | approveduse |
color.code.syntax.invalid | --color-code-syntax-invalid | #f07178 | approveduse |
color.code.syntax.deprecated | --color-code-syntax-deprecated | #bd787d | approveduse |
color.code.hued.string | --color-code-hued-string | #82d9a0 | approveduse |
color.code.hued.number | --color-code-hued-number | #e5c07b | approveduse |
color.code.hued.constant | --color-code-hued-constant | #e5c07b | approveduse |
color.code.hued.function | --color-code-hued-function | #82b4ff | approveduse |
color.code.hued.attribute | --color-code-hued-attribute | #e5c07b | approveduse |
color.code.hued.escape | --color-code-hued-escape | #e5c07b | approveduse |
color.code.hued.type | --color-code-hued-type | #78d4d4 | approveduse |
color.code.hued.property | --color-code-hued-property | #78d4d4 | approveduse |
color.code.hued.operator | --color-code-hued-operator | #e99499 | approveduse |
Diagnostics
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.diagnostic.error.underline | --color-diagnostic-error-underline | #e53935 | observeduse |
color.diagnostic.error.text | --color-diagnostic-error-text | #f73f35 | observeduse |
color.diagnostic.error.stripe | --color-diagnostic-error-stripe | #e53935 | observeduse |
color.diagnostic.error.bg | --color-diagnostic-error-bg | #2b0e0d | observeduse |
color.diagnostic.warning.underline | --color-diagnostic-warning-underline | #e5c07b | approveduse |
color.diagnostic.warning.text | --color-diagnostic-warning-text | #e5c07b | approveduse |
color.diagnostic.warning.stripe | --color-diagnostic-warning-stripe | #e5c07b | approveduse |
color.diagnostic.info.underline | --color-diagnostic-info-underline | #82b4ff | approveduse |
color.diagnostic.info.text | --color-diagnostic-info-text | #82b4ff | approveduse |
color.diagnostic.hint.underline | --color-diagnostic-hint-underline | #bd787d | approveduse |
color.diagnostic.unused.text | --color-diagnostic-unused-text | #d4868b | approveduse |
Diffs
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.diff.added.bg | --color-diff-added-bg | #0f1a0e | approveduse |
color.diff.added.gutter | --color-diff-added-gutter | #82d9a0 | approveduse |
color.diff.added.emphasis | --color-diff-added-emphasis | #82d9a0 | approveduse |
color.diff.added.shared-word | --color-diff-added-shared-word | #2c6540 | approveduse |
color.diff.removed.bg | --color-diff-removed-bg | #2b0e0d | observeduse |
color.diff.removed.gutter | --color-diff-removed-gutter | #e53935 | observeduse |
color.diff.removed.emphasis | --color-diff-removed-emphasis | #e53935 | observeduse |
color.diff.removed.shared-word | --color-diff-removed-shared-word | #953d46 | approveduse |
color.diff.modified.bg | --color-diff-modified-bg | #1f1a0c | approveduse |
color.diff.modified.gutter | --color-diff-modified-gutter | #e5c07b | approveduse |
color.diff.conflict-border | --color-diff-conflict-border | #f73f35 | observeduse |
color.diff.header-bg | --color-diff-header-bg | #241010 | observeduse |
Terminal
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.terminal.bg | --color-terminal-bg | #000000 | approveduse |
color.terminal.fg | --color-terminal-fg | #ffa2a7 | approveduse |
color.terminal.cursor | --color-terminal-cursor | #ffa2a7 | approveduse |
color.terminal.selection-bg | --color-terminal-selection-bg | #420f0c | observeduse |
color.terminal.selection-text | --color-terminal-selection-text | #f4eeee | observeduse |
color.terminal.prompt-bg | --color-terminal-prompt-bg | #000000 | approveduse |
color.terminal.prompt-text | --color-terminal-prompt-text | #ffa2a7 | approveduse |
color.terminal.ansi.0 | --color-terminal-ansi-0 | #000000 | approveduse |
color.terminal.ansi.1 | --color-terminal-ansi-1 | #f07178 | approveduse |
color.terminal.ansi.2 | --color-terminal-ansi-2 | #82d9a0 | approveduse |
color.terminal.ansi.3 | --color-terminal-ansi-3 | #e5c07b | approveduse |
color.terminal.ansi.4 | --color-terminal-ansi-4 | #82b4ff | approveduse |
color.terminal.ansi.5 | --color-terminal-ansi-5 | #c7a0e8 | approveduse |
color.terminal.ansi.6 | --color-terminal-ansi-6 | #78d4d4 | approveduse |
color.terminal.ansi.7 | --color-terminal-ansi-7 | #ffa2a7 | approveduse |
color.terminal.ansi.8 | --color-terminal-ansi-8 | #bd787d | approveduse |
color.terminal.ansi.9 | --color-terminal-ansi-9 | #ff9095 | approveduse |
color.terminal.ansi.10 | --color-terminal-ansi-10 | #a2edb8 | approveduse |
color.terminal.ansi.11 | --color-terminal-ansi-11 | #f2d49b | approveduse |
color.terminal.ansi.12 | --color-terminal-ansi-12 | #a4caff | approveduse |
color.terminal.ansi.13 | --color-terminal-ansi-13 | #dec0f4 | approveduse |
color.terminal.ansi.14 | --color-terminal-ansi-14 | #a0e7e7 | approveduse |
color.terminal.ansi.15 | --color-terminal-ansi-15 | #ffa2a7 | approveduse |
Charts
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.chart.series-1 | --color-chart-series-1 | #ffa2a7 | observeduse |
color.chart.series-2 | --color-chart-series-2 | #f73f35 | observeduse |
color.chart.series-3 | --color-chart-series-3 | #bd787d | observeduse |
color.chart.series-4 | --color-chart-series-4 | #a3676b | observeduse |
color.chart.axis | --color-chart-axis | #9e231f | observeduse |
color.chart.grid | --color-chart-grid | #2b0e0d | observeduse |
color.chart.label | --color-chart-label | #d4868b | approveduse |
Icons
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
color.icon.default | --color-icon-default | #e99499 | approveduse |
color.icon.decorative | --color-icon-decorative | #986f72 | approveduse |
Spacing
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
space.0 | --space-0 | 0px | observeduse |
space.1 | --space-1 | 1px | observeduse |
space.2 | --space-2 | 2px | observeduse |
space.4 | --space-4 | 4px | observeduse |
space.8 | --space-8 | 8px | observeduse |
space.12 | --space-12 | 12px | observeduse |
space.16 | --space-16 | 16px | observeduse |
space.24 | --space-24 | 24px | observeduse |
space.32 | --space-32 | 32px | observeduse |
space.48 | --space-48 | 48px | observeduse |
Density
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
density.compact.control-height | --density-compact-control-height | 24px | observeduse |
density.compact.row-height | --density-compact-row-height | 28px | observeduse |
density.compact.control-padding-x | --density-compact-control-padding-x | 8px | observeduse |
density.compact.icon | --density-compact-icon | 12px | observeduse |
density.compact.gap | --density-compact-gap | 2px | observeduse |
density.comfortable.control-height | --density-comfortable-control-height | 32px | observeduse |
density.comfortable.row-height | --density-comfortable-row-height | 36px | observeduse |
density.comfortable.control-padding-x | --density-comfortable-control-padding-x | 12px | observeduse |
density.comfortable.icon | --density-comfortable-icon | 16px | observeduse |
density.comfortable.gap | --density-comfortable-gap | 4px | observeduse |
Border widths
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
border.width.default | --border-width-default | 1px | observeduse |
border.width.emphasis | --border-width-emphasis | 2px | observeduse |
Radius
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
radius.none | --radius-none | 0px | observeduse |
Typography
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
font.family.mono | --font-family-mono | "SauceCodePro NFM", "Source Code Pro", "Cascadia Mono", "Consolas", "Liberation Mono", "DejaVu Sans Mono", monospace | observeduse |
font.weight.regular | --font-weight-regular | 400 | observeduse |
font.weight.bold | --font-weight-bold | 700 | observeduse |
font.size.ui-sm | --font-size-ui-sm | 12px | observeduse |
font.size.ui-md | --font-size-ui-md | 13px | observeduse |
font.size.ui-lg | --font-size-ui-lg | 14px | observeduse |
font.size.reading | --font-size-reading | 15px | observeduse |
font.size.code | --font-size-code | 13px | observeduse |
font.size.terminal | --font-size-terminal | 13px | observeduse |
font.size.display | --font-size-display | 32px | approveduse |
font.size.title | --font-size-title | 24px | approveduse |
font.size.h1 | --font-size-h1 | 20px | observeduse |
font.size.h2 | --font-size-h2 | 16px | observeduse |
font.size.h3 | --font-size-h3 | 13px | observeduse |
font.size.caption | --font-size-caption | 11px | observeduse |
font.line-height.ui-sm | --font-line-height-ui-sm | 16px | observeduse |
font.line-height.ui-md | --font-line-height-ui-md | 18px | observeduse |
font.line-height.ui-lg | --font-line-height-ui-lg | 20px | observeduse |
font.line-height.reading | --font-line-height-reading | 24px | observeduse |
font.line-height.code | --font-line-height-code | 19px | observeduse |
font.line-height.terminal | --font-line-height-terminal | 19px | observeduse |
font.line-height.display | --font-line-height-display | 40px | approveduse |
font.line-height.title | --font-line-height-title | 32px | approveduse |
font.line-height.h1 | --font-line-height-h1 | 28px | observeduse |
font.line-height.h2 | --font-line-height-h2 | 24px | observeduse |
font.line-height.h3 | --font-line-height-h3 | 18px | observeduse |
font.line-height.caption | --font-line-height-caption | 16px | observeduse |
font.letter-spacing.terminal | --font-letter-spacing-terminal | -0.5px | observeduse |
Focus
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
focus.ring | --focus-ring | 1px dashed #e53935 | observeduse |
focus.ring-container | --focus-ring-container | 2px solid #ffa2a7 | observeduse |
focus.ring-contrast | --focus-ring-contrast | 1px dashed #ffa2a7 | approveduse |
focus.offset | --focus-offset | -2px | observeduse |
focus.offset-container | --focus-offset-container | -3px | observeduse |
focus.offset-invalid | --focus-offset-invalid | -4px | observeduse |
Motion
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
motion.duration.fast | --motion-duration-fast | 80ms | observeduse |
motion.duration.base | --motion-duration-base | 120ms | observeduse |
motion.duration.slow | --motion-duration-slow | 150ms | observeduse |
Elevation
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
shadow.floating | --shadow-floating | 0px 6px 22px 0px rgb(0 0 0 / 55%) | observeduse |
Layout
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
layout.measure | --layout-measure | 72 | observeduse |
layout.content-max | --layout-content-max | 680px | observeduse |
layout.app-max | --layout-app-max | 1440px | observeduse |
Breakpoints
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
breakpoint.xs | --breakpoint-xs | 320px | observeduse |
breakpoint.sm | --breakpoint-sm | 600px | observeduse |
breakpoint.md | --breakpoint-md | 900px | observeduse |
breakpoint.lg | --breakpoint-lg | 1280px | observeduse |
Icon sizes
| Role | CSS variable | Default value | Status / eligibility |
|---|---|---|---|
icon.size.sm | --icon-size-sm | 12px | observeduse |
icon.size.md | --icon-size-md | 16px | observeduse |
icon.size.lg | --icon-size-lg | 20px | observeduse |
icon.stroke | --icon-stroke | 1.5px | observeduse |
Stacking
Primitives (inspection only; consume roles, not primitives)
ink.0ink.14ink.15ink.20ink.25ink.28ink.30ink.40ink.52ink.60red.40red.50red.100red.150red.200red.250red.300red.350red.400red.450red.500red.550red.650red.750red.760red.770red.800red.810red.850red.860rose.300rose.320rose.350rose.450rose.550rose.600rose.650rose.700rose.800coral.700paper.90paper.100amber.500amber.600amber.tintgreen.500green.600green.tintblue.500blue.600blue.tintalpha.transparentalpha.black-55alpha.black-65alpha.red-6alpha.red-12alpha.red-50ansi.0ansi.1ansi.2ansi.3ansi.4ansi.5ansi.6ansi.7ansi.8ansi.9ansi.10ansi.11ansi.12ansi.13ansi.14ansi.15ansi-readable.1ansi-readable.4ansi-readable.8ansi-readable.9ansi-readable.10ansi-readable.12ansi-readable.13ansi-readable.14ansi-readable.15modern.panelmodern.raisedmodern.overlaymodern.hovermodern.dividermodern.controlmodern.disabledmodern.subtlemodern.mutedmodern.textmodern.brightmodern.redmodern.greenmodern.ambermodern.bluemodern.violetmodern.cyanmodern.bright-redmodern.bright-greenmodern.bright-ambermodern.bright-bluemodern.bright-violetmodern.bright-cyanmodern.diff-shared-greenmodern.diff-shared-red
Actions
Buttons, links, icon buttons, button groups, segmented controls, destructive treatment.
Button button
The 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.
Variants: Primary, Secondary, Tertiary, Destructive. States: default, hover, focus-visible, active, disabled, loading.
Icon button icon-button
A square ghost button whose only content is a 16px line icon; it always carries an accessible name and, as a toggle, aria-pressed.
Variants: Action, Toggle. States: default, hover, focus-visible, active, disabled, toggled, toggled+focus-visible.
Link link
The native anchor for navigation; always underlined in the link-underline role, hover fills with the hover surface, the current page carries a 2px indicator bar.
Variants: Inline, Standalone, Current. States: default, hover, focus-visible, visited, current.
Button group button-group
Related independent native actions with individual tab stops.
Variants: Default. States: default, focus-visible, disabled.
Segmented control segmented-control
A short exclusive choice implemented with labelled native radios.
Variants: Default. States: default, checked, focus-visible, disabled.
Basic forms
Text, password, number, search, textarea, select, checkbox, radio, switch, range, fieldsets, labels, help text, validation.
Field field
The 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.
Variants: Default. States: default, required, invalid, disabled.
Text field text-field
Single-line free-text entry, covering every native input type that renders as a line box; search, number and password are variants, not separate components.
Variants: Text, Password with reveal, Search with clear, Number with stepper, With prefix and suffix. States: default, hover, focus-visible, placeholder-shown, filled, required, invalid, invalid+focus-visible, invalid+hover, disabled, disabled+filled, read-only, read-only+focus-visible, loading.
Textarea textarea
Multi-line free-text entry in the same box as the text field; resizes vertically only and can show a character count.
Variants: Default, With character count. States: default, hover, focus-visible, placeholder-shown, filled, required, invalid, invalid+focus-visible, disabled, read-only.
Select select
A themed single-choice combobox or multiple-choice list backed by a native select for forms and the no-JavaScript fallback.
Variants: Single, With option groups, Multiple. States: default, hover, focus-visible, required, invalid, invalid+focus-visible, disabled.
Checkbox checkbox
The 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.
Variants: Single, Group. States: default, hover, focus-visible, checked, mixed, checked+focus-visible, checked+disabled, disabled, invalid, required.
Radio group radio-group
A 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.
Variants: Vertical, Horizontal. States: default, hover, focus-visible, checked, checked+focus-visible, disabled, invalid, required.
Switch switch
An 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.
Variants: Default, With labels. States: default, hover, focus-visible, checked, checked+focus-visible, disabled, checked+disabled.
Fieldset fieldset
The native fieldset that groups related fields under a legend inside a 1px divider border; disabling it disables everything inside.
Variants: Default. States: default, disabled.
Number field number-field
The 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.
Variants: Default, With unit. States: default, hover, focus-visible, invalid, invalid+focus-visible, disabled, read-only.
Search field search-field
The 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.
Variants: Default, With shortcut hint. States: default, hover, focus-visible, placeholder-shown, filled, loading, disabled, no-results.
Range range
A bounded native numeric slider with visible output.
Variants: Default. States: default, focus-visible, disabled.
Advanced forms
Combobox, multiselect, date and time, file selection, repeaters, wizard, bounded form-builder specimen.
Combobox combobox
A text input that filters a listbox of suggestions; the APG combobox with list autocomplete, rendered with the list open.
Variants: Flat list, Grouped options, No results. States: default, hover, focus-visible, open, closed, selected, invalid, invalid+focus-visible, disabled, no-results.
Date picker date-picker
A themed date editor and calendar, alone or as a from/to pair, backed by native form values and constraints.
Variants: Single date, From / to. States: default, hover, focus-visible, invalid, invalid+focus-visible, disabled, read-only.
Time picker time-picker
A themed time editor with explicit step buttons, backed by native time values and constraints.
Variants: Time. States: default, hover, focus-visible, invalid, invalid+focus-visible, disabled.
File input file-input
A 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.
Variants: Button, Drop zone, With chosen files. States: default, hover, focus-visible, drop-target, invalid, disabled, loading.
Wizard wizard
A multi-step form with a stepper header, the current panel and back/next actions; native buttons with aria-current="step".
Variants: Numbered steps, Glyph markers. States: default, hover, focus-visible, current, disabled, invalid, busy.
Form builder form-builder
A bounded reference editor for five canonical field kinds, with keyboard ordering, local preview and explicit JSON definition import and export.
Variants: Reference definition. States: default, empty, invalid, disabled.
Multiple selection multiselect
A searchable native checkbox group that preserves all selected form values.
Variants: Default. States: default, checked, focus-visible, disabled, no-results.
Repeater repeater
Repeat native field rows with stable identities and keyboard ordering.
Variants: Default. States: default, empty, focus-visible, disabled.
Information display
Cards, lists, badges, chips, avatars, descriptions, tables, timelines, simple charts.
Card card
A 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.
Variants: Default, Link card, Selectable. States: default, hover, focus-visible, selected, selected+focus-visible.
List list
A 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.
Variants: Single, Multi, With metadata. States: default, hover, focus-visible, selected, selected+focus-visible, selected+container-inactive, disabled.
Badge badge
A 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.
Variants: Status, Count, Outline. States: default, empty.
Chip chip
A 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.
Variants: Filter, Removable, Static. States: default, hover, focus-visible, selected, selected+focus-visible, disabled.
Table table
A native data table with a header rule, row dividers, hover and selection fills, sortable headers with a glyph, and static empty and loading rows.
Variants: Default, Sticky header, Numeric, With caption. States: default, hover, selected, selected+focus-visible, selected+container-inactive, sorted, empty, loading.
Data table data-table
A 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.
Variants: Default, Selectable, Resizable. States: default, hover, focus-visible, selected, selected+focus-visible, sorted, mixed, empty, loading.
Description list description-list
Semantic terms and values that wrap into a readable single column.
Variants: Default. States: default, stacked.
Avatar avatar
A square entity image with a named text fallback.
Variants: Default. States: default, loading, error.
Timeline timeline
Chronological events with explicit dates, descriptions and current state.
Variants: Default. States: default, current.
Chart chart
A one-series numeric chart with an always-visible complete data table.
Variants: Default. States: default, empty, loading.
Keyboard key kbd
A literal key or short keyboard sequence rendered as native kbd text.
Variants: Default. States: default.
Feedback and overlays
Alerts, toasts, progress, loading, skeletons, tooltips, popovers, dialogs, drawers, empty and error states.
Alert alert
An inline status message with a mandatory glyph on the status tint behind a 1px status border; role alert for danger, status otherwise.
Variants: Danger, Warning, Success, Info, Neutral, Dismissible. States: default, hover, focus-visible.
Toast toast
A 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.
Variants: Plain, Five statuses stacked, With action. States: default, hover, focus-visible, stacked, reduced-motion.
Progress progress
A native progress element with a label and value; determinate fills with the primary colour, indeterminate shows static 45° stripes, never an animation.
Variants: Determinate, Indeterminate, With detail line. States: default, busy, error, reduced-motion.
Loading indicator loading-indicator
A static ⋯ glyph with a worded label inside an aria-busy container; inline next to text or as a centred block. There is no spinner.
Variants: Inline, Block. States: default, reduced-motion.
Tooltip tooltip
A 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.
Variants: Description, With shortcut. States: default, hover, focus-visible, open, closed.
Dialog dialog
A native dialog on the overlay surface behind a 1px overlay border and the backdrop; modal when shown, with trapped focus and Escape to close.
Variants: Confirm, Alert dialog, Form. States: default, open, closed, focus-trapped, hover, focus-visible, reduced-motion.
Drawer drawer
A 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.
Variants: Start edge, End edge. States: default, open, closed, focus-trapped, reduced-motion.
Empty state empty-state
What 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.
Variants: With action, Without action. States: default.
Skeleton skeleton
Static layout placeholders with a named loading status.
Variants: Default. States: default, loading, reduced-motion.
Popover popover
A non-modal supporting panel with light dismissal and ordinary tab order.
Variants: Default. States: default, open, closed, focus-visible.
Error state error-state
A specific failure explanation with an explicit recovery request.
Variants: Default. States: default, error, loading, disabled, focus-visible.
Developer interfaces
Editor syntax, gutters, caret, selections, search, diagnostics, diffs, terminal text and the historical ANSI slots.
Code editor code-editor
The editor surface, gutter, caret, current line, selection, bracket match and monochrome syntax roles that every code-editing or code-viewing host maps onto.
Variants: JavaScript, Markup, Folded region. States: default, focus-visible, selected, selected+container-inactive, current, read-only.
Diagnostics diagnostics
Inline 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.
Variants: Inline markers, Problems panel. States: default, hover, focus-visible, selected, selected+focus-visible, error, empty.
Diff view diff-view
Unified 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.
Variants: Unified, Split, Word-level, Merge conflict. States: default, hover, selected, focus-visible, expanded, collapsed.
Terminal terminal
Terminal text with the readable heritage sixteen ANSI slots (D-029, D-032), the cursor, the selection and the six text attributes; a transcript, the slot grid and the attributes.
Variants: Transcript, ANSI grid, Attributes. States: default, focus-visible, selected, busy, current.
Editor search editor-search
Literal search and match navigation over escaped read-only source text.
Variants: Default. States: default, filled, no-results, focus-visible.
Composed specimens
Settings panel, administrative form, filterable table, i3-inspired window frame; file browser, compact dashboard and form builder later.
Settings panel settings-panel
A 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.
Variants: Two columns, Narrow (360px). States: default, focus-visible, invalid, busy.
Admin form admin-form
A 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.
Variants: Identity step, Review step. States: default, invalid, busy, disabled.
Filterable table filterable-table
A 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.
Variants: Eight of 43, Nothing matches, Loading. States: default, hover, focus-visible, selected, no-results.
i3 window frame i3-window-frame
The 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.
Variants: Tiled, Tabbed, Floating. States: default, focus-visible, selected, container-inactive, error.
File browser file-browser
Location navigation and a filterable native file table with bounded host actions.
Variants: Default. States: default, selected, empty, no-results, focus-visible.
Compact dashboard compact-dashboard
Named operational metrics, a complete chart and an actionable native records table.
Variants: Default. States: default, loading, empty, focus-visible.
Stress fixtures and non-examples
Form composition and recovery
A local reference workflow preserves values through validation, review and an explicitly simulated save.
Scope
The validation and recovery pattern
composes admin-form, text-field, textarea, select, checkbox, alert, button and
table under proposed D-020. It is a reference composition, not an approved
profile or a persistence system; the component rules still apply. Its JSON
contract (spec/patterns/validation-recovery.json) records the components’
content digests, fields, fixtures, transitions and limits.
Interaction
- Submit to validate. Errors appear together in a focused summary linking to the affected fields; every entered value is kept, including unrelated notes and choices.
- Correct and submit again to reach review. Editing never announces errors per keystroke nor silently removes the last submitted summary. Review offers an explicit way back to editing.
- Confirming the review enters busy: controls are disabled and a pending message is shown.
- Only the separate Complete simulation action reaches success; a submit click alone never does.
Busy, disabled, invalid and success each mean something different. Reset clears in-memory values and errors.
The ws-07 conflict on Name and Hostname and the lowercase hostname rule (letters, digits, hyphen-separated segments) are local admin-form fixtures, not server checks; no request, account, storage or database is involved. Without JavaScript the page keeps static normal, invalid, busy and success examples.
Evidence and limits
Automated protocols cover keyboard submission, summary navigation, correction, preservation, review, busy, success, narrow layout and no-JS content; a test that exists is not a passing run (see the verification report). No manual screen-reader pass is claimed. Submit-time feedback follows the GOV.UK validation pattern.
Figma Variables bridge
A one-way, pinned Variables API bridge with explicit diffs and external ownership receipts.
Supported mapping
This independently written bridge maps sRGB colors to COLOR variables and scalar px spacing and radius to FLOAT variables.
- Aliases stay 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 turned into extra modes. Typography, shadows, composite borders and other unsupported types are reported, not flattened. Figma does not import this JSON natively; the adapter uses the Variables Plugin API. Fonts are neither imported nor distributed: install the families the pinned typography tokens name.
Generate a pinned payload
In a verified checkout, run:
node scripts/figma-bridge.mjs --ref FULL_COMMIT_OR_RELEASE_TAG > payload.json
The default sample is color.surface.default, color.text.default, space.4 and
radius.none, plus their two alias dependencies; --roles takes an explicit
comma-separated list instead. The command reads Git objects at the resolved
full revision, never moving branch bytes or the working tree, and records
source and payload digests, the mapping version, unsupported items and
excluded profiles.
Reviewed import route
Use the Figma connector’s use_figma Variables API route, in a new design
draft or an existing collection whose current receipt you hold, after reading
the connector’s current skill/API instructions. The 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
exportkeyword), then return the awaited call:return await importFigmaVariables(figma, { payload, receipt: null, documentKey, action: "dry-run" }); -
Pass the locally generated payload and the exact target file key as explicit inputs.
-
Inspect the returned diff. Dry-run only reads.
-
Call again with
action: "apply", only for the reviewed change. -
Keep the whole returned receipt outside the document and the repository. Every later dry-run and apply must pass that receipt and the same document key.
An exact re-import changes nothing; updates show before/after definitions. Owned variables left out of a payload are kept and reported, never silently removed. Receipts are trusted local ownership records, not importable third-party configuration.
Limits of this route:
- at most eight variables, dependencies included, per namespace; one collection; one default mode — small enough for one incremental connector operation. Larger payloads fail before anything changes;
- apply and rollback need the connector’s atomic script execution; this module is not a standalone plugin transaction manager;
- no team publishing permission or paid plan capability is assumed;
- permission, mode and API errors stay failures.
Rollback and conflicts
To undo the most recent apply, call with action: "rollback" and the current
receipt.
- Existing variables get back their exact recorded values and metadata.
- New variables are removed in reverse dependency order.
- The result returns the previous receipt. Keep it for another rollback or a later import.
- A newly created collection is removed only when it holds no unrelated variables.
- Rollback refuses to remove a variable that a kept variable in any local collection still references. Check any canvas bindings you added after the import before you roll back; this adapter does not audit every canvas node.
Before any change, the adapter compares every owned ID, value, description, scope, collection identity and mode against the receipt; missing, detached or hand-edited variables stop apply and rollback. It never adopts a matching name without a receipt. Resolve a conflict deliberately in Figma or in a separate draft; never discard ownership records to force an overwrite.
Actual verification status
A real six-variable sample was imported through the connector into a new design draft, with Starter/Full access and Plugin API 1.0.0.
- Alias IDs matched. Spacing and radius matched exactly.
- Color channels matched the exact float32 form of the source channels, with no tolerance.
- An exact re-import changed no IDs.
- A separately previewed spacing addition was applied and rolled back.
- A deliberate manual edit was rejected without being overwritten; the test edit was then explicitly restored.
Private receipts stay outside public artifacts; the issue evidence records the exact candidate and source revision. Source protocols also cover detached IDs, value updates, first rollback, unrelated variables, invalid payloads and extra modes against an explicit API fixture; these are fixture results, not account-capability claims. No component kit, native port, library publication, manual design review or automatic Figma-to-token sync is claimed. D-019 remains proposed.
Accessibility
Contrast targets, focus and keyboard contracts, reduced motion, forced colours, and what an automated pass does and does not prove.
What is claimed
WCAG 2.2 criteria are measurable design targets for the theme’s roles. A green contrast report and a clean automated scan show that the specified colours and the reference markup meet them; they are not a conformance claim for any adopting application. Keyboard operation, focus management, labels, reflow and manual review stay the consumer’s job. The theme’s own limits are under Limitations.
Contrast
- Ordinary text: at least 4.5:1 against the actual background of its state, measured on resolved sRGB values after compositing translucent colours.
- Large text (18.66px bold or 24px regular and above): at least 3:1.
- Control boundaries, focus rings, meaningful icons and chart marks: at least 3:1 (WCAG 1.4.11).
- Disabled text is exempt under WCAG 1.4.3; house policy still needs 3:1
(
text.disabled), below the ordinary-text floor by design. - Never round a value into a pass. The report keeps the unrounded ratio and
shows two decimals:
4.4996reads 4.50 and fails. - Heritage values that fail (
#a3676bas text,#7d1310anywhere as text) are flagged$deprecatedin theheritage-ansiprofile, never shown as ordinary passes.
exports/contrast.md lists every declared pair (the global pairs in
spec/contrast.json and every component’s stateTokens and contrast[]
entries) with its computed ratio. Waived pairs carry their reason; a waiver is
valid only for decorative graphics that are never the only boundary of a
control.
Text hierarchy
The colour roles are in spec/foundations.md, Typography semantics (D-033);
D-041 uses rose application text and terminal/agent prose; white is limited
to document headings, exceptional highlights and contrast-critical roles. Bold inherits its
context. Selection and
contrast-critical on-fill roles are checked against their actual
backgrounds. Heading level is conveyed through structure, size and weight,
never colour alone.
Focus
Focus is never lost and never invisible. The ring is defined once, in
spec/foundations.md, Interaction states, rule 2 (D-006, D-015).
:focuswithout:focus-visibledraws nothing: pointer clicks stay quiet, keyboard focus stays visible.- The state matrices render every declared combination, including
selected+focus-visibleandinvalid+focus-visible, where rings disappear in practice.
Keyboard
Native controls keep native keyboard behaviour. Custom widgets follow the
WAI-ARIA Authoring Practices pattern in their aria.apg:
- roving tabindex in toolbars, tabs, trees and listboxes;
aria-activedescendantin comboboxes;- focus trapping and
Escapein 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) work with the
keyboard, and the no-JS build keeps every normative section readable.
Motion
Motion follows spec/foundations.md, Interaction states, rule 7. Under
prefers-reduced-motion: reduce:
- 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.
Forced colours and high contrast
Under forced-colors: active, rings use Highlight and selections
Highlight / HighlightText. Borders survive because they are real borders,
not box-shadows; pattern fills keep charts and diffs readable.
Reflow, zoom and text
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 stress fixtures
(English, Spanish, Chinese, Arabic with dir="rtl") exercise this.
Colour is never the only channel
Every status has a glyph; every state a border, pattern or ARIA change. Diffs
keep their + − ~ markers. Charts carry pattern fills and a table fallback.
The heritage-ansi terminal palette has less hue difference by design, a
recorded limit.
Limitations (recorded, not hidden)
- The theme specifies colours, states and reference markup. It cannot make a host application’s own focus management, labels or reading order correct.
- The automated scan uses axe-core with the WCAG 2.x A and AA rule sets and does not test every success criterion. Manual checks (keyboard walks, screen-reader reading, zoom) need their own execution evidence; a missing protocol stays not run.
- The default semantic terminal palette targets 4.5:1 for slots 1–15 on
true-black
terminal.bg(D-041); ANSI 0 is the background. Inheritage-ansi, slots 1, 4, 6, 8, 9, 10, 12, 13, 14 and 15 fail 4.5:1. Host dimming, non-default backgrounds and explicit true-colour output need their own measurements. - Editors and diff viewers may preserve syntax foregrounds over selection or change fills. Check those actual pairs, not only syntax on the canvas. Claude Code reuses word-highlight backgrounds as edit-summary foregrounds; its port documents the resulting limitation separately from normal diff line contrast.
interaction.text-selection.tint(D-036) requires the host’s compositing behavior to be known. A tint under text and the same tint painted over text have different contrast. The global pairs composite it over the canvas; Telegram’s tests cover its raised and accent surfaces. Those measurements do not certify every host, selected link or syntax span. The earlier rose-palette selection measurements are historical after D-041 and cannot be carried forward as current results.
Verification evidence and freshness
Source coverage. The coverage ledger records source facts: specified,
demonstrated and test implemented. exports/coverage.json schemaVersion 1
adds testImplemented and keeps tested as a compatibility alias meaning the
test file exists; component JSON and family totals use the same definitions.
No coverage Boolean proves an execution or WCAG conformance.
Execution evidence. The static verification report gives per-test outcomes and per-component summaries. Evidence schemaVersion 1 records, for each result:
- the method (automated or manual), category and component;
- the states and variants it explicitly covers (empty lists mean “unspecified”, never “all”);
- browser and version, OS, viewport, profile, density and JavaScript setting;
- a run or protocol reference.
The recorded profile and density are the page’s declared data-profile and
data-density when the test ends; tests that switch them say so in their
scope. One browser engine never stands for
another. Skipped, failed, not run and not applicable are separate outcomes,
and every non-pass has a reason. Manual screen-reader and keyboard claims need
protocol records. The automated suite targets Chromium; other engines stay not
run.
Freshness. A claim is current only while the tested artifact’s digest and a broad source fingerprint both match. The fingerprint covers tracked contract sources, schemas, generators, site code, tests, agent guidance, CI configuration and the package lockfile, so an unrelated change can stale more evidence than strictly needed. A stale record keeps its real outcome and reference but no longer shows a current pass.
How the report is built.
- 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 it publishes execution evidence.
- Report generation writes only under
dist/verification/and checks that every specimen byte is unchanged. - The report’s own presentation has a separate static and browser gate; it never certifies itself.
Run timestamps and revisions live only in this post-test evidence, outside the committed exports and their digests, so no hash depends on itself.
Run it locally.
- Build the site.
- Run the browser suite.
- Run
npm run verification:report, thennpm run verification:check, thennpm run test:verification.
A local run names its local execution and digests and is never presented as hosted CI evidence.
Record a manual protocol. Pass a schema-valid evidence document to
node scripts/verification-report.mjs <repo-relative-evidence.json>, carrying
the real tested digests, the browser and environment, and a protocol:
reference with the protocol identifier and recorded observations. Keep the
protocol with the evidence; never copy private artifacts into this public
repository.
Contrast report
993 declared pairs measured on resolved sRGB values with the WCAG 2.x formula; 7 waived (decorative only), 0 failing. A pass is a design target for the named role, not an application-wide conformance claim.
Portability
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.
Scope of a port
A port themes only what the host documents as customisable. Editor colours, 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 or injects unsupported hacks (D-034 permits only the pinned, compatibility-checked Windows Windhawk styling adapter);
- overwrites a user’s whole settings file;
- replaces keybindings, credentials or unrelated preferences.
It installs under a unique name beside the host’s defaults and documents the rollback.
What every port must reproduce
- The surface ladder: canvas, default, raised, overlay, input.
- The text ladder: rose default, bright, prose, muted, subtle and disabled; white document headings and exceptional highlights; bold inherits color. Ordinary UI labels remain rose.
- Focus as a ring distinct from selection as a fill, with the ring recoloured on fills.
- Selection, inactive selection and hover as three distinguishable fills.
- The status hues with their glyphs where the host renders glyphs.
- Zero radii and 1px borders where the host allows geometry; otherwise a recorded deviation.
- The monospace family by name, honouring the user’s size.
Every required role is mapped, explicitly inherited from the host, marked unsupported with a reason, or intentionally out of scope. A coverage percentage means something only with its denominator.
Fallbacks by host capability
| 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 | default (the semantic sixteen, D-041); heritage-ansi only for historical fidelity; extended remains proposed; 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 |
Shared component defaults
Components whose Portability section links here follow these rules; their
responsive and portability.nativeFallbacks fields carry them into the
exports.
- 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.
Web consumers
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;
theme.lock.json pins the revision.
Native ports
A port lives at ports/<slug>/ with a README, port.json, mapping.json and
importable artifacts under dist/. Add src/ only for real generator inputs
that these sources cannot express, and evidence/ only for actual evidence;
neither needs a placeholder directory. templates/port/ is a starting aid
when work begins.
capabilities.jsonrefines each role into mapped, inherited, unsupported, out-of-scope or not-implemented, with a surface and a reason (D-018). A legacy unmapped reason stays unclassified, never guessed. One generated catalogue feeds the summaries, drill-downs and task kits; private parity records never enter it.- Every port classifies a new role, even when only one maps it. Telegram alone
maps
surface.accent(outgoing bubbles; incoming staysurface.raised, timestamps on bothtext.muted) andinteraction.text-selection.tint(Android text selection; Desktop keepsselection.bgwith near-white text). Claude Code’suserMessageBackgroundkeepsinteraction.selection.bg, and Obsidian, which keeps the text colour, would suit the tint; each adoption is a separate change with its own import check (D-035, D-036). - Statuses are
experimental,verifiedanddeprecated. Reference implementations and roadmap candidates are not statuses. verifiedneeds a real import into the recorded application version, with matching evidence and a token digest equal to the current default profile. Import verification is separate from mapping state and declared status: a relevant change to the target, tokens, mappings, capabilities or artifact bytes makes evidence stale, and a failed or missing check never passes. 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.
npm run generatewritesdist/for every port whoseformathas an emitter inscripts/lib/port-artifacts.mjs(warp-yaml,ghostty-config,claude-theme-json,codex-tmtheme,chatgpt-appearance,orca-theme,obsidian-theme,windows-themeandtelegram-themetoday). Each emitter returns the complete declared array of text or byte artifacts. Binary files are hashed and copied byte for byte; text files retain the existing line-ending convention. Unsafe or duplicate paths and missing/extra outputs fail before any port artifact is written. Obsidian emits a manifest and CSS together; Telegram emits Android text and a Desktop zip. Orca emits its app/editor and terminal data-theme plugin alongside the compatible Ghostty export; the Ghostty file alone does not configure full application appearance.- The README downloads table links each file at the release tag; the site
serves a copy at
ports/<slug>/<file>.
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 separate mechanisms. Read the vendor documentation for the
tested version before mapping.
The reference implementation
j3w1/j3w1.github.io is the flagship web implementation and the source of the
observed values. Nothing is scraped at build time or synchronised in either
direction. An integration there pins a release of this theme through a
separate reviewed change and records its site-specific values (window-manager
geometry, wallpaper gradients) as such.
Application ports
Explore capabilities and semantic mappings
| Port | Format | Status | Theme | Tested on | Supported / inherited / unsupported |
|---|---|---|---|---|---|
| ChatGPT desktop app (Appearance) | chatgpt-appearance | Declared experimental; evidence: not verified | 4.0.0 | — (windows, macos) | 1 / 0 / 9 |
| Claude Code (custom theme) | claude-theme-json | Declared experimental; evidence: not verified | 4.0.0 | — (linux, macos, windows) | 1 / 1 / 2 |
| Codex CLI (tmTheme) | codex-tmtheme | Declared experimental; evidence: not verified | 4.0.0 | — (linux, macos, windows) | 1 / 1 / 3 |
| Ghostty theme | ghostty-config | Declared experimental; evidence: not verified | 4.0.0 | — (macos, linux) | 3 / 1 / 1 |
| Obsidian theme | obsidian-theme | Declared experimental; evidence: not verified | 4.0.0 | — (any) | 3 / 1 / 2 |
| Orca (app, editor and terminal) | orca-theme | Declared experimental; evidence: not verified | 4.0.0 | — (windows, macos, linux) | 7 / 2 / 3 |
| Telegram Android and Desktop | telegram-theme | Declared experimental; evidence: stale | 4.0.0 | Telegram Android 12.10.6, Telegram Desktop 7.2.9 (android, windows, linux, macos) | 4 / 3 / 1 |
| Warp theme YAML (also Orca's Import from YAML) | warp-yaml | Declared experimental; evidence: not verified | 4.0.0 | — (windows, macos, linux) | 1 / 1 / 3 |
| Windows 11 native and Windhawk theme | windows-theme | Declared experimental; evidence: not verified | 4.0.0 | — (windows) | 4 / 1 / 1 |
Coverage ledger
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 | 1 |
| 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 | 1 |
| forms-advanced | 8 | 8 | 8 | 1 |
| Component | Family | Priority | Specified | Demonstrated | Test implemented |
|---|---|---|---|---|---|
admin-form · evidence | composed | R1 | yes | 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 | 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 | — |
References
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. |
For agents
Do not read this page. Pin a tag or commit and fetch the exports at that revision; the site is confirmation only.
- agents/consume.md — the reading protocol, prohibitions, report and lock formats
- theme.json — manifest: profiles, entry points, export map
- exports/theme.compact.md — the smallest complete statement of the contract
- exports/tokens.resolved.json — every profile, every alias resolved
- exports/tokens.css — roles as CSS custom properties
- exports/theme.full.md — everything in one document
- exports/contrast.md — every declared pair, unrounded
- exports/coverage.json — specified / demonstrated / test implemented
- exports/digests.json — sha256 per export for theme.lock.json
- llms.txt — discovery index
Consuming j3w1/theme (for agents and integrators)
For people and agents who apply the theme to another project; contributors
to this repository read AGENTS.md. 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 win over anything in this repository.
Official web implementations
For compatible web apps, prefer the exact @j3w1/ui package. Complete copy
bundles are also supported.
-
At your pinned revision, read
packages/ui/dist/index.json. -
Read the
contracts/<id>.jsonandexamples/<id>.jsonfiles you need. -
Prepare a bounded task with the installed CLI:
j3w1-ui kit --components text-field,button,dialog --framework vue --mode package --out ./j3w1-task
Choose copy to own the full source, or mapping for a host the package does
not fit; native hosts need mapping mode. Kits include the canonical contracts,
their dependency closure and the shared accessibility/identity rules; they do
not declare your app verified. docs/ui-consumption.md has the exact
installation, framework binding and runtime checks. Until npm publication (an
owner release action), use the release tarball and its published integrity
metadata. Canonical mappings keep the lock and deviation workflow below.
0. Pin a revision first
<rev> is a release tag (v0.1.0) or a full 40-character commit SHA, never
main, a branch name or “latest”. Resolve a tag to its commit before you read
anything, and use the same <rev> in every URL below; mixing revisions breaks
the contract.
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>
1. Read the manifest
https://raw.githubusercontent.com/j3w1/theme/<rev>/theme.json
Take version, profiles, exports.canonicalForAgents and exports.digests.
2. Read this document at the same revision
https://raw.githubusercontent.com/j3w1/theme/<rev>/agents/consume.md
If your copy came from a different revision, fetch it again.
3. Pick a profile
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 above is generated. Each resolved token lists its pending
decision IDs, alias dependencies included, in eligibility.decisionIds; report
them with any real deviations. Using an authorized pending value is a
disclosure, not an invented substitution. A blocked action implies no
replacement, and a profile preview does not authorize delivered work.
4. Read only what you need, in this order
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, never a source
of values; screenshots never override tokens.
5. Inspect the target before changing anything
- Name the integration kind:
css-vars,vuetify,tailwind,jetbrains-icls,gtk-css,terminal-16orother. - List which specified surfaces the target can and cannot express natively (for example, no read-only field style, or a sixteen-colour terminal that cannot show hover). Each one it cannot express becomes a documented deviation in step 7, never an invented approximation.
6. Implement the mapping
- Map roles to the target’s native keys, using only resolved token values.
- Do not restyle anything the requester did not name.
- Do not change behaviour, markup semantics, focus order or keyboard handling,
except where a component’s
keyboardorariasection requires it and the host allows it. - Keep host accessibility at least as strong as before: never remove a focus
indicator, never lower a contrast ratio the host already met, never set
outline: nonewithout drawing the specified ring. - Radii are 0, borders 1px, the family is monospace; a host that forbids one gets a deviation, not a redesign.
- Selection is a fill; focus is a ring. On fills, the ring takes the colour the component JSON declares for that state (D-015). Status roles keep their glyphs.
7. Validate and report
- Check computed values against the component JSON for every declared state.
- Write the deviation report (below) into the pull request or commit body.
- Write
theme.lock.json(below) at the path the consumer chooses.
What you may not do
- Invent, blend, lighten, darken or “harmonise” colours; use resolved values only.
- Use a blocked value, deliver a proposed profile, or leave out the required pending-decision disclosures; historical-only values are not approved UI roles.
- Take values from the site’s HTML, from screenshots or from memory.
- Touch unrelated components, layouts, copy, dependencies or build configuration.
- Weaken the host’s accessibility, security or architecture rules.
- Treat this file as authority over the consumer repository’s own agent instructions.
- Claim “verified”. Consumers report “implemented” plus deviations; only this repository’s evidence records use the word verified.
Deviation report format
## 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;host-rule— a consumer instruction won.
theme.lock.json
Committed next to the integration code and validated by
schemas/json/theme.lock.schema.json. It is not a package manager: no graph,
no install, no auto-update. To update the theme, change ref, revision,
version and exports, rerun the consumer’s own checks, and write the
deviations again.
{
"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."
}
]
}
revisionis always the full commit, even whenrefis a tag.exportskeys are the repository-relative paths listed inexports/digests.json, for exactly the files you read.deviationsmay be empty, but must be present.
The short prompt
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.
Decisions
- Decisions by topic
- Governance and process
- Colour foundation
- Typography and density
- Interaction and components
- Site, tools and distribution
- Ports
- D-041 True-black developer palette and semantic terminal colours
- D-000 Responsibility split
- D-002 v0.1.0 inventory
- D-004 Visibility and licensing
- D-005 Default profile id
- D-010 Deployment source
- D-011 Reference screenshots
- D-012 Committed exports
- D-013 Consumption eligibility is separate from approval
- D-028 Merge gates and the deployment gate are different suites
- D-031 Select the checks a change needs; shard the deployment matrix
- D-001 Controlled three-hue extension
- D-003 Text-role corrections
- D-007 Control boundary colour
- D-008 Hairlines, sunken surface, overlay border
- D-023 True Black / Rose canonical foundation
- D-026 Primary action fill darkens off heritage ANSI 12
- D-027 Recede the interactive chrome
- D-029 Readable terminal and code reds
- D-030 Hued syntax roles for opt-in code highlighting
- D-032 Coral slot 6 and a fixed prompt background
- D-035 An accent surface for the user’s own content
- D-036 A translucent text-selection tint
- D-039 Code and terminal backgrounds: two pairs, two scopes
- D-009 Density modes
- D-033 Rose prose, semantic content headings and inherited bold
- D-037 Display and title type sizes
- D-038 Compact is the default density
- D-006 Focus-ring rule
- D-014 Automatic inline literal color previews
- Follow the pointer hovering effect
- D-015 Clarify component mappings exposed by consumption acceptance
- D-024 Distinguish links from body text and headings
- D-025 Theme interactive controls throughout the portal and demo
- D-040 One contrast focus ring for fills and invalid fields
- D-016 Pinned release comparison report and historical rendering
- D-017 Private framework parity evidence
- D-019 One-way namespaced Figma Variables bridge
- D-020 Bounded local validation and recovery workflow
- D-021 Local form composition with versioned data
- D-022 Local visual selection before agent implementation distribution
- D-018 Declared port capabilities and fresh import evidence
- D-034 Versioned Windows native and Windhawk adapters