diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index af6742fb0c..10c157eb69 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -761,6 +761,7 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-08 | claude/document-viewer-optimization-tu8tnj | 2359e158cb7bca5954e9c5ee84ca0766964ad901 | PR #1741 document-viewer phone/PWA review-and-fix | supersede: fixed Production UI phone Zoom/section-trigger; handlePdfLoadSuccess clamp; prior P1/P2 fixes retained; merge-tree clean | prior verify:cheap+pr-local green; ui-smoke selectors fixed for overflow Zoom + revealPhoneHeaderControl; no provider gates | | 2026-08-08 | claude/mode-routing-search-pages-jabe17 | 6d1099b479358caa05c92f236848117feb920d4e | shared-home mode-routed search navigation | no high-confidence P0-P2 PR-introduced defects; prior bug-hunt P1/P2s appear fixed on tip; residual: prescribing submit-from-shared-home URL omits run=1 (pre-existing path), seed effect untested behaviourally, no browser/UI proof this pass | vitest app-modes+search-route-ownership+audit-navigation+pwa-manifest 61 pass; static read of focus files vs origin/main; ledger:lookup NOT REVIEWED; no provider/UI | | 2026-08-08 | cursor/safety-plan-phone-safe-area-624a (PR #1711) | ad1b1f5db24ed68ee4c0d5963620e4562829884e | heavy review-and-fix PR #1711 | fixed CodeRabbit sm:py guard parity; late-synced #1720 behind-but-clean; no P0/P1; Bugbot none; threads cleared; merge-tree clean; required CI green on 78c14205 pre-sync | vitest safety-plan+standalone 18p; verify:cheap 523/5582; verify:pr-local format+lint+typecheck+test+build+rag-fixtures; Production UI critical+(1)(2)(3)+PR required SUCCESS on 78c14205; no provider gates | +| 2026-08-09 | claude/inpage-nav-pr-2-6d32f9 | 249526988ea3d65c54e69ee7ca05e514bff50ed8 | in-page-nav PR 2: convert six information routes onto InPageNavHeader; delete the shell-owned pill rail | Shipped as PR #1766. Seven components converted; actions API widened for Server Components; two DSM routes' missing anchors wired; rail and section kind removed; new per-route rendered-DOM section contract added. | verify:pr-local 527/530 files 5708 tests; verify:cheap 529/530 5710 tests; verify:phone-chrome escalated to full Chromium 398 passed then 13/13 updated specs pass; typecheck + prettier clean; Playwright production build compiled. Residual failures proven pre-existing on pristine base 9ab3b73a (issues #285, pr-handoff-stop env). | | 2026-08-08 | dependabot/npm_and_yarn/js-yaml-4.3.1 | 072b83f79a70037a04a8412844c041db43c9ce48 | PR #1668 unblock | synced main; merge-tree clean; required CI was green on prior tip e9516021; js-yaml 4.3.1 + nanoid 3.3.18 preserved; no unresolved threads; CI re-run after sync | pre-sync PR required pass; Production UI skipped (deps); post-sync pending | | 2026-08-08 | claude/planning-build-intelligence-9ot0nm | 1ebc84bb288b516bb322c09cde2889e981d302a4 | AGENTS.md reasoning-effort calibration section (docs-only) | Authored and handed off as PR #1730; docs-only, pr-policy classifier returns clinicalRisk/operationalRisk/ragRanking false | prettier --check . (repo-wide, pass); docs:check-links (1665 refs resolve, pass); pr-policy classifyPullRequestFiles(AGENTS.md) | | 2026-08-08 | claude/planning-build-intelligence-9ot0nm | 2b0ad7d41d841c13515f10de7c41e449470dfa78 | pr-1730 review-and-fix | Deep review + Bugbot: no P0/P1; fixed 2 scoped P2 clarity risks (version-bump under-planning; live-state vs provider boundary). Residual: OPENAI_*_REASONING_EFFORT vocab overlap. Merge-tree clean; required CI was green pre-push. | prettier --check AGENTS.md; docs:check-links (1667); verify:pr-local (docs route pass); verify:cheap (524 files / 5607 tests pass); pr-policy classify clinical/operational/rag false; Bugbot no P0-P2 | @@ -824,14 +825,15 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-07 | cursor/site-testing-speed-08c1 | 91bac89827ae2f4f0e59aeed7de6344fe8779a95 | PR #1686 Autopilot+Bugbot review-and-fix: conflicts, threads, Static PR checks, CI/testing selection | fixed: merged origin/main (outstanding-issues #167/#255 archive + #256 keep); removed unused pathToFileURL; added ui-forms-section-nav to PR UI shards (21 specs); no unresolved threads; Bugbot unavailable (usage limit). Local: eslint file max-warnings0, vitest 36/36 focused, shard --validate OK, check:outstanding-issues OK. verify:cheap/pr-local blocked by foreign worktree heavy lock (PID 26228). | eslint scripts/playwright-pr-shards.mjs --max-warnings 0; vitest 36 passed; playwright-pr-shards --validate 21; check:outstanding-issues; verify:cheap/pr-local lock-blocked | | 2026-08-08 | cursor/more-modes-popup-2f4b | 04e6a80653c3ccf103577f6c3886b162498621ed | sidebar more-modes sheet popup | pass | focused-pw tablet rail; test:focused ClinicalSidebar; favourites+therapy wiring; verify:pr-local stages+build+rag-fixtures | | 2026-08-08 | cursor/more-modes-popup-2f4b | bea4b0c09b74368cf6d63e944bac9c1eec6b0c93 | sidebar more-modes sheet popup | pass | focused-pw tablet rail; test:focused ClinicalSidebar; favourites+therapy wiring; verify:pr-local stages+build+rag-fixtures | -| 2026-08-09 | cursor/differentials-diagnosis-links-9f18 | 0e77e7bc0842bef4ffc045c6dbea4626152490d1 | differentials diagnosis term links | implemented exact+alias termLinks chips on diagnosis+presentation pages; vitest 58/58; verify:pr-local green | vitest differential-diagnosis-links+detail+section-nav+route; verify:pr-local; ensure spot-check | -| 2026-08-09 | claude/m2-ds-gates-blocking | 8ed66a0570c95c2cc8597364467e67966b04854d | M2 design-system gates: #264 + gate 4 of #265 | ready-to-merge; gate 2 enumeration deliberately reverted as non-deterministic (#289) | ds-contract PASS (colour-only 4, numerals 2, inversions 0); mutation-verified x4; lint 0; tsc 0 errors; icon+type scale PASS; focused vitest 126p/3 files; verify:cheap 5777p with 10 pre-existing failures proven identical on pristine origin-main; format:check clean | -| 2026-08-09 | claude/inpage-nav-pr-2-6d32f9 | 249526988ea3d65c54e69ee7ca05e514bff50ed8 | in-page-nav PR 2: convert six information routes onto InPageNavHeader; delete the shell-owned pill rail | Shipped as PR #1766. Seven components converted; actions API widened for Server Components; two DSM routes' missing anchors wired; rail and section kind removed; new per-route rendered-DOM section contract added. | verify:pr-local 527/530 files 5708 tests; verify:cheap 529/530 5710 tests; verify:phone-chrome escalated to full Chromium 398 passed then 13/13 updated specs pass; typecheck + prettier clean; Playwright production build compiled. Residual failures proven pre-existing on pristine base 9ab3b73a (issues #285, pr-handoff-stop env). | | 2026-08-09 | claude/document-viewer-optimization-tu8tnj | b8c94dff2345a7d50c7bbce0c9c344740e6b92b1 | docs: document-viewer Phase 3 handover brief (PR #1765) | Docs-only. Adds docs/plans/document-viewer-phase3-handover.md scoping Phase 3 to all capabilities except crop-to-page overlay (bbox absent from DocumentDetailImage; plumbing crosses src/lib/**document** and forces a governance preflight). Corrects ledger #279: measured playwright@1.62.1 expects Chromium 151.0.7922.34, container ships 141.0.7390.37, CI runs HeadlessChrome/151.0.0.0, and pdfjs-dist 6.2.108 needs Map.getOrInsertComputed which ships in 151 not 141 - so the raster failure is container-only and neither proposed remedy (bump Playwright / pin pdfjs down) is needed. Cited #286 for the authorizationHeader casing trap after initially writing #285. | verify:pr-local all ten gates completed, none failed; docs:check-links 1688 references resolve; line refs re-verified against main 8db1e53 | | 2026-08-09 | claude/document-viewer-optimization-tu8tnj | 5a0d6be02bc92fa2615d2141b338ec8f7c1143b1 | docs: document-viewer Phase 3 handover brief (PR #1765) | Supersedes the earlier row, whose 'all ten gates completed' wording could read as all executable checks having run. Correct scope: verify:pr-local ran the ten gates APPLICABLE to docs-only changes (check:runtime, check:installed-lock-parity, format:changed, sitemap:check, docs:check-index, docs:check-inventory, docs:check-scripts, docs:check-links, check:branch-review-ledger, check:outstanding-issues); the risk router SKIPPED lint, typecheck, the full unit suite, RAG fixture validation, and build as recognised low-risk documentation scope. Also records the merge resolution: duplicate #286 (main's in-page-nav series vs this branch's authorizationHeader row) resolved by renumbering the branch row to #289, next-id 290, after the auto-merge silently dropped that detail row rather than conflicting. Review findings addressed: governance preflight now required by behaviour per AGENTS.md:257 rather than inferred from pr-policy path classification; API-route scope contradiction resolved; signed-URL warning corrected to state both identity bugs are already fixed on main with regression coverage. | verify:pr-local ten docs-scope gates passed, none failed; check:outstanding-issues 287 rows unique ids next-id=290 no ids deleted; ledger:dedupe 771 unique rows; git merge-tree vs origin/main exit 0; viewer line refs re-verified against 50ef12e | +| 2026-08-09 | claude/breadcrumb-header-mockups-cei6lw | 8effa5abe77e9008fb12f6ff996a51aa6d406ab5 | mockups: breadcrumb header study (3 directions) + sitemap/README | self-reviewed; design-scratch only, no production surface changed | typecheck, eslint(changed), prettier --check, sitemap:check, vitest(site-map/mockup-boundary/env-mockups/docs-inventory/route-reachability) 23 passed | +| 2026-08-09 | claude/breadcrumb-header-mockups-cei6lw | ca5e4e7ae9b53af49b362ad11ca988fdd1c3a9d0 | breadcrumb header shipped: InPageNavHeader breadcrumb shape + factsheet detail adoption | self-reviewed; browser-verified at 390/700/834/1280; phone-chrome gate blocked by #255 playwright drift | typecheck, lint, test (5806 passed, 1 pre-existing unrelated fail), build, check:bundle-budget, check:design-system-contract, format:changed, sitemap:check, docs+ledger checks | +| 2026-08-09 | claude/m2-ds-gates-blocking | 8ed66a0570c95c2cc8597364467e67966b04854d | M2 design-system gates: #264 + gate 4 of #265 | ready-to-merge; gate 2 enumeration deliberately reverted as non-deterministic (#289) | ds-contract PASS (colour-only 4, numerals 2, inversions 0); mutation-verified x4; lint 0; tsc 0 errors; icon+type scale PASS; focused vitest 126p/3 files; verify:cheap 5777p with 10 pre-existing failures proven identical on pristine origin-main; format:check clean | | 2026-08-09 | claude/m2-ds-gates-blocking | d204c6f7c84a5e7de3f28061121fda68e7d28670 | M2 design-system gates: #264 + gate 4 of #265 | ready-to-merge; supersedes the 8ed66a05 row — the tap-floor defect renumbered #289 to #291 after main claimed #289/#290, and main was merged in | post-merge ds-contract PASS (colour-only 4, numerals 2, inversions 0) against main's new #1765/#1766 component code; outstanding-issues guard PASS 289 rows next-id=292; ledger guard PASS 774 rows; format clean | | 2026-08-09 | claude/m2-ds-gates-blocking | e8447b042088998d34af750df72a946b1f074b97 | M2 design-system gates: #264 + gate 4 of #265 | ready-to-merge; supersedes the d204c6f7 row — seven review findings fixed, copilot-swe-agent commits merged keeping the safer numeral classifier, tap-floor defect renumbered #291 to #293 after main claimed #291/#292 | ds-contract PASS (colour-only 4, numerals 2, inversions 0); 11 reviewer cases probe-verified; mutation-verified incl. opacity and arbitrary-filter forms; lint 0; tsc 0 errors; format clean; outstanding-issues guard PASS 291 rows next-id=294 no ids deleted; ledger guard PASS 775 rows | -| 2026-08-09 | cursor/differentials-diagnosis-links-9f18 | 0daa9e2f9fc84e879fd661da94203568309234a6 | PR #1768 Autopilot+Bugbot review-and-fix | Merged origin/main (DIRTY was ledger+detail-page staleness; merge-tree clean). Fixed SEGMENT_SPLIT to spaced-slash only so Delirium / medical psychosis links while alcohol/benzo, DVT/PE, food/fluid stay intact. Dispositioned: Copilot termLinks ??{} + Fragment key already fixed; CodeRabbit clean-keys moot (visibleSectionItems already cleans); CodeRabbit bare-slash split rejected (clinical harm). No Bugbot findings. Threads cleared on push. Merge left to user. | vitest differential-diagnosis-links+detail+route 49/49; verify:cheap exit 0 (543 files, 5828 passed/4 skipped); verify:pr-local exit 0 (lint/typecheck/test/build/rag-fixtures); merge-tree clean vs origin/main; no provider gates | | 2026-08-09 | claude/document-viewer-phase-3-bj5k5v | 0436c3b495bee05777efc2ea1709230044e26b42 | document viewer Phase 3: page virtualization, rail windowing, signed-URL/decode priority, keyboard reading mode, first canvas browser gate | PR #1772 opened. Self-reviewed during authorship; two real races found and fixed (route effect overriding reader scroll position when pdf.js reports its page count; in-flight scroll gate armed a frame too late). #279 closed by tests/ui-document-canvas.spec.ts; #283 batch-route deferral recorded with the measurement that should decide it; #290 added for the OffscreenCanvas number; #291 added for a pre-existing root-only pr-handoff-stop failure; #252 updated with measured bundle headroom (+9.4% of 10%). Crop-to-page overlay out of scope by design. | verify:pr-local (1 pre-existing root-only failure: pr-handoff-stop, reproduced on origin/main worktree; 5839 passed), build OK, eval:rag:offline 36 golden cases 574 tests, check:bundle-budget within tolerance, check:playwright-pr-shards 23 specs, canvas gate skip-with-reason verified locally and fail-closed verified with CI=1. Browser gates unrunnable here (Chromium 141 vs pdfjs-dist 6 needing 151) - delegated. | | 2026-08-09 | claude/document-viewer-phase-3-bj5k5v | 2cd72f111414935d4028a19932992c4ab475dcea | document viewer Phase 3 review-and-fix | PR #1772 deep review+fix: merged origin/main (renumber OffscreenCanvas #290->#294, drop dup #291=#284); fixed key-repeat pageRef, maxObservedCanvasPixels budget, rail remount-via-key, reserved-slot shadow-inset for DS ratchet; dispositioned Bugbot/Codex/Copilot canvas+rotation as already fixed at 8397aeb; #252 tip-only wording; #294 aggregate budgets; CodeRabbit ledger nit deferred to this superseding row | verify:cheap: Test Files 545 passed (545), Tests 5856 passed \| 4 skipped (5860); verify:pr-local completed check:runtime check:installed-lock-parity format:changed sitemap:check docs:check-index docs:check-inventory docs:check-scripts docs:check-links check:branch-review-ledger check:outstanding-issues lint typecheck test build eval:rag:offline failed:(none); focused vitest 47 passed (keyboard+rail+budget+virtualization); design-system-contract legacy shadow aliases 220; merge-tree vs origin/main exit 0; Production UI delegated (pdfjs Map.getOrInsertComputed needs Chromium 151) | +| 2026-08-09 | cursor/differentials-diagnosis-links-9f18 | 0e77e7bc0842bef4ffc045c6dbea4626152490d1 | differentials diagnosis term links | implemented exact+alias termLinks chips on diagnosis+presentation pages; vitest 58/58; verify:pr-local green | vitest differential-diagnosis-links+detail+section-nav+route; verify:pr-local; ensure spot-check | +| 2026-08-09 | cursor/differentials-diagnosis-links-9f18 | 0daa9e2f9fc84e879fd661da94203568309234a6 | PR #1768 Autopilot+Bugbot review-and-fix | Merged origin/main (DIRTY was ledger+detail-page staleness; merge-tree clean). Fixed SEGMENT_SPLIT to spaced-slash only so Delirium / medical psychosis links while alcohol/benzo, DVT/PE, food/fluid stay intact. Dispositioned: Copilot termLinks ??{} + Fragment key already fixed; CodeRabbit clean-keys moot (visibleSectionItems already cleans); CodeRabbit bare-slash split rejected (clinical harm). No Bugbot findings. Threads cleared on push. Merge left to user. | vitest differential-diagnosis-links+detail+route 49/49; verify:cheap exit 0 (543 files, 5828 passed/4 skipped); verify:pr-local exit 0 (lint/typecheck/test/build/rag-fixtures); merge-tree clean vs origin/main; no provider gates | | 2026-08-09 | cursor/differentials-diagnosis-links-9f18 | f784e81bcc0b53ef76b3da07a8e81f96d9bf0c71 | pr-1768 unblock | merged origin/main onto ba590f9; merge-tree clean; DIRTY mergeability cleared; push tip follows amend with this ledger | merge-tree clean; threads resolved; auto-merge was armed | diff --git a/docs/design-system/COMPONENTS.md b/docs/design-system/COMPONENTS.md index f056dc154d..601576f07b 100644 --- a/docs/design-system/COMPONENTS.md +++ b/docs/design-system/COMPONENTS.md @@ -958,7 +958,7 @@ This generated snapshot is a local source-derived inventory. It does not assert | `AnswerFooter` | answer | yes | yes | no | yes | no | 0 | | `AsyncButton` | controls | yes | yes | inherited-global-root | yes | no | 4 | | `Breadcrumb` | layout | yes | yes | inherited-global-root | yes | no | 1 | -| `Button` | controls | yes | yes | inherited-global-root | yes | no | 2 | +| `Button` | controls | yes | yes | inherited-global-root | yes | no | 3 | | `Checkbox` | controls | yes | yes | no | yes | no | 0 | | `Chip` | controls | yes | yes | inherited-global-root | yes | no | 3 | | `Citation` | source | yes | yes | no | yes | no | 0 | @@ -990,7 +990,7 @@ This generated snapshot is a local source-derived inventory. It does not assert | `RetrievalStateBanner` | answer | yes | yes | inherited-global-root | yes | no | 1 | | `SafeBoldText` | layout | yes | yes | inherited-global-root | yes | no | 8 | | `SearchField` | controls | yes | yes | no | yes | no | 0 | -| `SegmentedControl` | controls | yes | yes | inherited-global-root | yes | no | 2 | +| `SegmentedControl` | controls | yes | yes | inherited-global-root | yes | no | 3 | | `Select` | controls | yes | yes | inherited-global-root | yes | no | 2 | | `Sheet` | layout | yes | yes | inherited-global-root | yes | no | 23 | | `Skeleton` | feedback | yes | yes | inherited-global-root | yes | no | 6 | diff --git a/docs/design-system/adoption-manifest.json b/docs/design-system/adoption-manifest.json index dbfd5d14a5..6cf4478078 100644 --- a/docs/design-system/adoption-manifest.json +++ b/docs/design-system/adoption-manifest.json @@ -272,11 +272,13 @@ "directImportFiles": [ "src/components/AccessibleTable.tsx", "src/components/clinical-dashboard/signed-image.tsx", + "src/components/in-page-nav/in-page-nav-header.tsx", "src/components/ui/confirm-dialog.tsx" ], "productImportFiles": [ "src/components/AccessibleTable.tsx", - "src/components/clinical-dashboard/signed-image.tsx" + "src/components/clinical-dashboard/signed-image.tsx", + "src/components/in-page-nav/in-page-nav-header.tsx" ], "designSync": { "listedInSourceMap": true, @@ -1338,11 +1340,13 @@ "entryExported": true, "directImportFiles": [ "src/components/clinical-dashboard/patient-profile-panel.tsx", - "src/components/clinical-dashboard/settings-dialog.tsx" + "src/components/clinical-dashboard/settings-dialog.tsx", + "src/components/in-page-nav/in-page-nav-header.tsx" ], "productImportFiles": [ "src/components/clinical-dashboard/patient-profile-panel.tsx", - "src/components/clinical-dashboard/settings-dialog.tsx" + "src/components/clinical-dashboard/settings-dialog.tsx", + "src/components/in-page-nav/in-page-nav-header.tsx" ], "designSync": { "listedInSourceMap": true, @@ -1473,6 +1477,7 @@ "tests/design-sync-visual-exports.test.ts", "tests/document-filter-panel.dom.test.tsx", "tests/document-organization.test.ts", + "tests/factsheet-save.dom.test.tsx", "tests/header-scroll-hide-contract.test.ts", "tests/image-lightbox-geometry.test.ts", "tests/mode-nav.dom.test.tsx", diff --git a/docs/search-chrome-behaviour.md b/docs/search-chrome-behaviour.md index 01fbe3f131..6e4867b594 100644 --- a/docs/search-chrome-behaviour.md +++ b/docs/search-chrome-behaviour.md @@ -4,15 +4,16 @@ This repo uses one shared search experience across the global shell, dashboard r ## Page ownership model -| Page state | Composer placement | Reserve owner | -| ----------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -| Answer home / standalone mode homes | In-flow hero composer on phones and larger breakpoints | Page content; no fixed phone dock reserve | -| Submitted/search-result views | Compact bottom dock on phones; in normal page flow on tablets and desktops | Shell/dashboard `--mobile-composer-reserve` on phones; page content on desktop | -| Answer result view | Overlaid glass header plus answer composer dock | Dashboard `#main-content` top/bottom reserves | -| Document detail/source routes | `DocumentViewer` floating composer | `DocumentViewer` content padding | -| Document section navigation | Header row disclosure (phone sheet) + rail index card at `lg` | None — adds no chrome and no reserve | -| Calculators (`/calculators`) | Page-owned composer (desktop top + phone bottom dock) | Calculators page pad; shell reserve stays `0` | -| Info/detail pages with no composer | No fixed composer | Idle shell padding only | +| Page state | Composer placement | Reserve owner | +| ----------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| Answer home / standalone mode homes | In-flow hero composer on phones and larger breakpoints | Page content; no fixed phone dock reserve | +| Submitted/search-result views | Compact bottom dock on phones; in normal page flow on tablets and desktops | Shell/dashboard `--mobile-composer-reserve` on phones; page content on desktop | +| Answer result view | Overlaid glass header plus answer composer dock | Dashboard `#main-content` top/bottom reserves | +| Document detail/source routes | `DocumentViewer` floating composer | `DocumentViewer` content padding | +| Document section navigation | Header row disclosure (phone sheet) + rail index card at `lg` | None — adds no chrome and no reserve | +| Record page breadcrumb header | Same header row without the disclosure or track; view mode inline from `sm` | None — portals into the phone collapse row, sticky at `sm+` | +| Calculators (`/calculators`) | Page-owned composer (desktop top + phone bottom dock) | Calculators page pad; shell reserve stays `0` | +| Info/detail pages with no composer | No fixed composer | Idle shell padding only | ## Default in-page navigation template @@ -93,10 +94,11 @@ detailed DocumentViewer rules remain invariant 22 — but new work mounts `InPag **Adopted so far:** `/differentials/diagnoses/[slug]`, `/services/[slug]`, `/forms/[slug]`, `/specifiers/[slug]` (record and catalogue reference), `/formulation/[slug]`, -`/dsm/diagnoses/[slug]` and its `/differentials` child. Each is also listed in +`/dsm/diagnoses/[slug]` and its `/differentials` child, and factsheet detail pages +(`/factsheets/[slug]`). Each is also listed in `isHeaderAddonSlotOwnedRoute` (`src/components/mode-nav/header-addon-slot.ts`), which is how the one-header-per-slot rule stays checkable. Still on their own patterns: medications -(`SectionTabs`), factsheets, and differentials presentations. +(`SectionTabs`) and differentials presentations. **Visual slots (adapt labels, back href, sections, and actions to the mode):** @@ -120,6 +122,33 @@ the one-header-per-slot rule stays checkable. Still on their own patterns: medic - Do not give the in-page header its own scroll-hide hook; share the universal collapse signal described under “Scroll hide/reveal”. +**The breadcrumb shape (pages with no section index).** The eight record pages behind +`InformationPageBreadcrumbs` have no sections, so the disclosure would open a sheet listing +one item and the track would render one full-width segment. Omit `sections` and +`InPageNavHeader` drops both and renders the breadcrumb shape instead — same row grammar, +same single collapse owner, none of the section machinery. `usePageSectionWeights` observes +nothing for an empty list, so those pages pay no measurement cost. Three optional props +shape that row: + +- `showBackLabel={false}` keeps the arrow alone at every width when the row also carries an + action or a mode, so the title owns the space. `back.label` is still the accessible name + and becomes the desktop tooltip. +- `primaryAction` promotes exactly **one** page action, as `Button variant="secondary"` — + not the filled `--command` slab, because a control pinned to every scroll position should + not be the page's heaviest. Its label is `sr-only` below `sm` so the accessible name does + not change with the breakpoint. A second promoted control is what turns the row back into + the wrapping toolbar this shape replaced; everything else belongs in `actions`. +- `mode` is a page-level **view** mode — how the page renders, not where you are in it — and + uses the shared `SegmentedControl`. Below `sm` it wraps to its own full-width band under + the row; from `sm` it sits inline and costs no extra height (measured on + `/factsheets/sertraline`: 131px phone, 75px from `sm`, 65px with no mode). + +Adopted by `src/components/factsheets/factsheet-detail-page.tsx`. When a page adopts this, +register its routes in `isHeaderAddonSlotOwnedRoute` +(`src/components/mode-nav/header-addon-slot.ts`) and add the component to the expected +claimants in `tests/mode-nav-addon-slot.dom.test.tsx`, or that guard fails: the slot holds +exactly one page-owned header. + **Not this template:** Therapy-style `ModeNav` (multi-route mode tabs via `ModeNavHeaderPortal`) is a different pattern for mode-level page switching. Info-page `PageHeader` / breadcrumb chrome is also not in-page section navigation. Existing diff --git a/mockups/README.md b/mockups/README.md index 690fd4e85e..ea223959a4 100644 --- a/mockups/README.md +++ b/mockups/README.md @@ -60,3 +60,17 @@ Static desktop/phone comps for the pages that need redesign (not ModeHome mockup These are PNGs for design review only. Runnable `/mockups/*` routes are a separate implementation step. **Perfected combined comps** (desktop + phone in one image, recommended directions only) live in [`public/mockups/mode-page-redesign-2026-07/perfected-combined/`](../public/mockups/mode-page-redesign-2026-07/perfected-combined/README.md). + +## Breadcrumb header study (2026-08-09) — shipped, route retired + +Three sticky header directions for record pages that use `InformationPageBreadcrumbs` and have **no in-page section index** — factsheets, services, forms, DSM, specifiers, formulation, medications. `InPageNavHeader` stays the default for in-page navigation per [`docs/search-chrome-behaviour.md`](../docs/search-chrome-behaviour.md); it is the wrong shape for those pages, because with no sections its disclosure opens a one-item sheet and its weighted track renders one full-width segment. Every direction kept that header's row grammar (back, title, ellipsis, one scroll owner) and dropped the section machinery: + +| Direction | Adds | Fits | +| -------------------- | ------------------------------------- | --------------------------------------- | +| 01 Crumb rail | Nothing — identity and return only | Forms, DSM, specifiers, formulation | +| 02 Action rail | One promoted primary action pill | Factsheets, services, medications | +| 03 Crumb rail + mode | Segmented view mode in the track slot | Factsheet reading level, medication age | + +**Outcome: direction 02 shipped**, as the breadcrumb shape of the existing `InPageNavHeader` rather than a new component — omitting `sections` drops the disclosure and the track, and `primaryAction` / `mode` / `showBackLabel` shape the row. Adopted first on `/factsheets/`, where the reading level rides the `mode` slot. Contract: `docs/search-chrome-behaviour.md` ("The breadcrumb shape"). + +**The runnable `/mockups/breadcrumb-header` route was removed once 02 shipped.** It was not deleted for tidiness: `check:bundle-budget` totals every built chunk, mockups included, and `main` sits at roughly +9.4% against a 10% tolerance, so the study's two scratch chunks (~9.8 KiB gzip) alone pushed the repo to +10.1% and failed `Build` — the same failure PR #1580 hit, at the same number. A design-scratch route that 404s in production is the wrong thing to spend the last of that headroom on. The table above is the durable record; recover the route from history if the alternatives need re-reading. diff --git a/src/components/factsheets/factsheet-detail-page.tsx b/src/components/factsheets/factsheet-detail-page.tsx index 2e5f2e6a10..f0a73e8f00 100644 --- a/src/components/factsheets/factsheet-detail-page.tsx +++ b/src/components/factsheets/factsheet-detail-page.tsx @@ -27,7 +27,8 @@ import { type Factsheet, } from "@/components/factsheets/factsheets-data"; import { factsheetGlyph } from "@/components/factsheets/factsheets-icons"; -import { InformationPageBreadcrumbs, InformationPageShell } from "@/components/information-page-shell"; +import { InPageNavHeader } from "@/components/in-page-nav/in-page-nav-header"; +import { InformationPageShell } from "@/components/information-page-shell"; import { cn, toneDanger, toneWarning } from "@/components/ui-primitives"; import { readSavedRegistrySlugs, @@ -40,6 +41,11 @@ function accentBorder(accent: string) { return `color-mix(in srgb, ${accent} 35%, var(--surface))`; } +const readingLevelOptions = [ + { value: "easy", label: "Easy read" }, + { value: "standard", label: "Standard" }, +]; + function Heading({ children }: { children: ReactNode }) { return

{children}

; } @@ -106,69 +112,58 @@ export function FactsheetDetailPage({ factsheet }: { factsheet: Factsheet }) { return ( <> - {/* action bar */} -
-
- -
- {factsheet.kind === "medRich" ? ( -
- {(["easy", "standard"] as const).map((level) => { - const isActive = readingLevel === level; - return ( - - ); - })} -
- ) : null} + {/* The breadcrumb shape of the shared in-page header: no section index, + so no disclosure and no segment track. Reading level is a page-level + view mode rather than an action, so it rides the `mode` slot and + stays out of the row reserved for verbs. */} + setReadingLevel(value === "standard" ? "standard" : "easy"), + } + : undefined + } + actionsTitle="This factsheet" + actionsDescription={`${factsheet.title} · updated ${factsheet.reviewedOn}`} + actionsNoun="factsheet" + testIdPrefix="factsheet" + actions={() => ( + // Deliberately does not close the sheet: saving is a state change + // you want to see reflected on the control you just pressed. The + // live region lives here too — `Sheet` makes the page subtree inert, + // so a background status would never reach assistive technology while + // the modal is open (including the denied-storage failure path). + <> {saveNotice} - -
-
-
+ + )} + />
@@ -349,7 +344,13 @@ export function FactsheetDetailPage({ factsheet }: { factsheet: Factsheet }) {
{/* sidebar */} -