Timing

Four duration tokens cover all transitions. Shorter for small, local changes; longer for large, spatial transformations. Hover each bar to see it in action.

Fast — 150ms --duration-fast
Normal — 200ms --duration-normal
Slow — 300ms --duration-slow
Slower — 500ms --duration-slower

When to use each

TokenValueUse for
fast150msButton hovers, tooltip show/hide, focus rings, color changes
normal200msCard hovers, scale transforms, sidebar highlights, toggle switches
slow300msPanel expand/collapse, filter panels, dialog entry, page cross-fades
slower500msFull-page transitions, image zoom, complex layout shifts

Easing Curves

The right easing brings life. Four curves cover our motion vocabulary. Hover each track to see the ball move with that easing.

Default (ease-in-out) cubic-bezier(0.4, 0, 0.2, 1)
Ease Out (decelerate) cubic-bezier(0, 0, 0.2, 1)
Ease In (accelerate) cubic-bezier(0.4, 0, 1, 1)
Spring (overshoot) cubic-bezier(0.34, 1.56, 0.64, 1)

Easing Selection

CurveUse for
DefaultMost transitions — balanced feel for general UI changes
Ease OutElements entering the viewport — fast start, gentle landing (fly-in, scale-in)
Ease InElements leaving the viewport — slow start, fast exit (never use for entrances)
SpringPlayful interactions — badge bounce, radial menu pop, sort confirmation
Rule of thumb: Use ease-out for entrances, ease-in for exits, default for state changes, and spring sparingly for delightful moments.

Hover Effects

Hover feedback is immediate and subtle. Each element type has a prescribed hover behavior. Try hovering the boxes below.

Lift
translateY(-2px)
Scale
scale(1.05)
Scale Subtle
scale(1.02)
Glow
box-shadow ring
Background
bg opacity
ElementHover EffectDurationTailwind
Image tile Scale up Normal (200ms) hover:scale-105 transition-transform
Glass card Lift + border glow Normal (200ms) hover:-translate-y-0.5 hover:border-border/80
Catalog card Scale subtle Normal (200ms) hover:scale-[1.02] transition-transform
Sidebar item Background tint Fast (150ms) hover:bg-sidebar-accent/50
Button (default) Opacity shift Fast (150ms) hover:bg-primary/80
Ghost button Background appear Fast (150ms) hover:bg-accent hover:text-accent-foreground
Focus ring Ring expand Fast (150ms) focus-visible:ring-2 ring-ring ring-offset-2
Link text Color shift Fast (150ms) hover:text-primary transition-colors
Window controls Background appear Fast (150ms) rgba(255,255,255,0.1), close → red

Loading States

Loading indicators reassure users that work is happening. We use skeletons for content placeholders and spinners for discrete actions.

Skeleton Placeholders

Skeleton TypeStylingUsage
Image placeholderanimate-pulse bg-muted rounded-xlGallery cells, catalog covers while loading
Text lineanimate-pulse bg-muted h-3 roundedTitles, descriptions, metadata
Avatar circleanimate-pulse bg-muted rounded-fullUser avatars, model icons

Spinners

xs (14px)
sm (20px)
md (28px)
lg (40px)
ContextIndicatorPlacement
Page loadingSkeleton gridReplace content cards with skeleton equivalents
Generate buttonSpinner (sm) + disabledInside button, replacing icon, pulse opacity on text
Image savingSpinner (xs) overlayCentered over the image tile
Catalog fetchSpinner (md)Centered in the scroll area
StartupSpinner (lg) + status textCentered full screen below logo

Page & Content Transitions

Svelte's built-in transition directives power content animations. We use three transition patterns depending on context.

Transition Presets

PresetSvelte DirectiveParametersUse for
Fade transition:fade duration: 200 Toast appear/dismiss, dialog overlay, simple content swap
Fly Up transition:fly y: 20, duration: 300 QuickSort image entrance, fresh content loading
Scale transition:scale start: 0.95, duration: 200 Dialog content panel, popup menus, popovers
Fly + Fade in:fly out:fade y: 30 / duration: 200 QuickSort: new image flies in, old fades out
Slide transition:slide duration: 300 Filter panel expand, collapsible sections

Dialog Transitions

Dialogs use a two-part transition: the overlay fades in (fade, duration: 200) while the content panel scales in (scale, start: 0.95, duration: 200). Exit is the reverse. This creates a pleasing "zoom from center" effect that draws focus.

QuickSort Transitions

QuickSort uses the most complex transition sequence. When sorting an image:

  1. Current image fades + scales out (200ms, ease-in)
  2. Brief pause (50ms) for visual separation
  3. Next image flies + scales in from below (300ms, ease-out)
  4. Action button bounces to confirm sort choice (spring easing)

Signature Effects

A few animations define the app's personality. These are the moments of delight that make VirtualLife feel alive.

1. Generate Button Gradient Shift

PropertyValue
Gradientlinear-gradient(120°, blue, pink, green, blue)
Background-size300% 300%
Animationgradient-shift 8s ease infinite
Hoverscale(1.02) + box-shadow glow
Active / GeneratingSpinner replaces icon, text pulses opacity

2. Aura Ring

A subtle pulsing ring that appears around key status indicators. Used sparingly — only for the "ready to generate" state and startup loading. The ring scales between 1.0 and 1.06 with opacity oscillation over 2s.

3. Radial Action Menu

On right-click over an image tile, a circular context menu expands from the cursor position. Actions fan out radially over 200ms with spring easing. Each item is a small circle (w-10 h-10 rounded-full) with an icon. The menu collapses on selection or blur with 150ms ease-in.

PropertyValue
Containerfixed z-50 at cursor position
Items7 actions: rerun, remove BG, upscale, delete, save, share, favorite
Item sizew-10 h-10 rounded-full bg-card/80 backdrop-blur-md
Expand radius80px from center
StaggerItems appear with 20ms stagger delay
Item hoverscale(1.1) + label tooltip

4. Animated Gradient Title

The app title in the title bar uses the same 120° gradient as the Generate button, animated at 20s ease infinite. This creates a slow, hypnotic color shift that reinforces the Life/Virtual duality. The slower speed (20s vs 8s for the button) keeps it ambient rather than attention-seeking.

5. Image Tile Overlay Cascade

When hovering an image tile in the gallery, an overlay appears with a gradient from bottom (from-transparent via-black/40 to-black/80) over 200ms. Action buttons fade in with a 50ms stagger delay, and metadata text slides up subtly (translateY(4px) → 0).

Reduced Motion

Respect the user's motion preferences. When prefers-reduced-motion: reduce is set, all animations must degrade gracefully.

AnimationStandardReduced Motion
Hover transforms scale / translateY / rotate Opacity change only
Page transitions fly / scale (300ms) Instant fade (100ms)
Gradient animation 8s / 20s infinite loop Static gradient (first position)
Spinner Continuous rotation Pulsing opacity (no rotation)
Aura ring Scale + opacity pulse Static ring, no animation
Skeleton pulse Opacity animation Static muted background
Implementation: Wrap all keyframe and transition declarations in @media (prefers-reduced-motion: no-preference) { ... }. In Svelte, use reducedMotion: 'reduce' parameter on transitions, or conditionally set durations to 0 based on a global reduced-motion store.