Motion
Quick to respond, soft to settle. Motion explains what changed and never makes the reader wait.
Principles
Respond at once
Pressed controls react within 100 milliseconds. Feedback never waits for a network round trip.
Settle softly
Entrances start quickly and ease into place. Exits are shorter than entrances and accelerate away.
Physical when touched
Springs move what the reader drags, toggles, or opens. Color and opacity never overshoot.
Quiet at rest
Looping motion means an AI is working. Idle interfaces hold still so real activity stands out.
Easing
Select a curve to play it. Springs are sampled from a physical model into CSS linear() curves, and each has a paired duration equal to its settling time.
Duration
Smaller movements are faster. Exits use the next shorter step than their entrance.
--duration-instant Press feedback
--duration-fast Hover, color, and exits
--duration-base State changes and popovers
--duration-slow Sheets, dialogs, and layout
--duration-deliberate Reveals and first paint
--duration-ambient Shimmer and breathing loops
Animations
Shared keyframes for entrances and ambient loops.
Fade in
animate-fade-inRise in
animate-rise-inBlur in
animate-blur-inScale in
animate-scale-inBreathe
animate-breatheFloat
animate-floatIn CSS
The theme overrides Tailwind's ease-out, ease-in, and ease-in-out, and adds ease-spring variants, so every component shares the
same curves.
<!-- Utilities read the theme tokens -->
<div class="transition-transform duration-(--duration-base) ease-out hover:-translate-y-0.5">
<div class="transition-[scale] duration-(--duration-spring) ease-spring data-[state=open]:scale-100">
<!-- Entrances, staggered by index -->
{#each items as item, index (item.id)}
<li class="animate-rise-in stagger" style="--index: {index}">{item.title}</li>
{/each}
<!-- Working state -->
<span class="text-shimmer animate-shimmer">Searching the web</span> In Svelte
Install the Motion item for transitions, scroll reveals, pointer effects, and JavaScript copies of every token.
<script lang="ts">
import { fade } from 'svelte/transition';
import { blurIn, pop, reveal, magnetic, duration, stagger } from '$lib/components/ui/motion';
</script>
<!-- Svelte transitions that fall back to a crossfade for reduced motion -->
{#if answer}
<p in:blurIn>{answer}</p>
{/if}
{#if open}
<div in:pop out:fade={{ duration: duration.fast }}>...</div>
{/if}
<!-- Reveal children in sequence the first time they scroll into view -->
<ul {@attach reveal({ children: true, stagger })}>...</ul>
<!-- Lean toward a fine pointer -->
<button {@attach magnetic({ strength: 0.3 })}>Generate</button> Reduced motion
When a reader turns on reduced motion, the theme finishes every CSS animation and transition immediately. Svelte transitions from the Motion item become a 160 millisecond crossfade, so a change is still visible without movement.
Scroll reveals never hide content, pointer effects switch off, and ambient loops such as the voice orb stop drawing frames. Streaming text shows the complete answer at once.