# j3w1 UI Theme Spec — compact (1.1.0, profile: default) Read this first; it is the smallest complete statement of the approved contract. Pin a tag or commit and fetch every file at that revision (see `agents/consume.md`). Values marked PROPOSED are not approved. 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. ## Identity True-black canvas (`#000000`), near-black panels (`#100c0c`) and chrome (`#090707`), with dark-red raised layers, rose interface text (`#e99499`), near-white reading text (`#f4eeee`), dark-red selections (`#911410`), a bright red focus ring (`#e53935`), 1px square borders, zero radii, one monospace family for chrome, reading and code. Red marks *interaction*: focus, selection, active borders, primary actions. It is not a decoration and it is not applied to everything. Darkness is the canvas; readable content is the priority. Five rules keep an implementation recognisable: 1. Preserve the warm-black / rose / red identity. Never substitute a charcoal-and-blue dark theme because the target ships one. 2. Use the role assigned to a token, never the token whose colour happens to look closest. 3. Keep content, selection, keyboard focus, diagnostics and changed text distinguishable from one another. Selection is a fill; focus is a ring. 4. Nothing is rounded, blurred, glowing or gradient-filled. Elevation is a 1px border and, for modals, a backdrop. 5. 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. ## Approved core roles (default) Surfaces, text, borders, interaction, actions and status. The code, diagnostic, diff, terminal, chart and icon groups are in `exports/tokens.resolved.json` and inlined in each component's JSON. | Role | Value | Alias of | Use | | --- | --- | --- | --- | | `color.surface.desktop` | `#000000` | `{color.primitive.ink.0}` | Behind every window.; use | | `color.surface.canvas` | `#000000` | `{color.primitive.ink.0}` | Page and terminal background.; use | | `color.surface.chrome` | `#090707` | `{color.primitive.ink.14}` | Window chrome, title bars, bars.; use | | `color.surface.chrome-alt` | `#1c0a09` | `{color.primitive.ink.40}` | Alternate chrome.; use | | `color.surface.sunken` | `#000000` | `{color.primitive.ink.0}` | Reading and detail panes set below the canvas.; use | | `color.surface.default` | `#100c0c` | `{color.primitive.ink.28}` | Panels and cards.; use | | `color.surface.raised` | `#241010` | `{color.primitive.ink.52}` | Sticky headers, menus, popovers.; use | | `color.surface.overlay` | `#241010` | `{color.primitive.ink.52}` | Dialogs and drawers.; use | | `color.surface.input` | `#000000` | `{color.primitive.ink.0}` | Form-control fill.; use | | `color.surface.code` | `#000000` | `{color.primitive.ink.0}` | Editor background.; use | | `color.surface.terminal` | `#000000` | `{color.primitive.ink.0}` | Terminal background.; use | | `color.text.default` | `#e99499` | `{color.primitive.rose.700}` | Interface and body text.; use | | `color.text.bright` | `#ffa2a7` | `{color.primitive.rose.800}` | Headings and emphasis. Links use color.text.link.; use | | `color.text.prose` | `#f4eeee` | `{color.primitive.paper.90}` | Long-form reading text.; use | | `color.text.muted` | `#bd787d` | `{color.primitive.rose.600}` | Secondary text; the darkest colour permitted for chrome text.; use | | `color.text.subtle` | `#ad7175` | `{color.primitive.rose.450}` | Metadata, comments, line numbers.; use | | `color.text.disabled` | `#8a5559` | `{color.primitive.rose.300}` | Disabled control text.; use | | `color.text.placeholder` | `#bd787d` | `{color.primitive.rose.600}` | Placeholder text, italic.; use | | `color.text.accent` | `#e53935` | `{color.primitive.red.800}` | Accent text on canvas and default surfaces only.; use | | `color.text.accent-strong` | `#f73f35` | `{color.primitive.red.850}` | Accent text safe on every surface.; use | | `color.text.link` | `#f73f35` | `{color.primitive.red.850}` | Strong red link text, distinct from rose body text and headings; retain the underline.; use | | `color.text.link-underline` | `#dc282e` | `{color.primitive.red.750}` | Link underline.; use | | `color.text.link-hover` | `#f4eeee` | `{color.primitive.paper.90}` | Link text on hover, over interaction.hover.bg-strong.; use | | `color.text.on-selection` | `#f4eeee` | `{color.primitive.paper.90}` | Text on the selection fill.; use | | `color.text.on-action` | `#f4eeee` | `{color.primitive.paper.90}` | Text on the primary action fill.; use | | `color.text.on-danger` | `#f9faf9` | `{color.primitive.paper.100}` | Text on the danger fill.; use | | `color.text.on-light` | `#0c0909` | `{color.primitive.ink.20}` | Text on light fills: bright rose, amber, green, blue.; use | | `color.text.inverse` | `#0c0909` | `{color.primitive.ink.20}` | Text on inverted surfaces.; use | | `color.border.default` | `#531310` | `{color.primitive.red.100}` | Decorative border (1.39:1; never the only edge of a control).; use | | `color.border.strong` | `#9e231f` | `{color.primitive.red.400}` | Emphasised 2px rule: sticky headers, section ends.; use | | `color.border.active` | `#e53935` | `{color.primitive.red.800}` | Active or focused control border.; use | | `color.border.disabled` | `#7d1310` | `{color.primitive.red.200}` | Disabled control border (exempt).; use | | `color.border.selected-indicator` | `#e53935` | `{color.primitive.red.800}` | 2px indicator bar on the selected tab or nav item.; use | | `color.border.selected-indicator-inactive` | `#9e231f` | `{color.primitive.red.400}` | Indicator bar when the container is not focused.; use | | `color.interaction.focus.ring` | `#e53935` | `{color.primitive.red.800}` | 1px dashed focus ring on controls and rows.; use | | `color.interaction.focus.ring-container` | `#ffa2a7` | `{color.primitive.rose.800}` | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry.; use | | `color.interaction.selection.bg` | `#911410` | `{color.primitive.red.350}` | Selected row, tab, workspace.; use | | `color.interaction.selection.text` | `#f4eeee` | `{color.primitive.paper.90}` | Text on the selection.; use | | `color.interaction.selection.inactive-bg` | `#420f0c` | `{color.primitive.red.50}` | Selection while the container is not focused.; use | | `color.interaction.selection.inactive-text` | `#e99499` | `{color.primitive.rose.700}` | Text on the inactive selection (7.02:1).; use | | `color.interaction.text-selection.bg` | `#911410` | `{color.primitive.red.350}` | ::selection background.; use | | `color.interaction.text-selection.text` | `#f4eeee` | `{color.primitive.paper.90}` | ::selection text.; use | | `color.interaction.hover.bg` | `#1c0a09` | `{color.primitive.ink.40}` | Hover on rows, ghost buttons, list items.; use | | `color.interaction.hover.bg-strong` | `#630f0d` | `{color.primitive.red.150}` | Hover on links and menu items.; use | | `color.interaction.pressed.bg` | `#420f0c` | `{color.primitive.red.50}` | Pressed fill.; use | | `color.interaction.disabled.bg` | `#160b0b` | `{color.primitive.ink.30}` | Disabled control fill.; use | | `color.interaction.marquee` | `rgb(145 20 16 / 12%)` | `{color.primitive.alpha.red-12}` | Drag marquee and ghost.; use | | `color.interaction.drop-target` | `#e53935` | `{color.primitive.red.800}` | Dashed drop-target border.; use | | `color.interaction.scrollbar.track` | `#0c0909` | `{color.primitive.ink.20}` | Scrollbar track.; use | | `color.interaction.scrollbar.thumb` | `#420f0c` | `{color.primitive.red.50}` | Scrollbar thumb.; use | | `color.interaction.scrollbar.thumb-hover` | `#911410` | `{color.primitive.red.350}` | Scrollbar thumb on hover.; use | | `color.action.primary.bg` | `#871f19` | `{color.primitive.red.300}` | Primary button fill (color12).; use | | `color.action.primary.text` | `#f4eeee` | `{color.primitive.paper.90}` | Primary button text (8.17:1).; use | | `color.action.primary.hover-bg` | `#911410` | `{color.primitive.red.350}` | Primary button hover (7.92:1 with prose).; use | | `color.action.primary.pressed-bg` | `#630f0d` | `{color.primitive.red.150}` | Primary button pressed.; use | | `color.action.primary.border` | `#9e231f` | `{color.primitive.red.400}` | Primary button border.; use | | `color.action.secondary.bg` | `rgb(0 0 0 / 0%)` | `{color.primitive.alpha.transparent}` | Secondary button has no fill.; use | | `color.action.secondary.text` | `#ffa2a7` | `{color.primitive.rose.800}` | Secondary button text.; use | | `color.action.secondary.border` | `#a3676b` | `{color.primitive.rose.350}` | Secondary button border (meets 3:1).; use | | `color.action.secondary.hover-bg` | `#630f0d` | `{color.primitive.red.150}` | Secondary button hover.; use | | `color.action.secondary.pressed-bg` | `#420f0c` | `{color.primitive.red.50}` | Secondary button pressed.; use | | `color.action.tertiary.text` | `#e99499` | `{color.primitive.rose.700}` | Ghost and icon button text.; use | | `color.action.tertiary.hover-bg` | `#1c0a09` | `{color.primitive.ink.40}` | Ghost and icon button hover.; use | | `color.action.destructive.text` | `#f73f35` | `{color.primitive.red.850}` | Destructive button text at rest (outline style).; use | | `color.action.destructive.border` | `#e53935` | `{color.primitive.red.800}` | Destructive button border.; use | | `color.action.destructive.hover-bg` | `#dc282e` | `{color.primitive.red.750}` | Destructive button hover fill.; use | | `color.action.destructive.hover-text` | `#f9faf9` | `{color.primitive.paper.100}` | Destructive button text on hover (4.58:1).; use | | `color.action.destructive.pressed-bg` | `#ae1914` | `{color.primitive.red.550}` | Destructive button pressed fill.; use | | `color.action.destructive.filled-bg` | `#dc282e` | `{color.primitive.red.750}` | Confirm-dialog destructive fill.; use | | `color.action.destructive.filled-text` | `#f9faf9` | `{color.primitive.paper.100}` | Text on the destructive fill.; use | | `color.status.danger.text` | `#f73f35` | `{color.primitive.red.850}` | Danger text (5.40:1).; use | | `color.status.danger.fill` | `#dc282e` | `{color.primitive.red.750}` | Danger fill.; use | | `color.status.danger.on-fill` | `#f9faf9` | `{color.primitive.paper.100}` | Text on the danger fill (4.58:1).; use | | `color.status.danger.tint` | `#2b0e0d` | `{color.primitive.ink.60}` | Danger background tint.; use | | `color.status.danger.border` | `#e53935` | `{color.primitive.red.800}` | Danger border, 2px on invalid inputs.; use | | `color.status.warning.text` | `#c9973f` | `{color.primitive.amber.500}` | Warning text (7.54:1).; use | | `color.status.warning.fill` | `#c9973f` | `{color.primitive.amber.500}` | Warning fill.; use | | `color.status.warning.on-fill` | `#0c0909` | `{color.primitive.ink.20}` | Text on the warning fill (7.54:1).; use | | `color.status.warning.tint` | `#1f1a0c` | `{color.primitive.amber.tint}` | Warning background tint.; use | | `color.status.warning.border` | `#c9973f` | `{color.primitive.amber.500}` | Warning border.; use | | `color.status.success.text` | `#86a46f` | `{color.primitive.green.500}` | Success text (7.13:1).; use | | `color.status.success.fill` | `#86a46f` | `{color.primitive.green.500}` | Success fill.; use | | `color.status.success.on-fill` | `#0c0909` | `{color.primitive.ink.20}` | Text on the success fill (7.13:1).; use | | `color.status.success.tint` | `#0f1a0e` | `{color.primitive.green.tint}` | Success background tint.; use | | `color.status.success.border` | `#86a46f` | `{color.primitive.green.500}` | Success border.; use | | `color.status.info.text` | `#7e9ebb` | `{color.primitive.blue.500}` | Info text (7.08:1).; use | | `color.status.info.fill` | `#7e9ebb` | `{color.primitive.blue.500}` | Info fill.; use | | `color.status.info.on-fill` | `#0c0909` | `{color.primitive.ink.20}` | Text on the info fill (7.08:1).; use | | `color.status.info.tint` | `#0f141c` | `{color.primitive.blue.tint}` | Info background tint.; use | | `color.status.info.border` | `#7e9ebb` | `{color.primitive.blue.500}` | Info border.; use | | `color.status.neutral.text` | `#bd787d` | `{color.primitive.rose.600}` | Neutral status text.; use | | `color.status.neutral.fill` | `#a3676b` | `{color.primitive.rose.350}` | Neutral fill (dunst urgency_low).; use | | `color.status.neutral.on-fill` | `#000000` | `{color.primitive.ink.0}` | Text on the neutral fill (4.71:1).; use | | `color.status.neutral.tint` | `#241010` | `{color.primitive.ink.52}` | Neutral background tint.; use | | `color.status.neutral.border` | `#a3676b` | `{color.primitive.rose.350}` | Neutral border.; use | ## Foundations | Token | Value | Use | | --- | --- | --- | | `border.width.default` | `1px` | Every border is 1px. | | `border.width.emphasis` | `2px` | Selected indicator, sticky header rule, invalid border. | | `radius.none` | `0px` | The only radius. Nothing is rounded. | | `font.family.mono` | `"SauceCodePro NFM", "Source Code Pro", "Cascadia Mono", "Consolas", "Liberation Mono", "DejaVu Sans Mono", monospace` | One family for chrome, reading and code. Not bundled; see theme.json font. | | `font.size.ui-sm` | `12px` | | | `font.size.ui-md` | `13px` | | | `font.size.ui-lg` | `14px` | | | `font.size.reading` | `15px` | | | `font.size.code` | `13px` | | | `font.size.terminal` | `13px` | URxvt pixelsize 13. | | `font.size.h1` | `20px` | | | `font.size.h2` | `16px` | | | `font.size.h3` | `13px` | | | `font.size.caption` | `11px` | Only with text.muted or lighter. | | `focus.ring` | `1px dashed #e53935` | outline on controls and rows. | | `focus.ring-container` | `2px solid #ffa2a7` | outline on focusable containers. | | `focus.offset` | `-2px` | outline-offset for controls. | | `focus.offset-container` | `-3px` | outline-offset for containers. | | `focus.offset-invalid` | `-4px` | outline-offset inside the 2px invalid border. | | `motion.duration.fast` | `80ms` | | | `motion.duration.base` | `120ms` | | | `motion.duration.slow` | `150ms` | The maximum. Reduced motion sets every duration to 0.01ms. | ## State rules ## 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 and menu items → `interaction.hover.bg-strong` with `text.link-hover` | link underline thickens 1px → 2px; cursor | | active / pressed | background → `interaction.pressed.bg`; border → `border.active` | border change; no translation | | focus-visible | the ring (below) | the ring itself; never a glow or a colour shift alone | | selected | fill `interaction.selection.bg` with `interaction.selection.text`; tabs and navigation use a 2px `border.selected-indicator` | `aria-selected` / `aria-current`; a check glyph in lists | | selected + focus-visible | the selection fill **and** a ring in the fill's on-fill colour (`interaction.focus.ring-container`, 7.92:1 on the selection) | both visible at once | | selected, container inactive | `interaction.selection.inactive-bg` with `interaction.selection.inactive-text`; no ring | lightness drop between the two fills | | disabled | `text.disabled`, `border.disabled`, `interaction.disabled.bg`; no hover or pressed styling; still focusable when `aria-disabled` | `disabled` / `aria-disabled`; `cursor: not-allowed`; never opacity | | invalid | border → `status.danger.border` at 2px; message in `status.danger.text` with the `✕` glyph; `aria-invalid`; `aria-describedby` | border width 1 → 2px; glyph; message | | invalid + focus-visible | the 2px danger border **and** the ring in `interaction.focus.ring-container` at `focus.offset-invalid` | double boundary | | read-only | `text.default`; 1px dotted `border.divider` bottom; no fill; focusable; selectable | `readonly`; dotted edge | | loading | size unchanged; a static `⋯` glyph replaces the leading icon; `aria-busy` | glyph; no animation under reduced motion | | busy / indeterminate | static 45° stripes of `action.primary.bg` on `interaction.pressed.bg`; `aria-valuetext` | stripes | | checked / mixed | fill `action.primary.bg`, glyph `✓` or `–` in `action.primary.text` | glyph | | expanded / collapsed | SVG chevron rotated 0 / 90° within the motion budget | `aria-expanded` | | current | 2px `border.selected-indicator` and `text.bright` | indicator bar | | drop-target / dragging | dashed `interaction.drop-target` border; `interaction.marquee` fill | dashed pattern | Rules that apply everywhere: 1. **Selection is a fill; focus is a ring.** Neither borrows the other's form. `#e53935` on `#911410` measures 2.15:1, which is why the ring recolours on fills instead of relying on hue. 2. **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 use `interaction.focus.ring-container` where declared; destructive buttons use their declared on-fill text role. Selection uses its declared on-fill ring. Keep the control or container geometry independently of the colour mapping. `:focus:not(:focus-visible)` draws nothing. Forced-colors mode uses `Highlight`. A host focus indicator is never removed without this replacement. 3. **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. 4. **Hover changes fills and borders, never text colour alone**, and only on hover-capable pointers. 5. **Disabled never uses opacity.** Opacity leaks the background and breaks contrast accounting. 6. **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. 7. **Motion.** Durations are 80 / 120 / 150 ms at most, on opacity, transform and background only; under `prefers-reduced-motion: reduce` every duration is 0.01 ms, nothing slides or shimmers, and no behaviour waits on `transitionend`. ## Component briefs ### 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. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-button. JSON: `exports/components/button.json`. States: default, hover, focus-visible, active, disabled, loading. Tokens: root.bg → `color.action.primary.bg` (#871f19); root.text → `color.action.primary.text` (#f4eeee); root.border → `color.action.primary.border` (#9e231f); root.bg-hover → `color.action.primary.hover-bg` (#911410); root.bg-active → `color.action.primary.pressed-bg` (#630f0d); root.ring → `color.interaction.focus.ring-container` (#ffa2a7); secondary.bg → `color.action.secondary.bg` (rgb(0 0 0 / 0%)); secondary.text → `color.action.secondary.text` (#ffa2a7); secondary.border → `color.action.secondary.border` (#a3676b); secondary.bg-hover → `color.action.secondary.hover-bg` (#630f0d); secondary.bg-active → `color.action.secondary.pressed-bg` (#420f0c); secondary.ring → `color.interaction.focus.ring` (#e53935); tertiary.text → `color.action.tertiary.text` (#e99499); tertiary.bg-hover → `color.action.tertiary.hover-bg` (#1c0a09); tertiary.bg-active → `color.interaction.pressed.bg` (#420f0c); destructive.text → `color.action.destructive.text` (#f73f35); destructive.border → `color.action.destructive.border` (#e53935); destructive.bg-hover → `color.action.destructive.hover-bg` (#dc282e); destructive.text-hover → `color.action.destructive.hover-text` (#f9faf9); destructive.bg-active → `color.action.destructive.pressed-bg` (#ae1914); destructive.bg-filled → `color.action.destructive.filled-bg` (#dc282e); destructive.text-filled → `color.action.destructive.filled-text` (#f9faf9); root.border-active → `color.action.primary.border` (#9e231f); secondary.border-active → `color.border.active` (#e53935); tertiary.border-active → `color.border.active` (#e53935); destructive.border-active → `color.action.destructive.border` (#e53935); root.text-disabled → `color.text.disabled` (#8a5559); root.bg-disabled → `color.interaction.disabled.bg` (#160b0b); root.border-disabled → `color.border.disabled` (#7d1310); loading.glyph → `color.action.primary.text` (#f4eeee). ### 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. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-checkbox. JSON: `exports/components/checkbox.json`. States: default, hover, focus-visible, checked, mixed, checked+focus-visible, checked+disabled, disabled, invalid, required. Tokens: box.bg → `color.surface.input` (#000000); box.border → `color.border.control` (#a3676b); box.bg-hover → `color.interaction.hover.bg` (#1c0a09); box.ring → `color.interaction.focus.ring` (#e53935); box.bg-checked → `color.action.primary.bg` (#871f19); box.bg-checked-hover → `color.action.primary.hover-bg` (#911410); box.glyph → `color.action.primary.text` (#f4eeee); box.ring-checked → `color.interaction.focus.ring-container` (#ffa2a7); box.border-invalid → `color.status.danger.border` (#e53935); box.bg-disabled → `color.interaction.disabled.bg` (#160b0b); box.border-disabled → `color.border.disabled` (#7d1310); box.glyph-disabled → `color.text.disabled` (#8a5559); text.default → `color.text.default` (#e99499); text.disabled → `color.text.disabled` (#8a5559); legend.text → `color.text.bright` (#ffa2a7); legend.required-mark → `color.status.danger.text` (#f73f35); message.text → `color.status.danger.text` (#f73f35). ### 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. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-dialog. JSON: `exports/components/dialog.json`. States: default, open, closed, focus-trapped, hover, focus-visible, reduced-motion. Tokens: backdrop.bg → `color.surface.backdrop` (rgb(0 0 0 / 65%)); root.bg → `color.surface.overlay` (#241010); root.border → `color.border.overlay` (#e53935); root.ring → `color.interaction.focus.ring-container` (#ffa2a7); root.z → `z.dialog` (30); title.text → `color.text.bright` (#ffa2a7); text.text → `color.text.default` (#e99499); close.text → `color.action.tertiary.text` (#e99499); close.bg-hover → `color.action.tertiary.hover-bg` (#1c0a09); close.ring → `color.interaction.focus.ring` (#e53935); button.text → `color.action.secondary.text` (#ffa2a7); button.border → `color.action.secondary.border` (#a3676b); button.bg-hover → `color.action.secondary.hover-bg` (#630f0d); primary.bg → `color.action.primary.bg` (#871f19); primary.text → `color.action.primary.text` (#f4eeee); primary.bg-hover → `color.action.primary.hover-bg` (#911410); destructive.bg → `color.action.destructive.filled-bg` (#dc282e); destructive.text → `color.action.destructive.filled-text` (#f9faf9); destructive.bg-hover → `color.action.destructive.hover-bg` (#dc282e); field.bg → `color.surface.input` (#000000); field.border → `color.border.control` (#a3676b); field.label → `color.text.bright` (#ffa2a7); note.text → `color.text.muted` (#bd787d). ### link — Link The native anchor for navigation; always underlined in the link-underline role, hover fills with the strong hover surface, the current page carries a 2px indicator bar. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-link. JSON: `exports/components/link.json`. States: default, hover, focus-visible, visited, current. Tokens: root.text → `color.text.link` (#f73f35); root.underline → `color.text.link-underline` (#dc282e); root.text-hover → `color.text.link-hover` (#f4eeee); root.bg-hover → `color.interaction.hover.bg-strong` (#630f0d); root.ring → `color.interaction.focus.ring` (#e53935); root.text-visited → `color.text.link` (#f73f35); root.text-current → `color.text.bright` (#ffa2a7); root.indicator → `color.border.selected-indicator` (#e53935); icon.stroke → `color.icon.default` (#e99499). ### menu — Menu A menu button that opens a list of actions on the raised surface inside a 1px overlay border; the active item is the selection fill and check items show a glyph. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-menu. JSON: `exports/components/menu.json`. States: default, hover, focus-visible, open, closed, disabled, checked, selected. Tokens: button.text → `color.text.default` (#e99499); button.bg → `color.surface.default` (#100c0c); button.border → `color.border.control` (#a3676b); button.bg-hover → `color.interaction.hover.bg` (#1c0a09); button.bg-open → `color.interaction.pressed.bg` (#420f0c); button.border-open → `color.border.active` (#e53935); button.ring → `color.interaction.focus.ring` (#e53935); surface.bg → `color.surface.raised` (#241010); surface.border → `color.border.overlay` (#e53935); item.text → `color.text.default` (#e99499); item.bg-hover → `color.interaction.hover.bg-strong` (#630f0d); item.text-hover → `color.text.link-hover` (#f4eeee); item.text-disabled → `color.text.disabled` (#8a5559); active.bg → `color.interaction.selection.bg` (#911410); active.text → `color.interaction.selection.text` (#f4eeee); active.ring → `color.interaction.focus.ring-container` (#ffa2a7); check.glyph → `color.text.bright` (#ffa2a7); shortcut.text → `color.text.muted` (#bd787d); separator.color → `color.border.divider` (#2b0e0d); chevron.color → `color.icon.decorative` (#a3676b). ### 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. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-radio-group. JSON: `exports/components/radio-group.json`. States: default, hover, focus-visible, checked, checked+focus-visible, disabled, invalid, required. Tokens: box.bg → `color.surface.input` (#000000); box.border → `color.border.control` (#a3676b); box.bg-hover → `color.interaction.hover.bg` (#1c0a09); box.ring → `color.interaction.focus.ring` (#e53935); box.bg-checked → `color.action.primary.bg` (#871f19); box.bg-checked-hover → `color.action.primary.hover-bg` (#911410); box.dot → `color.action.primary.text` (#f4eeee); box.ring-checked → `color.interaction.focus.ring-container` (#ffa2a7); box.border-invalid → `color.status.danger.border` (#e53935); box.bg-disabled → `color.interaction.disabled.bg` (#160b0b); box.border-disabled → `color.border.disabled` (#7d1310); box.dot-disabled → `color.text.disabled` (#8a5559); text.default → `color.text.default` (#e99499); text.disabled → `color.text.disabled` (#8a5559); legend.text → `color.text.bright` (#ffa2a7); legend.required-mark → `color.status.danger.text` (#f73f35); message.text → `color.status.danger.text` (#f73f35). ### select — Select A themed single-choice combobox or multiple-choice list backed by a native select for forms and the no-JavaScript fallback. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-select. JSON: `exports/components/select.json`. States: default, hover, focus-visible, required, invalid, invalid+focus-visible, disabled. Tokens: root.bg → `color.surface.input` (#000000); root.border → `color.border.control` (#a3676b); root.bg-hover → `color.interaction.hover.bg` (#1c0a09); root.border-focus → `color.border.active` (#e53935); root.ring → `color.interaction.focus.ring` (#e53935); root.ring-invalid → `color.interaction.focus.ring-container` (#ffa2a7); root.border-invalid → `color.status.danger.border` (#e53935); root.border-disabled → `color.border.disabled` (#7d1310); root.bg-disabled → `color.interaction.disabled.bg` (#160b0b); control.text → `color.text.default` (#e99499); control.text-disabled → `color.text.disabled` (#8a5559); chevron.stroke → `color.icon.default` (#e99499); option.bg → `color.surface.raised` (#241010); option.text → `color.text.default` (#e99499); option.bg-selected → `color.interaction.selection.bg` (#911410); option.text-selected → `color.interaction.selection.text` (#f4eeee); group.text → `color.text.muted` (#bd787d); label.text → `color.text.bright` (#ffa2a7); label.required-mark → `color.status.danger.text` (#f73f35); help.text → `color.text.muted` (#bd787d); message.text → `color.status.danger.text` (#f73f35). ### 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. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-table. JSON: `exports/components/table.json`. States: default, hover, selected, selected+focus-visible, selected+container-inactive, sorted, empty, loading. Tokens: root.bg → `color.surface.default` (#100c0c); header.text → `color.text.bright` (#ffa2a7); header.bg → `color.surface.default` (#100c0c); header.rule → `color.border.strong` (#9e231f); header.bg-sticky → `color.surface.raised` (#241010); sort.glyph → `color.text.bright` (#ffa2a7); sort.ring → `color.interaction.focus.ring` (#e53935); row.text → `color.text.default` (#e99499); row.divider → `color.border.divider` (#2b0e0d); row.bg-hover → `color.interaction.hover.bg` (#1c0a09); selected.bg → `color.interaction.selection.bg` (#911410); selected.text → `color.interaction.selection.text` (#f4eeee); selected.ring → `color.interaction.focus.ring-container` (#ffa2a7); selected.inactive-bg → `color.interaction.selection.inactive-bg` (#420f0c); selected.inactive-text → `color.interaction.selection.inactive-text` (#e99499); caption.text → `color.text.muted` (#bd787d); empty.text → `color.text.muted` (#bd787d); loading.glyph → `color.text.muted` (#bd787d). ### tabs — Tabs A tablist of buttons that switches between panels in place; the selected tab carries a 2px indicator bar and bright text, never a fill. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-tabs. JSON: `exports/components/tabs.json`. States: default, hover, focus-visible, selected, selected+focus-visible, selected+container-inactive, disabled. Tokens: list.bg → `color.surface.default` (#100c0c); list.border → `color.border.divider` (#2b0e0d); tab.text → `color.text.default` (#e99499); tab.text-selected → `color.text.bright` (#ffa2a7); tab.text-disabled → `color.text.disabled` (#8a5559); tab.bg-hover → `color.interaction.hover.bg` (#1c0a09); tab.bg-pressed → `color.interaction.pressed.bg` (#420f0c); tab.indicator → `color.border.selected-indicator` (#e53935); tab.indicator-inactive → `color.border.selected-indicator-inactive` (#9e231f); tab.ring → `color.interaction.focus.ring` (#e53935); panel.bg → `color.surface.default` (#100c0c); panel.text → `color.text.default` (#e99499). ### 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. Status: specified, demonstrated, test implemented. Site: https://j3w1.github.io/theme/reference/#c-text-field. JSON: `exports/components/text-field.json`. 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. Tokens: root.bg → `color.surface.input` (#000000); root.border → `color.border.control` (#a3676b); root.bg-hover → `color.interaction.hover.bg` (#1c0a09); root.border-focus → `color.border.active` (#e53935); root.ring → `color.interaction.focus.ring` (#e53935); root.ring-invalid → `color.interaction.focus.ring-container` (#ffa2a7); root.border-invalid → `color.status.danger.border` (#e53935); root.border-disabled → `color.border.disabled` (#7d1310); root.bg-disabled → `color.interaction.disabled.bg` (#160b0b); root.border-readonly → `color.border.divider` (#2b0e0d); input.text → `color.text.default` (#e99499); input.text-disabled → `color.text.disabled` (#8a5559); input.placeholder → `color.text.placeholder` (#bd787d); input.caret → `color.code.caret` (#e99499); input.selection-bg → `color.interaction.text-selection.bg` (#911410); input.selection-text → `color.interaction.text-selection.text` (#f4eeee); label.text → `color.text.bright` (#ffa2a7); label.required-mark → `color.status.danger.text` (#f73f35); help.text → `color.text.muted` (#bd787d); message.text → `color.status.danger.text` (#f73f35); affix.text → `color.text.muted` (#bd787d); affix.border → `color.border.divider` (#2b0e0d); action.text → `color.action.tertiary.text` (#e99499); action.bg-hover → `color.action.tertiary.hover-bg` (#1c0a09); loading.glyph → `color.text.muted` (#bd787d). ## Roles pending an owner decision These roles use-and-report their pending decision IDs at the pinned revision; their proposed status is unchanged. | Role | Value | Alias of | Use | | --- | --- | --- | --- | | `color.surface.backdrop` | `rgb(0 0 0 / 65%)` | `{color.primitive.alpha.black-65}` | Behind modal dialogs.; use-and-report (D-008) | | `color.border.divider` | `#2b0e0d` | `{color.primitive.ink.60}` | Hairline between rows and sections.; use-and-report (D-008) | | `color.border.control` | `#a3676b` | `{color.primitive.rose.350}` | Form-control boundary (4.45:1, meets the 3:1 non-text minimum).; use-and-report (D-007) | | `color.border.overlay` | `#e53935` | `{color.primitive.red.800}` | Every floating layer's 1px border.; use-and-report (D-008) | ## Where to read more - Reading protocol: https://raw.githubusercontent.com/j3w1/theme/v1.1.0/agents/consume.md - Manifest: https://raw.githubusercontent.com/j3w1/theme/v1.1.0/theme.json - Resolved tokens: https://raw.githubusercontent.com/j3w1/theme/v1.1.0/exports/tokens.resolved.json - Per-component JSON: https://raw.githubusercontent.com/j3w1/theme/v1.1.0/exports/components/.json - Full specification: https://raw.githubusercontent.com/j3w1/theme/v1.1.0/exports/theme.full.md - Digests: https://raw.githubusercontent.com/j3w1/theme/v1.1.0/exports/digests.json - Site (confirmation only): https://j3w1.github.io/theme/