Blogs/Technology

Web Accessibility with HTML and React: Developer Guide

Written byAmarnath Barpanda
Aug 4, 2026
14 Min Read
Web Accessibility with HTML and React: Developer Guide Hero
Too Long? Read This First

- Begin with semantic HTML. Native elements provide meaning and, for interactive controls, expected browser behaviour.
- Use real buttons for actions and links for navigation.
- Associate every form control with a persistent, programmatic label.
- Treat placeholder text as an example or hint, not as the field’s only label.
- Use ARIA to communicate information that HTML cannot express—not to replace suitable HTML.
- Remember that ARIA changes accessibility semantics but does not automatically add keyboard behaviour.
- Ensure every interactive feature works with a keyboard and has a visible focus indicator.
- Manage focus when dialogs open, routes change, elements disappear, or errors require attention.
- Announce important dynamic updates without overwhelming screen-reader users.
- Combine automated tools with keyboard testing, screen-reader testing, zoom, and real-user evaluation.
- The Web Content Accessibility Guidelines 2.2 provide the current W3C accessibility standard. They organise accessibility under four principles: content must be perceivable, operable, understandable, and robust.

A website can load quickly, pass functional tests, and look polished while remaining difficult or impossible for some people to use. A checkout flow may depend entirely on a mouse. A form may display errors visually without announcing them to a screen reader. A modal may open without moving keyboard focus, leaving the user interacting with content hidden behind it.

These are not cosmetic defects. They prevent users from completing real tasks.

Web accessibility means designing and developing websites and applications so people with different visual, auditory, motor, speech, and cognitive abilities can perceive, understand, navigate, and interact with them.

For developers, much of this work begins with three decisions:

  • Use native HTML elements for their intended purpose.
  • Add ARIA only when native HTML cannot express the required semantics.
  • Test the resulting behaviour with keyboards and assistive technologies.

This guide explains how to apply those principles in HTML and React, where common accessibility mistakes appear, and how to include accessibility throughout development rather than postponing it until an audit.

What Is Semantic HTML?

Semantic HTML means selecting elements according to the role of the content rather than its visual appearance.

A <nav> identifies a navigation region. A <button> represents an action. A <label> names a form control. Headings communicate the hierarchy of the page. These elements help browsers and assistive technologies construct a meaningful representation of the interface.

Semantic HTML benefits more than screen-reader users. It can improve keyboard interaction, browser behaviour, maintainability, automated testing, reader modes, and search-engine understanding.

However, semantic markup alone does not make an entire interface accessible. Correct HTML must be combined with readable content, sufficient contrast, logical focus order, clear form feedback, alternative text, responsive layouts, and accessible interaction design.

Common Semantic Elements and When to Use Them

ElementUse it forTypical example
<header>Introductory content for a page or sectionSite header or article introduction
<nav>A major collection of navigation linksPrimary menu or section navigation
<main>The page’s primary contentArticle, dashboard, or product view
<section>A related thematic group, normally with a headingFeatures or services section
<article>Self-contained, independently meaningful contentBlog post, comment, or news item
<aside>Complementary rather than primary contentSidebar, related links, or note
<footer>Closing information for a page or sectionCopyright, author, or contact details
<h1><h6>Hierarchical headingsPage and subsection headings
<ul>, <ol>, <li>Actual lists of related itemsSteps, features, or grouped links
<form>A collection of controls for submitting informationRegistration or search
<label>A visible name for a form controlEmail address or destination
<button>An action on the current interfaceSubmit, save, open, or close
<a>Navigation to another location or resourceInternal route or external page
<header>
Use it for
Introductory content for a page or section
Typical example
Site header or article introduction
1 of 13

An element should not be selected merely because its default appearance is convenient. CSS can change presentation; HTML should preserve meaning.

Use Semantic HTML First

Begin with the native element that most closely matches the intended behaviour. Use a button for an action, a link for navigation, and a heading for a heading.

This reduces the amount of accessibility behaviour developers must recreate manually.

Native interactive elements can provide:

  • Keyboard focus
  • Expected keyboard activation
  • Roles exposed to assistive technology
  • Form behaviour
  • Disabled states
  • Browser and operating-system conventions
  • More dependable compatibility across devices

Structural semantic elements do not all introduce keyboard behaviour, but they give the page meaningful regions and relationships.

Consider this page structure:

<header>
  <nav>
    <a href="/">Home</a>
  </nav>
</header>

<main>
  <section>
    <h2>Features</h2>
    <p>Explore powerful tools.</p>
  </section>
</main>

<footer>
  <p>&copy; 2025</p>
</footer>

The markup communicates that the page contains navigation, primary content, a features section, and a footer. A screen-reader user can navigate through these regions and headings rather than hearing an undifferentiated stream of content.

The heading structure must still be logical. Developers should not select heading levels according to font size or skip levels merely to obtain a particular visual style.

A common accessibility problem is using links and buttons according to appearance instead of behaviour.

Use a link when activating the control moves the user to another URL, route, page section, or downloadable resource. Use a button when it performs an action such as submitting a form, opening a dialog, expanding content, or changing the current interface.

This distinction helps keyboard and screen-reader users predict what activation will do. It also produces appropriate browser behaviour. Links can be copied, opened in another tab, and expose destinations. Buttons participate naturally in forms and support expected activation keys.

Styling a link to look like a button does not change its underlying purpose. The semantic element should follow the action.

Use ARIA When HTML Alone Is Not Enough

ARIA stands for Accessible Rich Internet Applications. It provides roles, states, and properties that can communicate information not represented by ordinary HTML.

ARIA becomes relevant in custom components such as tabs, dialogs, comboboxes, tree views, and live status regions. It can communicate that an accordion is expanded, identify the element controlled by a button, or announce an important asynchronous update.

Common attributes and roles include:

ARIA attribute or rolePurpose
aria-labelProvides an accessible name when no suitable visible label exists
aria-labelledbyDerives an accessible name from visible content elsewhere
aria-describedbyAssociates additional instructions, hints, or errors
aria-hidden="true"Removes non-essential content from the accessibility tree
aria-liveAnnounces dynamic updates
aria-expandedCommunicates whether collapsible content is open
aria-controlsIdentifies content controlled by another element
role="dialog"Identifies a custom dialog
role="tablist" and role="tab"Describe a custom tab interface
aria-label
Purpose
Provides an accessible name when no suitable visible label exists
1 of 9

For example:

<button aria-expanded="false" aria-controls="dropdown1">Menu</button>
<ul id="dropdown1" hidden>
  <li>Item 1</li>
</ul>

The button already has native button semantics. aria-expanded adds the component’s current state, while aria-controls identifies the controlled element.

The value of aria-expanded must remain synchronised with the visible state. ARIA that communicates stale or incorrect information can be worse than omitting that information entirely.

When Should Developers Avoid ARIA?

The W3C’s ARIA Authoring Practices Guide states that “No ARIA is better than bad ARIA.” Incorrect roles and attributes can cause assistive technology to communicate an experience that does not match the visible interface.

Avoid adding ARIA when:

  • A suitable native HTML element already exists.
  • The attribute repeats information already supplied by HTML.
  • The component does not implement the keyboard interaction expected for its role.
  • The state cannot be kept synchronised with the interface.
  • The implementation has not been tested with assistive technology.
  • ARIA is being used to compensate for unclear visual or content design.

Consider this example:

<!-- Avoid this -->
<div role="button">Click me</div>

The role tells assistive technology to treat the element as a button, but the role alone does not make the element focusable or add keyboard activation. W3C describes an ARIA role as a promise: assigning role="button" means the developer must also implement the behaviour users expect from a button.

The native version is more dependable:

<button>Click me</button>

The browser supplies button semantics, focusability, keyboard activation, and expected interaction without requiring custom recreation.

Accessible Names and Descriptions

Every interactive control needs a meaningful accessible name. The name answers, “What is this control?” An accessible description provides supporting information such as a hint, requirement, status, or error.

Visible text often supplies the best accessible name because sighted users and screen-reader users receive consistent information.

Use aria-labelledby to reference visible labels

aria-labelledby allows an element to receive its accessible name from one or more elements elsewhere on the page.

<div role="dialog" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Subscribe</h2>
  <p>Sign up for weekly updates.</p>
</div>

The dialog receives the name “Subscribe” from the heading. This makes the visible heading and the programmatic name consistent.

Let’s Build an Accessible Web Together!

We design and develop web experiences that everyone can use, simple, inclusive, and ADA-compliant.

When aria-labelledby references multiple IDs, assistive technologies combine the referenced text in the specified order. It also has high precedence in accessible-name calculation, so developers should use it deliberately. W3C’s accessible-name guidance explains how names and descriptions are calculated.

Use aria-describedby for supporting context

aria-describedby associates a control with supplementary information. It is useful for format requirements, hints, constraints, and validation feedback.

<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>

In this example, the input’s accessible name comes from the label, while the hint and error provide a description.

In a production form, validation messages should normally appear only when relevant. The interface may also need to communicate that the field is invalid, move attention to a useful error summary, or announce a newly displayed error. aria-describedby creates the relationship, but error timing and focus behaviour still require deliberate implementation.

Comparing Inaccessible and Accessible HTML

Small markup decisions can substantially change how a control behaves.

Inaccessible button

<div onclick="submitForm()">Submit</div>

This element responds to a pointer click, but it does not expose button semantics, receive keyboard focus naturally, or support standard button activation.

Accessible button

<button onclick="submitForm()">Submit</button>

The native button communicates its purpose and includes standard keyboard behaviour. It also works more predictably with forms and assistive technology.

Inaccessible form input

<input type="text" placeholder="Your Name">

Placeholder text is not a reliable label. It disappears after the user types, may have insufficient contrast, and can make it difficult to remember what the field requires.

Accessible form input

<label for="name">Name</label>
<input type="text" id="name">

The label remains visible and is programmatically associated with the input. Selecting the label can also move focus to the field.

Accessibility in React Applications

React renders HTML, so the core accessibility rules do not change. Semantic elements, labels, keyboard behaviour, focus order, and ARIA remain the foundation.

The additional challenge is that React applications frequently update the interface without a full page load. Components mount and unmount, dialogs appear, validation messages change, and client-side routing replaces page content. Visual users can often see these changes immediately, while assistive-technology users may need focus movement or an announcement.

Associate labels using htmlFor

React uses htmlFor in JSX to associate a label with a form control:

<label htmlFor="email">Email</label>
<input id="email" type="email" />

The value of htmlFor must match the input’s id. Reusable components should generate or accept stable IDs so multiple instances do not produce duplicate values.

React otherwise uses standard aria-* attribute names. The official React documentation confirms that ARIA attributes retain the same naming format as HTML.

Prefer native controls over custom interactive containers

The following component adds a role, keyboard focus, keyboard handling, and click handling to a div:

<div role="button" tabIndex={0} onKeyDown={handleKeyDown} onClick={handleClick}>
  Toggle
</div>

This illustrates the work required when a non-interactive element is turned into an interactive control. The keyboard handler must reproduce the expected activation behaviour and prevent inconsistent pointer and keyboard outcomes.

Where the control genuinely behaves like a button, a native button remains the stronger starting point. Custom roles are justified only when native HTML cannot express the intended interaction.

Announce important dynamic updates

React applications often change text without moving focus. A screen-reader user may not know that a save completed, a search returned no results, or an asynchronous request failed.

An ARIA live region can announce updates:

<div aria-live="polite">{message}</div>

A polite live region waits for the screen reader’s current announcement to finish. It is suitable for useful but non-urgent status updates.

Live regions should be used selectively. Repeated announcements for loading percentages, keystrokes, or minor visual changes can overwhelm the user. Urgent messages may require different treatment, but urgency should not be assigned merely to make an announcement occur sooner.

Keyboard Accessibility

A keyboard-accessible interface allows users to reach and operate all interactive functionality without a mouse.

Developers should verify that:

  • Interactive elements appear in a logical focus order.
  • Focus is always visible.
  • Buttons activate through expected keyboard input.
  • Links activate and expose destinations correctly.
  • Menus, tabs, dialogs, and other custom widgets follow established keyboard patterns.
  • Keyboard focus does not become trapped unintentionally.
  • Users can dismiss overlays without using a pointer.
  • Sticky headers and overlays do not obscure the focused element.
  • Drag-based interactions have an alternative.

Avoid adding positive tabIndex values to force a custom focus order. The DOM order should normally create the correct reading and keyboard sequence. CSS that substantially rearranges visible content can create a mismatch between what users see and the order assistive technology encounters.

WCAG 2.2 adds requirements relating to unobscured focus, target size, and alternatives to dragging, making these behaviours especially important in current interfaces.

Focus Management in React

Focus management becomes necessary when an interface changes in a way that would otherwise disorient a keyboard or screen-reader user.

1. Dialogs

When a dialog opens, focus should move into it. Keyboard focus should remain within the active modal dialog, and closing it should normally return focus to the control that opened it.

Adding role="dialog" and a label communicates the dialog’s semantics, but ARIA does not implement focus movement, dismissal, background inertness, or focus restoration.

2. Client-side routing

A single-page React application can replace most of the page without triggering the browser behaviour associated with a traditional navigation. After a route change, focus may remain on the link or button from the previous page.

The application should provide an intentional destination for focus or otherwise communicate that navigation completed. The correct approach depends on the layout and router, but the user should not have to discover the new page from an unexplained position.

3. Removed and disabled content

If the focused component is removed, focus may fall back to the document body. After deleting an item, closing a menu, or completing a step, move focus to a nearby logical location when necessary.

Do not move focus for every visual update. Unexpected focus movement can be as disruptive as missing focus management.

Accessible Forms and Validation

Accessible forms require more than labels.

Each field should have:

  • A persistent label
  • Clear instructions
  • Programmatic grouping where related controls belong together
  • A communicated required state
  • Error identification in text
  • A connection between the field and its error
  • A visible focus state
  • Sufficiently large interaction targets
  • Validation that does not rely on colour alone

When submission fails, users need to know that errors exist, which fields are affected, and how to correct them. For long forms, an error summary can provide an overview and links to invalid fields.

Do not clear valid user input after an error. Do not require the user to re-enter information unnecessarily. WCAG 2.2 specifically addresses redundant entry and accessible authentication.

Colour, Images, Motion, and Zoom

Semantic HTML and ARIA cover only part of web accessibility.

1. Colour

Do not use colour as the only way to communicate meaning. A red border alone may not tell a colour-blind user that a field contains an error. Include text, icons with accessible meaning, or another distinguishable indicator.

Text and meaningful interface elements also need sufficient contrast against their backgrounds.

2. Images

Informative images need alternative text that conveys their purpose in context. Decorative images should not create unnecessary screen-reader output. Complex charts may require an adjacent explanation or an accessible data representation rather than an unusually long alt attribute.

Let’s Build an Accessible Web Together!

We design and develop web experiences that everyone can use, simple, inclusive, and ADA-compliant.

3. Motion

Respect reduced-motion preferences and avoid unnecessary movement that could cause discomfort. Autoplaying animation should not interfere with reading or operation.

4. Zoom and reflow

Pages should remain usable when users enlarge text or zoom the browser. Content should not overlap, disappear, become clipped, or require unnecessary two-dimensional scrolling.

Responsive design is not automatically accessible. It must also accommodate magnification, text spacing changes, and browser-level overrides.

Choosing Between HTML and ARIA

RequirementPreferred approach
Page structure and landmarksSemantic HTML
Standard actionNative <button>
NavigationNative <a> with a valid destination
Visible form labelNative <label>
Name derived from existing visible contentaria-labelledby
Additional instructions or error contextaria-describedby
Dynamic status messageA carefully selected live region
Custom widget without a native equivalentARIA role, state, keyboard model, and focus behaviour
Decorative contentAppropriate native treatment or aria-hidden="true" when necessary
Page structure and landmarks
Preferred approach
Semantic HTML
1 of 9

The decision is not “HTML or ARIA” in every case. Strong implementations often use semantic HTML as the foundation and add a small amount of ARIA to communicate a state or relationship that HTML does not expose.

How to Test Accessibility in HTML and React

Accessibility cannot be verified through code review alone.

1. Begin with keyboard testing

Disconnect the mouse and complete the primary journeys using Tab, Shift+Tab, Enter, Space, arrow keys, and Escape where appropriate.

Check whether every control is reachable, focus is visible, the order makes sense, and no component traps the user.

2. Inspect the accessibility tree

Browser developer tools can show the computed role, name, description, and state of an element. This helps developers identify missing names, duplicate labels, incorrect roles, and unexpected ARIA precedence.

3. Use automated checks

Automated tools can detect problems such as missing labels, duplicate IDs, invalid ARIA, certain contrast failures, and some structural mistakes. They can run during development, component testing, and CI.

However, automation cannot determine whether focus moves appropriately, alternative text is meaningful, instructions are understandable, or a complete journey is usable.

4. Test with screen readers

At minimum, test critical journeys with representative browser and screen-reader combinations. Listen for control names, roles, states, headings, landmarks, errors, dynamic messages, and route changes.

Do not test only whether the screen reader “reads the page.” Test whether a user can understand and complete the task efficiently.

5. Include people with disabilities

Standards and tools are essential, but user evaluation identifies problems that technical checks may miss. Real users can reveal confusing interaction models, excessive announcements, unclear instructions, and workflows that technically pass criteria but remain difficult to use.

How Users Experience Inaccessible Interfaces

Missing accessibility changes the actual product experience.

Without semantic structure, a screen-reader user may hear a long sequence of text without useful headings or landmarks. Without a programmatic label, an input may be announced only as “edit text.” Without focus management, a modal can appear visually while keyboard focus remains behind it.

A keyboard user may encounter a clickable element that cannot receive focus. A user with low vision may lose the focused control behind a sticky header. A colour-blind user may see an error state without being able to distinguish it. A user with a motor disability may be unable to complete an interaction that depends entirely on dragging.

Accessibility work turns these hidden failures into explicit engineering requirements.

Building Accessibility into the Development Workflow

Accessibility is easier to maintain when it is included at every stage.

During design, define keyboard behaviour, focus movement, error states, accessible names, responsive layouts, and reduced-motion behaviour.

During development, start with semantic elements, use established accessible components where appropriate, and test individual components before assembling complete pages.

During review, include accessibility acceptance criteria instead of relying only on visual comparison.

During CI, run automated rules and component tests, but do not treat a passing scan as proof of conformance.

Before release, manually test the most important journeys with a keyboard, zoom, high-contrast or forced-colour settings where relevant, and representative assistive technologies.

After release, provide a way for users to report accessibility barriers and include those reports in the normal defect process.

Frequently Asked Questions

Why is semantic HTML important for accessibility?

Semantic HTML communicates structure and purpose to browsers and assistive technologies. Native interactive elements also provide established keyboard behaviour, reducing the amount of accessibility logic developers must recreate.

When should ARIA be used instead of semantic HTML?

Use ARIA when HTML cannot express a necessary role, state, property, or relationship. ARIA should enhance suitable HTML and custom widgets, not replace native elements that already provide the required semantics.

Does React make applications accessible automatically?

No. React supports semantic HTML and standard ARIA attributes, but developers remain responsible for labels, keyboard interaction, focus management, dynamic announcements, contrast, errors, and assistive-technology testing.

What is the difference between aria-labelledby and aria-describedby?

aria-labelledby defines an element’s accessible name using referenced content. aria-describedby provides supplementary information such as instructions, hints, requirements, or validation feedback.

Is accessibility good for SEO?

Semantic structure, headings, descriptive links, and meaningful alternative text can also help search engines understand content. However, accessibility is broader than SEO and should be implemented primarily so people can use the product.

Conclusion

Web accessibility is an engineering responsibility, not a final layer added after an interface is complete.

Semantic HTML provides the strongest foundation. ARIA can fill genuine semantic gaps, but it must remain synchronised with visible behaviour and be supported by the correct keyboard and focus model. React does not change these principles; it makes deliberate focus management and dynamic announcements more important because the interface can change without a page reload.

The most dependable process is straightforward: use native HTML wherever possible, build accessible behaviour into reusable components, test with more than automated tools, and include people with disabilities in evaluation.

An accessible interface is not only more inclusive. It is generally clearer, more predictable, easier to test, and more resilient across browsers, devices, and ways of interacting.

Author-Amarnath Barpanda
Amarnath Barpanda
LinkedIn

Experienced Frontend Developer with a coder's heart. Mastering Shopify intricacies, crafting seamless shopping experiences. Let's elevate the e-commerce game. 🚀🌟

Share this article

Phone

Next for you

8 Best GraphQL Libraries for Node.js in 2025 Cover

Technology

Aug 4, 202613 min read

8 Best GraphQL Libraries for Node.js in 2025

8 Best GraphQL Libraries for Node.js in 2026 Too Long? Read This First - Choose Apollo Server when you need a mature ecosystem, GraphOS integration, plugins, or Apollo Federation. - Choose GraphQL Yoga for a modern, portable server with Fetch API compatibility and built-in support for subscriptions over Server-Sent Events. - Choose Mercurius when your application already uses Fastify and runtime efficiency is a major priority. - Use GraphQL.js when you need the official JavaScript implementati

9 React Native Animation Libraries and Tools Compared Cover

Technology

Aug 4, 202615 min read

9 React Native Animation Libraries and Tools Compared

Too Long? Read This First - Use React Native Reanimated for gesture-driven, interruptible, and performance-sensitive interface animations. - Use the built-in Animated API for simple fades, transforms, and timed sequences without another dependency. - Pair React Native Gesture Handler with Reanimated for swipes, dragging, pinching, rotation, and other touch-driven experiences. - Use Lottie React Native for non-interactive motion graphics supplied by designers. - Choose React Native Skia for cust

9 Critical Practices for Secure Web Application Development Cover

Technology

Aug 4, 202616 min read

9 Critical Practices for Secure Web Application Development

Too Long? Read This First - Define security requirements and model threats before implementation begins. - Treat authentication, account recovery, and MFA as one complete identity system. - Apply server-side authorization to every protected action and object. - Prevent injection with parameterized APIs, structured validation, safe output handling, and restricted outbound requests. - Protect sessions and tokens throughout their complete lifecycle. - Minimise sensitive data and manage encryption