Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

shopify-accessible-components

Free, open-source, WCAG-conscious Liquid components for Shopify themes — a modal, a cart drawer, tabs, an accordion, a quantity selector, an image gallery, collapsible filters, and a skip link. Every one is keyboard-operable, screen-reader labelled, honors prefers-reduced-motion, and degrades to working markup if JavaScript fails to load.

→ Want to know how your live theme actually scores? Try the free audit — we built these components because we kept rebuilding the same accessible patterns from scratch for client themes, so we open-sourced them. No signup required.

Copy the snippet(s) you need into your theme's snippets/ folder, load the shared CSS/JS once, and render them like any other snippet.

Example output

The tabs component (accessible-tabs.liquid), rendered:

<a11y-tabs class="a11y-tabs">
  <div role="tablist" aria-label="Product information" class="a11y-tabs__list">
    <button role="tab" aria-selected="true"  tabindex="0"  aria-controls="AccessiblePanel-product-info-0">Description</button>
    <button role="tab" aria-selected="false" tabindex="-1" aria-controls="AccessiblePanel-product-info-1">Shipping</button>
    <button role="tab" aria-selected="false" tabindex="-1" aria-controls="AccessiblePanel-product-info-2">Reviews</button>
  </div>
  <div role="tabpanel" id="AccessiblePanel-product-info-0" tabindex="0">…</div>
  <div role="tabpanel" id="AccessiblePanel-product-info-1" tabindex="0" hidden>…</div>
  <div role="tabpanel" id="AccessiblePanel-product-info-2" tabindex="0" hidden>…</div>
</a11y-tabs>

Tab, Shift+Tab, and the Left/Right/Home/End arrow keys all work exactly as VoiceOver, NVDA, and JAWS users expect from the WAI-ARIA tabs pattern — no custom JS to write.

Features

Component File Pattern
Skip link snippets/skip-to-content.liquid First focusable element on the page
Modal snippets/accessible-modal.liquid Native <dialog>, focus trap, Escape + backdrop close
Cart drawer snippets/accessible-cart-drawer.liquid Same as modal, plus a live region for cart changes
Accordion snippets/accessible-accordion.liquid Native <details>/<summary>, no JS required
Tabs snippets/accessible-tabs.liquid WAI-ARIA tabs, roving tabindex, arrow-key navigation
Quantity selector snippets/accessible-quantity-selector.liquid Labelled stepper, live region announces the new value
Image gallery snippets/accessible-image-gallery.liquid Keyboard-navigable thumbnails, live region on selection
Product filters snippets/accessible-product-filters.liquid Disclosure groups, live region announces result count

Shared behavior lives in two files so it's loaded once, not duplicated per component:

  • assets/accessible-components.css — visually-hidden utility, skip-link, focus-visible rings, prefers-reduced-motion handling, and each component's layout styles
  • assets/accessible-components.js — a FocusTrap class, a clear-then-set announce() helper for live regions, and one Web Component per interactive pattern (<a11y-modal>, <a11y-cart-drawer>, <a11y-tabs>, <a11y-quantity-selector>, <a11y-gallery>, <a11y-filters>)

Installation

  1. Copy the snippet(s) you want from snippets/ into your theme's snippets/ folder.

  2. Copy assets/accessible-components.css and assets/accessible-components.js into your theme's assets/ folder.

  3. Copy the keys you need from locales/en.default.json into your theme's own locale file (or merge the whole file in if you don't have conflicting keys).

  4. Load the shared CSS and JS once, in layout/theme.liquid:

    {{ 'accessible-components.css' | asset_url | stylesheet_tag }}
    <script src="{{ 'accessible-components.js' | asset_url }}" defer="defer"></script>

Usage

{% render 'accessible-modal', id: 'size-guide', trigger_label: 'Size guide', title: 'Size guide', content: section.settings.size_guide_content %}

{% render 'accessible-tabs', id: 'product-info', label: 'Product information', tabs: product.metafields.custom.info_tabs.value %}

{% render 'accessible-accordion', items: product.metafields.custom.faqs.value %}

{% render 'accessible-quantity-selector', id: product.id, value: 1, min: 1 %}

{% render 'accessible-image-gallery', images: product.images, alt_fallback: product.title %}

{% render 'accessible-product-filters', filters: collection.filters, result_count: collection.products_count %}

{% render 'accessible-cart-drawer' %}

{% render 'skip-to-content' %}

Every snippet has a {% doc %} header with the full parameter list and a copy-pasteable @example — see the file itself, or examples/product-page-example.liquid for a full composition on a product page.

What it does NOT do

  • Not a full accessible theme. These are components to drop into your existing theme, not a theme-check replacement or an automatic fix for every accessibility issue on your store — contrast, heading structure, and content-level issues (missing alt text on merchant-uploaded images, for example) are still yours to fix.
  • Not an automated scanner. It doesn't test your live store; it gives you correct markup and behavior for eight common patterns. For an automated pass at what's actually on your store, see the audit link above or shopify-store-audit-toolkit.
  • No build step, but no framework integration either. These are Web Components on native browser APIs — if your theme already uses a JS framework, you may want to wrap them rather than use them as-is.
  • Not a full design system. The CSS provides functional, accessible defaults (spacing, focus rings, touch targets) — expect to reskin colors and typography to match your brand.

FAQ

Does this need a build step? No. No npm install, no bundler — copy the files, load two <script>/<link> tags, done.

Will this conflict with my theme's existing modal/tabs/etc.? The custom elements (<a11y-modal> and friends) and CSS classes (.a11y-*) are namespaced, so they shouldn't collide with an existing implementation. Use one or the other for a given component rather than both.

Does the cart drawer talk to Shopify's cart API for me? No — it renders cart.items from Liquid on page load and exposes .open(), .close(), and .announceCountChange(count) so your existing /cart/add.js / /cart/change.js fetch logic can drive it. It doesn't assume a particular cart-update implementation.

Why native <dialog> instead of a custom overlay div? Focus management, Escape-to-close, and top-layer stacking come from the browser for free and are more reliable than reimplementing them. Every evergreen browser supports it.

→ Rather have a Shopify Partner check your whole store for accessibility gaps, not just these eight patterns? Get the free audit

Related Shopify tools

Also from Ecom Swift LLC, free and open source:

Contributing

Issues and pull requests are welcome — another common pattern (a select/combobox, a rating widget), a bug in the keyboard handling, or a WCAG detail we got wrong. Please include:

  • Which component and Shopify theme/browser combination
  • Expected vs. actual keyboard/screen-reader behavior
  • A minimal reproduction where possible

Roadmap

  • Accessible select/combobox for variant pickers
  • Accessible star-rating display
  • Automated axe-core checks in CI against the examples/ composition

License

MIT © Ecom Swift LLC — see LICENSE.

Need help?

This project is maintained by Ecom Swift LLC, a Shopify Partner.


Get a full accessibility + performance + SEO read on your store

These eight components fix common patterns in your theme code. The free audit at audit.ecomswiftllc.com checks your actual live store — accessibility basics, SEO, speed, and AI/LLM visibility (can ChatGPT, Perplexity and Google AI actually see and cite your store) — in about a minute, no signup.

More free tools: www.ecomswiftllc.com/free-tools · Website: www.ecomswiftllc.com · Partner Directory: shopify.com/partners/directory/partner/waowy

About

Free, WCAG-conscious drop-in Liquid components for Shopify themes: accessible modal, cart drawer, tabs, accordion, quantity selector, image gallery, and filters.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages