Blocks
Arc Carousel
A showcase carousel whose cards hang on a downward arc and swing round it as you step.
A showcase for a handful of pieces, laid out on a curve. The cards hang on a downward arc, each one further from the centre sitting a little lower and turned a little more, three to a side. Step and the whole arc swings round: every card glides to its neighbour's slot on a slow, weighted curve, while the one arriving in the middle eases up in size and takes a lime outline. A card that wraps past the edge keeps travelling off-screen while it comes back in from the far side, so the loop never snaps. Swipe, drag, use the arrow keys, or click a side card to bring it to the centre. Plain CSS transitions, no animation library.
Projects we shipped this season.
A rotating look at recent launches across brand, product and research. Swipe, drag or use the arrows to browse.
1 of 8: Nightfall
Installation
pnpm dlx shadcn@latest add @tween-ui/arc-carouselUsing an AI editor? Set up MCP once, then just ask for it in plain words.
Usage
import ArcCarousel from '@/components/tweenui/arc-carousel';
export default function Page() {
return <ArcCarousel />;
}It ships with eight sample projects. Pass your own as items, and point the
button wherever the full list lives:
import ArcCarousel from '@/components/tweenui/arc-carousel';
export default function Page() {
return (
<ArcCarousel
badge="Just in"
title="New this season."
description="Fresh drops from the workshop, restocked every Friday."
cta={{ label: 'Shop the collection', href: '/shop' }}
items={[
{
title: 'Field Jacket',
tag: 'Outerwear',
meta: '$240',
image: '/shop/jacket.jpg',
href: '/shop/jacket',
},
{
title: 'Day Pack',
tag: 'Bags',
meta: '$120',
image: '/shop/pack.jpg',
href: '/shop/pack',
},
// …
]}
/>
);
}The colors are the Tween UI theme tokens, used as Tailwind classes rather than
raw hex: tween-ink-deep (#03110f) for the stage and the edge fades,
tween-ink (#071d1b) under each photo, a tween-teal (#045f64) glow
behind the arc, tween-teal-soft (#9fd4d6) for the supporting text and
tween-lime (#c6f56f) on the badge, the centre card's outline and arrow,
and the button. The CLI adds them to your @theme; with a manual install,
copy the CSS block from the Manual tab into your global stylesheet. Change a
token there and the block follows.
Autoplay
Autoplay is on by default: a new card takes the centre every 5 seconds, and a
pause / play button sits beside the arrows with a lime ring filling up as the
current card's time runs out. Set the pace with interval, or turn it off with
autoplay={false}, which also drops the button:
<ArcCarousel interval={4000} /> // a new card every 4s
<ArcCarousel autoplay={false} /> // steps only when the viewer doesProps
| Prop | Type | Default | Description |
|---|---|---|---|
items | ArcCarouselItem[] | Eight sample projects | The cards on the arc. |
badge | string | 'Featured work' | Pill above the title. Pass '' to drop it. |
title | string | 'Projects we shipped this season.' | Heading, and the carousel's accessible name. |
description | string | How to browse | Line under the heading. Pass '' to drop it. |
cta | { label: string; href: string } | null | View all projects | Button between the arrows. Pass null to drop it. |
autoplay | boolean | true | Step to the next card on its own. |
interval | number | 5000 | Time each card holds the centre, in ms. |
Each item is { title, image, tag?, meta?, href? }. The tag is the chip on
the top left of the card and meta the line under the title. With an href
the centre card is a link and shows a round arrow. Extends native <section>
attributes (className, style, …).
Behavior
- One card size, one arc. Every card is 312 × 430px and the slots are fixed offsets from the centre, so the curve keeps its shape whatever the content. Below 1024px of container width the whole arc scales to 80%, and below 768px to 62%, so it fits a phone without relaying out.
- Wraps without a jump. When a card has to move from one end of the loop to the other, a copy of it carries on off the near edge and fades, while the card itself is placed past the far edge and slides in. The copy is removed once its move has finished.
- Side cards step, the centre card opens. A click on a side card moves the arc one step toward it instead of following its link, and a swipe or drag of more than 50px sideways steps it the other way and swallows the click that ends it. Vertical swipes still scroll the page.
- The centre card steps forward. Side cards sit under a light shade of the stage color; the centre card loses it, grows 5%, takes a lime outline and turns its arrow lime, so the eye lands on it first.
- The edges fade, the arc does not stop. The soft fades on both sides sit above the cards, so the outer ones run out into the background instead of being cut off by the edge of the section.
- Light on the browser. Cards move on
transformandopacityonly, with no blur. Photos come at 1x and 2x with fixed dimensions; the five nearest the centre load first and the rest lazily. - Autoplay waits for you. It holds while the mouse rests on the cards, while the cards have keyboard focus and while the block is off-screen, and picks up where it left off. Stepping by hand gives the new card a full interval. A backgrounded tab never makes it skip ahead on return.
- It is in place before it runs. The first layout is written into the markup, so the arc renders already curved and nothing flashes into place on load.
Accessibility
The carousel is a focusable group with aria-roledescription="carousel",
named by the title, and the left and right arrow keys step it while it has
focus. Each card is a slide labelled n of total: title; cards off the
centre are hidden from screen readers and out of the tab order, so only the
card you are looking at can be reached. The current card is announced
politely after every step. The arrows are real buttons with labels and every
control has a visible lime focus ring. With autoplay on there is always a
labelled pause button, and the announcement goes quiet while the carousel plays
itself, so a screen reader is not interrupted every few seconds. Under
prefers-reduced-motion: reduce autoplay starts paused, the cards fade
between slots instead of travelling, and the centre card does not grow.