Skip to content

JavaScript Tutorial Accessibility with JavaScript

Accessible Buttons, Semantic HTML and ARIA

Lesson 218 of 258 3 min read By Deepak Paswan Updated Oct 4, 2026

ARIA (Accessible Rich Internet Applications) attributes add information for assistive technology, such as screen readers. It is powerful, and it is also easy to misuse.

The first rule of ARIA

> Do not use ARIA if a native HTML element already does the job.

<button>, <a>, <input>, <nav>, <main>, <dialog> and friends carry the right meaning and behaviour built in. ARIA only changes what is announced, never how things behave. A <div role="button"> still needs focus, Enter and Space handling written by you.

index.html
HTML
<!-- Bad: needs a role, tabindex, key handling... -->
<div role="button" tabindex="0">Save</div>

<!-- Good -->
<button type="button">Save</button>

When ARIA is needed

When no native element describes what you built, or the state is changing dynamically.

aria-expanded: a disclosure

A button that shows and hides content should say whether it is open.

index.html
HTML
<style>[hidden] { display: none; }</style>
<button id="toggle" aria-expanded="false" aria-controls="details">More info</button>
<div id="details" hidden>Here is the extra information.</div>
<script>
  const toggle = document.querySelector("#toggle");
  const details = document.querySelector("#details");

  toggle.addEventListener("click", () => {
    const open = toggle.getAttribute("aria-expanded") === "true";
    toggle.setAttribute("aria-expanded", String(!open));
    details.hidden = open;
  });
</script>

Screen readers announce “More info, button, collapsed” and then “expanded” after activation. (The native <details> element does this with no JavaScript at all, so use it when it fits.)

aria-pressed: a toggle button

index.html
HTML
<button id="mute" aria-pressed="false">Mute</button>
<script>
  const button = document.querySelector("#mute");
  button.addEventListener("click", () => {
    const pressed = button.getAttribute("aria-pressed") === "true";
    button.setAttribute("aria-pressed", String(!pressed));
    console.log("muted:", !pressed);
  });
</script>

aria-label: a name when there is no text

An icon-only button has no accessible name unless you give one.

index.html
HTML
<button aria-label="Close dialog">×</button>
<button aria-label="Search"><svg aria-hidden="true">...</svg></button>

aria-hidden="true" hides decorative content from assistive technology. Never put it on something focusable.

Useful attributes

AttributeUse
aria-labela name when the element has no visible text
aria-labelledbyname it with another element’s text
aria-describedbyextra help or error text (as in forms)
aria-expandedopen or closed
aria-pressedtoggle buttons
aria-currentthe current page or step in a set
aria-liveannounce changes (Live Regions)
aria-hiddenhide decoration
roleonly when no native element fits

Keep it truthful

ARIA is a promise. If you write aria-expanded="true", the content must really be expanded. Update ARIA in the same place as the visual change, so they never disagree.

Common mistakes

  • Using ARIA instead of HTML (div role="button" for everything).
  • Wrong or contradictory roles (a <button role="link">).
  • Forgetting to update states when JavaScript changes the page.
  • aria-label that repeats the visible text with different words (screen reader users hear something different from what is seen). Start the label with the visible text.
  • No keyboard support for custom roles.

Test with a screen reader

NVDA (Windows, free), VoiceOver (macOS and iOS) and TalkBack (Android) are built in or free. Even a quick check teaches a lot, and browser developer tools have an Accessibility panel showing the computed names and roles.

info
No ARIA is better than bad ARIA
Pages with native, well-structured HTML are often more accessible than pages with lots of ARIA. Add it deliberately.