From harness-claude
Adds enter/exit animations, list reordering motion with flip, and spring/tweened store transitions to Svelte elements using built-in and custom functions.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Add enter/exit animations, list reordering motion, and spring physics to Svelte elements using built-in and custom transitions
Implements native React View Transition API for smooth animations: page transitions, shared elements, route changes, Suspense reveals, list reorders, and directional navigation in Next.js.
Adds tiered animations (micro/meso/macro) to Stitch-generated components via CSS transitions, Framer Motion (React/Next.js), or Svelte transitions. Framework-aware and prefers-reduced-motion safe.
Guides Svelte and SvelteKit development for reactive components, stores, transitions, lifecycle hooks, SSR, file-based routing, and Vite deployment.
Share bugs, ideas, or general feedback.
Add enter/exit animations, list reordering motion, and spring physics to Svelte elements using built-in and custom transitions
{#each} blockBuilt-in transitions:
svelte/transition. They run when the element is conditionally rendered:<script>
import { fade, fly, slide, scale, blur } from 'svelte/transition'
let show = $state(true)
</script>
<button onclick={() => show = !show}>Toggle</button>
{#if show}
<div transition:fade>Fades in and out</div>
<div transition:fly={{ y: 20, duration: 300 }}>Flies from below</div>
<div transition:slide>Slides open/closed</div>
{/if}
in: and out: directives to apply different transitions for enter and exit:{#if show}
<div in:fly={{ x: -100 }} out:fade>
Flies in from left, fades out
</div>
{/if}
<div transition:fly={{ x: 0, y: 50, duration: 400, easing: quintOut }}>
Parameterized fly
</div>
Import easing functions from svelte/easing: linear, cubicIn, cubicOut, cubicInOut, quintOut, elasticOut, backOut, etc.
Custom transitions:
// CSS-based custom transition (most performant)
function swoosh(node: Element, { duration = 300 } = {}) {
return {
duration,
css: (t: number) => `
transform: translateX(${(1 - t) * 100}%) rotate(${(1 - t) * 45}deg);
opacity: ${t};
`,
};
}
// JS-based tick function (for canvas or non-CSS animations)
function typewriter(node: Element, { speed = 40 } = {}) {
const text = node.textContent ?? '';
return {
duration: text.length * speed,
tick: (t: number) => {
node.textContent = text.slice(0, Math.floor(t * text.length));
},
};
}
Animating list items with animate:flip:
animate:flip to smoothly reorder elements in {#each} blocks when items move:<script>
import { flip } from 'svelte/animate'
import { fade } from 'svelte/transition'
let items = $state(['A', 'B', 'C', 'D'])
function shuffle() {
items = [...items].sort(() => Math.random() - 0.5)
}
</script>
<button onclick={shuffle}>Shuffle</button>
{#each items as item (item)}
<div animate:flip={{ duration: 300 }} transition:fade>
{item}
</div>
{/each}
Note: (item) — the key expression — is required for animate:flip to track element identity.
Tweened and spring stores for smooth value transitions:
tweened for smooth numeric value interpolation (progress bars, counters):<script>
import { tweened } from 'svelte/motion'
import { cubicOut } from 'svelte/easing'
const progress = tweened(0, { duration: 500, easing: cubicOut })
function complete() { progress.set(1) }
</script>
<progress value={$progress} />
<button onclick={complete}>Complete</button>
spring for physics-based motion that overshoots and settles:<script>
import { spring } from 'svelte/motion'
const pos = spring({ x: 0, y: 0 }, { stiffness: 0.1, damping: 0.25 })
</script>
<div
style="transform: translate({$pos.x}px, {$pos.y}px)"
onmousemove={(e) => pos.set({ x: e.clientX, y: e.clientY })}
/>
Deferred transitions (crossfade):
crossfade to animate an element from one position to another across the DOM (shared element transition):<script>
import { crossfade } from 'svelte/transition'
const [send, receive] = crossfade({ duration: 400 })
</script>
{#if inList}
<div in:receive={{ key: item.id }} out:send={{ key: item.id }}>
{item.name}
</div>
{:else}
<div in:receive={{ key: item.id }} out:send={{ key: item.id }}>
{item.name}
</div>
{/if}
How transitions work:
Svelte compiles transition:, in:, and out: directives into JavaScript that runs when the element is added to or removed from the DOM. CSS-based transitions run entirely on the compositor thread and do not block the main thread.
global modifier:
By default, transitions only run when the direct parent {#if} changes. Add :global to run the transition when any ancestor condition changes:
{#if outerCondition}
{#if innerCondition}
<div transition:fade|global>
Fades when either condition changes
</div>
{/if}
{/if}
Transition events:
Listen to transition lifecycle events:
<div
transition:fly
onintrostart={() => console.log('entering')}
onintroend={() => console.log('entered')}
onoutrostart={() => console.log('leaving')}
onoutroend={() => console.log('left')}
/>
Performance considerations:
css) over JS tick functions — CSS runs off the main threadwidth/height — use transform: scaleX() and transform: scaleY() insteadslide animates height and may cause layout thrash on complex elements; prefer scale or custom transitions for performance-sensitive caseshttps://svelte.dev/docs/svelte/transition