Skip to content

v4.42 — Tint Alpha Governance

A design-system ticket about nine ungoverned tint alphas turned out to be two structural guard gaps and a 104-entry allowlist leak. This milestone closed both, fixed the worst measured contrast defect of any tint-ink milestone to date (1.32:1), and only then shipped the named tint scale OBJ-1986 originally asked for.

Summary

OBJ-1986 was filed during v4.26 as a narrow observation: accent tint backgrounds were hand-picked at nine different alpha values with no governing rule. Sizing it as an epic and re-censusing at kickoff found the real problem sat one layer underneath. Of the 14 hues that declare an -accessible ink token, only 6 carried an enforced contrast ceiling — the other 8 had a fix target and no check. And tintInkGuard.spec.ts's allowlist held 104 entries, 78 of them reasoned against OBJ-2472, an issue that had already shipped done — an open triage against a closed ticket, silently carrying real, in-scope defects with no owner.

Phase 1 closed the first gap by extending the existing measurement harness to all 14 hues rather than building a second one — the milestone's own declared top risk, named at kickoff and avoided in practice. Phases 2 and 3 then triaged all 78 orphaned entries plus 19 hover-deferred sites nobody had re-verified since they were written, dissolving nearly all of them structurally (the guard now computes the exemption instead of listing it) and finding exactly one real defect: a "Change" chip button reading 2.73:1. Phase 4 fixed the three sites Phase 1's new ceiling actually flagged. Phase 5 then widened the guard itself — to raw-CSS alphas, bracket-syntax Tailwind classes, and two more scan roots — which is what surfaced the milestone's real teeth: 52 new findings (Phase 5b) including eight genuine celebration-tier defects, and a -foreground-as-ink scanner blind spot that let BadgeModal.vue's Legendary rarity badge ship at 1.32:1 in dark mode, worse than anything either of the two prior tint-ink milestones found. That blind spot became Phase 5c, inserted mid-milestone and closed before Phase 6 could open, so Phase 6's completion claim would be honest against a lens that could actually see the whole defect class.

Phase 6 shipped OBJ-1986's original ask — but not as filed. A flat, shared alpha scale is exactly what DESIGN.md § v4.36 rule 3 already measured and rejected as "this milestone's own first guess, and it was wrong." The scale that shipped instead is 42 --ink-safe-<hue>-<level> tokens, one triple of wash/fill/max per ceilinged hue, each resolving through that hue's own ceiling rather than a shared number — so --ink-safe-primary-max is 0.10 and --ink-safe-rarity-legendary-max is 0.39, both provably AA-safe in their own right. 50 existing call sites migrated onto it; 233 above-ceiling raw occurrences were deliberately left alone — 37 of them background-role decorative fills and scrims with no ink riding on them, the other 196 border/box-shadow/filter properties that were never eligible for the ink-safe scale regardless of alpha — because migrating them would change nothing about accessibility and everything about intent. tintInkGuard.spec.ts now asserts expect(ALLOWLIST).toEqual([]) directly, the same discipline v4.40 established. The allowlist's arc across the milestone: 104 → 17 → 9 → 7 → 0.

Goal

Two things have to be true at milestone close. One — every hue that has an ink token has an enforced alpha ceiling, and no live site sits above it. Two — the tint guard's allowlist is empty. Only once both hold does the third thing — OBJ-1986's original ask, a governed tint scale — mean anything. A named scale declared over a surface with 78 untriaged defects and 8 unceilinged hues would be a notation change dressed up as governance.

Scope — What Shipped

  • Ceiling coverage (Phase 1, OBJ-2814): SHIPPED_CEILING extended from 6 to all 14 hues with a declared -accessible ink sibling, measured across 10 themes × 2 modes × both --card/--background using the existing v4.36 helpers. NO_CEILING_NEEDED names the other 25 discovered hues with a written reason. A sum assertion ties both lists to discoverHues().length so a future undeclared hue fails loud instead of silently passing.
  • Cross-element triage — primary (Phase 2, OBJ-2815): 52 orphaned CROSS_ELEMENT_ALLOWLIST entries on the thinnest-headroom hue in the system. Desi's triage protocol — computed exemption via isDecorativeDescendant(), keyed on "carries no accessible name," never on element type — dissolved 51 as decorative and fixed the one real defect, GoalCreate.vue's "Change" chip (2.73:1 → 4.92:1).
  • Cross-element triage — tail + hover (Phase 3, OBJ-2816): the remaining 26 cross-element entries plus 19 never-re-verified hover-deferred sites. Both buckets went to [] — the hover exemption made structural (accessibleClearsAaAtAlpha()/restPassesAA()) rather than case-by-case, since every site turned out to carry exclusively accessible ink underneath.
  • Fix the newly-ceilinged sites (Phase 4, OBJ-2817): the 3 genuine accent sites Phase 1's new /14 ceiling flagged (of ~9 the kickoff census over-estimated), fixed rather than re-allowlisted.
  • Guard coverage for raw-CSS alphas (Phase 5, OBJ-2818): scanRawCssTints() extended to real CSS declarations and TS/JS object-literal string values; bracket-syntax Tailwind (bg-<hue>/[N]) matched; SCAN_ROOTS widened to design_system/src and marketing_landing. 404 raw occurrences reduced to 44 genuine flat-fill-plus-ink candidates by property-context classification.
  • Raw-CSS co-occurrence triage (Phase 5b, OBJ-2831, inserted): disposed all 52 findings Phase 5's widened lens parked, including 8 real celebration-tier rarity-*/gold fixes. All four affected allowlist buckets independently re-verified [] three times over (Codi, Riley, Roy) as an anti-whitewash control.
  • The -foreground-as-ink blind spot (Phase 5c, OBJ-2837, inserted): closed a scanner gap where a hue's own -foreground token used as ink over that hue's tint matched neither of the guard's two ink tests. Fixed the milestone's worst measured defect (BadgeModal.vue, 1.32:1) plus three related sites (AchievementCard.vue, two TimelineEvent.vue sites, RankChip.vue). Surfaced and re-specced a shape-based hazard assertion that would have false-positived two correctly dark:-guarded sites.
  • The governed tint scale, allowlist flip, and close (Phase 6, OBJ-2819): 42 --ink-safe-<hue>-<level> tokens declared in design_system/css/tokens.css, each asserted at or below its hue's Phase 1 ceiling; 50 call sites migrated across 28 files; expect(ALLOWLIST).toEqual([]) plus a live-tree zero-unlisted-violations scan; the -foreground token contract ruled opaque-fill-only in DESIGN.md; all milestone docs landed.

Phases

PhaseNameStatusPR(s)Highlights
1Ceiling coverage for every hue with an ink tokenShipped#2562SHIPPED_CEILING 6 → 14 hues; no second measurement path built
2Cross-element triage — primary (52 entries)Shipped#256351 decorative-exempt, 1 real fix (GoalCreate.vue chip)
3Cross-element triage — tail + hover re-verificationShipped#2573Remaining 26 + 19 hover sites → [], both made structural
4Fix the sites Phase 1's ceilings newly flagShipped#25753 accent sites fixed, CEILING_ALLOWLIST []
5Guard coverage for raw-CSS alphas and unscanned rootsShipped#2576Raw-CSS, bracket-syntax, and two new scan roots covered
5bRaw-CSS co-occurrence and ceiling triage (inserted)Shipped#258452 findings disposed; 8 real celebration-tier fixes
5cThe -foreground-as-ink blind spot (inserted)Shipped#2587, #2590, #2594Fixed the milestone's worst defect, 1.32:1 → AA
6The governed tint scale, allowlist flip, and closeShipped#2596, #2595, #2605, #2604, #261242 tokens declared; toEqual([]) flipped; docs landed

Key Decisions

  • The scale is per-hue-clamped, not a flat shared value — because the flat version was already tried and rejected. DESIGN.md § v4.36 rule 3 measured a flat α ≤ 0.10 rule during an earlier milestone and found it wrong. v4.42's 42 tokens each resolve through their own hue's measured ceiling instead, so wash/fill/max means something different — and is safe — per hue.
  • The scale is ink-safe only; above-ceiling sites are documented, not swept. Of 401 raw-CSS tint occurrences, 233 sit above their hue's ceiling. Only 37 of those are background-role — decorative fills, scrims, gradients with no ink riding on them; the remaining 196 are border/box-shadow/filter properties, never a tint under ink and never eligible for the scale regardless of alpha. None is a defect. Migrating either group onto the scale would be a blind sweep of the exact shape DESIGN.md § v4.37 warns against, for zero accessibility gain.
  • "Assert measured, not shape-matched" — the milestone's most expensive lesson. Phase 5c's hazard assertion was originally specified as a shape check (no element pairing an opaque fill class with -foreground ink). It fired on two sites that measure 7.35:1 and carry a dark: override precisely to handle the hazard being asked about — a false positive of exactly the class the check existed to remove. Re-specced to assert the resolved contrast ratio directly.
  • The guard's own re-census always wins over this ROADMAP's prose. Stated at kickoff and proven true twice: Phase 4's site count came in at 3 against an indicative ~9, and Phase 5c's came in at 9 against an indicative 5, each time because the guard saw AST shapes and cross-file structure a hand-written sizing table couldn't.
  • Prop-level mode gating that no test enforces is not a structural exemption. RankChip.vue's gold tone looked light-mode-only at every real consumer, but its own Storybook story rendered it unconditionally — an unenforced invariant, not a guarantee. Fixed rather than exempted.
  • The -foreground token contract is opaque-fill-only, now written down. Phase 5c's defect happened because a token measured against an opaque fill (--rarity-legendary-foreground, 5.49:1) was read as ink over a 20% tint of the same hue (1.32:1) — the token wasn't wrong, the call site was reading a contract it never offered. DESIGN.md now states this explicitly rather than leaving it implicit.
  • Real opaque-fill defects found along the way were routed out, not folded in. Three -foreground-on-solid-fill violations (bg-coach, bg-rarity-epic dark, hover:bg-destructive-interactive light) surfaced during Phase 6's measurement pass. They're a different rule (Solid-Fill Own-Foreground Floor) than the tint-alpha class this milestone governs — filed as OBJ-2851 rather than expanding this close phase's scope.

Requirements Coverage

20 / 20 requirements satisfied — CEILING-1..3, TRIAGE-1..6, SITE-1..2, GUARD-1..4, SCALE-1..3, DOCS-1..3, per the milestone audit.

CategoryCountStatus
CEILING-1..33All satisfied
TRIAGE-1..66All satisfied
SITE-1..22All satisfied
GUARD-1..44All satisfied
SCALE-1..33All satisfied
DOCS-1..33All satisfied

Full detail: MILESTONE-AUDIT.md on GitHub.

Outcomes

Every hue in the app that carries an ink token now has a measured, enforced AA contrast ceiling — 14 of 14, up from 6 — and the tint guard's allowlist, which held 104 entries at kickoff (78 of them a leak against a closed issue), is [], asserted directly rather than inferred from an empty diff. A call site that needs a same-hue tint under ink no longer hand-derives an alpha against a ceiling it may not know exists — it reaches for var(--ink-safe-<hue>-<level>), which already resolves correctly in every theme and mode. The single worst measured contrast defect either of Objectuve's two prior tint-ink milestones ever found (1.32:1, Legendary rarity badge, dark mode) is fixed. Two of the three guard gaps DESIGN.md's OBJ-2456 note had tracked since a prior milestone are now closed for real, not just marked closed on an issue tracker; the third — a same-hue tint layer stacked on another — is recorded as still open rather than silently dropped.

Tech Debt

  • (OBJ-2851, routed out, not fixed here) Three solid-fill -foreground violations found during Phase 6's measurement pass — bg-coach (3.69:1, all 20 theme×mode contexts, 7 call sites including UiButton.vue:17), bg-rarity-epic dark (3.01:1), hover:bg-destructive-interactive light (4.32:1). A different rule than tint-alpha governance; filed separately and routed to Roy for triage.
  • (carried on the accessibility ledger, not fixed here) A stacked-tint-layer scanner gap remains open — a same-hue tint composited on top of another tint layer is still invisible to tintInkGuard.spec.ts.
  • (Phase 6, deferred census work) opaqueFillHueIn()'s variant-prefix regex accepts any prefix including hover:, so a hover-only opaque fill can technically excuse a resting-state pairing. Provably hiding nothing at milestone close; flagged for future tightening, not fixed here.
  • (ROADMAP self-correction, recorded by this audit) The milestone ROADMAP cites Phase 6 Task 1's content as landing via commit 18ae4dc02, which is not itself an ancestor of master — the content folded into Task 2's squash-merge (2c62b20ee) instead. No content is missing; the citation is stale.
  • b462f0775 — Phase 1: ceiling coverage extended to 14 hues (#2562)
  • f98b791b5 — Phase 2: primary cross-element triage, 52 entries (#2563)
  • 0e30b82c5 — Phase 3: tail cross-element + hover re-verification (#2573)
  • 7335ea10f — Phase 4: fix the newly-ceilinged accent sites (#2575)
  • 712734e6b — Phase 5: raw-CSS + bracket-syntax + widened scan roots (#2576)
  • dd397633f — Phase 5b: raw-CSS co-occurrence triage, 52 findings disposed (#2584)
  • 927a573eb — Phase 5c: -foreground-as-ink blind spot closed (#2587, #2590, #2594)
  • 2c62b20ee — Phase 6 Task 2: 42 ink-safe tokens declared (#2596)
  • dfd263cca — Phase 6 Task 3: expect(ALLOWLIST).toEqual([]) flipped (#2595)
  • 07f2e35d7 — Phase 6 Task 4: 50 call sites migrated across 28 files (#2605)
  • 25e1bec97 — Phase 6 Task 5: DESIGN.md rulings recorded (#2604)
  • 0d4128320 — Phase 6 Task 6: scale mechanics, ledger, CHANGELOG, ROADMAP close (#2612)

Last updated: 2026-08-27

Loading…