Motion

A complete reference for Fractalsvelte's built-in animation, interaction transitions, timing, and reduced-motion behaviour.

Fractalsvelte uses motion to make a change of state legible: a surface opens, a panel leaves, content expands, or a control acknowledges a press. This page documents the motion that exists in the package today—by component, variant, trigger, and lifecycle phase.

There are two kinds of motion in the library:

  • Motion-runtime animation uses the package’s motion integration. It can keep content mounted during its exit animation and follows MotionProvider’s reduced-motion policy.
  • CSS interaction transition is a short browser transition for a visual property such as a hover background, an accordion indicator, or a spinner. It does not provide enter/exit lifecycle handling.

Global rules

Timing vocabulary

The public timing presets are available from fractalsvelte/motion. Durations are in seconds where stated; spring presets settle according to their spring configuration rather than a fixed duration.

NameValuesUsed by built-in components
instant0msAvailable for custom motion; not selected by a built-in component.
fast140ms, easeOutMenu/popover/collapsible entry and exit; dialog/sheet/drawer exit.
standard200ms, easeOutToast entry and exit; default transition for the public <Motion> component.
emphasized320ms, cubic-bezier (0.2, 0.8, 0.2, 1)Available for custom motion.
gentlespring: stiffness 280, damping 28Dialog, alert-dialog, sheet, and drawer entry.
snappyspring: stiffness 420, damping 32Button, Toggle, and Toggle Group press acknowledgement.

Reduced motion

The default policy is reducedMotion="user": Fractalsvelte reads prefers-reduced-motion and, when it is enabled, removes transform-based movement from its Motion-runtime animations. Opacity still changes. Runtime animations are also capped at 140ms where a duration applies.

Provider settingResult
"user" (default)Remove transform motion only when the operating system requests reduced motion.
"always"Always remove transform motion in that subtree.
"never"Keep transform motion even when the operating system requests reduced motion. Use only for controlled previews/tests.

The CSS transitions on Button, Toggle, Toggle Group, and Accordion are disabled by their own prefers-reduced-motion media queries. Spinner remains animated, but slows from 640ms per rotation to 2000ms per rotation.

Svelte
<script lang="ts">
	import { MotionProvider } from 'fractalsvelte/motion';
</script>

<MotionProvider reducedMotion="user">
	<Workspace />
</MotionProvider>

MotionProvider’s transition prop is currently the default for the public <Motion> component. Built-in component timings are intentionally fixed to the table above; MotionProvider currently affects their reduced-motion policy, not their timing preset.

Motion-runtime component reference

Dialog and Alert Dialog

Both the concise components (<Dialog> / <AlertDialog>) and their compound Content implementations use the same panel motion. default, destructive, and any content variants do not alter it.

Phase / triggerAnimated elementNormal motionReduced motion
Open via trigger or controlled open stateNative <dialog> panelOpacity 0 → 1; scale 0.96 → 1; gentle springOpacity 0 → 1; no scale transform.
Close button, cancel action, Escape, or controlled open = falseNative <dialog> panelOpacity 1 → 0; scale 1 → 0.98; fast (140ms)Opacity 1 → 0; no scale transform.
BackdropNative dialog backdropNo animated keyframes; it appears/disappears with the dialog.Same.

Closing waits for the panel exit animation before calling dialog.close(). The backdrop is deliberately static; only the panel has runtime animation.

Sheet and Drawer

Sheet and Drawer share identical motion behaviour. The side prop is the variant that changes their direction. This applies to both concise and compound APIs.

sideOpening movementClosing movement
leftOpacity 0 → 1, x -32px → 0Opacity 1 → 0, x 0 → -32px
right (default)Opacity 0 → 1, x 32px → 0Opacity 1 → 0, x 0 → 32px
topOpacity 0 → 1, y -32px → 0Opacity 1 → 0, y 0 → -32px
bottomOpacity 0 → 1, y 32px → 0Opacity 1 → 0, y 0 → 32px

Opening uses the gentle spring. Closing uses fast (140ms, easeOut) and completes before the native dialog closes. With reduced motion, all x/y travel is removed while the opacity transition remains. The backdrop has no independent animation.

Dropdown Menu, Popover, Menubar, and Navigation Menu

Every open content surface below uses the menu preset. Alignment choices such as align="start" and align="end" only change positioning; they do not change the animation.

ComponentOpen conditionOpen motionClose motionExit handling
Dropdown Menu (concise and compound)Trigger toggles open; compound content also closes on Escape or item selectionOpacity 0 → 1, y -4px → 0, scale 0.98 → 1; fastOpacity 1 → 0, y 0 → -4px, scale 1 → 0.98; fastContent remains mounted until the animation ends, then unmounts.
PopoverTrigger toggles open; Close button sets it falseSame menu presetSame menu presetSame mounted-through-exit behaviour.
Menubar ContentThe parent Menubar’s active item matches the content itemSame menu presetSame menu presetSame mounted-through-exit behaviour.
Navigation Menu ContentThe parent Navigation Menu’s active item matches the content itemSame menu presetSame menu presetSame mounted-through-exit behaviour.

While one of these surfaces is playing its exit animation it has aria-hidden="true" and inert, so it cannot receive keyboard focus or pointer interaction. With reduced motion, only opacity changes; the y and scale transforms are removed.

Collapsible

Phase / triggerNormal motionReduced motion
Open via its trigger or bound open = trueContent opacity 0 → 1, y 12px → 0; fastOpacity 0 → 1; no y transform.
Close via its trigger or bound open = falseContent opacity 1 → 0, y 0 → 8px; fastOpacity 1 → 0; no y transform.

The content stays mounted until its exit finishes and is aria-hidden/inert as soon as it is closed. This is opacity-and-position continuity only: the component does not animate its height.

Toast

Toast uses the toast preset for both manual dismissal and an auto-close caused by its duration prop. The neutral and accent tone variants change colour only, not motion.

PhaseNormal motionReduced motion
Initial openOpacity 0 → 1, y 12px → 0, scale 0.98 → 1; standard (200ms)Opacity 0 → 1; no y or scale transform.
Dismiss button, auto-close, or bound open = falseOpacity 1 → 0, y 0 → 8px, scale 1 → 0.98; standardOpacity 1 → 0; no y or scale transform.

The toast remains mounted for its exit and becomes aria-hidden and inert as soon as closing starts.

Button, Toggle, and Toggle Group

These controls acknowledge an activation rather than animating their selected state over time. Every size and visual variant uses the same press animation. Disabled controls do not animate.

ComponentTriggerRuntime motionCSS interaction transitionReduced motion
ButtonClick on any button or link variantScale 1 → 0.97 → 1; snappy spring120ms transitions for background, border colour, colour, and transform; active state translates y by 1px.Runtime scale is removed; CSS transitions and active translate are disabled.
ToggleClick, while pressed changesScale 1 → 0.96 → 1; snappy spring120ms background and border-colour transition.Runtime scale and CSS transition are removed.
Toggle GroupClick an enabled item, while its selection changesScale 1 → 0.96 → 1; snappy spring on that item120ms background transition.Runtime scale and CSS transition are removed.

Keyboard activation uses the browser’s normal button activation semantics. These components do not currently animate on focus, hover, or a programmatic value change that occurs without clicking an item.

CSS-only animation and transitions

These are part of the component experience, but do not use the public Motion runtime or lifecycle presence primitive.

ComponentStateBehaviourReduced motion
AccordionHovering a triggerTrigger background transitions over 120ms.Disabled.
AccordionOpening/closing an itemContent opacity 0 → 1 with y 12px → 0 on open and opacity 1 → 0 with y 0 → 8px on close (fast); the + indicator rotates to 45deg over 160ms.Content opacity changes without y movement; indicator transition disabled.
SpinnerLoadingContinuous 360° rotation every 640ms. All sizes use the same rotation; size only changes dimensions.Rotation continues, slowed to 2000ms per turn.

Stateful collections and layout blocks

Tabs use a persistent FLIP selection indicator and a wait-sequenced panel exit/entry. Tree View uses mounted-through-exit branch presence plus a moving selection surface. Toggle Group uses a moving selection surface in single mode; multiple selection keeps each item’s direct state feedback. Carousel preserves the outgoing slide through its directional exit, then enters the incoming slide from the corresponding direction; horizontal and vertical pointer swipes use both distance and velocity thresholds.

Combobox result rows enter with a short stagger, while filtered rows leave through normal DOM filtering. Command, Select, Resizable, Hero, Navigation Sidebar, Page Header, and Split Layout retain short CSS state transitions where applicable; their state and accessibility do not depend on motion. Grid and App Shell animate layout changes under the same reduced-motion policy. Context Menu and Hover Card use the menu presence lifecycle described above.

Using the public Motion API

Use <Motion> to animate an element you own. It runs the supplied animate keyframes once when it mounts and again when the animate value changes. initial supplies the first starting value. It does not currently include an exit prop, layout animation, drag, variants, or an AnimatePresence-style public component.

Svelte
<script lang="ts">
	import { Motion, presets, transitions } from 'fractalsvelte/motion';
</script>

<Motion
	as="section"
	initial={presets.fadeUp.initial}
	animate={presets.fadeUp.animate}
	transition={transitions.standard}
>
	Your content
</Motion>

The public preset keyframes are:

PresetInitialAnimatedExit (for a presence implementation)
fadeopacity 0opacity 1opacity 0
fadeUpopacity 0, y 12pxopacity 1, y 0opacity 0, y 8px
scaleopacity 0, scale 0.96opacity 1, scale 1opacity 0, scale 0.98
menuopacity 0, y -4px, scale 0.98opacity 1, y 0, scale 1opacity 0, y -4px, scale 0.98
toastopacity 0, y 12px, scale 0.98opacity 1, y 0, scale 1opacity 0, y 8px, scale 0.98

Accessibility checklist

  • Keep motion as confirmation or orientation, never as the only indicator of a new state.
  • Leave reducedMotion="user" enabled for application UI.
  • Test opening, closing, escaping, and rapid state changes with prefers-reduced-motion: reduce enabled.
  • Do not add a transform-only custom animation without ensuring that opacity, semantics, focus, and state remain understandable when that transform is removed.