
I remember building fast, modern interfaces and realizing that performance alone wasn’t enough; accessibility was the missing layer. A website can look flawless and still exclude users who rely on screen readers, keyboard navigation, or assistive technology.
Web accessibility is not an optional enhancement; it is a foundational engineering decision. It ensures that semantic structure, ARIA usage, and interaction patterns work consistently across browsers, devices, and assistive tools.
In this guide, I’ll break down how to apply semantic HTML correctly, when ARIA is appropriate, and how to implement accessibility properly in React, without overengineering or adding unnecessary complexity.

Semantic HTML means using HTML tags that describe the content's meaning, not just its appearance. These tags tell browsers and assistive technologies what kind of content they’re interacting with, making your website more meaningful, especially for people using screen readers.
Good for screen readers, SEO, and other tools to understand your layout.
| Tag | Use For | Example |
<header> | Page or section header | Top section of page |
<nav> | Navigation links | Menus or link bars |
<main> | Main content (1 per page) | Article, dashboard, etc. |
<section> | Thematic grouping | Features, Services |
<article> | Self-contained content | Blog post, comment |
<aside> | Secondary content | Sidebars, ads, notes |
<footer> | Page or section footer | Copyright, contact |
<h1> to <h6> | Headings (semantic levels) | Titles and subtitles |
<ul>, <ol>, <li> | Lists | FAQs, features |
<form> | Group of input fields | Sign-up or search |
<label> | Label for form field | Name, Email |
<button> | Clickable actions | Submit, Next |
<a> | Navigation links | Internal or external |
Start with HTML that means something. Don’t use a <div> or <span> just because it works visually use a <button> if it’s clickable, a <nav> if it’s navigation, and so on.
<header>
<nav>
<a href="/">Home</a>
</nav>
</header>
<main>
<section>
<h2>Features</h2>
<p>Explore powerful tools.</p>
</section>
</main>
<footer>
<p>© 2025</p>
</footer>ARIA stands for Accessible Rich Internet Applications. It allows you to fill in accessibility gaps when you're building custom UI components that native HTML doesn’t support well (like custom modals, tabs, dropdowns, etc.)
| Aria Attribute | Use case |
aria-label | Label for element with no visible text |
aria-hidden=”true” | Hide from screen readers |
aria-live | Announce dynamic content updates |
aria-expanded | Toggled state (dropdowns, accordions) |
aria-controls | Describes what element is controlled |
role=”dialog” | Custom modals |
role=”tablist”, role=”tab” | Custom tab interfaces |
Example
<button aria-expanded="false" aria-controls="dropdown1">Menu</button>
<ul id="dropdown1" hidden>
<li>Item 1</li>
</ul>ARIA is powerful, but it can cause more harm than good if misused. You should avoid ARIA:
Example of What Not to Do:
<!-- Avoid this -->
<div role="button">Click me</div>Correct Semantic Version:
<button>Click me</button>Sometimes the visible label for an element comes from another part of the page. Use ‘aria-labelledby’ to associate it.
We design and develop web experiences that everyone can use, simple, inclusive, and ADA-compliant.
Example:
<div role="dialog" aria-labelledby="dialog-title">
<h2 id="dialog-title">Subscribe</h2>
<p>Sign up for weekly updates.</p>
</div>This is helpful when you want to add supporting context, like form hints or validation messages.
Example:
<label for="email">Email</label>
<input id="email" aria-describedby="email-hint email-error" />
<p id="email-hint">We'll never share your email.</p>
<p id="email-error" style="color:red;">Email is required.</p>Screen readers will read: “Email. We’ll never share your email. Email is required.”
Sometimes seeing the difference makes it click. Here are the examples of accessible vs. non-accessible code:
<div onclick="submitForm()">Submit</div>No role or semantics
Not keyboard accessible
<button onclick="submitForm()">Submit</button>Screen reader-friendly
Built-in keyboard support
<input type="text" placeholder="Your Name">No label for screen readers
<label for="name">Name</label>
<input type="text" id="name">Proper label association
React developers need to consider a few extra steps:
Use ‘htmlFor’ instead of ‘for’ in labels:
<label htmlFor="email">Email</label>
<input id="email" type="email" />Manage keyboard events on custom components:
<div role="button" tabIndex={0} onKeyDown={handleKeyDown} onClick={handleClick}>
Toggle
</div>Use ‘aria-live’ for dynamically changing content:
<div aria-live="polite">{message}</div>Consider accessible libraries like @radix-ui/react-*, reach-ui, or react-aria.
We design and develop web experiences that everyone can use, simple, inclusive, and ADA-compliant.
While building or auditing your React components, studying a thorough Competitive analysis for UX can also reveal patterns in how others solve similar accessibility challenges, helping you benchmark your approach.
Accessibility decisions directly impact how users interact with your product. When semantic structure is missing, screen readers fail to interpret intent. When keyboard focus is unmanaged, navigation breaks. When ARIA is misused, assistive technology produces misleading output.
Engineering accessible interfaces prevents these structural failures before they affect real users.
"I couldn't find the checkout button on your site. I had to ask a friend to help."
Or someone with color blindness:
"I didn’t realize the red box meant an error. There was no text or icon."
Designing with accessibility in mind makes sure everyone can access what you’ve built.
| Goal | Use |
Basic structure and meaning | Semantic HTML |
Label an element (no text) | aria-label or aria-labelledby |
Add extra instructions | aria-describedby |
Dynamic or custom elements | ARIA roles/attributes |
Hide decorative content | aria-hidden="true" |
This kind of decision-making is similar to how developers weigh LLM fine tuning vs RAG, where each method has its own strengths depending on the problem you’re solving.
Semantic HTML provides built-in meaning, keyboard behavior, and screen reader compatibility, reducing the need for ARIA and improving SEO structure.
ARIA should only be used when native HTML elements cannot provide required behavior, such as custom dialogs, tabs, or dynamic components.
React does not automatically make components accessible. Developers must manage focus states, keyboard events, ARIA attributes, and proper labeling.
aria-labelledby defines the accessible name of an element, while aria-describedby adds supplementary context such as hints or error messages.
Yes. Proper semantic structure improves crawlability, content hierarchy, and search engine understanding.
Accessibility is an engineering discipline, not a finishing step. By prioritizing semantic HTML, applying ARIA only when necessary, and validating interactions with real assistive tools, developers create systems that scale reliably across users and environments.
Inclusive design improves usability, SEO structure, legal compliance, and long-term maintainability. The goal is not complexity, it is clarity. And clarity in structure always improves accessibility. What matters is the intention to include, the willingness to learn, and the small steps taken consistently.