Keyboard, Focus, and ARIA
Introduction
Many users navigate pages without a mouse. They use the keyboard, switch devices, voice control, or assistive technology. This chapter explains focus order, focusable elements, skip links, tabindex, and basic ARIA attributes such as aria-label, aria-labelledby, aria-describedby, aria-expanded, and aria-hidden.
Prerequisites
- Accessibility basics
- Accessible forms
- A page with links, buttons, and form controls to test
What Focus Means
Focus is the current element ready to receive keyboard input.
Common focusable elements:
<a href="..."><button><input><select><textarea><summary>- elements with
tabindex="0"(use carefully)
Try this:
- Open a page.
- Press
Tab. - Watch which element gets focus.
- Press
Enteror Space depending on the control.
If you cannot see where focus is, the page is hard to use.
Natural Tab Order
The browser follows DOM order:
Tab order:
- skip link
- Home
- Docs
- Open example
Keep DOM order logical. Do not use CSS layout tricks that make visual order wildly different from keyboard order.
Tip
Test Without a Mouse
Before publishing, put your mouse aside. Can you reach every interactive element, understand where you are, and activate controls?
Do Not Remove Focus Styles
Bad CSS pattern:
:focus {
outline: none;
}If you remove the default outline, replace it with a visible alternative:
:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 3px;
}HTML provides focusable controls; CSS must make focus visible.
Skip Links
Skip links help keyboard users bypass repeated navigation:
CSS often hides skip links until focused:
.skip-link {
position: absolute;
left: -999px;
}
.skip-link:focus {
left: 1rem;
top: 1rem;
}The link must become visible when focused.
tabindex
tabindex changes focus behavior.
| Value | Meaning |
|---|---|
0 | Add to natural tab order |
-1 | Programmatically focusable, not reachable by Tab |
| Positive number | Custom order — avoid |
tabindex="0"
Use rarely for custom interactive elements or regions that truly need focus:
<div tabindex="0">
Keyboard-focusable region
</div>But if the element is interactive, prefer a native element:
<button type="button">Open panel</button>tabindex="-1"
Useful for moving focus after navigation:
<main id="main-content" tabindex="-1">
<h1>Dashboard</h1>
</main>JavaScript can focus it:
document.querySelector("#main-content").focus();Avoid positive tabindex
<!-- Avoid -->
<input tabindex="3">
<input tabindex="1">Positive values create a custom tab order that is hard to maintain and often confusing.
ARIA: What It Is
ARIA (Accessible Rich Internet Applications) adds accessibility information when native HTML cannot express a custom pattern.
Important rule:
Native HTML first. ARIA only when needed.Good:
<button type="button">Save</button>Avoid:
<div role="button" tabindex="0">Save</div>The fake button still needs keyboard event handling and testing. The real button already works.
aria-label
Provides an accessible name when visible text is missing or insufficient:
<button type="button" aria-label="Close dialog">×</button>Use it for icon-only buttons. If visible text exists and is good, do not duplicate it.
Bad:
<button aria-label="Submit form">Submit form</button>The visible text already names the button.
aria-labelledby
Uses another element as the accessible name:
<section aria-labelledby="pricing-heading">
<h2 id="pricing-heading">Pricing</h2>
<p>Plans for every team.</p>
</section>Useful when the label is already visible on the page.
aria-describedby
Adds extra description:
<label for="password">Password</label>
<input id="password" name="password" type="password" aria-describedby="password-help">
<p id="password-help">Use at least 8 characters.</p>The label names the control. The description adds details.
aria-expanded
Indicates whether a collapsible region is open:
<button
type="button"
aria-expanded="false"
aria-controls="menu"
>
Menu
</button>
<nav id="menu" hidden>
<a href="index.html">Home</a>
<a href="about.html">About</a>
</nav>JavaScript must update both the visual state and the ARIA state:
button.setAttribute("aria-expanded", "true");
menu.hidden = false;If ARIA says open while the UI is closed, assistive technology receives wrong information.
aria-hidden
Hides content from the accessibility tree:
<span aria-hidden="true">★</span>
<span>Featured</span>Use for decorative icons when nearby text already communicates meaning.
Do not hide focusable or meaningful content:
<!-- Bad: focusable link hidden from screen readers -->
<a href="checkout.html" aria-hidden="true">Checkout</a>If something is interactive or important, it should not be aria-hidden.
Warning
ARIA Must Match Reality
ARIA attributes do not change visual behavior by themselves. If you use aria-expanded, aria-hidden, or roles, your JavaScript and CSS must keep the visual and accessibility states synchronized.
Accessible Names: Quick Examples
| Element | Accessible name source |
|---|---|
<button>Save</button> | Button text |
<img alt="Company logo"> | alt text |
<input> with <label> | Associated label |
| Icon button | aria-label |
| Section | aria-labelledby heading |
When in doubt, inspect the element in DevTools Accessibility pane.
Keyboard Checklist
For each page:
- Can you Tab to every interactive element?
- Is focus visible?
- Does tab order match visual/logical order?
- Do links activate with Enter?
- Do buttons activate with Enter/Space?
- Can you escape or close custom dialogs/menus?
- Does focus return to a sensible place after closing a modal?
- Are hidden elements removed from the tab order?
Mini Exercise
Improve a simple navigation page:
- Add a skip link to
<main id="main-content">. - Tab through the page and note the order.
- Add a visible focus style with CSS.
- Add
aria-current="page"to the active nav link. - Create one icon-only button with
aria-label.
FAQ
Should I add role="button" to <button>?
No. <button> already has the correct role.
Is tabindex="0" bad?
Not always, but native focusable elements are better for interactive controls. Use tabindex="0" only when you know why a non-native element must receive focus.
Can I hide text visually but keep it for screen readers?
Yes, with a tested visually-hidden CSS utility. Do not use display: none or aria-hidden="true" if screen readers need it.
Does ARIA change behavior?
No. ARIA changes accessibility information. It does not add keyboard handling, open menus, or manage focus.
What comes next?
HTML SEO basics — titles, meta descriptions, headings, semantic content, images, and links for search engines.