> The first rule of ARIA, the roles and states that actually earn their place in JS components, and the anti-patterns that make interfaces worse for screen reader users.

*Source : https://designerdiscussion.com/blog/aria-when-to-use-it/*

---

[Home](https://designerdiscussion.com/)/[Blog](https://designerdiscussion.com/blog)/[Development](https://designerdiscussion.com/category/development)

Development

# ARIA: When to Use It, and Above All When to Skip It

The first rule of ARIA, the roles and states that actually earn their place in JS components, and the anti-patterns that make interfaces worse for screen reader users.

// DD EditorialSep 21, 20267 min read

![](https://designerdiscussion.com/covers/web-accessibility-wcag-guide.png)

ARIA has a reputation problem. Developers reach for it as a fix-all (“sprinkle some `aria-label` on it”), while accessibility specialists keep warning that badly applied ARIA is worse than none. Both reactions make sense once you understand what ARIA actually does: it changes what assistive technology _announces_, and nothing else. It adds no keyboard support, no focus handling, no behavior. That single fact explains most of the rules below.

## What ARIA is, in one paragraph

WAI-ARIA (Accessible Rich Internet Applications) is a W3C specification that lets you describe an element’s **role** (what it is), its **states** (checked, expanded, selected) and its **properties** (its label, what it controls, what describes it) to the accessibility tree. Browsers pass that information to screen readers and other assistive tools. If your markup already carries the right meaning, the accessibility tree is correct without a single ARIA attribute.

## The first rule of ARIA

The W3C’s _Using ARIA_ note opens with a rule that sounds almost self-defeating: if a native HTML element or attribute already has the semantics and behavior you need, use it instead of repurposing another element and patching it with ARIA.

Compare the two versions of the same control:

```
<!-- Native: focusable, Enter and Space work, announced as "button" -->
<button type="button">Save draft</button>

<!-- Rebuilt: you now owe focus, key handling and the role -->
<div role="button" tabindex="0" onclick="save()" onkeydown="handleKey(event)">
  Save draft
</div>
```

The `div` version only reaches parity if you remember `tabindex`, handle both Enter and Space, manage a disabled state and style the focus ring. Miss one and keyboard users are stuck. The native `<button>` gives you all of it for free.

The same logic applies across the board: `<a href>` for navigation, `<input type="checkbox">` for toggles, `<details>` and `<summary>` for simple disclosure, `<dialog>` for modals, `<nav>`, `<main>` and `<header>` instead of landmark roles. If you are building a component library, bake these choices into the base components; our [design systems guide](https://designerdiscussion.com/blog/design-systems-guide/) covers why accessibility decisions belong at that level rather than in each screen.

## The other four rules, briefly

The same W3C note lists four more rules. They read like common sense, yet each one is regularly broken in production:

1.  **Don’t change native semantics** unless you really have to. `<h2 role="tab">` turns a heading into something that is no longer a heading.
2.  **Every interactive ARIA control must be keyboard operable.** A role is a promise; the keyboard behavior is on you.
3.  **Never put `role="presentation"` or `aria-hidden="true"` on a focusable element.** Users can tab to it and hear nothing.
4.  **Every interactive element needs an accessible name**, whether from visible text, a `<label>`, `aria-labelledby` or, as a last resort, `aria-label`.

## Where ARIA earns its place

Native HTML still has gaps, especially for the dynamic widgets that JavaScript frameworks make easy to build. This is where ARIA is the right tool:

Pattern

Attributes that matter

What they do

Disclosure / accordion

`aria-expanded`, `aria-controls`

Announces open or closed on the trigger button

Tabs

`role="tablist"`, `role="tab"`, `role="tabpanel"`, `aria-selected`

Exposes the tab structure and the active tab

Form errors

`aria-invalid`, `aria-describedby`

Flags the field and links it to its error message

Live updates

`aria-live="polite"`, `role="status"`, `role="alert"`

Reads out changes that happen away from the focus

Icon-only buttons

`aria-label`

Gives a name when there is no visible text

Current page in nav

`aria-current="page"`

Tells users which link they are on

Two details make the difference between working and merely present. First, **states must stay in sync with the UI**: an accordion that visually opens while `aria-expanded` stays `false` announces the opposite of reality. Update the attribute in the same function that toggles the panel. Second, **live regions must exist in the DOM before their content changes**. Injecting a new element that already contains `aria-live` and its message is often missed by screen readers.

For French teams working against the RGAA, which builds on WCAG and adds its own test methodology, [the ARIA guide by Agence RGAA and its examples](https://agence-rgaa.fr/accessibilite-numerique/aria/) (in French) walks through these patterns criterion by criterion, a useful reference when an audit report cites a specific failure.

## Anti-patterns that keep showing up in JS components

**The clickable `div` or `span`.** The most common one, usually born in a card component where the whole tile is clickable. Wrap the title in a real link and extend its hit area with CSS instead.

**`aria-label` on a generic element.** Putting `aria-label` on a plain `div` or `span` with no role is prohibited by the ARIA specification, and screen readers handle it inconsistently. The label belongs on an interactive element or a landmark.

**`role="menu"` for site navigation.** The menu role describes an application menu, like the one in a desktop app, with arrow-key navigation. On a website header it changes how screen readers present the links and creates expectations the component doesn’t meet. A `<nav>` with a list of links is the correct pattern.

**Redundant roles.** `<button role="button">` or `<ul role="list">` add noise without adding meaning. (One exception: some browsers drop list semantics when you remove bullets with CSS, which is why you sometimes see `role="list"` on purpose.)

**Hiding content that is still focusable.** An off-canvas menu hidden with `aria-hidden="true"` but still reachable by Tab creates invisible, silent focus stops. Use `inert` or `display: none` on closed panels.

**Shouting live regions.** `aria-live="assertive"` or `role="alert"` on every toast interrupts whatever the user is listening to. Keep assertive announcements for errors that block the task; use `polite` for the rest.

**A label that contradicts the visible text.** A button showing “Buy” with `aria-label="Add product to basket"` breaks voice control: users say what they see and nothing happens. The accessible name should contain the visible text.

## A quick check before you ship

Open the accessibility panel in your browser’s DevTools and inspect each custom component: role, name and states should read like a sentence that makes sense. Then tab through it and listen with a screen reader, NVDA on Windows or VoiceOver on macOS. Automated checkers such as axe will catch missing names and invalid attributes, but they can’t tell you whether `aria-expanded` actually follows the panel. Our [WCAG guide](https://designerdiscussion.com/blog/web-accessibility-wcag-guide/) lists the free tools worth adding to that routine.

The takeaway is less ARIA, placed with more intent. Start from semantic HTML, add ARIA only where native elements run out, and treat every role you write as a contract your JavaScript has to honor.

![DD Editorial](https://designerdiscussion.com/avatar.svg)

DD Editorial

// DesignerDiscussion editorial team

We test tools, read the docs so you don't have to, and rank the agencies actually shipping great work.

\[ ./related \]

## Related _posts_

[

Development ![](https://designerdiscussion.com/covers/choosing-a-web-stack-2026.png)

### How to Choose a Web Stack in 2026

// DD Editorial 10 min read Jun 15, 2026

](https://designerdiscussion.com/blog/choosing-a-web-stack-2026)[

Development ![](https://designerdiscussion.com/covers/react-server-components-explained.png)

### React Server Components, Explained Without the Headache

// DD Editorial 11 min read Jun 20, 2026

](https://designerdiscussion.com/blog/react-server-components-explained)[

Development ![](https://designerdiscussion.com/covers/headless-cms-comparison.png)

### Headless CMS in 2026: Sanity vs Strapi vs Contentful vs Storyblok

// DD Editorial 11 min read Jun 8, 2026

](https://designerdiscussion.com/blog/headless-cms-comparison)

  dd@signal:~ — subscribe.sh

$ ./join --weekly-signal

\> one email a week. design intel, dev drops, agency rankings. zero noise.
