Images — standalone + gallery
Video & iframe — auto-detected from the href
YouTube, Vimeo and direct .mp4 links become video slides (played by Plyr, served from the app's own vendors — no CDN); any other URL opens as an iframe. Size iframe/video slides with lightbox-width / lightbox-height.
The video demos stream from YouTube/Vimeo — they need network access; everything else on this page is served locally.
From data — no DOM elements
When the media list comes from an API payload (a lister row's photos, a record's attachments), skip the markup: g.lightbox.open(items, startAt) builds the slideshow from plain objects.
The rules
- The route declares
'vendors' => ['glightbox']+'libs' => ['lightbox']. Marked elements need NO page JS — the lib inits on load and re-scans onlister:loaded; after injecting marked elements yourself, callg.lightbox.refresh(). - The attribute contract: bare
lightbox= standalone, a shared value = one gallery;lightbox-title/lightbox-descriptioncaption the slide;lightbox-typeforcesimage|video|externalwhen the href alone cannot tell;lightbox-width/lightbox-heightsize video/iframe slides. - Captions and controls are theme-aware (both themes, focus-visible rings, reduced-motion honored); the overlay stays dark by design. Never restyle the vendor by hand — the lib's stylesheet already aligns it.
- Docs:
templates/app/docs/libs/lightbox.md; the harness: /tests/lightbox.