Purpose#
The ProductModal reusable renders a global modal shell used to display product-related HTML fragments (typically variant selection UI on mobile). It is driven by the Alpine store $store.productModal (defined in Assets/js/theme.js).Where it's rendered#
This reusable is rendered globally from Structure/LayoutA.liquid:{% render 'Reusables\\ProductModal\\Default' %}
Because it is mounted at layout level, it can be opened from any page/component that can call $store.productModal.open(...).This reusable is rendered without parameters.Template behavior (Liquid + Alpine)#
Uses x-show="$store.productModal.visible" for visibility.
On close animation completion, it calls:
@after-leave="$store.productModal.flush()"
(Important: the store currently does not define flush(); see Notes.)The outer x-data defines checkScreenSize().
On init and on window resize, if the viewport matches (min-width: 1440px) and the modal is open, it closes the modal:
if (window.matchMedia('(min-width: 1440px)').matches) {
if ($store.productModal.visible) {
$store.productModal.close();
}
}
This effectively makes the ProductModal a mobile-first UI (it will auto-close on desktop).Click outside closes the modal:
@click.outside="$store.productModal.close()"
Close button calls $store.productModal.close().
The modal body renders the current HTML via:
<div x-html="$store.productModal.html"></div>
Data contract (JS runtime)#
This reusable is store-driven.DOM contract#
The store expects that a hidden DOM node exists with this selector:
[x-ref=variantModalContent-<uniqueKey>]
The modal content is sourced from that element’s innerHTML.Store contract#
The reusable expects the Alpine store $store.productModal to exist and expose:JavaScript#
Global object#
Global object: productmodalreusabledefaultIt is currently an empty object ({}) and is not referenced by the Liquid template.
The logic is implemented in the Alpine store $store.productModal.
Global Alpine stores#
$store.productModal#
Defined in Assets/js/theme.js.A global modal controller for showing product-related HTML fragments.
open(product, uniqueKey):finds the element [x-ref=variantModalContent-${uniqueKey}]
copies innerHTML into this.html
after 300ms, clears product and html
Services / API calls#
This reusable does not call APIs.Dependencies#
Liquid: Reusables/ProductModal/Default.liquid
Alpine store: $store.productModal (defined in Assets/js/theme.js)
Runtime DOM contract: [x-ref=variantModalContent-<uniqueKey>]
Notes#
The template uses @after-leave="$store.productModal.flush()", but the store implementation in Assets/js/theme.js does not define flush().If this is intentional, consider adding a flush() method that clears product and html.
Otherwise, remove the @after-leave handler to avoid runtime errors.
Because it uses x-html, ensure the injected HTML is trusted and originates from theme templates (not user input).
Modified at 2026-04-14 13:18:56