ARIA Roles
ARIA (Accessible Rich Internet Applications) roles help screen readers understand custom components.
Landmark Roles
<!-- Implicit landmarks (use native elements) -->
<header> <!-- role="banner" -->
<nav> <!-- role="navigation" -->
<main> <!-- role="main" -->
<aside> <!-- role="complementary" -->
<footer> <!-- role="contentinfo" -->
<!-- Explicit landmarks (when native elements don't fit) -->
<div role="search">
<input type="search" aria-label="Search">
</div>
<div role="form" aria-label="Contact form">
<!-- form content -->
</div>
Widget Roles
<!-- Tab interface -->
<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel1">Tab 1</button>
<button role="tab" aria-selected="false" aria-controls="panel2">Tab 2</button>
</div>
<div role="tabpanel" id="panel1">Content 1</div>
<div role="tabpanel" id="panel2" hidden>Content 2</div>
<!-- Dialog -->
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
<h2 id="dialog-title">Confirm Action</h2>
<p>Are you sure?</p>
<button>Yes</button>
<button>No</button>
</div>
<!-- Alert -->
<div role="alert">
Error: Invalid email address
</div>
<!-- Progress -->
<div role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
50% complete
</div>
Common ARIA Attributes
| Attribute | Purpose | Example |
|---|---|---|
aria-label |
Label for element | aria-label="Close" |
aria-labelledby |
ID of label element | aria-labelledby="title" |
aria-describedby |
ID of description | aria-describedby="help-text" |
aria-hidden |
Hide from screen readers | aria-hidden="true" |
aria-expanded |
Expanded state | aria-expanded="false" |
aria-selected |
Selected state | aria-selected="true" |
aria-checked |
Checked state | aria-checked="mixed" |
aria-disabled |
Disabled state | aria-disabled="true" |
aria-required |
Required field | aria-required="true" |
aria-live |
Live region | aria-live="polite" |
ARIA Best Practices
<!-- ❌ Don't use ARIA when native HTML works -->
<div role="button" onclick="...">Click</div>
<!-- ✅ Use native button -->
<button onclick="...">Click</button>
<!-- ❌ Don't add ARIA to semantic elements -->
<nav aria-label="navigation">...</nav>
<!-- ✅ Use aria-label only when needed -->
<nav aria-label="Main">...</nav>
<!-- ✅ Use aria-labelledby for complex labels -->
<section aria-labelledby="section-title">
<h2 id="section-title">Section Title</h2>
<p>Content...</p>
</section>
Alt Text
Alternative text makes images accessible to screen readers and when images fail to load.
Alt Text Rules
<!-- ❌ Bad alt text -->
<img src="photo.jpg" alt="">
<img src="photo.jpg" alt="image">
<img src="photo.jpg" alt="photo.jpg">
<img src="photo.jpg" alt="photo of a dog sitting on a chair in a park on a sunny day with trees in the background and a fence">
<!-- ✅ Good alt text -->
<img src="dog.jpg" alt="Golden retriever sitting in park">
<img src="chart.png" alt="Sales increased 25% in Q4">
<img src="logo.png" alt="">
When to Use Empty Alt
<!-- Decorative images: empty alt -->
<img src="decoration.png" alt="">
<!-- Functional images: descriptive alt -->
<img src="search-icon.png" alt="Search">
<!-- Informative images: describe content -->
<img src="infographic.png" alt="Infographic showing 5 steps to clean code">
<!-- Images with text: include all text -->
<img src="banner.png" alt="Sale! 50% off all items">
Complex Images
<!-- Simple image with long description -->
<img src="chart.png" alt="Q4 sales chart" aria-describedby="chart-desc">
<div id="chart-desc">
<p>Sales chart showing:
- October: $10,000
- November: $15,000
- December: $20,000</p>
</div>
<!-- Image in figure -->
<figure>
<img src="diagram.png" alt="Architecture diagram">
<figcaption>Figure 1: System architecture showing client-server communication</figcaption>
</figure>
Alt Text for Different Contexts
| Image Type | Alt Text Approach |
|---|---|
| Informative | Describe the content |
| Decorative | Empty alt (alt="") |
| Functional | Describe the function |
| Text in image | Include all text |
| Complex | Use aria-describedby |
| Group | Alt for main + descriptions for each |
Testing Alt Text
<!-- Test: Would this make sense read aloud? -->
<img src="photo.jpg" alt="Photo"> <!-- ❌ No -->
<img src="photo.jpg" alt="Team meeting in conference room"> <!-- ✅ Yes -->
<!-- Test: Is it concise? -->
<img src="chart.png" alt="Sales chart"> <!-- Too vague -->
<img src="chart.png" alt="Q4 sales up 25%"> <!-- ✅ Good -->
Accessible Forms
Accessible forms ensure all users can interact with form controls.
Labels are Essential
<!-- ❌ No label (inaccessible) -->
<input type="email" placeholder="Email">
<!-- ✅ Explicit label (best) -->
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<!-- ✅ Implicit label -->
<label>
Email:
<input type="email" name="email">
</label>
<!-- ✅ aria-label (when visual label isn't possible) -->
<input type="email" aria-label="Email address">
Required Fields
<!-- Visual indicator + aria-required -->
<label for="name">
Name <span aria-hidden="true">*</span>
</label>
<input type="text" id="name" name="name" required aria-required="true">
<!-- Required with description -->
<label for="password">Password</label>
<input type="password" id="password" required aria-required="true" aria-describedby="password-req">
<p id="password-req">Must be at least 8 characters</p>
Error Messages
<label for="email">Email</label>
<input type="email" id="email" aria-invalid="true" aria-describedby="email-error">
<p id="email-error" role="alert">Please enter a valid email address</p>
<!-- Live region for dynamic errors -->
<div aria-live="polite">
<!-- Errors announced to screen readers -->
</div>
Fieldsets and Legends
<!-- Group related fields -->
<fieldset>
<legend>Personal Information</legend>
<label for="name">Name:</label>
<input type="text" id="name">
<label for="email">Email:</label>
<input type="email" id="email">
</fieldset>
<!-- Radio button groups -->
<fieldset>
<legend>Preferred contact method</legend>
<input type="radio" id="contact-email" name="contact" value="email">
<label for="contact-email">Email</label>
<input type="radio" id="contact-phone" name="contact" value="phone">
<label for="contact-phone">Phone</label>
</fieldset>
Form Instructions
<!-- Provide clear instructions -->
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-help">
<p id="password-help">Must be at least 8 characters with one uppercase letter</p>
<!-- Required field indicator -->
<p><span aria-hidden="true">*</span> indicates required field</p>
<!-- Error summary -->
<div role="alert" aria-live="assertive">
<h3>Please fix the following errors:</h3>
<ul>
<li><a href="#email">Email is required</a></li>
<li><a href="#password">Password must be 8+ characters</a></li>
</ul>
</div>
Practice Problems
Create a reusable React component implementing HTML Accessibility. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for HTML Accessibility using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize HTML Accessibility 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 analysisQuiz
1. What is the first rule of ARIA?
2. When should you use alt="" (empty alt)?
3. Why are labels important for forms?
4. What does aria-live="polite" do?
Flashcards
Question
What is the first rule of ARIA?
Click to reveal answer
Answer
If you can use a native HTML element with the semantics you need, do so. ARIA is a fallback, not a first choice.
Question
What is the purpose of alt text?
Click to reveal answer
Answer
Provides alternative text for screen readers and when images fail to load. Should describe the image content or be empty for decorative images.
Question
How do you make forms accessible?
Click to reveal answer
Answer
Use labels for all controls, provide clear instructions, indicate required fields, and use fieldset/legend for groups.
Question
What does aria-hidden="true" do?
Click to reveal answer
Answer
Hides the element from screen readers. Use for decorative content that doesn't add meaning.
Question
What is HTML Accessibility?
Click to reveal answer
Answer
HTML Accessibility is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Use native HTML elements before ARIA
- 2.Always provide alt text for images
- 3.Labels are essential for form accessibility
- 4.Use ARIA roles to enhance semantics
- 5.Test with screen readers and keyboard navigation
Interview Tips
- •Know the first rule of ARIA
- •Understand when to use empty alt vs descriptive alt
- •Be familiar with common ARIA attributes
- •Know how to make forms accessible
Cheat Sheet
HTML Accessibility Cheat Sheet
ARIA Principles:
- Use native HTML first
- Don't break native semantics
- All interactive elements must be keyboard accessible
- Don't use role="presentation" or aria-hidden="true" on focusable elements
Landmarks:
- header → banner
- nav → navigation
- main → main
- aside → complementary
- footer → contentinfo
Key ARIA Attributes:
- aria-label: Label for element
- aria-labelledby: ID of label
- aria-describedby: ID of description
- aria-hidden: Hide from screen readers
- aria-expanded: Expanded state
- aria-live: Announce changes
Forms:
- Always use labels
- Indicate required fields
- Provide clear error messages
- Use fieldset/legend for groups
Images:
- Informative: descriptive alt
- Decorative: empty alt
- Functional: describe function