From 3a122c081ecb8b77f5e3ba1dd805b2e43c3be46b Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:03:54 +0800 Subject: [PATCH] fix: polish focus and mobile interaction states --- src/app/globals.css | 29 ++++-- .../favourites-command-library-page.tsx | 30 +++--- src/components/therapy-compass/bindings.tsx | 2 +- .../therapy-compass/screens/sheets-screen.tsx | 5 +- src/components/ui-primitives.tsx | 4 +- tests/ui-smoke.spec.ts | 93 +++++++++++++++++++ 6 files changed, 133 insertions(+), 30 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index b10398895..c9121de89 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -263,7 +263,6 @@ --shadow-lux: inset 0 1px 0 rgb(255 255 255 / 64%), 0 12px 34px rgb(8 16 24 / 7%); --shadow-inset: inset 0 1px 0 rgb(255 255 255 / 58%); --shadow-card: var(--shadow-tight); - --ring-focus: 0 0 0 3px color-mix(in srgb, var(--focus) 30%, transparent); --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem; @@ -418,7 +417,6 @@ --shadow-elevated: 0 2px 8px rgb(0 0 0 / 46%), 0 12px 30px rgb(0 0 0 / 48%), 0 28px 58px rgb(0 0 0 / 50%); --shadow-lux: inset 0 1px 0 rgb(255 255 255 / 4%), 0 18px 44px rgb(0 0 0 / 64%); --shadow-inset: inset 0 1px 0 rgb(255 255 255 / 4%); - --ring-focus: 0 0 0 3px color-mix(in srgb, var(--focus) 30%, transparent); color-scheme: dark; } @@ -487,17 +485,22 @@ select { -webkit-tap-highlight-color: transparent; } -button:focus-visible, -a:focus-visible, -summary:focus-visible, -input:focus-visible, -textarea:focus-visible, -select:focus-visible { - box-shadow: var(--ring-focus); +/* One focus owner per interaction. Buttons and links use a single external + outline; form fields draw the same outline inside their own box so scroll + containers and sheets cannot clip it at an edge. Keeping these unlayered is + intentional: it prevents component-level `focus:ring-*` utilities from + stacking a second halo on top of the shared treatment. */ +:where(button, a, summary, input[type="checkbox"], input[type="radio"], input[type="range"]):focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; } +:where(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), textarea, select):focus-visible { + box-shadow: var(--shadow-inset); + outline: 2px solid var(--focus); + outline-offset: -2px; +} + button, a, summary { @@ -1106,6 +1109,14 @@ summary::-webkit-details-marker { letter-spacing: 0; } +/* The universal composer form is the visible control and already owns its + focus-within border + halo. Suppress the nested text field's global focus + paint so the pill never renders competing or clipped concentric rings. */ +.answer-footer-search-input:focus-visible { + box-shadow: none; + outline: none; +} + .answer-footer-search-input::placeholder { color: color-mix(in srgb, var(--text-muted) 72%, var(--text-soft)); font-weight: 560; diff --git a/src/components/clinical-dashboard/favourites-command-library-page.tsx b/src/components/clinical-dashboard/favourites-command-library-page.tsx index 30cd4dd25..1499d88f9 100644 --- a/src/components/clinical-dashboard/favourites-command-library-page.tsx +++ b/src/components/clinical-dashboard/favourites-command-library-page.tsx @@ -455,25 +455,23 @@ function FavouriteMobileCard({ }) { return (
onSelect(item.id)} - onKeyDown={(event) => { - if (event.key === "Enter" || event.key === " ") { - event.preventDefault(); - onSelect(item.id); - } - }} - aria-pressed={selected} + data-testid={`favourite-mobile-card-${item.id}`} className={cn( - "min-w-0 max-w-full rounded-lg border bg-[color:var(--surface)] p-3 shadow-[var(--shadow-tight)]", + "relative min-w-0 max-w-full rounded-lg border bg-[color:var(--surface)] p-3 shadow-[var(--shadow-tight)]", selected ? "border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)]/35 shadow-[inset_3px_0_0_var(--clinical-accent)]" : "border-[color:var(--border)]", - focusRing, )} > -
+
-
+
@@ -504,11 +502,11 @@ function FavouriteMobileCard({
-
event.stopPropagation()}> +
diff --git a/src/components/therapy-compass/bindings.tsx b/src/components/therapy-compass/bindings.tsx index 7a4ff5afb..6e5945321 100644 --- a/src/components/therapy-compass/bindings.tsx +++ b/src/components/therapy-compass/bindings.tsx @@ -413,7 +413,7 @@ export function TcProvider({ sheetClinician, toggleClinician: () => setSheetClinician((prev) => !prev), clinicianTrack: s( - "position:relative;width:42px;height:24px;border-radius:12px;flex:none;cursor:pointer;transition:background .15s ease;background:" + + "position:relative;width:42px;height:24px;border:0;padding:0;border-radius:12px;flex:none;cursor:pointer;transition:background .15s ease;background:" + (sheetClinician ? "var(--clinical-accent)" : "var(--border-strong)") + ";", ), diff --git a/src/components/therapy-compass/screens/sheets-screen.tsx b/src/components/therapy-compass/screens/sheets-screen.tsx index 7e9aad567..1aacb0ab5 100644 --- a/src/components/therapy-compass/screens/sheets-screen.tsx +++ b/src/components/therapy-compass/screens/sheets-screen.tsx @@ -128,9 +128,10 @@ export function SheetsScreen() {