diff --git a/src/services/aria.ts b/src/services/aria.ts index 77e18cd5a..b9694298f 100755 --- a/src/services/aria.ts +++ b/src/services/aria.ts @@ -27,9 +27,14 @@ class Aria { this.controller = controller; } + private firstMessageSent = false; + private firstMessageTimeout: number | null = null; + attach() { const container = this.controller.container; if (this.span.parentNode !== container) { + // Append the element empty first to ensure screen readers detect the live region + // before any content is added. This fixes the issue where the first alert isn't announced. domFrag(container).prepend(domFrag(this.span)); } } @@ -89,7 +94,24 @@ class Aria { if (this.controller.options.logAriaAlerts && this.msg) { console.log(this.msg); } - this.span.textContent = this.msg; + + // For the first message, use a 50ms delay to ensure the empty live region + // is registered with screen readers before adding content. Screen readers need + // actual time (not just a different execution context) to process the empty element. + // We debounce to ensure that if multiple messages arrive within the first 50ms, + // only the final message is announced (avoiding out-of-order announcements). + if (!this.firstMessageSent) { + if (this.firstMessageTimeout !== null) { + clearTimeout(this.firstMessageTimeout); + } + this.firstMessageTimeout = setTimeout(() => { + this.firstMessageSent = true; + this.firstMessageTimeout = null; + this.span.textContent = this.msg; + }, 50); + } else { + this.span.textContent = this.msg; + } } } return this.clear();