Keyboard Navigation and Focus

Make every interactive thing reachable and operable without a mouse.

A huge share of users — power users, people with motor disabilities, anyone whose mouse just died — drive the web from the keyboard. If a control can be clicked but not tabbed to, it's broken for them.

Tab order and tabindex

  • Native interactive elements (<a>, <button>, <input>) are focusable automatically — another reason to prefer them.
  • tabindex="0" adds a custom element into the natural tab order.
  • tabindex="-1" makes an element focusable by script (.focus()) but skips it in tabbing.
  • Avoid positive tabindex values like tabindex="5" — they hijack the order and become a maintenance nightmare.

Never kill the focus ring

The visible outline on a focused element is how keyboard users know where they are. Removing it with outline: none and nothing in its place is one of the most common accessibility crimes on the web. If you dislike the default ring, style a nicer one with :focus-visible — don't delete it.

button:focus-visible {
  outline: 3px solid #005fcc;
  outline-offset: 2px;
}

Example

Try it yourself
Loading editor…
Press Run to see the result.

When to use it

  • A developer ensures all interactive elements can be reached with Tab and activated with Enter or Space without a mouse.
  • A custom modal uses tabindex="-1" on the dialog and JavaScript focus management to trap focus inside while it is open.
  • A complex widget uses roving tabindex (tabindex="0" on one item, tabindex="-1" on others) to allow arrow-key navigation within a group.

More examples

Focusable elements and tabindex

Links, buttons, and inputs are focusable by default; tabindex="0" adds a div to the tab order with a role.

Example · html
<!-- Naturally focusable elements -->
<a href="/courses">Courses</a>
<button>Submit</button>
<input type="text">

<!-- Making a div keyboard-focusable -->
<div tabindex="0" role="button"
     onkeydown="if(event.key==='Enter'||event.key===' '){this.click()}">
  Custom button
</div>

Skip link to bypass navigation

A visually hidden skip link becomes visible on focus, letting keyboard users bypass repeated navigation fast.

Example · html
<a href="#main" class="skip-link"
   style="position:absolute;left:-999px"
   onfocus="this.style.left='4px'">
  Skip to main content
</a>

<header>...<nav>...</nav></header>
<main id="main">
  <h1>Page Content</h1>
</main>

Focus trap inside a dialog

Trapping Tab and Shift+Tab inside an open modal prevents focus from escaping to background content.

Example · html
<dialog id="dlg">
  <h2>Confirm</h2>
  <button id="cancel">Cancel</button>
  <button id="ok">OK</button>
</dialog>

<script>
  const dlg = document.getElementById("dlg");
  dlg.showModal();
  dlg.addEventListener("keydown", e => {
    if (e.key !== "Tab") return;
    const focusable = [...dlg.querySelectorAll("button")];
    const first = focusable[0], last = focusable.at(-1);
    if (e.shiftKey && document.activeElement === first) {
      e.preventDefault(); last.focus();
    } else if (!e.shiftKey && document.activeElement === last) {
      e.preventDefault(); first.focus();
    }
  });
</script>

Discussion

  • Be the first to comment on this lesson.
Keyboard Navigation and Focus — HTML | SoundsCode