Quality, Performance, Accessibility, and Shipping Intermediate 12 min read

Accessibility Checklist

A comprehensive accessibility checklist for Shopify themes covering WCAG guidelines, keyboard navigation, screen readers, and inclusive design patterns.

Accessible themes work for everyone, including users with visual, motor, auditory, or cognitive disabilities. Accessibility isn’t just ethical; it improves SEO, usability, and legal compliance.

WCAG Guidelines

Web Content Accessibility Guidelines (WCAG) organize requirements into four principles:

PrincipleMeaning
PerceivableInformation must be presentable in ways users can perceive
OperableInterface must be operable by all users
UnderstandableContent and interface must be understandable
RobustContent must work with assistive technologies

Accessibility Checklist

Semantic HTML

## Semantic Structure
- [ ] Use proper heading hierarchy (h1 → h2 → h3)
- [ ] Only one `<h1>` per page
- [ ] Use `<nav>` for navigation regions
- [ ] Use `<main>` for primary content
- [ ] Use `<header>` and `<footer>` appropriately
- [ ] Use `<article>` for self-contained content
- [ ] Use `<section>` with headings
- [ ] Use `<button>` for actions, `<a>` for navigation

Example Structure

<header role="banner">
<nav aria-label="Main navigation">
{# Navigation #}
</nav>
</header>
<main id="main-content" role="main">
<h1>{{ page.title }}</h1>
<article>
{# Content #}
</article>
</main>
<footer role="contentinfo">
{# Footer #}
</footer>

Keyboard Navigation

## Keyboard Accessibility
- [ ] All interactive elements are focusable
- [ ] Focus order follows visual order
- [ ] Focus is visible (not `outline: none`)
- [ ] Skip link to main content
- [ ] Escape closes modals/drawers
- [ ] Tab trapping in modals
- [ ] Arrow keys for menu navigation
- [ ] Enter/Space activate buttons
{# First element in body #}
<a href="#main-content" class="skip-link">
Skip to main content
</a>
.skip-link {
position: absolute;
top: -100%;
left: 0;
padding: 1rem;
background: var(--color-background);
z-index: 9999;
}
.skip-link:focus {
top: 0;
}

Focus Styles

/* Never remove outlines without replacement */
:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Modern focus-visible */
:focus:not(:focus-visible) {
outline: none;
}
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}

Images and Media

## Images and Media
- [ ] All images have `alt` attributes
- [ ] Decorative images use `alt=""`
- [ ] Informative images have descriptive alt text
- [ ] Complex images have extended descriptions
- [ ] Videos have captions
- [ ] Audio has transcripts
- [ ] Autoplay is disabled or easily stopped

Alt Text Examples

{# Decorative image #}
{{ image | image_url: width: 400 | image_tag: alt: '' }}
{# Informative image #}
{{ product.featured_image | image_url: width: 600 | image_tag:
alt: product.featured_image.alt | default: product.title
}}
{# Complex image with extended description #}
<figure>
{{ image | image_url: width: 800 | image_tag:
alt: 'Size chart showing measurements',
aria-describedby: 'size-chart-description'
}}
<figcaption id="size-chart-description">
Detailed size chart with measurements in inches...
</figcaption>
</figure>

Forms

## Form Accessibility
- [ ] All inputs have associated labels
- [ ] Required fields are indicated
- [ ] Error messages are clear and specific
- [ ] Errors are announced to screen readers
- [ ] Form has logical tab order
- [ ] Autocomplete attributes are used
- [ ] Input types match content (email, tel, etc.)

Accessible Form Example

<form>
<div class="form-field">
<label for="email">
Email
<span class="required" aria-label="required">*</span>
</label>
<input
type="email"
id="email"
name="email"
required
autocomplete="email"
aria-describedby="email-error"
>
<span id="email-error" class="form-error" role="alert" hidden>
Please enter a valid email address
</span>
</div>
<button type="submit">Subscribe</button>
</form>

Color and Contrast

## Color and Contrast
- [ ] Text has 4.5:1 contrast ratio (AA)
- [ ] Large text has 3:1 contrast ratio
- [ ] UI components have 3:1 contrast
- [ ] Color is not the only indicator
- [ ] Links are distinguishable from text
- [ ] Focus indicators have sufficient contrast

Color Indicators

{# Bad: color only #}
<span style="color: red;">Out of stock</span>
{# Good: color plus text #}
<span class="stock-status stock-status--out">
<span class="visually-hidden">Status:</span>
Out of Stock
</span>

ARIA Attributes

## ARIA Usage
- [ ] Use ARIA only when HTML isn't sufficient
- [ ] ARIA labels for icon-only buttons
- [ ] aria-expanded for collapsible content
- [ ] aria-current for navigation
- [ ] aria-live for dynamic content
- [ ] role attributes where needed
- [ ] aria-hidden for decorative elements

ARIA Examples

{# Icon button with label #}
<button aria-label="Add to cart">
{% render 'icon-cart' %}
</button>
{# Expandable navigation #}
<button
aria-expanded="false"
aria-controls="submenu-1"
>
Shop
</button>
<ul id="submenu-1" hidden>
{# Submenu items #}
</ul>
{# Current page in navigation #}
<a href="/" {% if template == 'index' %}aria-current="page"{% endif %}>
Home
</a>
{# Live region for cart updates #}
<div aria-live="polite" aria-atomic="true" class="visually-hidden">
{{ cart.item_count }} items in cart
</div>

Modals and Drawers

## Modal Accessibility
- [ ] Focus trapped inside modal
- [ ] Escape key closes modal
- [ ] Focus returns to trigger on close
- [ ] Background content is inert
- [ ] Modal has accessible name
- [ ] Close button is labeled
<div
class="modal"
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
hidden
>
<h2 id="modal-title">Quick View</h2>
<button
class="modal__close"
aria-label="Close modal"
>
×
</button>
<div class="modal__content">
{# Content #}
</div>
</div>

Screen Reader Utilities

/* Visually hidden but accessible */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Show on focus (for skip links) */
.visually-hidden-focusable:focus {
position: static;
width: auto;
height: auto;
overflow: visible;
clip: auto;
white-space: normal;
}

Product Information

## E-commerce Accessibility
- [ ] Price changes are announced
- [ ] Sale prices include original price
- [ ] Variant selection is keyboard accessible
- [ ] Add to cart confirmation is announced
- [ ] Product images have alt text
- [ ] Stock status is programmatically indicated

Accessible Price Display

<div class="price">
{%- if product.compare_at_price > product.price -%}
<span class="visually-hidden">Regular price:</span>
<del aria-hidden="true">{{ product.compare_at_price | money }}</del>
<span class="visually-hidden">Sale price:</span>
<ins>{{ product.price | money }}</ins>
{%- else -%}
<span class="visually-hidden">Price:</span>
{{ product.price | money }}
{%- endif -%}
</div>

Testing Tools

Automated Testing

## Automated Tools
- [ ] axe DevTools (Chrome extension)
- [ ] WAVE Web Accessibility Evaluator
- [ ] Lighthouse Accessibility audit
- [ ] Pa11y CLI

Manual Testing

## Manual Testing
- [ ] Keyboard-only navigation
- [ ] Screen reader testing (VoiceOver, NVDA)
- [ ] Zoom to 200% (check layout)
- [ ] Windows High Contrast mode
- [ ] Reduced motion preference

Screen Reader Testing

## Screen Reader Tests
- [ ] Navigate by headings
- [ ] Navigate by landmarks
- [ ] Read product information
- [ ] Complete checkout flow
- [ ] Use search feature
- [ ] Navigate menus

Common Issues

Quick Fixes

IssueFix
Missing alt textAdd descriptive alt or alt=""
Low contrastIncrease color contrast
Missing form labelsAdd <label> with for attribute
Non-focusable elementAdd tabindex="0" or use button
Missing skip linkAdd skip link to main content
Icon-only buttonAdd aria-label

Accessibility Statement

Include in your theme:

{# pages/accessibility.liquid #}
<h1>Accessibility Statement</h1>
<p>We are committed to ensuring our website is accessible to all users.</p>
<h2>Standards</h2>
<p>We aim to conform to WCAG 2.1 Level AA.</p>
<h2>Feedback</h2>
<p>If you encounter accessibility issues, please contact us at {{ shop.email }}.</p>

Practice Exercise

Audit a theme page for accessibility:

  1. Run axe DevTools
  2. Test with keyboard only
  3. Test with a screen reader
  4. Fix top 5 issues
  5. Document improvements

Key Takeaways

  1. Semantic HTML is the foundation
  2. Keyboard access for all interactions
  3. Alt text for meaningful images
  4. Sufficient contrast for readability
  5. ARIA supplements, doesn’t replace HTML
  6. Testing with real assistive technology
  7. Continuous improvement over perfection

What’s Next?

The next lesson covers SEO Basics in Themes for search engine optimization.

Finished this lesson?

Mark it complete to track your progress.

Discussion

Loading comments...