Skip to content

0.31.0 — Image Lightbox galleries; fixed overlays escape their ancestors

Choose a tag to compare

@github-actions github-actions released this 04 Jun 00:53
· 16 commits to main since this release

Added

  • ImageLightbox gallery mode. Pass images={[{ src, alt, width?, height? }]}
    (two or more) and the open modal gains left/right chevrons, an i / n counter
    (bottom-right, mono), and / keyboard navigation. A single-entry array (or a
    lone src) behaves exactly as before — no chevrons, no counter. Navigation runs a
    directional slide+scale+crossfade: the incoming image enters from the side of the
    pressed chevron (right/next → from the right), shifting 32px as it scales from 0.95 and
    fades in (250ms, ease-out, 125ms stagger); the outgoing image leaves the opposite way
    (250ms, ease-in). Differing aspect ratios resize the stage underneath the crossfade.
  • ImageLightbox self-measures. width/height are now optional. The aspect ratio
    is read from the loaded <img> (naturalWidth/naturalHeight); explicit dimensions,
    when given, still win and avoid the pre-load reflow. Consumers without dimension
    metadata no longer need glue. The inline thumbnail measures the cover image
    independently of the open modal (aspect ratios are tracked per image), so paging a
    gallery never resizes the thumbnail. A dimensionless image still has a brief pre-load
    reflow; pass width/height to reserve its box.
  • ImageLightbox fit prop. fit="cover" crops the closed thumbnail to fill its
    box (the expanded modal always uses contain, so the whole image stays visible).
    Backed by a new --imagelightbox-tile-object-fit token (default contain). cover
    only crops when the thumbnail has its own box (an aspect from width/height, or a
    CSS-constrained container).
  • Shared portal action (src/system/internal/portal.ts) and a
    check:overlay-portal publish gate. Any component whose <style> declares
    position: fixed must portal that layer via use:portal, or the build fails. Anchored
    position: absolute popovers (Tooltip) are exempt.

Fixed

  • Fixed-position overlays no longer get clipped or painted under other content. A
    position: fixed modal is only window-relative while no ancestor establishes a
    containing block or stacking context for it; a transformed / isolation: isolate /
    contain / will-change ancestor (common on real pages, and present on the editor's
    own preview pane) silently traps it. ImageLightbox's modal and Dialog's backdrop
    now portal to <body> (use:portal), escaping such ancestors. Dialog's inline
    preview variant stays in flow (use:portal={!inline}); enabled is read once at mount.
    Because the layer lives at <body>: DOM events from it no longer bubble to a consumer
    ancestor; a subtree-scoped CSS-variable theme no longer reaches it (this library themes
    via :root, so unaffected in practice); and an SSR show=true renders in flow on the
    server, then relocates on hydration.

Changed

  • ImageLightbox internals restructured. The inline thumbnail is now its own
    <button> that stays in flow; the overlay, morphing stage, and chrome render in a
    separate body-portaled layer. The zoom-from-thumbnail open/close morph, drag/zoom
    panning, and extended toolbar are unchanged. prefers-reduced-motion is now honored
    (all transitions collapse to an instant swap).
  • ImageLightbox modal is now an accessible dialog. It exposes role="dialog" +
    aria-modal with a label (the image's alt, or Image N of M in a gallery), moves
    focus into the modal on open and restores it to the thumbnail on close, traps Tab
    within the modal, and announces the gallery position via a polite live region.