View Transition API Carousel — HTML CSS JS Snippet

View Transition API Carousel · Cards · Plain HTML, CSS & JS · Live preview

What's included

Features

Uses the real browser View Transitions API — document.startViewTransition — not a hand-rolled animation
Zero keyframes, zero CSS transitions, zero JS-driven transform math for the slide-change animation itself
One CSS declaration (view-transition-name) is the entire animation setup
Graceful feature detection — falls back to an instant, fully functional swap in unsupported browsers
The browser automatically interpolates position, size, and appearance between the before/after DOM snapshots
Dots, arrows, and keyboard navigation all route through the same update() function uniformly

About this UI Snippet

View Transition API Carousel — The Browser Animates the Diff, Not You

Screenshot of the View Transition API Carousel snippet rendered live

Every other carousel in this library animates a transition by hand — a transform, an opacity, a keyframe. This one hands that job to the browser itself: document.startViewTransition(callback) snapshots the DOM as it is right now, runs the callback (which just replaces the slide's HTML — no animation code inside it at all), snapshots the DOM again afterward, and then automatically cross-fades and morphs between the two snapshots for any element carrying a matching view-transition-name.

One CSS property is the entire animation setup

The *only* animation-related code in this snippet is view-transition-name: vtc-active-slide on .vtc-slide in the CSS. That single declaration tells the browser "treat this element as a named subject to animate between states" — there's no transition, no @keyframes, no JS-driven transform anywhere in the slide-change logic. paint() just tears down the old slide's markup and writes in the new one synchronously; the browser handles everything about *how* that change appears.

Feature detection, not a hard dependency

Because the View Transitions API isn't universal yet, every call is guarded: typeof document.startViewTransition === 'function'. When it's missing, update() just calls paint() directly — the carousel still works, it simply swaps instantly instead of animating. That's a deliberate progressive-enhancement pattern: the feature adds polish where supported and never breaks functionality where it isn't.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly what document.startViewTransition does with the DOM snapshots it captures before and after its callback runs, and why the callback itself (paint()) contains no animation code whatsoever. It's also worth asking the assistant to give the icon and title their own separate view-transition-names so they animate independently from the background, or to add a prefers-reduced-motion check that skips the transition wrapper entirely for users who've requested reduced motion.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a carousel in plain HTML, CSS, and vanilla JavaScript that uses the native browser View Transitions API to animate between slides, with no hand-written CSS transitions, keyframes, or JavaScript-driven transform animation for the slide-change effect itself — no library.

Requirements:
- A single stage element that, at any time, contains the markup for exactly one currently active slide (icon, title, and background), swapped out for a different slide's markup on navigation — implemented as a straightforward synchronous DOM replacement with no animation logic inside that replacement function.
- The slide element must carry a view-transition-name CSS property, which is the only styling responsible for enabling the automatic browser-driven cross-fade/morph animation between the old and new slide states.
- Before performing the DOM replacement, feature-detect whether the browser supports the View Transitions API by checking whether document.startViewTransition exists as a function. If supported, wrap the slide-replacement logic inside a call to document.startViewTransition, passing the replacement logic as its callback. If not supported, call the replacement logic directly with no wrapping, so the carousel still functions correctly (just without the animated transition) in browsers lacking the API.
- Display a small text note on the page reporting to the user whether their current browser supports the View Transitions API or not, determined by the same feature check.
- Previous/next buttons and a row of dynamically generated indicator dots that trigger the same slide-update logic (including the same view-transition wrapping/fallback behavior) as any other navigation method.
- Left/Right arrow key support performing the same next/previous action as the buttons.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="vtc-wrap">
  <div class="vtc-stage" id="vtcStage">
    <div class="vtc-slide" style="background:linear-gradient(160deg,#6366f1,#4338ca)"><span>🎧</span><h3>Headphones</h3></div>
  </div>
  <div class="vtc-controls">
    <button class="vtc-btn" id="vtcPrev" aria-label="Previous">‹</button>
    <div class="vtc-dots" id="vtcDots"></div>
    <button class="vtc-btn" id="vtcNext" aria-label="Next">›</button>
  </div>
  <p class="vtc-support" id="vtcSupport"></p>
</div>

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA support note tells you whether your browser has the View Transitions API; the first slide appears.
  2. 2
    Click the arrowsIn a supporting browser, the slide smoothly cross-fades and morphs into the next one — no hand-written animation.
  3. 3
    Click a dotJump directly to that slide, with the same automatic browser-driven transition.
  4. 4
    Try it in an unsupported browserThe carousel still works perfectly — it just swaps instantly instead of animating.
  5. 5
    Inspect the CSSNotice there's no transition or @keyframes rule driving the slide change at all — just one view-transition-name declaration.

Real-world uses

Common Use Cases

Modern-browser-first product pages
Ship a polished native-feeling transition with almost no animation code to maintain.
Learning the View Transitions API
One of the clearest minimal examples of startViewTransition applied to a real, common UI pattern.
Progressive enhancement showcases
Demonstrates a feature that enhances supporting browsers without breaking unsupported ones.
Lightweight content rotators
Get smooth, correct-looking transitions without shipping any custom animation logic at all.

Got questions?

Frequently Asked Questions

It marks an element as a named "subject" the browser should track across a startViewTransition call — if an element with that same name exists in both the before and after DOM snapshots, the browser automatically animates between its old and new state (position, size, opacity) rather than treating it as an unrelated element being removed and added.

The feature is checked with typeof document.startViewTransition === "function" before use — when it's missing, paint() is called directly with no wrapping transition call, so the carousel still functions correctly, just without the animated cross-fade/morph.

Yes — give the icon, the title, and the background each their own unique view-transition-name, and the browser will animate each one's before/after state independently rather than treating the whole slide as one blob.

Yes, in supporting browsers it also works for full page navigations (same-document or cross-document), which is a separate but related use of the same API — this snippet demonstrates the single-page, same-document version.

Arrows and dots are real labeled buttons, fully keyboard-operable via Left/Right arrow keys; consider also checking prefers-reduced-motion and skipping startViewTransition's animation (calling paint() directly) for users who've requested reduced motion.