Difficulty implementing tab handling advice for screen readers

Hello,

I have been trying to implement Tab Handling in line with the advice from the example, however I have been struggling to get my notice read by screen readers.

I wrote this simple ViewPlugin which places a hint in a div when the editor has focus (styling omitted for brevity, but it doesn’t touch display or visibility):

const tabEscapePlugin = ViewPlugin.fromClass(
  class {
    hint: HTMLDivElement;

    constructor(view: EditorView) {
      this.hint = document.createElement("div");
      this.hint.ariaLive = "assertive";
      this.hint.classList.add("tab-escape-hint");

      view.dom.appendChild(this.hint);
    }

    destroy() {
      this.hint.remove();
    }

    update(update: ViewUpdate) {
      if (update.view.hasFocus) {
        this.hint.innerText =
          "Editor is focused, press Escape to navigate to other elements using tab.";
      } else {
        this.hint.innerText = "";
      }
    }
  },
);

However while this works fine visually (the hint appears and disappears when the editor is focused/unfocused), there is never any announcement (tested on macOS using VoiceOver with both Firefox and Safari). While debugging this, I recorded my screen then played it back slowly, and saw that my message was being registered by the screen reader, it was just immediately being interrupted with the first line of code in the document. I’m not sure why this is happening, as my understanding is that assertive alerts shouldn’t get interrupted by ordinary content.

Does anyone have an example of how to implement the guidance in a way that alerts a screen reader? I’ve tried a few variations on this (different aria-live and role values) but have not managed to get a working solution yet. Any help would be greatly appreciated.

I would also love to know how to do this properly. You may have some luck with delaying the message to appear a bit after focus, so that the screen reader has time to process the new items created by focusing (field information, first line), but that’s going to be a kludge, since you can’t really know how long it needs for that.

What might also work is if you create this alert the first time Tab is pressed, maybe.

Unfortunately I’ve experimented with delays and have not found something that works there either. As long as it is still reading line-by-line the code it will not successfully announce the alert, whether I do it with my widget or dispatch an EditorView.announce effect. I assume this would also be a problem with the Tab method as long as it is reading, so the only solution really would be to try and address whatever is causing it to behave like this.

The only workaround I have found is to put a visually hidden element before the code editor in the tabbing order with the message content so it is seen first. This isn’t ideal though, since it’s not guaranteed to notify the user and disrupts the tab order. For now I’m leaving it out in the hopes of coming up with something better.