Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 7.6Intermediate ~18 min · shot 6 of 7

Motion, Focus and Screen Readers

In one line

Animation changes what is on the screen but not what keyboards and screen readers reach. Move focus on purpose, return it when something closes, announce through a live region that already exists, and make hidden things unreachable with inert.

0/7 · 0%

Think of it like this

A theatre curtain hides the stage from the audience, but the actors can still be heard behind it. Moving a menu off-screen with a transform is the curtain: you cannot see it, and a keyboard or screen reader still walks straight into it.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

focus
The element that receives keyboard input right now. Tab moves it.
live region
An element whose added text a screen reader reads out without moving focus.
inert
An HTML attribute that makes an element and everything inside it unreachable: no focus, no clicks, no accessibility tree.
accessibility tree
The version of the page that screen readers read. Transforms and animations do not change it.

Step by step

01Decide where focus goes

For every panel, dialog or menu that opens, write where focus goes when it opens, and where it returns when it closes.

02Close after the exit

Let the exit animation finish, then close, set inert or hidden, and return focus. Never leave focus inside something that has gone.

03Pick a fallback

If the opener may disappear, pick a safe place (a heading, a main landmark with tabindex="-1") and focus it.

04Test with the keyboard, then a screen reader

Put the mouse away. Tab through the open and closed states. Then listen with a real screen reader: the console cannot tell you what is announced.

Try it yourself

  1. 1

    Open the menu and watch inert flip

    Press Menu. The line under it says menu open, and the menu slides in (a transform track, 300 ms). Press again: it slides out and the line says menu closed (inert).

  2. 2

    Lose focus on purpose

    In the dialog example delete the line if (document.activeElement === document.body) fallback.focus();. The last line prints after the fix: focus is on #body (the # is only the script's wording): nothing chose a target, so focus stays on the page.

Code & diagrams

Focus when a dialog opens and closes

The script opens and closes a native dialog and prints where focus is. The third line is the lost-focus case: the opener was removed while the dialog was open.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

dialog open: focus is on #close
dialog closed: focus is on #open
opener removed while open: focus is on body
after the fix: focus is on #fallback
An off-screen menu is still reachable

The menu is moved off-screen with a transform. Press Menu to slide it in and out: the script sets `inert` while it is closed.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

closed with transform: a link inside can take focus: true
closed with transform: the link is still on the page: visible
closed with inert: a link inside can take focus: false

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget inert on the closed menu

In the menu example, delete the line menu.inert = !open; // off-screen means unreachable, press Run, press Menu twice (open, then close), then press Tab twice.

terminal
$ Press Tab with the menu closed
── what you'll see ──
Focus goes to the link `Bikes` inside the closed menu, whose right edge is at 0 px (measured: `document.activeElement.id` is `l1`). The focus ring is off-screen, and a screen reader reads the links of a menu nobody can see.

Myth vs fact

Myth

If it is off-screen, it is hidden.

Fact

A transform, left or opacity: 0 only change how it looks. Links stay focusable and text stays readable until you use inert, hidden, display: none or visibility: hidden.

Remember this

  1. 1

    The problem this solves: mouse users judge a page by what moves. Keyboard and screen-reader users judge it by where focus is and what is announced, and animation does nothing for either. A page can look perfect and still strand focus on something invisible.

  2. 2

    Focus follows the content. When a dialog opens, focus must move into it, and back to the opener when it closes. A native <dialog> with showModal() does both: the example shows focus on Close when open and on #open after close(). A hand-made panel must do it in code. With an exit animation, close and return focus when it ends.

  3. 3

    Focus can be lost. If the opener is removed while the dialog is open, there is nowhere to return to. The example measures it: focus falls to body, so the next Tab starts from the top of the page. Choose a fallback target on purpose.

  4. 4

    Hidden by transform is not hidden. A transform moves pixels. Links inside stay focusable and readable: the example focuses a link in a closed off-screen menu and it works. inert removes the whole subtree from focus, clicks and the accessibility tree, and then the same focus() does nothing. Set it when the exit starts, or use visibility: hidden when the exit ends.

  5. 5

    Announcements. Screen readers announce text added to a live region (role="status") that already exists, which is why Phase 4's #toasts is in the HTML from the start. The announcement does not depend on the animation, so keep a toast long enough to be read. This course cannot run a screen reader in the page: test with a real one (Ravi does).

Explain it without notes

01

Why is an off-screen menu moved with a transform still a problem for keyboard users?

02

What happens to focus if the element that opened a dialog is removed while it is open?

Practice

01

A side panel slides in and out. Write the two lines that make it unreachable when closed and reachable when open.

02

A toast announces "Added to cart". Why does it need to be in the HTML before the message is added?

Done when you can

  • You move focus into a panel when it opens and back when it closes

  • You choose a fallback for lost focus

  • You use inert or visibility: hidden for things moved out of view