Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions docs/design-system/COMPONENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -665,6 +665,7 @@ tab order, never removes its accessible name); disabled via `controlBase` encodi
`--focus` outline only. **Rules.** Verb-first specific labels, never "OK"/bare "Confirm" ·
hover/active from semantic tokens, never `brightness-*` filters · one filled command per
surface. **Landed.** Danger contrast and hover/active tokens plus the 48px tap-floor comment.
**Tap vs compact-meta (DS-P1-09 / DS-P2-24).** Primary `Button` / `primaryControl` / filled command actions stay `min-h-tap` at **all** breakpoints — no `sm:min-h-10`, `sm:min-h-9`, `lg:min-h-9`, or `min-h-11`. Metadata, disclosure, filter chips, and table micro-actions may use `min-h-compact-meta` (40px) or a documented prefixed compact (`sm:min-h-compact-meta`). `--row-compact` / `min-h-9` (36px) is row height, not a tap target. Recipes: `interactiveCompact` and `tableMicroActionRow` are the named compact-meta exceptions in `ui-primitives.tsx`; `controlBase` stays tap-sized. Full table: TOKENS §2 “Compact-meta vs tap”.
**Open defects → PR.** ref forwarding and the needless client boundary → follow-on.

**Command CTA path.** Pick one encoding; do not invent a fourth.
Expand DownExpand Up@@ -703,7 +704,9 @@ component; broader Gate 9 motion sweep still tracks other surfaces.
### 9.5 `Chip`

**Purpose.** Compact label for tones, filters, categories. Static chips are text, not
targets (tap-exempt under the inline exception). **Modes (union, PR 4).** `static` (no
targets (tap-exempt under the inline exception). Interactive filter chips that are **not**
primary CTAs may use `min-h-compact-meta` (or `sm:min-h-compact-meta`) per TOKENS density
policy; do not copy that step-down onto a filled command action. **Modes (union, PR 4).** `static` (no
removal props representable) · `removable` (`onRemove` + `removeLabel` both required).
**Rules.** Remove control keeps a small visible glyph inside an overlapping hit target
that does not inflate the chip · truncated labels need a full-value path · category tones
Expand DownExpand Up@@ -971,7 +974,10 @@ sanitiser's tiny grammar renders as plain text.
`disabled:opacity` uses migrate in PR 3 · the module splits in PR 12
(`styles/recipes.ts`, actions, feedback, forms, clinical-source, source-metadata
contract) so generic primitives stop importing clinical application modules · recipes
never restate a token value. The 2026-07-31 icon regression (lucide imports replaced
never restate a token value. `interactiveCompact` and `tableMicroActionRow` use
`min-h-tap` on phones and `sm:min-h-compact-meta` on pointer layouts (metadata /
disclosure, not primaries). Do not copy that step-down onto `controlBase` or
`primaryControl`. The 2026-07-31 icon regression (lucide imports replaced
with glyph spans by an unverified merge, repaired in `0b0f393c7`) is the cautionary case:
this file is load-bearing for the icon vocabulary; changes to it require the focused DOM
tests to run.
Expand Down
86 changes: 52 additions & 34 deletions docs/design-system/TOKENS.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/app/globals.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -117,6 +117,9 @@
the edge-to-edge dock keeps its height (see the max-width: 430px block,
which covers the composer input as well as the icon/send buttons). */
--spacing-tap: 3rem;
/* Metadata/disclosure only, never primary CTAs, not a tap-floor substitute.
--row-compact is 36px row height and must not be reused as tap. */
--spacing-compact-meta: 2.5rem;
--spacing-phone-frame: 52.75rem;
/* Ward Flow's fixed phone navigation bar and the matching content reserve. */
--spacing-ward-phone-bar: 3.5rem;
Expand Down
11 changes: 8 additions & 3 deletions src/components/DocumentTagCloud.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,8 +74,11 @@ function DocumentTagChip({
onTagClick?: (tag: SmartDocumentTag) => void;
}) {
const Icon = groupIcon[tag.group];
// Compact-meta visual density (filter chips, not primary CTAs). ::before
// expands the hit toward the 48px tap floor without painting a 48px chip.
// Ring/border stay for Agent 4.
const tagClassName = cn(
"relative inline-flex max-w-full items-center gap-1 rounded-md border font-semibold shadow-[var(--shadow-inset)] before:absolute before:-inset-y-2 before:-inset-x-1 before:content-['']",
"relative inline-flex max-w-full items-center gap-1 rounded-md border font-semibold shadow-[var(--shadow-inset)] before:absolute before:-inset-y-3 before:-inset-x-1 before:content-['']",
compact ? "min-h-6 px-2 text-2xs" : "min-h-7 px-2 text-2xs",
groupToneClass(tag.group),
tag.queryMatched && "ring-2 ring-[color:var(--focus)]/25",
Expand DownExpand Up@@ -160,7 +163,8 @@ export function DocumentTagCloud({
type="button"
onClick={() => setExpanded(true)}
className={cn(
"w-fit rounded-md border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] font-semibold text-[color:var(--text-muted)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)]",
// Compact-meta show-more, not a primary CTA. relative+::before expands hit.
"relative w-fit rounded-md border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] font-semibold text-[color:var(--text-muted)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)] before:absolute before:-inset-y-3 before:-inset-x-1 before:content-['']",
compact ? "min-h-6 px-2 text-2xs" : "min-h-7 px-2 text-2xs",
)}
aria-label={`Show ${hiddenCount} more document tags`}
Expand DownExpand Up@@ -188,7 +192,8 @@ export function DocumentTagCloud({
type="button"
onClick={() => setExpanded(true)}
className={cn(
"inline-flex items-center rounded-md border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] font-semibold text-[color:var(--text-muted)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)]",
// Compact-meta show-more, not a primary CTA. relative+::before expands hit.
"relative inline-flex items-center rounded-md border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] font-semibold text-[color:var(--text-muted)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)] before:absolute before:-inset-y-3 before:-inset-x-1 before:content-['']",
compact ? "min-h-6 px-2 text-2xs" : "min-h-7 px-2 text-2xs",
)}
aria-label={`Show ${hiddenCount} more document tags`}
Expand Down
8 changes: 5 additions & 3 deletions src/components/calculators/bedside-sheet.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,7 +177,8 @@ export function CalculatorsBedsideSheetMockup() {
key={calc.id}
href={`#sheet-${calc.id}`}
className={cn(
"inline-flex min-h-9 shrink-0 items-center gap-1.5 rounded-md border px-2.5 text-2xs font-bold",
// Compact-meta jump chips (in-sheet nav), not primary CTAs.
"inline-flex min-h-compact-meta shrink-0 items-center gap-1.5 rounded-md border px-2.5 text-2xs font-bold",
derived.started
? "border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]"
: "border-[color:var(--border)] bg-[color:var(--surface)] text-[color:var(--text-muted)]",
Expand DownExpand Up@@ -219,7 +220,7 @@ export function CalculatorsBedsideSheetMockup() {
onClick={copySession}
disabled={!startedCalcs.length}
className={cn(
"inline-flex min-h-9 items-center gap-1.5 rounded-md border border-[color:var(--border)] bg-[color:var(--surface)] px-2.5 text-2xs font-bold text-[color:var(--text-muted)] transition hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)] disabled:pointer-events-none disabled:opacity-40",
"inline-flex min-h-tap items-center gap-1.5 rounded-md border border-[color:var(--border)] bg-[color:var(--surface)] px-2.5 text-2xs font-bold text-[color:var(--text-muted)] transition hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)] disabled:pointer-events-none disabled:opacity-40",
focusRing,
)}
>
Expand All@@ -238,7 +239,8 @@ export function CalculatorsBedsideSheetMockup() {
key={calc.id}
href={`#sheet-${calc.id}`}
className={cn(
"inline-flex min-h-9 shrink-0 items-center gap-2 rounded-md border px-2.5 text-2xs font-bold text-[color:var(--text-heading)]",
// Compact-meta jump chips (session dock), not primary CTAs.
"inline-flex min-h-compact-meta shrink-0 items-center gap-2 rounded-md border px-2.5 text-2xs font-bold text-[color:var(--text-heading)]",
derived.flags.length > 0
? "border-[color:var(--danger-border)] bg-[color:var(--danger-soft)]"
: "border-[color:var(--border)] bg-[color:var(--surface)]",
Expand Down
3 changes: 2 additions & 1 deletion src/components/calculators/directory-grid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,7 +237,8 @@ export function CalculatorsDirectoryGridMockup() {
aria-pressed={active}
onClick={() => setDomain(chip.id)}
className={cn(
"inline-flex min-h-10 shrink-0 items-center rounded-md border px-3 text-sm-minus font-bold",
// Compact-meta domain filters, not primary CTAs.
"inline-flex min-h-compact-meta shrink-0 items-center rounded-md border px-3 text-sm-minus font-bold",
active
? "border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]"
: "border-[color:var(--border)] bg-[color:var(--surface)] text-[color:var(--text-muted)] hover:text-[color:var(--text)]",
Expand Down
1 change: 1 addition & 0 deletions src/components/clinical-dashboard/answer-content.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -513,6 +513,7 @@ export function NaturalLanguageAnswer({
<button
type="button"
onClick={() => setSourceOnlyNoticeOpen((current) => !current)}
// Compact-meta disclosure (not a primary CTA). Copy below already uses tap.
className="inline-flex min-h-7 w-full max-w-[68ch] items-center gap-1 px-2 py-0.5 text-left transition hover:bg-[color:var(--warning-soft)]/60 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)]"
aria-expanded={sourceOnlyNoticeOpen}
aria-controls="source-only-disclosure-detail"
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -184,7 +184,7 @@ export function AnswerViewModeControl({
aria-label={`Show ${mode.label.toLowerCase()} answer view`}
title={mode.label}
className={cn(
"inline-flex min-h-tap min-w-0 flex-1 basis-[4.75rem] items-center justify-center gap-1.5 rounded-md px-2 text-xs font-semibold transition sm:flex-none sm:basis-auto sm:px-2.5 lg:min-h-9",
"inline-flex min-h-tap min-w-0 flex-1 basis-[4.75rem] items-center justify-center gap-1.5 rounded-md px-2 text-xs font-semibold transition sm:flex-none sm:basis-auto sm:px-2.5 lg:min-h-compact-meta",
active
? "bg-[color:var(--primary)] text-[color:var(--primary-contrast)] shadow-sm"
: "text-[color:var(--text-muted)] hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)]",
Expand Down
6 changes: 3 additions & 3 deletions src/components/clinical-dashboard/evidence-panels.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -900,7 +900,7 @@ export function ClinicalNotesChecklistPanel({
<button
type="button"
onClick={onOpenTables}
className="inline-flex min-h-tap items-center gap-1.5 rounded-md px-2 text-xs font-semibold text-[color:var(--clinical-accent)] transition hover:bg-[color:var(--clinical-accent-soft)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] lg:min-h-9"
className="inline-flex min-h-tap items-center gap-1.5 rounded-md px-2 text-xs font-semibold text-[color:var(--clinical-accent)] transition hover:bg-[color:var(--clinical-accent-soft)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] lg:min-h-compact-meta"
>
<Table2 aria-hidden="true" className="h-3.5 w-3.5" />
Tables
Expand DownExpand Up@@ -1105,7 +1105,7 @@ export function SafetyFindingsListContent({ findings, query }: { findings: Safet
<Link
href={finding.href}
onClick={() => query && logCitationOpen(query, finding.citation)}
className="inline-flex min-h-tap min-w-0 items-center gap-1 text-xs font-semibold text-[color:var(--primary)] transition hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] lg:min-h-8"
className="inline-flex min-h-tap min-w-0 items-center gap-1 text-xs font-semibold text-[color:var(--primary)] transition hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] lg:min-h-compact-meta"
aria-label={`Open source ${formatSafetyFindingLabel(finding)}`}
>
<span className="truncate">{formatCompactCitationLabel(finding.citation)}</span>
Expand DownExpand Up@@ -1291,7 +1291,7 @@ export function AnswerFeedbackPanel({
disabled={Boolean(pending)}
onClick={() => onSubmit(item.type)}
className={cn(
"inline-flex min-h-tap items-center justify-center gap-1.5 rounded-lg border px-2.5 text-center text-xs font-semibold transition disabled:cursor-not-allowed disabled:opacity-60 lg:min-h-10",
"inline-flex min-h-tap items-center justify-center gap-1.5 rounded-lg border px-2.5 text-center text-xs font-semibold transition disabled:cursor-not-allowed disabled:opacity-60 lg:min-h-compact-meta",
feedbackToneClass(item.tone),
)}
>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -392,7 +392,7 @@ function ContinueStrip({ item, onOpen }: { item: FavouriteItem; onOpen: (item: F
onClick={() => onOpen(item)}
aria-label={`Continue ${item.title}`}
className={cn(
"inline-flex min-h-tap w-full shrink-0 items-center justify-center gap-2 rounded-lg bg-[color:var(--command)] px-4 text-sm font-bold text-[color:var(--command-contrast)] shadow-[var(--e1)] transition hover:bg-[color:var(--command-hover)] sm:min-h-9 sm:w-auto",
"inline-flex min-h-tap w-full shrink-0 items-center justify-center gap-2 rounded-lg bg-[color:var(--command)] px-4 text-sm font-bold text-[color:var(--command-contrast)] shadow-[var(--e1)] transition hover:bg-[color:var(--command-hover)] sm:w-auto",
focusRing,
)}
>
Expand DownExpand Up@@ -1715,7 +1715,7 @@ export function FavouritesCommandLibraryPage({ query = "", demoMode }: { query?:
}
}}
className={cn(
"inline-flex min-h-tap items-center justify-center gap-2 rounded-lg bg-[color:var(--clinical-accent)] px-4 text-sm font-bold text-[color:var(--clinical-accent-contrast)] disabled:opacity-60 sm:min-h-10",
"inline-flex min-h-tap items-center justify-center gap-2 rounded-lg bg-[color:var(--clinical-accent)] px-4 text-sm font-bold text-[color:var(--clinical-accent-contrast)] disabled:opacity-60",
focusRing,
)}
>
Expand Down
10 changes: 5 additions & 5 deletions src/components/clinical-dashboard/favourites-hub.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -379,7 +379,7 @@ export function FavouritesHub({
aria-describedby="favourites-sort-unavailable"
className={cn(
floatingControl,
"min-h-tap cursor-not-allowed px-3 text-xs opacity-60 hover:border-[color:var(--border-lux)] hover:bg-[color:var(--surface-raised)] hover:shadow-[var(--shadow-inset)] sm:min-h-9 sm:px-2.5",
"min-h-tap cursor-not-allowed px-3 text-xs opacity-60 hover:border-[color:var(--border-lux)] hover:bg-[color:var(--surface-raised)] hover:shadow-[var(--shadow-inset)] sm:px-2.5",
)}
>
<ArrowUpDown aria-hidden="true" className="h-4 w-4" />
Expand All@@ -395,7 +395,7 @@ export function FavouritesHub({
aria-describedby="favourites-add-unavailable"
className={cn(
primaryControl,
"min-h-tap cursor-not-allowed justify-center px-3 text-xs opacity-60 hover:bg-[color:var(--command)] hover:shadow-[var(--e1)] active:translate-y-0 sm:min-h-9 sm:px-2.5",
"min-h-tap cursor-not-allowed justify-center px-3 text-xs opacity-60 hover:bg-[color:var(--command)] hover:shadow-[var(--e1)] active:translate-y-0 sm:px-2.5",
)}
>
<Plus aria-hidden="true" className="h-4 w-4" />
Expand DownExpand Up@@ -558,7 +558,7 @@ export function FavouritesHub({
aria-describedby="favourites-new-set-unavailable"
className={cn(
floatingControl,
"mt-3 min-h-9 w-full cursor-not-allowed px-3 text-xs opacity-60 hover:border-[color:var(--border-lux)] hover:bg-[color:var(--surface-raised)] hover:shadow-[var(--shadow-inset)]",
"mt-3 w-full cursor-not-allowed px-3 text-xs opacity-60 hover:border-[color:var(--border-lux)] hover:bg-[color:var(--surface-raised)] hover:shadow-[var(--shadow-inset)]",
)}
>
<Plus aria-hidden="true" className="h-4 w-4" />
Expand DownExpand Up@@ -592,10 +592,10 @@ function FavouriteItemRow({ item, onBrowseSets }: { item: FavouriteItem; onBrows
</div>
</div>
<div className="hidden items-center gap-1.5 sm:flex">
<Link href={item.href} className={cn(floatingControl, "min-h-9 px-2.5 text-xs")}>
<Link href={item.href} className={cn(floatingControl, "px-2.5 text-xs")}>
{item.primaryAction}
</Link>
<button type="button" onClick={onBrowseSets} className={cn(floatingControl, "min-h-9 px-2.5 text-xs")}>
<button type="button" onClick={onBrowseSets} className={cn(floatingControl, "px-2.5 text-xs")}>
<Folder aria-hidden="true" className="h-3.5 w-3.5" />
Browse sets
</button>
Expand Down
Loading
Loading