mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(a11y): switch names, contrast floors, and chart-toggle pressed state
- Admin SettingsToggle: name the Radix switch via aria-label so it no longer announces as a bare 'switch, on/off' (one shared component, six call sites). - Replace the phantom text-destructive-foreground / text-warning-foreground utilities with the vetted destructive pattern (bg-destructive text-white dark:bg-destructive/60) and a readable foreground on the warning wash — no more dead classes riding on cascade order. - Drop the sub-AA opacity tiers: lab sparkline + titration marker off muted/70, state icons (query-error, chart empty/error, med sort) off muted/60 to the full token so they clear the light-theme floor. - Chart range toggles expose aria-pressed (lab, custom-metric, health, mood, compliance) so the active window is conveyed to assistive tech.
This commit is contained in:
1 parent
9f4c7e48fe
commit
930fa81124
16 files changed
+21
-11
No files matched your search
@@ -192,6 +192,11 @@ export function SettingsToggle({
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
// The visible label is a sibling `<p>` with no association to the
|
||||
// Radix switch button, so a screen reader would announce a bare
|
||||
// "switch, on/off". Name it explicitly — one fix covers every admin
|
||||
// toggle that mounts this shared component.
|
||||
aria-label={label}
|
||||
checked={checked}
|
||||
onCheckedChange={onCheckedChange}
|
||||
disabled={disabled}
|
||||
|
||||
@@ -56,7 +56,7 @@ export function ChartEmptyState({
|
||||
style={height === undefined ? undefined : { height }}
|
||||
>
|
||||
<LineChartIcon
|
||||
className="text-muted-foreground/60 h-8 w-8"
|
||||
className="text-muted-foreground h-8 w-8"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="text-sm font-medium">{title}</p>
|
||||
|
||||
@@ -73,7 +73,7 @@ export function ChartErrorState({
|
||||
style={height === undefined ? undefined : { height }}
|
||||
>
|
||||
<AlertTriangle
|
||||
className="text-muted-foreground/60 h-8 w-8"
|
||||
className="text-muted-foreground h-8 w-8"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="text-sm font-medium">{title}</p>
|
||||
|
||||
@@ -90,6 +90,7 @@ export function ComplianceLineChart({
|
||||
<Button
|
||||
key={r.label}
|
||||
variant={activeRangePoints === r.points ? "default" : "ghost"}
|
||||
aria-pressed={activeRangePoints === r.points}
|
||||
size="sm"
|
||||
className="min-h-11 px-3 text-xs"
|
||||
onClick={() => setActiveRangePoints(r.points)}
|
||||
|
||||
@@ -1666,6 +1666,7 @@ export function HealthChart({
|
||||
<Button
|
||||
key={r.labelKey}
|
||||
variant={rangePoints === r.points ? "default" : "ghost"}
|
||||
aria-pressed={rangePoints === r.points}
|
||||
size="sm"
|
||||
className="min-h-11 px-2 text-xs sm:px-3"
|
||||
onClick={() => {
|
||||
|
||||
@@ -1050,6 +1050,7 @@ export function MoodChart({
|
||||
<Button
|
||||
key={r.labelKey}
|
||||
variant={rangePoints === r.points ? "default" : "ghost"}
|
||||
aria-pressed={rangePoints === r.points}
|
||||
size="sm"
|
||||
className="min-h-11 px-2 text-xs sm:px-3"
|
||||
onClick={() => setRangePoints(r.points)}
|
||||
|
||||
@@ -106,6 +106,7 @@ export function CustomMetricChart({
|
||||
key={r.key}
|
||||
size="sm"
|
||||
variant={range === r.key ? "secondary" : "ghost"}
|
||||
aria-pressed={range === r.key}
|
||||
className="h-7 px-2 text-xs"
|
||||
onClick={() => setRange(r.key)}
|
||||
>
|
||||
|
||||
@@ -115,6 +115,7 @@ export function LabBiomarkerChart({
|
||||
key={r.key}
|
||||
size="sm"
|
||||
variant={range === r.key ? "secondary" : "ghost"}
|
||||
aria-pressed={range === r.key}
|
||||
className="h-7 px-2 text-xs"
|
||||
onClick={() => setRange(r.key)}
|
||||
>
|
||||
|
||||
@@ -54,7 +54,7 @@ export function LabTrendSparkline({
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
className="text-muted-foreground/70 overflow-visible"
|
||||
className="text-muted-foreground overflow-visible"
|
||||
aria-hidden
|
||||
role="presentation"
|
||||
>
|
||||
|
||||
@@ -927,7 +927,7 @@ function DeleteRowConfirm({
|
||||
e.preventDefault();
|
||||
onConfirm();
|
||||
}}
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
className="bg-destructive dark:bg-destructive/60 text-white hover:bg-destructive/90"
|
||||
>
|
||||
{t("medications.detail.intake.deleteRow.confirmAction")}
|
||||
</AlertDialogAction>
|
||||
|
||||
@@ -228,7 +228,7 @@ export function IntakeHistoryEditable({
|
||||
}}
|
||||
disabled={bulkBusy}
|
||||
aria-busy={bulkBusy || undefined}
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
className="bg-destructive dark:bg-destructive/60 text-white hover:bg-destructive/90"
|
||||
>
|
||||
{t("medications.detail.intake.bulkDelete.confirmAction")}
|
||||
</AlertDialogAction>
|
||||
@@ -262,7 +262,7 @@ export function IntakeHistoryEditable({
|
||||
}}
|
||||
disabled={rowDeleteBusy}
|
||||
aria-busy={rowDeleteBusy || undefined}
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
className="bg-destructive dark:bg-destructive/60 text-white hover:bg-destructive/90"
|
||||
>
|
||||
{t("medications.detail.intake.deleteRow.confirmAction")}
|
||||
</AlertDialogAction>
|
||||
|
||||
@@ -327,7 +327,7 @@ export function MedicationTable({
|
||||
)
|
||||
) : (
|
||||
<ChevronsUpDown
|
||||
className="text-muted-foreground/60 size-3.5"
|
||||
className="text-muted-foreground size-3.5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -183,7 +183,7 @@ export function TitrationTimeline({
|
||||
? "border-primary bg-primary/15 text-primary"
|
||||
: step.isPast
|
||||
? "border-border bg-muted text-muted-foreground"
|
||||
: "border-border/60 text-muted-foreground/70 border-dashed",
|
||||
: "border-border text-muted-foreground border-dashed",
|
||||
)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
|
||||
@@ -228,7 +228,7 @@ export function WithingsCard({
|
||||
<a
|
||||
href="/api/withings/connect"
|
||||
data-testid="withings-reconnect-banner"
|
||||
className="border-warning/30 bg-warning/10 text-warning-foreground hover:bg-warning/20 block rounded-md border px-3 py-2 text-sm transition-colors"
|
||||
className="border-warning/30 bg-warning/10 text-foreground hover:bg-warning/20 block rounded-md border px-3 py-2 text-sm transition-colors"
|
||||
>
|
||||
<span className="font-medium">
|
||||
{t("settings.integrations.withings.reconnect.banner.title")}
|
||||
|
||||
@@ -204,7 +204,7 @@ function ShareLinksCard() {
|
||||
{t("common.cancel")}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
className="bg-destructive dark:bg-destructive/60 text-white hover:bg-destructive/90"
|
||||
onClick={() => revokeMutation.mutate(link.id)}
|
||||
>
|
||||
{t("settings.sharing.revoke")}
|
||||
|
||||
@@ -40,7 +40,7 @@ export function QueryErrorCard({
|
||||
className="flex flex-col items-center justify-center gap-3 px-6 py-10 text-center"
|
||||
>
|
||||
<AlertTriangle
|
||||
className="text-muted-foreground/60 h-8 w-8"
|
||||
className="text-muted-foreground h-8 w-8"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="text-sm font-medium">{title ?? t("common.loadFailed")}</p>
|
||||
|
||||
Reference in new issue
Block a user