From 7510216c672f293d26b8fdfe04a9ed3fd286b7f6 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Wed, 19 Aug 2026 01:42:55 +0800 Subject: [PATCH 1/3] fix(ui): mark search empty-state tone by stroke and restore its desktop tap floor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restyle SearchResultsEmptyState as a left-aligned panel with a leading state mark, replacing the dashed centred box and the 48px tinted glyph disc. The mark carries tone the way .search-band-lead already does — as stroke count rather than hue. Under forced colors --clinical-accent resolves to LinkText and --warning resolves to CanvasText, so hue alone would render a degraded search and a filtered-to-zero search identically. One stroke is a search that ran; a doubled stroke is one that did not complete. The glyph moves beside the heading rather than into it, so nothing enters the accessible name that the existing getByRole("heading", ...) assertions resolve. Also restores the desktop tap floor on the control the filtered path leads with: sm:min-h-9 (36px) to sm:min-h-10 (40px). emptyStateAction and the shelf controls in this file already meet 40px from sm; this one did not. The phone floor was already correct at min-h-tap. No copy string, data-testid, heading level, live-region behaviour or action wiring changed. SearchResultsHeaderBand is untouched, so its eighteen consumers are unaffected. The mark uses Tailwind utilities rather than a new unlayered class: .search-band-lead is unlayered specifically so it can beat Tailwind's utilities layer, and this mark has no utility to beat. Co-Authored-By: Claude Fable 5 --- .../search-results-header-band.tsx | 77 +++++++++++++++---- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/src/components/clinical-dashboard/search-results-header-band.tsx b/src/components/clinical-dashboard/search-results-header-band.tsx index 91150ecaf3..9c815dd33a 100644 --- a/src/components/clinical-dashboard/search-results-header-band.tsx +++ b/src/components/clinical-dashboard/search-results-header-band.tsx @@ -962,24 +962,65 @@ export function SearchResultsEmptyState({ ].filter(Boolean); return ( -
+
+ {/* The state mark, carrying the same vocabulary as the band's own lead: + the stroke *count* — not the hue — is what separates a degraded search + from a healthy one, because `--clinical-accent` resolves to LinkText + under forced colors while `--warning` resolves to CanvasText, and hue + alone would render "filtered" and "could not complete" identically. + Utilities rather than an unlayered class: `.search-band-lead` is + unlayered precisely so it can beat Tailwind's utilities layer, and this + mark has no utility to beat — adding a class here would enlarge the + inventory `style-contract-registry` polices for nothing. `box-content` + + `w-0` makes the border the entire width. Exactly one width class per + branch: two would both emit `border-left-width` and the winner would be + stylesheet order rather than the branch. */} + {/* Visible copy is never inside the live region: a region that mounts already populated is silent in most screen readers, and wrapping the visible tree would flash empty for a frame. The sr-only live region below is populated after mount (query-only) or omitted (filtered). NOT `role="status"`: the band already owns that role on every search route and a second one makes singular `getByRole("status")` ambiguous. */} - - {degraded ? ( - - ) : filtered ? ( - - ) : ( - - )} - - {emptyTitle} -

{emptyBody}

+ {/* The glyph sits beside the heading rather than inside it: a 48px tinted + disc above centred copy is a container the icon vocabulary does not + have, and putting the SVG inside `Title` would put a node inside the + accessible name that every heading assertion resolves. */} +
+ + {degraded ? ( + + ) : filtered ? ( + + ) : ( + + )} + +
+ {emptyTitle} +

{emptyBody}

+
+
{!filtered ? (
{liveMessage} @@ -991,7 +1032,7 @@ export function SearchResultsEmptyState({ own action, which is also the fix for the single button that said "Clear filters" and called `clearSearch`. */} {filtered ? ( -
+
{lastFilter ? (