SAMPLE REPORT. Generated by Threshwise from a fictitious demo store built with deliberate accessibility bugs. Your Fix Pack looks like this, for your own site. Scan your site free

Threshwise by Mogley Works · Accessibility Fix Pack · 2026-10-01 19:34 UTC

demo-store.example (SAMPLE)

6pages tested
194failing instances
41root causes to fix
6critical
13high
10health indicator*

Executive summary

Threshwise tested 6 pages of demo-store.example (SAMPLE) (shopify, theme Dawn-demo). Automated checks found 194 failing element instances, which trace back to 41 probable root causes, of which 6 critical and 13 high priority. 9 sit in shared theme components, so one change fixes every page they appear on.

Automated testing (axe-core plus keyboard heuristics) typically detects only part of all WCAG issues. Criteria such as meaningful alt text, logical reading order, error messaging, captions and overall screen-reader usability need human review. Use the manual checklist to complete the evaluation. This report is technical testing evidence, not legal advice and not a certification of compliance with the ADA, EAA, AODA or any law. *The health indicator tracks progress between scans; it is not a compliance score.

Standards tested

WCAG 2.2 Level A and AA success criteria that can be tested automatically (via axe-core 4.13.0), plus best-practice rules (marked separately) and keyboard-navigation heuristics (Tab traversal, focus visibility, focus traps, unreachable controls). Criteria failed: 1.1.1, 1.4.3, 2.1.1, 2.4.2, 2.4.3, 2.4.4, 2.4.7, 2.5.8, 3.1.1, 4.1.2.

Pages tested

TemplatePages foundNot deep-scanned (same template)
home10
collection10
contact10
cart10
page20
product31

Could not test: https://demo-store.example/pages/about (http_404); https://demo-store.example/pages/faq (http_404)

What to fix first

CRITICALWCAG AAutomatically detected

1. Button has no accessible name

4.1.2 Name, Role, Value — 6 pages, 6 instances — Theme / shared component · component: header

Why it matters: A screen reader announces just "button"; voice-control users cannot say its name to click it. Affects screen-reader users, voice-control users.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Give icon-only buttons an aria-label describing the action.
  2. Or include visually-hidden text inside the button.
  3. Mark the decorative <svg> with aria-hidden="true" focusable="false".
Found (example)
<button class="header__icon header__icon--menu" onclick="document.getElementById('menu-drawer').classList.toggle('is-open')"><svg width="20" height="20" class="icon icon-hamburger"><path d="M0 4h20M0 10h20M0 16h20" stroke="#000"></path></svg></button>

.header__icon--menu
https://demo-store.example/

Suggested change
<button class="header__icon header__icon--menu" onclick="document.getElementById('menu-drawer').classList.toggle('is-open')" aria-label="Open menu"> … </button>
Platform pattern
<button type="button" aria-label="{{ 'general.menu.open' | t | default: 'Open menu' }}">{% render 'icon-hamburger' %}</button>
Generic before/after
<button class="header__icon--menu"><svg>…</svg></button>
→
<button class="header__icon--menu" aria-label="Open menu" aria-expanded="false" aria-controls="menu-drawer"><svg aria-hidden="true" focusable="false">…</svg></button>
All affected locations (6)
https://demo-store.example/.header__icon--menu
https://demo-store.example/cart.header__icon--menu
https://demo-store.example/products/linen-shirt.header__icon--menu
https://demo-store.example/pages/contact.header__icon--menu
https://demo-store.example/collections/all.header__icon--menu
https://demo-store.example/products/linen-trousers.header__icon--menu

Rule reference: button-name

CRITICALWCAG ADetected automatically – needs human judgement

2. Image has no text alternative

1.1.1 Non-text Content — 6 pages, 6 instances — Theme / shared component · component: header

Why it matters: Screen readers announce the file name or just "image", so the meaning (product, logo, chart) is lost. Affects screen-reader users.

Automation can detect a missing alt but cannot judge if alt text is meaningful. Review each image: decorative → alt="", informative → describe purpose.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add an alt attribute describing the image purpose (not "image of…").
  2. If purely decorative, use alt="" so it is skipped.
  3. For links that contain only an image, the alt must describe the link destination.
Found (example)
<img src="/logo-demo-store.png" width="120" height="30">

img[src$="logo-demo-store.png"]
https://demo-store.example/

Suggested change
<img src="/logo-demo-store.png" width="120" height="30" alt="Logo demo store">  <!-- or alt="" if decorative -->
Platform pattern
{{ product.featured_image | image_url: width: 600 | image_tag: alt: product.featured_image.alt | default: product.title }}
Generic before/after
<img src="/blue-linen-shirt.jpg">
→
<img src="/blue-linen-shirt.jpg" alt="Blue linen shirt, front view">
All affected locations (6)
https://demo-store.example/img[src$="logo-demo-store.png"]
https://demo-store.example/cartimg
https://demo-store.example/products/linen-shirtimg[src$="logo-demo-store.png"]
https://demo-store.example/pages/contactimg
https://demo-store.example/collections/allimg[src$="logo-demo-store.png"]
https://demo-store.example/products/linen-trousersimg[src$="logo-demo-store.png"]

Rule reference: image-alt

CRITICALWCAG AAutomatically detected

3. Link has no discernible text

2.4.4 Link Purpose (In Context) · 4.1.2 Name, Role, Value — 6 pages, 12 instances — Theme / shared component · component: footer

Why it matters: Screen readers announce "link" or read out the URL; users cannot tell where it goes. Affects screen-reader users, voice-control users.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add text inside the link, or an aria-label for icon-only links (e.g. social icons).
  2. If the link wraps an image, give the image alt text describing the destination.
Found (example)
<a href="https://instagram.com/demo" class="list-social__link"><svg class="icon icon-instagram" width="18" height="18"><rect width="18" height="18"></rect></svg></a>

li:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/

Suggested change
<a href="https://instagram.com/demo" class="list-social__link" aria-label="Instagram"> … </a>
Platform pattern
<a href="{{ settings.social_instagram_link }}" aria-label="Instagram">{% render 'icon-instagram' %}</a>
Generic before/after
<a href="https://instagram.com/brand"><svg>…</svg></a>
→
<a href="https://instagram.com/brand" aria-label="Instagram"><svg aria-hidden="true" focusable="false">…</svg></a>
All affected locations (12)
https://demo-store.example/li:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/li:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/cartli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/cartli:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/products/linen-shirtli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/products/linen-shirtli:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/pages/contactli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/pages/contactli:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/collections/allli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/collections/allli:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/products/linen-trousersli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/products/linen-trousersli:nth-child(2) > .list-social__link[href$="demo"]

Rule reference: link-name

CRITICALWCAG AAutomatically detected

4. Link has no discernible text

2.4.4 Link Purpose (In Context) · 4.1.2 Name, Role, Value — 6 pages, 6 instances — Theme / shared component · component: header

Why it matters: Screen readers announce "link" or read out the URL; users cannot tell where it goes. Affects screen-reader users, voice-control users.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add text inside the link, or an aria-label for icon-only links (e.g. social icons).
  2. If the link wraps an image, give the image alt text describing the destination.
Found (example)
<a href="/" class="header__heading-link"><img src="/logo-demo-store.png" width="120" height="30"></a>

.header__heading-link
https://demo-store.example/

Suggested change
<a href="/" class="header__heading-link" aria-label="Describe destination"> … </a>
Platform pattern
<a href="{{ settings.social_instagram_link }}" aria-label="Instagram">{% render 'icon-instagram' %}</a>
Generic before/after
<a href="https://instagram.com/brand"><svg>…</svg></a>
→
<a href="https://instagram.com/brand" aria-label="Instagram"><svg aria-hidden="true" focusable="false">…</svg></a>
All affected locations (6)
https://demo-store.example/.header__heading-link
https://demo-store.example/cart.header__heading-link
https://demo-store.example/products/linen-shirt.header__heading-link
https://demo-store.example/pages/contact.header__heading-link
https://demo-store.example/collections/all.header__heading-link
https://demo-store.example/products/linen-trousers.header__heading-link

Rule reference: link-name

CRITICALWCAG AAutomatically detected

5. Link has no discernible text

2.4.4 Link Purpose (In Context) · 4.1.2 Name, Role, Value — 6 pages, 6 instances — Theme / shared component · component: header

Why it matters: Screen readers announce "link" or read out the URL; users cannot tell where it goes. Affects screen-reader users, voice-control users.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add text inside the link, or an aria-label for icon-only links (e.g. social icons).
  2. If the link wraps an image, give the image alt text describing the destination.
Found (example)
<a href="/cart" class="header__icon header__icon--cart"><svg class="icon icon-cart" width="20" height="20"><circle cx="10" cy="10" r="8"></circle></svg></a>

.header__icon--cart
https://demo-store.example/

Suggested change
<a href="/cart" class="header__icon header__icon--cart" aria-label="Open cart"> … </a>
Platform pattern
<a href="{{ settings.social_instagram_link }}" aria-label="Instagram">{% render 'icon-instagram' %}</a>
Generic before/after
<a href="https://instagram.com/brand"><svg>…</svg></a>
→
<a href="https://instagram.com/brand" aria-label="Instagram"><svg aria-hidden="true" focusable="false">…</svg></a>
All affected locations (6)
https://demo-store.example/.header__icon--cart
https://demo-store.example/cart.header__icon--cart
https://demo-store.example/products/linen-shirt.header__icon--cart
https://demo-store.example/pages/contact.header__icon--cart
https://demo-store.example/collections/all.header__icon--cart
https://demo-store.example/products/linen-trousers.header__icon--cart

Rule reference: link-name

CRITICALWCAG AAutomatically detected

6. Select menu has no label

4.1.2 Name, Role, Value — 2 pages, 2 instances — Page template · component: main

Why it matters: The purpose of the dropdown (size, colour, sort order) is not announced. Affects screen-reader users, voice-control users.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Associate a <label for> with the select, or add aria-label.
Found (example)
<select name="quantity"><option>1</option><option>2</option></select>

select
https://demo-store.example/products/linen-shirt

Suggested change
<select name="quantity" aria-label="Quantity"> … </select>
Generic before/after
<select name="sort_by">…</select>
→
<label for="SortBy">Sort by</label>
<select id="SortBy" name="sort_by">…</select>
All affected locations (2)
https://demo-store.example/products/linen-shirtselect
https://demo-store.example/products/linen-trousersselect

Rule reference: select-name

HIGHWCAG AAutomatically detected

7. Form field has no label

4.1.2 Name, Role, Value — 1 page, 1 instance — Single page

Why it matters: Screen-reader users hear "edit text" with no idea what to type; placeholder text disappears on input and is not a reliable label. Affects screen-reader users, users with cognitive disabilities, voice-control users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add a <label for="id"> associated with the field (visually hidden if design requires).
  2. Or aria-label / aria-labelledby when a visible label is impossible.
  3. Do not rely on placeholder alone.
Found (example)
<input type="number" value="1" name="updates[]">

input[type="number"]
https://demo-store.example/cart

Suggested change
<label for="updates[]-input">updates[]</label>
<input type="number" value="1" name="updates[]" id="updates[]-input">
Platform pattern
<label for="NewsletterForm-{{ section.id }}" class="visually-hidden">{{ 'newsletter.label' | t }}</label>
<input id="NewsletterForm-{{ section.id }}" type="email" name="contact[email]" autocomplete="email">
Generic before/after
<input type="email" name="contact[email]" placeholder="Email">
→
<label for="NewsletterEmail" class="visually-hidden">Email address</label>
<input id="NewsletterEmail" type="email" name="contact[email]" placeholder="Email" autocomplete="email">

Rule reference: label

HIGHWCAG ADetected automatically – needs human judgement

8. Image has no text alternative

1.1.1 Non-text Content — 2 pages, 4 instances — Content (editor) · component: main

Why it matters: Screen readers announce the file name or just "image", so the meaning (product, logo, chart) is lost. Affects screen-reader users.

Automation can detect a missing alt but cannot judge if alt text is meaningful. Review each image: decorative → alt="", informative → describe purpose.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Content fixes (image alt text, link text in rich text) are made in the Shopify admin, not the theme: Products → image → "Add alt text", or Online Store → Navigation.

  1. Add an alt attribute describing the image purpose (not "image of…").
  2. If purely decorative, use alt="" so it is skipped.
  3. For links that contain only an image, the alt must describe the link destination.
Found (example)
<img src="/files/linen-shirt-front_1200x.jpg" width="400" height="400">

img[width="400"]
https://demo-store.example/products/linen-shirt

Suggested change
<img src="/files/linen-shirt-front_1200x.jpg" width="400" height="400" alt="Linen shirt front 1200x">  <!-- or alt="" if decorative -->
Platform pattern
{{ product.featured_image | image_url: width: 600 | image_tag: alt: product.featured_image.alt | default: product.title }}
Generic before/after
<img src="/blue-linen-shirt.jpg">
→
<img src="/blue-linen-shirt.jpg" alt="Blue linen shirt, front view">
All affected locations (4)
https://demo-store.example/products/linen-shirtimg[width="400"]
https://demo-store.example/products/linen-shirtimg[src$="care-label.png"]
https://demo-store.example/products/linen-trousersimg[width="400"]
https://demo-store.example/products/linen-trousersimg[src$="care-label.png"]

Rule reference: image-alt

HIGHWCAG AAutomatically detected

9. Form field has no label

4.1.2 Name, Role, Value — 1 page, 1 instance — Page template · component: contact-form

Why it matters: Screen-reader users hear "edit text" with no idea what to type; placeholder text disappears on input and is not a reliable label. Affects screen-reader users, users with cognitive disabilities, voice-control users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add a <label for="id"> associated with the field (visually hidden if design requires).
  2. Or aria-label / aria-labelledby when a visible label is impossible.
  3. Do not rely on placeholder alone.
Found (example)
<textarea name="contact[body]"></textarea>

textarea
https://demo-store.example/pages/contact

Suggested change
<label for="contact[body]-input">contact[body]</label>
<textarea name="contact[body]" id="contact[body]-input">
Platform pattern
<label for="NewsletterForm-{{ section.id }}" class="visually-hidden">{{ 'newsletter.label' | t }}</label>
<input id="NewsletterForm-{{ section.id }}" type="email" name="contact[email]" autocomplete="email">
Generic before/after
<input type="email" name="contact[email]" placeholder="Email">
→
<label for="NewsletterEmail" class="visually-hidden">Email address</label>
<input id="NewsletterEmail" type="email" name="contact[email]" placeholder="Email" autocomplete="email">

Rule reference: label

HIGHWCAG AAAutomatically detected

10. Touch target is too small (WCAG 2.2)

2.5.8 Target Size (Minimum) — 6 pages, 12 instances — Theme / shared component · component: header

Why it matters: Small, tightly packed controls are hard to hit for users with tremors or on mobile. Affects users with motor impairments.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Make interactive targets at least 24×24 CSS px, or space them so a 24px circle around each does not overlap another.
Found (example)
<a href="/collections/all">Shop</a>

a[href$="all"]
https://demo-store.example/

Suggested change
a[href$="all"] { min-width: 24px; min-height: 24px; }
Generic before/after
.swatch { width: 16px; height: 16px; }
→
.swatch { min-width: 24px; min-height: 24px; }
All affected locations (12)
https://demo-store.example/a[href$="all"]
https://demo-store.example/a[href$="contact"]
https://demo-store.example/carta[href$="all"]
https://demo-store.example/carta[href$="contact"]
https://demo-store.example/products/linen-shirta[href$="all"]
https://demo-store.example/products/linen-shirta[href$="contact"]
https://demo-store.example/pages/contacta[href$="all"]
https://demo-store.example/pages/contacta[href$="contact"]
https://demo-store.example/collections/alla[href$="all"]
https://demo-store.example/collections/alla[href$="contact"]
https://demo-store.example/products/linen-trousersa[href$="all"]
https://demo-store.example/products/linen-trousersa[href$="contact"]

Rule reference: target-size

HIGHWCAG AAutomatically detected

11. Select menu has no label

4.1.2 Name, Role, Value — 1 page, 1 instance — Single page

Why it matters: The purpose of the dropdown (size, colour, sort order) is not announced. Affects screen-reader users, voice-control users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Associate a <label for> with the select, or add aria-label.
Found (example)
<select name="sort_by"><option>Featured</option></select>

select
https://demo-store.example/collections/all

Suggested change
<select name="sort_by" aria-label="Sort by"> … </select>
Generic before/after
<select name="sort_by">…</select>
→
<label for="SortBy">Sort by</label>
<select id="SortBy" name="sort_by">…</select>

Rule reference: select-name

HIGHWCAG AAAutomatically detected

12. Text colour contrast is too low

1.4.3 Contrast (Minimum) — 6 pages, 6 instances — Theme / shared component · component: footer

Why it matters: Low-contrast text is hard or impossible to read for people with low vision, older users and anyone on a phone in sunlight. Affects low-vision users, colour-blind users.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Raise contrast to at least 4.5:1 for normal text, 3:1 for large text (≥24px or ≥18.66px bold).
  2. Change the colour in the theme/design tokens (one change fixes every page).
Found (example)
<p class="muted">© Demo Store</p>

.muted
https://demo-store.example/

Suggested change
/* #b5b5b5 on #f6f6f6 = 1.89:1, needs 4.5:1 */
.muted { color: #696969; } /* ≈5.08:1 */
Platform pattern
Theme settings → Colors: adjust the scheme used by this section, or override in assets/base.css.
Generic before/after
.price--sale { color: #ff8a8a; } /* 2.6:1 on white */
→
.price--sale { color: #c62828; } /* 5.6:1 on white */
All affected locations (6)
https://demo-store.example/.muted
https://demo-store.example/cartp
https://demo-store.example/products/linen-shirt#shopify-section-footer > .muted
https://demo-store.example/pages/contactp
https://demo-store.example/collections/allp
https://demo-store.example/products/linen-trousers#shopify-section-footer > .muted

Rule reference: color-contrast

HIGHWCAG ADetected automatically – needs human judgement

13. Image has no text alternative

1.1.1 Non-text Content — 2 pages, 4 instances — Content (editor)

Why it matters: Screen readers announce the file name or just "image", so the meaning (product, logo, chart) is lost. Affects screen-reader users.

Automation can detect a missing alt but cannot judge if alt text is meaningful. Review each image: decorative → alt="", informative → describe purpose.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Content fixes (image alt text, link text in rich text) are made in the Shopify admin, not the theme: Products → image → "Add alt text", or Online Store → Navigation.

  1. Add an alt attribute describing the image purpose (not "image of…").
  2. If purely decorative, use alt="" so it is skipped.
  3. For links that contain only an image, the alt must describe the link destination.
Found (example)
<img src="/files/hero_2000x.jpg" width="800" height="300">

img[src$="hero_2000x.jpg"]
https://demo-store.example/

Suggested change
<img src="/files/hero_2000x.jpg" width="800" height="300" alt="Hero 2000x">  <!-- or alt="" if decorative -->
Platform pattern
{{ product.featured_image | image_url: width: 600 | image_tag: alt: product.featured_image.alt | default: product.title }}
Generic before/after
<img src="/blue-linen-shirt.jpg">
→
<img src="/blue-linen-shirt.jpg" alt="Blue linen shirt, front view">
All affected locations (4)
https://demo-store.example/img[src$="hero_2000x.jpg"]
https://demo-store.example/collections/allimg[src$="linen-shirt_400x.jpg"]
https://demo-store.example/collections/allimg[src$="linen-trousers_400x.jpg"]
https://demo-store.example/collections/allimg[src$="linen-dress_400x.jpg"]

Rule reference: image-alt

HIGHWCAG AAutomatically detected

14. ARIA button/link/menuitem has no name

4.1.2 Name, Role, Value — 2 pages, 2 instances — Page template · component: main

Why it matters: The control is announced without its action. Affects screen-reader users, voice-control users.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add text content or aria-label.
Found (example)
<div class="swatch" role="button" style="background:#c00" onclick="this.classList.add('active')"></div>

.swatch
https://demo-store.example/products/linen-shirt

Suggested change
<div class="swatch" role="button" style="background:#c00" onclick="this.classList.add('active')" aria-label="Previous">
Generic before/after
<div role="button"><i class="icon-close"></i></div>
→
<button type="button" aria-label="Close"><i class="icon-close" aria-hidden="true"></i></button>
All affected locations (2)
https://demo-store.example/products/linen-shirt.swatch
https://demo-store.example/products/linen-trousers.swatch

Rule reference: aria-command-name

HIGHWCAG ADetected automatically – needs human judgement

15. Image has no text alternative

1.1.1 Non-text Content — 1 page, 3 instances — Content (editor) · component: product-card

Why it matters: Screen readers announce the file name or just "image", so the meaning (product, logo, chart) is lost. Affects screen-reader users.

Automation can detect a missing alt but cannot judge if alt text is meaningful. Review each image: decorative → alt="", informative → describe purpose.

Fix once: single location.

Where: Content fixes (image alt text, link text in rich text) are made in the Shopify admin, not the theme: Products → image → "Add alt text", or Online Store → Navigation.

  1. Add an alt attribute describing the image purpose (not "image of…").
  2. If purely decorative, use alt="" so it is skipped.
  3. For links that contain only an image, the alt must describe the link destination.
Found (example)
<img src="/files/linen-shirt_600x.jpg" width="200" height="200">

img[src$="linen-shirt_600x.jpg"]
https://demo-store.example/

Suggested change
<img src="/files/linen-shirt_600x.jpg" width="200" height="200" alt="Linen shirt 600x">  <!-- or alt="" if decorative -->
Platform pattern
{{ product.featured_image | image_url: width: 600 | image_tag: alt: product.featured_image.alt | default: product.title }}
Generic before/after
<img src="/blue-linen-shirt.jpg">
→
<img src="/blue-linen-shirt.jpg" alt="Blue linen shirt, front view">
All affected locations (3)
https://demo-store.example/img[src$="linen-shirt_600x.jpg"]
https://demo-store.example/img[src$="linen-trousers_600x.jpg"]
https://demo-store.example/img[src$="linen-dress_600x.jpg"]

Rule reference: image-alt

HIGHWCAG AAutomatically detected

16. Link has no discernible text

2.4.4 Link Purpose (In Context) · 4.1.2 Name, Role, Value — 1 page, 3 instances — Page template · component: product-card

Why it matters: Screen readers announce "link" or read out the URL; users cannot tell where it goes. Affects screen-reader users, voice-control users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add text inside the link, or an aria-label for icon-only links (e.g. social icons).
  2. If the link wraps an image, give the image alt text describing the destination.
Found (example)
<a href="/products/linen-shirt" class="card__link"><img src="/files/linen-shirt_600x.jpg" width="200" height="200"></a>

a[href$="linen-shirt"]
https://demo-store.example/

Suggested change
<a href="/products/linen-shirt" class="card__link" aria-label="Describe destination"> … </a>
Platform pattern
<a href="{{ settings.social_instagram_link }}" aria-label="Instagram">{% render 'icon-instagram' %}</a>
Generic before/after
<a href="https://instagram.com/brand"><svg>…</svg></a>
→
<a href="https://instagram.com/brand" aria-label="Instagram"><svg aria-hidden="true" focusable="false">…</svg></a>
All affected locations (3)
https://demo-store.example/a[href$="linen-shirt"]
https://demo-store.example/a[href$="linen-trousers"]
https://demo-store.example/a[href$="linen-dress"]

Rule reference: link-name

HIGHWCAG AAKeyboard heuristic – confirm manually

17. Keyboard focus is not visible

2.4.7 Focus Visible — 6 pages, 54 instances — Theme / shared component

Why it matters: Sighted keyboard users cannot see where they are on the page. Affects keyboard-only users, low-vision users.

Heuristic: we compared the element style before/after focus. Confirm by tabbing through the page.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Do not remove outlines (outline: none) without a replacement.
  2. Add a clear :focus-visible style with ≥3:1 contrast.
Found (example)
<button class="header__icon header__icon--menu" onclick="document.getElementById('menu-drawer').classList.toggle('is-open')">

#shopify-section-header > button.header__icon.header__icon--menu
https://demo-store.example/

Suggested change
#shopify-section-header > button.header__icon.header__icon--menu:focus-visible { outline: 3px solid currentColor; outline-offset: 2px; }
Generic before/after
a:focus, button:focus { outline: none; }
→
:focus-visible { outline: 3px solid #1a5fb4; outline-offset: 2px; }
All affected locations (54)
https://demo-store.example/#shopify-section-header > button.header__icon.header__icon--menu
https://demo-store.example/#shopify-section-header > a.header__heading-link
https://demo-store.example/nav > ul.list-menu > li:nth-of-type(1) > a
https://demo-store.example/nav > ul.list-menu > li:nth-of-type(2) > a
https://demo-store.example/#shopify-section-header > a.header__icon.header__icon--cart
https://demo-store.example/#MainContent > div.product-grid > div.card-wrapper.product-card > a.card__link
https://demo-store.example/cart#shopify-section-header > button.header__icon.header__icon--menu
https://demo-store.example/cart#shopify-section-header > a.header__heading-link
https://demo-store.example/cartnav > ul.list-menu > li:nth-of-type(1) > a
https://demo-store.example/cartnav > ul.list-menu > li:nth-of-type(2) > a
https://demo-store.example/cart#shopify-section-header > a.header__icon.header__icon--cart
https://demo-store.example/carttbody > tr:nth-of-type(2) > td:nth-of-type(2) > input
https://demo-store.example/cart#MainContent > button.btn
https://demo-store.example/cart#shopify-section-footer > form.newsletter-form > input
https://demo-store.example/cart#shopify-section-footer > form.newsletter-form > button.btn
https://demo-store.example/cart#shopify-section-footer > ul.list-social > li:nth-of-type(1) > a.list-social__link
https://demo-store.example/cart#shopify-section-footer > ul.list-social > li:nth-of-type(2) > a.list-social__link
https://demo-store.example/products/linen-shirt#shopify-section-header > button.header__icon.header__icon--menu
https://demo-store.example/products/linen-shirt#shopify-section-header > a.header__heading-link
https://demo-store.example/products/linen-shirtnav > ul.list-menu > li:nth-of-type(1) > a
https://demo-store.example/products/linen-shirtnav > ul.list-menu > li:nth-of-type(2) > a
https://demo-store.example/products/linen-shirt#shopify-section-header > a.header__icon.header__icon--cart
https://demo-store.example/products/linen-shirt#shopify-section-template--1234__main > button.btn.product-form__submit
https://demo-store.example/products/linen-shirt#shopify-section-footer > form.newsletter-form > input
https://demo-store.example/products/linen-shirt#shopify-section-footer > form.newsletter-form > button.btn
https://demo-store.example/products/linen-shirt#shopify-section-footer > ul.list-social > li:nth-of-type(1) > a.list-social__link
https://demo-store.example/products/linen-shirt#shopify-section-footer > ul.list-social > li:nth-of-type(2) > a.list-social__link
https://demo-store.example/pages/contact#shopify-section-header > button.header__icon.header__icon--menu
https://demo-store.example/pages/contact#shopify-section-header > a.header__heading-link
https://demo-store.example/pages/contactnav > ul.list-menu > li:nth-of-type(1) > a
https://demo-store.example/pages/contactnav > ul.list-menu > li:nth-of-type(2) > a
https://demo-store.example/pages/contact#shopify-section-header > a.header__icon.header__icon--cart
https://demo-store.example/pages/contact#MainContent > form.contact-form > input
https://demo-store.example/pages/contact#MainContent > form.contact-form > button.btn
https://demo-store.example/pages/contact#shopify-section-footer > form.newsletter-form > input
https://demo-store.example/pages/contact#shopify-section-footer > form.newsletter-form > button.btn
https://demo-store.example/pages/contact#shopify-section-footer > ul.list-social > li:nth-of-type(1) > a.list-social__link
https://demo-store.example/pages/contact#shopify-section-footer > ul.list-social > li:nth-of-type(2) > a.list-social__link
https://demo-store.example/collections/all#shopify-section-header > button.header__icon.header__icon--menu
https://demo-store.example/collections/all#shopify-section-header > a.header__heading-link
https://demo-store.example/collections/allnav > ul.list-menu > li:nth-of-type(1) > a
https://demo-store.example/collections/allnav > ul.list-menu > li:nth-of-type(2) > a
https://demo-store.example/collections/all#shopify-section-header > a.header__icon.header__icon--cart
https://demo-store.example/collections/all#MainContent > ul.grid > li.grid__item > a
https://demo-store.example/products/linen-trousers#shopify-section-header > button.header__icon.header__icon--menu
https://demo-store.example/products/linen-trousers#shopify-section-header > a.header__heading-link
https://demo-store.example/products/linen-trousersnav > ul.list-menu > li:nth-of-type(1) > a
https://demo-store.example/products/linen-trousersnav > ul.list-menu > li:nth-of-type(2) > a
https://demo-store.example/products/linen-trousers#shopify-section-header > a.header__icon.header__icon--cart
https://demo-store.example/products/linen-trousers#shopify-section-template--1234__main > button.btn.product-form__submit
https://demo-store.example/products/linen-trousers#shopify-section-footer > form.newsletter-form > input
https://demo-store.example/products/linen-trousers#shopify-section-footer > form.newsletter-form > button.btn
https://demo-store.example/products/linen-trousers#shopify-section-footer > ul.list-social > li:nth-of-type(1) > a.list-social__link
https://demo-store.example/products/linen-trousers#shopify-section-footer > ul.list-social > li:nth-of-type(2) > a.list-social__link
HIGHWCAG AAutomatically detected

18. Link has no discernible text

2.4.4 Link Purpose (In Context) · 4.1.2 Name, Role, Value — 1 page, 3 instances — Single page

Why it matters: Screen readers announce "link" or read out the URL; users cannot tell where it goes. Affects screen-reader users, voice-control users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add text inside the link, or an aria-label for icon-only links (e.g. social icons).
  2. If the link wraps an image, give the image alt text describing the destination.
Found (example)
<a href="/products/linen-shirt"><img src="/files/linen-shirt_400x.jpg"></a>

a[href$="linen-shirt"]
https://demo-store.example/collections/all

Suggested change
<a href="/products/linen-shirt" aria-label="Describe destination"> … </a>
Platform pattern
<a href="{{ settings.social_instagram_link }}" aria-label="Instagram">{% render 'icon-instagram' %}</a>
Generic before/after
<a href="https://instagram.com/brand"><svg>…</svg></a>
→
<a href="https://instagram.com/brand" aria-label="Instagram"><svg aria-hidden="true" focusable="false">…</svg></a>
All affected locations (3)
https://demo-store.example/collections/alla[href$="linen-shirt"]
https://demo-store.example/collections/alla[href$="linen-trousers"]
https://demo-store.example/collections/alla[href$="linen-dress"]

Rule reference: link-name

HIGHWCAG AAutomatically detected

19. Page language is not declared

3.1.1 Language of Page — 2 pages, 2 instances — Single page

Why it matters: Screen readers guess the pronunciation language; English text may be read with the wrong voice. Affects screen-reader users.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add lang to the <html> element.
Found (example)
<html>

html
https://demo-store.example/products/linen-shirt

Suggested change
<html lang="en">  <!-- use the page's real language code -->
Platform pattern
<html lang="{{ request.locale.iso_code }}"> in layout/theme.liquid
Generic before/after
<html>
→
<html lang="en">
All affected locations (2)
https://demo-store.example/products/linen-shirthtml
https://demo-store.example/products/linen-trousershtml

Rule reference: html-has-lang

MEDIUMWCAG AAAutomatically detected

20. Text colour contrast is too low

1.4.3 Contrast (Minimum) — 2 pages, 2 instances — Page template · component: main

Why it matters: Low-contrast text is hard or impossible to read for people with low vision, older users and anyone on a phone in sunlight. Affects low-vision users, colour-blind users.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Raise contrast to at least 4.5:1 for normal text, 3:1 for large text (≥24px or ≥18.66px bold).
  2. Change the colour in the theme/design tokens (one change fixes every page).
Found (example)
<span class="price--sale">$39.00</span>

span
https://demo-store.example/products/linen-shirt

Suggested change
/* #ff9d9d on #ffffff = 1.98:1, needs 4.5:1 */
span { color: #a46363; } /* ≈4.62:1 */
Platform pattern
Theme settings → Colors: adjust the scheme used by this section, or override in assets/base.css.
Generic before/after
.price--sale { color: #ff8a8a; } /* 2.6:1 on white */
→
.price--sale { color: #c62828; } /* 5.6:1 on white */
All affected locations (2)
https://demo-store.example/products/linen-shirtspan
https://demo-store.example/products/linen-trousersspan

Rule reference: color-contrast

MEDIUMWCAG AAAutomatically detected

21. Text colour contrast is too low

1.4.3 Contrast (Minimum) — 2 pages, 2 instances — Page template · component: main

Why it matters: Low-contrast text is hard or impossible to read for people with low vision, older users and anyone on a phone in sunlight. Affects low-vision users, colour-blind users.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Raise contrast to at least 4.5:1 for normal text, 3:1 for large text (≥24px or ≥18.66px bold).
  2. Change the colour in the theme/design tokens (one change fixes every page).
Found (example)
<s class="muted">$59.00</s>

s
https://demo-store.example/products/linen-shirt

Suggested change
/* #b5b5b5 on #ffffff = 2.05:1, needs 4.5:1 */
s { color: #737373; } /* ≈4.74:1 */
Platform pattern
Theme settings → Colors: adjust the scheme used by this section, or override in assets/base.css.
Generic before/after
.price--sale { color: #ff8a8a; } /* 2.6:1 on white */
→
.price--sale { color: #c62828; } /* 5.6:1 on white */
All affected locations (2)
https://demo-store.example/products/linen-shirts
https://demo-store.example/products/linen-trouserss

Rule reference: color-contrast

MEDIUMWCAG AAutomatically detected

22. Page language is not declared

3.1.1 Language of Page — 1 page, 1 instance — Single page

Why it matters: Screen readers guess the pronunciation language; English text may be read with the wrong voice. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add lang to the <html> element.
Found (example)
<html>

html
https://demo-store.example/cart

Suggested change
<html lang="en">  <!-- use the page's real language code -->
Platform pattern
<html lang="{{ request.locale.iso_code }}"> in layout/theme.liquid
Generic before/after
<html>
→
<html lang="en">

Rule reference: html-has-lang

MEDIUMWCAG AKeyboard heuristic – confirm manually

23. Clickable element cannot be reached with the keyboard

2.1.1 Keyboard — 2 pages, 2 instances — Single page

Why it matters: Elements with click handlers but no keyboard focus (div/span "buttons") are unusable without a mouse. Affects keyboard-only users, screen-reader users, voice-control users.

Heuristic: element looks interactive (cursor:pointer / onclick / role) but never received focus while tabbing. Confirm manually.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Use a real <button> or <a href>.
  2. If you must use a div, add role="button", tabindex="0" and Enter/Space key handlers.
Found (example)
<div class="swatch" role="button" style="background:#c00" onclick="this.classList.add('active')"></div>

div.swatch
https://demo-store.example/products/linen-shirt

Suggested change
<button type="button" class="swatch" role="button" style="background:#c00" onclick="this.classList.add('active')"></button>
Generic before/after
<div class="swatch" onclick="pick('red')">Red</div>
→
<button type="button" class="swatch" onclick="pick('red')" aria-pressed="false">Red</button>
All affected locations (2)
https://demo-store.example/products/linen-shirtdiv.swatch
https://demo-store.example/products/linen-trousersdiv.swatch
MEDIUMWCAG AAutomatically detected

24. Page language is not declared

3.1.1 Language of Page — 1 page, 1 instance — Single page

Why it matters: Screen readers guess the pronunciation language; English text may be read with the wrong voice. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add lang to the <html> element.
Found (example)
<html>

html
https://demo-store.example/pages/contact

Suggested change
<html lang="en">  <!-- use the page's real language code -->
Platform pattern
<html lang="{{ request.locale.iso_code }}"> in layout/theme.liquid
Generic before/after
<html>
→
<html lang="en">

Rule reference: html-has-lang

MEDIUMWCAG AAAutomatically detected

25. Text colour contrast is too low

1.4.3 Contrast (Minimum) — 1 page, 3 instances — Page template · component: product-card

Why it matters: Low-contrast text is hard or impossible to read for people with low vision, older users and anyone on a phone in sunlight. Affects low-vision users, colour-blind users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Raise contrast to at least 4.5:1 for normal text, 3:1 for large text (≥24px or ≥18.66px bold).
  2. Change the colour in the theme/design tokens (one change fixes every page).
Found (example)
<p class="price--sale">$39</p>

.card-wrapper.product-card:nth-child(1) > .price--sale
https://demo-store.example/

Suggested change
/* #ff9d9d on #ffffff = 1.98:1, needs 4.5:1 */
.card-wrapper.product-card:nth-child(1) > .price--sale { color: #a46363; } /* ≈4.62:1 */
Platform pattern
Theme settings → Colors: adjust the scheme used by this section, or override in assets/base.css.
Generic before/after
.price--sale { color: #ff8a8a; } /* 2.6:1 on white */
→
.price--sale { color: #c62828; } /* 5.6:1 on white */
All affected locations (3)
https://demo-store.example/.card-wrapper.product-card:nth-child(1) > .price--sale
https://demo-store.example/.card-wrapper.product-card:nth-child(2) > .price--sale
https://demo-store.example/.card-wrapper.product-card:nth-child(3) > .price--sale

Rule reference: color-contrast

MEDIUMWCAG AAutomatically detected

26. Page has no title

2.4.2 Page Titled — 1 page, 1 instance — Single page

Why it matters: The title is the first thing announced and identifies the tab. Affects screen-reader users, users with cognitive disabilities.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add a unique, descriptive <title>.
Found (example)
<html>

html
https://demo-store.example/

Suggested change
<title>Page topic – Site name</title>
Generic before/after
<head>…</head>
→
<title>Blue Linen Shirt – Brand</title>

Rule reference: document-title

MEDIUMWCAG AAutomatically detected

27. Page language is not declared

3.1.1 Language of Page — 1 page, 1 instance — Single page

Why it matters: Screen readers guess the pronunciation language; English text may be read with the wrong voice. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add lang to the <html> element.
Found (example)
<html>

html
https://demo-store.example/

Suggested change
<html lang="en">  <!-- use the page's real language code -->
Platform pattern
<html lang="{{ request.locale.iso_code }}"> in layout/theme.liquid
Generic before/after
<html>
→
<html lang="en">

Rule reference: html-has-lang

MEDIUMWCAG AAutomatically detected

28. Page language is not declared

3.1.1 Language of Page — 1 page, 1 instance — Single page

Why it matters: Screen readers guess the pronunciation language; English text may be read with the wrong voice. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add lang to the <html> element.
Found (example)
<html>

html
https://demo-store.example/collections/all

Suggested change
<html lang="en">  <!-- use the page's real language code -->
Platform pattern
<html lang="{{ request.locale.iso_code }}"> in layout/theme.liquid
Generic before/after
<html>
→
<html lang="en">

Rule reference: html-has-lang

MEDIUMWCAG AAAutomatically detected

29. Touch target is too small (WCAG 2.2)

2.5.8 Target Size (Minimum) — 1 page, 3 instances — Single page

Why it matters: Small, tightly packed controls are hard to hit for users with tremors or on mobile. Affects users with motor impairments.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Make interactive targets at least 24×24 CSS px, or space them so a 24px circle around each does not overlap another.
Found (example)
<a href="/products/linen-shirt"><img src="/files/linen-shirt_400x.jpg"></a>

a[href$="linen-shirt"]
https://demo-store.example/collections/all

Suggested change
a[href$="linen-shirt"] { min-width: 24px; min-height: 24px; }
Generic before/after
.swatch { width: 16px; height: 16px; }
→
.swatch { min-width: 24px; min-height: 24px; }
All affected locations (3)
https://demo-store.example/collections/alla[href$="linen-shirt"]
https://demo-store.example/collections/alla[href$="linen-trousers"]
https://demo-store.example/collections/alla[href$="linen-dress"]

Rule reference: target-size

MEDIUMWCAG AKeyboard heuristic – confirm manually

30. Focus moves to hidden or off-screen content

2.4.3 Focus Order — 6 pages, 12 instances — Theme / shared component

Why it matters: Keyboard users tab into closed menus/drawers and lose track of focus. Affects keyboard-only users, screen-reader users.

Heuristic: a focused element was invisible or outside the viewport. Confirm manually.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. When a drawer/menu is closed, hide it with display:none / hidden / inert, not just transform or opacity.
Found (example)
<a href="/pages/about">

#menu-drawer > a:nth-of-type(1)
https://demo-store.example/

Suggested change
#menu-drawer > a:nth-of-type(1) { visibility: hidden; } /* while closed; or add the inert attribute */
Generic before/after
.menu-drawer { transform: translateX(-100%); }
→
.menu-drawer:not([open]) { visibility: hidden; } /* or the inert attribute */
All affected locations (12)
https://demo-store.example/#menu-drawer > a:nth-of-type(1)
https://demo-store.example/#menu-drawer > a:nth-of-type(2)
https://demo-store.example/cart#menu-drawer > a:nth-of-type(1)
https://demo-store.example/cart#menu-drawer > a:nth-of-type(2)
https://demo-store.example/products/linen-shirt#menu-drawer > a:nth-of-type(1)
https://demo-store.example/products/linen-shirt#menu-drawer > a:nth-of-type(2)
https://demo-store.example/pages/contact#menu-drawer > a:nth-of-type(1)
https://demo-store.example/pages/contact#menu-drawer > a:nth-of-type(2)
https://demo-store.example/collections/all#menu-drawer > a:nth-of-type(1)
https://demo-store.example/collections/all#menu-drawer > a:nth-of-type(2)
https://demo-store.example/products/linen-trousers#menu-drawer > a:nth-of-type(1)
https://demo-store.example/products/linen-trousers#menu-drawer > a:nth-of-type(2)
LOWBest practiceAutomatically detected

31. Content is outside landmarks

Not a WCAG failure on its own (best practice) — 2 pages, 14 instances — Page template · component: main

Why it matters: Content not inside header/nav/main/footer can be missed when navigating by landmark. Affects screen-reader users.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Place all visible content inside <header>, <nav>, <main>, <aside> or <footer>.
Found (example)
<img src="/files/linen-shirt-front_1200x.jpg" width="400" height="400">

img[width="400"]
https://demo-store.example/products/linen-shirt

Suggested change
<header><div class="announcement">Free shipping</div></header>
All affected locations (14)
https://demo-store.example/products/linen-shirtimg[width="400"]
https://demo-store.example/products/linen-shirt.h1
https://demo-store.example/products/linen-shirt#shopify-section-template--1234__main > p
https://demo-store.example/products/linen-shirtlegend
https://demo-store.example/products/linen-shirtselect
https://demo-store.example/products/linen-shirth4
https://demo-store.example/products/linen-shirt.product__description
https://demo-store.example/products/linen-trousersimg[width="400"]
https://demo-store.example/products/linen-trousers.h1
https://demo-store.example/products/linen-trousers#shopify-section-template--1234__main > p
https://demo-store.example/products/linen-trouserslegend
https://demo-store.example/products/linen-trousersselect
https://demo-store.example/products/linen-trousersh4
https://demo-store.example/products/linen-trousers.product__description

Rule reference: region

LOWBest practiceAutomatically detected

32. Dialog has no accessible name

Not a WCAG failure on its own (best practice) — 1 page, 1 instance — Page template · component: popup-newsletter

Why it matters: When a popup/modal opens, screen readers do not announce what it is. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add aria-labelledby pointing at the dialog heading, or aria-label.
Found (example)
<div role="dialog" class="popup-newsletter"><button class="popup__close">×</button></div>

.popup-newsletter
https://demo-store.example/

Suggested change
<div role="dialog" class="popup-newsletter" aria-labelledby="dialog-title">  <!-- and id="dialog-title" on its heading -->
Generic before/after
<div role="dialog">
→
<div role="dialog" aria-modal="true" aria-labelledby="popup-title"><h2 id="popup-title">Get 10% off</h2>

Rule reference: aria-dialog-name

LOWBest practiceAutomatically detected

33. Page has no main landmark

Not a WCAG failure on its own (best practice) — 2 pages, 2 instances — Single page

Why it matters: Screen-reader users cannot jump straight to the main content. Affects screen-reader users, keyboard-only users.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Wrap the primary content in <main id="MainContent">.
Found (example)
<html>

html
https://demo-store.example/products/linen-shirt

Suggested change
<main id="MainContent" tabindex="-1"> … </main>
Generic before/after
<div id="content">…</div>
→
<main id="MainContent">…</main>
All affected locations (2)
https://demo-store.example/products/linen-shirthtml
https://demo-store.example/products/linen-trousershtml

Rule reference: landmark-one-main

LOWBest practiceAutomatically detected

34. Page has no main heading (h1)

Not a WCAG failure on its own (best practice) — 2 pages, 2 instances — Single page

Why it matters: Users jumping to the h1 cannot find the page topic. Affects screen-reader users.

Fix once: this is one root cause repeated on 2 pages — fix the shared template, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add exactly one h1 that describes the page (product title, collection name).
Found (example)
<html>

html
https://demo-store.example/products/linen-shirt

Suggested change
<h1 class="title">Summer Collection</h1>
Platform pattern
<h1 class="title">{{ collection.title }}</h1> in sections/main-collection-banner.liquid
All affected locations (2)
https://demo-store.example/products/linen-shirthtml
https://demo-store.example/products/linen-trousershtml

Rule reference: page-has-heading-one

LOWBest practiceAutomatically detected

35. Content is outside landmarks

Not a WCAG failure on its own (best practice) — 1 page, 2 instances — Single page

Why it matters: Content not inside header/nav/main/footer can be missed when navigating by landmark. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Place all visible content inside <header>, <nav>, <main>, <aside> or <footer>.
Found (example)
<h1>Your cart</h1>

h1
https://demo-store.example/cart

Suggested change
<header><div class="announcement">Free shipping</div></header>
All affected locations (2)
https://demo-store.example/carth1
https://demo-store.example/carttable

Rule reference: region

LOWBest practiceAutomatically detected

36. Page has no main landmark

Not a WCAG failure on its own (best practice) — 1 page, 1 instance — Single page

Why it matters: Screen-reader users cannot jump straight to the main content. Affects screen-reader users, keyboard-only users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Wrap the primary content in <main id="MainContent">.
Found (example)
<html>

html
https://demo-store.example/cart

Suggested change
<main id="MainContent" tabindex="-1"> … </main>
Generic before/after
<div id="content">…</div>
→
<main id="MainContent">…</main>

Rule reference: landmark-one-main

LOWBest practiceAutomatically detected

37. Content is outside landmarks

Not a WCAG failure on its own (best practice) — 1 page, 3 instances — Single page

Why it matters: Content not inside header/nav/main/footer can be missed when navigating by landmark. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Place all visible content inside <header>, <nav>, <main>, <aside> or <footer>.
Found (example)
<h1>Contact</h1>

h1
https://demo-store.example/pages/contact

Suggested change
<header><div class="announcement">Free shipping</div></header>
All affected locations (3)
https://demo-store.example/pages/contacth1
https://demo-store.example/pages/contactinput[name="contact[name]"]
https://demo-store.example/pages/contacttextarea

Rule reference: region

LOWBest practiceAutomatically detected

38. Content is outside landmarks

Not a WCAG failure on its own (best practice) — 1 page, 3 instances — Single page

Why it matters: Content not inside header/nav/main/footer can be missed when navigating by landmark. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Place all visible content inside <header>, <nav>, <main>, <aside> or <footer>.
Found (example)
<div class="banner__heading">Summer linen</div>

.banner__heading
https://demo-store.example/

Suggested change
<header><div class="announcement">Free shipping</div></header>
All affected locations (3)
https://demo-store.example/.banner__heading
https://demo-store.example/img[src$="hero_2000x.jpg"]
https://demo-store.example/.product-grid

Rule reference: region

LOWBest practiceAutomatically detected

39. Page has no main landmark

Not a WCAG failure on its own (best practice) — 1 page, 1 instance — Single page

Why it matters: Screen-reader users cannot jump straight to the main content. Affects screen-reader users, keyboard-only users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Wrap the primary content in <main id="MainContent">.
Found (example)
<html>

html
https://demo-store.example/pages/contact

Suggested change
<main id="MainContent" tabindex="-1"> … </main>
Generic before/after
<div id="content">…</div>
→
<main id="MainContent">…</main>

Rule reference: landmark-one-main

LOWBest practiceAutomatically detected

40. Page has no main landmark

Not a WCAG failure on its own (best practice) — 1 page, 1 instance — Single page

Why it matters: Screen-reader users cannot jump straight to the main content. Affects screen-reader users, keyboard-only users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Wrap the primary content in <main id="MainContent">.
Found (example)
<html>

html
https://demo-store.example/collections/all

Suggested change
<main id="MainContent" tabindex="-1"> … </main>
Generic before/after
<div id="content">…</div>
→
<main id="MainContent">…</main>

Rule reference: landmark-one-main

LOWBest practiceAutomatically detected

41. Content is outside landmarks

Not a WCAG failure on its own (best practice) — 1 page, 1 instance — Single page

Why it matters: Content not inside header/nav/main/footer can be missed when navigating by landmark. Affects screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Place all visible content inside <header>, <nav>, <main>, <aside> or <footer>.
Found (example)
<div id="MainContent">

#MainContent
https://demo-store.example/collections/all

Suggested change
<header><div class="announcement">Free shipping</div></header>

Rule reference: region

Needs human review (2)

Automation could not decide these. They are not counted as failures.

MANUAL REVIEWWCAG AANeeds human review

1. Touch target is too small (WCAG 2.2)

2.5.8 Target Size (Minimum) — 6 pages, 12 instances — Theme / shared component · component: footer

Why it matters: Small, tightly packed controls are hard to hit for users with tremors or on mobile. Affects users with motor impairments.

Fix once: this is one root cause repeated on 6 pages — fix the shared theme component, not each page.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Make interactive targets at least 24×24 CSS px, or space them so a 24px circle around each does not overlap another.
Found (example)
<a href="https://instagram.com/demo" class="list-social__link"><svg class="icon icon-instagram" width="18" height="18"><rect width="18" height="18"></rect></svg></a>

li:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/

Suggested change
li:nth-child(1) > .list-social__link[href$="demo"] { min-width: 24px; min-height: 24px; }
Generic before/after
.swatch { width: 16px; height: 16px; }
→
.swatch { min-width: 24px; min-height: 24px; }
All affected locations (12)
https://demo-store.example/li:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/li:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/cartli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/cartli:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/products/linen-shirtli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/products/linen-shirtli:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/pages/contactli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/pages/contactli:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/collections/allli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/collections/allli:nth-child(2) > .list-social__link[href$="demo"]
https://demo-store.example/products/linen-trousersli:nth-child(1) > .list-social__link[href$="demo"]
https://demo-store.example/products/linen-trousersli:nth-child(2) > .list-social__link[href$="demo"]

Rule reference: target-size

MANUAL REVIEWWCAG ANeeds human review

2. No way to skip repeated navigation

2.4.1 Bypass Blocks — 1 page, 1 instance — Single page

Why it matters: Keyboard users must tab through the whole menu on every page. Affects keyboard-only users, screen-reader users.

Fix once: single location.

Where: Shopify admin → Online Store → Themes → … → Edit code. Header/footer/nav live in sections/header.liquid, sections/footer.liquid and snippets/*.liquid; product cards usually in snippets/card-product.liquid.

  1. Add a "Skip to content" link as the first focusable element, targeting <main id="MainContent">.
Found (example)
<html>

html
https://demo-store.example/

Suggested change
<a class="skip-link visually-hidden-focusable" href="#MainContent">Skip to content</a>
Generic before/after
<body><header>…
→
<body><a class="skip-link" href="#MainContent">Skip to content</a><header>…

Rule reference: bypass

Manual test checklist

Record PASS / FAIL / NOT_TESTED / NOT_APPLICABLE for each check.

CheckWCAGHow to testResult
Complete the main journey with the keyboard only2.1.1, 2.1.2, 2.4.3
  1. Unplug/ignore the mouse. Start at the home page and press Tab.
  2. Complete the key task (e.g. find a product, choose a variant, add to cart, reach checkout; or submit the contact form).
  3. Use Enter/Space to activate, Escape to close popups, arrow keys in menus/radios.
  4. FAIL if any step needs a mouse, focus gets stuck, or focus disappears.
NOT_TESTED
Focus is always visible and not hidden behind sticky bars2.4.7, 2.4.11
  1. Tab through the page.
  2. At every stop you must clearly see which element has focus.
  3. Focused elements must not be fully covered by a sticky header, cookie banner or chat bubble.
NOT_TESTED
Screen reader: headings and landmarks describe the page1.3.1, 2.4.6
  1. Windows: start NVDA (free, nvaccess.org) and press H / Shift+H. Mac: VoiceOver (Cmd+F5), VO+U → Headings.
  2. The list of headings should outline the page; landmarks should include banner, navigation, main, contentinfo.
  3. Record FAIL if headings are missing, out of order, or used for styling only.
NOT_TESTED
Screen reader: every control announces name, role and state4.1.2, 2.5.3
  1. With NVDA/VoiceOver on, Tab to each button, link and field.
  2. Each should announce a meaningful name ("Add to cart", not "button"), the role, and state (expanded/collapsed, selected, checked).
  3. Menus/accordions must announce expanded/collapsed when toggled.
NOT_TESTED
Image alt text is meaningful (not just present)1.1.1
  1. For each informative image, the alt should convey what a sighted user learns from it (product, colour, view).
  2. Decorative images should have alt="".
  3. File names ("IMG_2231.jpg") or "image" are FAIL.
NOT_TESTED
Form errors are announced and explained3.3.1, 3.3.3, 4.1.3
  1. Submit each form empty and with an invalid email.
  2. Errors must be shown in text (not colour only), placed next to the field, and announced by the screen reader (aria-live or focus moves to the error summary).
  3. Each error should say how to fix it.
NOT_TESTED
Popups and drawers manage focus correctly2.4.3, 2.1.2
  1. Open each popup/drawer (cart drawer, menu, newsletter popup, quick view).
  2. Focus should move into it; Tab should stay inside while open; Escape should close it; focus should return to the button that opened it.
NOT_TESTED
Page works at 200% zoom and 320px width1.4.4, 1.4.10
  1. Zoom the browser to 200% (and 400% on desktop).
  2. No content or function may be lost; no horizontal scrolling for text blocks.
NOT_TESTED
Colour is not the only way information is shown1.4.1, 1.4.11
  1. Out-of-stock variants, sale prices, required fields and errors must also use text or icons, not just colour.
  2. Icons and input borders need 3:1 contrast against the background.
NOT_TESTED
Checkout / payment widgets are usable2.1.1, 4.1.2
  1. Proceed to checkout using keyboard and a screen reader.
  2. Note: Shopify's hosted checkout is maintained by Shopify; issues inside it should be reported to Shopify, but your theme's cart and any checkout apps are your responsibility.
NOT_TESTED
Confirm: Keyboard focus is not visible2.4.7
  1. Heuristic: we compared the element style before/after focus. Confirm by tabbing through the page.
  2. Example: https://demo-store.example/ → #shopify-section-header > button.header__icon.header__icon--menu
NOT_TESTED
Confirm: Clickable element cannot be reached with the keyboard2.1.1
  1. Heuristic: element looks interactive (cursor:pointer / onclick / role) but never received focus while tabbing. Confirm manually.
  2. Example: https://demo-store.example/products/linen-shirt → div.swatch
NOT_TESTED
Confirm: Focus moves to hidden or off-screen content2.4.3
  1. Heuristic: a focused element was invisible or outside the viewport. Confirm manually.
  2. Example: https://demo-store.example/ → #menu-drawer > a:nth-of-type(1)
NOT_TESTED
Confirm: Touch target is too small (WCAG 2.2)2.5.8
  1. Automation flagged this as needing human review.
  2. Example: https://demo-store.example/ → li:nth-child(1) > .list-social__link[href$="demo"]
NOT_TESTED
Confirm: No way to skip repeated navigation2.4.1
  1. Automation flagged this as needing human review.
  2. Example: https://demo-store.example/ → html
NOT_TESTED

Verification status & remediation history

Baseline scan. After you deploy fixes, run a re-scan: each root cause is marked VERIFIED when it no longer appears on the pages where it was found, or REGRESSION if a verified fix comes back.

Limitations