Blog

Shopify Dawn Theme Accessibility: Audit, Common Issues, and How to Fix Them

TestParty
TestParty
December 28, 2025

Dawn is Shopify's flagship free theme and the foundation for many custom Shopify stores. As Shopify's reference implementation for Online Store 2.0, Dawn sets the standard that other themes follow. So how accessible is it? We conducted a comprehensive accessibility audit to find out.

The good news: Dawn is more accessible than most Shopify themes. The challenging news: it still has significant issues that affect users with disabilities. This guide covers what works, what doesn't, and how to fix the problems.

Key Takeaways

  • Dawn provides a solid accessibility foundation - Better than most themes, with proper landmarks and some ARIA implementation
  • Keyboard navigation has notable gaps - Several interactive elements are difficult or impossible to use without a mouse
  • Focus visibility needs improvement - Focus indicators are present but often insufficient for WCAG compliance
  • Image accessibility is merchant-dependent - Dawn supports alt text but doesn't enforce it or provide fallbacks
  • JavaScript components need attention - Modal dialogs, carousels, and mega menus have accessibility issues

Dawn Theme Accessibility Overview

Released as part of Online Store 2.0, Dawn represents Shopify's commitment to modern theme architecture. It uses semantic HTML5 elements, CSS custom properties, and modular JavaScript. From an accessibility standpoint, this architecture provides both advantages and challenges.

What Dawn Gets Right

Dawn includes several accessibility features that many themes lack:

Semantic HTML structure: Dawn uses appropriate heading hierarchy, landmark regions, and semantic elements like `<nav>`, `<main>`, and `<article>`.

Skip link implementation: A skip-to-content link is included, allowing keyboard users to bypass navigation.

ARIA landmark roles: The theme properly identifies header, main content, and footer regions.

Focus states present: Unlike many themes that remove focus outlines entirely, Dawn includes visible focus states.

Translation support: Accessibility text uses Shopify's translation system, supporting multilingual stores.

Where Dawn Falls Short

Our audit identified several significant accessibility issues:

Insufficient color contrast: Multiple elements fail WCAG 2.1 AA contrast requirements.

Keyboard traps in modals: The cart drawer and search modal have focus management issues.

Missing form labels: Several form inputs rely on placeholder text instead of proper labels.

Carousel accessibility: The slideshow component lacks proper controls and announcements.

Focus indicator visibility: While focus states exist, they often don't provide sufficient contrast.

Detailed Accessibility Audit Results

Let's examine each major component of Dawn and its accessibility status.

Header and Navigation

Component: Main navigation menu

Issues found:

  • Dropdown menus don't announce their expanded state to screen readers
  • Mega menu panels trap keyboard focus in some configurations
  • Mobile menu button lacks proper aria-expanded state updates
  • Search form opens without moving focus appropriately

WCAG violations:

  • 4.1.2 Name, Role, Value (dropdown state not communicated)
  • 2.1.1 Keyboard (focus management in dropdowns)
  • 2.4.3 Focus Order (search modal focus)

Fixes:

{% comment %} Fix dropdown aria-expanded - in header.liquid {% endcomment %}
{% comment %} Original: {% endcomment %}
<button class="menu-drawer__menu-item" aria-expanded="false">

{% comment %} Fixed: Add JavaScript to toggle aria-expanded {% endcomment %}
<button
  class="menu-drawer__menu-item"
  aria-expanded="false"
  aria-controls="submenu-{{ link.handle }}"
>
// Add to global.js or header component
document.querySelectorAll('.menu-drawer__menu-item').forEach(button => {
  button.addEventListener('click', function() {
    const expanded = this.getAttribute('aria-expanded') === 'true';
    this.setAttribute('aria-expanded', !expanded);
  });
});

Product Image Gallery

Component: Product page image carousel/gallery

Issues found:

  • Thumbnail buttons lack accessible names
  • Gallery navigation arrows only have visual icons
  • Image zoom modal doesn't manage focus
  • Carousel doesn't announce current slide to screen readers

WCAG violations:

  • 1.1.1 Non-text Content (icon-only buttons)
  • 2.4.4 Link Purpose (unclear thumbnail function)
  • 4.1.2 Name, Role, Value (carousel state)

Fixes:

{% comment %} Fix thumbnail accessibility in product-media-gallery.liquid {% endcomment %}
<button
  class="thumbnail"
  aria-label="{{ 'products.product.media.view_image' | t }} {{ forloop.index }} {{ 'accessibility.of' | t }} {{ product.media.size }}"
  aria-current="{% if forloop.first %}true{% else %}false{% endif %}"
  data-thumbnail-index="{{ forloop.index0 }}"
>
  <img src="{{ media | image_url: width: 100 }}" alt="" loading="lazy">
</button>
{% comment %} Fix navigation arrows {% endcomment %}
<button
  class="slider-button slider-button--prev"
  aria-label="{{ 'accessibility.previous_slide' | t }}"
  disabled
>
  {% render 'icon-arrow' %}
</button>

Product Variant Selector

Component: Color, size, and other variant options

Issues found:

  • Color swatches don't announce the color name
  • Selected state is visual only (color change)
  • Unavailable variants aren't announced to screen readers
  • No grouping or relationship indicated between options

WCAG violations:

  • 1.4.1 Use of Color (selection indicated by color alone)
  • 4.1.2 Name, Role, Value (selection state)
  • 1.3.1 Info and Relationships (option grouping)

Fixes:

{% comment %} Fix variant selector in product-variant-picker.liquid {% endcomment %}
<fieldset class="product-form__input">
  <legend class="form__label">
    {{ option.name }}
    <span class="visually-hidden">{{ 'accessibility.current_selection' | t }}: </span>
    <span class="product-form__selected-value">{{ option.selected_value }}</span>
  </legend>

  {% for value in option.values %}
    {% assign variant_available = value.available %}
    <label class="product-form__option">
      <input
        type="radio"
        name="{{ option.name }}"
        value="{{ value | escape }}"
        {% if option.selected_value == value %}checked{% endif %}
        {% unless variant_available %}disabled{% endunless %}
        class="visually-hidden"
      >
      <span class="product-form__option-label">
        {% if option.name == 'Color' %}
          <span
            class="color-swatch"
            style="background-color: {{ value | downcase }}"
            aria-hidden="true"
          ></span>
        {% endif %}
        {{ value }}
        {% unless variant_available %}
          <span class="visually-hidden">{{ 'accessibility.sold_out' | t }}</span>
        {% endunless %}
      </span>
    </label>
  {% endfor %}
</fieldset>

Cart Drawer

Component: Slide-out cart panel

Issues found:

  • Focus doesn't move to drawer when opened
  • No focus trap within the drawer
  • Close button has icon only without accessible name
  • Quantity input lacks proper labeling
  • Focus doesn't return to trigger when closed

WCAG violations:

  • 2.4.3 Focus Order
  • 2.1.2 No Keyboard Trap (inverse - focus should be trapped)
  • 1.1.1 Non-text Content

Fixes:

// Cart drawer focus management
class CartDrawer extends HTMLElement {
  constructor() {
    super();
    this.focusableElements = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
  }

  open(triggeredBy) {
    this.triggerElement = triggeredBy;
    this.setAttribute('open', '');

    // Move focus to first focusable element
    const firstFocusable = this.querySelector(this.focusableElements);
    if (firstFocusable) {
      firstFocusable.focus();
    }

    // Trap focus
    this.addEventListener('keydown', this.trapFocus.bind(this));

    // Announce to screen readers
    this.setAttribute('aria-hidden', 'false');
  }

  close() {
    this.removeAttribute('open');
    this.setAttribute('aria-hidden', 'true');

    // Return focus to trigger
    if (this.triggerElement) {
      this.triggerElement.focus();
    }

    this.removeEventListener('keydown', this.trapFocus);
  }

  trapFocus(event) {
    if (event.key !== 'Tab') return;

    const focusables = this.querySelectorAll(this.focusableElements);
    const firstFocusable = focusables[0];
    const lastFocusable = focusables[focusables.length - 1];

    if (event.shiftKey && document.activeElement === firstFocusable) {
      lastFocusable.focus();
      event.preventDefault();
    } else if (!event.shiftKey && document.activeElement === lastFocusable) {
      firstFocusable.focus();
      event.preventDefault();
    }
  }
}

Search Modal

Component: Predictive search overlay

Issues found:

  • Focus management issues when opening
  • Search results not announced
  • Loading state not communicated
  • Form lacks proper labeling
  • Results list role not specified

WCAG violations:

  • 2.4.3 Focus Order
  • 4.1.3 Status Messages
  • 1.3.1 Info and Relationships

Fixes:

{% comment %} Fix search form in predictive-search.liquid {% endcomment %}
<form action="{{ routes.search_url }}" method="get" role="search" class="search-modal__form">
  <label for="Search-In-Modal" class="visually-hidden">
    {{ 'general.search.search' | t }}
  </label>

  <input
    id="Search-In-Modal"
    type="search"
    name="q"
    value="{{ search.terms | escape }}"
    placeholder="{{ 'general.search.placeholder' | t }}"
    aria-describedby="search-results-status"
    autocomplete="off"
    autocorrect="off"
    autocapitalize="off"
    spellcheck="false"
  >

  <div
    id="search-results-status"
    class="visually-hidden"
    role="status"
    aria-live="polite"
  >
    {% comment %} JavaScript updates this with result count {% endcomment %}
  </div>

  <div
    id="predictive-search-results"
    role="listbox"
    aria-label="{{ 'general.search.results' | t }}"
  >
    {% comment %} Results rendered here {% endcomment %}
  </div>
</form>

Footer

Component: Footer links and newsletter form

Issues found:

  • Newsletter input uses placeholder instead of label
  • Link groups lack heading structure
  • Social media icons lack accessible names
  • Payment icons don't have alt text

WCAG violations:

  • 1.3.1 Info and Relationships
  • 1.1.1 Non-text Content
  • 3.3.2 Labels or Instructions

Fixes:

{% comment %} Fix newsletter form in footer.liquid {% endcomment %}
<div class="footer__newsletter">
  <h2 class="footer__heading">{{ section.settings.newsletter_heading }}</h2>

  {% form 'customer', class: 'footer__newsletter-form' %}
    <div class="newsletter-form__field-wrapper">
      <label for="NewsletterForm--{{ section.id }}" class="visually-hidden">
        {{ 'newsletter.label' | t }}
      </label>
      <input
        id="NewsletterForm--{{ section.id }}"
        type="email"
        name="contact[email]"
        class="field__input"
        placeholder="{{ 'newsletter.label' | t }}"
        aria-required="true"
        autocorrect="off"
        autocapitalize="off"
        required
      >
      <button type="submit" class="newsletter-form__button" aria-label="{{ 'newsletter.button_label' | t }}">
        {% render 'icon-arrow' %}
      </button>
    </div>
  {% endform %}
</div>

Color Contrast Issues in Dawn

Dawn's default color scheme has several contrast issues that need addressing:

Problem Areas

+----------------------+----------------+----------------+------------+----------------+
|       Element        |   Foreground   |   Background   |   Ratio    |    Required    |
+----------------------+----------------+----------------+------------+----------------+
|      Body text       |    #121212     |    #ffffff     |   18.1:1   |      Pass      |
+----------------------+----------------+----------------+------------+----------------+
|      Link text       |    #121212     |    #ffffff     |   18.1:1   |      Pass      |
+----------------------+----------------+----------------+------------+----------------+
|   Placeholder text   |    #767676     |    #ffffff     |   4.5:1    |   Borderline   |
+----------------------+----------------+----------------+------------+----------------+
|      Sale badge      |    #ffffff     |    #f44336     |   4.2:1    |      Fail      |
+----------------------+----------------+----------------+------------+----------------+
|   Disabled button    |    #b4b4b4     |    #f7f7f7     |   2.5:1    |      Fail      |
+----------------------+----------------+----------------+------------+----------------+

CSS Fixes for Contrast

/* Fix low contrast elements in base.css */

/* Sale badge - increase contrast */
.price--on-sale .price__sale {
  color: #ffffff;
  background-color: #c62828; /* Darker red for better contrast */
}

/* Placeholder text - ensure sufficient contrast */
::placeholder {
  color: #595959; /* Darker gray for 7:1 ratio */
  opacity: 1;
}

/* Disabled state - maintain readability */
.button:disabled,
.button[disabled] {
  color: #595959;
  background-color: #e0e0e0;
}

/* Focus indicators - high visibility */
:focus-visible {
  outline: 3px solid #005fcc;
  outline-offset: 2px;
}

Implementing Dawn Accessibility Fixes

Here's a systematic approach to improving Dawn's accessibility:

Step 1: Create an Accessibility Override File

Create a new snippet file to contain accessibility enhancements:

{% comment %} snippets/accessibility-enhancements.liquid {% endcomment %}

{% comment %} Additional skip links {% endcomment %}
<div class="skip-links">
  <a href="#MainContent" class="skip-link">{{ 'accessibility.skip_to_content' | t }}</a>
  <a href="#ProductInfo" class="skip-link">{{ 'accessibility.skip_to_product' | t }}</a>
  <a href="#FooterContent" class="skip-link">{{ 'accessibility.skip_to_footer' | t }}</a>
</div>

{% comment %} Live region for dynamic updates {% endcomment %}
<div id="accessibility-announcer" class="visually-hidden" role="status" aria-live="polite"></div>

<style>
  .skip-link {
    position: absolute;
    top: -40px;
    left: 0;
    background: #000;
    color: #fff;
    padding: 8px 16px;
    z-index: 1000;
    text-decoration: none;
  }

  .skip-link:focus {
    top: 0;
  }
</style>

Step 2: Add Accessibility JavaScript

Create a JavaScript file for accessibility enhancements:

// assets/accessibility.js

// Screen reader announcement helper
function announce(message) {
  const announcer = document.getElementById('accessibility-announcer');
  if (announcer) {
    announcer.textContent = message;
    // Clear after announcement
    setTimeout(() => { announcer.textContent = ''; }, 1000);
  }
}

// Focus management for modals
function trapFocusInElement(element) {
  const focusableElements = element.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );

  const firstFocusable = focusableElements[0];
  const lastFocusable = focusableElements[focusableElements.length - 1];

  element.addEventListener('keydown', function(e) {
    if (e.key === 'Tab') {
      if (e.shiftKey && document.activeElement === firstFocusable) {
        lastFocusable.focus();
        e.preventDefault();
      } else if (!e.shiftKey && document.activeElement === lastFocusable) {
        firstFocusable.focus();
        e.preventDefault();
      }
    }

    if (e.key === 'Escape') {
      element.querySelector('[data-close]')?.click();
    }
  });
}

// Cart drawer accessibility enhancement
document.addEventListener('DOMContentLoaded', function() {
  const cartDrawer = document.querySelector('cart-drawer');
  if (cartDrawer) {
    const originalOpen = cartDrawer.open;
    cartDrawer.open = function() {
      originalOpen.apply(this, arguments);
      trapFocusInElement(this);
      announce('Cart drawer opened');
    };
  }
});

Step 3: Update Theme.liquid

Include the accessibility enhancements in your theme:

{% comment %} In layout/theme.liquid, after opening body tag {% endcomment %}
{% render 'accessibility-enhancements' %}

{% comment %} Before closing body tag {% endcomment %}
<script src="{{ 'accessibility.js' | asset_url }}" defer="defer"></script>

Automated Testing and Ongoing Maintenance

Fixing Dawn's accessibility issues is just the beginning. You need ongoing monitoring to maintain compliance as you update your store.

Manual auditing of every change isn't practical for most merchants. Automated accessibility testing tools can scan your store continuously and alert you to new issues before they affect customers.

For a comprehensive approach to Shopify accessibility beyond Dawn-specific fixes, see our complete Shopify accessibility guide.

Frequently Asked Questions

Is Shopify Dawn theme accessible?

Dawn is more accessible than most Shopify themes but still has notable issues. Our audit found problems with keyboard navigation, color contrast, focus management, and ARIA implementation. With the fixes outlined in this guide, Dawn can be made WCAG 2.1 AA compliant.

What accessibility issues does Dawn have?

Key issues include: insufficient focus indicators, keyboard traps in modals, missing form labels, color contrast failures on sale badges and disabled elements, and incomplete ARIA implementation in dropdowns and carousels.

How do I make my Dawn theme accessible?

Apply the code fixes in this guide to address specific issues. Add proper ARIA attributes to interactive elements, improve color contrast in CSS, implement focus management for modals, and add accessible names to icon buttons.

Should I use Dawn theme for accessibility?

Dawn provides a better starting point than most themes, but it still requires work to achieve full accessibility. If you need an accessible Shopify store, Dawn with modifications is a reasonable choice, but budget time and resources for accessibility improvements.

Does Shopify test Dawn for accessibility?

Shopify performs some accessibility testing on Dawn, which is why it's better than average. However, it doesn't meet full WCAG 2.1 AA compliance out of the box. The issues identified in this audit indicate that more thorough accessibility testing would benefit the theme.


This article was written with AI assistance and reviewed by accessibility professionals at TestParty. Our platform helps Shopify merchants achieve accessibility compliance through source code remediation, fixing the actual HTML, CSS, and JavaScript rather than relying on overlay widgets that don't address underlying issues.

Stay informed

Accessibility insights delivered
straight to your inbox.

Contact Us

Automate the software work for accessibility compliance, end-to-end.

Empowering businesses with seamless digital accessibility solutions—simple, inclusive, effective.

Book a Demo