Selected from the full 30-issue list in the deliverable, one per WCAG criterion plus the most prevalent patterns. Each issue includes the affected element, plain-English explanation, paste-able fix, and developer-hour estimate.
P0
WCAG 1.1.1 · Non-text content
Issue 1 / 30
Site logo image has no alt text
Element: #shopify-override > img · Affects every page on the store
The header logo is rendered as a plain <img> with no alt attribute. Screen-reader users hear the image filename or nothing at all when navigating to the home page — the brand identity is invisible to assistive tech.
Fix (in sections/header.liquid or wherever the logo is rendered):
<!-- Before -->
<img src="{{ section.settings.logo | image_url: width: 200 }}">
<!-- After -->
<img src="{{ section.settings.logo | image_url: width: 200 }}"
alt="{{ shop.name | escape }}"
width="{{ section.settings.logo.width }}"
height="{{ section.settings.logo.height }}"
loading="eager">
Estimated developer time: 5 minutes. Fixes the logo across every page in one edit.
P0
WCAG 1.3.1 · Info and relationships
Issue 2 / 30
Empty heading elements creating phantom landmarks
Element: ...> h5 with no text content · 4 instances in the cart-drawer modal
Several <h5> tags in the cart-drawer template wrap empty containers used purely for layout. Screen readers announce them as "heading, level 5" with no content, creating phantom landmarks during heading-by-heading navigation.
Fix (replace with semantic div when used for layout):
{%- comment -%} Before — empty h5 used as layout container {%- endcomment -%}
<h5 class="cart-drawer__row">{{ block.shopify_attributes }}</h5>
{%- comment -%} After — div with appropriate role if needed {%- endcomment -%}
<div class="cart-drawer__row" {{ block.shopify_attributes }}>
{%- if block.settings.heading != blank -%}
<h2>{{ block.settings.heading | escape }}</h2>
{%- endif -%}
</div>
Estimated developer time: 15 minutes. 4 occurrences in sections/cart-drawer.liquid.
P0
WCAG 1.3.1 · Info and relationships
Issue 3 / 30
Newsletter signup input has no programmatic label
Element: input[type="email"] in footer newsletter form · Affects every page
The footer email-capture field uses a placeholder for guidance ("Enter your email"). Placeholders are not labels — they disappear when the user starts typing and assistive tech treats them inconsistently. A programmatic label is required.
Fix (in sections/footer.liquid):
<label for="NewsletterFormEmail" class="visually-hidden">
Email address
</label>
<input id="NewsletterFormEmail"
type="email"
name="contact[email]"
autocomplete="email"
placeholder="Enter your email"
required>
Estimated developer time: 10 minutes. Use the existing .visually-hidden CSS class — Dawn-derived themes ship with it.
P1
WCAG 1.4.3 · Contrast minimum
Issue 4 / 30
Promo banner copy below 4.5:1 contrast ratio
Light gray #9CA3AF on white = 2.85:1. Required: 4.5:1.
The "Free shipping over $75" promo banner uses a light gray that fails the AA 4.5:1 minimum for body text. Affected at 5 surface points across the home, collection, and product templates.
Fix:
/* In your theme's main stylesheet — typically assets/base.css */
.promo-banner__text {
/* Before */
color: #9CA3AF; /* 2.85:1 — fail */
/* After — passes 4.55:1 on white */
color: #4B5563;
}
Estimated developer time: 5 minutes. Verify with a contrast checker (e.g., webaim.org/resources/contrastchecker) before committing.
P0
WCAG 2.4.1 · Bypass blocks
Issue 5 / 30
Skip-link points to a non-existent anchor
Link href: #megamenu--main-menu — no element with this id exists on the page.
A keyboard-skip link in the header references an anchor that was renamed during a theme update. Keyboard users tabbing onto the link and pressing Enter land nowhere — the link is silently broken.
Fix (the simplest path is to make the skip-link match an existing landmark):
<!-- In layout/theme.liquid, just inside <body> -->
<a class="skip-to-content-link visually-hidden-focusable"
href="#MainContent">
Skip to main content
</a>
<!-- And ensure the main wrapper has the matching id -->
<main id="MainContent" tabindex="-1" role="main">
{{ content_for_layout }}
</main>
Estimated developer time: 10 minutes. Cited by federal courts (Robles v. Domino's) — high-priority remediation.
P1
WCAG 3.2.2 · On Input
Issue 6 / 30
Newsletter form has no visible submit button
7 forms across the site rely on Enter-key submission only.
Several signup forms are designed for Enter-key submission with no visible button. This is unreliable for assistive tech and breaks for users on touch keyboards or those who don't realize Enter submits.
Fix:
{%- comment -%} Add a real submit button to every form {%- endcomment -%}
<form action="/contact#contact_form" method="post">
{%- comment -%} ...inputs... {%- endcomment -%}
<button type="submit" class="newsletter__btn">
Subscribe
<span class="visually-hidden">to our newsletter</span>
</button>
</form>
Estimated developer time: 30 minutes. 7 forms × ~4 minutes each.
P1
WCAG 4.1.1 · Parsing
Issue 7 / 30
Duplicate id attributes from repeated section types
21 instances. Most-cited collision: id="Image with Text" appearing 4 times.
When a Shopify theme reuses the same section type (e.g., "Image with Text") multiple times on a page, naive id generation causes collisions. HTML id attributes must be unique — duplicates cause assistive tech to behave unpredictably and break aria-labelledby / aria-describedby references.
Fix (in any section template that generates ids — use Shopify's section.id Liquid variable):
{%- comment -%} Before — collides when section is reused {%- endcomment -%}
<div id="Image with Text">
<h2>{{ section.settings.heading }}</h2>
</div>
{%- comment -%} After — unique per instance via section.id {%- endcomment -%}
<div id="ImageWithText--{{ section.id }}">
<h2 id="ImageWithText__heading--{{ section.id }}">
{{ section.settings.heading | escape }}
</h2>
</div>
Estimated developer time: 2 hours. Audit every section template; replace static ids with section.id-suffixed unique values.
P0
WCAG 4.1.2 · Name, Role, Value
Issue 8 / 30
Cart drawer close button has no accessible name
Element: icon-only <button> in the cart-drawer header
The cart-drawer modal's close button renders an X icon with no accompanying text or aria-label. Screen readers announce it as "button" with no purpose, leaving users unable to dismiss the drawer.
Fix:
{%- comment -%} sections/cart-drawer.liquid {%- endcomment -%}
<button type="button"
class="cart-drawer__close"
aria-label="Close cart"
data-cart-close>
{%- render 'icon-close' -%}
</button>
Estimated developer time: 5 minutes. Apply the same pattern to the menu-open and search-open icon buttons (Issues 9 + 10).
P1
WCAG 4.1.2 · Name, Role, Value
Issue 9 / 30
Cart quantity +/− buttons have no labels
Element: stepper controls in cart-drawer line items
The plus and minus buttons that adjust line-item quantity in the cart drawer are unlabeled icons. Screen-reader users hear "button, button" and can't tell which increases vs decreases quantity.
Fix:
<div class="quantity">
<button type="button"
aria-label="Decrease quantity"
data-quantity-decrease>−</button>
<input type="number" value="{{ item.quantity }}"
aria-label="Quantity for {{ item.title | escape }}">
<button type="button"
aria-label="Increase quantity"
data-quantity-increase>+</button>
</div>
Estimated developer time: 15 minutes.
P2
WCAG 1.3.1 · Info and relationships
Issue 10 / 30
Heading hierarchy skips levels in the homepage layout
Home page: h1 → h4 (skipping h2 and h3)
The home page uses one <h1> for the hero, then jumps to <h4> in the "Featured collection" section. Screen-reader users navigating by heading hierarchy lose track of structure when levels are skipped.
Fix (in section templates — use logical heading levels, style with CSS classes for visual sizing):
{%- comment -%} Before — visual jump h1 → h4 {%- endcomment -%}
<h4 class="featured-collection__title">Featured</h4>
{%- comment -%} After — semantically correct h2, styled to match h4 visually {%- endcomment -%}
<h2 class="featured-collection__title h4">
{{ section.settings.title | escape }}
</h2>
Estimated developer time: 1 hour. Audit every section template; ensure heading levels follow document order.