a11y(toaster): announce toasts to screen readers (U-F)
The toast is the app's main feedback channel but had no live region, so a screen reader never spoke it. Each toast now carries role + aria-live by severity: error/fatal interrupt (alert/assertive), everything else waits its turn (status/polite).
This commit is contained in:
@@ -19,9 +19,14 @@ export default function Toaster() {
|
|||||||
<div className="absolute bottom-4 left-4 z-overlay flex flex-col gap-2 w-80 max-w-[calc(100vw-2rem)]">
|
<div className="absolute bottom-4 left-4 z-overlay flex flex-col gap-2 w-80 max-w-[calc(100vw-2rem)]">
|
||||||
{toasts.map((toast) => {
|
{toasts.map((toast) => {
|
||||||
const { icon: Icon, color, bar } = LEVEL_STYLE[toast.level];
|
const { icon: Icon, color, bar } = LEVEL_STYLE[toast.level];
|
||||||
|
const urgent = toast.level === "error" || toast.level === "fatal";
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={toast.id}
|
key={toast.id}
|
||||||
|
// Announce toasts to screen readers — the toast is the app's main feedback channel.
|
||||||
|
// Errors interrupt (assertive/alert); everything else waits its turn (polite/status).
|
||||||
|
role={urgent ? "alert" : "status"}
|
||||||
|
aria-live={urgent ? "assertive" : "polite"}
|
||||||
className="toast-card glass relative overflow-hidden rounded-xl px-3 py-3 flex items-start gap-3 animate-[popIn_0.16s_ease]"
|
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}`} />
|
<Icon className={`w-5 h-5 shrink-0 mt-0.5 ${color}`} />
|
||||||
|
|||||||
Reference in New Issue
Block a user