From 3b25a1c311848bf573cdfe9546d8493f10cec6f9 Mon Sep 17 00:00:00 2001 From: Stephen Clower Date: Wed, 11 Mar 2026 16:52:45 -0400 Subject: [PATCH] Add small delay when setting first ARIA alert This corrects a problem where screen readers weren't registering the first alert that Mathquill generated. --- src/controller.ts | 5 +++++ src/services/aria.ts | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/controller.ts b/src/controller.ts index 2f9e72800..1c3bb68c0 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -50,6 +50,11 @@ class ControllerBase { this.options = options; this.aria = new Aria(this.getControllerSelf()); + // Attach the aria element immediately to ensure it's in the DOM before first use + // This prevents the first alert from being lost + if (container) { + this.aria.attach(); + } this.ariaLabel = 'Math Input'; this.ariaPostLabel = ''; diff --git a/src/services/aria.ts b/src/services/aria.ts index 77e18cd5a..6030fde7f 100755 --- a/src/services/aria.ts +++ b/src/services/aria.ts @@ -27,9 +27,13 @@ class Aria { this.controller = controller; } + private firstMessageSent = false; + 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 +93,18 @@ class Aria { if (this.controller.options.logAriaAlerts && this.msg) { console.log(this.msg); } - this.span.textContent = this.msg; + + // For the first message only, 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. + if (!this.firstMessageSent) { + this.firstMessageSent = true; + setTimeout(() => { + this.span.textContent = this.msg; + }, 50); + } else { + this.span.textContent = this.msg; + } } } return this.clear();