ClipPath Carousel
A carousel with clip-path reveal transitions in four built-in styles — wipe, iris, diagonal, and box — supporting drag, autoplay, and controlled and uncontrolled modes.
npx shadcn@latest add https://webberui.com/r/clippath-carousel.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<ClippathCarousel />
Installation
npx shadcn@latest add https://webberui.com/r/clippath-carousel.jsonOr, once registries are configured in components.json, install it as @webberui/clippath-carousel.
Usage
Every direct child is one slide, and the first item is the initial view. On a change, the entering slide is revealed from its collapsed state to fully open with clip-path, layered over the exiting slide.
import { ClipPathCarousel } from "@/components/ui/clippath-carousel";
<ClipPathCarousel variant="iris">
<div className="flex h-full items-center justify-center bg-sky-500">One</div>
<div className="flex h-full items-center justify-center bg-rose-500">Two</div>
<div className="flex h-full items-center justify-center bg-emerald-500">Three</div>
</ClipPathCarousel>;Reveal styles
variant offers four single-clip-path transitions:
wipe: wipes open from the left or right edge depending on directioniris: a circular iris expanding outward from the centerdiagonal: a triangle expanding diagonally from the top-left corner (next slide) or the bottom-right corner (previous slide)box: a rectangle expanding from the center in all directions
Autoplay
Set autoPlay and interval; it pauses automatically when the pointer enters, when the keyboard focus enters, or when the tab goes to the background.
<ClipPathCarousel autoPlay interval={3000}>
{/* ... */}
</ClipPathCarousel>Programmatic control
Get next() / prev() / goTo() through the ref.
import {
ClipPathCarousel,
type ClipPathCarouselHandle,
} from "@/components/ui/clippath-carousel";
const ref = React.useRef<ClipPathCarouselHandle>(null);
<ClipPathCarousel ref={ref}>{/* ... */}</ClipPathCarousel>;
<button onClick={() => ref.current?.next()}>Next</button>;Controlled mode
Passing index and onIndexChange switches to controlled mode; otherwise use defaultIndex and stay uncontrolled.
const [index, setIndex] = React.useState(0);
<ClipPathCarousel index={index} onIndexChange={setIndex}>
{/* ... */}
</ClipPathCarousel>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Every direct child is one slide |
variant | "wipe" | "iris" | "diagonal" | "box" | "wipe" | clip-path reveal style |
index | number | — | Controlled current index |
defaultIndex | number | 0 | Initial index in uncontrolled mode |
onIndexChange | (index: number) => void | — | Fires when the current index changes |
loop | boolean | true | Whether to loop at the end and the start |
duration | number | 0.6 | Duration of the reveal animation (seconds) |
autoPlay | boolean | false | Whether to autoplay |
interval | number | 4000 | Autoplay interval (milliseconds) |
showArrows | boolean | true | Whether to show the left and right arrows |
showIndicators | boolean | true | Whether to show the indicator dots at the bottom |
enableDrag | boolean | true | Whether drag / swipe switching is allowed |
aspectRatio | string | "16 / 9" | Container aspect ratio (a CSS aspect-ratio value) |
slideClassName | string | — | Applied to each slide's outer element |
className | string | — | Applied to the outermost container |
The ref gives you a ClipPathCarouselHandle: next(), prev(), goTo(index).
How it works
- During a change both the exiting and the entering slide are rendered: the exiting one rests on the bottom layer while the entering one is revealed over it with
clip-path, and the exiting slide unmounts once the animation ends. - The transition starts before paint (in a layout effect), avoiding a flash where the whole slide shows outright.
- A drag (or touch swipe) past the offset or flick velocity threshold switches to the previous or next slide; below the threshold it springs back into place.
- Both controlled (
index+onIndexChange) and uncontrolled (defaultIndex) modes are supported; when the number of children changes, the uncontrolled index is clamped back into the valid range automatically.
Accessibility
- The container carries
role="group"andaria-roledescription="輪播"(Traditional Chinese for "carousel"), and announces the current slide number through anaria-liveregion. - The viewport is focusable, with the left and right arrow keys mapping to previous and next slide; the arrows and the indicator dots are all buttons with an
aria-label. - When the user has "reduce motion" enabled at the system level, the clip-path reveal and the drag are disabled and switching becomes an instant swap with no large movement.
Cover Carousel
A horizontal carousel of cover cards whose indicator is driven continuously (scrubbed) by the scroll ratio, with full keyboard navigation and ARIA.
Thumbnail Gallery
A gallery whose main image and thumbnail strip stay in sync, supporting drag to change images, arrow keys, slide/fade transitions, and a thumbnail strip on any of four sides.