
As Shopify developers, our responsibility extends beyond just building beautiful storefronts. We must ensure that the themes we create are usable by everyone, regardless of their abilities.
However, building modern, interactive UI components—like dropdowns, modals, and drawers—while managing accessibility standards can be a daunting task. Juggling ARIA attributes, keyboard navigation, and focus behavior often leads to complex and brittle JavaScript.
Enter Alpine.js, a lightweight JavaScript framework that is the perfect companion to Liquid. It allows you to compose behavior directly in your markup, making it incredibly simple to build fully accessible and interactive components with minimal code.
Let’s walk through how to build an accessible dropdown navigation menu to see just how powerful this combination can be.
Why Alpine.js is a Perfect Fit for Shopify Themes
Before we dive in, it’s worth understanding why Alpine.js works so well with Liquid:
- Minimal Setup: Add a single script tag to your
theme.liquid
file, and you’re ready to go. - Declarative Syntax: You write your logic directly in your HTML, which keeps your Liquid templates clean and easy to understand.
- Tiny Footprint: Alpine.js is extremely small, so it won’t slow down your store.
- No Build Step Required: You can write Alpine.js directly in your theme files without needing a complex JavaScript build process.
Building an Accessible Dropdown Menu
Let’s build a common UI pattern: a dropdown menu that is triggered by a button.
The Basic HTML & Alpine.js Setup
Here is the complete code for our accessible dropdown. We’ll break down what each part does below.
<nav x-data="{ open: false }">
<button
@click="open = !open"
:aria-expanded="open"
aria-haspopup="menu"
aria-controls="dropdown-menu"
class="nav-button"
>
Menu
</button>
<ul
id="dropdown-menu"
role="menu"
x-show="open"
x-trap="open"
@keydown.escape.window="open = false"
@click.outside="open = false"
class="dropdown-menu"
x-cloak
>
<li><a href="/" role="menuitem">Home</a></li>
<li><a href="/collections" role="menuitem">Collections</a></li>
<li><a href="/about" role="menuitem">About</a></li>
</ul>
</nav>
Understanding the Alpine.js Directives
x-data="{ open: false }"
: This initializes a new Alpine component and defines its reactive state. Here, we’re creating a single variable,open
, and setting it tofalse
.@click="open = !open"
: This is an event listener. When the button is clicked, it toggles the value ofopen
.x-show="open"
: This conditionally shows or hides the<ul>
element. It will only be visible whenopen
istrue
.@click.outside="open = false"
: This is a convenient helper that detects a click outside of the<ul>
element and, if the menu is open, closes it.x-cloak
: This directive hides the element until Alpine.js has fully initialized, preventing a “flash” of unstyled content on page load.
The Accessibility Enhancements
This is where Alpine.js truly shines. Notice how simple it is to manage complex accessibility requirements:
:aria-expanded="open"
: The colon:
before the attribute name creates a dynamic binding. The value ofaria-expanded
will automatically update totrue
orfalse
based on theopen
state, clearly communicating the dropdown’s status to assistive technologies.aria-haspopup="menu"
&aria-controls="dropdown-menu"
: These static ARIA attributes establish the relationship between the button and the menu it controls.x-trap="open"
: This is a powerful feature from the Alpine.js Focus plugin. When the menu is open (open
istrue
), it traps the user’s focus within the<ul>
. This means a user navigating with a keyboard cannot accidentally tab away to other elements on the page, a critical feature for accessible modals and dropdowns.@keydown.escape.window="open = false"
: This allows a user to close the menu at any time by pressing theEscape
key—an expected and essential keyboard navigation feature.
Final Thoughts: Accessibility Doesn’t Have to Be Hard
With just a few simple, declarative attributes, we’ve created a dropdown menu that is not only interactive but also fully accessible. It supports screen readers, keyboard navigation, and expected user behaviors without a single line of complex, external JavaScript.
This is the power of combining Alpine.js with Liquid. It allows you to build the modern, dynamic experiences that customers expect, while upholding the responsibility we have as developers to make the web accessible to everyone.
❓ What are your favorite tools for building accessible components in Shopify?