Transitions
CSS-based enter/leave animations using snabbdom hooks. Sygnal uses a Vue-style transition system where a single name prop generates six CSS classes for fine-grained control over enter and leave animations.
import { Transition } from 'sygnal'
function AnimatedList({ state }) { return ( <div> <button className="toggle">Toggle</button> <Transition name="fade"> {state.visible && <div className="content">Animated!</div>} </Transition> </div> )}
AnimatedList.initialState = { visible: true }AnimatedList.intent = ({ DOM }) => ({ TOGGLE: DOM.click('.toggle') })AnimatedList.model = { TOGGLE: (state) => ({ ...state, visible: !state.visible }),}| Prop | Type | Default | Description |
|---|---|---|---|
name | string | 'v' | Base name for generated CSS classes |
duration | number | — | Explicit timeout in ms. If omitted, listens for transitionend event |
CSS Class Lifecycle
Section titled “CSS Class Lifecycle”Given name="fade", the following classes are applied automatically:
On enter (element inserted):
fade-enter-from+fade-enter-activeadded simultaneously- Next frame:
fade-enter-fromremoved,fade-enter-toadded - On transition end:
fade-enter-activeandfade-enter-toremoved
On leave (element removed):
fade-leave-from+fade-leave-activeadded simultaneously- Next frame:
fade-leave-fromremoved,fade-leave-toadded - On transition end:
fade-leave-activeandfade-leave-toremoved, then element is removed from DOM
CSS Example
Section titled “CSS Example”The demo above uses this stylesheet:
/* Active classes define the transition properties */.fade-enter-active,.fade-leave-active { transition: opacity 0.3s ease;}
/* "from" classes define the starting state */.fade-enter-from,.fade-leave-to { opacity: 0;}Slide Example
Section titled “Slide Example”.slide-enter-active,.slide-leave-active { transition: transform 0.3s ease;}
.slide-enter-from { transform: translateX(-100%);}
.slide-leave-to { transform: translateX(100%);}<Transition name="slide"> {state.showPanel && <div className="panel">Slides in and out</div>}</Transition>Around a Collection
Section titled “Around a Collection”A Collection has no element of its own: its items are its parent’s children. A <Transition> around it applies to each item: an item that is added enters, an item that is removed leaves (it keeps its place until its leave ends), and the items already there when the Collection first renders all enter. Items that don’t change are left alone.
<ul> <Transition name="fade"> <Collection of={TodoItem} from="todos" /> </Transition></ul>This is the same as putting the <Transition> around the root element in the item’s own view, which also works.
Two cases behave differently:
- An item whose view returns a fragment (
<>…</>) or text has no element of its own to animate: its elements appear and go at once, with no enter or leave classes. Give the item one root element (<li>), or put a<Transition>around each element inside the item’s view. - On a server-rendered page, the client’s first render adopts the server’s elements, except the ones under a
<Transition>: those are made again so their enter can run. Around a Collection, that is every item: the server’s items are replaced by the client’s, and each plays its enter transition when the page starts (focus or text typed into an item before start-up is lost). This is by design: the alternative is an enter that never runs.
Explicit Duration
Section titled “Explicit Duration”If your animation doesn’t use CSS transitions (or you want a fixed timeout), pass duration:
<Transition name="fade" duration={500}> {state.visible && <div>Uses setTimeout instead of transitionend</div>}</Transition>