Sonner renders toasts as <li> elements inside an <ol> within an
aria-live="polite"<section>. Screen readers announce list-item
positions (e.g. "1 of 1") and empty-list state ("list of 0") on every
toast add/remove, producing duplicate or triple read-outs.
Fix: disable Sonner's built-in live region (aria-live → "off") and
provide a separate, flat <div aria-live="polite"> that contains only
plain text — no list markup. A MutationObserver watches for new toast
text nodes inside Sonner and mirrors them into the custom live region,
so the screen reader announces each message exactly once.
Accessible toast container that wraps Sonner.
Sonner renders toasts as
<li>elements inside an<ol>within anaria-live="polite"<section>. Screen readers announce list-item positions (e.g. "1 of 1") and empty-list state ("list of 0") on every toast add/remove, producing duplicate or triple read-outs.Fix: disable Sonner's built-in live region (
aria-live→"off") and provide a separate, flat<div aria-live="polite">that contains only plain text — no list markup. AMutationObserverwatches for new toast text nodes inside Sonner and mirrors them into the custom live region, so the screen reader announces each message exactly once.