motion-slider

Gesture-driven horizontal carousel with spring snap, velocity-based flicking, and rubber-band edges. Works with any child content.

Preview

motion-split Spring motion.

Each character springs in independently, with controllable interval and duration.

motion-stagger
Spring physics
Velocity flick
Gesture driven
Accessible

Children reveal with cascading spring delay.

motion-counter fluid · spring · native

Numbers animate to their target with eased timing.

Import

import 'motion-components/motion-slider'   // just this one
import 'motion-components'               // or the whole library

Usage

<motion-slider gap="24">
  <div>Slide one</div>
  <div>Slide two</div>
  <div>Slide three</div>
</motion-slider>

How it works

Each direct child becomes a slide, sized to fill the slider width. Pointer events are tracked across the window so drag doesn't break when the cursor leaves the element. On release, velocity is sampled over the last 6 pointer events: a fast flick snaps to the adjacent slide; a slow drag snaps to the nearest.

The spring uses the captured velocity as its initial condition, so the snap animation feels like a physical continuation of the gesture rather than a teleport.

Properties

Attribute Property Type Default Description
gap gap number 24 Gap between slides in px
arrows arrows boolean true Show prev/next arrow buttons. Set arrows="false" to hide.

CSS custom properties

Property Type Default Description
--mc-color-accent color #2563eb Color of the keyboard focus ring on the arrows

Methods

Method Description
goTo(index, initialVelocity?) Animates to the slide at index. The optional initialVelocity is in px/s. Called before the slider is built, it sets the starting slide instead. The index is clamped to the slide range.
next() Moves to the next slide. Does nothing on the last one.
prev() Moves to the previous slide. Does nothing on the first one.
index Read-only. Index of the active slide.

Events

Event Detail Description
motion-change index Fired when the active slide changes. Bubbles and is composed.

Other components also fire a bubbling motion-change. A listener on a parent element should check event.target.

import type { MotionSlider } from 'motion-components'

const slider = document.querySelector<MotionSlider>('motion-slider')!
slider.goTo(2)

document.querySelector('#prev')!.addEventListener('click', () => slider.prev())
document.querySelector('#next')!.addEventListener('click', () => slider.next())

document.addEventListener('motion-change', (event) => {
  if (event.target !== slider) return
  console.log('slide', slider.index)
})

Keyboard

Focus the slider and use ArrowLeft / ArrowRight to navigate.

Accessibility

When prefers-reduced-motion is set, transitions are instant with no animation.