From d23ead4b27403adc848b912bcd4b9dee03b370f0 Mon Sep 17 00:00:00 2001 From: rzen Date: Thu, 6 Aug 2026 20:54:18 -0400 Subject: [PATCH] =?UTF-8?q?Pro=20surfaces=20audit=20through=20a=20fixture-?= =?UTF-8?q?gated=20tier=20override=20=E2=80=94=20a=20toy=20sandbox,=20neve?= =?UTF-8?q?r=20a=20bypass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issues card ruled into 10 ▸ audits: the every-surface claim reaches tier-gated UI via a launch flag honored only when isFixtureLaunch is also set — the fixture already scopes the app to a scratch container and a throwaway board, so the override grants Pro on a disposable sandbox and never over real boards. Manual-checklist-forever would asterisk the claim; debug-only would leave release unauditable. Partial surfaces pin the free-reachable state and audit the full one under the override. (Carries 10's in-flight zoom-control bullet from the parallel session.) Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy --- DESIGN/10-accessibility.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESIGN/10-accessibility.md b/DESIGN/10-accessibility.md index 6a587a0..85b25e4 100644 --- a/DESIGN/10-accessibility.md +++ b/DESIGN/10-accessibility.md @@ -44,6 +44,7 @@ The stance is committed in 00-vision.md: **accessibility is a requirement of "na ## Text scaling & visual accommodations - **Full relative scaling** (decided): relative text styles everywhere, no fixed point sizes. Card face, lane header, and masonry metrics derive from font metrics, so layout survives the largest system text sizes; the no-horizontal-scroll invariant is untouched (lane count is the user's choice; lanes scroll vertically), and 03-board-ui.md's graceful-truncation rules apply at every scale. +- **Board zoom is that scaling's user-facing control** (settled 2026-08-02; behavior in 03-board-ui.md ▸ Layout, rows in 11-command-nexus.md). macOS ships no system text-size setting, so the commitment above had nothing to move it — the point size the whole board derives from is read once and never changes. View ▸ Zoom In / Zoom Out / Actual Size supply the multiplier: one ladder, one effective body size, and every em multiple and every text style scaling off it together. This is an accessibility feature before it is a convenience one, which is why it is a first-class menu command with a chord rather than a setting buried in a pane, and why **the strip's own truncation rules are the acceptance test** — 03's graceful-truncation promise "at every scale" is only checkable now that a scale exists. **A zoom change announces its new level** ("Zoom 125%") through the ordinary announcement path: it is chrome, not information — nothing about the board's meaning changes — so no label, value, or trait anywhere else moves with it. - **Contrast is pinned to WCAG AA.** Lane and card colors render as edge accents (03-board-ui.md's top-edge band / left-edge stripe), so text never sits on them — they are supplementary decoration, never the sole carrier of information, and carry no text-contrast obligation. The ≥ 4.5:1 automatic-contrast rule binds where text does sit on a user-chosen color: the **board** background (palette pairs verified at design time; arbitrary hex computes its text color at runtime against that threshold). An `#RRGGBBAA` background with alpha computes against the color **composited over its effective backdrop** in the active appearance (the board's over the window background; light and dark resolve differently), recomputed on appearance change. Palette names and hex share one ink-selection code path — the palette's AA claim is pinned by a computed-contrast test over all 12 backgrounds in both appearances (ratified 2026-07-29). **The AA obligation binds the primary label tier** (ruled 2026-07-29): the ink seam moves the whole label hierarchy with the primary, and subordinate tiers (.secondary, .quaternary) inherit the system vocabulary's own contrast posture, which sits below 4.5:1 on any background including the system's — the platform-standard reading; the strict path for users who need more is Increase Contrast, which raises accents and washes to full alpha (▸ Visual accommodations). Increase Contrast strengthens borders and the selection indicator. - **State is never color-alone**: selection is a ring plus trait, cut-pending is dim plus stated value, the trash header is hatched plus labeled — all already patterned; kept as a rule. - **Reduce Motion is a per-voice rule, not a feature list** (settled): movement animations go **instant**, appear/disappear transitions go **crossfade**, uniformly — every animated surface derives its reduced variant from its voice, the store's reload seam included (the largest animated surface in the app), so new surfaces never need individual rulings. The named cases — reflow-on-drag, search animate-out, the drag replica's lift and settle transitions (its 1:1 tracking never animates, like the selection marquee, which needs no variant — 03-board-ui.md ▸ Motion), the lane-resize rubber-band feedback (03-board-ui.md ▸ Lane), trash animations — are applications of the rule, not the rule itself. **Reduce Transparency**: glass underlays go solid, wherever they appear. @@ -51,7 +52,7 @@ The stance is committed in 00-vision.md: **accessibility is a requirement of "na ## Verification -- **Automated audits are test failures**: Xcode's accessibility audit (`performAccessibilityAudit`) runs in UI tests over every surface — board (trash shown and hidden), card window (Preview, Edit, raw source), welcome, template chooser, board popover, board settings sheet. +- **Automated audits are test failures**: Xcode's accessibility audit (`performAccessibilityAudit`) runs in UI tests over every surface — board (trash shown and hidden), card window (Preview, Edit, raw source), welcome, template chooser, board popover, board settings sheet. **Pro surfaces audit through a fixture-gated tier override** (ruled 2026-08-06): the audit's every-surface claim reaches tier-gated UI (the settings sheet's Pro sections, remote/credentials, the card History section in its present state) via a launch flag honored **only when `UITestLaunch.isFixtureLaunch` is also set** — the fixture flag already redirects registry and app state to a scratch container and builds a throwaway board, so the override grants Pro on a disposable sandbox and never over real boards; a bare tier flag in the shipping binary would be a subscription bypass, and this one is not (a Terminal user gains a Pro-looking toy, not a working subscription). The weighed alternatives lose on the audit's own terms: a permanent manual-checklist carve-out would asterisk the every-surface claim, and a debug-build-only override would leave release builds unauditable. Surfaces whose reachable-state audit is genuinely partial pin what the free fixture can reach (the disabled settings row, the absent History section) *and* audit the full surface under the override — both states are shipping states, both audit. - **A manual VoiceOver smoke script** lives with the test plan: create lane → create card → rename → cut/paste to another lane → external edit lands (announcement heard) → delete → restore from the trash (⌘X/⌘V) → Empty Trash. Run per release; it is the canonical "does the board actually work blind" check. ## Changes from Kanban