WAI-ARIA Attributes Table
| Attribute | What it communicates | Field note |
|---|---|---|
aria-label | Accessible name for non-text elements | Use when visible text is absent - icon buttons |
aria-labelledby | Reference another element as the name | IDREF: points to visible text - stronger than aria-label |
aria-describedby | Reference supplementary description | IDREF: tooltips, hints, error messages |
aria-expanded | Toggle state (true/false) | For collapsible panels, accordions, dropdown triggers |
aria-hidden="true" | Hide from screen readers | For decorative content; does NOT remove from tab order |
aria-live="polite" | Announce dynamic changes | polite waits; assertive interrupts - use sparingly |
role="alert" | Important time-sensitive message | Implicit aria-live=assertive - already announced |
aria-current="page" | Current item in a set | Better than .active class - communicates state, not just style |
WAI-ARIA (Web Accessibility Initiative - Accessible Rich Internet Applications) is the attribute vocabulary that makes custom widgets understandable to screen readers. The table below is the working eight - the attributes that cover real accessibility work, from icon button names to dynamic update announcements.
Bottom line: the first rule of ARIA is don't use ARIA - native HTML elements (button, nav, label) have built-in semantics that ARIA attributes cannot replicate. ARIA is for when native HTML runs out: custom widgets, dynamic updates, and states that HTML has no element for. The second rule: never change native behaviour with ARIA when a native element exists.
The honest part: ARIA attributes are promises to screen reader users. aria-expanded="true" tells the user a panel is open - if the panel doesn't actually open, the ARIA is lying. Every ARIA attribute is a contract with the user, and broken contracts are worse than no ARIA at all (which at least has no false expectations).
How to use
- Start with native HTML: if a button, nav, or label element exists, use it before reaching for ARIA.
- Add ARIA for the gaps: aria-label on icon buttons, aria-expanded on disclosure triggers, aria-live on dynamic updates.
- Test with a screen reader: VoiceOver (Cmd+F5 on Mac) or NVDA (free on Windows) - what you hear is what the user gets.
Frequently asked questions
What is the first rule of ARIA?
Don't use ARIA. Native HTML elements (button, nav, label, table) have built-in semantics, keyboard support and screen reader announcements that ARIA attributes cannot replicate. A <div role="button"> requires you to manually add keyboard support (Enter and Space), focus management, and role announcement - all of which <button> gives you for free. ARIA is for gaps, not replacements.
What is the difference between aria-label and aria-labelledby?
aria-label takes a string directly (aria-label="Close dialog") - use it when there is no visible text to reference. aria-labelledby takes an ID reference (aria-labelledby="heading-id") - use it when visible text already exists, because screen readers announce the REFERENCED element's text, which is more robust (updates automatically when the visible text changes). The rule: aria-labelledby when visible text exists, aria-label when it doesn't.
When should I use aria-live and what does polite vs assertive mean?
aria-live announces dynamically inserted content to screen readers without them needing to re-focus. polite waits for the current announcement to finish before reading the new content (chat messages, form success); assertive INTERRUPTS immediately (error alerts, time-critical updates). The honest advice: use polite 95% of the time, assertive only for genuinely urgent messages, and never put aria-live on content that changes every keystroke (the screen reader will read every character).
What is the difference between aria-hidden and display: none?
display: none removes the element from BOTH rendering AND the accessibility tree - screen readers cannot see it at all. aria-hidden="true" removes it from the accessibility tree but it still occupies visual space and participates in layout. The trap: aria-hidden on a focusable element is a failure - the element is invisible to screen readers but still in the tab order, creating a 'ghost' tab stop. Use hidden or display: none when you want it fully gone.