Carousel — <mv-carousel>
Carousel built on native scroll snap: arrows, dots synced via IntersectionObserver, keyboard, looping, autoplay with a progress dot (pauses on hover, focus and reduced motion), multiple slides per view, vertical mode and mouse drag.
Category Layout Type Web Component (<mv-carousel>) Status stable Keywords carousel, slider, scroll-snap, autoplay, gallery, swipe, drag
When to use
A set of slides, like featured articles or photos, must be browsed one or a few at a time with arrows and dots A horizontally scrollable row of cards should snap into place and support swipe, drag and keyboard A hero rotates a few promotions automatically, with a pause button and reduced-motion support
Avoid when
Product photos need synced thumbnails, hover zoom and fullscreen → use Product Gallery instead Logos or testimonials should scroll continuously without user navigation → use Marquee instead Critical content sits on later slides; users rarely go past the first, so show it directly
Get Carousel. Included in every plan: the Pro pack ships all 226 components with the CLI and the MCP server used below.
See plans
Install
node scripts/add.mjs carousel --out ./src/marvelous
AI agent with the Marvelous UI MCP server: install_components({ slugs: ["carousel"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Files copied (dependencies included): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, components/carousel/carousel.js, components/carousel/carousel.css.
Usage
Canonical markup — start from it and customize with attributes, data-* and CSS variables:
<div style="display:grid;gap:2rem;width:100%;max-width:44rem;margin-inline:auto">
<mv-carousel id="carousel-demo-hero" label="Featured destinations" loop autoplay="5000">
<article style="position:relative;display:grid;align-content:end;gap:.25rem;aspect-ratio:16/8;padding:1.5rem;border-radius:var(--mv-radius-xl);color:#fff;background:radial-gradient(120% 90% at 85% 10%,#ffd7a8 0,transparent 55%),linear-gradient(135deg,#f0845c,#b14a6d 60%,#5b2a6e)">
<span style="font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;opacity:.85">Portugal · 4 nights</span>
<h3 style="margin:0;font-size:1.75rem;letter-spacing:-.02em">Lisbon</h3>
<p style="margin:0;max-width:26rem;font-size:.875rem;opacity:.9">Yellow trams, miradouros and pastéis de nata at sunset.</p>
</article>
<article style="position:relative;display:grid;align-content:end;gap:.25rem;aspect-ratio:16/8;padding:1.5rem;border-radius:var(--mv-radius-xl);color:#fff;background:radial-gradient(110% 80% at 20% 0%,#ffc4d6 0,transparent 55%),linear-gradient(160deg,#e45f7f,#7d3c98 55%,#2c2a6b)">
<span style="font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;opacity:.85">Japan · 9 nights</span>
<h3 style="margin:0;font-size:1.75rem;letter-spacing:-.02em">Kyoto</h3>
<p style="margin:0;max-width:26rem;font-size:.875rem;opacity:.9">Wooden temples, the lanes of Gion and cherry blossoms in spring.</p>
</article>
<article style="position:relative;display:grid;align-content:end;gap:.25rem;aspect-ratio:16/8;padding:1.5rem;border-radius:var(--mv-radius-xl);color:#fff;background:radial-gradient(120% 90% at 80% 0%,#9ff3e1 0,transparent 50%),linear-gradient(150deg,#1f9c9a,#1d5f8a 55%,#16294f)">
<span style="font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;opacity:.85">Iceland · 6 nights</span>
<h3 style="margin:0;font-size:1.75rem;letter-spacing:-.02em">Reykjavik</h3>
<p style="margin:0;max-width:26rem;font-size:.875rem;opacity:.9">Northern lights, steaming lagoons and roads at the edge of the world.</p>
</article>
<article style="position:relative;display:grid;align-content:end;gap:.25rem;aspect-ratio:16/8;padding:1.5rem;border-radius:var(--mv-radius-xl);color:#fff;background:radial-gradient(120% 90% at 15% 10%,#ffe29a 0,transparent 55%),linear-gradient(140deg,#e89b3c,#c2553a 55%,#6b2b2b)">
<span style="font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;opacity:.85">Morocco · 5 nights</span>
<h3 style="margin:0;font-size:1.75rem;letter-spacing:-.02em">Marrakech</h3>
<p style="margin:0;max-width:26rem;font-size:.875rem;opacity:.9">Fragrant souks, hidden riads and mint tea on the rooftops.</p>
</article>
</mv-carousel>
<mv-carousel id="carousel-demo-cards" label="Recent articles" controls="footer" style="--mv-carousel-per-view:3;--mv-carousel-gap:.75rem">
<a href="#" style="display:grid;gap:.35rem;padding:1rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-lg);background:var(--mv-surface);color:inherit;text-decoration:none"><span style="font-size:.75rem;color:var(--mv-accent-fg);font-weight:600">Design</span><strong style="font-size:.9rem;line-height:1.3">Designing a token system that lasts</strong><span style="font-size:.75rem;color:var(--mv-fg-muted)">6 min read</span></a>
<a href="#" style="display:grid;gap:.35rem;padding:1rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-lg);background:var(--mv-surface);color:inherit;text-decoration:none"><span style="font-size:.75rem;color:var(--mv-accent-fg);font-weight:600">Accessibility</span><strong style="font-size:.9rem;line-height:1.3">Visible focus, no compromises</strong><span style="font-size:.75rem;color:var(--mv-fg-muted)">4 min read</span></a>
<a href="#" style="display:grid;gap:.35rem;padding:1rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-lg);background:var(--mv-surface);color:inherit;text-decoration:none"><span style="font-size:.75rem;color:var(--mv-accent-fg);font-weight:600">CSS</span><strong style="font-size:.9rem;line-height:1.3">Scroll snap: the carousel without JavaScript</strong><span style="font-size:.75rem;color:var(--mv-fg-muted)">8 min read</span></a>
<a href="#" style="display:grid;gap:.35rem;padding:1rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-lg);background:var(--mv-surface);color:inherit;text-decoration:none"><span style="font-size:.75rem;color:var(--mv-accent-fg);font-weight:600">Performance</span><strong style="font-size:.9rem;line-height:1.3">Jank-free animation with transform</strong><span style="font-size:.75rem;color:var(--mv-fg-muted)">5 min read</span></a>
<a href="#" style="display:grid;gap:.35rem;padding:1rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-lg);background:var(--mv-surface);color:inherit;text-decoration:none"><span style="font-size:.75rem;color:var(--mv-accent-fg);font-weight:600">Web Components</span><strong style="font-size:.9rem;line-height:1.3">Native forms with ElementInternals</strong><span style="font-size:.75rem;color:var(--mv-fg-muted)">7 min read</span></a>
<a href="#" style="display:grid;gap:.35rem;padding:1rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-lg);background:var(--mv-surface);color:inherit;text-decoration:none"><span style="font-size:.75rem;color:var(--mv-accent-fg);font-weight:600">Motion</span><strong style="font-size:.9rem;line-height:1.3">Respecting prefers-reduced-motion</strong><span style="font-size:.75rem;color:var(--mv-fg-muted)">3 min read</span></a>
</mv-carousel>
</div>
API
Attributes
Name Type Default Description labelstring Accessible name of the region (aria-label). loopboolean Wraps back to the start after the last slide (and vice versa). autoplaynumber (ms) | boolean Automatic scrolling (5000 ms if empty), with a play/pause button; disabled with reduced motion. orientationhorizontal | vertical horizontalScroll direction (vertical: height via --mv-carousel-height). controlsoverlay | footer overlayArrows overlaid on the edges or grouped in the footer with the dots. no-controlsboolean Hides the arrows. no-dotsboolean Hides the dots. dragtrue | false trueMouse drag (touch uses native scrolling).
Properties
Name Type Description indexnumber Active page (setting it = goTo). lengthnumber Number of slides. pagesnumber Number of positions (slides − per view + 1). playingboolean Autoplay active (read-only).
Methods
Name Description next() / prev()Next / previous slide. goTo(index, { smooth })Goes to a position. play() / pause()Controls autoplay.
Events
Name Description mv-changedetail: { index, slide } when the active position changes.
Content structure
Name Description childrenEach child becomes a slide (role=group, “2 of 5”).
CSS classes
Name Description mv-carousel-track / -slide / -arrow / -dots / -dot / -playGenerated parts; data-active on the current slide.
CSS variables
Name Default Description --mv-carousel-per-view1Slides visible at once (integer; can vary per media query). --mv-carousel-gapvar(--mv-space-4)Space between slides. --mv-carousel-height20remHeight in vertical orientation. --mv-carousel-radius0Track radius (clips slides while scrolling).
Accessibility
APG Carousel pattern: labeled region with aria-roledescription=“carousel”, slides are role=group with aria-roledescription=“slide” (“2 of 5”). Arrows and dots are named buttons (aria-current on the active dot). The track is focusable: ←/→ (↑/↓ when vertical), Home/End. Autoplay has a pause button and stops on hover and focus, off screen, in a hidden tab and with reduced motion; the track is aria-live=polite only when it is not auto-scrolling.