Skip to content

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 ​

ParameterTypeDefaultDescription
section_idstringrequiredParent section id used for unique DOM IDs.
color_schemestringoptionalColor scheme handle for modal background styling.
enable_video_loopingbooleanoptionalWhether to loop product videos.

Dependencies & Assets ​

TypeFiles / Components
CSSExternal stylesheet (likely section-product.css or similar)
JavaScriptcomponent-product-media-modal.js (defines <product-media-modal> custom element)
Custom Elements<product-media-modal>
Snippetscomponent-product-media (renders individual media items)
Iconsicon-close.svg (inline via inline_asset_content)
DataRequires 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:

liquid
class="product-media-modal__dialog color-{{ color_scheme }} gradient"
  • Color scheme integration: Modal uses theme color scheme for consistent styling.

Markup Structure ​

liquid
<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 ​

liquid
<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-label for screen readers.
  • Icon display: Shows close icon for visual indication.

Media Content ​

liquid
<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-media snippet.
  • Video looping: Video looping controlled by enable_video_looping parameter.

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 ​

liquid
{%- 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 ​

  1. Custom element requirement: Snippet requires component-product-media-modal.js to be loaded, which defines the <product-media-modal> custom element.

  2. Modal integration: Modal is opened by clicking .light-box-zoom-trigger elements in the gallery component. The trigger's data-modal attribute targets this modal.

  3. Modal ID format: Modal ID must match gallery trigger's data-modal attribute: #ProductModal-\{\{ section_id \}\}.

  4. Close button ID: Close button uses ID format: ModalClose-\{\{ section_id \}\} for JavaScript targeting.

  5. Custom element methods: The custom element provides:

    • showModal(opener): Opens modal and shows active media
    • hideModal(): Closes modal and restores body scroll
    • showActiveMedia(): Highlights and scrolls active media into view
  6. Active media tracking: Custom element tracks which media opened the modal via openedBy property.

  7. Active media display: When modal opens, the clicked media item is marked with active class and scrolled into view.

  8. Body scroll lock: When modal opens, overflow-hidden class is added to body to prevent background scrolling.

  9. Open attribute: Modal uses open attribute (set by custom element) to control visibility.

  10. Click outside handling: Custom element listens for pointerup events on modal backdrop to close modal (mouse only, not touch).

  11. Close button handling: Close button click triggers hideModal() method.

  12. Icon dependency: Requires icon-close.svg in the assets/ folder for close button.

  13. 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)
  14. Translation keys: Uses translation keys from locales/en.default.json:

    • products.modal.label
    • accessibility.close
  15. Accessibility features:

    • role="dialog" on dialog container
    • aria-modal="true" indicating modal behavior
    • aria-label on dialog and content
    • aria-label on close button
    • tabindex="-1" on dialog for focus management
    • tabindex="0" on content for keyboard navigation
  16. Media prioritization: Selected variant's featured media is rendered first, then excluded from main loop to avoid duplication.

  17. Media rendering: All media items rendered via component-product-media snippet for consistency.

  18. Video looping: Video looping controlled by enable_video_looping parameter, passed to media component.

  19. Color scheme integration: Modal uses theme color scheme via color-\{\{ color_scheme \}\} class.

  20. Gradient class: Modal includes gradient class for additional styling options.

  21. Data media ID: Each media item should have data-media-id attribute matching the media object's ID for active media targeting.

  22. Scroll into view: Active media is scrolled into view using scrollIntoView() method.

  23. Active class management: Custom element adds active class to clicked media, removes from others.

  24. Conditional rendering: Modal typically only renders when zoom is enabled (image_zoom != 'none').

  25. No inline styles: All styling handled by external CSS files.

  26. Semantic HTML: Uses proper semantic structure with roles and ARIA attributes.

  27. Focus trap: Modal should trap focus within modal when open (handled by CSS or JavaScript).

  28. Escape key: Escape key should close modal (handled by custom element or browser default).

  29. Backdrop click: Clicking modal backdrop (outside content) closes modal (handled by custom element).

  30. Pointer event handling: Custom element uses pointerup event with pointerType === 'mouse' check to distinguish mouse from touch.

  31. Modal content structure: Content area uses role="document" for semantic structure.

  32. Tabindex management: Dialog uses tabindex="-1" to prevent tabbing, content uses tabindex="0" to allow tabbing.

  33. Media ID matching: Custom element matches media IDs between gallery triggers and modal content to find active media.

  34. Query selector: Custom element uses querySelector to find media items by data-media-id attribute.

  35. Class list manipulation: Custom element adds/removes active class to highlight active media.

  36. Body class management: Custom element adds/removes overflow-hidden class to body for scroll lock.

  37. Attribute management: Custom element sets/removes open attribute to control modal visibility.

  38. 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
  39. Media exclusion logic: Featured media excluded from loop using unless media.id == product.selected_or_first_available_variant.featured_media.id.

  40. Featured media check: Component checks if product.selected_or_first_available_variant.featured_media != null before rendering.

  41. Liquid syntax: Uses {% liquid %} block for cleaner conditional logic.

  42. Media array iteration: Loops through product.media array to render all media items.

  43. Media component parameter: Passes loop: enable_video_looping parameter to media component.

  44. Unique ID generation: Section ID used for unique DOM IDs to support multiple modals on page.

  45. Modal targeting: Gallery triggers use data-modal="#ProductModal-\{\{ section_id \}\}" to target this modal.

  46. Close button targeting: Custom element finds close button using querySelector('[id^="ModalClose-"]') selector.

  47. No JavaScript file reference: JavaScript file must be loaded separately (typically in product section).

  48. Conditional script loading: Modal JavaScript typically loaded conditionally when zoom is enabled.

  49. Accessibility labels: All interactive elements have descriptive aria-label attributes.

  50. Translation support: Modal labels use translation filters for internationalization.