Shopify Dawn Theme Accessibility: Audit, Common Issues, and How to Fix Them
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.
Related Resources
- Best Shopify Accessibility Tool 2025
- Complete Shopify Accessibility Guide
- Shopify Dawn Theme Documentation
- WCAG 2.1 Quick Reference
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.


Automate the software work for accessibility compliance, end-to-end.
Empowering businesses with seamless digital accessibility solutions—simple, inclusive, effective.
Book a Demo