Skip to main content

Overview

Accessibility rules ensure your website is usable by everyone, including people with disabilities. Accessible sites also perform better in search results and provide better user experiences overall.
Why accessibility matters: 15% of the world’s population has some form of disability. Making your site accessible expands your audience and is often legally required.

Image Alt Text

Rule: a11y/image-alt

What it checks:
  • All images have alt attributes
  • Alt text is descriptive (not empty or generic)
  • Decorative images use empty alt (alt="")
  • Complex images have detailed descriptions
Why it matters: Screen readers rely on alt text to describe images to visually impaired users. Missing or poor alt text creates barriers.
<!-- ❌ Bad: Missing alt -->
<img src="product.jpg">

<!-- ❌ Bad: Generic alt text -->
<img src="product.jpg" alt="image">
<img src="product.jpg" alt="photo">

<!-- ❌ Bad: Redundant "image of" -->
<img src="product.jpg" alt="Image of a laptop computer">

<!-- ✅ Good: Descriptive alt text -->
<img src="product.jpg" alt="MacBook Pro 16-inch with M3 chip, space gray">

<!-- ✅ Good: Decorative image (empty alt) -->
<img src="decorative-line.svg" alt="" aria-hidden="true">

<!-- ✅ Good: Complex image with long description -->
<img src="sales-chart.png" 
     alt="Sales growth chart showing 45% increase in Q4 2025"
     longdesc="#chart-description">
<div id="chart-description">
  <p>Detailed description: Monthly sales from January to December 2025...</p>
</div>
Alt text guidelines:
  • Be specific and concise (typically 125 characters or less)
  • Describe the content and function, not just appearance
  • Don’t say “image of” or “picture of” (screen readers announce it’s an image)
  • For decorative images, use alt="" (empty, not missing)
  • For functional images (buttons, links), describe the action
  • For charts/graphs, summarize the key insight

ARIA Landmarks

Rule: a11y/landmarks

What it checks:
  • Page has semantic HTML5 landmarks
  • Proper use of <header>, <nav>, <main>, <aside>, <footer>
  • ARIA roles used correctly
  • One <main> landmark per page
Why it matters: Landmarks help screen reader users navigate and understand page structure quickly.
Issue: No <main> landmark
<!-- ❌ Bad: No semantic landmarks -->
<div id="header">...</div>
<div id="content">
  <div id="article">...</div>
</div>
<div id="footer">...</div>

<!-- ✅ Good: Semantic HTML5 landmarks -->
<header>
  <nav aria-label="Main navigation">
    <!-- Navigation menu -->
  </nav>
</header>

<main>
  <article>
    <h1>Article Title</h1>
    <!-- Article content -->
  </article>
  
  <aside>
    <!-- Related content, sidebar -->
  </aside>
</main>

<footer>
  <!-- Footer content -->
</footer>
ARIA landmark roles:
HTML5 ElementARIA RolePurpose
<header>bannerSite header
<nav>navigationNavigation menu
<main>mainPrimary content
<aside>complementaryRelated content
<footer>contentinfoSite footer
<section>regionThematic content
<article>articleSelf-contained content
<form>formForm
<search>searchSearch widget
Use HTML5 semantic elements (<main>, <nav>, etc.) instead of ARIA roles when possible. HTML5 elements have better browser support.
What it checks:
  • “Skip to main content” link exists
  • Link is first focusable element
  • Link works correctly
Why it matters: Keyboard users need to skip repetitive navigation and jump straight to main content.

Color Contrast

Rule: a11y/color-contrast

What it checks:
  • Text meets WCAG AA contrast ratio (4.5:1 for normal text, 3:1 for large text)
  • Links are distinguishable from surrounding text
  • UI components have sufficient contrast
Why it matters: Low contrast makes text hard to read for users with low vision or color blindness.
WCAG Contrast Requirements:
LevelNormal TextLarge Text (18pt+)
AA4.5:13:1
AAA7:14.5:1
/* ❌ Bad: Insufficient contrast (2.5:1) */
.text {
  color: #999; /* Light gray */
  background: #fff; /* White */
}

/* ✅ Good: Sufficient contrast (7:1) */
.text {
  color: #555; /* Dark gray */
  background: #fff; /* White */
}

/* ✅ Good: High contrast for links */
a {
  color: #0056b3; /* Dark blue (4.5:1 on white) */
  text-decoration: underline; /* Don't rely on color alone */
}

a:hover {
  color: #003d82; /* Even darker on hover */
}
Testing tools:

Keyboard Navigation

Rule: a11y/keyboard

What it checks:
  • All interactive elements are keyboard accessible
  • Focus order is logical
  • Focus indicators are visible
  • No keyboard traps
Why it matters: Many users navigate with keyboard only (not mouse). Inaccessible keyboard navigation excludes these users.
Keyboard navigation requirements:
<!-- ❌ Bad: Div button (not keyboard accessible) -->
<div onclick="handleClick()">Click me</div>

<!-- ✅ Good: Semantic button (keyboard accessible) -->
<button onclick="handleClick()">Click me</button>

<!-- ✅ Good: If you must use div, add role and tabindex -->
<div role="button" tabindex="0" onclick="handleClick()" onkeypress="handleKeyPress(event)">
  Click me
</div>
Focus styles (never remove!):
/* ❌ Bad: Removing focus outline */
button:focus {
  outline: none; /* Don't do this! */
}

/* ✅ Good: Custom focus style */
button:focus {
  outline: 2px solid #0056b3;
  outline-offset: 2px;
}

/* ✅ Good: Focus-visible (only for keyboard) */
button:focus-visible {
  outline: 2px solid #0056b3;
  outline-offset: 2px;
}
Logical tab order:
<!-- Tab order follows visual order -->
<nav>
  <a href="/" tabindex="1">Home</a> <!-- Don't manually set tabindex unless necessary -->
  <a href="/about">About</a>
  <a href="/contact">Contact</a>
</nav>
Avoid using tabindex values greater than 0. They create unpredictable tab orders. Use natural DOM order instead.

Form Labels

Rule: a11y/form-labels

What it checks:
  • All form inputs have associated labels
  • Labels are properly linked to inputs
  • Required fields are indicated
  • Error messages are accessible
<!-- ❌ Bad: No label -->
<input type="text" placeholder="Enter your name">

<!-- ❌ Bad: Label not associated -->
<label>Name</label>
<input type="text">

<!-- ✅ Good: Explicit label association -->
<label for="name">Name</label>
<input type="text" id="name" name="name">

<!-- ✅ Good: Implicit label association -->
<label>
  Name
  <input type="text" name="name">
</label>

<!-- ✅ Good: Required field indication -->
<label for="email">
  Email <span aria-label="required">*</span>
</label>
<input type="email" id="email" name="email" required aria-required="true">

<!-- ✅ Good: Error message -->
<label for="username">Username</label>
<input type="text" 
       id="username" 
       name="username" 
       aria-invalid="true" 
       aria-describedby="username-error">
<span id="username-error" role="alert">
  Username must be at least 6 characters
</span>
Form best practices:
  • Always use <label> elements (not placeholders alone)
  • Use aria-required="true" on required fields
  • Use aria-invalid="true" for fields with errors
  • Link error messages with aria-describedby
  • Group related inputs with <fieldset> and <legend>

Heading Hierarchy

Rule: a11y/heading-order

What it checks:
  • Headings follow logical order (H1 → H2 → H3)
  • No skipped heading levels (H1 → H3)
  • One H1 per page
  • Headings describe content accurately
<!-- ❌ Bad: Skipped heading levels -->
<h1>Page Title</h1>
<h3>Subsection</h3> <!-- Skipped H2 -->
<h5>Detail</h5> <!-- Skipped H3, H4 -->

<!-- ✅ Good: Logical heading hierarchy -->
<h1>Page Title</h1>
<h2>Main Section</h2>
<h3>Subsection</h3>
<h4>Detail</h4>
<h2>Another Main Section</h2>
<h3>Subsection</h3>
Heading outline example:
<main>
  <h1>Market Research Toolkit</h1>
  
  <section>
    <h2>Website Auditing</h2>
    <p>...</p>
    
    <h3>230+ Audit Rules</h3>
    <p>...</p>
    
    <h3>21 Categories</h3>
    <p>...</p>
  </section>
  
  <section>
    <h2>Marketing Psychology</h2>
    <p>...</p>
    
    <h3>70+ Mental Models</h3>
    <p>...</p>
  </section>
</main>
Visual vs. semantic heading levels:If you need an H2 to look like an H4, use CSS (don’t change the heading level):
<h2 class="text-small">Small-looking H2</h2>
.text-small {
  font-size: 1rem; /* Makes it look smaller */
}

ARIA Attributes

Common ARIA Attributes

Provides accessible name when visible text isn’t sufficient:
<!-- ✅ Icon button with aria-label -->
<button aria-label="Close dialog">
  <svg>...</svg> <!-- Just an X icon -->
</button>

<!-- ✅ Search input with aria-label -->
<input type="search" aria-label="Search products" placeholder="Search...">
References other elements for labels/descriptions:
<!-- aria-labelledby: Points to label -->
<h2 id="dialog-title">Confirm Delete</h2>
<div role="dialog" aria-labelledby="dialog-title">
  <p>Are you sure you want to delete this item?</p>
</div>

<!-- aria-describedby: Points to description -->
<label for="password">Password</label>
<input type="password" 
       id="password" 
       aria-describedby="password-requirements">
<div id="password-requirements">
  Must be at least 8 characters with 1 number
</div>
Hides decorative content from screen readers:
<!-- ✅ Hide decorative icon -->
<button>
  <svg aria-hidden="true">...</svg>
  Save Document
</button>

<!-- ❌ Don't hide meaningful content -->
<img src="product.jpg" alt="MacBook Pro" aria-hidden="true"> <!-- Bad! -->
Announces dynamic content changes:
<!-- ✅ Announce status updates -->
<div aria-live="polite" aria-atomic="true">
  Item added to cart
</div>

<!-- ✅ Announce errors immediately -->
<div role="alert" aria-live="assertive">
  Error: Payment failed
</div>
aria-live values:
  • off: Don’t announce (default)
  • polite: Announce when user is idle
  • assertive: Announce immediately (use sparingly)

Testing Accessibility

WAVE

Browser extension for visual accessibility evaluation

axe DevTools

Automated accessibility testing in browser DevTools

Lighthouse

Accessibility audit built into Chrome DevTools

NVDA Screen Reader

Free screen reader for Windows (test with real tools)

WCAG Compliance Levels

LevelRequirementsUse Case
AMinimumBare minimum (not recommended as target)
AAStandardTarget for most sites (legally required in many jurisdictions)
AAAEnhancedMaximum accessibility (difficult to achieve for all content)
WCAG 2.1 Level AA is the standard target and is legally required in many countries (EU, US Section 508, ADA).

Accessibility Checklist

1

Images & Media

  • All images have descriptive alt text
  • Decorative images use alt=""
  • Videos have captions and transcripts
2

Semantic HTML

  • Use <header>, <nav>, <main>, <footer> landmarks
  • Add skip link to main content
  • Proper heading hierarchy (H1 → H2 → H3)
3

Keyboard Navigation

  • All interactive elements keyboard accessible
  • Visible focus indicators
  • Logical tab order
  • No keyboard traps
4

Color & Contrast

  • Text meets 4.5:1 contrast ratio (WCAG AA)
  • Don’t rely on color alone to convey information
  • Links distinguishable from text
5

Forms

  • All inputs have labels
  • Required fields marked
  • Error messages accessible
  • Use ARIA attributes appropriately
6

Test & Validate

  • Run automated tests (axe, Lighthouse, WAVE)
  • Test with keyboard only
  • Test with screen reader
  • Get feedback from users with disabilities

Content Rules

Content quality, heading structure, and readability

Core SEO Rules

Title tags, meta descriptions, and semantic HTML

Running Audits

Learn how to run accessibility audits

Audit Categories

Explore all 21 audit categories

Build docs developers (and LLMs) love