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.
<!--
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.
Fix the highlighted fields to update the output.
The accessible name is the text a screen reader announces for a control. It is computed in a fixed order, first match winning:
aria-labelledbyaria-label- the element’s own labelling: a
<label>, a<caption>,alttext - the
titleattribute
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
- Pick the pattern.
- Put in the name you want announced, and the visible text if there is any.
- 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?
<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?
Can I use both aria-label and a label element?
aria-label wins, which means the <label> is visible and ignored. That
mismatch is the Label in Name failure again.