MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3216

Planning bug: a contrast ratio was measured in ONE theme context and generalised to "light" — `data-palette` makes it twenty, and four of them failed

Done
Description

Found while running MOTIR-3207 (the orb-glyph contrast fix), whose analysis this is about. The card is otherwise a strong one — the defect is real, the remedy it names is the right one, and its acceptance criteria all stand. What is wrong is the scope of the measurement its reasoning rests on, and that scope would have chosen the wrong FIX.

The claim, and what falsifies it

MOTIR-3207 states, twice:

"Light passes with 0.32 of headroom." "Prefer a theme-aware mix over lowering it globally: light has headroom at 32% … A [data-theme='dark'] override of the stop … keeps both."

Its evidence is a two-cell table — light / dark — sampled from --el-accent and --el-accent-text. But data-palette is a shipped axis: ten palettes re-point --color-primary-fill and --color-primary-foreground together, so the orb's first gradient stop is twenty colours, not two. Resolved from the token cascade (tests/theme/paletteCascade.ts) over all ten × both themes, at the shipped 32%:

contextratio
default dark2.78:1
cobalt dark2.86:1
spectrum dark3.00:1 (2.9991)
evergreen LIGHT2.94:1

Four contexts under WCAG 1.4.11's 3:1 bar, not one — and one of them is a light context.

Why it would have chosen the wrong fix, not just an incomplete number

The card's recommendation is conditional on the falsified clause. A [data-theme='dark'] override of the stop leaves evergreen's light orb at 2.94:1, so a run following the card's stated preference ships a fix that still fails the bar it was written to clear — and the natural repair from there is a per-palette exception, which is a row-fix for a rule-level defect (no-special-case-fix-the-general-rule). The run instead shipped one global --orb-lit-mix: 26%, at which the worst of the twenty is 3.09:1.

The shape, and its siblings

This is the family notes.html #292 and #340 already carry — a claim about a whole set, made by a measurement whose SCOPE is not recorded beside it — on a third axis. #292's grep was scoped to the wrong directory; #340's to the wrong vocabulary; this one to the wrong rendering context. The tell is specific and cheap to check: a colour measurement is a measurement in ONE data-palette × data-theme context, and this repo ships twenty. The sibling guard the card itself names as the shape to mirror, tests/theme/brand-tile-contrast.test.ts, says so in its own header — "AND IT ASSERTS EVERY PALETTE, NOT JUST THE DEFAULT" — so the correction was one file away from the card that needed it.

What has already been done (this bug is a RECORD, not scheduled work)

  • The fix shipped as one global token, with tests/theme/orb-glyph-contrast.test.ts re-deriving all twenty contexts on every run — so the count cannot silently rot.
  • MOTIR-3207's description carries an on-the-record Amendment section with the table above; the falsified clauses are left in place rather than rewritten.
  • design/ai-chat/design-notes.md § B and ai-callout-menu.mock.html panel 9 record the corrected numbers.
  • The lesson is in motir-meta/notes.html.

The rule this argues for

A card that states a contrast, a colour or a token ratio as evidence names the context it was sampled in, and where the surface is themed, the measurement is taken over the whole data-palette × data-theme matrix — or the card says explicitly which contexts it did not measure and why. The mechanised half already exists: derive the ratio from the token CHAIN, as the two tests/theme/*-contrast guards now both do.

Context refs

  • MOTIR-3207 — the card whose analysis this records.
  • MOTIR-3183 — the design pass that took the original two-cell measurement (motir-core#2193).
  • tests/theme/brand-tile-contrast.test.ts — the sibling guard that already asserts every palette.
  • tests/theme/paletteCascade.ts — the cascade model the re-measurement used.