Semantic HTML as the Accessibility Foundation: Why Structure Comes Before ARIA
Semantic HTML as the Accessibility Foundation: Why Structure Comes Before ARIA
The first rule of ARIA is: do not use ARIA if you can use a native HTML element or attribute instead. Native HTML elements carry built-in accessibility — keyboard interactivity, screen reader announcements, and predictable behaviors — that ARIA can only approximate. Yet developers routinely reach for <div> with role="button" instead of <button>, or <span> with role="link" instead of <a>. Understanding what semantic HTML provides for free is the foundation of every accessible interface.
What “Semantic” Means for Accessibility
Semantic HTML means using elements for their intended purpose. Each HTML element carries an implicit role, state, and behavior that assistive technologies depend on:
| HTML Element | Implicit Role | Built-in Behavior |
|---|---|---|
<button> | button | Focusable, activated by Enter and Space, click event |
<a href="..."> | link | Focusable, activated by Enter, navigates |
<input type="checkbox"> | checkbox | Focusable, toggled by Space, checked state |
<select> | listbox | Focusable, arrow key navigation, option selection |
<h1>–<h6> | heading (level 1-6) | Heading navigation in screen readers |
<nav> | navigation | Landmark navigation in screen readers |
<main> | main | Landmark navigation, skip target |
<table> | table | Cell-by-cell navigation, header association |
When you use a <div> or <span>, the element has no role, no keyboard behavior, and no screen reader announcement. You must manually recreate everything the native element provides — and you will almost certainly miss something.
The Cost of Fake Semantics
The <div> Button
<!-- Inaccessible -->
<div class="btn" onclick="handleClick()">Submit</div>
What is missing:
- No keyboard focus: Users cannot Tab to it. Fix: add
tabindex="0". - No keyboard activation: Enter and Space do nothing. Fix: add a
keydownhandler for Enter and Space. - No role: Screen readers announce “Submit” with no context. Fix: add
role="button". - No form participation: Will not submit a form on Enter. Fix: complex JavaScript workaround.
- No disabled state:
disabledattribute does nothing on divs. Fix:aria-disabled="true"plus CSS plus preventing click events.
<!-- "Fixed" but fragile -->
<div class="btn" role="button" tabindex="0" onclick="handleClick()" onkeydown="if(event.key==='Enter'||event.key===' '){handleClick()}">
Submit
</div>
Compare with the native element:
<!-- Accessible by default -->
<button onclick="handleClick()">Submit</button>
One line. Keyboard focus, activation, role, form participation, and disabled state all work natively.
The <span> Link
<!-- Inaccessible -->
<span class="link" onclick="navigate('/about')">About Us</span>
Missing: focus, keyboard activation, link role, right-click context menu, middle-click new tab, link styling, link discovery by screen readers. The native alternative:
<a href="/about">About Us</a>
The <div> Checkbox
Custom-styled checkboxes built from <div> elements lack checked state management, label association, form participation, and the Space key toggle. The accessible approach is to style the native <input type="checkbox"> using modern CSS techniques that hide the native checkbox visually while preserving its functionality.
Semantic Elements for Page Structure
Landmarks
HTML5 introduced semantic sectioning elements that create landmark regions automatically:
<header>(top-level) maps tobanner<nav>maps tonavigation<main>maps tomain<aside>maps tocomplementary<footer>(top-level) maps tocontentinfo<section>with an accessible name maps toregion
Screen reader users navigate by landmark to jump between page sections. Without these elements, the page is an undifferentiated block of content.
Headings
Heading elements (<h1> through <h6>) create the page outline. Screen reader users navigate by heading more than any other method — pressing H in NVDA jumps to the next heading, and the elements list shows all headings as a navigable tree.
A page with no heading elements forces screen reader users to read linearly from top to bottom. A page with a clear heading hierarchy lets users jump directly to the section they need.
Lists
<ul>, <ol>, and <dl> communicate grouping and relationship:
- Screen readers announce “list, 5 items” when entering a list, giving users an immediate sense of scope
- Ordered lists communicate sequence
- Description lists (
<dl>,<dt>,<dd>) communicate term-definition pairs
A set of links in a <div> with <br> separators gives screen readers no grouping context. The same links in a <ul> are immediately understood as a related set.
Forms
Native form elements provide the most significant accessibility value:
Label Association
<label for="email">Email address</label>
<input type="email" id="email" required />
The <label> element creates a programmatic association — screen readers announce the label when the input receives focus. Clicking the label focuses the input, expanding the interactive target. No ARIA is needed.
Input Types
HTML5 input types (email, tel, url, number, date, search) provide:
- Appropriate virtual keyboard on mobile devices
- Built-in validation patterns
- Screen reader type announcements (“email edit text”)
- Browser auto-fill support
Required and Validation
The required attribute communicates to screen readers that a field must be filled. The pattern attribute provides client-side validation. The :invalid and :valid CSS pseudo-classes enable styling. These are free with native HTML.
Fieldset and Legend
Group related form controls with <fieldset> and <legend>:
<fieldset>
<legend>Shipping Address</legend>
<label for="street">Street</label>
<input id="street" type="text" />
<!-- More address fields -->
</fieldset>
Screen readers announce the legend as context for each field within the fieldset: “Shipping Address, Street, edit text.” This grouping is essential for complex forms like multi-step wizards.
When ARIA Is Necessary
ARIA is necessary when no native HTML element provides the needed semantics:
- Tabs: No native tab element exists, so
role="tablist",role="tab", androle="tabpanel"are needed - Combobox: The search autocomplete pattern requires
role="combobox"because no native element combines text input with suggestion list - Tree view: Hierarchical lists with expand/collapse need
role="tree"androle="treeitem" - Live regions:
aria-livecommunicates dynamic content changes that no native element handles - States:
aria-expanded,aria-selected,aria-pressed, andaria-currentcommunicate widget states that have no HTML equivalent
The pattern should be: HTML first, ARIA to fill gaps, JavaScript to manage behavior. If you find yourself adding more than one or two ARIA attributes to an element, reconsider whether a different HTML element would serve better.
Common Anti-Patterns
- Redundant ARIA:
<button role="button">adds nothing. The<button>already has the button role. Remove the redundant attribute. - ARIA without behavior:
role="button"on a<div>without keyboard handling creates a worse experience than no ARIA — the screen reader announces “button” but the element does not behave like one. - Overriding native semantics:
<h2 role="button">strips the heading role and replaces it with button. Use a button inside the heading:<h2><button>Section Title</button></h2>. aria-labelon non-interactive elements: Screen readers may ignorearia-labelon<div>and<span>elements without a role. Usearia-labelon interactive elements and landmarks.
Testing Semantic HTML
- Disable CSS: View the page without styles. The content should be readable and logically ordered based on the HTML structure alone.
- Heading outline: Use the HeadingsMap browser extension to view the heading hierarchy. It should form a logical table of contents.
- Landmark list: Use a screen reader’s landmark list. All major page sections should appear.
- Tab through the page: Every interactive element should receive focus in a logical order without any custom
tabindexvalues.
Key Takeaways
Semantic HTML is not a best practice — it is the prerequisite. Native HTML elements provide keyboard interactivity, screen reader announcements, form participation, and predictable behaviors that ARIA can only partially replicate. Use <button> for buttons, <a> for links, <input> for form fields, landmarks for page structure, and headings for content hierarchy. Reach for ARIA only when no native element serves the purpose. The most accessible codebase is often the one with the least ARIA — because it uses HTML as intended.
Sources
- W3C WAI: Using ARIA — The five rules of ARIA, emphasizing native HTML first.
- MDN Web Docs: HTML Elements Reference — Comprehensive reference for semantic HTML elements.
- WebAIM: Semantic Structure — Guidance on using HTML structure for accessibility.
- The A11Y Project: Use Semantic HTML — Community guide to semantic HTML for accessibility.