A few ways to give visual work some room. Replace the frames with your own images.
Grid
An even image grid with two columns on narrow screens and three when wide.
tsx
"use client";import { cn } from "@/lib/utils";type Gallery01Props = { className?: string;};const IMAGES = [ "/assets/brand/demos/attachments/attachment-1.png", "/assets/brand/demos/attachments/attachment-2.png", "/assets/brand/demos/attachments/attachment-3.png", "/assets/brand/demos/attachments/attachment-4.png", "/assets/brand/demos/attachments/attachment-5.png", "/assets/brand/demos/attachments/attachment-1.png",] as const;export function Gallery01({ className }: Gallery01Props) { return ( <section className={cn("@container w-full bg-background", className)}> <div className="mx-auto w-full max-w-4xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14"> <div className="mx-auto max-w-md text-center"> <h2 className="text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl"> Gallery </h2> <p className="mt-2 text-sm tracking-wide text-muted-foreground"> A simple grid of images — swap in your own shots. </p> </div> <ul className="mt-8 grid grid-cols-2 gap-2 @[40rem]:grid-cols-3 @[40rem]:gap-3"> {IMAGES.map((src, index) => ( <li key={`${src}-${index}`} className="aspect-square overflow-hidden rounded-xl bg-muted" > <img src={src} alt="" className="size-full object-cover" /> </li> ))} </ul> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add gallery-01Usage
import { Gallery01 } from "@/components/blocks/gallery/gallery-01"<Gallery01 />Caption cards
A denser grid of filled image cards, each with a short caption.
Selected frames
Filled image cards with a short caption under each shot.

Launch hero

Token surfaces

Detail shots

Product UI

Brand moments

Dark mode
tsx
"use client";import { cn } from "@/lib/utils";type Gallery02Props = { className?: string;};const IMAGES = [ { src: "/assets/brand/demos/attachments/attachment-1.png", caption: "Launch hero", }, { src: "/assets/brand/demos/attachments/attachment-2.png", caption: "Token surfaces", }, { src: "/assets/brand/demos/attachments/attachment-3.png", caption: "Detail shots", }, { src: "/assets/brand/demos/attachments/attachment-4.png", caption: "Product UI", }, { src: "/assets/brand/demos/attachments/attachment-5.png", caption: "Brand moments", }, { src: "/assets/brand/demos/attachments/attachment-2.png", caption: "Dark mode", },] as const;export function Gallery02({ className }: Gallery02Props) { return ( <section className={cn("@container w-full bg-background", className)}> <div className="mx-auto w-full max-w-4xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14"> <div className="mx-auto max-w-md text-center"> <h2 className="text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl"> Selected frames </h2> <p className="mt-2 text-sm tracking-wide text-muted-foreground"> Filled image cards with a short caption under each shot. </p> </div> <ul className="mt-8 grid grid-cols-1 gap-3 @[32rem]:grid-cols-2 @[40rem]:grid-cols-3"> {IMAGES.map((image) => ( <li key={image.caption} className="overflow-hidden rounded-xl bg-muted p-2" > <div className="aspect-[4/3] overflow-hidden rounded-lg bg-background"> <img src={image.src} alt="" className="size-full object-cover" /> </div> <p className="px-1 pt-2.5 pb-1 text-xs tracking-wide text-muted-foreground"> {image.caption} </p> </li> ))} </ul> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add gallery-02Usage
import { Gallery02 } from "@/components/blocks/gallery/gallery-02"<Gallery02 />Marquee
An infinite horizontal strip of frames that pauses when hovered.
tsx
"use client";import { cn } from "@/lib/utils";type Gallery03Props = { className?: string;};const IMAGES = [ "/assets/brand/demos/attachments/attachment-1.png", "/assets/brand/demos/attachments/attachment-2.png", "/assets/brand/demos/attachments/attachment-3.png", "/assets/brand/demos/attachments/attachment-4.png", "/assets/brand/demos/attachments/attachment-5.png",] as const;export function Gallery03({ className }: Gallery03Props) { const track = [...IMAGES, ...IMAGES]; return ( <section className={cn("w-full overflow-hidden bg-background", className)}> <style>{` @keyframes gallery-03-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } } `}</style> <div className="mx-auto w-full max-w-4xl px-4 py-10 sm:px-6 sm:py-14"> <div className="mx-auto max-w-md text-center"> <h2 className="text-xl font-medium tracking-wide text-foreground sm:text-2xl"> In motion </h2> <p className="mt-2 text-sm tracking-wide text-muted-foreground"> A slow horizontal strip of frames — pause by hovering the track. </p> </div> </div> <div className="w-full overflow-hidden pb-10 sm:pb-14" aria-hidden="true" > <div className="flex w-max gap-3 pr-3 hover:[animation-play-state:paused]" style={{ animation: "gallery-03-scroll 36s linear infinite" }} > {track.map((src, index) => ( <div key={`${src}-${index}`} className="aspect-[4/3] w-[16rem] shrink-0 overflow-hidden rounded-xl bg-muted sm:w-[18rem]" > <img src={src} alt="" className="size-full object-cover" /> </div> ))} </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add gallery-03Usage
import { Gallery03 } from "@/components/blocks/gallery/gallery-03"<Gallery03 />