component-product-media-modal Snippet
snippets/component-product-media-modal.liquid renders a product media modal (lightbox) for displaying product images and videos in full-screen view. It uses the <product-media-modal> custom element for JavaScript functionality and integrates with the product media gallery component. The modal displays all product media with the selected variant's featured media prioritized first.
What It Does
- Renders a full-screen modal for product media viewing.
- Displays all product media items in a scrollable container.
- Prioritizes selected variant's featured media as the first item.
- Integrates with gallery component via click triggers.
- Provides close button for modal dismissal.
- Uses native
<dialog>-like behavior via custom element. - Supports keyboard and click-outside dismissal.
- Shows active media with scroll-to behavior.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
section_id | string | required | Parent section id used for unique DOM IDs. |
color_scheme | string | optional | Color scheme handle for modal background styling. |
enable_video_looping | boolean | optional | Whether to loop product videos. |
Dependencies & Assets
| Type | Files / Components |
|---|---|
| CSS | External stylesheet (likely section-product.css or similar) |
| JavaScript | component-product-media-modal.js (defines <product-media-modal> custom element) |
| Custom Elements | <product-media-modal> |
| Snippets | component-product-media (renders individual media items) |
| Icons | icon-close.svg (inline via inline_asset_content) |
| Data | Requires product object with media array and selected_or_first_available_variant |
- Custom element handles modal open/close behavior and active media management.
- External CSS handles all modal styling.
- Modal integrates with gallery component via click triggers.
Dynamic Styles
The snippet does not include inline styles. All styling is handled by external CSS files. However, the component uses dynamic class names based on color scheme:
class="product-media-modal__dialog color-{{ color_scheme }} gradient"- Color scheme integration: Modal uses theme color scheme for consistent styling.
Markup Structure
<product-media-modal id="ProductModal-{{ section_id }}" class="product-media-modal media-modal">
<div class="product-media-modal__dialog color-{{ color_scheme }} gradient" role="dialog" aria-label="{{ 'products.modal.label' | t }}" aria-modal="true" tabindex="-1">
<button id="ModalClose-{{ section_id }}" type="button" class="product-media-modal__toggle" aria-label="{{ 'accessibility.close' | t }}">
{{ 'icon-close.svg' | inline_asset_content }}
</button>
<div class="product-media-modal__content color-{{ color_scheme }} gradient" role="document" aria-label="{{ 'products.modal.label' | t }}" tabindex="0">
<!-- Product media items -->
</div>
</div>
</product-media-modal>- Custom element: Wrapped in
<product-media-modal>custom element. - Unique ID: Modal ID format:
ProductModal-\{\{ section_id \}\}for unique targeting. - Semantic structure: Uses proper ARIA roles and attributes.
Close Button
<button
id="ModalClose-{{ section_id }}"
type="button"
class="product-media-modal__toggle"
aria-label="{{ 'accessibility.close' | t }}"
>
{{ 'icon-close.svg' | inline_asset_content }}
</button>- Unique ID: Close button ID format:
ModalClose-\{\{ section_id \}\}. - Accessibility: Includes
aria-labelfor screen readers. - Icon display: Shows close icon for visual indication.
Media Content
<div class="product-media-modal__content color-{{ color_scheme }} gradient" role="document" aria-label="{{ 'products.modal.label' | t }}" tabindex="0">
{%- liquid
if product.selected_or_first_available_variant.featured_media != null
assign media = product.selected_or_first_available_variant.featured_media
render 'component-product-media', media: media, loop: enable_video_looping
endif
-%}
{%- for media in product.media -%}
{%- liquid
unless media.id == product.selected_or_first_available_variant.featured_media.id
render 'component-product-media', media: media, loop: enable_video_looping
endunless
-%}
{%- endfor -%}
</div>- Variant prioritization: Selected variant's featured media rendered first.
- Media exclusion: Featured media excluded from loop to avoid duplication.
- Media rendering: All media items rendered via
component-product-mediasnippet. - Video looping: Video looping controlled by
enable_video_loopingparameter.
Behavior
- Modal opening: Opens when gallery media items are clicked (handled by JavaScript).
- Active media display: Shows the clicked media item and scrolls it into view.
- Close functionality: Close button dismisses modal.
- Click outside: Clicking outside modal (on backdrop) dismisses it.
- Body scroll lock: Prevents body scrolling when modal is open.
- Keyboard support: Supports keyboard navigation and dismissal.
- Focus management: Manages focus when modal opens/closes.
Usage Example
{%- if section.settings.image_zoom != 'none' -%}
{% render 'component-product-media-modal',
section_id: section.id,
color_scheme: section.settings.color_scheme,
enable_video_looping: section.settings.enable_video_looping
%}
{%- endif -%}Typically used in:
- Product pages (
sections/product.liquid) when zoom is enabled (image_zoom != 'none')
Implementation Notes
Custom element requirement: Snippet requires
component-product-media-modal.jsto be loaded, which defines the<product-media-modal>custom element.Modal integration: Modal is opened by clicking
.light-box-zoom-triggerelements in the gallery component. The trigger'sdata-modalattribute targets this modal.Modal ID format: Modal ID must match gallery trigger's
data-modalattribute:#ProductModal-\{\{ section_id \}\}.Close button ID: Close button uses ID format:
ModalClose-\{\{ section_id \}\}for JavaScript targeting.Custom element methods: The custom element provides:
showModal(opener): Opens modal and shows active mediahideModal(): Closes modal and restores body scrollshowActiveMedia(): Highlights and scrolls active media into view
Active media tracking: Custom element tracks which media opened the modal via
openedByproperty.Active media display: When modal opens, the clicked media item is marked with
activeclass and scrolled into view.Body scroll lock: When modal opens,
overflow-hiddenclass is added to body to prevent background scrolling.Open attribute: Modal uses
openattribute (set by custom element) to control visibility.Click outside handling: Custom element listens for
pointerupevents on modal backdrop to close modal (mouse only, not touch).Close button handling: Close button click triggers
hideModal()method.Icon dependency: Requires
icon-close.svgin theassets/folder for close button.CSS class dependencies: Snippet relies on CSS classes:
.product-media-modal.media-modal.product-media-modal__dialog.product-media-modal__toggle.product-media-modal__content.color-\{\{ color_scheme \}\}.gradient.active(added by JavaScript to active media)
Translation keys: Uses translation keys from
locales/en.default.json:products.modal.labelaccessibility.close
Accessibility features:
role="dialog"on dialog containeraria-modal="true"indicating modal behavioraria-labelon dialog and contentaria-labelon close buttontabindex="-1"on dialog for focus managementtabindex="0"on content for keyboard navigation
Media prioritization: Selected variant's featured media is rendered first, then excluded from main loop to avoid duplication.
Media rendering: All media items rendered via
component-product-mediasnippet for consistency.Video looping: Video looping controlled by
enable_video_loopingparameter, passed to media component.Color scheme integration: Modal uses theme color scheme via
color-\{\{ color_scheme \}\}class.Gradient class: Modal includes
gradientclass for additional styling options.Data media ID: Each media item should have
data-media-idattribute matching the media object's ID for active media targeting.Scroll into view: Active media is scrolled into view using
scrollIntoView()method.Active class management: Custom element adds
activeclass to clicked media, removes from others.Conditional rendering: Modal typically only renders when zoom is enabled (
image_zoom != 'none').No inline styles: All styling handled by external CSS files.
Semantic HTML: Uses proper semantic structure with roles and ARIA attributes.
Focus trap: Modal should trap focus within modal when open (handled by CSS or JavaScript).
Escape key: Escape key should close modal (handled by custom element or browser default).
Backdrop click: Clicking modal backdrop (outside content) closes modal (handled by custom element).
Pointer event handling: Custom element uses
pointerupevent withpointerType === 'mouse'check to distinguish mouse from touch.Modal content structure: Content area uses
role="document"for semantic structure.Tabindex management: Dialog uses
tabindex="-1"to prevent tabbing, content usestabindex="0"to allow tabbing.Media ID matching: Custom element matches media IDs between gallery triggers and modal content to find active media.
Query selector: Custom element uses
querySelectorto find media items bydata-media-idattribute.Class list manipulation: Custom element adds/removes
activeclass to highlight active media.Body class management: Custom element adds/removes
overflow-hiddenclass to body for scroll lock.Attribute management: Custom element sets/removes
openattribute to control modal visibility.Event listener setup: Custom element sets up event listeners in constructor:
- Click listeners on gallery triggers (set up once)
- Click listener on close button
- Pointerup listener on modal for backdrop clicks
Media exclusion logic: Featured media excluded from loop using
unless media.id == product.selected_or_first_available_variant.featured_media.id.Featured media check: Component checks if
product.selected_or_first_available_variant.featured_media != nullbefore rendering.Liquid syntax: Uses
{% liquid %}block for cleaner conditional logic.Media array iteration: Loops through
product.mediaarray to render all media items.Media component parameter: Passes
loop: enable_video_loopingparameter to media component.Unique ID generation: Section ID used for unique DOM IDs to support multiple modals on page.
Modal targeting: Gallery triggers use
data-modal="#ProductModal-\{\{ section_id \}\}"to target this modal.Close button targeting: Custom element finds close button using
querySelector('[id^="ModalClose-"]')selector.No JavaScript file reference: JavaScript file must be loaded separately (typically in product section).
Conditional script loading: Modal JavaScript typically loaded conditionally when zoom is enabled.
Accessibility labels: All interactive elements have descriptive
aria-labelattributes.Translation support: Modal labels use translation filters for internationalization.