A11y Aria
ARIA (Accessible Rich Internet Applications) is a set of HTML attributes like role , aria-label , and aria-expanded that describe the purpose and state of custom widgets to screen readers when native HTML elements aren't enough.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free HTML & CSS course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll know when ARIA helps, when it hurts, and how to bolt correct roles, states, and live regions onto custom widgets without breaking screen readers.
ARIA is like subtitles on a film. The film (your page) already has visual cues, and subtitles (ARIA) make that same information available to people who can't see the screen. But here's the catch: ARIA only writes the subtitle — it never changes the film. Slapping role="button" on a <div> tells the screen reader "this is a button" but adds zero clicking, focusing, or keyboard behaviour. A real <button> brings the subtitle and the behaviour for free.
The first rule of ARIA: don't use ARIA. If a native HTML element gives you the role and behaviour you need, use it instead.
ARIA stands for Accessible Rich Internet Applications . It does exactly three things, and it's worth keeping them straight:
The example below puts all three on one custom toggle so you can see how they read out. Notice the comments stating exactly what a screen reader announces.
Every interactive element needs an accessible name — the text a screen reader speaks. Three attributes give you one:
States communicate what's happening right now. The three you'll reach for most:
When content changes without a page reload — a "saved" toast, a form error, a cart count — sighted users see it, but a screen reader stays silent unless you mark the container as a live region . Use aria-live="polite" for routine updates and aria-live="assertive" (or role="alert" ) for urgent errors that should interrupt.
Some widgets — tabs, menus, sliders — have no native HTML element. Here ARIA earns its keep: you build the structure from <div> s, then describe it with a coordinated set of roles and states (a "design pattern"). A tab set needs role="tablist" , role="tab" with aria-selected , and role="tabpanel" .
These icon-only buttons are silent to a screen reader. Add an accessible name to each by filling in the blanks. One concept, one minute.
This "Read more" disclosure shows and hides a panel, but the screen reader never learns whether it's open. Add the missing state and keep it in sync.
Support is faded now — only an outline is given. Build a button that, when clicked, makes a live region announce that the form was saved. Use what you learned in Section 4.
💡 Pro tip: before adding any of these, ask "is there a native element that already does this?" — the answer is usually yes.
You can now add ARIA the way pros do — sparingly and correctly:
Up next: Complex Forms & Validation — where good labelling and live regions make error handling genuinely usable.
Practice quiz
In ARIA terms, what does a 'role' describe?
- A condition that changes as the user interacts
- The accessible name spoken by a screen reader
- What an element IS, like role="tab" or role="alert"
- The keyboard behaviour of a custom widget
Answer: What an element IS, like role="tab" or role="alert". A role tells assistive technology what a thing is. States change at runtime; properties set fixed characteristics.
What is 'the first rule of ARIA'?
- Don't use ARIA — prefer a native HTML element when one fits
- Always add a role to every element
- Put aria-hidden on every decorative element
- Use aria-label on every div
Answer: Don't use ARIA — prefer a native HTML element when one fits. Native elements like button and nav carry the correct role and behaviour for free; ARIA only changes the spoken label, not behaviour.
When should you use aria-label rather than aria-labelledby?
- When the name already exists as visible text on the page
- Only on non-interactive span elements
- Whenever you need to add help text after the name
- When there is no visible text, such as an icon-only button
Answer: When there is no visible text, such as an icon-only button. aria-label types the name directly and is used when there is no visible text. aria-labelledby points at existing visible text by id.
What does aria-hidden="true" do?
- Removes the element from keyboard focus order
- Removes the element and its children from the accessibility tree
- Disables the element so it cannot be clicked
- Hides the element visually with display: none
Answer: Removes the element and its children from the accessibility tree. It removes content from the accessibility tree so screen readers skip it, but it does NOT remove the element from keyboard focus.
Why must you never put aria-hidden="true" on a focusable element?
- Keyboard users can still Tab to it while screen readers stay silent, creating a confusing dead spot
- It would make the element visually invisible
- It automatically deletes the element from the DOM
- It changes the element's role to presentation
Answer: Keyboard users can still Tab to it while screen readers stay silent, creating a confusing dead spot. aria-hidden does not affect focus, so a focusable element inside it becomes a 'ghost' stop a keyboard user reaches but the reader cannot announce.
Which attribute announces dynamic content updates without a page reload?
- aria-label
- aria-expanded
- aria-live
- aria-current
Answer: aria-live. A live region uses aria-live (or role="status"/role="alert") so changes to its content are announced automatically.
Which aria-live value is used for urgent errors that must interrupt the user?
- polite
- assertive
- off
- passive
Answer: assertive. aria-live="assertive" (or role="alert") interrupts immediately; aria-live="polite" waits until the reader is idle.
Which state attribute reports whether a disclosure or menu is currently open?
- aria-current
- aria-haspopup
- aria-describedby
- aria-expanded
Answer: aria-expanded. aria-expanded mirrors open/closed and is flipped between "true" and "false" with JavaScript on each toggle.
If you build a custom toggle as a div role="button", what must you still add by hand?
- Nothing — the role provides all behaviour
- tabindex and JavaScript to respond to Enter and Space
- Only a CSS background colour
- An aria-live region
Answer: tabindex and JavaScript to respond to Enter and Space. ARIA roles only change the spoken label. A div role="button" needs tabindex="0" and key handlers — which is why native button is preferred.
Which set of roles correctly structures an accessible custom tab widget?
- role="menu", role="menuitem", role="group"
- role="navigation", role="link", role="region"
- role="tablist", role="tab", role="tabpanel"
- role="grid", role="row", role="cell"
Answer: role="tablist", role="tab", role="tabpanel". A tab set uses role="tablist" containing role="tab" buttons (with aria-selected) and matching role="tabpanel" panels.