Lesson 07 · Part 2 · Focus

Managing focus

Focus is the spot on the page that receives your keystrokes right now. It is the keyboard's version of the mouse pointer. If you hide it, jump it around, or lock it in a box with no door, the page turns into a maze.

In one minute

In plain words

Picture a spotlight on a stage. Only the actor in the light gets to speak. Pressing Tab moves the light to the next actor. If the light is invisible, nobody knows who is on. If it jumps to the back row for no reason, the audience is lost. If you close a door and the light was in that room, it must come back to where it started.

1. Focus must be visible

Who uses the keyboard? People who cannot use a mouse (motor disability, tremor, repetitive strain), blind screen reader users, and power users who simply prefer it. For all of them the focus indicator (the ring around the active control) is the only way to know where they are. A ring is visible when it contrasts at least 3 : 1 with its surroundings (WCAG 1.4.11). WCAG 2.2 also says the focused control must not be hidden behind a sticky header or cookie banner (2.4.11).

Lab: with and without the ring

Step 1. Choose a version, click in the box, then press Tab through the buttons.

Focus is on: nothing yet (only this line tells you; a keyboard user has no such line)


          
Fails.

Why :focus-visible and not :focus? It shows the ring for keyboard users, and usually hides it for mouse clicks on buttons, so designers have no excuse to remove it. A good ring is 2 px or thicker, solid, and offset a little from the control.

2. Tab order follows the source order

By default Tab visits links, buttons, and form fields in the order they appear in the HTML, not the order they appear on screen. CSS can move things around on screen (flex-direction: row-reverse, order, grid placement) without moving them in the HTML. Then sighted keyboard users watch the focus ring jump left, right, left. This fails WCAG 2.4.3 (Focus Order) and 1.3.2 (Meaningful Sequence).

Lab: who is first?

Step 2. The HTML order is always A, B, C. Pick a layout, then press Tab through the three buttons and read the log.

Focus log (screen order is shown on the buttons, left to right):

Nothing yet.

Fine.
Never fix order with a positive tabindex

tabindex="1" or higher jumps those items ahead of everything else on the page, even the skip link and the menu. It is fragile: add one new button and the whole order is wrong. Change the HTML order instead.

3. Move focus when the context changes, and give it back

When a dialog opens, a screen reader user hears nothing unless focus moves there, because the new content may be far from where they were. When it closes, focus must return to the button that opened it. If not, focus falls back to the top of the page and they must start over. Also: do not move focus on hover, on typing, or on a timer. Move it only when the user asked for a new place.

Lab: open a note

Step 3. Pick a version. Open the note with the keyboard (Enter), then close it with Escape or Close, and watch where focus lands.

Focus is on: nothing yet

Try it.

The native <dialog> element with showModal() does most of this for you: it moves focus in, blocks the page behind, closes on Escape, and returns focus to the opener. Prefer it to hand-made dialogs. After inserting new content such as a form error summary, you can also move focus to the heading with tabindex="-1" and .focus().

4. Contain focus in a modal, with a way out

A modal blocks the rest of the page. While it is open, the page behind must not be reachable, or a keyboard user tabs "behind" the modal and loses it. That is called containing focus. Done wrong it becomes a keyboard trap (WCAG 2.1.2): focus goes in and cannot come out. So: contain only while the modal is open, and always offer Escape plus a visible Close button.

Lab: leaky versus contained

Step 4. Pick a version, open the modal, and press Tab several times. Press Escape to leave.

Page behind the modal

Focus is on: nothing yet

Open the modal.

Modern code makes the page behind inert. The inert attribute removes it from the tab order and from the accessibility tree at once. The lab uses it. Without it, you must write the wrap-around key handling yourself, and every screen reader browse mode can still wander off.

5. Skip links and tabindex

A skip link is the first focusable control on the page. It jumps past the header and menu to the main content, so keyboard users do not press Tab forty times on every page (WCAG 2.4.1). This course page has one: press Tab once after the page loads. The target (<main id="main" tabindex="-1">) needs tabindex="-1" so some browsers can actually move focus there.

Lab: what does tabindex do?

Step 5. Choose a value for the box below. Press Tab to see if it is reached. Press the "Call .focus()" button to see if a script can reach it.

A plain div


          
Pick a value.

Use tabindex="0" rarely, only when you build a custom widget that must be reached (and then also give it a role and key handling; see the ARIA lessons). Use tabindex="-1" for things you move focus to by code: headings, error summaries, dialogs.

Common mistakes

You seeWhy it is a problemFix
*:focus { outline: none }Keyboard users cannot see where they are.Style :focus-visible clearly.
CSS reorders items on screenFocus jumps around the page.Reorder the HTML.
tabindex="5"Breaks the natural order, fragile.Only 0 or -1.
Dialog opens, focus stays on the buttonScreen reader users do not know it opened.Move focus into it.
Dialog closes, focus is lostUser restarts from the top of the page.Return focus to the opener.
Modal with no Escape or CloseA keyboard trap (2.1.2).Always offer both.
Sticky header covers the focused linkFocus is hidden (2.4.11).scroll-padding-top.
Try it yourself
  1. Open the note in lab 3 with the keyboard, then close it. Focus should land back on "Open note".
  2. On any website you use, press Tab repeatedly. Can you always see the ring? Does the order make sense?
  3. Press Shift+Tab to go backwards through lab 4's modal.

Remember

Edit this example in the playground