Transitions✨ Premium

Shared Element

A single element, never copied, slides from one slot to the other in 0.8 s with a 17 px overshoot before settling, grows by 15% and turns from pink to violet: the object stays the same while its place changes. A CSS transition on transform, between two positions known in advance — 0.6 KB of JavaScript for the toggle, direct arrival when the user reduces motion.

CSSSharedPremium

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Transitions has 52 effects, including 9 free. Explore the category →

3 usage examples

Pawn moving two squares — an online chess course for beginners — Shared Element example 1

① Pawn moving two squares — an online chess course for beginners

WhenLesson on openings: the student clicks the board and sees the white pawn leave e2 and land on e4, the starting square staying highlighted in yellow.
WhyThe piece slides from one square to the other instead of disappearing and reappearing: the student follows it with their eyes and understands that a pawn may advance two squares on its first move.
SettingsVertical path (translateY(-80px), two 40 px squares) instead of horizontal; 34 px ivory pawn that takes a golden glow on e4; 320 px board drawn with conic-gradient, Unicode pieces.
Order moving to "Shipped" — the back office of a ceramicist's shop — Shared Element example 2

② Order moving to "Shipped" — the back office of a ceramicist's shop

WhenTuesday, shipping day: the potter marks order #1182 (glazed stoneware vase) as shipped, the card leaves the "Being prepared" column for "Shipped".
WhyThe card travels from one column to the other: the potter sees what changed status without looking for it, and it turns from orange to green on the way.
SettingsThe element becomes a 216 × 70 px card (10 px corners, left-aligned text); 240 × 300 px columns; 260 px path, growth reduced to 1.04; orange #d97706 → #f59e0b gradient then green #15803d → #22c55e.
Operator moved to another shift — a factory schedule in three 8-hour shifts — Shared Element example 3

③ Operator moved to another shift — a factory schedule in three 8-hour shifts

WhenOn Thursday, the shop foreman fills the open night spot: replacement R. Diallo's badge leaves the morning shift and crosses the afternoon one.
WhyThe same badge moving avoids thinking it is a duplicate or a deletion: you follow the person from one shift to the other.
SettingsThree 140 px columns, the shared element only stops in the first and last (312 px path); 120 × 44 px pill badge, yellow "morning" then indigo "night"; overshoot kept, margins widened so it stays in frame.

How it works

The .shared-container scene is 300 × 200 px and sets two 110 × 130 px .shared-slots side by side, dashed-bordered, 30 px apart. The shared element .shared-ball (60 px circle, #e11d48 → #f43f5e gradient, pink shadow) belongs to neither: it is absolutely positioned in the scene, 25 px from the left edge. The whole .shared-trigger area is a button (role="button", tabindex="0"): a click, Enter or Space toggles the .active class on the scene and updates aria-pressed. Each area on the page has its own listener; nothing is exposed on window.

With .active, the element gets transform: translateX(190px) scale(1.15) (190 px = 300 px scene − 85 − 25), a violet #7c3aed → #a78bfa gradient and a violet shadow. The 0.8 s transition follows cubic-bezier(.34, 1.56, .64, 1), a curve that goes past 1: measured every 100 ms in Chromium 151 and Firefox 153, the element passes 116, 176, 214 then 228 px, overshoots its place by 17 px around 400-500 ms, comes back and settles at 211 px at 800 ms. The movement goes through transform, which the browser composites without recomputing layout; the shadow changes color during the trip, but the gradient, which does not interpolate, switches at the click. A second click plays the reverse path with the same bounce.

This is a hand-made technique: both positions are known in advance and written in the CSS, the element never changes parent in the DOM. The View Transitions API does something else: it photographs the page before and after a DOM change and interpolates between the two pictures (see View Transitions Page Reveal). Here, no snapshot: the real element moves, with its text, hover states and focus, and it works in every browser that knows CSS transitions. The trade-off: if the destination depends on layout (a card changing lists), positions must be measured in JavaScript, as the FAQ describes.

Accessibility

  • Keyboard: the area is a button reachable with Tab; Enter and Space toggle like the click (measured in Chromium and Firefox), aria-pressed switches to true, and a 2 px white outline marks focus. Its aria-label ("Move the element to the other slot") states the action; replace it with yours ("Mark the order as shipped").
  • Reduced motion: under prefers-reduced-motion, the transition is removed and the element is in the other slot by the next frame — measured: 211 px 60 ms after the click, no overshoot.
  • State change: the movement is seen but not heard. If the slot carries meaning (status, team), also write it in the element's text or announce it in an aria-live area: a screen reader only perceives aria-pressed.
  • Contrast: white "GO" gives 4.7:1 on red #e11d48 but 3.7:1 on the light pink and 2.7:1 on #a78bfa; keep the text short and bold, or darken the end of the gradients. The slot labels (30% white, 2.6:1) and the bottom hint (3.1:1) are decorative: do not give them information.

Browser compatibility

CSS transitions on transform, box-shadow and background, and about fifteen lines of JavaScript, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26: same 190 px path, same overshoot, same arrival at scale 1.15, same return on the second click; direct arrival under reduced motion. Only the background gradient does not interpolate (no browser animates background-image): it switches at the start.

Chrome 51+✓ Full
Firefox 50+✓ Full
Safari 10+✓ Full
Edge 79+✓ Full
Mobile iOS 10+✓ Full, on tap
Android Chrome 51+✓ Full, on tap

Without JavaScript, the element stays in the first slot and clicking does nothing. With reduced motion, it changes slot without a path. Without CSS transitions (very old browsers), it jumps from one slot to the other.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="shared-root">
  <div class="shared-trigger" role="button" tabindex="0" aria-pressed="false" aria-label="…">
    <div class="shared-container">
      <div class="shared-slot"><span class="shared-slot-label">Slot A</span></div>
      <div class="shared-slot"><span class="shared-slot-label">Slot B</span></div>
      <div class="shared-ball">GO</div>
    </div>
    <span class="shared-hint">Click to toggle</span>
  </div>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Shared Element, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Path (CSS — translateX(190px)) 190 px to the right Computed for the 300 px scene. translateY(-80px) gives a vertical path; translate(190px, 40px) a diagonal. The overshoot is about 9% of the path: leave that margin at the edge.
Curve and duration (CSS — transition: .8s cubic-bezier(.34, 1.56, .64, 1)) 0.8 s, overshoot then return cubic-bezier(.2, .8, .2, 1) arrives without overshooting; 0.5 s for a short move, beyond 1 s the gesture feels slow.
Growth on arrival (CSS — scale(1.15)) ×1.15 scale(1) keeps the size; an element that changes importance (selected, highlighted) can go to 1.3.
Colors (CSS — .shared-ball background and box-shadow) pink #e11d48 → violet #7c3aed The shadow glides from one color to the other, the gradient switches at the start. For a color that changes on the way, use a plain background-color (it interpolates).
Element shape (CSS — .shared-ball) 60 px circle A card, a badge, an image: change width, height and border-radius, then recompute the path so it lands in the second slot.

FAQ

No, nor anything else from the View Transitions API. That API (document.startViewTransition, view-transition-name) photographs the page before and after a DOM change and animates between the two pictures; that is what View Transitions Page Reveal (fx-0786) does. Shared Element animates the real element with a CSS transition on transform, between two positions written in the stylesheet. Advantages: it works everywhere, keeps text selectable and focus during the trip. Limit: the element does not change parent, positions must be known.
With the FLIP technique: measure its position (var first = el.getBoundingClientRect()), move it in the DOM (columnB.appendChild(el)), measure the new one (last), apply without transition el.style.transform = 'translate(' + (first.left - last.left) + 'px, ' + (first.top - last.top) + 'px)' to put it visually back at its starting point, then, on the next frame (requestAnimationFrame), reset transform to zero with this effect's transition. The element slides to its new place, with the same overshoot.
The background is a gradient (background-image), and browsers do not interpolate one gradient into another: the value switches at the start of the transition. The shadow, however, is a plain color and goes from pink to violet during the whole trip. For a color that changes on the way, replace the gradient with background-color, or layer a violet ::before pseudo-element whose opacity goes from 0 to 1.