Meridian LabsShopify accessibility audits

Accessibility audit report: Harbor & Pine Goods

Prepared by
Meridian Labs
Report date
September 25, 2026
Testing dates
September 22 to 23, 2026
Site
harborandpine.example
Theme
A customized version of Shopify's free Dawn theme (as seen in the page source)
Report ID
SAMPLE-0001

Important: this report is testing and remediation guidance, not legal advice or a compliance guarantee. It describes what we found on the pages listed below on the dates listed above. Sites change, and pages outside the scope were not tested. For legal questions, including any demand letter or lawsuit, speak to a lawyer.

1. Summary

The three to fix first:

  1. The size and color pickers on the product page have no name, so screen reader users can't tell which option they're choosing (finding 1).
  2. Nothing is announced when a customer presses Add to cart, so screen reader users don't know it worked (finding 2).
  3. The cart button in the header has no name, so screen reader users hear "link" and nothing else (finding 3).

What already works well: every page has a clear, unique title; the Add to cart and Check out buttons are real buttons with clear names; body text contrast is good; pages reflow at 320 px wide without sideways scrolling; and the checkout entry form fields all have visible labels.

2. Scope

Pages tested
PageURL testedDesktopMobile
Homeharborandpine.example/TestedTested
Collectionharborandpine.example/collections/kitchenTestedTested
Productharborandpine.example/products/sage-stoneware-mugTestedTested
Cartharborandpine.example/cartTestedTested
Checkout entry (up to the payment step; no order placed)Reached from the cart's Check out buttonTestedTested

Standard used as the yardstick: WCAG 2.2, levels A and AA.

Also tested on every page: the header, main menu and mobile menu drawer, footer, and any pop-up (newsletter or cookie banner).

Not tested: pages outside the table, content behind a login, the payment step itself, emails, PDFs, third-party sites the store links to, and native apps.

3. How we tested

  1. Automated scan with axe-core at desktop (1280 px) and mobile (390 px) widths. Every automated result was checked by hand; false positives were removed.
  2. Keyboard only: every control reachable, visible focus, logical order, no traps, menus and pop-ups usable and closable.
  3. Screen readers: VoiceOver (Safari, macOS and iOS) and NVDA (Firefox, Windows). Names, roles and states of controls; headings; image text; form labels and errors; live updates such as "added to cart".
  4. Visual: color contrast of text, prices and buttons; zoom to 200% and reflow at 320 px; motion and auto-playing content.

4. Priority levels

Priority levels
LevelMeaning
BlockerStops some customers from finding, choosing or buying a product. Fix first.
SeriousMakes a key task very hard for some customers.
ModerateCauses confusion or extra effort.
MinorPolish; fix when convenient.

5. Findings

Highest priority first. Findings 7 and 8 come from a third-party app and are also listed in section 6.

1. The size and color pickers have no name

Blocker

Where
Product page, the Size and Color dropdowns under the price (selector: variant-selects select)
What happens
The words "Size" and "Color" are shown above each dropdown but aren't linked to it. A screen reader says "popup button, Medium" or "popup button, Sage", with no hint which option it is.
Who it affects
Blind and low-vision customers using screen readers, and people using voice control, who can't say "Size" to move to the dropdown. Some will buy the wrong size or give up.
WCAG 2.2
1.3.1 Info and Relationships (Level A); 4.1.2 Name, Role, Value (Level A)
How to reproduce
  1. On a Mac, open the product page in Safari and turn on VoiceOver (Command + F5).
  2. Press Tab until you reach the first dropdown.
  3. VoiceOver says "Medium, popup button" and never says "Size".
How to fix
In the theme code, give each dropdown a real label. The theme's "Pills" style for the variant picker already uses a labeled group, so switching to it in the theme editor (Product information, Variant picker block, Style) also fixes this with no code. To keep dropdowns, edit the snippet:
{%- comment -%} before {%- endcomment -%}
<span class="form__label">{{ option.name }}</span>
<select name="options[{{ option.name | escape }}]">

{%- comment -%} after {%- endcomment -%}
<label class="form__label" for="Option-{{ section.id }}-{{ forloop.index0 }}">
  {{ option.name }}
</label>
<select id="Option-{{ section.id }}-{{ forloop.index0 }}"
  name="options[{{ option.name | escape }}]">
Where the fix lives
Theme: snippets/product-variant-options.liquid, or the Variant picker block setting in the theme editor
Screenshot
Drawing of the product page: the words Size and Color sit above two dropdowns showing Medium and Sage. Both dropdowns are outlined in red to mark the problem. Size Medium Color Sage Add to cart
The two dropdowns, outlined. The labels above them are plain text, not linked to the dropdowns.
Re-check result
Filled in after the free re-check.

2. "Added to cart" isn't announced

Serious

Where
Product page, after pressing Add to cart (selector: #cart-notification)
What happens
A small "Added to your cart" panel slides in at the top right, but keyboard focus stays on the Add to cart button and screen readers say nothing. The panel closes on its own after a few seconds.
Who it affects
Screen reader users, who can't tell whether the item was added and may add it two or three times. Keyboard users and people using screen magnifiers may miss the panel before it closes.
WCAG 2.2
4.1.3 Status Messages (Level AA)
How to reproduce
  1. Turn on VoiceOver or NVDA and go to the product page.
  2. Tab to Add to cart and press Enter.
  3. Nothing is read out. The cart count in the header changes silently.
How to fix
Add a status message area once in the theme layout and fill it when the cart updates. Also stop the panel closing on a timer, or move focus into it so people can read it and choose "View cart" or "Continue shopping".
<!-- layout/theme.liquid, just after <body> -->
<p id="CartStatus" class="visually-hidden" role="status"></p>

// assets/product-form.js, after the item is added
document.getElementById('CartStatus').textContent =
  'Added to cart: ' + productTitle + '. Cart now has ' + itemCount + ' items.';
Where the fix lives
Theme: layout/theme.liquid and assets/product-form.js
Screenshot
In a real report: the product page with the "Added to your cart" panel open and focus still on the button.
Re-check result
Filled in after the free re-check.

3. The cart button in the header has no name

Serious

Where
Site header, every page (counted once) (selector: a.header__icon--cart)
What happens
The cart is a shopping bag icon with a number bubble. Screen readers say "link" or "link, 2". The theme's hidden "Cart" text was removed during customization.
Who it affects
Screen reader users, who can't find the cart from the header, and voice control users, who can't say "click Cart".
WCAG 2.2
2.4.4 Link Purpose (In Context) (Level A); 4.1.2 Name, Role, Value (Level A)
How to reproduce
  1. Turn on a screen reader and Tab through the header.
  2. After the search and account icons you reach a link read only as "link, 2".
How to fix
Hide the icon from screen readers and add text that says what the link is and how many items are in the cart.
<a href="{{ routes.cart_url }}" class="header__icon header__icon--cart" id="cart-icon-bubble">
  <span aria-hidden="true">{% render 'icon-cart' %}</span>
  <span class="visually-hidden">
    Cart, {{ cart.item_count }} {{ cart.item_count | pluralize: 'item', 'items' }}
  </span>
</a>
Where the fix lives
Theme: sections/header.liquid
Screenshot
In a real report: the header with the cart icon outlined.
Re-check result
Filled in after the free re-check.

4. You can't see where you are in the mobile menu

Serious

Where
Menu drawer, every page at mobile width or when zoomed (selector: .menu-drawer a)
What happens
Links in the menu drawer show no focus outline, because custom CSS removes it. After the last link, pressing Tab moves focus to the page behind the drawer, where it is hidden under the drawer.
Who it affects
Keyboard users, including people who zoom in on a laptop (which switches the store to the mobile menu), and people with low vision.
WCAG 2.2
2.4.7 Focus Visible (Level AA); 2.4.11 Focus Not Obscured (Minimum) (Level AA)
How to reproduce
  1. Zoom the browser to 200%, or make the window narrow.
  2. Tab to the menu button and press Enter.
  3. Keep pressing Tab. No outline shows on the links, and after "Contact" focus disappears behind the drawer.
How to fix
In the theme editor, open Theme settings, then Custom CSS, and remove .menu-drawer a:focus { outline: none; }. Add a visible focus style instead. Then make the drawer keep focus inside it while open, and close on Escape (Dawn's own trapFocus helper in assets/global.js does this, so check that the customized drawer still calls it).
.menu-drawer a:focus-visible,
.menu-drawer button:focus-visible {
  outline: 3px solid #0b1533;
  outline-offset: 2px;
}
Where the fix lives
Theme: Custom CSS in Theme settings, and assets/global.js
Screenshot
In a real report: the open drawer with focus on a link and no outline showing.
Re-check result
Filled in after the free re-check.

5. Product gallery images have no text description

Moderate

Where
Product page, the main image and its 5 thumbnails (selector: .product__media img)
What happens
The images have no alt text. Screen readers read the file name, such as "H P mug sage 02 underscore 1200 x", or skip the image. The thumbnail buttons are read only as "button".
Who it affects
Blind and low-vision customers, who miss details the photos show, such as the glaze color, size in the hand and the handle shape.
WCAG 2.2
1.1.1 Non-text Content (Level A); 4.1.2 Name, Role, Value (Level A) for the thumbnail buttons
How to reproduce
  1. With a screen reader on, move to the product images.
  2. Listen for a file name or "image" with no description.
How to fix
  1. In your Shopify admin, open Products, choose the product, click each image and add alt text, for example "Sage green stoneware mug, held in one hand, about 4 inches tall".
  2. In the theme, make sure the gallery prints that text: alt="{{ media.alt | escape }}". This theme's customized thumbnail snippet leaves it out.
  3. Give each thumbnail button a name, for example aria-label="Show image {{ forloop.index }} of {{ product.media.size }}".
Where the fix lives
Content in Shopify admin (alt text), and theme: snippets/product-thumbnail.liquid
Screenshot
In a real report: the gallery with each image outlined and the text a screen reader reads out.
Re-check result
Filled in after the free re-check.

6. Sale prices are hard to read

Moderate

Where
Home page product cards and the product page price (selectors: .price-item--sale, .price-item--regular s)
What happens
The sale price is light coral (#E8837A) on white: a contrast ratio of 2.6:1. The crossed-out regular price is light gray (#A3A3A3): 2.5:1. Text this size needs at least 4.5:1.
Who it affects
Customers with low vision or color vision deficiency, older customers, and anyone shopping on a phone outdoors.
WCAG 2.2
1.4.3 Contrast (Minimum) (Level AA)
How to reproduce
  1. Open the home page and find a product on sale.
  2. Measure the price colors with a contrast checker, such as the one built into your browser's developer tools.
How to fix
In the theme editor, open Theme settings, then Custom CSS, and change the two colors. A darker red such as #B42318 keeps the "sale" feel at 6.6:1, and #6B6B6B for the old price gives 5.3:1. If the colors come from a color scheme instead, change them in Theme settings, Colors.
Where the fix lives
Theme: Custom CSS or the color scheme in Theme settings
Screenshot
Drawing of two price lines. Before: the sale price 24 dollars in light coral next to a crossed-out 32 dollars in light gray, both hard to read. After: the same prices in dark red and dark gray, much easier to read. Before $24.00 $32.00 After $24.00 $32.00 2.6:1 and 2.5:1 6.6:1 and 5.3:1
Current sale price colors (left) and the suggested ones (right).
Re-check result
Filled in after the free re-check.

7. Star ratings are read as "star, star, star, star, star"

Moderate

Where
Product page under the title, and home page product cards. Comes from the reviews app (called "ReviewNest" in this sample) (selector: .rn-stars img)
What happens
The rating is five separate star images, each with the alt text "star". Screen readers read "star" five times whether the product is rated 1 or 5, and never say the number of reviews.
Who it affects
Screen reader users, who can't tell how well a product is rated.
WCAG 2.2
1.1.1 Non-text Content (Level A); 1.3.1 Info and Relationships (Level A)
How to reproduce
  1. With a screen reader on, move to the stars under the product title.
  2. You hear "star" five times and no rating.
How to fix
Check the app's settings for an accessibility or "star style" option first. If there isn't one, send this finding to the app's developer and ask for a single text alternative such as "Rated 4.6 out of 5, 128 reviews". As a stopgap, the theme can print that text from the standard product reviews metafields (product.metafields.reviews.rating and reviews.rating_count) if the app fills them in.
Where the fix lives
App: ReviewNest (settings or developer); theme stopgap in sections/main-product.liquid
Screenshot
In a real report: the star rating outlined, with what the screen reader reads out.
Re-check result
Filled in after the free re-check.

8. The "Write a review" star picker only works with a mouse

Moderate

Where
Product page, the review form opened by "Write a review". Comes from the reviews app (selector: .rn-form .rn-star-picker span)
What happens
The stars for choosing a rating are plain elements that only respond to a click. Tab skips over them, so the form can't be sent without a mouse.
Who it affects
Keyboard users and screen reader users, who can't leave a review. It doesn't stop a purchase, which is why it isn't a blocker here.
WCAG 2.2
2.1.1 Keyboard (Level A); 4.1.2 Name, Role, Value (Level A)
How to reproduce
  1. Open "Write a review" using only the keyboard.
  2. Press Tab. Focus jumps from the close button to the Name field, skipping the stars.
How to fix
This is inside the app, so the store owner can't change it directly. Send the finding to the developer and ask them to use a group of radio buttons ("1 star" to "5 stars") for the rating. If they can't fix it, consider a reviews app whose form works with a keyboard.
Where the fix lives
App: ReviewNest (developer)
Screenshot
In a real report: the review form with the stars outlined.
Re-check result
Filled in after the free re-check.

6. Issues from third-party apps

Problems caused by installed apps (reviews, pop-ups, chat, upsells) are listed here separately. The store owner usually can't change the app's code; the fix is to change settings, swap the app or send the finding to the app's developer.

App issues
AppProblemFindingSuggested action
ReviewNest (reviews)Star rating read as five "star" images, no score7Check app settings; ask the developer for a text rating; theme stopgap from review metafields
ReviewNest (reviews)Review form star picker needs a mouse8Send to the developer; consider another reviews app if it isn't fixed

7. Re-check

One free re-check of the pages in section 2 within 30 days of this report. Reply to the report email when your fixes are live. We'll update the "Re-check result" line on each finding and send a short summary.

8. What this report is and isn't

See the audit price