Svelte Transition Engine: JS-Driven CSS Keyframe Generation
- Last updated
- Prerequisites:
- Svelte template directives (transition:)
- Understanding of CSS animations and keyframes
- svelte
- transitions
- animation
- keyframes
- css
- motion
Read at your depth
The practical view
Svelte transitions animate elements as they enter/leave the DOM: <p transition:fade> or with parameters {#if show}<div transition:fly={{ y: 100, duration: 300 }}>{/if}. Built-in transitions: fade, fly, slide, scale, draw, blur, crossfade. Custom transitions are functions returning { duration, delay, easing, css(t, u) } where t goes 0→1 (u = 1 - t). Svelte generates the CSS keyframes from your css function and applies them. Use in: transitions for enter, out: for leave, and both with transition:. Transitions require the element to be in a {#if}/{#each} block (conditional mounting).
The same idea in other frameworks
React has no built-in transitions; libraries (framer-motion) or CSS classes fill the gap.Angular animations (@angular/animations) build a similar JS-described motion engine.Vue's <Transition> component wraps CSS/JS hooks with a similar enter/leave lifecycle.Legacy vs modern
Hand-written CSS classes toggling vs Svelte transition directive
Manually toggling CSS classes requires waiting for animationend and matching enter/exit styles; the transition directive generates keyframes and handles removal.
el.classList.add('fade-in'); // ❌ OLD: manual class + animationend handling{#if show}<div transition:fade={{ duration: 300 }}>Hi</div>{/if} // ✅ NEWInterview gotchas
Context
This probes the removal coordination — interviewers want the animationend/tick mechanism and the fallback path for transitions that only use JS ticks, not just the happy-path CSS case.
The mechanical answer
For CSS-based transitions, Svelte generates keyframes and listens for the animationend event on the element to know the outro completed, then removes the element from the DOM (and destroys its bindings). If the transition returns no css (only a tick function), there is no CSS animation to listen to, so the runtime falls back to polling the tick function's returned duration: it waits that duration and then removes the element. The engine also handles interruption: if an intro is in flight and the element starts an outro (or vice versa), the current animation is cancelled and the new one starts from the element's current state. This lifecycle is what guarantees the DOM is never left with half-animated ghost elements.
Trap
The naive answer is 'the element is removed immediately when the condition flips'. The element stays until the outro finishes (animationend or duration tick). Another trap: assuming transitions work on elements not in a conditional block — the transition lifecycle is tied to mount/unmount, so {#if}/{#each} is required. Candidates who mention animationend and the tick fallback show real understanding.
Context
This probes the custom-transition authoring model — interviewers want you to explain the t/u parameters and why generating CSS keyframes (rather than running JS per frame) is the design choice.
The mechanical answer
css(t, u) is called per keyframe step: t goes from 0 (start) to 1 (end) as the transition progresses, and u is the complement (u = 1 - t). Your function returns a style string (e.g. opacity: ${t}) which Svelte evaluates across the transition duration to build a full set of @keyframes. The engine then runs that animation as native CSS, which is compositor-friendly and off the main thread where possible — far cheaper than a JS rAF loop mutating styles every frame. The JS function is only the description (the 'what'), and CSS is the execution (the 'how'). This is why Svelte transitions are smooth: the heavy per-frame work is delegated to the browser's animation machinery, while JS stays in control of the keyframe definition and lifecycle.
Trap
The naive answer is 'Svelte animates with JavaScript frame by frame'. It generates CSS keyframes from your JS function — per-frame JS animation only happens when you use the tick() hook. Another trap: ignoring u — easing functions that need the complement (e.g. elastic) rely on u; using only t limits the expressiveness. Candidates who explain the keyframe generation and the t/u semantics show real engine-level understanding.