Interrupting without hijacking focus
An agent that needs an answer mid-run has to interrupt without stealing focus, because moving focus is a change of context — role="alert" and a relocated focus ring are two different decisions, and usually only one of them is defensible.
Document seven has a total field the extractor cannot read. The agent can guess, or it can stop and ask. Product wants it to ask, so somebody builds a question card that slides in above the run panel, and the obvious next line of code is to focus it — otherwise nobody answers, and the run sits there.
That line of code is the decision. It has a name in the specification, it costs something specific, and once you can price it you can also see the cases where paying is the right call and the cases where it is vandalism.
What role="alert" actually is
MDN is unambiguous about the expansion. Setting role="alert" “is equivalent to setting aria-live="assertive" and aria-atomic="true"”. Assertive interrupts whatever the screen reader is currently saying. Atomic re-presents the whole region, which for a short question is correct rather than fatal — the opposite of the streaming case, where atomic was the bug.
The property that makes it the right tool here is the next one:
The element with the alert role does not have to be able to receive focus, as screen readers… will automatically announce the updated content regardless of where keyboard focus is.
That is MDN describing precisely the capability the focus-stealing line of code was reaching for. The announcement already crosses the room. Moving focus is not how you get heard; it is a second, separate thing you would be doing on top of being heard.
And the constraint, from the same page: “Because of its intrusive nature, the alert role must be used sparingly and only in situations where the user’s immediate attention is required.” A blocked run waiting on a human answer clears that bar. A field finishing extraction does not, and belongs in the polite transitions region from the announcing-the-stream lesson.
Moving focus is a change of context, by definition
WCAG defines “change of context” in its glossary, and the Understanding document for 3.2.2 On Input restates it: changes of context include changes of user agent, of viewport, of focus, and of content that changes the meaning of the page. Its own worked example is focus being changed to another component when that component receives focus.
Read that precisely, because this is where people over-claim. The glossary tells you that moving focus is a change of context. It does not tell you that every change of context is a failure. 3.2.2 On Input, Level A, prohibits a specific case: a change of context caused by changing the setting of a user interface component, without warning. An agent moving focus mid-run is not a response to the user changing a setting, so 3.2.2 is not the criterion that catches it.
What the definition gives you is the honest vocabulary. When you move focus you are performing a change of context on someone who did not ask for one, and you can say that in the specification’s own words without pretending a criterion forbids it.
MDN adds the practical corollary on the status role page: do not give focus to a status when its content updates, and if a situation requires that focus needs to be moved, then a status or other live region is likely not the appropriate mechanism. That sentence is a fork in the road, and it is worth taking literally. Needing to move focus is evidence that you are building a dialog, not a notification.
Where people get burned
The concrete cost of a stolen focus, in the review gate specifically: a reviewer is halfway through typing a corrected invoice total into document three when the agent asks about document seven. Focus jumps. The next six keystrokes go into the question card. In a screen reader the user has also lost their reading position, which is not the same thing as their focus position and is not restored by returning focus. None of that is visible to the developer who wrote the .focus() call, because on a sighted mouse-driven test the card appears and looks helpful.
Two patterns, and this course picks one
What follows is this course’s judgement, not a rule from any source. MDN documents what alert does; WCAG defines what a change of context is. Neither compares the two patterns or rules on which to use. The comparison below is the course’s, built on top of those facts.
Pattern A — announce, do not move. The question card is inserted into the DOM with role="alert", it is a heading or a landmark so it is reachable by structural navigation, and the announcement names the way in: “Input needed on document 7. Press R to review.” Focus stays exactly where the user put it. The user arrives, if they arrive, because they chose to.
Pattern B — a real modal dialog. The interrupt is a dialog against the APG pattern: role="dialog", aria-modal="true", focus moved into it on open, contained while open, returned to the invoking element on close. It is a change of context and it is disruptive by design, which is the point.
The course’s position is that the deciding question is whether the run is blocked, and that only one pattern is defensible per case:
- The run continues without the answer — the agent is asking so it can improve a result, not so it can proceed. Pattern A. Moving focus here is taking the user’s hands off their own work for something that was not urgent enough to stop the machine.
- The run is blocked and the user started it moments ago — nothing is happening until this is answered, and the user is plausibly still watching. Pattern B is defensible, and the full APG focus contract is the price of using it: in on open, contained while open, returned on close.
- The run is blocked but the user left — four minutes have passed and they are working on something else on the same page. Pattern A again. A dialog that seizes focus from someone who has moved on is the worst case in this whole lesson, because the disruption lands on work the interrupt has nothing to do with.
What is not defensible in any of the three is doing both. An alert plus a focus move announces the question twice — once from the live region, once from the newly focused element — and the second announcement usually interrupts the first. If you build the dialog, the dialog’s own name announces it on focus and it does not need role="alert" as well.
Check your recall
Answer from memory — no scrolling back.
Retrieval check
An engineer sends you a PR that adds a question card with role="alert" and a .focus() call on it, and the description says “belt and braces.” Write the review comment.
Check your answer
Roughly this, and the last paragraph is the part that sticks:
These two do different things and running both makes it worse, not safer.
role="alert"already expands toaria-live="assertive"plusaria-atomic="true", and MDN is explicit that the element does not need focus — the content is announced wherever keyboard focus happens to be. Adding.focus()gets you a second announcement from the newly focused element, which usually cuts off the first.It also costs something the demo will not show. Moving focus is a change of context by WCAG’s own glossary definition, and this one is not user-initiated, so a reviewer mid-correction on another document loses their cursor and their next few keystrokes.
Pick one. If the run is blocked and the user is still here, build the real dialog against the APG pattern — focus in on open, contained while open, returned to the invoking element on close — and drop the alert, because the dialog announces itself. If the run is not blocked, keep the alert, drop the
.focus(), and put the route in the announcement text: “Input needed on document 7. Press R to review.” That choice is our own house rule rather than a W3C requirement, and it is written down in the conformance note with the reasoning attached.
Hands on
Decide the interrupt, and write the rule down
Done when: CONFORMANCE.md records the interrupt decision as a named house rule with its condition (blocked or not blocked), the sourced facts it rests on, and an explicit line saying the choice between the two patterns is your judgement rather than a W3C requirement — plus a 4.1.3 row for the interrupt announcement itself.
- Establish the fact the decision hangs on: when the review gate asks for input mid-run, does the run block? If it depends, write down what it depends on. Everything below follows from this answer, so guessing it makes the rest worthless.
- Pick the pattern. If Pattern B, write out the four obligations of the APG focus contract as separate lines you can verify: focus moves to an element inside the dialog on open, Tab and Shift+Tab do not move focus outside it, and focus returns to the element that invoked the dialog on close.
- Test whether the interrupt is announced at all today. The two ways a correctly-roled alert stays silent are being present in the DOM at page load and being toggled by hiding and showing rather than by inserting content. Check which one you have before recording a pass.
- Add a 4.1.3 Status Messages row at Level AA for the interrupt announcement, separate from the streaming row. Different message, different test, different remediation. Provenance
humanorunchecked. - Write the house rule into the note under a heading of its own, in one short paragraph: the condition, the pattern, and one sentence stating that the choice is your team’s judgement built on MDN’s description of
alertand WCAG’s definition of a change of context, and that neither source rules on it. - Bring the rule into the chat. I will check that it names a condition rather than a preference, and that the sentence separating sourced fact from your judgement is actually there — that is the sentence people delete when they tidy.
What this does not cover
This lesson is about a moment: one question, one announcement, one decision about focus. It says nothing about the record the run leaves behind, which is a structural problem rather than a temporal one. A finished trace with nested steps has an established keyboard model already specified down to the keystroke, and the shortcut almost everybody takes instead is the one pattern in the APG that cannot express nesting at all. The a-trace-tree-you-can-navigate lesson works through that.
It also leaves the note unsigned. You now have rows for streaming, cancel and interrupt, several of them resting on reasoning nobody has published, and at some point somebody has to put a name on the document and hand it to an engineering lead. The conformance-note lesson is about what makes that document worth signing, and it is the one lesson in this course with no primary source at all.
Read this next — primary source
ARIA: alert roleMDN Web Docs (Mozilla) — free. Mozilla documents the platform rather than selling a product built on it.
Three sentences on this page decide the whole lesson: what the role expands to, that it announces regardless of where focus is, and that its intrusiveness means it must be used sparingly. Read the full page for the accessibility-concerns section, which spells out the failure modes of an alert that is present at page load or that is toggled by hiding and showing rather than by inserting content. Those are the two ways a correctly-roled alert still goes unannounced, and they are the two ways teams implement it.
Stuck, curious, or think this lesson is wrong? Ask your teaching agent. The lessons are the scaffold; the conversation is where the learning gets unstuck.