viewTransition()
Style shared View Transitions API animations with a hashed bag class. You still set unique view-transition-name values at runtime.
The View Transitions API styles its animation with ::view-transition-old(name) and ::view-transition-new(name),
matched against a view-transition-name you set on the element. That name has to be unique per element. Two elements
with the same name break the transition.
Panda extracts and shares styles across every place you call a style function. Those two rules don't mix: if the CSS
were keyed on view-transition-name, Panda couldn't dedupe it, since the name is different every time. So Panda
doesn't touch view-transition-name at all. Instead, viewTransition() gives you a class, generated from a real
CSS property built for exactly this, view-transition-class. Panda can extract and share a class like any other
atomic style. You still set the unique name yourself, however your framework wants it.
Import viewTransition from styled-system/css.
Basic example
import { viewTransition } from 'styled-system/css'
const slide = viewTransition({
group: {
animationDuration: '0.4s',
animationTimingFunction: 'ease-in-out'
},
imagePair: { isolation: 'isolate' },
old: { opacity: 0 },
new: { opacity: 1 }
})
// → "vt_xxx"
slide is a class name, "vt_xxx". Panda emits the CSS behind it:
@layer utilities {
.vt_xxx {
view-transition-class: vt_xxx;
}
::view-transition-group(.vt_xxx) {
animation-duration: 0.4s;
animation-timing-function: ease-in-out;
}
::view-transition-image-pair(.vt_xxx) {
isolation: isolate;
}
::view-transition-old(.vt_xxx) {
opacity: 0;
}
::view-transition-new(.vt_xxx) {
opacity: 1;
}
}
Call viewTransition() again somewhere else with the exact same options and you get the exact same class back, no
duplicate CSS. That's the whole point: one shared bag class, however many elements use it.
Wiring it into your framework
viewTransition() only gets you the class. Every framework has its own way of setting view-transition-name, so
that part is still on you.
React
Pass the class into React's View Transition Class props, enter, exit, share, update, default. Reserve
name for elements that need a shared-element transition:
import { ViewTransition } from 'react'
import { viewTransition } from 'styled-system/css'
const slide = viewTransition({
group: { animationDuration: '0.4s' },
old: { opacity: 0 },
new: { opacity: 1 }
})
export function HeroImage() {
return (
<ViewTransition name="hero" share={slide}>
<img src="/hero.jpg" alt="Hero" />
</ViewTransition>
)
}
<ViewTransition> needs a React build that exports it, Canary or Experimental as of this writing. Next.js can opt in
with experimental.viewTransition: true.
Astro
Astro owns the name through transition:name. Put the bag class on the element next to it:
---
import { viewTransition } from '../styled-system/css'
const slide = viewTransition({
group: { animationDuration: '0.4s' },
old: { opacity: 0 },
new: { opacity: 1 }
})
---
<img class={slide} transition:name="hero" src="/hero.jpg" alt="Hero" />
Enable client routing with <ClientRouter /> from astro:transitions in your layout, or the browser never starts a
transition to animate.
Solid or Nuxt
Neither framework wraps this for you, so you set both the class and the name yourself:
import { viewTransition } from 'styled-system/css'
const slide = viewTransition({
group: { animationDuration: '0.4s' },
old: { opacity: 0 },
new: { opacity: 1 }
})
<img
class={slide}
style={{ viewTransitionName: 'hero' }}
src="/hero.jpg"
alt="Hero"
/>
Slots
Four options, each one a slot in the View Transitions pseudo-element tree:
| Option | CSS selector |
|---|---|
group | ::view-transition-group(.vt_*) |
imagePair | ::view-transition-image-pair(.vt_*) |
old | ::view-transition-old(.vt_*) |
new | ::view-transition-new(.vt_*) |
Each one takes a style object, same shape as css(). For animations, set animationName to a theme keyframe or a
name you defined yourself.
Shipping a shared transition from a design system
Export the bag as a constant from your library source, the same way you'd export a shared cva:
export const slide = viewTransition({
old: { opacity: 0 },
new: { opacity: 1 }
})
panda lib serializes it into build info, so apps consuming your library get the CSS without re-scanning your
source. It tree-shakes the same way recipes do, only the bags an app actually imports ship. There's no
theme.viewTransitions config registry to register this in. If you've shared a cva or a plain css() helper from
a design system before, this works the same way.