Blog

Accessible & Interactive: Building Modern UI Components with Alpine.js in Your Shopify Theme

A tutorial on using Alpine.js to create accessible and interactive UI components like dropdowns and modals within a Liquid-based theme, covering ARIA attributes, keyboard navigation, and focus behavior.

Accessible & Interactive: Building Modern UI Components with Alpine.js in Your Shopify Theme

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 to false.
  • @click="open = !open": This is an event listener. When the button is clicked, it toggles the value of open.
  • x-show="open": This conditionally shows or hides the <ul> element. It will only be visible when open is true.
  • @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 of aria-expanded will automatically update to true or false based on the open 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 is true), 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 the Escape 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?