CodeFrog
Why Use Semantic HTML (Even When Automated Tests Pass)
← Back to CodeFrog

Why Use Semantic HTML (Even When Automated Tests Pass)

Overview

Your accessibility tests might show zero errors, but that doesn’t mean your site is truly accessible. Automated testing tools can only detect a fraction of accessibility issues. If your pages are built with generic <div> and <span> elements propped up with ARIA attributes, you’re missing out on the built-in accessibility, keyboard behavior, and cross-device support that native HTML elements provide for free.

This guide explains why semantic HTML should be your foundation for accessibility — not an afterthought.

The First Rule of ARIA

The W3C’s rules of ARIA use state:

If you can use a native HTML element with the semantics and behavior you require already built in, instead of repurposing an element and adding an ARIA role, state, or property to make it accessible, then do so.

ARIA (Accessible Rich Internet Applications) was designed as a bridge — a way to fill gaps where HTML didn’t yet have the right element. It was never meant to replace semantic HTML. Think of ARIA as a polyfill: useful when nothing else works, but ideally replaced by the real thing as browsers evolve.

Native HTML Elements vs ARIA

Many developers reach for ARIA attributes out of habit when a native HTML element would do the job better. Here’s a reference for common patterns:

Buttons and Controls

<!-- Avoid: ARIA on a generic element -->
<div role="button" tabindex="0" onclick="handleClick()"
     onkeydown="if(event.key==='Enter')handleClick()">
  Save
</div>

<!-- Prefer: Native button -->
<button type="button" onclick="handleClick()">Save</button>

The native <button> gives you keyboard activation (Enter and Space), focus management, form submission support, and proper accessibility-tree mapping — with zero extra code.

Dialogs and Modals

<!-- Avoid: ARIA dialog on a div -->
<div role="dialog" aria-modal="true" aria-labelledby="title">
  <h2 id="title">Confirm</h2>
  <!-- requires custom JS for focus trapping, Escape key, etc. -->
</div>

<!-- Prefer: Native dialog element -->
<dialog id="confirm-dialog">
  <h2>Confirm</h2>
  <p>Are you sure?</p>
  <button onclick="this.closest('dialog').close()">Cancel</button>
  <button>OK</button>
</dialog>

The <dialog> element provides focus trapping, Escape-to-close, and proper modal behavior automatically.

Disclosure Widgets (Expand/Collapse)

<!-- Avoid: Custom disclosure with ARIA -->
<button aria-expanded="false" aria-controls="panel">More info</button>
<div id="panel" hidden>Additional content here.</div>
<!-- requires JS to toggle aria-expanded and hidden -->

<!-- Prefer: Native details/summary -->
<details>
  <summary>More info</summary>
  <p>Additional content here.</p>
</details>

Live Regions and Status Messages

<!-- Avoid: ARIA live region on a div -->
<div role="status" aria-live="polite">Calculation complete: 42</div>

<!-- Prefer: Native output element -->
<output>Calculation complete: 42</output>

The <output> element has an implicit role="status" and is announced by screen readers automatically.

Landmark Regions

<!-- Avoid: ARIA landmarks on divs -->
<div role="navigation">...</div>
<div role="main">...</div>
<div role="banner">...</div>
<div role="contentinfo">...</div>

<!-- Prefer: Native landmark elements -->
<nav>...</nav>
<main>...</main>
<header>...</header>
<footer>...</footer>

Form Validation

<!-- Avoid: ARIA for form states -->
<input type="text" aria-required="true" aria-disabled="false">

<!-- Prefer: Native HTML attributes -->
<input type="text" required>

HTML5 form attributes like required, disabled, pattern, min, max, and type (email, url, tel, date, etc.) provide built-in validation, native error messages, and correct mobile keyboard layouts.

Range Inputs

<!-- Avoid: Custom slider with ARIA -->
<div role="slider" tabindex="0"
     aria-valuemin="0" aria-valuemax="100" aria-valuenow="50">
</div>
<!-- requires extensive JS for drag, keyboard arrows, value display -->

<!-- Prefer: Native range input -->
<input type="range" min="0" max="100" value="50">

Hiding Content

<!-- Avoid: ARIA hidden on a container -->
<div aria-hidden="true">
  <!-- still possible to Tab into children -->
</div>

<!-- Prefer: inert attribute -->
<div inert>
  <!-- truly non-interactive: can't be tabbed, clicked, or read -->
</div>

The inert attribute prevents all interaction — keyboard, mouse, and assistive technology — in a single declaration.

Complete Reference Table

Purpose Use Instead of ARIA Native HTML
Clickable actions <div role="button"> <button> or <input type="button">
Modal dialogs <div role="dialog"> <dialog>
Expand/collapse aria-expanded + JS <details> and <summary>
Status messages <div aria-live="polite"> <output>
Navigation <div role="navigation"> <nav>
Main content <div role="main"> <main>
Page header <div role="banner"> <header>
Page footer <div role="contentinfo"> <footer>
Sidebar <div role="complementary"> <aside>
Required fields aria-required="true" required attribute
Disabled controls aria-disabled="true" disabled attribute
Input validation aria-invalid + JS pattern, type, min, max
Sliders <div role="slider"> <input type="range">
Hidden content aria-hidden="true" inert attribute
Date input <div role="group"> + JS <input type="date">

Why Native Elements Are Better

Built-in Keyboard Behavior

Native elements come with keyboard support that you’d otherwise have to code yourself. A <button> responds to Enter and Space. A <select> supports arrow-key navigation. A <details> toggles with Enter. With ARIA on a <div>, you’re responsible for re-implementing all of this — and getting it right across browsers and devices.

Cross-Device Compatibility

Native elements adapt to the device automatically. An <input type="date"> shows a date picker on mobile and a dropdown on desktop. A <select> renders as a native picker on iOS and Android. ARIA-enhanced custom widgets don’t get this behavior — they display the same way everywhere, often with poor mobile usability.

Screen Reader Reliability

Screen readers on iOS (VoiceOver) and Android (TalkBack) sometimes ignore or misinterpret ARIA hints, but they consistently recognize native element roles. Using native HTML means your interface works reliably across all screen readers, not just desktop ones.

Smaller Code, Better Performance

ARIA-heavy markup means more attributes for the browser to process and more JavaScript for keyboard handling, focus management, and state updates. Native elements eliminate this overhead. Less code means faster parsing, rendering, and smaller HTML payloads.

Resilience to Specification Changes

The ARIA specification evolves over time. Roles or attributes you rely on today could be deprecated or redefined. Native HTML elements, by contrast, have decades of backwards compatibility. Using them insulates your code from ARIA specification churn.

Automatic Internationalization

Native form controls like <input type="date"> automatically respect the user’s locale settings for date formatting, number separators, and language. Custom ARIA widgets need manual internationalization work.

Reduced Maintenance

Every ARIA attribute you add is a potential point of failure. If aria-expanded gets out of sync with the actual UI state, screen reader users get incorrect information. Native elements keep their state in sync automatically because the browser manages it.

When ARIA Is Still Justified

ARIA remains valuable in specific situations:

The key principle: start with semantic HTML, then layer ARIA only where native elements fall short.

How CodeFrog Helps

CodeFrog’s accessibility testing detects many common issues, but automated tests can’t tell you whether you’re using the best element for the job — only whether the current markup has detectable violations. A <div role="button"> with proper ARIA might pass automated checks, but a native <button> would be better in every way.

Use CodeFrog’s testing as your starting point:

  1. Run accessibility scans to catch detectable violations
  2. Review your markup and replace ARIA-heavy patterns with native elements where possible
  3. Test with a screen reader (VoiceOver, NVDA) to verify the experience
  4. Test with keyboard only to confirm all interactions work without a mouse

Going beyond automated test results to use semantic HTML proactively is what separates a truly accessible site from one that merely passes a checklist.

External Resources


Automated tests are a starting point, not the finish line. Semantic HTML is the foundation of true accessibility — use native elements first, and reach for ARIA only when HTML falls short.