diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 0bca45f0b0..7bd29b4a07 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -585,3 +585,6 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-01 | claude/ds-v2-tooling-loop (PR #1568) | f6317ea48355c26e080ce45b6f574159e8397e24 | PR #1568 unblock/fix | merged origin/main (behind-but-clean; merge-tree was clean); cleared GitHub DIRTY/CONFLICTING; advisory review threads left open (not merge/CI blockers); auto-merge remains armed | merge-tree clean; ahead 7 behind 0; no provider-backed checks; no product code fix needed | | 2026-08-01 | claude/ds-v2-tooling-loop (PR #1568) | b76bcc9fbc39f2f986a52869c810ba3cb89994f2 | PR #1568 review-thread resolve | fixed and resolved all 13 review threads (fail-closed project identity, inventory exit code, provenance demo id, buildCmd execution, chrome pin, context7 rollback); ledger row for superseded 93d41c1 dispositioned | node --check scripts; design-sync --dry-run; no provider-backed checks | | 2026-08-01 | claude/ds-v2-correctness | 1aa9822978668a10447ef3246bd60cda6f1208cb | ds-v2 PR-A correctness (HCM/contrast/prop-unions) | ready-to-merge | unit 4878p; verify:ui 344p; style-contract HCM 5p; verify:pr-local green (eval:rag:offline 574/574; fixture 36/23); typecheck+ds-contract green | +| 2026-08-02 | claude/ds-v2-values | d33721b436c05014e8ef1f362a945fe9574e676e | DS V2 PR-V values | Wave 2 self-verified before push: 5a close-out (no value deltas remain after decision J2; contract metrics unchanged at rawColorLiterals 2 / literalShadowClasses 0 / legacyTapClasses 0), 5b tap knob 2.75rem to 3rem in @theme with --tap-min reduced to var(--spacing-tap) across 426 *-tap sites and three pins flipped in-commit, 5c --radius-md 8px to 10px across 243 rounded-md sites with the 4px-grid pin naming both half-steps and 14 arbitrary radius literals absorbed. Two carve-outs recorded in SPEC 4.10/4.6: the phone composer stays 44px below 431px, and the 2px mode-nav hairline stays off the ladder. An offline.html tap bump was reverted before push because it would have forced a service-worker CACHE_VERSION bump. Known-red local gate: tests/ui-smoke.spec.ts:2221 phone runway pin fails on unmodified origin/main here too (maxOffset 99 at base vs 111 at this head, reproduced in a pristine capture worktree) while CI run 30720360137 is green on the same tree - plan finding L, CI binding. | typecheck exit 0; verify:pr-local PASS with 4880 passed 3 skipped (4883) in 471 files, production build compiled, client-bundle secret scan passed, eval:rag:offline fixture line plus 574/574 (better than the J4 known-red baseline); verify:ui 346 passed 1 failed (the known-red phone runway pin); verify:phone-chrome --full=never contracts 116/116 and ui-tools 87/87 with the same single known-red test; test:e2e:critical 15/15; design-system contract, branch-review-ledger, outstanding-issues, sitemap, docs link/index/inventory/scripts all exit 0 | +| 2026-08-02 | claude/ds-v2-values | 48012d359b84daae347201697c82f3e433c182c5 | PR #1571 review-and-fix (ds-v2-values tap/radius) | fixed: Tools submit + account-setup close onto h-tap; phone composer input 44px pin; gate 2 demoted to implemented-partial; SPEC 407→426; short-runway/short-answer smoke pins retuned; verify:cheap + verify:pr-local + 2 smoke tests green; CI Production UI in progress | verify:cheap 471/4879; verify:pr-local build+fixtures; test:e2e 2 phone smoke passed; design-system-contract raw 2/0/0 | +| 2026-08-02 | claude/ds-v2-values | d2a29198415292a28ed1dbf1c740c94f6c12ddb3 | PR #1571 review-and-fix (ds-v2-values tap/radius) | fixed+clarified: gate2 objective acknowledges h-10/unwired audit not blocked; overlay 430px assertion scoped to media block; prior tip 48012d35 Production UI+PR required green | verify:cheap; verify:pr-local; 2 smoke; UI overlay contract; CI Production UI (1)(2)(3)+PR required SUCCESS on 48012d35 | diff --git a/docs/design-system.md b/docs/design-system.md index 6a421d3945..5d5489c7fe 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -121,22 +121,27 @@ Icon **glyphs** use the parallel `--spacing-icon-*` scale in `@theme`: a few roles (nav, composer, hero, panel headings); most icons stay one fixed size. - **Not** for container tiles (`iconTilePremium` / panel-heading tile h-9, empty-state tile h-10) or non-icon boxes (the `ToggleSwitch` knob, status dots) — those keep the integer spacing - scale. Icon glyph size is independent of the 44px tap target (§3), which stays on + scale. Icon glyph size is independent of the tap target (§3), which stays on `--spacing-tap`. ## 3. Spacing & tap targets - 4px grid via Tailwind spacing; safe-area env paddings on shell edges. -- Interactive targets use the `--spacing-tap` token (44px): `min-h-tap` / `min-w-tap` / - `size-tap`. Do **not** hand-write `min-h-11` / `h-[44px]` for tap semantics. -- Exception (documented in `globals.css`): controls scrolled deep inside sheets stay on - `min-h-12` (48px) to satisfy the ui-smoke sub-pixel tap check — do not "fix" them down. +- Interactive targets use the `--spacing-tap` token (48px): `min-h-tap` / `min-w-tap` / + `size-tap`. Do **not** hand-write `min-h-11` / `h-[44px]` for tap semantics, and size a grid + track that holds a tap target with `var(--spacing-tap)` rather than a copy of the number. +- `min-h-12` sites now match the token instead of exceeding it — leave them; never "fix" a tap + target down. +- Exception (documented in `globals.css`): the phone composer icon buttons stay 44px below + 431px, because the dock height is part of the search-chrome contract. ## 4. Radius & shadows -- Radii come from `@theme` and run on the 4px grid — `xs` 4 · `sm` 6 · `md` 8 · `lg` 12 · - `xl` 16 · `2xl` 20 (px). `sm` is the one deliberate half-step, for chips and pills that read - as too heavy at 8. Roles are unchanged: `rounded-md` chips/pills · `rounded-lg` +- Radii come from `@theme` — `xs` 4 · `sm` 6 · `md` 10 · `lg` 12 · `xl` 16 · `2xl` 20 (px). + Two deliberate half-steps off the 4px grid: `sm` at 6, for chips and pills that read as too + heavy at 8, and `md` at 10, which is the control radius the v2 layer also declares. A third + half-step fails `tests/design-token-contract.test.ts`. Roles are unchanged: `rounded-md` + chips/pills · `rounded-lg` controls/cards/panels · `rounded-xl`+ sheets/dialogs. Never pass a radius token through an arbitrary value (`rounded-[var(--radius-md)]` → `rounded-md`) — the plain utility is the same token. @@ -172,7 +177,7 @@ rung — never a new number. the applications-launcher DetailDialog migration is the template for converting one. - Empty and loading states use `EmptyState` / `LoadingPanel`, not bespoke markup. - **Icon-only buttons use `IconButton`** (`ui-primitives.tsx`): its `label` prop is required and - renders `aria-label` + an `aria-hidden` glyph + a 44px tap target, so an unlabeled icon button + renders `aria-label` + an `aria-hidden` glyph + a `--spacing-tap` hit area, so an unlabeled icon button cannot be written by accident. Pass a recipe (`toolbarButton`, …) via `className` for chrome. - Composer-chrome caveat: the `answer-footer-search-*` / `desktop-home-search-*` classes are intentionally **unlayered** and beat Tailwind utilities on the same element — check the class diff --git a/docs/design-system/DECISIONS.md b/docs/design-system/DECISIONS.md index cf628fe78a..8e6f621a52 100644 --- a/docs/design-system/DECISIONS.md +++ b/docs/design-system/DECISIONS.md @@ -42,9 +42,17 @@ anything that cannot be verified is listed as blocked rather than guessed. `globals.css` — the token the `min-h-tap` / `h-tap` / `size-tap` utilities compile against. `--tap-min` in the v2 layer becomes a pure alias (`var(--spacing-tap)`), and the v2 layer never declares `--spacing-tap`. What moves with it, in the same change: the contract-test -pin (`ckb-v2-token-contract.test.ts:181-185`), the `Button` comment that still claims 44px, -and a dedicated visual-QA pass — it is a 407-call-site geometry change and ships as its own -PR (5b). No existing 48px target (`min-h-12`, 53 sites) is touched. +pins, the component comments that still claim 44px, and a dedicated visual-QA pass — it is a +426-call-site geometry change and ships as its own commit (5b). No existing 48px target +(`min-h-12`) is touched. + +**Landed (PR 5b).** Two follow-through rules came out of doing it, both now in SPEC §4.10. +A grid track sized to hold a tap-sized child reads `var(--spacing-tap)` instead of copying +its value, or the track and its child desync on the next move — seven hardcoded `2.75rem` +tracks were audited, and the one with no gap between columns (the launcher search row) was +rebound; the other six absorb the extra 4px into an existing gap or the row's own padding. +And the phone composer keeps a written 44px exception below 431px, because its height is +part of the search-chrome contract and 44px already clears WCAG 2.5.5. **Rejected.** Declaring the value in the v2 layer. In the design side's `:root`-structural copy it silently **loses** a same-specificity cascade tie to `@theme` (later wins), leaving diff --git a/docs/design-system/GATES.md b/docs/design-system/GATES.md index 8e7273fc45..71202d2f15 100644 --- a/docs/design-system/GATES.md +++ b/docs/design-system/GATES.md @@ -37,20 +37,20 @@ those rules as gates. ## 2 · The twelve system gates, labelled -| # | Gate | Status | Evidence / path to blocking | -| --- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 | Contrast ≥4.5:1 for every text/background pair, both themes, live **and** v2; `--decoration-soft` asserted below 4.5 and never on a text node | **implemented-blocking** | v2 pairs + `--text-placeholder` + `--decoration-soft` tier + recipe AST (`decoration-on-text.contract.test.ts`); live full matrix remains follow-on. | -| 2 | Tap targets ≥48px interactive; static pills never carry `min-h-tap`; no production target reduced | **implemented-partial** | Legacy-class and literal ratchets run; the 48px value itself lands in PR 5b (until then the token honestly says 44); geometry assertions live in the held visual harness. | -| 3 | Focus outline present, `--focus`, no companion ring | **planned** | `--focus` is referenced nowhere in the DS export (finding N3). Global fix first, then the check. | -| 4 | Non-colour encoding on every status indicator | **manual → planned** | `MissingValue`/`DoseLine` text marks arrive in PR 6; contract test follows. | -| 5 | Tables: semantic caption, associated headers, `aria-controls` on the expander | **implemented-partial** | Expander and alignment tested; semantic-caption rule fails today (div caption, optional). | -| 6 | Enum resilience — neutral fallback, never throws | **implemented-blocking** | `source-badges-off-vocab.dom.test.tsx`. | -| 7 | Elevation monotonicity — no child heavier than its parent | **implemented-partial** | Token-ramp monotonicity is tested; the per-surface child/parent rule is planned (needs a render-tree check). | -| 8 | Edge ownership — no `border-*` + `ring-*` on one surface; no 1px spread in a drop shadow | **implemented-partial** | Ladder is tested spread-free; the per-surface co-occurrence lint and spread-term check are planned (PR 9). The spread form was chosen _because_ it is mechanically lintable. | -| 9 | No layout-property animation; no hardcoded durations | **planned** | PR 9 wires `--duration-*`/`--ease-*` and adds the lint; ten known layout-animation sites are the seed list. | -| 10 | Named z and motion tokens have real consumers; no unapproved `z-` value | **implemented-partial** | `require-z-index-ladder` lints z discipline; motion-token consumption is planned (one consumer today against 25 hardcoded durations). | -| 11 | Published-type conformance — every `dtsPropsFor` entry assignable from source props | **planned** | PR 12 generates declarations from real types; known drift: `confirmPhraseLabel`, Sheet omissions, narrowed callbacks. | -| 12 | Component coverage — every registered component has a direct behavioural test | **implemented-partial** | Tested subset in COMPONENTS §0; registry-wide requirement lands with PR 12. | +| # | Gate | Status | Evidence / path to blocking | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 1 | Contrast ≥4.5:1 for every text/background pair, both themes, live **and** v2; `--decoration-soft` asserted below 4.5 and never on a text node | **implemented-blocking** | v2 pairs + `--text-placeholder` + `--decoration-soft` tier + recipe AST (`decoration-on-text.contract.test.ts`); live full matrix remains follow-on. | +| 2 | Tap targets ≥48px interactive (token + declared carriers today; fixed-height `h-10` controls and full interactive enumeration not yet blocked); static pills never carry `min-h-tap`; no production target reduced | **implemented-partial** | Enforced today: `--spacing-tap` is 48px and pinned from both sides (`ckb-v2-token-contract` asserts the `@theme` knob is ≥48 and that `--tap-min` is its alias), `ui-style-contract` measures the rendered floor for declared `min-height` carriers in Chromium, and the legacy-class / literal ratchets still run. **Not blocked today:** the Chromium audit skips elements whose declared height is below the floor (a fixed `h-10` control would not fail it), and `test:e2e:style-contract` is not part of `verify:cheap`. Path to blocking: enumerate rendered interactive elements with an explicit exception list (phone composer below 431px is already written — SPEC §4.10), then wire that audit into required CI. Per-surface geometry stays in the held visual harness. | +| 3 | Focus outline present, `--focus`, no companion ring | **planned** | `--focus` is referenced nowhere in the DS export (finding N3). Global fix first, then the check. | +| 4 | Non-colour encoding on every status indicator | **manual → planned** | `MissingValue`/`DoseLine` text marks arrive in PR 6; contract test follows. | +| 5 | Tables: semantic caption, associated headers, `aria-controls` on the expander | **implemented-partial** | Expander and alignment tested; semantic-caption rule fails today (div caption, optional). | +| 6 | Enum resilience — neutral fallback, never throws | **implemented-blocking** | `source-badges-off-vocab.dom.test.tsx`. | +| 7 | Elevation monotonicity — no child heavier than its parent | **implemented-partial** | Token-ramp monotonicity is tested; the per-surface child/parent rule is planned (needs a render-tree check). | +| 8 | Edge ownership — no `border-*` + `ring-*` on one surface; no 1px spread in a drop shadow | **implemented-partial** | Ladder is tested spread-free; the per-surface co-occurrence lint and spread-term check are planned (PR 9). The spread form was chosen _because_ it is mechanically lintable. | +| 9 | No layout-property animation; no hardcoded durations | **planned** | PR 9 wires `--duration-*`/`--ease-*` and adds the lint; ten known layout-animation sites are the seed list. | +| 10 | Named z and motion tokens have real consumers; no unapproved `z-` value | **implemented-partial** | `require-z-index-ladder` lints z discipline; motion-token consumption is planned (one consumer today against 25 hardcoded durations). | +| 11 | Published-type conformance — every `dtsPropsFor` entry assignable from source props | **planned** | PR 12 generates declarations from real types; known drift: `confirmPhraseLabel`, Sheet omissions, narrowed callbacks. | +| 12 | Component coverage — every registered component has a direct behavioural test | **implemented-partial** | Tested subset in COMPONENTS §0; registry-wide requirement lands with PR 12. | **Computed-style suites (planned, PR 1–2):** Tailwind conflict order · v2 cascade, ancestor **and** same-node forms · HCM remapping under all three v2 selectors · z-index · target @@ -68,33 +68,33 @@ as design tokens · export/preview parity. ## 3 · The prohibition table -| Never do this | Gate | Status | -| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | -| Reintroduce `.ckb-v2:not(.dark)` | Cascade computed-style test (PR 1) + contract-test selector filter update | implemented-blocking — landed #1538; contract parser + dark-ink regression guard | -| Add `prefers-color-scheme` rules to the v2 layer | — (users pin a theme against OS preference) | **manual** | -| Paint a numeral with a status colour; use amber/red decoratively | Colour-boundary lint | planned | -| Put `--decoration-soft`/`--text-soft` on a text node | Tier pinned both sides in the token contract; recipe AST (`decoration-on-text.contract.test.ts`) | implemented-blocking (recipes); broader AST follow-on | -| Uppercase a dose unit | `Quantity` pins `normal-case` + unit test | implemented-blocking (branch) | -| Invert a PDF, diagram or clinical image in any theme | `DocumentFrame` refuses transforms; CSS check | planned | -| Border **and** ring on one surface, or a 1px spread in a drop shadow | Gate 8 | implemented-partial | -| A child shadow heavier than its parent's | Gate 7 | implemented-partial | -| Use `--shadow-tight`/any alias in new code | PR 9 recipe cleanup, then lint | planned | -| Raw pixel size, padding, radius or line-height in markup | Contract ratchet | implemented-partial (colour/shadow/tap literals only) | -| Animate `width`, `height`, `grid-template-*`, `top`, `left`, `gap` | Gate 9 | planned | -| Hardcode a transition duration | Gate 9 | planned | -| A `z-` value outside the named rungs | `require-z-index-ladder` (ESLint) | implemented-blocking | -| Ship a registered component without a direct test | Gate 12 | implemented-partial | -| Lower a production tap target below 48px | Gate 2 + `ui-smoke` flake history + held visual probe | implemented-partial | -| Construct an enabled control with no action, destination or name | PR 4 discriminated unions + DOM contracts; `require-button-wiring` covers raw `