"The First Rule of ARIA is: don't use ARIA. The Second Rule is: when you have to, use this small set well."
The Spec Itself Tells You To Avoid It
The W3C's Using ARIA document opens with a literal warning: "If you can use a native HTML element... with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so." Most ARIA misuse comes from adding ARIA to elements that already had the right semantics. <button aria-label="Submit">Submit</button> — the aria-label is silently redundant; the button's text content is already the accessible name.
So when does ARIA earn its keep? Five situations cover 90% of legitimate cases.
1. Naming An Unnamed Element
When the visible text isn't a useful name — icon-only buttons, search inputs labeled only by a magnifying-glass icon, close buttons that show only an X — give them a name.
aria-label="Close menu"— the accessible name comes from this attribute (use when no visible label exists).aria-labelledby="id-of-another-element"— the name comes from text inside another element (use when the label is visible somewhere else on screen).
2. Describing An Element
For helper text, hints, or validation messages associated with an input:
aria-describedby="id"— points to the element holding the description. Screen readers read the name first, then the description. Use for password hints ("At least 8 characters with one number"), validation errors, format guidance.
3. Communicating State
When an interactive element's state matters but isn't obvious from its native semantics:
aria-expanded="true" | "false"— for buttons that open menus, accordions, dropdowns.aria-pressed="true" | "false"— for toggle buttons (think bold/italic in an editor).aria-checked="true" | "false" | "mixed"— for custom checkboxes (mixed = indeterminate).aria-selected="true" | "false"— for items in a custom tab list or listbox.aria-disabled="true"— for elements that look disabled but you still want focusable (so screen reader users know it exists). Different from thedisabledattribute.
4. Form Validation
aria-required="true"— only needed when you can't use the nativerequiredattribute (custom form controls). With native HTML inputs,requiredalready sets this.aria-invalid="true"— paired with the field once validation fails. Screen readers announce "invalid entry" when the field gets focus. Pair witharia-describedbypointing at the error message.
5. Live Regions For Dynamic Content
When content updates without the user navigating (a toast notification, a save confirmation, search results that filter as the user types), screen readers won't notice — unless the updated region is a live region:
aria-live="polite"— announces the update when the screen reader is idle. Default choice for most notifications.aria-live="assertive"— interrupts whatever the screen reader is saying. For critical messages (errors, lost connection). Use sparingly — it's the all-caps shout.role="status"— shorthand for polite live region. Use for status messages.role="alert"— shorthand for assertive live region. Use for errors.
Hiding From Assistive Tech: aria-hidden="true"
When something is visible but should be skipped by screen readers — decorative icons, redundant text — set aria-hidden="true". Most common case: a button that has both a visible icon and a visible text label. The icon is decorative; the text is the name. Hide the icon from screen readers so it doesn't announce "image, settings" before "button, Settings."
tabindex="-1" or the native hidden attribute.The Mantra: Native First, ARIA Second
aria-expanded, every aria-checked, every aria-invalid requires JavaScript to maintain. Native wins on maintenance every time.Pippa's Note
aria-label="Message Pippa" on the textarea (because the visible label is hidden for visual design), aria-busy="true" when she's streaming a reply (so screen readers know to wait), and aria-live="polite" on the new-message announcement region (so a screen reader user is told when her reply lands). That's it. Everything else is native — <button>, <label>, <a>, semantic roles by markup choice. Small ARIA surface, fewer places for state to drift out of sync.