Complete HTML Course Module 10: Advanced: Accessibility in HTML

Keyboard Navigation and Focus in HTML

  • Beginner
  • Lesson 10.3
  • 5 min read
  • 47 of 69 in the course

In short

Many people use the web with a keyboard only. In this lesson you will learn how keyboard navigation and focus work in HTML: tab order, tabindex, skip links and why real buttons matter. How does keyboard navigation work? Open any website and press the Tab key.

Many people use the web with a keyboard only. In this lesson you will learn how keyboard navigation and focus work in HTML: tab order, tabindex, skip links and why real buttons matter.

How does keyboard navigation work?

Open any website and press the Tab key. A box or outline jumps to the first link or button. Press Tab again and it moves to the next one. Shift + Tab moves backwards. Enter follows a link or presses a button, and Space presses buttons and ticks checkboxes. The element that is ready to receive your key presses is said to have focus.

Only some elements can receive focus by default. They are called focusable elements:

  • <a> with an href
  • <button>
  • <input>, <select> and <textarea>
  • <summary> (inside <details>)
  • elements with contenteditable

The tab order is the order in which focus moves. By default it follows the order of elements in your HTML source, top to bottom. That is one more reason to write your HTML in a logical order: if CSS later moves things around visually, the keyboard order still follows the source, and users may see focus jump around in a confusing way.

What does tabindex do? (0, -1, and never positive)

The tabindex attribute changes whether an element can get focus.

ValueWhat it doesWhen to use it
tabindex="0"Adds the element to the normal tab orderRarely, for custom widgets that have no native element
tabindex="-1"Can get focus from JavaScript, but Tab skips itHeadings or panels you move focus to with code
tabindex="1" or moreJumps to the front of the tab orderNever

Positive values break the natural order. An element with tabindex="3" gets focus before everything else on the page, so the user jumps around in a strange way. The tab order becomes very hard to maintain. Always use 0 or -1 only.

<main>
  <h1 id="results-title" tabindex="-1">Search results</h1>
  <p>12 recipes found for "pasta".</p>
</main>

<!-- Bad: positive tabindex creates a confusing order -->
<input type="text" tabindex="2">
<input type="email" tabindex="1">

After a search, your JavaScript can call focus() on the heading so keyboard and screen reader users start reading at the results. Because it uses -1, the heading does not become an extra Tab stop. The second example would focus the email field first, then the text field, the opposite of the reading order.

On most sites the navigation menu comes before the main content. A keyboard user would have to press Tab through every menu link on every page. A skip link fixes this. It is the first link on the page and jumps straight to <main>. It is hidden until it gets focus.

<body>
  <a class="skip-link" href="#main-content">Skip to main content</a>
  <header>
    <nav aria-label="Main">
      <a href="/">Home</a>
      <a href="/courses">Courses</a>
      <a href="/blog">Blog</a>
      <a href="/pricing">Pricing</a>
      <a href="/login">Log in</a>
    </nav>
  </header>
  <main id="main-content" tabindex="-1">
    <h1>Learn to code for free</h1>
  </main>
</body>
.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  left: 1rem;
  top: 1rem;
  background: #ffffff;
  padding: 0.5rem 1rem;
}

Load the page and press Tab once. "Skip to main content" appears in the top left corner. Press Enter and focus jumps to the <main> element, so the next Tab goes to the first link inside the content. The tabindex="-1" on <main> makes the focus move reliable in all browsers. You will learn the CSS part properly in the CSS course.

Why a button beats a div with onclick

Here is a very common problem on real websites:

<!-- Looks like a button, but keyboard users cannot use it -->
<div class="btn" onclick="addToCart()">Add to cart</div>

<!-- Works with mouse, keyboard, touch and screen readers -->
<button type="button" class="btn" onclick="addToCart()">Add to cart</button>

The <div> works with a mouse. But Tab skips it, Enter and Space do nothing, and a screen reader does not say "button". To fix the <div> you would need role="button", tabindex="0" and extra JavaScript for the Enter and Space keys. The <button> gives you all of that for free in one word. Use <button> for actions and <a href> for going to another page.

Common mistake: Writing <a href="#" onclick="..."> for an action like "Open menu". Links are for navigation. If nothing new loads, use a <button>.

Focus visibility: never hide the outline

The ring that shows focus is called the focus indicator. Some designers remove it with CSS (outline: none) because they think it looks ugly. This leaves keyboard users lost, like using a computer with an invisible mouse pointer. In the CSS course you will learn the :focus-visible selector, which lets you design a nice focus style that only shows for keyboard users. Until then, leave the browser's default outline alone.

Tip: Test every page you build with the keyboard only. Put your mouse away, press Tab from the top, and check that you can reach and use everything, and always see where you are.

Practice exercise

Build a small page with a skip link, a <nav> with five links, and a <main> with an <h1> and two buttons. Test it with the keyboard only. Then add a fake <div onclick> button and notice that Tab skips it.

Hint: The href of the skip link must match the id of <main>, with a # in front.

Key takeaways

  • Tab, Shift + Tab, Enter and Space are the main keyboard controls.
  • The tab order follows your HTML source order, so keep it logical.
  • Use tabindex="0" or tabindex="-1" only, never a positive number.
  • Add a skip link so keyboard users can jump past the navigation.
  • Use real <button> and <a> elements, and never remove the focus outline.