// Layout shell shared by the public gallery page (GalleryClient) and its // loading placeholder (GallerySkeleton). Column count, gaps, radii and // container padding are defined once here, so the skeleton's geometry can // never drift from the grid it stands in for. export function GalleryHeroFrame({ children, backdrop, }: { children: React.ReactNode; /** Cover image + scrim, absolutely positioned behind the text. */ backdrop?: React.ReactNode; }) { return (
{backdrop}
{children}
); } export function GalleryContainer({ children }: { children: React.ReactNode }) { return
{children}
; } export function MasonryGrid({ children }: { children: React.ReactNode }) { return (
{children}
); } // Per-tile geometry. Deliberately carries no background so callers can pick // one (photo tiles: bg-gray-100, skeleton tiles: the skeleton surface) without // two `bg-*` utilities fighting over CSS order. export const masonryTileClass = 'mb-2 md:mb-3 break-inside-avoid overflow-hidden rounded-xl'; /** * Reserves the tile's aspect ratio up front so the image can load into a box * that is already the right size — late arrivals never reflow the columns. * Returns undefined when the photo has no stored dimensions. */ export function aspectStyle(width?: number, height?: number): React.CSSProperties | undefined { return width && height ? { aspectRatio: `${width} / ${height}` } : undefined; }