diff --git a/.design-sync/conventions.md b/.design-sync/conventions.md index aacdffc1b4..0457bebec8 100644 --- a/.design-sync/conventions.md +++ b/.design-sync/conventions.md @@ -2,10 +2,9 @@ > The system of record is `docs/design-system/` (SPEC · TOKENS · COMPONENTS · DECISIONS · > GATES). This file is the designer-facing build idiom; where it disagrees with that set, -> treat the disagreement as a defect here and follow the set. Known nuance: the -> `--shadow-*` role aliases below are **legacy-compat only** — new code uses the `--e0`… -> `--e4` tiers directly; the aliases are scheduled for retirement -> (`docs/design-system/TOKENS.md` §6). +> treat the disagreement as a defect here and follow the set. New code uses the `--e0`… +> `--e4` tiers directly; the remaining `--shadow-*` roles are legacy-compat aliases +> documented in `docs/design-system/TOKENS.md` §6. ## Setup @@ -31,8 +30,8 @@ arbitrary-value form — never hardcoded colours: `--info-solid`. For a filled non-danger status use `-bg` + `-text`. - Elevation: the `--e0` … `--e4` ladder — `shadow-[var(--e2)]`, `hover:shadow-[var(--e3)]`. `--e0` flush · `--e1` resting hairline · `--e2` cards/popovers · `--e3` hover/lifted chrome · - `--e4` modals/sheets/drawers. The role names are aliases onto tiers: - `--shadow-tight`→`--e1`, `--shadow-card`/`--shadow-soft`→`--e2`, `--shadow-hover`→`--e3`, + `--e4` modals/sheets/drawers. The remaining role names are aliases onto tiers: + `--shadow-card`/`--shadow-soft`→`--e2`, `--shadow-hover`→`--e3`, `--shadow-elevated`/`--shadow-lux`→`--e4`. `--shadow-inset` stays bespoke. Never hand-roll a `shadow-[0_…]` literal. - Focus ring: `focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]`. diff --git a/docs/branch-review-records/a575dd1afa8eb2039c28d877548cd8f17499a67838484db45ce4d92c7593d198.record.md b/docs/branch-review-records/a575dd1afa8eb2039c28d877548cd8f17499a67838484db45ce4d92c7593d198.record.md new file mode 100644 index 0000000000..4776e65d6e --- /dev/null +++ b/docs/branch-review-records/a575dd1afa8eb2039c28d877548cd8f17499a67838484db45ce4d92c7593d198.record.md @@ -0,0 +1 @@ +| 2026-08-14 | claude/design-token-relands | f94fa299a61f1337825083fe1178c66f65680329 | design tokens: --shadow-tight retirement reland, contract ratchet re-pin, search-band badge token, DS Track A3 closeout | self-review; 4 commits, value-preserving, gates green except a pre-existing main failure | verify:pr-local (all stages pass except pre-existing check:medication-lexicon-report, reproduced on pristine origin/main); vitest 601 files/6510 passed; design-token-contract + tailwind-merge 69 passed; check:design-system-contract zero slack, mutation-verified; tailwind emission probe for the new utilities | diff --git a/docs/design-system.md b/docs/design-system.md index 5d5489c7fe..73e9116a2a 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -150,7 +150,7 @@ Icon **glyphs** use the parallel `--spacing-icon-*` scale in `@theme`: instead of bleeding. `--e0` flush · `--e1` resting hairline · `--e2` cards/popovers · `--e3` hover/lifted chrome · `--e4` modals/sheets/drawers. Dark lifts with a top highlight rather than more black. -- The role names are **aliases onto tiers**, not independent values: `--shadow-tight` → `--e1`; +- The remaining role names are **aliases onto tiers**, not independent values: `--shadow-card` / `--shadow-soft` → `--e2`; `--shadow-hover` → `--e3`; `--shadow-elevated` / `--shadow-lux` → `--e4`. `--shadow-inset`, `--shadow-rail-active`, `--shadow-focus` and `--glow-primary/soft` stay bespoke. All are removed under forced-colors, ladder included. @@ -220,7 +220,7 @@ image"}` — never a possibly-empty variable alone. | `Number(query.page ?? 1)` | `parseInt` + `Number.isFinite` + `>= 1` clamp | | `alt={caption}` | `alt={caption?.trim() \|\| "Clinical document image"}` | | new `z-[73]` for a popover | an existing ladder rung, or `Sheet` | -| `shadow-[0_5px_12px_rgba(0,122,120,0.16)]` | `shadow-[var(--shadow-tight)]` | +| `shadow-[0_5px_12px_rgba(0,122,120,0.16)]` | `shadow-[var(--e1)]` | ## 9. Verification gates — Definition of Done for UI PRs diff --git a/docs/design-system/GATES.md b/docs/design-system/GATES.md index e485e7190b..202fb362d1 100644 --- a/docs/design-system/GATES.md +++ b/docs/design-system/GATES.md @@ -111,11 +111,11 @@ theme-list parity, and remote design-project publication remain separate concern | 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 | `check:design-system-contract` — `imageInversions`, pinned at **zero**, not ratcheted | **implemented-blocking** (9 Aug 2026) — CSS `filter`/`backdrop-filter` plus the Tailwind `invert`/`hue-rotate` utilities; see §5 | -| Border **and** ring on one surface, or a 1px spread in a drop shadow | `check:design-system-contract` — `edgeOwnershipConflicts` (27) + `onePixelShadowSpreads` (2) | **implemented-blocking for new use** — AST/CSS ratchets with per-path pins; the recorded debt itself is Gate 8's remaining half | +| Border **and** ring on one surface, or a 1px spread in a drop shadow | `check:design-system-contract` — `edgeOwnershipConflicts` (25) + `onePixelShadowSpreads` (2) | **implemented-blocking for new use** — AST/CSS ratchets with per-path pins; the recorded debt itself is Gate 8's remaining half | | A child shadow heavier than its parent's | Gate 7 | implemented-partial | -| Use `--shadow-tight`/any alias in new code | `check:design-system-contract` — `legacyShadowAliases`, ratcheted at 224 with per-path pins | **implemented-blocking for new use** — a new alias in any file fails; retiring the existing 224 is `#262` | -| Raw pixel size, padding, radius, gap or line-height in markup | `check:design-system-contract` — `rawPaddingLiterals` (67), `rawRadiusLiterals` (24), `rawGapLiterals` (34), `rawLineHeightLiterals` (3) | **implemented-blocking for new use** (9 Aug 2026) — per-path ratchets over both the utility and the CSS-declaration spelling, so a literal cannot move into `globals.css` to escape. Values containing a CSS function (`env(`, `clamp(`, `max(`, `calc(`) are sanctioned computed forms and exempt. Raw _size_ is still covered only for tap/shadow/colour | -| Animate `width`, `height`, `grid-template-*`, `top`, `left`, `gap` | `check:design-system-contract` — `layoutTransitionExceptions`, ratcheted at 12 with per-path pins | **implemented-blocking for new use** — `SAFE_TRANSITION_PROPERTIES` carries the compositor-only allowlist; phone chrome's deliberate `grid-template-rows` is in the recorded 12 | +| Use `--shadow-tight`/any alias in new code | `check:design-system-contract` — `legacyShadowAliases`, ratcheted at 119 with per-path pins | **implemented-blocking for new use** — a new alias in any file fails. `--shadow-tight` itself is retired onto `--e1` and additionally pinned by a tracked-tree sweep in `tests/design-token-contract.test.ts` (it was silently reverted once by the `acf78bf` merge); the remaining 119 are the other six aliases, still `#262` | +| Raw pixel size, padding, radius, gap or line-height in markup | `check:design-system-contract` — `rawPaddingLiterals` (63), `rawRadiusLiterals` (24), `rawGapLiterals` (32), `rawLineHeightLiterals` (3) | **implemented-blocking for new use** (9 Aug 2026) — per-path ratchets over both the utility and the CSS-declaration spelling, so a literal cannot move into `globals.css` to escape. Values containing a CSS function (`env(`, `clamp(`, `max(`, `calc(`) are sanctioned computed forms and exempt. Raw _size_ is still covered only for tap/shadow/colour | +| Animate `width`, `height`, `grid-template-*`, `top`, `left`, `gap` | `check:design-system-contract` — `layoutTransitionExceptions`, ratcheted at 11 with per-path pins | **implemented-blocking for new use** — `SAFE_TRANSITION_PROPERTIES` carries the compositor-only allowlist; phone chrome's deliberate `grid-template-rows` is in the recorded 11 | | Hardcode a transition duration | `check:design-system-contract` — `hardcodedMotionClasses` (**zero**) + `hardcodedCssMotionDurations` (42) | **implemented-blocking** for the Tailwind `duration-*`/`delay-*`/`transition-all` form; the CSS form is a ratchet, so its 42 are debt | | A `z-` value outside the named rungs | `require-z-index-ladder` (ESLint) | implemented-blocking | | Ship a registered component without a direct publication test | Gate 12 | implemented-blocking | diff --git a/docs/outstanding-issues-inbox/04a0b8d6-8af9-4779-8af8-8e3b6e9a3b2b.json b/docs/outstanding-issues-inbox/04a0b8d6-8af9-4779-8af8-8e3b6e9a3b2b.json new file mode 100644 index 0000000000..1613fbd2b7 --- /dev/null +++ b/docs/outstanding-issues-inbox/04a0b8d6-8af9-4779-8af8-8e3b6e9a3b2b.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "id": "04a0b8d6-8af9-4779-8af8-8e3b6e9a3b2b", + "createdOn": "2026-08-14", + "action": "done", + "payload": { + "id": "#262", + "outcome": "CLOSED 2026-08-14 — all three parts settled, verified against code rather than rows. (1) The --shadow-tight retirement is re-landed: 130 call sites across 67 files onto var(--e1) and both declarations deleted. It had closed 2026-08-10 via PR #1803 and was silently reverted by the acf78bf merge on 2026-08-11; two stranded comments (globals.css 'the resting-hairline role is gone', and the token test's 'unlike the --shadow-tight assertion above') survived that merge while the code they describe did not, which is how the loss stayed invisible. The alias was a pure pass-through in both themes, and the forced-colors block scopes ':root, .dark' — the same html element the alias is declared on — so it already resolved through the flattened '--e1: none'; value-preserving in light, dark and forced-colors. tests/design-token-contract.test.ts now sweeps the tracked src tree for BOTH spellings (declaration and var() consumer) rather than asserting the declaration alone, so the gate no longer depends on which half of a bad merge lands. Mutation-verified in both directions. (2) NOT ACTIONABLE BY DESIGN, and this was already adjudicated — docs/design-system/GATES.md section 3 records that the decidable half of step selection shipped 9 Aug inside check:design-system-contract (a declared @theme step that no production surface selects fails the build; it caught --text-2xl-compact, retired 10 Aug, closing #297, and the exemption list is empty by design and gated from both sides). The remaining half — which existing step a component picks — is explicitly documented there as something 'nothing mechanical can' gate, being a judgement about the rendered design rather than a property of the source, with a standing instruction not to write an ESLint rule duplicating the arbitrary-value check that already ships via check:type-scale. So this part needs no work and should not be re-attempted. (3) SHIPPED in PR #1780 per #301 — rawPaddingLiterals, rawRadiusLiterals and rawLineHeightLiterals are all live baseline keys enforced at check-design-system-contract.mjs:215-232 over both the class and CSS-declaration spellings, plus rawGapLiterals beyond the original ask. Note for anyone re-measuring part 2: a raw grep for text- overcounts, because it matches the --text-*: declarations and doc comments too — that is the 733-vs-705 discrepancy GATES.md line 66 already warns about, and it reproduces today (a naive sweep returns 773). Use the AST class-root pass." + } +} diff --git a/docs/outstanding-issues-inbox/11e56221-8d46-4c9a-a89c-78621f18e754.json b/docs/outstanding-issues-inbox/11e56221-8d46-4c9a-a89c-78621f18e754.json new file mode 100644 index 0000000000..0601cf5ff3 --- /dev/null +++ b/docs/outstanding-issues-inbox/11e56221-8d46-4c9a-a89c-78621f18e754.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "id": "11e56221-8d46-4c9a-a89c-78621f18e754", + "createdOn": "2026-08-14", + "action": "add", + "payload": { + "pri": "P3", + "type": "task", + "summary": "Three mode-nav icon glyphs sit at 17px, off the --spacing-icon-* scale, and no gate flags them", + "detail": "Split out of #275 rather than folded into its badge-box token. mode-nav/mode-nav.tsx:64 and :214 and mode-nav/nav-slot-ink.tsx:44 size their with h-[1.0625rem] w-[1.0625rem] — 17px against an icon scale of 12/14/16/20/24 (--spacing-icon-xs..xl in the globals.css @theme block). #275 counted these among its five files because they share the badge's number, but they are a different role: the badge is a text-bearing box sized around its own --text-2xs numeral, these are glyphs. They are now the only consumers of that value, since the badge moved to --spacing-search-band-badge. Nothing gates this: check-icon-scale.mjs enforces only the retired 4.5 (18px) half-step and its header states it deliberately does NOT flag arbitrary h-[Nrem], because non-icon boxes legitimately use that form. So this is unguarded and will not self-report. Why it was not just fixed: snapping to size-icon-md (16px) or size-icon-lg (20px) visibly changes nav chrome at every breakpoint, and 17px is close enough to 16 that the choice looks arbitrary without seeing it rendered — a design call, not a token swap. Next: get a Chromium look at mode-nav at phone and desktop widths with the icon at 16 and at 20, pick one, then migrate all three together. If 17px turns out to be deliberate, say so in a comment at the call site and consider whether check:icon-scale should flag off-scale arbitrary icon sizes on -typed elements specifically, which would have surfaced this. Stop: do not add a 17px step to --spacing-icon-* to make the problem go away — that token block's own comment argues against widening the scale off the 4px grid, and it would sanction the drift rather than resolve it.", + "source": "session 2026-08-14; split from #275; check-icon-scale.mjs header" + } +} diff --git a/docs/outstanding-issues-inbox/25f6b53a-86e2-42e9-9b38-1c2daae28892.json b/docs/outstanding-issues-inbox/25f6b53a-86e2-42e9-9b38-1c2daae28892.json new file mode 100644 index 0000000000..0c8413f20d --- /dev/null +++ b/docs/outstanding-issues-inbox/25f6b53a-86e2-42e9-9b38-1c2daae28892.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "id": "25f6b53a-86e2-42e9-9b38-1c2daae28892", + "createdOn": "2026-08-14", + "action": "done", + "payload": { + "id": "#319", + "outcome": "RESOLVED 2026-08-14 — the retirement is re-landed. 130 var(--shadow-tight) call sites across 67 files now read var(--e1), and both role-alias declarations are deleted; a tracked-tree grep for the token returns zero. This row was queued as inbox request 210e3db5 and reconciled into the ledger by PR #1936 while the work was already in flight, which is why the fix arrives as a done rather than a cancel. Value-preservation was confirmed before editing rather than inherited from #1803: the alias was a pure pass-through in both themes, and the forced-colors block at globals.css:3613 scopes ':root, .dark' — the same html element the alias is declared on — so --shadow-tight already resolved through the flattened '--e1: none' there. Identical in light, dark and forced-colors. The .ckb-v2 redeclaration hazard this row's neighbours warn about does not bite for the same reason: .ckb-v2 sits on and .ckb-v2.ckb-v2 outspecifies :root, so both spellings substitute against the winning v2 tier. Guard added, and it is deliberately stronger than the one #1803 shipped: tests/design-token-contract.test.ts now sweeps the tracked src tree for BOTH spellings (a '--shadow-tight:' declaration and a 'var(--shadow-tight)' consumer) rather than asserting only that the declaration points at --e1. A declaration-only assertion would have caught the acf78bf revert, but only because the declarations happened to come back alongside the call sites; the tree sweep is independent of which half of a bad merge lands. Mutation-verified in both directions. Two artefacts confirm the original loss and are now consistent again: the globals.css comment 'the resting-hairline role is gone' and this test file's 'unlike the --shadow-tight assertion above' both survived acf78bf while the code they describe did not. This row's stop rule was honoured — the contract baseline was NOT refreshed to absorb the change; it was tightened DOWN to the moved measurement (legacyShadowAliases 220 -> 119) in its own commit, so the movement is pinned rather than hidden. #302 was re-measured after the reland rather than actioned on its stale numbers, and is closed in the same PR. Not bundled with the #1800 re-land, per this row's other stop rule." + } +} diff --git a/docs/outstanding-issues-inbox/39b08439-bd16-4b6d-b218-f04ad0a9a8cd.json b/docs/outstanding-issues-inbox/39b08439-bd16-4b6d-b218-f04ad0a9a8cd.json new file mode 100644 index 0000000000..2aeacb29aa --- /dev/null +++ b/docs/outstanding-issues-inbox/39b08439-bd16-4b6d-b218-f04ad0a9a8cd.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "id": "39b08439-bd16-4b6d-b218-f04ad0a9a8cd", + "createdOn": "2026-08-14", + "action": "done", + "payload": { + "id": "#302", + "outcome": "RESOLVED 2026-08-14 — the five ratchets carrying slack are re-pinned to measured, in the same PR that paid the debt, which is the coupling this row asked for. legacyShadowAliases 220 -> 119 (the row measured 193 on 2026-08-12; the reland of --shadow-tight pays down the debt the acf78bf revert had re-hidden, so the real gap was 101 units, not 27), edgeOwnershipConflicts 27 -> 25, rawPaddingLiterals 67 -> 63, rawGapLiterals 34 -> 32, layoutTransitionExceptions 12 -> 11. Regenerated with 'node scripts/check-design-system-contract.mjs --print-debt-baseline' rather than hand-edited, so per-path debtByPath moved with the totals — those are what findDebtPathRegressions compares, and the retirement moved them wholesale. Every metric in the diff decreases; nothing was absorbed upward. Verified zero slack on every ratchet afterwards by diffing the baseline against a fresh --print-metrics run. Mutation-verified: reintroducing one alias in button.tsx now fails at both the total (119 -> 120) and the per-path level, where the old 220 ceiling passed it silently. This is not the baseline refresh #262 warns against — that stop rule forbids refreshing to HIDE the movement, whereas this pins the movement in. The stale GATES.md section 3 numbers for all five were corrected in the same commit per #301. Residual risk this row should still be read for: nothing enforces the coupling. A future PR can still pay debt down and leave the ceiling, and only a manual --print-metrics diff will notice. A cheap guard would be a check that fails when any baseline metric exceeds its measured value by more than a stated tolerance." + } +} diff --git a/docs/outstanding-issues-inbox/69b9cd4a-9c2a-4e37-a146-48c7e540b87e.json b/docs/outstanding-issues-inbox/69b9cd4a-9c2a-4e37-a146-48c7e540b87e.json new file mode 100644 index 0000000000..a65f1527d1 --- /dev/null +++ b/docs/outstanding-issues-inbox/69b9cd4a-9c2a-4e37-a146-48c7e540b87e.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "id": "69b9cd4a-9c2a-4e37-a146-48c7e540b87e", + "createdOn": "2026-08-14", + "action": "add", + "payload": { + "pri": "P2", + "type": "issue", + "summary": "check:medication-lexicon-report has been failing on main for every local verify:pr-local, and no CI job runs it", + "detail": "Found 2026-08-14 while running the PR preflight for an unrelated design-token change. 'npm run check:medication-lexicon-report' reports 'docs/medication-interaction-lexicon-review.md is stale. Run npm run medications:lexicon-report and commit the result.' and exits 1. Two things make this worth a row rather than a quick fix in a passing PR. FIRST, it is on main, not on any branch: reproduced in a clean worktree checked out at pristine origin/main (both d47aa6d and, after a merge, 79b01b3), with a diff touching zero medication, lexicon or data/ files. SECOND, and this is the part that explains why it went unnoticed, NOTHING IN CI RUNS IT — a grep for medication-lexicon-report across .github/workflows/ returns nothing. It is reached only through the local verify:pr-local chain, where it is the LAST step, so it fails every local PR preflight while every CI run stays green. The failure mode is therefore self-concealing in the direction that matters: the gate is invisible to the required checks and visible only to whoever is about to hand off, who then has to decide whether an unrelated stale generated doc is theirs to fix. It was not fixed in the design-token PR that found it, deliberately: the report is a clinical-facing generated document and regenerating it inside a CSS-token PR would bundle a clinical-risk artefact with unrelated chores, which AGENTS.md PR bundling explicitly forbids. Next: run 'npm run medications:lexicon-report', read the resulting diff to confirm it is a pure regeneration and not a content change needing clinical review, and commit it in its own PR. Then decide the real question this exposes — either wire the check into CI so it cannot silently rot again, or move it out of verify:pr-local so it stops failing preflights it does not gate. A check in the local chain but not in CI is the worst of both. Stop: do not simply delete the check or drop it from verify:pr-local to get a green preflight; the staleness is real and the generated file is a clinical artefact.", + "source": "session 2026-08-14; PR #1942 preflight; reproduced on pristine origin/main d47aa6d and 79b01b3; grep over .github/workflows" + } +} diff --git a/docs/outstanding-issues-inbox/7268da45-5b77-4583-a3cb-27e5ec7067b1.json b/docs/outstanding-issues-inbox/7268da45-5b77-4583-a3cb-27e5ec7067b1.json new file mode 100644 index 0000000000..9b24047fb2 --- /dev/null +++ b/docs/outstanding-issues-inbox/7268da45-5b77-4583-a3cb-27e5ec7067b1.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "id": "7268da45-5b77-4583-a3cb-27e5ec7067b1", + "createdOn": "2026-08-14", + "action": "add", + "payload": { + "pri": "P3", + "type": "issue", + "summary": "Claude Code web containers can ship Node 22 with no node_modules, so npm ci fails engine-strict before any work starts", + "detail": "Hit 2026-08-14 at the start of a Claude Code on the web session, and it blocks a session completely until worked around, so it is worth recording even though the cause is the container image rather than this repo. The container provided /opt/node20, /opt/node21 and /opt/node22 with node22 on PATH, no nvm, and no node_modules in either the primary checkout or a fresh worktree. package.json requires node >=24.15.0 <25 with engine-strict, so 'npm ci --include=dev' aborts immediately with 'notsup Required: {node: >=24.15.0 <25, npm: 11.x} Actual: {npm: 10.9.7, node: v22.22.2}'. Nothing in the repo can fix this from inside, because the failure happens before any repo script can run — .nvmrc correctly says 24 and is simply not consulted, and there is no nvm for it to drive. Workaround used, which took about a minute and is safe: fetch the current 24.x from the nodejs.org dist index, untar to /opt/node24, and prefix subsequent commands with 'export PATH=/opt/node24/bin:/opt/node24/bin:/root/.local/bin:/root/.cargo/bin:/usr/local/go/bin:/opt/node22/bin:/opt/maven/bin:/opt/gradle/bin:/opt/rbenv/bin:/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'. Everything downstream then behaved normally — npm ci, the full unit suite, build, and the Playwright-free gates all passed. Worth knowing that this is a DIFFERENT surface from the Codex Cloud provisioning path: scripts/setup-codex-cloud.sh and scripts/setup-codex-worktree.mjs cover Codex, and docs/codex-cloud.md is explicit that Cloud mirrors the tracked toolchain, but neither runs for a Claude Code web session, so that hardening does not carry over. Next: decide whether this deserves repo-side help at all. Options are a short note in the AGENTS.md or CLAUDE.md orientation telling an agent to install Node 24 to /opt/node24 and re-export PATH rather than concluding the environment is broken, or a small bootstrap script equivalent to the Codex ones that a web session can run first. Prefer the note: a bootstrap script that downloads a runtime is a bigger surface than the problem. Stop: do not relax the engines range, drop engine-strict, or pass --force to get npm ci through — the Node 24 floor is enforced deliberately in several places (preinstall, check:runtime, scripts/dev-free-port.mjs) and loosening it to accommodate a bad container would disable a real guard.", + "source": "session 2026-08-14; Claude Code web container for PR #1942" + } +} diff --git a/docs/outstanding-issues-inbox/dcb09280-0436-4651-a707-b0007e872d7b.json b/docs/outstanding-issues-inbox/dcb09280-0436-4651-a707-b0007e872d7b.json new file mode 100644 index 0000000000..45bc75845d --- /dev/null +++ b/docs/outstanding-issues-inbox/dcb09280-0436-4651-a707-b0007e872d7b.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "id": "dcb09280-0436-4651-a707-b0007e872d7b", + "createdOn": "2026-08-14", + "action": "done", + "payload": { + "id": "#275", + "outcome": "RESOLVED 2026-08-14 for the badge box; two carve-outs re-filed rather than guessed. SCOPE RE-MEASURED AGAIN on merged main and it has SHRUNK back, in the opposite direction to this row's 2026-08-12 re-measure: the badge role is down from five files to ONE. #170's convergence landed in between — document-search-results.tsx now renders the shared control and therapy-compass/filter-sheet.tsx was deleted outright (PRs #1885, #1889, #1910) — so the extraction reabsorbed the leak this row was written about. Fixed: 1.0625rem is now --spacing-search-band-badge in the globals.css @theme block, consumed as h-search-band-badge / min-w-search-band-badge at result-filter-control.tsx:236. Value-preserving and PROVEN, not inferred: compiling globals.css through @tailwindcss/postcss emits '.h-search-band-badge { height: var(--spacing-search-band-badge) }' and the matching min-width rule. No ratchet moved. NOT done, deliberately, each now its own follow-up: (a) pr-[0.6875rem] and min-[414px]:max-[429px] stay raw — the repo defines ZERO --breakpoint-* tokens and eight peer sites use the same raw min-[]/max-[] form (359px, 389px, 414px), so naming one window while the peers stay raw is this row's own drift on another axis, and Tailwind named breakpoints would add variants across the whole utility surface; that is a repo-wide decision. (b) The three remaining 1.0625rem hits in mode-nav.tsx:64,214 and nav-slot-ink.tsx:44 are NOT this token — they size glyphs, a 17px icon against a 12/14/16/20/24 --spacing-icon-* scale, so folding them under a badge token would merge two roles that only share a number. check:icon-scale deliberately does not flag arbitrary h-[Nrem], so they are unguarded but real. Snapping them to 16 or 20px is a visible nav-chrome change and a design call. This row's stop rule ('do not change the measurements themselves') was honoured — every measured justification comment is intact and no value moved." + } +} diff --git a/docs/outstanding-issues-inbox/fc23f1ec-c597-4690-b256-2263ecf73c86.json b/docs/outstanding-issues-inbox/fc23f1ec-c597-4690-b256-2263ecf73c86.json new file mode 100644 index 0000000000..893918ed2e --- /dev/null +++ b/docs/outstanding-issues-inbox/fc23f1ec-c597-4690-b256-2263ecf73c86.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "id": "fc23f1ec-c597-4690-b256-2263ecf73c86", + "createdOn": "2026-08-14", + "action": "add", + "payload": { + "pri": "P3", + "type": "rec", + "summary": "Decide whether responsive breakpoint windows get named tokens, or stay raw min-[]/max-[] everywhere", + "detail": "Split out of #275 rather than guessed at. The repo defines ZERO --breakpoint-* tokens, and at least nine sites hand-write the arbitrary form: min-[414px]:max-[429px] at clinical-dashboard/result-filter-control.tsx:231, plus max-[359px] (search-heading-mockups, differentials/diagnosis-map-panel.tsx:1036, clinical-dashboard/account-setup-dialog.tsx:98) and max-[389px] (factsheets/factsheets-search-page.tsx:176, clinical-dashboard/search-results-header-band.tsx:532, factsheets-compact-view-mockups). #275 asked for the 414-429 window to be tokenised alongside the badge box; that was deliberately NOT done, because naming one window while eight peers stay raw reintroduces exactly the one-call-site drift #275 exists to stop, just on a different axis. This is a real decision with two defensible answers and it should be made once, for all of them. (a) Stay raw and say so in docs/design-system/GATES.md: the values are per-device band edges carrying measured justifications in their own comments, they are not a scale, and a Tailwind 4 --breakpoint-* entry adds BOTH the min and max variant to every utility in the build for a single consumer. (b) Name them: Tailwind 4 --breakpoint- generates : and max-:, so the 414-429 window needs two entries (414px and 430px, since max-[429px] is inclusive and max- is exclusive), and 359/389 would want their own. Note the mockup hits are design scratch and out of scope for any gate. Next: pick (a) or (b), record it in GATES.md section 3 so the next session does not re-derive it, and only then migrate. Stop: do not migrate one window ahead of the decision.", + "source": "session 2026-08-14; split from #275 during the design-token relands PR" + } +} diff --git a/docs/redesign/02-design-direction.md b/docs/redesign/02-design-direction.md index 743a95b233..89ef27b360 100644 --- a/docs/redesign/02-design-direction.md +++ b/docs/redesign/02-design-direction.md @@ -45,7 +45,7 @@ A precision clinical instrument: calm, quiet, and trustworthy. A true-neutral gr - Spacing: Tailwind 4px grid, used on the 4/8/12/16/24/32/48/64 rhythm; no arbitrary off-scale values in new code. - Radius scale (Tailwind `@theme` override), on the 4px grid: `xs 0.25rem` (4) · `sm 0.375rem` (6, the one deliberate half-step, for chips/pills) · `md 0.5rem` (8) chips/inner elements · `lg 0.75rem` (12) controls/inputs/cards/panels · `xl 1rem` (16) sheets/dialogs · `2xl 1.25rem` (20) large shells. -- Elevation is a numbered ladder `--e0 … --e4`: one monotonic sequence that sorts by name, hue-tinted rather than flat grey, with negative spread so a shadow pulls inward instead of bleeding. `--e0` flush · `--e1` resting hairline · `--e2` cards/popovers · `--e3` hover/lifted chrome · `--e4` modals/sheets/drawers. The role names are aliases onto tiers, not independent values: `--shadow-tight`→`--e1`, `--shadow-card`/`--shadow-soft`→`--e2`, `--shadow-hover`→`--e3`, `--shadow-elevated`/`--shadow-lux`→`--e4`. `--shadow-inset` stays a bespoke hairline top-light. Dark lifts with a top highlight rather than more black. Never hand-roll a `shadow-[0_…]` literal. +- Elevation is a numbered ladder `--e0 … --e4`: one monotonic sequence that sorts by name, hue-tinted rather than flat grey, with negative spread so a shadow pulls inward instead of bleeding. `--e0` flush · `--e1` resting hairline · `--e2` cards/popovers · `--e3` hover/lifted chrome · `--e4` modals/sheets/drawers. The remaining role names are aliases onto tiers, not independent values: `--shadow-card`/`--shadow-soft`→`--e2`, `--shadow-hover`→`--e3`, `--shadow-elevated`/`--shadow-lux`→`--e4`. `--shadow-inset` stays a bespoke hairline top-light. Dark lifts with a top highlight rather than more black. Never hand-roll a `shadow-[0_…]` literal. ### Motion diff --git a/docs/redesign/permanent-colour-direction.md b/docs/redesign/permanent-colour-direction.md index 613d671a81..b02e15ee18 100644 --- a/docs/redesign/permanent-colour-direction.md +++ b/docs/redesign/permanent-colour-direction.md @@ -102,7 +102,7 @@ Shadows are `--e0` … `--e4` — one monotonic sequence that sorts by name, hue | `--e3` | Hover, lifted chrome | | `--e4` | Modals, sheets, drawers | -The role names are aliases onto tiers, not independent values: `--shadow-tight` → `--e1`; `--shadow-card` / `--shadow-soft` → `--e2`; `--shadow-hover` → `--e3`; `--shadow-elevated` / `--shadow-lux` → `--e4`. Dark lifts with a top highlight rather than more black. Reach for a tier; never hand-roll a `shadow-[0_…]`. +The remaining role names are aliases onto tiers, not independent values: `--shadow-card` / `--shadow-soft` → `--e2`; `--shadow-hover` → `--e3`; `--shadow-elevated` / `--shadow-lux` → `--e4`. Dark lifts with a top highlight rather than more black. Reach for a tier; never hand-roll a `shadow-[0_…]`. ## Role contract diff --git a/scripts/design-system-contract-baseline.json b/scripts/design-system-contract-baseline.json index 075da926e7..aa5fed15e8 100644 --- a/scripts/design-system-contract-baseline.json +++ b/scripts/design-system-contract-baseline.json @@ -5,19 +5,19 @@ "legacyTapClasses": 0, "colourOnlyStatusIndicators": 4, "statusColouredNumerals": 2, - "edgeOwnershipConflicts": 27, + "edgeOwnershipConflicts": 25, "onePixelShadowSpreads": 2, "hardcodedCssMotionDurations": 42, "rawCssZIndices": 9, "legacyPaletteUtilities": 0, "darkColorOverrides": 0, - "legacyShadowAliases": 220, + "legacyShadowAliases": 119, "arbitraryTracking": 0, - "rawPaddingLiterals": 67, + "rawPaddingLiterals": 63, "rawRadiusLiterals": 24, - "rawGapLiterals": 34, + "rawGapLiterals": 32, "rawLineHeightLiterals": 3, - "layoutTransitionExceptions": 12, + "layoutTransitionExceptions": 11, "textSoftConsumers": 0, "errorStateCountProps": 0, "failedStateResultCounts": 0 @@ -39,15 +39,13 @@ "src/components/clinical-dashboard/visual-evidence.tsx": 1 }, "edgeOwnershipConflicts": { + "src/components/DocumentTagCloud.tsx": 3, "src/components/clinical-dashboard/dashboard-nav.tsx": 1, "src/components/clinical-dashboard/favourites-command-library-page.tsx": 1, "src/components/clinical-dashboard/favourites-library-nav.tsx": 1, "src/components/clinical-dashboard/master-search-header.tsx": 5, - "src/components/differentials/diagnosis-map-panel.tsx": 1, - "src/components/DocumentTagCloud.tsx": 3, "src/components/formulation/formulation-builder-page.tsx": 3, "src/components/formulation/formulation-compare-page.tsx": 1, - "src/components/formulation/formulation-home-page.tsx": 1, "src/components/pwa-lifecycle.tsx": 5, "src/components/services/services-navigator-page.tsx": 1, "src/components/specifiers/specifier-builder-page.tsx": 1, @@ -66,98 +64,77 @@ "legacyPaletteUtilities": {}, "darkColorOverrides": {}, "legacyShadowAliases": { - "src/app/globals.css": 9, + "src/app/globals.css": 1, "src/app/layout.tsx": 1, "src/app/not-found.tsx": 1, - "src/components/applications-launcher-page.tsx": 8, + "src/components/DocumentViewer.tsx": 1, + "src/components/applications-launcher-page.tsx": 3, "src/components/calculators/bedside-sheet.tsx": 1, "src/components/calculators/calculator-sheet.tsx": 1, - "src/components/calculators/calculator-ui.tsx": 2, "src/components/calculators/clinical-console.tsx": 1, - "src/components/calculators/directory-grid.tsx": 3, - "src/components/calculators/guided-flow.tsx": 4, + "src/components/calculators/directory-grid.tsx": 2, + "src/components/calculators/guided-flow.tsx": 3, "src/components/calculators/search-detail.tsx": 8, - "src/components/calculators/search-page.tsx": 7, - "src/components/clinical-dashboard/account-setup-dialog.tsx": 4, + "src/components/calculators/search-page.tsx": 2, + "src/components/clinical-dashboard/ClinicalSidebar.tsx": 2, + "src/components/clinical-dashboard/account-setup-dialog.tsx": 1, "src/components/clinical-dashboard/auth-panel.tsx": 1, - "src/components/clinical-dashboard/ClinicalSidebar.tsx": 3, - "src/components/clinical-dashboard/cross-mode-links.tsx": 1, - "src/components/clinical-dashboard/dashboard-nav.tsx": 3, - "src/components/clinical-dashboard/differentials-home.tsx": 3, - "src/components/clinical-dashboard/document-search-results.tsx": 7, - "src/components/clinical-dashboard/evidence-panels.tsx": 1, - "src/components/clinical-dashboard/favourites-command-library-page.tsx": 7, - "src/components/clinical-dashboard/favourites-hub.tsx": 3, - "src/components/clinical-dashboard/favourites-library-nav.tsx": 5, + "src/components/clinical-dashboard/dashboard-nav.tsx": 2, + "src/components/clinical-dashboard/differentials-home.tsx": 2, + "src/components/clinical-dashboard/document-search-results.tsx": 4, + "src/components/clinical-dashboard/favourites-command-library-page.tsx": 2, + "src/components/clinical-dashboard/favourites-hub.tsx": 1, + "src/components/clinical-dashboard/favourites-library-nav.tsx": 3, "src/components/clinical-dashboard/library-health-strip.tsx": 1, - "src/components/clinical-dashboard/master-search-header.tsx": 6, + "src/components/clinical-dashboard/master-search-header.tsx": 5, "src/components/clinical-dashboard/medication-prescribing-workspace.tsx": 1, "src/components/clinical-dashboard/medication-record-page.tsx": 2, "src/components/clinical-dashboard/mode-action-popup.tsx": 2, - "src/components/clinical-dashboard/settings-dialog.tsx": 4, - "src/components/clinical-dashboard/signed-image.tsx": 1, + "src/components/clinical-dashboard/settings-dialog.tsx": 3, "src/components/clinical-dashboard/source-preview-popover.tsx": 1, "src/components/clinical-dashboard/universal-search-command-surface.tsx": 1, - "src/components/differentials/diagnosis-map-panel.tsx": 8, - "src/components/differentials/differential-detail-page.tsx": 3, + "src/components/differentials/diagnosis-map-panel.tsx": 4, + "src/components/differentials/differential-detail-page.tsx": 2, "src/components/differentials/differential-presentation-actions.tsx": 1, "src/components/differentials/differential-presentation-workflow-page.tsx": 2, - "src/components/document-viewer/document-clinical-summary.tsx": 2, - "src/components/document-viewer/non-pdf-source-preview.tsx": 1, - "src/components/document-viewer/pdf-canvas-viewer.tsx": 2, - "src/components/document-viewer/section-nav.tsx": 1, - "src/components/DocumentViewer.tsx": 3, - "src/components/dsm/dsm-comparison-page.tsx": 2, + "src/components/dsm/dsm-comparison-page.tsx": 1, "src/components/dsm/dsm-diagnosis-page.tsx": 1, - "src/components/dsm/dsm-differential-considerations-page.tsx": 3, - "src/components/dsm/dsm-search-page.tsx": 4, - "src/components/factsheets/factsheet-detail-page.tsx": 2, + "src/components/dsm/dsm-differential-considerations-page.tsx": 2, + "src/components/dsm/dsm-search-page.tsx": 2, "src/components/factsheets/factsheets-home-page.tsx": 2, - "src/components/factsheets/factsheets-search-page.tsx": 3, - "src/components/forms/forms-search-results-page.tsx": 2, - "src/components/formulation/formulation-builder-page.tsx": 4, + "src/components/factsheets/factsheets-search-page.tsx": 2, + "src/components/formulation/formulation-builder-page.tsx": 1, "src/components/formulation/formulation-home-page.tsx": 1, "src/components/formulation/formulation-map-page.tsx": 1, "src/components/mode-home-template.tsx": 2, "src/components/mode-nav/mode-nav.tsx": 1, - "src/components/patient-safety-plan.tsx": 3, - "src/components/pwa-lifecycle.tsx": 3, - "src/components/registry-record-loader.tsx": 2, + "src/components/patient-safety-plan.tsx": 1, + "src/components/pwa-lifecycle.tsx": 1, + "src/components/registry-record-loader.tsx": 1, "src/components/route-error-boundary.tsx": 1, - "src/components/secondary-navigation.tsx": 1, - "src/components/services/service-detail-page.tsx": 1, - "src/components/services/services-navigator-page.tsx": 9, - "src/components/specifiers/specifier-builder-page.tsx": 1, "src/components/specifiers/specifier-map-page.tsx": 1, "src/components/specifiers/specifier-ui.tsx": 1, "src/components/specifiers/specifiers-home-page.tsx": 1, - "src/components/therapy-compass/bindings.tsx": 1, - "src/components/therapy-compass/controls.ts": 6, + "src/components/therapy-compass/controls.ts": 5, "src/components/therapy-compass/screens/brief-screen.tsx": 4, - "src/components/therapy-compass/screens/compare-screen.tsx": 4, - "src/components/therapy-compass/screens/detail-screen.tsx": 1, - "src/components/therapy-compass/screens/other-screen.tsx": 1, + "src/components/therapy-compass/screens/compare-screen.tsx": 3, "src/components/therapy-compass/screens/pathways-screen.tsx": 1, - "src/components/therapy-compass/screens/recommend-screen.tsx": 3, - "src/components/therapy-compass/screens/sheets-screen.tsx": 5, + "src/components/therapy-compass/screens/recommend-screen.tsx": 2, + "src/components/therapy-compass/screens/sheets-screen.tsx": 4, "src/components/therapy-compass/therapy-card.tsx": 1, "src/components/ui/answer-card.tsx": 1, - "src/components/ui/button.tsx": 3, - "src/components/ui/document-frame.tsx": 1, - "src/components/ui/segmented-control.tsx": 1, + "src/components/ui/button.tsx": 1, "src/components/ui/sheet.tsx": 1, "src/components/ui/toast.tsx": 1, "src/components/ui/tooltip.tsx": 1, - "src/components/ui-primitives.tsx": 6 + "src/components/ui-primitives.tsx": 3 }, "arbitraryTracking": {}, "rawPaddingLiterals": { "src/app/globals.css": 17, - "src/components/clinical-dashboard/document-search-results.tsx": 1, "src/components/clinical-dashboard/result-filter-control.tsx": 1, "src/components/clinical-record-panels.tsx": 2, "src/components/differentials/differential-detail-page.tsx": 1, - "src/components/services/service-detail-page.tsx": 1, "src/components/therapy-compass/bindings.tsx": 1, "src/components/therapy-compass/controls.ts": 2, "src/components/therapy-compass/screens/brief-screen.tsx": 7, @@ -166,7 +143,7 @@ "src/components/therapy-compass/screens/pathways-screen.tsx": 4, "src/components/therapy-compass/screens/recommend-screen.tsx": 6, "src/components/therapy-compass/screens/sheets-screen.tsx": 9, - "src/components/therapy-compass/therapy-card.tsx": 3 + "src/components/therapy-compass/therapy-card.tsx": 1 }, "rawRadiusLiterals": { "src/app/globals.css": 22, @@ -181,8 +158,7 @@ "src/components/therapy-compass/screens/detail-screen.tsx": 4, "src/components/therapy-compass/screens/pathways-screen.tsx": 4, "src/components/therapy-compass/screens/recommend-screen.tsx": 2, - "src/components/therapy-compass/screens/sheets-screen.tsx": 2, - "src/components/therapy-compass/therapy-card.tsx": 2 + "src/components/therapy-compass/screens/sheets-screen.tsx": 2 }, "rawLineHeightLiterals": { "src/app/globals.css": 3 @@ -192,8 +168,7 @@ "src/components/calculators/guided-flow.tsx": 1, "src/components/clinical-dashboard/answer-content.tsx": 1, "src/components/clinical-dashboard/master-search-header.tsx": 4, - "src/components/document-viewer/section-nav.tsx": 1, - "src/components/secondary-navigation.tsx": 1 + "src/components/document-viewer/section-nav.tsx": 1 }, "textSoftConsumers": {}, "errorStateCountProps": {}, diff --git a/src/app/globals.css b/src/app/globals.css index de7e25a484..23dfd37aae 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -104,6 +104,15 @@ --spacing-icon-lg: 1.25rem; /* 20px — header / primary controls */ --spacing-icon-xl: 1.5rem; /* 24px — hero / composer emphasis */ + /* Active-filter count bubble on the search band. 17px is deliberately off + both the 4px grid and the icon scale above: it is a text-bearing box sized + to its own --text-2xs numeral plus the tinted ring, not a glyph, so + snapping it to 16px clips the digit and 20px reads as the solid alert disc + the pill is written to avoid. Held as a token because the raw value has + already leaked once — it reached mode-nav and a since-deleted therapy + filter sheet before the shared control absorbed those call sites (#275). */ + --spacing-search-band-badge: 1.0625rem; + /* Type scale — sub-`xs` micro steps plus the 13–22px "between" steps the dense clinical UI reaches for. These are intentionally SIZE-ONLY: unlike Tailwind's default text-* tokens they carry no `--line-height` / @@ -442,7 +451,6 @@ resting-hairline role is gone: its call sites reach for --e1 directly. */ --shadow-card: var(--e2); --shadow-soft: var(--e2); - --shadow-tight: var(--e1); --shadow-hover: var(--e3); --shadow-elevated: var(--e4); --shadow-lux: inset 0 1px 0 rgb(255 255 255 / 90%), var(--e4); @@ -690,7 +698,6 @@ --shadow-card: var(--e2); --shadow-soft: var(--e2); - --shadow-tight: var(--e1); --shadow-hover: var(--e3); --shadow-elevated: var(--e4); --shadow-lux: var(--e4); diff --git a/src/app/mockups/answer-evidence-popups/page.tsx b/src/app/mockups/answer-evidence-popups/page.tsx index 47c1f3d6ad..5f8e577db5 100644 --- a/src/app/mockups/answer-evidence-popups/page.tsx +++ b/src/app/mockups/answer-evidence-popups/page.tsx @@ -122,13 +122,13 @@ function Pill({ children, tone = "neutral" }: { children: ReactNode; tone?: "neu } function Action({ children, primary = false }: { children: ReactNode; primary?: boolean }) { - const baseClass = `inline-flex min-h-10 max-w-full min-w-0 items-center justify-center gap-2 rounded-md px-3 text-center text-xs font-semibold leading-tight transition duration-160 ease-[var(--ease-spring)] hover:-translate-y-px hover:shadow-[var(--shadow-tight)] active:translate-y-0 active:scale-[0.99] ${focusRing} [&>svg]:h-3.5 [&>svg]:w-3.5 [&>svg]:shrink-0 sm:min-h-11 sm:text-sm`; + const baseClass = `inline-flex min-h-10 max-w-full min-w-0 items-center justify-center gap-2 rounded-md px-3 text-center text-xs font-semibold leading-tight transition duration-160 ease-[var(--ease-spring)] hover:-translate-y-px hover:shadow-[var(--e1)] active:translate-y-0 active:scale-[0.99] ${focusRing} [&>svg]:h-3.5 [&>svg]:w-3.5 [&>svg]:shrink-0 sm:min-h-11 sm:text-sm`; return ( diff --git a/src/components/DocumentViewer.tsx b/src/components/DocumentViewer.tsx index 30567753b0..5252a9d0fd 100644 --- a/src/components/DocumentViewer.tsx +++ b/src/components/DocumentViewer.tsx @@ -1088,7 +1088,7 @@ export function DocumentViewer({
{documentSearchPending ? ( diff --git a/src/components/applications-launcher-page.tsx b/src/components/applications-launcher-page.tsx index ef5c727899..f00c828988 100644 --- a/src/components/applications-launcher-page.tsx +++ b/src/components/applications-launcher-page.tsx @@ -281,7 +281,7 @@ function ToolSearch({ aria-label={copy.openSelectedAriaLabel} data-testid="tools-local-search-submit" className={cn( - "grid h-tap w-tap place-items-center rounded-full bg-[color:var(--clinical-accent)] text-[color:var(--clinical-accent-contrast)] shadow-[var(--shadow-tight)] transition hover:bg-[color:var(--clinical-accent-hover)]", + "grid h-tap w-tap place-items-center rounded-full bg-[color:var(--clinical-accent)] text-[color:var(--clinical-accent-contrast)] shadow-[var(--e1)] transition hover:bg-[color:var(--clinical-accent-hover)]", focusRing, )} > @@ -490,7 +490,7 @@ function ToolCard({ - + Details @@ -531,7 +531,7 @@ function MobileToolRow({ {app.description} - + Details @@ -673,7 +673,7 @@ function DetailDialog({ app, open, onClose }: { app: LauncherApp; open: boolean; target={app.external ? "_blank" : undefined} rel={app.external ? "noopener noreferrer" : undefined} className={cn( - "inline-flex min-h-12 w-full items-center justify-center gap-3 rounded-lg bg-[color:var(--clinical-accent)] px-4 text-sm font-extrabold text-[color:var(--clinical-accent-contrast)] shadow-[var(--shadow-tight)] hover:bg-[color:var(--clinical-accent-hover)]", + "inline-flex min-h-12 w-full items-center justify-center gap-3 rounded-lg bg-[color:var(--clinical-accent)] px-4 text-sm font-extrabold text-[color:var(--clinical-accent-contrast)] shadow-[var(--e1)] hover:bg-[color:var(--clinical-accent-hover)]", focusRing, )} > diff --git a/src/components/calculator-mockups/calculator-ui.tsx b/src/components/calculator-mockups/calculator-ui.tsx index 3287217dcd..1291a6cce1 100644 --- a/src/components/calculator-mockups/calculator-ui.tsx +++ b/src/components/calculator-mockups/calculator-ui.tsx @@ -247,7 +247,7 @@ export function ScoreBandBar({ {started ? (