From 766b41387174aa449c66613ee918557092acd76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Wed, 21 Oct 2026 09:36:05 +0200 Subject: [PATCH] fix(ui): keep a disabled chosen segment readable and the range rows at 44 px A faded primary fill reads about 2:1 in light mode, so a disabled chosen segment swaps to the token pair Button uses instead of the opacity. The small range rows keep 44 px under a mouse as well, as the chart tabs had, and the changelog names the fill by what it is. --- CHANGELOG.md | 2 +- src/components/ui/__tests__/segmented.test.tsx | 10 +++++++++- src/components/ui/segmented.tsx | 15 +++++++++------ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 789e762d7..7850a2335 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ drew them on a grey track with the chosen option raised. Above a chart that read heavier than the chart itself. The shared control now draws what the dashboard's range tabs always had: no track, the chosen option filled in - the accent colour. The range rows above charts are back to their smaller + the highlight colour. The range rows above charts are back to their smaller type. Keyboard navigation and the touch size the shared control brought stay as they are. diff --git a/src/components/ui/__tests__/segmented.test.tsx b/src/components/ui/__tests__/segmented.test.tsx index 91794e816..7f3770b4c 100644 --- a/src/components/ui/__tests__/segmented.test.tsx +++ b/src/components/ui/__tests__/segmented.test.tsx @@ -79,7 +79,15 @@ describe("", () => { const small = html({ size: "sm" }); expect(small).toContain("text-xs"); expect(small).not.toContain("text-sm"); - expect(small.match(/pointer-coarse:min-h-11/g)?.length).toBe(2); + expect(small.match(/min-h-11/g)?.length).toBe(2); + }); + + it("keeps a disabled chosen segment on the readable token pair", () => { + const chosen = html({ disabled: true }) + .match(/]*>/g) + ?.find((b) => b.includes('aria-checked="true"')); + expect(chosen).toContain("disabled:bg-primary-disabled"); + expect(chosen).toContain("disabled:opacity-100"); }); it("renders pressed buttons in toggle mode", () => { diff --git a/src/components/ui/segmented.tsx b/src/components/ui/segmented.tsx index 3ce63c16b..fada16f3a 100644 --- a/src/components/ui/segmented.tsx +++ b/src/components/ui/segmented.tsx @@ -1,8 +1,8 @@ "use client"; /** - * The segmented control: a short row of mutually exclusive choices on one - * track (zoom level, chronicle grouping, cards or table, recent or all, + * The segmented control: a short row of mutually exclusive choices (zoom + * level, chronicle grouping, cards or table, recent or all, * Paperless-ngx or Papra). One look everywhere, the one the dashboard chart * tabs always had: no track, the chosen segment filled in the primary colour, * the others plain until hovered. A muted track with a raised segment was @@ -26,8 +26,9 @@ * setter that saves a preference is not called twice. * * Touch: every segment is at least 44 px on a coarse pointer and 36 px under - * a mouse, the same floor `Button` carries. The four controls this replaced - * sat at 36, 36, 44 and 44. + * a mouse, the same floor `Button` carries; a `sm` range row keeps 44 px + * under a mouse too, as the chart tabs always had. The four controls this + * replaced sat at 36, 36, 44 and 44. * * An option with an `icon` and `iconOnly` on the control renders the icon and * names the segment through `aria-label` and `title`. @@ -150,11 +151,13 @@ export function Segmented({ iconOnly ? "size-9 pointer-coarse:size-11" : size === "sm" - ? "min-h-9 px-2 sm:px-3 pointer-coarse:min-h-11 pointer-coarse:min-w-11" + ? "min-h-11 px-2 sm:px-3 pointer-coarse:min-w-11" : "min-h-9 px-3 pointer-coarse:min-h-11", stretch && "flex-1", checked - ? "bg-primary text-primary-foreground hover:bg-primary/90" + ? // A faded fill reads ~2:1 in light mode; disabled swaps to + // the token pair `Button` uses instead of the opacity. + "bg-primary text-primary-foreground hover:bg-primary/90 disabled:bg-primary-disabled disabled:text-primary-disabled-foreground disabled:opacity-100" : "text-foreground hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", )} >