fix(a11y): don't force atomic re-announce of the whole toast stack
/code-review caught that role="status" on the toast container implies aria-atomic="true", so every new toast made a screen reader re-read the entire stack instead of just the new one — the opposite of the U-F fix's intent. Use a bare aria-live="polite" (defaults atomic=false, announcing only the added toast); error toasts keep role="alert". Also simplified the conditional role to a plain ternary.
This commit is contained in:
@@ -18,11 +18,11 @@ export default function Toaster() {
|
||||
return (
|
||||
// Stable, always-mounted polite live region — a polite region must PRE-EXIST in the DOM for a
|
||||
// screen reader to announce content later inserted into it (a region created together with its
|
||||
// first message is unreliably announced). Non-error toasts render inside here and are announced
|
||||
// politely; error/fatal toasts additionally carry role="alert", which announces assertively on
|
||||
// insertion regardless.
|
||||
// first message is unreliably announced). `aria-live` (not role="status") on purpose:
|
||||
// role="status" implies aria-atomic="true", which would re-read the WHOLE stack on every toast;
|
||||
// aria-live defaults atomic=false, so only the newly-added toast is spoken. Error/fatal toasts
|
||||
// additionally carry role="alert", which announces assertively on insertion regardless.
|
||||
<div
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
className="absolute bottom-4 left-4 z-overlay flex flex-col gap-2 w-80 max-w-[calc(100vw-2rem)]"
|
||||
>
|
||||
@@ -32,7 +32,7 @@ export default function Toaster() {
|
||||
return (
|
||||
<div
|
||||
key={toast.id}
|
||||
{...(urgent ? { role: "alert" } : {})}
|
||||
role={urgent ? "alert" : undefined}
|
||||
className="toast-card glass relative overflow-hidden rounded-xl px-3 py-3 flex items-start gap-3 animate-[popIn_0.16s_ease]"
|
||||
>
|
||||
<Icon className={`w-5 h-5 shrink-0 mt-0.5 ${color}`} />
|
||||
|
||||
Reference in New Issue
Block a user