Semantic HTML and keyboard access

Why the right element is most of accessibility, how tab order really works, and the fixes that come free with native controls.

Use the element that already does the job

A native element arrives with behaviour, keyboard support, focus handling and a role that assistive technology understands. A div with a click handler has none of that, and every fix you add afterwards is a partial reimplementation of what the browser already had.

<!-- does not focus, does not respond to Enter or Space, no role -->
<div class="btn" onclick="save()">Save</div>

<!-- focusable, keyboard-operable, announced as a button, disabled state is free -->
<button type="button" onclick="save()">Save</button>

<!-- a link navigates; a button acts. Do not swap them -->
<a href="/orders">View orders</a>

<!-- placeholder is not a label -->
<input type="email" placeholder="Email address">

<!-- explicit label, associated by id -->
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="email" required>
Instead ofUseYou get free
div + clickbuttonFocus, Enter/Space, role, disabled
div + onclicka hrefRight-click, middle-click, status bar, screen-reader link list
div wrappernav, main, header, footerLandmarks for quick navigation
div + role=listul / liItem count announced, correct semantics
label styled as textlabel for or wrapping labelClick-to-focus, correct announcement

Keyboard access and tab order

The default tab order follows the document order. Adding a positive tabindex removes an element from that flow and reorders everything, which is why it is almost always a mistake - if the order feels wrong, move the markup instead.

<!-- avoid: positive tabindex reorders the whole page -->
<input tabindex="1">

<!-- include something that is not focusable by default -->
<div tabindex="0" role="region" aria-label="Terminal output">...</div>

<!-- keep it in the tree, but not in the tab order -->
<button tabindex="-1">Close</button>

<!-- focus target for skip links needs tabindex="-1" to accept focus -->
<h2 id="section-2" tabindex="-1">Section 2</h2>
  • Press Tab through the page and confirm the focus indicator is always visible and never clipped by overflow.
  • Everything reachable by mouse must be reachable by keyboard: menus, carousels, date pickers, custom selects.
  • Escape must close dialogs and menus; arrow keys move within a group, Tab moves between groups.
  • Do not disable the outline with outline: none unless you replace it with something clearly visible.
  • Touch targets should be at least 24 by 24 CSS pixels, with 44 by 44 as a comfortable target.
/* a focus ring that survives on both light and dark backgrounds */
:focus-visible {
  outline: 3px solid #1a5fff;
  outline-offset: 2px;
  border-radius: 2px;
}

/* never remove the indicator without a replacement */
/* button:focus { outline: none; }  <- do not do this */

Structure that assistive tech can navigate

A screen reader user often jumps by heading, landmark or link list rather than reading linearly. Consistent headings and landmarks turn a 2000-word page into something navigable in seconds.

  • One h1 describing the page, then a logical h2/h3 outline with no skipped levels.
  • Wrap the page in header, nav, main, footer; label multiple navs with aria-label.
  • Give tables a caption and use th with scope for headers.
  • Wrap a group of related inputs in fieldset with a legend.
  • Keep the page lang correct, and mark inline language changes.
⚠️
Do not fix structure by adding ARIA to a bad layout. Announcing a div as a button does not make it work like one, and incorrect ARIA is worse than none - it misleads the people it was meant to help.

FAQ

Can I use a div if I add role and keydown handling?
You can make it work, but you must then reproduce focus behaviour, Enter and Space handling, disabled state and form participation. Use the native element unless you are building a genuinely novel control.
What is the difference between focus and focus-visible?
:focus matches whenever an element has focus, including mouse clicks. :focus-visible matches when the browser judges the focus should be shown, which is almost always for keyboard navigation - that is the one to style.

WCAG in plain language Focus management, ARIA and testing

Last refreshed 2026-09-18.