Shopify Builds>LUS Brands>Dawn media gallery hardened for hidden slides and deferred video

Dawn Media Gallery Hardened for Hidden Slides and Deferred Video

A surgical patch to Dawn's media-gallery custom element so that selecting, reordering and announcing media all skip slides the server has hidden for the current variant — and so a variant with no matching thumbnail degrades to a sensible slide instead of an exception.

Dawn's media-gallery element assumes every slide in the DOM is a slide it may show. Once a product gallery hides media that doesn't belong to the selected variant, that assumption activates hidden slides, prepends the wrong element and throws on a missing thumbnail. This patch restricts selection, reordering and the live-region announcement to visible media, with a three-step fallback, and hands deferred video to an optional enhancement hook one frame later.

The Problem

Dawn's MediaGallery is written for a gallery where every media item is available to every variant. Its setActiveMedia finds a slide by id and, failing that, takes the first slide in the DOM. Its variant-change path prepends the active element to the front of the track. Its thumbnail lookup assumes the matching thumbnail exists. Each of those is correct when the DOM and the visible gallery are the same set.

They stop being the same set the moment a gallery hides media per variant. The alt-text gallery on this storefront does exactly that: it classifies each media item against the selected variant in Liquid and marks the non-matching ones hidden. Dawn's element then breaks in three specific ways. It can activate a hidden slide, leaving the visible gallery showing nothing selected. It prepends the wrong element when reordering for the new variant. And it throws when the thumbnail for the requested media isn't there — which, for a variant whose media is entirely hidden, is every time. A thrown exception inside the variant-change handler also stops everything after it, including the live-region announcement a screen-reader user relies on to know the gallery changed.

The Constraint

MediaGallery couldn't be swapped out. It is core Dawn machinery: variant switching calls it, the slider component depends on it and the product modal reaches into it. A fork would have meant re-owning all of that. The only acceptable change was to teach the existing element about a hidden state it didn't know existed.

The hidden state itself lives in markup. It's decided server-side by the alt-text classifier and expressed on each slide, which means the JavaScript cannot own the rule about what's hidden — it has to read the DOM and filter on what the server marked, never re-derive variant matching on its own.

And the changes had to stay small enough to diff. Every edited region has to be identifiable against Dawn 15.4.1, so a future theme update can be reconciled rather than re-done.

What We Built

media-gallery.js is Dawn's file with four regions changed, each verified by diff against Dawn 15.4.1 as the only deltas.

A visible-media selector. One selector — media items carrying data-media-id that the server hasn't marked hidden — is used everywhere the element used to query for slides. The element never asks "which media matches this variant"; it asks "which media is visible," and the server has already answered that.

Selection with a fallback chain. setActiveMedia now tries the requested media id restricted to visible media first; then the requested id at all, in case the caller knows something the filter doesn't; then the first visible media. Dawn's original fell back to the first media in the DOM, which after filtering is as likely to be hidden as not.

Reordering that respects visibility. The prepend path computes the list of visible slides and prepends the first visible one — guarding against it already being first, so a no-op stays a no-op — rather than blindly prepending whatever element is active. Thumbnails get the same treatment with a null guard, so a variant whose thumbnail is hidden no longer throws on the thumbnail's dataset.

An announcement that has something to announce. announceLiveRegion is called only when a thumbnail actually exists. A live region that fires for a slide the shopper can't see announces a change that didn't happen.

Deferred video, one frame later. playActiveMedia returns early when the active slide has no deferred media, and otherwise hands the deferred media element to window.ifEnhanceGalleryVideo — when that hook exists — inside a requestAnimationFrame, so the deferred-media DOM has been inserted before anything tries to enhance it.

Why This Way

Filtering by selector keeps the server as the single source of truth about which media belongs to a variant. The JavaScript never re-derives the matching rules; it reads what the Liquid pass decided and works within it. That's the same discipline the gallery itself follows, where the browser applies keys carried across from render time rather than recomputing them, and it means the two halves can't disagree about what's hidden because only one of them decides.

Deferring the video enhancement hook by a frame is a sequencing fix: Dawn inserts deferred media into the slide on demand, and enhancing it in the same tick races that insertion. The typeof guard on the hook keeps the gallery independent of it — the hook is an option, not a dependency.

What this leaves behind is a fork of a vendor file, however small. Four regions of media-gallery.js now differ from Dawn, and every Dawn update has to be reconciled against them by hand; the diff discipline exists to make that tractable, not to make it free. We also chose a fallback chain over a hard failure. A variant whose media is entirely hidden gets the first visible slide, which is a sensible gallery rather than the correct one, and nothing tells the merchandiser that the classifier found nothing for that variant.

Implementation Notes

  • Verified by diff against Dawn 15.4.1: setActiveMedia, the prepend branch, the thumbnail lookup and playActiveMedia are the only changed regions.
  • Whether a slide is hidden is decided in Liquid by the alt-text classifier and expressed on the slide itself; this file reads that state and never sets it.
  • window.ifEnhanceGalleryVideo is optional. The call is typeof-guarded, so the gallery works identically without it.
  • media-gallery.min.js is the pipeline output of this file; the source is the artifact that carries the diff.
  • The hidden attribute is one of the markers the visible-media selector respects, so a slide hidden by any path is skipped, not just one hidden by the classifier.

Edge Cases

  • A variant with no visible thumbnail no longer throws on activeThumbnail.dataset; the null guard skips the thumbnail step and the slide still activates.
  • A slide already in first position is not re-prepended, avoiding a needless DOM move and the slider reset that comes with it.
  • A requested media id that resolves only to a hidden slide falls through to the first visible slide, so the gallery always shows something selected.
  • A slide with no deferred media returns from playActiveMedia before any video logic runs, so image-only variants never touch the video path.
  • The enhancement hook missing entirely — a page that doesn't load it — leaves Dawn's deferred media behaving as Dawn shipped it.

Platform Primitives Used

  • custom-elementsmedia-gallery is a Dawn custom element; the patch changes four of its methods and none of its lifecycle.
  • section-rendering-api — Dawn's variant change re-renders the product section through it and then calls into this element, which is the path every one of these fixes sits on.

Where It Runs

The product detail page, on all four product templates that mount the alt-text gallery: the default product template, the 2026 redesign template, the kids redesign template and the waiting-list template. The asset is loaded wherever Dawn loads it; the fixes only matter where slides can be hidden, which is every product page on this storefront.

What This Demonstrates

How We Know

One JavaScript asset from the theme, roughly 145 lines in the changed regions and their surroundings, diffed against Dawn 15.4.1, plus two client working-session records. The records document the product-page zoom and magnifier requirement and the handling of deferred video agreed alongside it — the requirements this hardening sits behind. Neither describes the patch. The selector, the fallback chain and the frame-deferred hook are code-side decisions, read from the diff; the client's record explains why the gallery had to hide media and play video, and the code explains what Dawn's element needed in order to cope.

Related Builds

The Buy-vs-Build Question

There's no app for this — it's a consequence of building the variant gallery natively rather than buying one. A variant-image app rewrites the gallery client-side and brings its own slider, which sidesteps Dawn's element entirely and pays for it with a repaint. Keeping Dawn's element meant teaching it four things about hidden slides, including when not to announce. Where component-level fixes belong in the theme and where a tool earns its place is the question on the accessibility remediation decision page.

Provenance & Evidence

  • Client: LUS Brands — loveurcurls.com
  • Surface: Product detail page
  • Templates served: four product templates
  • Complexity: Medium
  • Attribution: Built on top of Dawn 15.4.1; this asset is our modification. media-gallery.js is Dawn's file, and four regions of it — selection, prepend, thumbnail lookup and playActiveMedia — are ours, verified by diff. The element, its lifecycle and everything else in the file are Dawn's.
  • Status: Live, verified 2026-09-07
  • Evidence: One JavaScript asset diffed against Dawn 15.4.1, plus two client working-session records
  • Confidence: Strong — the client record establishes the gallery requirements; the diff shows what changed and why
  • Primary capability: Accessibility remediation at component level

Ready to Teach Your Theme What It Doesn't Know?

You dream it. We build it. If a feature you added has quietly broken a vendor component underneath it, Contact us today and we'll find the four lines that need to change — and leave the rest alone.

More builds