Search topics
Forms replaced by a thank-you that is never announced
On success, a newsletter form is swapped for a thank-you paragraph that nothing announces, and the Subscribe button that had focus goes with it, so focus falls to the page.
signup.replaceWith(thanks); The problem
Screen reader users press Subscribe and hear nothing, and the button they were on is gone, so they don't know whether they signed up or where they are on the page.
A bookshop's footer has a newsletter sign-up: "Sign up for our newsletter", an Email address field and a Subscribe button. The form sends in the background, and the shop already announces failures in a status line under it. Success is drawn differently: the field and the button disappear, and the thank-you takes their place, "Thanks! Check your inbox to confirm." It looks tidy, and it says the job is done. A screen reader user presses Subscribe and hears nothing. The button they were on no longer exists, so focus has nowhere to be, and when they move on they may find themselves somewhere else on the page, with no word on whether the sign-up worked.
"Thanks! Check your inbox to confirm." is a status message unless it takes focus. Success Criterion 4.1.3 asks that a status message can be found by assistive technology through its role or properties, so that it is announced without focus moving. Content that takes focus is outside the criterion, because taking focus is a change of context and the content is read as focus lands, so that is a way to pass too. Here the thank-you does neither. It is a new p with no role, written into no live region, and nothing focuses it, which is failure F103. Meanwhile the Subscribe button that had focus is removed with the form. The HTML standard's focus fixup then leaves focus on the page's body, and nothing new is given to the screen reader to read there.
The thank-you is drawn where the form was, so anyone looking at the footer sees it. The people who miss it are those who rely on what is spoken or shown in braille, and they also lose the control they were on.
1 of 10 groups affected
- No vision (affected)
- Low vision (not affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (not affected)
- Reach, strength (not affected)
- Cognitive (not affected)
- Seizures (not affected)
- Without vision Screen reader users press Subscribe and hear nothing, so they can't tell whether they are signed up. The button they were on is gone with the form, and they have to find their place on the page again.
A scan finds a labelled field, a named button and a status line, and passes the form. It never sends the form, since on a live site that would sign someone up, so it never sees the form swapped for the thank-you. EaseWeb's audit raises a lead for a page with a form and no live region at all; this page has the status line for failures, so it doesn't get that lead, and no check can tell from the markup that the form will be replaced. AI assistants asked to make the thank-you accessible may add role="status" to the new paragraph as it is created, which arrives together with its words and may not be read, or wrap the form in a live region, which gets the thank-you read but still leaves focus on the page's body. Only sending the form with a screen reader running shows what is said and where focus goes.
Try it
Press Subscribe, listen, then press Tab.
The panels under the page show where focus went and what a screen reader says. Is the thank-you heard, and where does the next Tab start?
Broken Can you fix this?
Screen reader says:
(Press Subscribe)
The fix
When the result takes the form's place, move focus to it; or keep the form and write the result into its status line.
- 1Never remove the focused button without giving focus to what took its place.
- 2Put the whole result in the text that takes focus, or in a status region that was there before the send.
- 3Keep failures in the status line, with focus on the button.
<section aria-labelledby="signup-title"> <h2 id="signup-title">Sign up for our newsletter</h2> <form id="signup"> <label for="signup-email">Email address</label> <input id="signup-email" name="email" type="email" autocomplete="email" required> <button>Subscribe</button> </form> <p id="signup-message" role="status"></p></section>A thank-you that takes focus
<section aria-labelledby="signup-title"> <h2 id="signup-title">Sign up for our newsletter</h2> <form id="signup"> <label for="signup-email">Email address</label> <input id="signup-email" name="email" type="email" autocomplete="email" required> <button>Subscribe</button> </form> <p id="signup-message" role="status"></p></section>Failures are announced: they go into the status line, and the form and its focused button stay. Success is the gap. The new paragraph has no role and nothing focuses it, so it isn't read, and replaceWith() removes the Subscribe button that had focus, so focus falls to the page's body.
The design stays as drawn. The thank-you gets tabindex="-1", which lets a script focus it without adding it to the Tab order, and it is focused as soon as it is in the page. A screen reader reads it as focus lands, and the next Tab goes on from there, to whatever follows the form, so nobody loses their place. Failures are unchanged: the form stays, focus stays on Subscribe, and the status line says what went wrong.
Both get the thank-you read and leave nobody on a removed button. Move focus to the thank-you when the design must replace the form: it is two lines of script, and the page looks exactly as drawn. Keep the form when the design can change: focus never moves, one status line handles both results, and the address stays in view to be checked. Neither is better for every site; it depends on whether the form has to go.
Focus the whole message. Give focus to the element that holds the result in words, not to an icon, a heading that only says "Done", or the top of the page. Keep the thank-you short enough to be read in one go; if it needs a heading, make the heading say the result ("You're signed up") and focus the heading.
After the new markup renders. In a component framework, the success branch often swaps the form inside an {#if} or redraws the whole footer, which removes the focused button just as replaceWith() does. Move focus once the new content is in the page (after Svelte's tick(), React's effect, Vue's nextTick()), and check that a later redraw doesn't replace the element you focused.
The focus ring. After keyboard use, browsers usually draw a focus ring around the focused paragraph. Leave it: it shows sighted keyboard users where they are now. It usually doesn't appear after a mouse click.
Failures stay put. A failed send must not replace the form, since people need it to try again. Keep focus on Subscribe and write the failure into the status line, as both fixes do. Form success and error messages that are never announced explains that line, and why it is emptied before each message.
Embedded forms. Forms from mailing-list and marketing services often bring a script that swaps the form for their own thank-you. Test the embed on your page; if its script can't be changed, check whether the service can redirect to a thank-you page instead, which a screen reader announces as a new page.
Not fixes. Adding role="status" or aria-live to the new paragraph as it is created gives a region that arrives together with its words, which is often not announced; status messages that are shown but never announced shows that failure. Wrapping the form in a live region and swapping it inside gets the thank-you read, but focus still falls to the page's body.
Fixing this with an AI coding assistant? Get this guide as Markdown, or get every guide in your coding agent.
Design ideas
A thank-you can take the form’s place, and take focus with it.
Three ways to replace the form with a thank-you. Tab to Subscribe and press Enter in each: two move the focus ring onto the thank-you, and one leaves it nowhere.
In the design file
- Mark where the keyboard lands once the thank-you appears, and draw it with its focus ring.
- Write the thank-you as one sentence that says what happened and what to do next.
- Draw the failed send too, with the form still in place and the message under it.
- Write the ratios next to the thank-you’s colours, at least 4.5:1 for its words.
Passes
- A card with the next step The form gives way to a white card with a teal tick, “Check your inbox” and the address, and a “Use another address” button that brings the form back. The card takes focus as it appears, so its first line is read and the next Tab reaches the button. Teal on white is 9.08:1.
- A centred heading with an envelope An envelope in a pale teal circle sits over “You’re on the list” in large type and one line on what comes next, all centred where the form was. The heading takes focus, so it is read first and the line after it follows. The ink on white is 14.66:1.
Fails
- “Subscribed!” rising into the form’s placeWCAG 4.1.3 The form goes, and a tick with “Subscribed!” rises into its place in about a third of a second. The movement shows the change and stops, but nothing takes focus: after Enter, no ring shows anywhere, and a screen reader hears nothing. Focus the new line and the movement can stay. The sample brings the form back after four seconds.
Rule of thumb: Move to show a change, then stop
Verify the fix
4 checks, no mouse.
Test with: Screen reader, Keyboard
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the form component, its script or the embed changes.
Limits
Screen readers and browsers differ in what they read when focus moves to a paragraph, and in where they continue after an element is removed. This pattern covers one short form whose success replaces it in place. A form that stays on the page and announces its result is covered in form success and error messages that are never announced, and a form that goes to a new page or view in single-page app route changes that are never announced. Test your real forms, including embedded ones, with the screen readers your users use.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.