# 600 Reference 50513148

> Web Accessibility Reference

- Skill: `tools-only/600-reference-50513148` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/600-reference-50513148`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/600-reference-50513148/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tools-only/600-reference-50513148

---

# Web Accessibility Reference

**Comprehensive reference for WCAG 2.1/2.2, ARIA, semantic HTML, keyboard navigation, screen readers, and accessible patterns**

**Version**: 1.0
**Last Updated**: 2025-10-27
**Lines**: ~1,900

---

## Table of Contents

1. [WCAG Guidelines](#wcag-guidelines)
2. [ARIA Reference](#aria-reference)
3. [Semantic HTML](#semantic-html)
4. [Keyboard Navigation](#keyboard-navigation)
5. [Screen Reader Support](#screen-reader-support)
6. [Focus Management](#focus-management)
7. [Color and Contrast](#color-and-contrast)
8. [Forms and Validation](#forms-and-validation)
9. [Dynamic Content](#dynamic-content)
10. [Testing Tools and Workflows](#testing-tools-and-workflows)
11. [Common Accessible Patterns](#common-accessible-patterns)
12. [Mobile Accessibility](#mobile-accessibility)
13. [Anti-Patterns and Fixes](#anti-patterns-and-fixes)

---

## WCAG Guidelines

### WCAG Principles (POUR)

**Perceivable**: Information and user interface components must be presentable to users in ways they can perceive.

**Operable**: User interface components and navigation must be operable.

**Understandable**: Information and the operation of user interface must be understandable.

**Robust**: Content must be robust enough that it can be interpreted reliably by a wide variety of user agents, including assistive technologies.

### Conformance Levels

**Level A (Minimum)**
- Basic web accessibility features
- Must be satisfied
- Example: Text alternatives for non-text content

**Level AA (Recommended)**
- Target for most websites and applications
- Addresses the biggest and most common barriers
- Example: Color contrast ratio of 4.5:1

**Level AAA (Enhanced)**
- Highest level of accessibility
- Not always achievable for all content
- Example: Color contrast ratio of 7:1

**Best Practice**: Target WCAG 2.1 Level AA for production applications.

---

### WCAG 2.1 Level A Guidelines

#### 1.1.1 Non-text Content (A)

All non-text content has a text alternative.

```html
<!-- ❌ Bad -->
<img src="logo.png">

<!-- ✅ Good -->
<img src="logo.png" alt="Acme Corporation Logo">

<!-- ✅ Good: Decorative -->
<img src="decoration.png" alt="">

<!-- ✅ Good: Complex content -->
<img src="chart.png" alt="Sales data for Q4 2025" longdesc="chart-description.html">
```

#### 1.2.1 Audio-only and Video-only (A)

Provide an alternative for time-based media.

```html
<!-- Video alternative -->
<video controls>
  <source src="video.mp4" type="video/mp4">
  <track kind="descriptions" src="descriptions.vtt" srclang="en" label="English">
</video>
<details>
  <summary>Transcript</summary>
  <p>Full text transcript of the video...</p>
</details>
```

#### 1.2.2 Captions (Prerecorded) (A)

Captions are provided for all prerecorded audio content.

```html
<video controls>
  <source src="video.mp4" type="video/mp4">
  <track kind="captions" src="captions-en.vtt" srclang="en" label="English" default>
  <track kind="captions" src="captions-es.vtt" srclang="es" label="Español">
</video>
```

#### 1.3.1 Info and Relationships (A)

Information, structure, and relationships can be programmatically determined.

```html
<!-- ❌ Bad: Visual structure only -->
<div>
  <div style="font-size: 24px; font-weight: bold;">Section Title</div>
  <div>Content here</div>
</div>

<!-- ✅ Good: Semantic structure -->
<section>
  <h2>Section Title</h2>
  <p>Content here</p>
</section>
```

#### 1.3.2 Meaningful Sequence (A)

The reading order of content is meaningful.

```html
<!-- ❌ Bad: Visual order != DOM order -->
<div style="display: flex; flex-direction: column-reverse;">
  <div>Second (appears first)</div>
  <div>First (appears second)</div>
</div>

<!-- ✅ Good: DOM order matches visual order -->
<div style="display: flex; flex-direction: column;">
  <div>First</div>
  <div>Second</div>
</div>
```

#### 1.3.3 Sensory Characteristics (A)

Instructions don't rely solely on sensory characteristics.

```html
<!-- ❌ Bad: Shape-only reference -->
<p>Click the round button to continue</p>

<!-- ✅ Good: Multiple characteristics -->
<p>Click the "Continue" button (round, blue) to proceed</p>

<!-- ✅ Better: Direct reference -->
<button id="continue-btn">Continue</button>
```

#### 1.4.1 Use of Color (A)

Color is not the only visual means of conveying information.

```html
<!-- ❌ Bad: Color only -->
<span style="color: red;">Error</span>
<span style="color: green;">Success</span>

<!-- ✅ Good: Color + icon + text -->
<span style="color: red;">
  <svg aria-hidden="true"><use href="#error-icon"/></svg>
  Error: Field is required
</span>
```

#### 1.4.2 Audio Control (A)

If audio plays automatically for more than 3 seconds, provide a mechanism to pause/stop it.

```html
<audio id="background-music" autoplay loop>
  <source src="music.mp3" type="audio/mp3">
</audio>
<button onclick="document.getElementById('background-music').pause()">
  Pause Background Music
</button>
```

#### 2.1.1 Keyboard (A)

All functionality is available from a keyboard.

```html
<!-- ❌ Bad: Mouse-only interaction -->
<div onmouseover="showTooltip()">Hover me</div>

<!-- ✅ Good: Keyboard accessible -->
<button
  onmouseenter="showTooltip()"
  onfocus="showTooltip()"
  onmouseleave="hideTooltip()"
  onblur="hideTooltip()"
>
  Show tooltip
</button>
```

#### 2.1.2 No Keyboard Trap (A)

Keyboard focus can be moved away from a component using only the keyboard.

```javascript
// ✅ Good: Allow escape from modal
function Modal({ onClose }) {
  useEffect(() => {
    const handleEscape = (e) => {
      if (e.key === 'Escape') onClose();
    };
    document.addEventListener('keydown', handleEscape);
    return () => document.removeEventListener('keydown', handleEscape);
  }, [onClose]);
}
```

#### 2.1.4 Character Key Shortcuts (A - WCAG 2.1)

If a keyboard shortcut uses only character keys, then at least one of the following is true:
- Can be turned off
- Can be remapped
- Only active when component has focus

```javascript
// ✅ Good: Scoped shortcuts
function Editor() {
  const editorRef = useRef();

  const handleKeyPress = (e) => {
    if (!editorRef.current.contains(document.activeElement)) {
      return; // Only active when editor has focus
    }

    if (e.key === 's' && e.ctrlKey) {
      e.preventDefault();
      save();
    }
  };

  return <div ref={editorRef} onKeyDown={handleKeyPress}>...</div>;
}
```

#### 2.2.1 Timing Adjustable (A)

User can turn off, adjust, or extend time limits.

```html
<div role="alert">
  <p>Your session will expire in 60 seconds.</p>
  <button onclick="extendSession()">Extend Session</button>
</div>
```

#### 2.2.2 Pause, Stop, Hide (A)

Provide mechanism to pause, stop, or hide moving, blinking, or auto-updating content.

```html
<div class="carousel">
  <button aria-label="Pause carousel">⏸</button>
  <button aria-label="Play carousel">▶</button>
  <!-- Carousel items -->
</div>
```

#### 2.3.1 Three Flashes or Below Threshold (A)

Content does not flash more than three times per second.

```css
/* ❌ Bad: Rapid flashing */
@keyframes flash {
  0%, 50% { opacity: 1; }
  25%, 75% { opacity: 0; }
}

/* ✅ Good: Slow transition */
@keyframes fade {
  0% { opacity: 0; }
  100% { opacity: 1; }
}
```

#### 2.4.1 Bypass Blocks (A)

A mechanism is available to bypass blocks of content that are repeated.

```html
<!-- Skip navigation link -->
<a href="#main-content" class="skip-link">Skip to main content</a>

<nav><!-- Navigation --></nav>

<main id="main-content">
  <!-- Main content -->
</main>

<style>
  .skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
  }

  .skip-link:focus {
    left: 0;
    top: 0;
    z-index: 9999;
  }
</style>
```

#### 2.4.2 Page Titled (A)

Web pages have titles that describe topic or purpose.

```html
<head>
  <title>User Profile - Settings | Acme Corp</title>
</head>
```

#### 2.4.3 Focus Order (A)

Focusable components receive focus in an order that preserves meaning.

```html
<!-- ✅ Good: Logical focus order -->
<form>
  <label for="first-name">First Name</label>
  <input id="first-name" type="text">

  <label for="last-name">Last Name</label>
  <input id="last-name" type="text">

  <button type="submit">Submit</button>
</form>
```

#### 2.4.4 Link Purpose (In Context) (A)

The purpose of each link can be determined from the link text alone.

```html
<!-- ❌ Bad: Non-descriptive -->
<a href="report.pdf">Click here</a>

<!-- ✅ Good: Descriptive -->
<a href="report.pdf">Download 2025 Annual Report (PDF, 2MB)</a>

<!-- ✅ Good: Context -->
<h2>2025 Annual Report</h2>
<p>Our annual financial summary.</p>
<a href="report.pdf">Download (PDF, 2MB)</a>
```

#### 2.5.1 Pointer Gestures (A - WCAG 2.1)

All functionality that uses multipoint or path-based gestures can be operated with a single pointer.

```javascript
// ✅ Good: Single-pointer alternative
function ImageZoom() {
  return (
    <div>
      {/* Pinch to zoom alternative */}
      <button onClick={zoomIn}>Zoom In</button>
      <button onClick={zoomOut}>Zoom Out</button>
      <img src="photo.jpg" alt="Landscape" />
    </div>
  );
}
```

#### 2.5.2 Pointer Cancellation (A - WCAG 2.1)

For functionality that can be operated using a single pointer:
- No down-event trigger
- Abort or undo available
- Up-event reverses down-event
- Essential to trigger on down-event

```javascript
// ✅ Good: Up-event trigger
<button onPointerUp={handleClick}>Click me</button>

// ❌ Bad: Down-event trigger (avoid unless essential)
<button onPointerDown={handleClick}>Click me</button>
```

#### 2.5.3 Label in Name (A - WCAG 2.1)

The accessible name contains the visible text label.

```html
<!-- ❌ Bad: Mismatch -->
<button aria-label="Submit form">Send</button>

<!-- ✅ Good: Match -->
<button aria-label="Send message">Send</button>

<!-- ✅ Better: No aria-label needed -->
<button>Send</button>
```

#### 2.5.4 Motion Actuation (A - WCAG 2.1)

Functionality triggered by device motion can also be operated by user interface components.

```javascript
// ✅ Good: Motion + button alternative
function ShakeToRefresh() {
  return (
    <>
      {/* Shake to refresh */}
      <button onClick={refresh}>Refresh</button>
    </>
  );
}
```

#### 3.1.1 Language of Page (A)

The default human language of each page can be programmatically determined.

```html
<html lang="en">
  <head>
    <title>Welcome</title>
  </head>
  <body>
    <p>This is English content.</p>
    <p lang="es">Este es contenido en español.</p>
  </body>
</html>
```

#### 3.2.1 On Focus (A)

When a component receives focus, it does not initiate a change of context.

```javascript
// ❌ Bad: Auto-submit on focus
<input onFocus={submitForm} />

// ✅ Good: Explicit action required
<input />
<button onClick={submitForm}>Submit</button>
```

#### 3.2.2 On Input (A)

Changing the setting of a user interface component does not automatically cause a change of context.

```javascript
// ❌ Bad: Auto-submit on change
<select onChange={submitForm}>
  <option>Option 1</option>
  <option>Option 2</option>
</select>

// ✅ Good: Explicit action
<select onChange={handleChange}>
  <option>Option 1</option>
  <option>Option 2</option>
</select>
<button onClick={submitForm}>Apply</button>
```

#### 3.3.1 Error Identification (A)

If an input error is automatically detected, the item in error is identified and described to the user in text.

```html
<label for="email">Email</label>
<input
  id="email"
  type="email"
  aria-invalid="true"
  aria-describedby="email-error"
>
<span id="email-error" role="alert">
  Error: Please enter a valid email address
</span>
```

#### 3.3.2 Labels or Instructions (A)

Labels or instructions are provided when content requires user input.

```html
<label for="password">
  Password
  <span aria-label="required">*</span>
</label>
<input
  id="password"
  type="password"
  aria-describedby="password-hint"
  required
>
<p id="password-hint">
  Must be at least 8 characters with one uppercase letter and one number
</p>
```

#### 4.1.1 Parsing (A)

In content implemented using markup languages, elements have complete start and end tags, are nested correctly, do not contain duplicate attributes, and IDs are unique.

```html
<!-- ❌ Bad: Duplicate IDs -->
<div id="content">First</div>
<div id="content">Second</div>

<!-- ✅ Good: Unique IDs -->
<div id="content-1">First</div>
<div id="content-2">Second</div>
```

#### 4.1.2 Name, Role, Value (A)

For all user interface components, the name and role can be programmatically determined.

```html
<!-- ❌ Bad: No role/name -->
<div onclick="toggleMenu()">Menu</div>

<!-- ✅ Good: Proper role/name -->
<button
  aria-label="Toggle menu"
  aria-expanded="false"
  aria-controls="menu"
  onclick="toggleMenu()"
>
  Menu
</button>
```

---

### WCAG 2.1 Level AA Guidelines

#### 1.2.4 Captions (Live) (AA)

Captions are provided for all live audio content.

```html
<!-- Live streaming with captions -->
<video controls>
  <source src="livestream.m3u8" type="application/x-mpegURL">
  <track kind="captions" src="live-captions.vtt" srclang="en" label="English">
</video>
```

#### 1.2.5 Audio Description (Prerecorded) (AA)

Audio description is provided for all prerecorded video content.

```html
<video controls>
  <source src="video.mp4" type="video/mp4">
  <track kind="descriptions" src="audio-description.vtt" srclang="en" label="Audio Description">
</video>
```

#### 1.3.4 Orientation (AA - WCAG 2.1)

Content does not restrict its view and operation to a single display orientation.

```css
/* ✅ Good: Support both orientations */
@media (orientation: portrait) {
  .content { flex-direction: column; }
}

@media (orientation: landscape) {
  .content { flex-direction: row; }
}
```

#### 1.3.5 Identify Input Purpose (AA - WCAG 2.1)

The purpose of each input field can be programmatically determined when the input field serves a purpose from the autocomplete list.

```html
<label for="email">Email</label>
<input
  id="email"
  type="email"
  autocomplete="email"
  name="email"
>

<label for="street">Street Address</label>
<input
  id="street"
  type="text"
  autocomplete="street-address"
  name="street"
>
```

#### 1.4.3 Contrast (Minimum) (AA)

Text has a contrast ratio of at least 4.5:1 (or 3:1 for large text).

```css
/* ❌ Bad: Insufficient contrast (2.8:1) */
.text {
  color: #999999;
  background-color: #ffffff;
}

/* ✅ Good: Sufficient contrast (4.5:1) */
.text {
  color: #767676;
  background-color: #ffffff;
}

/* ✅ Good: Large text (3:1) */
.large-text {
  font-size: 18pt;
  color: #949494;
  background-color: #ffffff;
}
```

#### 1.4.4 Resize Text (AA)

Text can be resized up to 200% without loss of content or functionality.

```css
/* ✅ Good: Use relative units */
.text {
  font-size: 1rem; /* Not px */
  line-height: 1.5;
}

/* ✅ Good: Responsive containers */
.container {
  max-width: 100%;
  overflow: auto;
}
```

#### 1.4.5 Images of Text (AA)

Use actual text rather than images of text.

```html
<!-- ❌ Bad: Text in image -->
<img src="heading.png" alt="Welcome to our site">

<!-- ✅ Good: Actual text -->
<h1>Welcome to our site</h1>
```

#### 1.4.10 Reflow (AA - WCAG 2.1)

Content can be presented without loss of information or functionality at 320 CSS pixels width.

```css
/* ✅ Good: Responsive design */
.content {
  max-width: 100%;
  word-wrap: break-word;
}

@media (max-width: 320px) {
  .sidebar {
    display: block;
    width: 100%;
  }
}
```

#### 1.4.11 Non-text Contrast (AA - WCAG 2.1)

Visual presentation of UI components and graphical objects have a contrast ratio of at least 3:1.

```css
/* ✅ Good: Button with sufficient contrast */
.button {
  background-color: #0066cc; /* 3:1 against white */
  border: 2px solid #0052a3;
  color: #ffffff;
}

.button:focus {
  outline: 2px solid #0052a3; /* 3:1 against background */
}
```

#### 1.4.12 Text Spacing (AA - WCAG 2.1)

No loss of content or functionality when text spacing is adjusted.

```css
/* User may apply these adjustments */
* {
  line-height: 1.5 !important;
  letter-spacing: 0.12em !important;
  word-spacing: 0.16em !important;
}

p {
  margin-bottom: 2em !important;
}

/* ✅ Good: Design accommodates text spacing */
.container {
  padding: 1em;
  min-height: fit-content;
}
```

#### 1.4.13 Content on Hover or Focus (AA - WCAG 2.1)

Additional content triggered by hover or focus is dismissible, hoverable, and persistent.

```javascript
// ✅ Good: Accessible tooltip
function Tooltip({ trigger, content }) {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      <button
        onMouseEnter={() => setIsVisible(true)}
        onFocus={() => setIsVisible(true)}
        onMouseLeave={() => setIsVisible(false)}
        onBlur={() => setIsVisible(false)}
        aria-describedby="tooltip"
      >
        {trigger}
      </button>
      {isVisible && (
        <div
          id="tooltip"
          role="tooltip"
          onMouseEnter={() => setIsVisible(true)}
        >
          {content}
        </div>
      )}
    </div>
  );
}
```

#### 2.4.5 Multiple Ways (AA)

More than one way is available to locate a page within a set of pages.

```html
<!-- Navigation menu -->
<nav>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
  </ul>
</nav>

<!-- Search -->
<form role="search">
  <input type="search" aria-label="Search site">
  <button type="submit">Search</button>
</form>

<!-- Site map -->
<a href="/sitemap">Site Map</a>
```

#### 2.4.6 Headings and Labels (AA)

Headings and labels describe topic or purpose.

```html
<!-- ✅ Good: Descriptive headings -->
<h1>User Account Settings</h1>
<h2>Privacy Preferences</h2>
<h3>Email Notifications</h3>

<!-- ✅ Good: Descriptive labels -->
<label for="email-frequency">How often would you like to receive emails?</label>
<select id="email-frequency">
  <option>Daily</option>
  <option>Weekly</option>
  <option>Monthly</option>
</select>
```

#### 2.4.7 Focus Visible (AA)

Keyboard focus indicator is visible.

```css
/* ❌ Bad: Removing focus indicator */
:focus {
  outline: none;
}

/* ✅ Good: Visible focus indicator */
:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

/* ✅ Good: Custom focus style */
button:focus-visible {
  box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.5);
  outline: 2px solid #0066cc;
}
```

#### 3.1.2 Language of Parts (AA)

The human language of each passage or phrase can be programmatically determined.

```html
<p>
  The word <span lang="fr">rendezvous</span> is French.
</p>

<blockquote lang="es">
  <p>La vida es bella.</p>
</blockquote>
```

#### 3.2.3 Consistent Navigation (AA)

Navigational mechanisms that are repeated on multiple pages occur in the same relative order.

```html
<!-- Same navigation on every page -->
<nav aria-label="Main">
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>
```

#### 3.2.4 Consistent Identification (AA)

Components that have the same functionality are identified consistently.

```html
<!-- ✅ Good: Consistent icon usage -->
<button aria-label="Close dialog">
  <svg><use href="#close-icon"/></svg>
</button>

<!-- Same icon/label in different context -->
<button aria-label="Close panel">
  <svg><use href="#close-icon"/></svg>
</button>
```

#### 3.3.3 Error Suggestion (AA)

If an input error is detected and suggestions for correction are known, then the suggestions are provided.

```html
<label for="username">Username</label>
<input
  id="username"
  type="text"
  aria-invalid="true"
  aria-describedby="username-error"
>
<span id="username-error" role="alert">
  Username must be 3-20 characters. Try: johndoe123
</span>
```

#### 3.3.4 Error Prevention (Legal, Financial, Data) (AA)

For pages that cause legal commitments or financial transactions, submissions are reversible, checked, or confirmed.

```html
<form>
  <h2>Payment Details</h2>
  <!-- Form fields -->

  <button type="button" onclick="showConfirmation()">
    Review Order
  </button>
</form>

<div id="confirmation" role="dialog" aria-labelledby="confirm-title">
  <h2 id="confirm-title">Confirm Your Order</h2>
  <!-- Order summary -->
  <button onclick="submitOrder()">Confirm and Pay</button>
  <button onclick="editOrder()">Edit Order</button>
</div>
```

#### 4.1.3 Status Messages (AA - WCAG 2.1)

Status messages can be programmatically determined through role or properties.

```html
<!-- Success message -->
<div role="status" aria-live="polite">
  Your changes have been saved.
</div>

<!-- Error alert -->
<div role="alert" aria-live="assertive">
  Error: Unable to save changes.
</div>

<!-- Progress indicator -->
<div role="status" aria-live="polite" aria-atomic="true">
  Uploading: 45% complete
</div>
```

---

### WCAG 2.1 Level AAA Guidelines (Selected)

#### 1.2.6 Sign Language (Prerecorded) (AAA)

Sign language interpretation is provided for all prerecorded audio content.

#### 1.2.7 Extended Audio Description (Prerecorded) (AAA)

Where pauses in foreground audio are insufficient for audio descriptions to convey the sense of the video, extended audio description is provided.

#### 1.4.6 Contrast (Enhanced) (AAA)

Text has a contrast ratio of at least 7:1 (or 4.5:1 for large text).

```css
/* ✅ AAA: Enhanced contrast (7:1) */
.text {
  color: #595959;
  background-color: #ffffff;
}
```

#### 1.4.8 Visual Presentation (AAA)

For text presentation:
- Width is no more than 80 characters
- Text is not justified
- Line spacing is at least 1.5
- Paragraph spacing is at least 2x line spacing

```css
/* ✅ AAA: Optimal reading */
.text {
  max-width: 80ch;
  line-height: 1.5;
  text-align: left; /* Not justified */
}

.text p {
  margin-bottom: 3em; /* 2x line height */
}
```

#### 2.1.3 Keyboard (No Exception) (AAA)

All functionality is available from a keyboard without exception.

#### 2.4.9 Link Purpose (Link Only) (AAA)

The purpose of each link can be identified from link text alone.

```html
<!-- ✅ AAA: No context needed -->
<a href="report.pdf">Download 2025 Annual Report (PDF, 2MB)</a>
```

#### 2.4.10 Section Headings (AAA)

Section headings are used to organize the content.

```html
<article>
  <h1>Article Title</h1>

  <section>
    <h2>Introduction</h2>
    <p>...</p>
  </section>

  <section>
    <h2>Methods</h2>
    <p>...</p>
  </section>

  <section>
    <h2>Results</h2>
    <p>...</p>
  </section>
</article>
```

---

## ARIA Reference

### ARIA Roles

#### Landmark Roles

**banner**: Site header with logo, site title

```html
<header role="banner">
  <h1>Site Name</h1>
</header>
```

**navigation**: Collection of navigational links

```html
<nav role="navigation" aria-label="Main">
  <ul>
    <li><a href="/">Home</a></li>
  </ul>
</nav>
```

**main**: Main content of the document

```html
<main role="main">
  <article>...</article>
</main>
```

**complementary**: Supporting content (sidebar)

```html
<aside role="complementary">
  <h2>Related Links</h2>
</aside>
```

**contentinfo**: Footer information

```html
<footer role="contentinfo">
  <p>&copy; 2025 Company</p>
</footer>
```

**search**: Search functionality

```html
<form role="search">
  <input type="search" aria-label="Search">
  <button type="submit">Search</button>
</form>
```

**region**: Significant section of content

```html
<section role="region" aria-labelledby="region-title">
  <h2 id="region-title">Special Offers</h2>
</section>
```

**form**: Landmark containing form controls

```html
<form role="form" aria-labelledby="form-title">
  <h2 id="form-title">Contact Us</h2>
</form>
```

#### Widget Roles

**button**: Clickable element that triggers a response

```html
<div role="button" tabindex="0">Custom Button</div>
<!-- Prefer: <button>Custom Button</button> -->
```

**checkbox**: Checkable input with three states: true, false, mixed

```html
<div role="checkbox" aria-checked="true" tabindex="0">
  Accept terms
</div>
```

**radio**: Checkable input in a group of radio roles

```html
<div role="radiogroup" aria-labelledby="group-label">
  <span id="group-label">Size</span>
  <div role="radio" aria-checked="true" tabindex="0">Small</div>
  <div role="radio" aria-checked="false" tabindex="-1">Large</div>
</div>
```

**textbox**: Input that allows free-form text

```html
<div role="textbox" contenteditable="true" aria-label="Note"></div>
```

**link**: Interactive reference to a resource

```html
<span role="link" tabindex="0" onclick="navigate()">Click here</span>
<!-- Prefer: <a href="...">Click here</a> -->
```

**menubar**: Presentation of a menu in a horizontal bar

```html
<ul role="menubar">
  <li role="menuitem"><a href="/file">File</a></li>
  <li role="menuitem"><a href="/edit">Edit</a></li>
</ul>
```

**menu**: List of choices presented to the user

```html
<ul role="menu">
  <li role="menuitem">Save</li>
  <li role="menuitem">Save As...</li>
  <li role="separator"></li>
  <li role="menuitem">Exit</li>
</ul>
```

**menuitem**: Option in a menu

**menuitemcheckbox**: Checkable menuitem

```html
<ul role="menu">
  <li role="menuitemcheckbox" aria-checked="true">Show toolbar</li>
</ul>
```

**menuitemradio**: Checkable menuitem in group of menuitems

```html
<ul role="menu">
  <li role="menuitemradio" aria-checked="true">Small</li>
  <li role="menuitemradio" aria-checked="false">Large</li>
</ul>
```

**option**: Selectable item in a listbox

```html
<div role="listbox" aria-label="Colors">
  <div role="option" aria-selected="true">Red</div>
  <div role="option" aria-selected="false">Blue</div>
</div>
```

**progressbar**: Element that displays progress status

```html
<div
  role="progressbar"
  aria-valuenow="75"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-label="Upload progress"
>
  75%
</div>
```

**scrollbar**: Graphical object that controls scrolling

```html
<div
  role="scrollbar"
  aria-controls="content"
  aria-valuenow="50"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-orientation="vertical"
  tabindex="0"
></div>
```

**slider**: Input where user selects a value from a range

```html
<div
  role="slider"
  aria-valuenow="50"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-label="Volume"
  tabindex="0"
></div>
```

**spinbutton**: Form of range with increase/decrease buttons

```html
<div
  role="spinbutton"
  aria-valuenow="5"
  aria-valuemin="0"
  aria-valuemax="10"
  aria-label="Quantity"
  tabindex="0"
></div>
```

**switch**: Checkbox that represents on/off values

```html
<button
  role="switch"
  aria-checked="true"
  aria-label="Enable notifications"
>
  <span>On</span>
</button>
```

**tab**: Tab in a tablist

```html
<div role="tablist" aria-label="Sections">
  <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>
```

**tablist**: List of tab elements

**tabpanel**: Container for resources associated with a tab

**combobox**: Composite widget with input and popup

```html
<div role="combobox" aria-expanded="false" aria-haspopup="listbox">
  <input type="text" aria-autocomplete="list" aria-controls="listbox">
  <ul role="listbox" id="listbox" hidden>
    <li role="option">Option 1</li>
    <li role="option">Option 2</li>
  </ul>
</div>
```

**grid**: Composite widget containing cells of tabular data

```html
<div role="grid" aria-labelledby="grid-title">
  <div role="row">
    <div role="columnheader">Name</div>
    <div role="columnheader">Age</div>
  </div>
  <div role="row">
    <div role="gridcell">John</div>
    <div role="gridcell">30</div>
  </div>
</div>
```

**listbox**: Widget that allows user to select one or more items

```html
<ul role="listbox" aria-label="Fruits">
  <li role="option" aria-selected="true">Apple</li>
  <li role="option" aria-selected="false">Banana</li>
</ul>
```

**tree**: Widget that allows user to select items from hierarchical list

```html
<ul role="tree" aria-label="File system">
  <li role="treeitem" aria-expanded="true">
    Documents
    <ul role="group">
      <li role="treeitem">Resume.pdf</li>
    </ul>
  </li>
</ul>
```

**treegrid**: Grid whose rows can be expanded and collapsed

**treeitem**: Item in a tree

#### Document Structure Roles

**article**: Self-contained composition

```html
<article role="article">
  <h2>Article Title</h2>
  <p>Content...</p>
</article>
```

**definition**: Definition of a term or concept

```html
<dl>
  <dt>Accessibility</dt>
  <dd role="definition">The practice of making content usable by all people</dd>
</dl>
```

**directory**: List of references to members of a group

**document**: Content that contains primarily static information

**feed**: Scrollable list of articles

**figure**: Perceivable content with optional caption

```html
<figure role="figure" aria-labelledby="fig-caption">
  <img src="chart.png" alt="">
  <figcaption id="fig-caption">Sales data for 2025</figcaption>
</figure>
```

**group**: Set of user interface objects

```html
<div role="group" aria-labelledby="group-title">
  <h3 id="group-title">Shipping Address</h3>
  <input type="text" aria-label="Street">
  <input type="text" aria-label="City">
</div>
```

**heading**: Heading for a section of the page

```html
<div role="heading" aria-level="2">Section Title</div>
<!-- Prefer: <h2>Section Title</h2> -->
```

**img**: Container for a collection of elements that form an image

```html
<div role="img" aria-label="Company logo">
  <svg>...</svg>
</div>
```

**list**: Group of non-interactive list items

```html
<div role="list">
  <div role="listitem">Item 1</div>
  <div role="listitem">Item 2</div>
</div>
<!-- Prefer: <ul><li>Item 1</li><li>Item 2</li></ul> -->
```

**listitem**: Single item in a list

**math**: Mathematical expression

```html
<div role="math" aria-label="Pythagorean theorem">
  a² + b² = c²
</div>
```

**note**: Parenthetic or ancillary content

```html
<aside role="note" aria-label="Editor's note">
  <p>This article was updated on 2025-10-27.</p>
</aside>
```

**presentation/none**: Element whose semantics should be removed

```html
<table role="presentation">
  <tr>
    <td>Layout cell</td>
  </tr>
</table>
```

**separator**: Divider between sections

```html
<hr role="separator">
<div role="separator" aria-orientation="horizontal"></div>
```

**table**: Non-interactive table

```html
<div role="table" aria-labelledby="table-title">
  <div id="table-title">Employee List</div>
  <div role="rowgroup">
    <div role="row">
      <div role="columnheader">Name</div>
      <div role="columnheader">Title</div>
    </div>
  </div>
  <div role="rowgroup">
    <div role="row">
      <div role="cell">John Doe</div>
      <div role="cell">Engineer</div>
    </div>
  </div>
</div>
<!-- Prefer: <table>...</table> -->
```

**term**: Word or phrase with an optional corresponding definition

```html
<span role="term" aria-describedby="def1">ARIA</span>
<span id="def1">Accessible Rich Internet Applications</span>
```

**toolbar**: Collection of commonly used controls

```html
<div role="toolbar" aria-label="Text formatting">
  <button aria-label="Bold">B</button>
  <button aria-label="Italic">I</button>
  <button aria-label="Underline">U</button>
</div>
```

**tooltip**: Contextual popup that displays information

```html
<button aria-describedby="tooltip1">Help</button>
<div role="tooltip" id="tooltip1">
  Click for more information
</div>
```

#### Live Region Roles

**alert**: Important, time-sensitive message

```html
<div role="alert">
  Error: Your session has expired
</div>
```

**log**: Live region where new information is added

```html
<div role="log" aria-live="polite" aria-atomic="false">
  <p>User joined: Alice</p>
  <p>User joined: Bob</p>
</div>
```

**marquee**: Live region with non-essential information that changes

```html
<div role="marquee" aria-live="off">
  Latest news: ...
</div>
```

**status**: Advisory information for user

```html
<div role="status" aria-live="polite">
  Changes saved successfully
</div>
```

**timer**: Numerical counter or countdown

```html
<div role="timer" aria-live="off" aria-atomic="true">
  Time remaining: 5:00
</div>
```

#### Window Roles

**alertdialog**: Dialog that contains an alert message

```html
<div role="alertdialog" aria-modal="true" aria-labelledby="alert-title">
  <h2 id="alert-title">Confirm Delete</h2>
  <p>Are you sure you want to delete this item?</p>
  <button>Delete</button>
  <button>Cancel</button>
</div>
```

**dialog**: Application window designed to interrupt

```html
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Settings</h2>
  <!-- Dialog content -->
</div>
```

### ARIA States and Properties

#### Widget Attributes

**aria-autocomplete**: Indicates autocomplete behavior

```html
<input
  type="text"
  role="combobox"
  aria-autocomplete="list"
  aria-controls="suggestions"
>
```

Values: `none`, `inline`, `list`, `both`

**aria-checked**: Indicates checked state

```html
<div role="checkbox" aria-checked="true" tabindex="0">
  Option 1
</div>
```

Values: `true`, `false`, `mixed`

**aria-disabled**: Indicates element is disabled

```html
<button aria-disabled="true">Submit</button>
```

Values: `true`, `false`

**aria-expanded**: Indicates expanded state

```html
<button aria-expanded="false" aria-controls="menu">
  Menu
</button>
<ul id="menu" hidden>...</ul>
```

Values: `true`, `false`, `undefined`

**aria-haspopup**: Indicates element triggers popup

```html
<button aria-haspopup="menu">Options</button>
```

Values: `false`, `true`, `menu`, `listbox`, `tree`, `grid`, `dialog`

**aria-hidden**: Indicates element is hidden from accessibility tree

```html
<span aria-hidden="true">★</span>
<span class="sr-only">5 stars</span>
```

Values: `true`, `false`, `undefined`

**aria-invalid**: Indicates invalid value

```html
<input
  type="email"
  aria-invalid="true"
  aria-describedby="email-error"
>
<span id="email-error">Please enter a valid email</span>
```

Values: `true`, `false`, `grammar`, `spelling`

**aria-label**: Defines accessible name

```html
<button aria-label="Close dialog">×</button>
```

**aria-level**: Defines hierarchical level

```html
<div role="heading" aria-level="2">Subsection</div>
```

**aria-modal**: Indicates modal dialog

```html
<div role="dialog" aria-modal="true">...</div>
```

Values: `true`, `false`

**aria-multiline**: Indicates multiline text input

```html
<div role="textbox" aria-multiline="true" contenteditable>
</div>
```

Values: `true`, `false`

**aria-multiselectable**: Indicates multiple selection allowed

```html
<ul role="listbox" aria-multiselectable="true">
  <li role="option">Option 1</li>
  <li role="option">Option 2</li>
</ul>
```

Values: `true`, `false`

**aria-orientation**: Indicates orientation

```html
<div role="scrollbar" aria-orientation="vertical">
</div>
```

Values: `horizontal`, `vertical`, `undefined`

**aria-placeholder**: Defines placeholder text

```html
<div
  role="textbox"
  contenteditable
  aria-placeholder="Enter text here"
>
</div>
```

**aria-pressed**: Indicates pressed state of toggle button

```html
<button aria-pressed="true">Bold</button>
```

Values: `true`, `false`, `mixed`, `undefined`

**aria-readonly**: Indicates element is not editable

```html
<input type="text" aria-readonly="true" value="Read only">
```

Values: `true`, `false`

**aria-required**: Indicates required field

```html
<input
  type="text"
  aria-required="true"
  aria-label="Email (required)"
>
```

Values: `true`, `false`

**aria-selected**: Indicates selected state

```html
<div role="option" aria-selected="true">Option 1</div>
```

Values: `true`, `false`, `undefined`

**aria-sort**: Indicates sort order

```html
<th role="columnheader" aria-sort="ascending">Name</th>
```

Values: `ascending`, `descending`, `none`, `other`

**aria-valuemax**: Maximum value

```html
<div
  role="slider"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-valuenow="50"
>
</div>
```

**aria-valuemin**: Minimum value

**aria-valuenow**: Current value

**aria-valuetext**: Human-readable value text

```html
<div
  role="slider"
  aria-valuenow="2"
  aria-valuetext="Medium"
>
</div>
```

#### Live Region Attributes

**aria-atomic**: Indicates if entire region should be announced

```html
<div role="status" aria-live="polite" aria-atomic="true">
  Loading: 50%
</div>
```

Values: `true`, `false`

**aria-busy**: Indicates element is being modified

```html
<div aria-busy="true">Loading...</div>
```

Values: `true`, `false`

**aria-live**: Indicates live region update priority

```html
<div aria-live="polite">Status message</div>
```

Values: `off`, `polite`, `assertive`

**aria-relevant**: Indicates what changes should be announced

```html
<div
  role="log"
  aria-live="polite"
  aria-relevant="additions text"
>
</div>
```

Values: `additions`, `removals`, `text`, `all`

#### Relationship Attributes

**aria-activedescendant**: Identifies active descendant

```html
<div
  role="combobox"
  aria-activedescendant="option-2"
>
  <input type="text">
  <ul role="listbox">
    <li role="option" id="option-1">Option 1</li>
    <li role="option" id="option-2">Option 2</li>
  </ul>
</div>
```

**aria-colcount**: Defines total number of columns

```html
<div role="table" aria-colcount="10">
  <!-- Only showing 3 of 10 columns -->
  <div role="row">
    <div role="cell" aria-colindex="1">Cell 1</div>
    <div role="cell" aria-colindex="2">Cell 2</div>
    <div role="cell" aria-colindex="3">Cell 3</div>
  </div>
</div>
```

**aria-colindex**: Defines column index

**aria-colspan**: Defines number of columns spanned

**aria-controls**: Identifies controlled elements

```html
<button aria-expanded="false" aria-controls="dropdown">
  Expand
</button>
<div id="dropdown" hidden>Content</div>
```

**aria-describedby**: References descriptive elements

```html
<input
  type="password"
  aria-describedby="password-hint"
>
<p id="password-hint">Must be at least 8 characters</p>
```

**aria-details**: References detailed description

```html
<img
  src="chart.png"
  alt="Sales chart"
  aria-details="chart-description"
>
<div id="chart-description">
  <h3>Detailed Description</h3>
  <p>The chart shows...</p>
</div>
```

**aria-errormessage**: References error message

```html
<input
  type="email"
  aria-invalid="true"
  aria-errormessage="email-error"
>
<span id="email-error" role="alert">
  Please enter a valid email address
</span>
```

**aria-flowto**: Identifies next element in alternate reading order

```html
<div id="step1" aria-flowto="step2">Step 1</div>
<div id="step2">Step 2</div>
```

**aria-labelledby**: References labeling elements

```html
<div role="dialog" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Confirm Action</h2>
</div>
```

**aria-owns**: Identifies owned elements

```html
<div role="listbox" aria-owns="option1 option2">
  <div role="option" id="option1">Option 1</div>
</div>
<div role="option" id="option2">Option 2 (elsewhere in DOM)</div>
```

**aria-posinset**: Defines position in set

```html
<div role="listitem" aria-posinset="2" aria-setsize="10">
  Item 2 of 10
</div>
```

**aria-rowcount**: Defines total number of rows

**aria-rowindex**: Defines row index

**aria-rowspan**: Defines number of rows spanned

**aria-setsize**: Defines total number of items in set

```html
<div role="listitem" aria-posinset="1" aria-setsize="5">
  Item 1 of 5
</div>
```

---

## Semantic HTML

### Document Structure

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Page Title - Site Name</title>
</head>
<body>
  <!-- Skip link -->
  <a href="#main-content" class="skip-link">Skip to main content</a>

  <!-- Site header -->
  <header>
    <div class="logo">
      <img src="logo.png" alt="Company Name">
    </div>

    <!-- Main navigation -->
    <nav aria-label="Main">
      <ul>
        <li><a

…(truncated)
