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.
--duration-fast
--duration-normal
--duration-slow
--duration-slower
When to use each
| Token | Value | Use for |
|---|---|---|
fast | 150ms | Button hovers, tooltip show/hide, focus rings, color changes |
normal | 200ms | Card hovers, scale transforms, sidebar highlights, toggle switches |
slow | 300ms | Panel expand/collapse, filter panels, dialog entry, page cross-fades |
slower | 500ms | Full-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.
cubic-bezier(0.4, 0, 0.2, 1)
cubic-bezier(0, 0, 0.2, 1)
cubic-bezier(0.4, 0, 1, 1)
cubic-bezier(0.34, 1.56, 0.64, 1)
Easing Selection
| Curve | Use for |
|---|---|
| Default | Most transitions — balanced feel for general UI changes |
| Ease Out | Elements entering the viewport — fast start, gentle landing (fly-in, scale-in) |
| Ease In | Elements leaving the viewport — slow start, fast exit (never use for entrances) |
| Spring | Playful interactions — badge bounce, radial menu pop, sort confirmation |
Hover Effects
Hover feedback is immediate and subtle. Each element type has a prescribed hover behavior. Try hovering the boxes below.
translateY(-2px)
scale(1.05)
scale(1.02)
box-shadow ring
bg opacity
| Element | Hover Effect | Duration | Tailwind |
|---|---|---|---|
| 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 Type | Styling | Usage |
|---|---|---|
| Image placeholder | animate-pulse bg-muted rounded-xl | Gallery cells, catalog covers while loading |
| Text line | animate-pulse bg-muted h-3 rounded | Titles, descriptions, metadata |
| Avatar circle | animate-pulse bg-muted rounded-full | User avatars, model icons |
Spinners
| Context | Indicator | Placement |
|---|---|---|
| Page loading | Skeleton grid | Replace content cards with skeleton equivalents |
| Generate button | Spinner (sm) + disabled | Inside button, replacing icon, pulse opacity on text |
| Image saving | Spinner (xs) overlay | Centered over the image tile |
| Catalog fetch | Spinner (md) | Centered in the scroll area |
| Startup | Spinner (lg) + status text | Centered 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
| Preset | Svelte Directive | Parameters | Use 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:
- Current image fades + scales out (200ms, ease-in)
- Brief pause (50ms) for visual separation
- Next image flies + scales in from below (300ms, ease-out)
- 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
| Property | Value |
|---|---|
| Gradient | linear-gradient(120°, blue, pink, green, blue) |
| Background-size | 300% 300% |
| Animation | gradient-shift 8s ease infinite |
| Hover | scale(1.02) + box-shadow glow |
| Active / Generating | Spinner replaces icon, text pulses opacity |
2. Aura Ring
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.
| Property | Value |
|---|---|
| Container | fixed z-50 at cursor position |
| Items | 7 actions: rerun, remove BG, upscale, delete, save, share, favorite |
| Item size | w-10 h-10 rounded-full bg-card/80 backdrop-blur-md |
| Expand radius | 80px from center |
| Stagger | Items appear with 20ms stagger delay |
| Item hover | scale(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.
| Animation | Standard | Reduced 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 |
@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.