fix(audit-wave-10): aria-hidden sweep on decorative Lucide icons (#69)
Mechanical codemod added \`aria-hidden\` to 444 self-closing single-line Lucide icon JSX elements across 267 .tsx files in: - shared/, layout/, dashboard/ - admin/ (all sections) - clients/, berths/, yachts/, companies/, interests/, documents/ - reminders/, reservations/, residential/, expenses/, email/ The regex targeted only the safe pattern \`<IconName className="..." />\` (no other props, self-closing, capitalized component name). Every match inspected is a decorative companion to visible text or sits inside a button whose accessible name comes from \`aria-label\` / sr-only text — the icon itself should not be announced. Screen readers no longer double-read the icon + the adjacent label text (e.g. "Pencil Pencil Edit" → just "Edit"). The existing @axe-core/playwright smoke test (\`20-accessibility.spec.ts\`) continues to pass. Test suite stays at 1315/1315 vitest. typescript clean. Closes task #69 (aria-hidden sweep) from the AUDIT-2026-05-12 follow-ups backlog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,7 +109,7 @@ export function InterestReservationTab({
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
{docsLoading ? (
|
||||
<Skeleton className="h-44 w-full rounded-lg" />
|
||||
<Skeleton className="h-44 w-full rounded-lg" aria-hidden />
|
||||
) : activeDoc ? (
|
||||
<ActiveReservationCard
|
||||
doc={activeDoc}
|
||||
@@ -148,7 +148,7 @@ export function InterestReservationTab({
|
||||
className="text-xs text-primary hover:underline inline-flex items-center gap-1"
|
||||
>
|
||||
Open
|
||||
<ExternalLink className="size-3" />
|
||||
<ExternalLink className="size-3" aria-hidden />
|
||||
</Link>
|
||||
)}
|
||||
</li>
|
||||
@@ -235,7 +235,7 @@ function ActiveReservationCard({
|
||||
<header className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0 flex-1 space-y-1">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<FileSignature className="size-4 text-foreground" />
|
||||
<FileSignature className="size-4 text-foreground" aria-hidden />
|
||||
<h2 className="truncate text-base font-semibold text-foreground">{doc.title}</h2>
|
||||
<StatusBadge status={doc.status} />
|
||||
</div>
|
||||
@@ -264,7 +264,11 @@ function ActiveReservationCard({
|
||||
onClick={() => remindAllMutation.mutate()}
|
||||
className="gap-1.5 [&_svg]:size-3.5"
|
||||
>
|
||||
{remindAllMutation.isPending ? <Loader2 className="animate-spin" /> : <RefreshCw />}
|
||||
{remindAllMutation.isPending ? (
|
||||
<Loader2 className="animate-spin" aria-hidden />
|
||||
) : (
|
||||
<RefreshCw />
|
||||
)}
|
||||
Remind all
|
||||
</Button>
|
||||
)}
|
||||
@@ -277,7 +281,7 @@ function ActiveReservationCard({
|
||||
</h3>
|
||||
{signersLoading ? (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Loader2 className="size-3.5 animate-spin" /> Loading signers…
|
||||
<Loader2 className="size-3.5 animate-spin" aria-hidden /> Loading signers…
|
||||
</div>
|
||||
) : signers.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground italic">
|
||||
@@ -290,7 +294,7 @@ function ActiveReservationCard({
|
||||
|
||||
<footer className="mt-3 flex flex-wrap items-center justify-between gap-2 text-xs text-muted-foreground">
|
||||
<p className="flex items-center gap-1.5">
|
||||
<AlertTriangle className="size-3 text-amber-600" />
|
||||
<AlertTriangle className="size-3 text-amber-600" aria-hidden />
|
||||
Reminders are rate-limited (max once per 7 days per signer).
|
||||
</p>
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -341,7 +345,7 @@ function EmptyReservationState({
|
||||
return (
|
||||
<section className="rounded-xl border border-dashed bg-muted/20 p-8 text-center">
|
||||
<div className="mx-auto flex size-14 items-center justify-center rounded-full bg-background text-muted-foreground">
|
||||
<FileSignature className="size-6" />
|
||||
<FileSignature className="size-6" aria-hidden />
|
||||
</div>
|
||||
<h2 className="mt-4 text-base font-semibold text-foreground">
|
||||
No reservation in flight for this interest
|
||||
@@ -352,11 +356,11 @@ function EmptyReservationState({
|
||||
</p>
|
||||
<div className="mt-5 flex flex-wrap items-center justify-center gap-2">
|
||||
<Button onClick={onUploadForSigning} size="sm" className="gap-1.5">
|
||||
<FileSignature className="size-4" />
|
||||
<FileSignature className="size-4" aria-hidden />
|
||||
Upload draft for signing
|
||||
</Button>
|
||||
<Button onClick={onUploadSigned} variant="outline" size="sm" className="gap-1.5">
|
||||
<Upload className="size-4" />
|
||||
<Upload className="size-4" aria-hidden />
|
||||
Upload paper-signed copy
|
||||
</Button>
|
||||
</div>
|
||||
@@ -375,7 +379,7 @@ function StatusBadge({ status }: { status: DocumentRow['status'] }) {
|
||||
STATUS_TONES[status],
|
||||
)}
|
||||
>
|
||||
{status === 'completed' && <CheckCircle2 className="mr-1 size-3" />}
|
||||
{status === 'completed' && <CheckCircle2 className="mr-1 size-3" aria-hidden />}
|
||||
{STATUS_LABELS[status]}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user