MenuClose

Block Gallery — React Page Block

Work gallery: a responsive grid of tiles that lift on hover, with an optional lightbox — a real dialog (focus moves in, Escape closes, arrows navigate, Tab stays trapped, focus returns).

View source
PreviewReact
Starting live preview

Usage

import { BlockGallery } from "@/components/BlockGallery";

export function Work() {
  return (
    <BlockGallery
      title="Recent releases, up close."
      items={[
        { title: "Fieldnote launch", caption: "2026 · identity + site", media: <img src="/work/fieldnote.png" alt="" /> },
        { title: "Orbit 2.4", caption: "2026 · product film" },
      ]}
      {...{
        title: "Recent releases, up close.",
        lightbox: true,
        accent: "#d5ed9a",
        scheme: "dark"
      }}
    />
  );
}
Implementation notes & API

Purpose: portfolio/work gallery with an accessible lightbox. Copy it, ship it — every string, link and color arrives through props. Runtime: pure DOM + CSS. No WebGPU, canvas, animation library or external assets; SSR-safe on first render. Theming: root custom properties (--vb-bg, --vb-fg, --vb-accent, --vb-border, --vb-radius, --vb-container) or the accent/scheme/className/style props. scheme flips the dark default to light. Guardrails: responsive via container queries down to 320px; interactive behavior degrades on touch; prefers-reduced-motion drops entrance/scroll motion while content stays complete. The default artwork is fictional CSS scenery visibly marked as demo; pass media for real screenshots and photography.

PropertyTypeNotes / default
eyebrow / title / descriptionReactNodeSection header
itemsreadonly GalleryItem[]{ title, caption?, media? } — media is any node; default artwork is generated in CSS
lightboxbooleantrue; tiles open the dialog when set
closeLabel / previousLabel / nextLabelstringAccessible labels for the dialog controls
demoNoteReactNode | nullFictional-artwork marker; pass null with real work
scheme"dark" | "light"dark
accentstring (hex)Sets --vb-accent for the whole block
className / stylestring / CSSPropertiesApplied to the section root
childrenReactNodeReplaces the default content entirely
View source on GitHubFree for commercial projects · MIT