ARIA Label Generator

Accessible names for the patterns that need them, with the rule that trips people up: aria-label replaces the visible text rather than adding to it.

Enable JavaScript to customise; default output below.

Each of these needs a name for a different reason, and three of them are better solved without aria-label at all.

What a screen reader should announce. A verb and an object: "Close dialog", not "Close" and not "Button".

Where the control has visible text too. WCAG 2.5.3 asks that the accessible name contain it, so speech-recognition users can say what they see.

Used where labelling by reference is better than labelling by attribute, which is whenever the text is already on the page.

Only needed when the name is in a different language from the page, in which case it goes in a lang attribute on the element.

Live preview aria-labels.html
<!--
	The accessible name is what assistive technology announces. It is computed in
	this order, first match winning:

	  aria-labelledby, aria-label, the element's own labelling (a <label>, a
	  <caption>, alt text), then the title attribute.

	So aria-label REPLACES the visible text rather than adding to it, and
	aria-labelledby beats aria-label. That order is the source of most of the
	surprises in this area.
-->
<!--
	Icon-only button: the SVG is hidden from assistive technology and the name comes
	from aria-label. The <button> element is what makes it keyboard operable; a div
	with a click handler needs role, tabindex and a key handler to catch up.
-->
<button type="button" class="icon-button" aria-label="Close dialog">
	<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
		<path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="2" fill="none" />
	</svg>
</button>

<!--
	Three rules that cover most of it:

	1. Do not put aria-label on a plain <div> or <span>. Without a role it is
	   ignored by most assistive technology, and adding a role to get the label
	   announced means taking on the keyboard behaviour of that role.
	2. If the control has visible text, the accessible name has to contain it
	   (WCAG 2.5.3). A button reading "Search" with aria-label="Submit" cannot be
	   operated by voice.
	3. Prefer a real label, a heading or visible text over an attribute. Text on
	   the page is translated, searchable and visible to everyone; an attribute is
	   none of those.
-->

Output is valid and updates as you type.

The accessible name is the text a screen reader announces for a control. It is computed in a fixed order, first match winning:

  1. aria-labelledby
  2. aria-label
  3. the element’s own labelling: a <label>, a <caption>, alt text
  4. the title attribute

Two consequences follow, and they are where most of the mistakes live. aria-label replaces the visible text rather than adding to it. And aria-labelledby beats aria-label, so a control with both announces the first one.

This writes the markup for the patterns that genuinely need a name, and for three that are better solved without aria-label at all.

How to use

  1. Pick the pattern.
  2. Put in the name you want announced, and the visible text if there is any.
  3. Copy the markup and read the comments, which say why each pattern is shaped that way.

Example

An icon-only button:

<button type="button" class="icon-button" aria-label="Close dialog">
	<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
		<path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="2" fill="none" />
	</svg>
</button>

Three things are doing work. The <button> element gives keyboard operation and the button role for free. aria-hidden on the SVG keeps the icon out of the name, so the announcement is “Close dialog, button” rather than something with the path data’s title in it. And focusable="false" stops older Internet Explorer putting the SVG in the tab order, which is harmless to keep and occasionally still useful.

Pitfalls

aria-label on a <div> or <span> usually does nothing. Without a role, most assistive technology ignores it. Adding a role to make it announce means taking on that role’s keyboard behaviour too, which for a button is Enter, Space and a visible focus state. Use the right element instead.

The visible text has to be in the name. WCAG 2.5.3, Label in Name. A button that reads “Search” with aria-label="Submit query" cannot be operated by somebody using voice control, because saying “click Search” finds nothing. If you need to extend the name, append to it with visually hidden text rather than replacing it.

Do not put the role in the name. aria-label="Main navigation" on a <nav> is announced as “main navigation navigation”. The role is already read out.

Landmarks only need names when there are several. One <nav> needs nothing. Two need distinguishing, and a heading with aria-labelledby is better than an attribute, because then the name is visible to everyone.

A form field wants a real <label>. It is clickable, which doubles the tap target, it is visible, and it gets translated. aria-label on an input is a fallback for when there is genuinely no room for a label, and “the design does not have one” is a design decision worth pushing back on.

A hint belongs in aria-describedby. Description is announced after the name and can be skipped by the user; a hint folded into aria-label becomes part of the name and is repeated every time the field is read.

title is the last resort. It is announced inconsistently, it does not appear on touch devices, and it is not shown on focus. It is fourth in the order for a reason.

Do not change a toggle’s label and its state. A mute button whose label flips between “Mute” and “Unmute” as well as flipping aria-pressed announces two contradictory things. Keep the name and let the state carry the change.

Compatibility

Everything runs in the browser: nothing is uploaded and nothing is stored.

The name computation order is from Accessible Name and Description Computation, which every current screen reader and browser implements consistently enough to rely on. aria-current takes a token rather than a boolean: page for the current page in a pagination list, and true only where none of the specific tokens fit.

The visually-hidden CSS in the external link pattern uses clip-path: inset(50%) rather than the old clip: rect(...), which is deprecated, and keeps white-space: nowrap so a long hidden string does not force a scrollbar. It deliberately does not use display: none, which would remove the text from the name it is there to extend.

aria-hidden="true" on a decorative SVG is the standard pattern. On an SVG that carries meaning, remove it and add a <title> as the first child instead, which becomes the accessible name.

Values you type are escaped for HTML, so a label containing a quote cannot break out of the attribute.

Frequently asked questions

When should I use aria-label at all?
When the control has no visible text and no other way to get one: an icon-only button, an icon-only link, a landmark that needs distinguishing, an <iframe>. Everywhere else, real text is better.
aria-label or aria-labelledby?
aria-labelledby when the text is already on the page, because then the name and the visible text cannot drift apart. aria-label when the text does not exist anywhere.
Does aria-label translate?
Browser translation tools do translate it, and they are less reliable with attributes than with text. That is another argument for text on the page.
Can I use both aria-label and a label element?
You can, and aria-label wins, which means the <label> is visible and ignored. That mismatch is the Label in Name failure again.
How do I check what is announced?
VoiceOver on macOS with Safari, NVDA on Windows with Firefox, and the accessibility tree in your browser’s developer tools, which shows the computed name without any screen reader at all. The tree is the quickest check and the screen reader is the honest one.
Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.