Focus Carousel

Swipe through a row of cards that snap to the centre, where the focused card grows to full size and the rest recede.

  • CSS
  • Cards
  • Drag
  • Added
01Dawn
02Noon
03Dusk
04Night
05Storm
Swipe or scroll sideways

AI prompt

Create a centre-focus card carousel with native scroll snapping and CSS scroll-driven animations. The track is a horizontal flex row with overflow-x: auto, scroll-snap-type: x mandatory, and inline padding of half the track width minus half a card so the first and last cards can reach the centre. Each card has scroll-snap-align: center. Animate each card with animation-timeline: view(inline) and a keyframe that goes from scale(0.8) and opacity 0.45 at 0%, to scale(1) and opacity 1 at 50%, back to scale(0.8) and opacity 0.45 at 100%, so the card at the centre is largest. Wrap the animation in @supports (animation-timeline: view()) so other browsers get a plain snapping row. Hide the scrollbar visually but keep the track keyboard-focusable. Under prefers-reduced-motion, drop the scaling and keep the snapping. The cards read Dawn, Noon, Dusk, Night, and Storm.

Source

Demo.tsx
'use client'

import s from './demo.module.css'

export default function SnapCarousel({ labels }: { labels: Record<string, string> }) {
  return (
    <div className={s.wrap}>
      <div className={s.track} tabIndex={0} role="region" aria-label={labels.carousel}>
        {labels.items.split('|').map((t, i) => (
          <div key={t} className={s.card} style={{ '--h': 30 + i * 60 } as React.CSSProperties}>
            <span className={s.n}>{String(i + 1).padStart(2, '0')}</span>
            <strong className={s.name}>{t}</strong>
          </div>
        ))}
      </div>
      <span className={s.hint}>{labels.hint}</span>
    </div>
  )
}
demo.module.css
.wrap {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  gap: 10px;
}

.track {
  --card: 120px;
  display: flex;
  gap: 12px;
  padding: 8px calc(50% - var(--card) / 2);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.track::-webkit-scrollbar { display: none; }
.track:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }

.card {
  flex: none;
  display: grid;
  align-content: space-between;
  width: var(--card);
  height: 150px;
  padding: 12px;
  border-radius: 14px;
  background: linear-gradient(160deg, oklch(64% 0.1 var(--h)), oklch(36% 0.06 var(--h)));
  scroll-snap-align: center;
}

.n { font: 500 var(--text-xs) / 1 var(--font-mono); color: oklch(100% 0 0 / 0.7); }
.name { font: 700 var(--text-xl) / 1 var(--font-display); color: oklch(98% 0.01 80); }

.hint {
  font: 500 var(--text-xs) / 1 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  color: var(--stage-muted);
}

@supports (animation-timeline: view()) {
  .card {
    animation: focus linear both;
    animation-timeline: view(inline);
  }
}

@keyframes focus {
  0%, 100% { transform: scale(0.8); opacity: 0.45; }
  50% { transform: scale(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .card { animation: none; }
}

Suggest an idea

Seen a motion or a theme you would like to find here? Describe it, and it opens as a prefilled GitHub issue for you to post.

What are you suggesting?

Needs a GitHub account. Suggestions are public.