You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
ImageLightboxfit 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.