Overview
The Image Lightbox provides a full-screen overlay for viewing images at a larger size than their inline presentation. CORE-OWNED. Requirements: REQ-00049 (lightbox), REQ-00050 (focus trapping), REQ-00053 (focus restoration), REQ-00054 (scroll locking).
The lightbox backdrop uses --st-color-overlay-scrim at 80% alpha so the overlay remains semantically tied to the theme token surface instead of a raw color literal.
Who this is for
- Implementors adding lightbox behavior to images in pages and articles
- Developers customizing lightbox configuration, gallery grouping, or the rehype plugin
Two components work together:
Lightbox.astro(src/core/components/ui/Lightbox.astro) — the full-screen overlay modal. Uses native<dialog>for dialog semantics, focus trapping, scroll locking, and Escape-to-close.LightboxImage.astro(src/core/components/ui/LightboxImage.astro) — the Astro template trigger component. Renders a<a data-lightbox><img /></a>wrapper with typed props.
A rehype plugin (rehype-lightbox-images.ts) provides the markdown/MDX integration path, wrapping rendered <img> elements in the same <a data-lightbox> markup at build time.
Usage Paths
Astro templates
Use LightboxImage for typed prop access to all features (hi-res source, explicit captions, named gallery groups):
---
import LightboxImage from "@core/components/ui/LightboxImage.astro";
import Lightbox from "@core/components/ui/Lightbox.astro";
---
<LightboxImage src="/images/photo.jpg" alt="A scenic mountain view" />
<LightboxImage
src="/images/team.jpg"
alt="Team photo"
hires="/images/team-hires.jpg"
caption="The full team at our annual retreat"
group="team"
/>
<Lightbox />
When using LightboxImage in Astro templates, the author must include <Lightbox /> on the page. For collection-routed pages (articles, pages), the template route includes <Lightbox /> automatically when lightbox is enabled.
Markdown/MDX content
For markdown/MDX content in the articles and pages collections, lightbox is controlled via configuration:
- Site config (
src/site/config/platform.config.ts): per-collection defaults viaplatformConfig.lightbox.collections - Frontmatter: per-page override via
lightbox: trueorlightbox: false
When enabled, the rehype-lightbox-images plugin wraps eligible <img> elements in <a data-lightbox> at build time. All wrapped images join a single implicit gallery.
LightboxImage Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
src | string | Yes | — | Image source URL |
alt | string | Yes | — | Alt text for the image |
hires | string | No | — | Hi-res image source for the overlay |
caption | string | No | — | Explicit caption (overrides alt text in overlay) |
group | string | No | "implicit" | Gallery group name for independent gallery navigation |
class | string | No | — | Additional CSS classes for the wrapper <a> element |
Configuration
Site-level (site.schema.ts → platform.config.ts → site.ts)
// src/site/config/platform.config.ts
export const platformConfig = {
// ...
lightbox: {
...DEFAULT_SITE_CONFIG.lightbox,
collections: { articles: true }, // default
},
};
The collections record maps collection names to boolean defaults. Collections not listed default to false. The type is constrained to "articles" | "pages". site.ts assigns this same value to siteConfig.lightbox so route templates and the markdown rehype plugin use the same source.
Per-page (frontmatter)
lightbox: true # Enable lightbox for this page (overrides collection default)
lightbox: false # Disable lightbox for this page
# Omit to defer to collection default
Resolution order: frontmatter → site config collection default → false.
Gallery Grouping
- Implicit gallery: All ungrouped lightbox images on a page (no
groupprop, orgroup="implicit") form one gallery. - Named groups: Use the
groupprop to create independent galleries (e.g.,group="before-after"). Each named group navigates independently. - Single images: An image that is the sole member of its group opens without navigation controls.
Accessibility
role="dialog"andaria-modal="true"via native<dialog>with.showModal()- Visually-hidden “Image viewer” label via
aria-label - Focus trapping within the overlay while open (REQ-00050)
- Focus restoration to the trigger element on close (REQ-00053)
- Initial focus moves to the close button on open
- Scroll locking while overlay is active (REQ-00054)
- Keyboard: Escape closes, Left/Right arrows navigate gallery, Tab cycles interactive elements
- Touch: swipe left/right navigates gallery on touch devices
- Transitions respect
prefers-reduced-motion - Semantic z-index via
var(--st-layer-modal-backdrop)/var(--st-layer-modal-surface)in scoped<style>(REQ-00189)
Progressive Enhancement
Without JavaScript, lightbox-enabled images render as <a href="[src]"><img /></a>. Clicking opens the full-size image directly in the browser tab. The inline image display is unaffected. When a hires prop is provided, the <a> href points to the hi-res source.
CSS API
| Class | Element | Description |
|---|---|---|
.lightbox | Root wrapper | Component root element |
.lightbox__backdrop | Overlay backdrop | Semi-transparent background |
.lightbox__panel | Dialog panel | Full-screen flex container |
.lightbox__close | Close button | Top-right close control |
.lightbox__content | Image + caption | Centered content container |
.lightbox__image | <img> element | Fit-to-viewport image |
.lightbox__caption | Caption text | Below-image caption |
.lightbox__nav | Nav button (both) | Prev/next navigation controls |
.lightbox__nav--prev | Prev button | Left-positioned nav control |
.lightbox__nav--next | Next button | Right-positioned nav control |
.lightbox-image | LightboxImage <a> | Trigger wrapper class |