skip to content

Accessibility and ARIA

The explicit accessibility layer of markup: ARIA roles, states, and properties, how accessible names are computed, live regions, and focus management. Interviewers ask because the first rule of ARIA is to avoid ARIA, and knowing when you actually need it is the real skill.

part ofHTMLoverview, primer and where to startread it →
on this pageshow

explore

questions

30

In HTML and ARIA, what is an element's accessible name, and what does the browser compute it from for `<button>Save</button>` and `<img src="logo.png" alt="Acme home">`?

level: juniorimportance: must knowfreq 60%

answer

  1. what the screen reader says out loud
  2. not a single attribute — a computation
  3. each element type has its own source
  4. button text, img alt, table caption
  5. empty name means bare "button"

basics

~20 s

An element's accessible name is the short label assistive technology announces for it. The browser computes it per element type: a button's name comes from its own text content, an image's from its alt attribute.

solid answer

~50 s

The accessible name is the string a screen reader or voice-control tool announces to identify an element — it is what the user hears alongside the role, as in "Save, button". It is **computed**, not stored in one attribute: the browser runs the accessible name computation and takes the first source that yields text. For `<button>Save</button>` the source is the element's own text content, so the name is "Save". For `<img src="logo.png" alt="Acme home">` the source is `alt`, so the name is "Acme home". Different elements have different native sources: a `<table>` takes its name from `<caption>`, a `<fieldset>` from `<legend>`, a `<figure>` from `<figcaption>`, and a form control from its associated `<label>`. An interactive control with no name at all is announced as bare "button" or "edit text", which leaves the user with no idea what it does.

go deeper

for a junior

Be able to say that the accessible name is what a screen reader announces to identify an element, and name the obvious sources: a button's text, an image's alt, a form field's label.

for a middle

Explain that the name is computed by an algorithm over an ordered list of sources rather than read from one attribute, and map the native source for buttons, images, tables, fieldsets and form controls.

for a senior

Show that you verify names against the accessibility tree in DevTools rather than inferring them from markup, and that you catch nameless controls in review and in automated role-plus-name test queries.

for a principal

Own the guarantee at the system level: pick component APIs that make a missing accessible name impossible to ship, and put a name assertion into the shared test and lint tooling so the property is enforced rather than remembered.

## What an accessible name actually is Every element exposed to assistive technology sits in the **accessibility tree** — a parallel tree the browser builds from the DOM, where each node carries a small set of properties: a **role** (what kind of thing it is), a **name** (which one it is), sometimes a **value** and a **state**, and optionally a **description**. The accessible name is the identity string. A screen reader typically announces name plus role — "Save, button", "Search, edit text", "Acme home, link". Voice-control software matches spoken commands against the same string: "click Save" works because the button's accessible name is "Save". Automated tooling uses it too — queries like `getByRole('button', { name: 'Save' })` in Testing Library and Playwright match on the computed accessible name, not on the raw markup. The key word is **computed**. There is no single `name` attribute in HTML. The browser follows the accessible name computation, a specified algorithm shared by HTML and ARIA, and walks a list of candidate sources in order until one produces a non-empty string. ## Where the name comes from, element by element Each element type has its own native source, defined by the HTML Accessibility API Mappings: ```html <button>Save</button> <!-- name: "Save" (text content) --> <a href="/help">Help centre</a> <!-- name: "Help centre" (text content) --> <img src="logo.png" alt="Acme home"> <!-- name: "Acme home" (alt) --> <label for="q">Search</label> <input id="q" type="search"> <!-- name: "Search" (associated label) --> <table><caption>Q3 revenue</caption>…</table> <!-- name: "Q3 revenue" (caption) --> <fieldset><legend>Shipping</legend>…</fieldset> <!-- name: "Shipping" (legend) --> <figure><img …><figcaption>Fig 1</figcaption></figure> <!-- name: "Fig 1" (figcaption) --> ``` Notice the pattern: elements whose visible content *is* the label (buttons, links, headings) take their name from their **subtree text**, while elements whose content is not a label (images, tables, groups, form fields) take it from a dedicated attribute or a companion element. This is the practical reason to reach for the right native element. `<button>Save</button>` gets a role, keyboard activation, focusability and a name from one element with no extra attributes. A `<div>` with a click handler gets none of them and must have all four reproduced by hand. ## alt="" is a deliberate empty name `<img alt="">` is not a missing alt. An empty `alt` tells the browser the image carries no information the user needs, and the image is removed from the accessibility tree entirely — a screen reader passes over it silently. That is the correct markup for a decorative divider or a background flourish. Omitting `alt` altogether is a different thing: with no `alt` there is no name source, so assistive technology may fall back to announcing the file name from `src`, which is why users hear "logo dot png, image". Every `<img>` needs an `alt` attribute; the judgement is only whether its value is empty or descriptive. ## Controls with no name The common failure is an interactive control that computes to an empty name: ```html <button><svg viewBox="0 0 16 16"><path d="…"/></svg></button> ``` The subtree contains an SVG with no text, so the computed name is empty and the control is announced as just "button". The user can reach it and press it but cannot tell what it does, and a voice-control user has no phrase to say. The same happens with an input whose only visible hint is a `placeholder`, since `placeholder` is at best a last-resort fallback and disappears the moment the user types. ## How to check it Do not guess from the markup — read the computed value. Chrome DevTools shows it in the **Accessibility** pane of the Elements panel, including which source produced it; Firefox has an Accessibility Inspector that shows the same tree. Automated scanners such as axe report controls whose accessible name is empty as a violation, and a `getByRole(…, { name })` query in a test suite fails loudly when a refactor silently strips the name. The habit worth forming is to treat the accessible name as a real, inspectable output of your markup, the same way you treat computed styles — something you verify rather than assume.

  • Why is `<img alt="">` acceptable but omitting the alt attribute entirely is not?
    `alt=""` is an explicit statement that the image is decorative: the browser removes it from the accessibility tree and a screen reader skips it silently. With no `alt` at all there is no name source, so assistive technology often falls back to announcing the file name from `src` — users hear "logo dot png, image". Every `<img>` needs the attribute; only its value is a judgement call.
  • An icon-only `<button>` containing just an inline SVG is announced as "button" with nothing else. Why?
    The button's native name source is its subtree text, and an SVG shape contributes no text, so the computation yields an empty string. The element still has a role and is focusable, so the user can press it but has no idea what it does, and voice control has no phrase to match. The control needs a name from some source before it is usable.
  • How does the accessible name differ from the role?
    The role says what kind of thing an element is — button, link, checkbox, table — and comes from the element itself or an explicit `role` attribute. The name says which particular one it is. Screen readers announce them together, as in "Save, button", and both must be present for the control to be usable: a nameless button and an unlabelled role are equally opaque.

saying these in an interview costs you the question

  • Thinks every element needs aria-label to have a name
  • Says the accessible name is always just the visible text
  • Confuses the accessible name with the element's role
  • Treats alt="" as a mistake rather than a decorative marker
  • Believes a placeholder counts as a proper label

context

open as a page

In HTML, what do tabindex="0", tabindex="-1", and a positive value such as tabindex="3" each do to an element?

level: juniorimportance: must knowfreq 72%

basics

~20 s

tabindex="0" puts an element in the tab order at its DOM position; a negative value such as -1 makes it focusable by script or click but never by Tab; a positive value jumps it ahead of everything else and wrecks the order.

open as a page

A toolbar button in your HTML contains only an <svg> icon and no text. What are your options for giving it an accessible name, and which would you choose?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Give the button an accessible name with aria-label="Delete", with visually hidden real text inside it, or with aria-labelledby pointing at existing on-screen text. Hide the decorative icon with aria-hidden="true" so it adds nothing. A title tooltip alone is not enough.

open as a page

In ARIA, what is the difference between aria-live="polite" and aria-live="assertive" on a live region, and which of the two do role="status" and role="alert" imply?

level: juniorimportance: must knowfreq 52%

basics

~10 s

aria-live="polite" queues the announcement until the screen reader finishes what it is saying; aria-live="assertive" interrupts immediately. role="status" implies polite, role="alert" implies assertive, and both imply aria-atomic="true".

open as a page

In web accessibility, the "first rule of ARIA use" is quoted constantly in interviews. What does the rule say, and what does following it look like in real markup?

level: juniorimportance: must knowfreq 70%

basics

~20 s

The first rule of ARIA is to not use ARIA when a native HTML element already provides the semantics and behaviour you need. Reach for button, a with href, input and label first; add ARIA only to fill real gaps.

open as a page

In a disclosure widget where a button shows and hides a panel, what does aria-expanded communicate, which element must carry it, and what do its values mean?

level: juniorimportance: must knowfreq 64%

basics

~20 s

aria-expanded reports whether the content a control governs is currently open. It belongs on the interactive control itself, not on the panel, and takes the string values "true" or "false" — omitting it means the control is not expandable at all.

open as a page

A page contains `<button aria-labelledby="t" aria-label="Close" title="Dismiss dialog">Save</button>` and `<span id="t">Cancel</span>`. Which string does a screen reader announce as the button's name, and what is the general precedence order of naming sources?

level: middleimportance: must knowfreq 62%

basics

~20 s

The button is announced as "Cancel". The accessible name computation takes the first source that yields text, in order: aria-labelledby, then aria-label, then the element's native source such as text content, alt, label, caption or legend, and finally title.

open as a page

In HTML, what is the difference between aria-label, aria-labelledby, and aria-describedby, and when do you reach for each?

level: middleimportance: must knowfreq 76%

basics

~20 s

aria-label supplies an element's accessible name as a literal string, aria-labelledby builds that name from text already on the page by referencing element IDs, and aria-describedby attaches supplementary text announced after the name. Labels identify; descriptions supplement.

open as a page

In HTML, why is a status message that gets inserted into the page inside a brand-new element carrying aria-live="polite" often announced by nothing at all, and what markup pattern fixes it?

level: middleimportance: must knowfreq 62%

basics

~20 s

Screen readers announce only changes inside a live region that already existed in the accessibility tree; a region injected together with its text presents no change to observe. Render the empty container in the initial markup, then write text into it.

open as a page

A team ships a clickable control as `<div class="btn" onclick="save()">Save</div>`. What does a real `<button>` element give you that this div does not, and what would you have to add to make the div behave equivalently?

level: middleimportance: must knowfreq 82%

basics

~20 s

A native button is focusable, fires on Enter and Space, reports a button role, and submits forms. The div has none of it — you must add a role, tabindex, key handlers, disabled handling and focus styling just to draw level.

open as a page

When a modal dialog opens on a page, what must happen to keyboard focus while it is open and when it closes?

level: seniorimportance: must knowfreq 56%

basics

~20 s

Move focus into the dialog when it opens, keep it inside while it is open, make the rest of the page inert so Tab cannot escape, and return focus to the control that opened the dialog once it closes.

open as a page

In ARIA, what is the difference between an element's accessible name and its accessible description, and which of the two does a `title` attribute supply when the element already has a name from another source?

level: middleimportance: should knowfreq 42%

basics

~20 s

The accessible name identifies an element and is announced first; the accessible description is supplemental detail announced afterwards and often skipped. A title attribute supplies the name only when nothing else does — otherwise it is demoted to the description.

open as a page

In HTML, what does the inert attribute do to the subtree it is applied to, and how does that differ from aria-hidden="true"?

level: middleimportance: should knowfreq 38%

basics

~10 s

inert removes a whole subtree from interaction: nothing inside can be focused or clicked, and it disappears from the accessibility tree. aria-hidden="true" only hides content from assistive technology, leaving it focusable and clickable.

open as a page

In HTML, how does aria-labelledby resolve when its value lists several IDs, and what happens if one of those IDs does not exist on the page?

level: middleimportance: should knowfreq 48%

basics

~20 s

aria-labelledby takes a space-separated list of element IDs, gathers each referenced element's text, and joins the pieces in the order the IDs are listed, not DOM order. IDs that match nothing are skipped silently and contribute no text.

open as a page

In an ARIA live region such as <p aria-live="polite">Showing <span>12</span> results</p>, what does aria-atomic control, and what does the default aria-atomic="false" mean when only the number changes?

level: middleimportance: should knowfreq 38%

basics

~20 s

aria-atomic decides how much of a live region is announced when part of it changes. The default, false, announces only the changed part — so the region above says just "13". Setting aria-atomic="true" re-reads the whole region: "Showing 13 results".

open as a page

What actually changes when you write `<a href="/settings" role="button">Settings</a>`, and what stays exactly the same?

level: middleimportance: should knowfreq 50%

basics

~20 s

Only the announced role changes. ARIA writes to the accessibility tree, so a screen reader says "button", but the element still navigates on activation, still responds to Enter and not Space, and still offers link context menus.

open as a page

ARIA has several states that all seem to mean "this one is on" — aria-checked, aria-selected, aria-pressed and aria-current. Which widget does each belong to?

level: middleimportance: should knowfreq 40%

basics

~20 s

aria-checked belongs to checkboxes, radios and switches; aria-selected to items chosen inside a composite such as a listbox option, tab or grid row; aria-pressed to toggle buttons; and aria-current marks the current item in a set, like the active nav link.

open as a page

In ARIA, how do the properties aria-haspopup, aria-controls and aria-owns differ in what they tell assistive technology?

level: middleimportance: should knowfreq 36%

basics

~20 s

aria-haspopup says a control opens a popup and of what type; aria-controls names, by id, the element this control governs; aria-owns re-parents elements in the accessibility tree so they become children of this element despite the DOM saying otherwise.

open as a page

In ARIA, what is the difference between a role, a state, and a property, and how does that difference show up in the markup?

level: middleimportance: should knowfreq 52%

basics

~20 s

An ARIA role says what an element is (role="tab"), a state says what it is doing right now and changes with interaction (aria-expanded), and a property describes a stable trait or relationship (aria-haspopup). States and properties are both aria-* attributes.

open as a page

A `<button>` that visibly reads "Save" is announced by a screen reader as "Submit form", and voice-control users report that saying "click Save" does nothing. How would you find where that name came from, and why does the mismatch break voice control?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Read the computed accessible name and its source in the browser's accessibility tree — DevTools names the winning source directly, and it will be an aria-label or aria-labelledby outranking the visible text. Voice control matches spoken commands against that name, so a name that omits the visible label leaves nothing to say.

open as a page

In a client-side-routed app where navigation swaps the page content without a full document load, why do keyboard and screen-reader users lose their place, and what markup makes the fix possible?

level: seniorimportance: should knowfreq 44%

basics

~20 s

The link that was clicked is removed along with the old view, so focus falls back to the document body: tabbing restarts at the top and nothing is announced. The fix is a focus target in the new view, such as a heading carrying tabindex="-1".

open as a page

A password field in a form has a permanent format hint under it, and on a failed submit an error message appears in the same area. How do you wire both to the input in HTML, and what does aria-describedby not do for you?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Give the hint and the error each an id and list both in the input's aria-describedby, in the order you want them read, alongside aria-invalid="true" on failure. aria-describedby only attaches text to the field; it does not announce anything on its own.

open as a page

An audit flags <button aria-label="Add to cart">Buy now</button>. What problem does the mismatch between the aria-label and the visible text create, and how would you fix it?

level: seniorimportance: should knowfreq 36%

basics

~20 s

The aria-label replaces the visible text in the accessibility tree, so a voice-control user who says "click Buy now" matches nothing and cannot activate the button. Make the accessible name start with, or contain, the visible words.

open as a page

A search page writes its result count into an aria-live="polite" region on every keystroke, and screen-reader users report a constant stream of speech they cannot get past. How do you diagnose and fix the noise?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Announce settled results, not keystrokes. Debounce the update so one message is written after typing pauses, keep the region polite and atomic, write a full self-contained sentence, and use aria-busy while a multi-step update is in flight.

open as a page

An audit of a product built largely from `<div>` elements annotated with ARIA roles reports almost no automated violations, yet screen-reader users describe the interface as unusable. Why can incorrect ARIA be worse than no ARIA, and how would you prioritise the fixes?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Incorrect ARIA overrides what the browser already knew and makes assistive technology report a widget that does not behave as promised. Automated tools verify attribute syntax, not truthfulness. Fix by deleting wrong ARIA and restoring native elements first.

open as a page

When should a control use aria-disabled="true" instead of the HTML disabled attribute, and what must you handle yourself if you do?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Use aria-disabled when the control must stay focusable and discoverable — toolbar items, or a submit button whose blocked reason the user needs to read. It only announces the state, so you must block the action, style the control, and keep it out of any effect yourself.

open as a page

Across a large web application, how would you decide between one shared application-level ARIA live region and per-component live regions, and what breaks in each approach?

level: principalimportance: should knowfreq 24%

basics

~20 s

Most products are best served by a small fixed set of shared live regions owned by the app shell — usually one polite and one assertive — with a single API feature code calls. Per-component regions scale badly: they multiply, they get unmounted mid-message, and nobody can rate-limit them.

open as a page

In the accessible name computation, does `aria-labelledby` pointing at an element styled `display: none` still produce a name, and what is the accessible name of `<button>Delete <span aria-hidden="true">×</span></button>`?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Yes — a hidden element referenced directly by aria-labelledby still contributes its text, which is a deliberate exception. Inside the button's own subtree the opposite applies: an aria-hidden descendant is skipped, so the name is "Delete".

open as a page

Across a large component library, `<div>`-based controls with hand-written ARIA keep reappearing even though native elements exist for them. How would you make native-first the default, and when is a custom ARIA widget genuinely justified?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Make the native element the easiest path: ship styled primitives, remove CSS resets that punish real elements, lint for role and tabindex on generic tags, and require a keyboard pass in review. Custom widgets are justified only where HTML has no equivalent.

open as a page