j3w1 / theme

j3w1 / theme4.0.0

j3w1 / command

  • SPEC Overview
  • SPEC Foundations
  • SPEC Components
  • SPEC Patterns
  • SPEC Tokens
  • SPEC Tools
  • SPEC Workbench
  • SPEC Form builder
  • SPEC Task kit
  • SPEC Recipes
  • SPEC Figma bridge
  • SPEC Report an issue
  • SPEC Implement
  • SPEC For agents
  • SPEC Ports
  • SPEC Releases
  • SPEC Specification
  • DEMO Vue application demo

Foundations

True Black / Rose is the permanent default: black canvas, warm dark panels, the original rose foregrounds and dark-red actions. Exact roles and accessibility rules remain authoritative.

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:

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

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 :hover it 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 :hover alone drives both, never the hover or any-hover media 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 uses text.muted, never text.subtle. Red links cap its brightness: at this hue it stays at or below #3f0c0a (D-035);
  • sunken (#000000) for reading and detail panes;
  • input for form-control fills;
  • true-black chrome and warm rose-black chrome-alt for 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

RoleUse
text.proselong-form reading; aliases rose text.default
text.headingwhite document/content titles and H1–H6
text.highlightdeliberate exceptional white highlights
text.brightbrighter rose UI labels
text.defaultrose interface and body text
text.mutedsecondary text; the darkest colour permitted for ordinary chrome text
text.subtlemetadata, comments, line numbers
text.disableddisabled controls only (exempt under WCAG 1.4.3; at least 3:1 by policy)
text.accentrestrained red accent text on canvas and default surfaces
text.accent-strongred emphasis suitable for raised surfaces
text.linkunderlined 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.

StateVisualNon-colour channel
defaultrest tokens—
hover (@media (hover: hover) only)background → interaction.hover.bg; links use it too with text.link-hover, menu items → interaction.hover.bg-stronglink underline thickens 1px → 2px; cursor
active / pressedbackground → interaction.pressed.bg; border → border.activeborder change; no translation
focus-visiblethe ring (below)the ring itself; never a glow or a colour shift alone
selectedfill interaction.selection.bg with interaction.selection.text; tabs and navigation use a 2px border.selected-indicatoraria-selected / aria-current; a check glyph in lists
selected + focus-visiblethe selection fill and the ordinary ring (rule 1; D-027)both visible at once
text selected, host keeps the text colourtranslucent 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.textthe host’s selection handles
selected, container inactiveinteraction.selection.inactive-bg with interaction.selection.inactive-text; no ringlightness drop between the two fills
disabledtext.disabled, border.disabled, interaction.disabled.bg; no hover or pressed styling; still focusable when aria-disableddisabled / aria-disabled; cursor: not-allowed (rule 5)
invalidborder → status.danger.border at 2px; message in status.danger.text with the ✕ glyph; aria-invalid; aria-describedbyborder width 1 → 2px; glyph; message
invalid + focus-visiblethe 2px danger border and focus.ring-contrast at focus.offset-invaliddouble boundary
read-onlytext.default; 1px dotted border.divider bottom; no fill; focusable; selectablereadonly; dotted edge
loadingsize unchanged; a static ⋯ glyph replaces the leading icon; aria-busyglyph; no animation under reduced motion
busy / indeterminatestatic 45° stripes of action.primary.bg on interaction.pressed.bg; aria-valuetextstripes
checked / mixedfill action.primary.bg, glyph ✓ or – in action.primary.textglyph
expanded / collapsedSVG chevron rotated 0 / 90° within the motion budgetaria-expanded
current2px border.selected-indicator and text.brightindicator bar
drop-target / draggingdashed interaction.drop-target border; interaction.marquee filldashed pattern

Rules that apply everywhere:

  1. 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: #e53935 falls below it on action.primary.bg and status.danger.fill, so the ring recolours on those fills; it meets it on interaction.selection.bg (D-027), so a selected row keeps the ordinary control ring.
  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 focus.ring-contrast (the control ring in interaction.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 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.

Actions

Four button tones:

  • primary, the only filled button at rest: action.primary.bg (#7d1310) with action.primary.text (bright rose; D-041).
  • secondary, an outline: border.control, text text.bright.
  • tertiary, a ghost or icon button: text.default, hover interaction.hover.bg.
  • destructive, an outline in action.destructive.text (#f73f35) that fills on hover with action.destructive.hover-bg (#dc282e) and #f9faf9 text. 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 #e53935 underline, #f73f35 text, #e53935 stripe, #2b0e0d line background;
  • warning: wavy amber;
  • info: dotted blue;
  • hint: dotted text.subtle;
  • unused: text.muted with a dotted underline.

Diffs:

  • added: #0f1a0e line background, green gutter mark and +; word-level additions as a 2px green underline (no fill, so text contrast is untouched);
  • removed: #2b0e0d background, red gutter mark and −; word-level removals underlined and struck through;
  • modified: #1f1a0c background, amber gutter mark and ~;
  • conflicts: a double #f73f35 border.

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-line uses the neutral default layer;
  • code.line-number uses the readable subtle tier; active numbers and the caret step up to the normal and bright text tiers;
  • code.selection-bg is a subdued red selection fill;
  • code.search-match-bg marks matches, with the current match inverted by code.search-current-bg and code.search-current-text;
  • code.bracket-match is a 1px accent border;
  • code.indent-guide is a neutral separator and code.whitespace is 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-ansi carries the sixteen Xresources slots exactly. color.primitive.ansi.* remains byte-for-byte historical; the heritage profile explicitly restores its historical slot and background assignments.
  • default uses 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 #ffa2a7 for 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.
  • extended remains 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:

ModeControlRowTypeHorizontal paddingGapsUse
compact24px28pxui-sm8px2pxmatches the workstation
comfortable32px36pxui-md12px4pxeveryday 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.

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.4996 reads 4.50 and fails.
  • Heritage values that fail (#a3676b as text, #7d1310 anywhere as text) are flagged $deprecated in the heritage-ansi profile, 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).

  • :focus without :focus-visible draws nothing: pointer clicks stay quiet, keyboard focus stays visible.
  • The state matrices render every declared combination, including selected+focus-visible and invalid+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-activedescendant in comboboxes;
  • focus trapping and Escape in dialogs and drawers;
  • arrow keys in radio groups and menus.

Positive tabindex is banned. Every icon-only control has an accessible name. The site’s own controls (search, filters, inspector, copy) 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. In heritage-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.

  1. A build first publishes a static not-run report and a subject manifest.
  2. The browser reporter checks that source and specimen assets still match that subject before it publishes execution evidence.
  3. Report generation writes only under dist/verification/ and checks that every specimen byte is unchanged.
  4. 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.

  1. Build the site.
  2. Run the browser suite.
  3. Run npm run verification:report, then npm run verification:check, then npm 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.