Carousel

Slotted slides at discrete positions, with buttons, keyboard, swipe and indicators sharing one selection.

<tp-carousel>
Carousel
Default density Compact density Restart from the initial state Show the copyable source
Single sign-on SAML and OIDC are now available on every plan.
Audit log Every permission change, kept for 400 days.
Faster exports Large CSV exports finish up to 5× sooner.
Scheduled reports Email a dashboard to your team every Monday.

Usage

Each child of tp-carousel is one slide. Previous and next Buttons, arrow keys, swipe and the indicators all move the same numeric selection, and the current position is announced politely. Always give the Carousel a label, and size it through CSS: it fills the inline size it is given. Transitions never block interaction by default. A drag can grab a moving carousel and continues from the presented position.

Several per view

Layout, navigation and indicator settings live in the options property. layout.itemsPerView accepts fractions, so a partly visible next slide hints that there is more; items-per-movement sets how far each step moves. Resolution order is defaults, base options, root scalar properties, then the matching breakpoint. breakpoints maps nonnegative pixel widths or positive @ratio keys to responsive overrides.

Items per view
Default density Compact density Restart from the initial state Show the copyable source
AtlasDesign system · 12 members Billing v2Payments · 5 members OnboardingGrowth · 8 members Mobile appiOS and Android · 9 members SearchPlatform · 4 members Status pageInfrastructure · 3 members

Indicators and placement

indicators.type is fraction by default, or bullets and progress. Set clickable: true to make bullets real Buttons. navigation.placement puts the arrows in the footer, inside the viewport or outside it. navigation.hideOnClick and indicators.hideOnClick toggle their controls when noninteractive slide content is clicked. Indicator count is snap count, which may differ from item count or mounted virtual count.

Bullets inside
Default density Compact density Restart from the initial state Show the copyable source

“We replaced three internal tools in a week.”

Priya Raman, Head of Ops at Northwind

“Release reviews went from an hour to ten minutes.”

Daniel Okafor, Engineering Manager at Lumen

“Our designers and engineers finally share one source.”

Mara Lindqvist, Design Lead at Fjord

Loop and autoplay

loop lets navigation wrap past either end. autoplay sets an interval in milliseconds and adds a Pause/Resume Button before the slides; hover, focus, reduced motion and hidden tabs pause the timer automatically. Root loop permits loopMode='continuous' (default) or 'rewind'. Continuous mode permutes owned shells without cloning consumer controls or changing logical source order.

Autoplay
Default density Compact density Restart from the initial state Show the copyable source
Press ⌘K anywhere Jump to any project, page or setting.
Invite your team Projects are more useful with reviewers.
Connect GitHub Link pull requests to tasks automatically.

Use autoplay sparingly: moving content is hard to read and easy to miss. Prefer it for decorative or repeated content, never for the only path to information.

Vertical

orientation="vertical" moves the track, keys and swipe to the block axis. A vertical Carousel needs a definite block size.

Vertical
Default density Compact density Restart from the initial state Show the copyable source
Design reviewTue, Oct 13 · 10:00 Sprint planningWed, Oct 14 · 14:30 Customer call: LumenThu, Oct 15 · 09:00 Release 2.5 go/no-goFri, Oct 16 · 16:00

Effects

By default the track slides between items. The effect property swaps in a richer transition, built by a factory you import, so pages without one bundle none. carouselCrossfadeEffect() fades between slides, carouselLayeredEffect() turns media away with perspective, carouselParallaxEffect() moves media more slowly than its slide, carouselFocusEffect() dims and shrinks slides away from the aligned one, and carouselShaderEffect() runs a WebGL transition: wipe, displace, chromatic, crosswarp or glass.

  • Stack effects keep every item at the viewport origin and force one item per view and per movement. Conflicting layout options are overridden with a tp-diagnostic.
  • Track effects keep the moving track, so any layout, loop or centering works. With several items in view, set interaction.navigateOnItemClick so a click on a partly visible item brings it into view.
  • Effects need the transform transport. With transport: 'scroll' the effect is ignored and a diagnostic reports it.
Crossfade
Default density Compact density Restart from the initial state Show the copyable source
Single sign-on SAML and OIDC are now available on every plan.
Audit log Every permission change, kept for 400 days.
Faster exports Large CSV exports finish up to 5× sooner.

Mark the one image or video per slide that an effect transforms with data-carousel-media, and text that should reveal in order with data-carousel-layer="1", "2" and so on. Effects follow dragging, play backwards when a navigation reverses and settle instantly under reduced motion; slide semantics and announcements don’t change.

Layered 3D
Default density Compact density Restart from the initial state Show the copyable source
A winding road through green hills at sunset
Hill country Three days by car · from $420
Mountain lake at dawn
Alpine lakes Hut to hut · from $610
A bicycle in a wet alley
Old town by bike Guided weekend · from $290

The Layered effect turns the outgoing photo away in perspective, turns the next one in, then raises its caption layers in order. Every step follows a drag, so readers can scrub through it.

Parallax
Default density Compact density Restart from the initial state Show the copyable source
A rooftop terrace
A building facade
Mountain lake at dawn
A bicycle in a wet alley

Parallax keeps the sliding track, so it works with several slides in view, centering and loop. Each photo travels more slowly than its clipped slide. With neighbors peeking in, interaction.navigateOnItemClick lets a click on a partly visible slide bring it into view.

Focus
Default density Compact density Restart from the initial state Show the copyable source
Mountain lake at dawn
Dolomites
A building facade
Porto
A winding road through green hills at sunset
Tuscany
A rooftop terrace
Lisbon
A bicycle in a wet alley
Amsterdam

Focus dims and shrinks cards as they move away from the center and reveals the centered card’s caption as it arrives, which suits a row of destinations or products.

Shader transition
Default density Compact density Restart from the initial state Show the copyable source

The shader effect draws a WebGL2 transition between the two data-carousel-media images: wipe sweeps a noise-edged front, displace and chromatic flow a noise map through the frame, crosswarp zooms and morphs across, and glass sweeps a refracting ridge. direction takes an edge or a corner. Captions stay live text above the canvas. Images need crossorigin="anonymous" from a CORS-enabled host; without WebGL2, loaded media or motion, the effect falls back to a crossfade. All shader carousels on a page share one WebGL2 context. Each carousel displays frames in its own canvas, so many carousels never exhaust the browser’s context limit.

Items from data

Virtual archive with controlled selection
Default density Compact density Restart from the initial state Show the copyable source
Showing invoices 1–2 of 200

Set items to an array and renderItem to a Lit template to build slides from data instead of children. virtual keeps only the slides near the viewport in the DOM, so a long archive stays light. Setting value makes selection controlled: each proposal waits for your listener to assign it, which is where a router or store would step in. Do not combine default slide children with data items. Leaving the controlled value unchanged also rejects it. Do not switch between controlled and uncontrolled mode during one host lifetime.

Events

tp-value-change proposes a new position and can be canceled; tp-value-commit follows once it is accepted. Read the committed position from index, or drive the Carousel through its controller.

Commit
Default density Compact density Restart from the initial state Show the copyable source
Create a workspace
Import your data
Invite your team
Step 1 of 3
const result = await carousel.controller?.scrollToId('review', { speed: 0 }); // {status, index, id, reason}: the actual committed destination const unsubscribe = carousel.controller?.subscribe((snapshot) => console.log(snapshot)); unsubscribe?.();

Navigation resolves accepted, unchanged, rejected, or cancelled, with actual committed index/id and a reason code. User rejection does not reject the Promise.

Keyboard

Arrow keys follow the active axis and writing direction; Home and End jump to the first and last slide. Keys owned by a control inside a slide, such as a text field or slider, stay with that control. Optional PageUp/PageDown use the same snap navigation. An inner Carousel owns its own navigation.

Custom effects

CarouselEffect objects have:

  • name and layout (track or stack);
  • optional duration, easing, easingRole (standard, enter or exit, the motion token used without easing) and constrain(input, diagnose);
  • attach(context), which returns { frame(frame), update?(), detach() }.

Each frame provides:

  • phase (drag, animate, settle), position, velocity (items per second), direction;
  • items, each with progress, shell and content;
  • current, next and amount, the pair being transitioned and how far.

The context provides the viewport, track, effect surface, orientation, direction, invalidate() and diagnose(). Restore everything you change in detach(). Helpers stackEffectConstraint, carouselItemProgress and mergeCarouselOptions are exported.

Grid/multirow layout, unsnapped free momentum, zoom, linked controllers, thumbnail controllers, URL routing, raw HTML renderers and a public plugin/module injection system are not supported. Effects are typed presentation objects, not installable modules.

API Reference

Every element also accepts motion-policy.

AttributePropertyTypeDefaultDescriptionindexindexnumber—The committed position; setting it navigates there.looploopbooleanfalseLets navigation wrap past either end.items-per-movementitemsPerMovementnumber1How many slides one step of navigation moves.autoplayautoplaynumber0The interval between automatic advances, in milliseconds; zero turns autoplay off. Autoplay adds a pause control and pauses on hover, focus, reduced motion and hidden tabs.labellabelstring''The accessible name of the carousel, such as "Customer stories".PropertyTypeDefaultDescriptionvaluenumber | undefined—The selected position; set it before the carousel initializes to control selection, then publish each accepted proposal back.defaultValuenumber | undefined—The position shown first, when the carousel owns its selection.itemsreadonly T[] | undefined—Builds the slides from data instead of children; any array, even an empty one, switches to data.getItemId((item: T, index: number) => CarouselId) | undefined—The stable identity of a data item, which keeps the selection when items reorder; defaults to the item itself or its id.getItemLabel((item: T, index: number) => string) | undefined—The name of a data item's slide, announced with its position.getItemOptions((item: T, index: number) => CarouselItemOptions) | undefined—Per-item behavior for a data item: disabled, its own autoplay delay, or a label.renderItem((item: T, context: CarouselRenderContext) => unknown) | undefined—Renders a data item's slide, with its id, index and whether it is selected or visible.optionsCarouselOptions{}Layout, interaction, navigation, indicators, scrollbar, wheel, keyboard, autoplay, virtualization and breakpoint settings, merged over the defaults.onControllerChange((controller: CarouselController | null) => void) | undefined—Called with the carousel's controller once it initializes, and with null when it is released.effectCarouselEffect | nullnullA transition created by an effect factory, such as carouselCrossfadeEffect(); null keeps the sliding track. Effects transform each slide's element marked data-carousel-media and reveal data-carousel-layer elements in order.controllerread-onlyCarouselController | null—The handle for driving and observing the carousel from code (scroll to a slide, read its snapshot, control autoplay); null before it initializes.indicatorTyperead-onlystring—The indicator style in effect, from options.indicators.controlsPlacementread-onlystring—Where the previous and next buttons sit, from options.navigation.EventTypeDescriptiontp-value-changeTpValueChangeEvent<number>Cancelable proposal of a new position before it commits; a controlled carousel must publish the accepted value.tp-value-commitTpValueCommitEvent<number>After a new position commits, with the position and the previous one.SlotDescriptiondefaultThe slides, one per child.previousReplaces the built-in previous button.nextReplaces the built-in next button.indicatorsReplaces the built-in indicators.PartDescription::part(carousel)The root region holding the viewport and its controls.::part(carousel-announcements)The polite live region announcing the position.::part(carousel-autoplay-control)The pause and resume button autoplay adds.::part(carousel-controls)The row of controls, placed in the footer, inside or outside the viewport.::part(carousel-effect-surface)The layer an effect draws on in place of the moving track.::part(carousel-indicator)The indicators: a fraction, bullets or a progress bar.::part(carousel-item)The shell around one slide.::part(carousel-scrollbar)The optional scrollbar track.::part(carousel-status)The visible position text.::part(carousel-thumb)The thumb of the scrollbar.::part(carousel-track)The moving strip of slides.::part(carousel-viewport)The clipping window the slides move through.