Studio mic
$129
Native scroll-snap row, so touch, trackpad and no-JS scrolling work, with spring-driven arrows, page dots, arrow keys and mouse drag that throws to the next snap point. APG carousel semantics: a labeled region of slides announced as 3 of 8.
import { AspectRatio } from "@/components/ui/aspect-ratio";import { Carousel, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious,} from "@/components/ui/carousel";type Project = { slug: string; title: string; cover: string };// Slides scroll natively (touch, trackpad, no JS); buttons, dots and drag spring.export function ProjectGallery({ projects }: { projects: Project[] }) { return ( <Carousel label="Projects"> <CarouselContent className="sm:[--per-view:2] lg:[--per-view:3]"> {projects.map((p) => ( <CarouselItem key={p.slug}> <AspectRatio ratio={16 / 9} className="rounded-xl"> <img src={p.cover} alt={p.title} /> </AspectRatio> </CarouselItem> ))} </CarouselContent> <CarouselPrevious /> <CarouselNext /> <CarouselDots /> </Carousel> );}Native scroll-snap row (touch, trackpad, no-JS all work) with spring-driven buttons, dots, arrow keys and mouse drag. APG carousel semantics: a labeled region of "n of total" slides.
label*stringAccessible name of the carousel, e.g. "Projects".
| Prop | Type | Default |
|---|---|---|
label*Accessible name of the carousel, e.g. "Projects". | string | — |
Also accepts every <section> prop.
The scrolling row. Slides per view and the gap are CSS variables: sm:[--per-view:2] [--gap:0.5rem].
Also accepts every <div> prop (none of its own).
One slide, sized from the row's --per-view and --gap (or any basis-*). Announced as "3 of 8".
Also accepts every <div> prop (none of its own).
Overlaid at the left edge by default; hides at the start.
Overlaid at the right edge by default; hides at the end.
One dot per page (not per slide: with two slides in view there are fewer pages).
* required. Every component also takes className.