Skip to content
intermediatePhase 39 · Accessibility

ARIA

Use ARIA roles, states, and properties to enhance accessibility.

1h
0 problems
Topic Progress0%

ARIA Roles

ARIA Roles

Define the purpose of elements for assistive technologies.

Landmark Roles

<div role="banner">{/* header */}</div>
<nav role="navigation">{/* nav */}</nav>
<main role="main">{/* main */}</main>
<aside role="complementary">{/* aside */}</aside>
<footer role="contentinfo">{/* footer */}</footer>

Widget Roles

// Button
<button role="button">Click me</button>
<div role="button" tabIndex={0}>Click me</div>

// Tab
<div role="tablist">
  <button role="tab" aria-selected="true">Tab 1</button>
  <button role="tab" aria-selected="false">Tab 2</button>
</div>
<div role="tabpanel">Panel 1</div>

// Menu
<ul role="menu">
  <li role="menuitem">Option 1</li>
  <li role="menuitem">Option 2</li>
</ul>

// Dialog
<div role="dialog" aria-modal="true">
  <h2>Dialog Title</h2>
  <p>Dialog content</p>
</div>

Live Region Roles

// Status updates
<div role="status" aria-live="polite">
  {loading ? 'Loading...' : `${count} results`}
</div>

// Alert messages
<div role="alert">
  {error && 'Error: Please fix the issues below'}
</div>

// Timer
<div role="timer" aria-live="off">
  {formatTime(remaining)}
</div>

Role Usage Guide

Role Use When
button Clickable element
link Navigates to URL
tab Tab in tabbed interface
menuitem Item in menu
dialog Modal or popup
alert Urgent message
status Status update
progressbar Loading indicator

ARIA States

ARIA States

Indicate current state of elements.

Common States

// Expanded (accordion)
<button aria-expanded={isOpen}>
  {isOpen ? 'Collapse' : 'Expand'}
</button>

// Selected (tabs)
<button role="tab" aria-selected={isActive}>
  Tab {index}
</button>

// Checked (checkbox)
<input type="checkbox" aria-checked={isChecked} />
<div role="checkbox" aria-checked="mixed">Partial</div>

// Disabled
<button disabled aria-disabled="true">Cannot click</button>

// Hidden
<div hidden aria-hidden="true">Not visible</div>

// Current (navigation)
<a href="/current" aria-current="page">Current Page</a>

Form States

// Required
<input required aria-required="true" />

// Invalid
<input
  aria-invalid={hasError}
  aria-describedby="error-message"
/>
{hasError && <span id="error-message">Error text</span>}

// Error
<div role="alert" aria-live="assertive">
  {errorMessage}
</div>

// Loading
<button aria-busy={isLoading} disabled={isLoading}>
  {isLoading ? 'Loading...' : 'Submit'}
</button>

Composite States

// Tree view
<div role="treeitem" aria-expanded={isExpanded} aria-selected={isSelected}>
  {label}
</div>

// Grid cell
<div role="gridcell" aria-selected={isActive}>
  {value}
</div>

// Listbox option
<div role="option" aria-selected={isSelected} aria-checked={isChecked}>
  {option.label}
</div>

ARIA Properties

ARIA Properties

Provide additional information about elements.

Labeling

// aria-label (visible text not suitable)
<button aria-label="Close dialog">×</button>
<input aria-label="Search products" />

// aria-labelledby (reference to visible text)
<h2 id="section-title">Section Title</h2>
<div aria-labelledby="section-title">Content</div>

// aria-describedby (additional description)
<input
  aria-describedby="password-hint"
/>
<p id="password-hint">Must be 8+ characters</p>

// aria-errormessage (error text)
<input
  aria-invalid="true"
  aria-errormessage="email-error"
/>
<span id="email-error">Invalid email</span>

Relationships

// Controls (element this controls)
<button aria-controls="menu-1">Menu</button>
<ul id="menu-1" role="menu">...</ul>

// Labelledby (label reference)
<div role="dialog" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Title</h2>
</div>

// Describedby (description reference)
<input aria-describedby="input-desc" />
<span id="input-desc">Help text</span>

// Owns (owns another element)
<div aria-owns="popup-1">Trigger</div>
<div id="popup-1" role="tooltip">Popup content</div>

Live Regions

// Polite (waits for user to finish)
<div aria-live="polite">
  {statusMessage}
</div>

// Assertive (interrupts immediately)
<div aria-live="assertive">
  {errorMessage}
</div>

// Off (no announcement)
<div aria-live="off">
  {debugInfo}
</div>

// Atomic (announce entire region)
<div aria-live="polite" aria-atomic="true">
  {announcement}
</div>

// Relevant (what changes to announce)
<div aria-live="polite" aria-relevant="additions removals">
  {list.map(item => <Item key={item.id} item={item} />)}
</div>

Drag and Drop

<div
  draggable
  aria-grabbed={isGrabbed}
  aria-dropeffect="copy"
>
  Draggable item
</div>

<div aria-dropeffect="copy" aria-dropactive={isOver}>
  Drop target
</div>
},
{
  "id": "ch4",
  "title": "When NOT to Use ARIA",
  "content": "## When NOT to Use ARIA

First rule of ARIA: Don't use ARIA if you can use native HTML.

Use Native HTML Instead

// ❌ Bad: ARIA button
<div role="button" tabIndex={0} onClick={handleClick}>
  Click me
</div>

// ✅ Good: Native button
<button onClick={handleClick}>Click me</button>

// ❌ Bad: ARIA navigation
<div role="navigation" aria-label="Main">
  <a href="/">Home</a>
</div>

// ✅ Good: Native nav
<nav aria-label="Main">
  <a href="/">Home</a>
</nav>

// ❌ Bad: ARIA checkbox
<div
  role="checkbox"
  aria-checked={checked}
  onClick={() => setChecked(!checked)}
  onKeyDown={(e) => {
    if (e.key === ' ') setChecked(!checked);
  }}
  tabIndex={0}
/>

// ✅ Good: Native checkbox
<input
  type="checkbox"
  checked={checked}
  onChange={(e) => setChecked(e.target.checked)}
/>

ARIA Myths

  1. Myth: More ARIA = more accessible
    Truth: Wrong ARIA is worse than no ARIA

  2. Myth: ARIA fixes everything
    Truth: Semantic HTML is better

  3. Myth: ARIA is required
    Truth: Often not needed with proper HTML

When ARIA IS Needed

  • Custom widgets with no HTML equivalent
  • Complex interactive patterns
  • Dynamic content updates
  • Enhancing semantic HTML

Testing ARIA

# Run accessibility tests
npm run test:a11y

# Use axe-core
npx axe http://localhost:3000

# Use lighthouse
npx lighthouse http://localhost:3000 --only-categories=accessibility

Practice Problems

0/3solved
Build ARIA Component

Create a reusable React component implementing ARIA. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
ARIA Testing

Write unit and integration tests for ARIA using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
ARIA Performance

Optimize ARIA for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is the first rule of ARIA?

Question 1 options

2. When should you use aria-live="assertive"?

Question 2 options

3. What is the primary purpose of ARIA?

Question 3 options

4. What is a common mistake when implementing ARIA?

Question 4 options

Flashcards

Question

What is ARIA?

Answer

Accessible Rich Internet Accessibility - attributes that make web content more accessible.

Question

What are ARIA roles?

Answer

Attributes that define the purpose of an element for assistive technologies.

Question

What are ARIA states?

Answer

Attributes that indicate the current state of an element (expanded, selected, checked).

Question

When should you NOT use ARIA?

Answer

When native HTML elements provide the same functionality and semantics.

Question

What is ARIA?

Answer

ARIA is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Don't use ARIA if native HTML works
  • 2.ARIA roles define element purpose
  • 3.ARIA states indicate current condition
  • 4.ARIA properties provide additional info
  • 5.Test with screen readers and axe-core

Interview Tips

  • Explain the first rule of ARIA
  • Discuss when ARIA is needed vs not needed
  • Know common ARIA attributes

Cheat Sheet

ARIA Cheat Sheet

First Rule

Don't use ARIA if native HTML works.

Common Roles

  • button, link, tab, menuitem
  • dialog, alert, status
  • tablist, tabpanel, treeitem

Common States

  • aria-expanded
  • aria-selected
  • aria-checked
  • aria-disabled
  • aria-invalid

Common Properties

  • aria-label
  • aria-labelledby
  • aria-describedby
  • aria-live
  • aria-hidden