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
motionintegration. It can keep content mounted during its exit animation and followsMotionProvider’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.
| Name | Values | Used by built-in components |
|---|---|---|
instant | 0ms | Available for custom motion; not selected by a built-in component. |
fast | 140ms, easeOut | Menu/popover/collapsible entry and exit; dialog/sheet/drawer exit. |
standard | 200ms, easeOut | Toast entry and exit; default transition for the public <Motion> component. |
emphasized | 320ms, cubic-bezier (0.2, 0.8, 0.2, 1) | Available for custom motion. |
gentle | spring: stiffness 280, damping 28 | Dialog, alert-dialog, sheet, and drawer entry. |
snappy | spring: stiffness 420, damping 32 | Button, 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 setting | Result |
|---|---|
"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.
<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 / trigger | Animated element | Normal motion | Reduced motion |
|---|---|---|---|
Open via trigger or controlled open state | Native <dialog> panel | Opacity 0 → 1; scale 0.96 → 1; gentle spring | Opacity 0 → 1; no scale transform. |
Close button, cancel action, Escape, or controlled open = false | Native <dialog> panel | Opacity 1 → 0; scale 1 → 0.98; fast (140ms) | Opacity 1 → 0; no scale transform. |
| Backdrop | Native dialog backdrop | No 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.
side | Opening movement | Closing movement |
|---|---|---|
left | Opacity 0 → 1, x -32px → 0 | Opacity 1 → 0, x 0 → -32px |
right (default) | Opacity 0 → 1, x 32px → 0 | Opacity 1 → 0, x 0 → 32px |
top | Opacity 0 → 1, y -32px → 0 | Opacity 1 → 0, y 0 → -32px |
bottom | Opacity 0 → 1, y 32px → 0 | Opacity 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.
| Component | Open condition | Open motion | Close motion | Exit handling |
|---|---|---|---|---|
| Dropdown Menu (concise and compound) | Trigger toggles open; compound content also closes on Escape or item selection | Opacity 0 → 1, y -4px → 0, scale 0.98 → 1; fast | Opacity 1 → 0, y 0 → -4px, scale 1 → 0.98; fast | Content remains mounted until the animation ends, then unmounts. |
| Popover | Trigger toggles open; Close button sets it false | Same menu preset | Same menu preset | Same mounted-through-exit behaviour. |
| Menubar Content | The parent Menubar’s active item matches the content item | Same menu preset | Same menu preset | Same mounted-through-exit behaviour. |
| Navigation Menu Content | The parent Navigation Menu’s active item matches the content item | Same menu preset | Same menu preset | Same 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 / trigger | Normal motion | Reduced motion |
|---|---|---|
Open via its trigger or bound open = true | Content opacity 0 → 1, y 12px → 0; fast | Opacity 0 → 1; no y transform. |
Close via its trigger or bound open = false | Content opacity 1 → 0, y 0 → 8px; fast | Opacity 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.
| Phase | Normal motion | Reduced motion |
|---|---|---|
| Initial open | Opacity 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 = false | Opacity 1 → 0, y 0 → 8px, scale 1 → 0.98; standard | Opacity 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.
| Component | Trigger | Runtime motion | CSS interaction transition | Reduced motion |
|---|---|---|---|---|
| Button | Click on any button or link variant | Scale 1 → 0.97 → 1; snappy spring | 120ms 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. |
| Toggle | Click, while pressed changes | Scale 1 → 0.96 → 1; snappy spring | 120ms background and border-colour transition. | Runtime scale and CSS transition are removed. |
| Toggle Group | Click an enabled item, while its selection changes | Scale 1 → 0.96 → 1; snappy spring on that item | 120ms 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.
| Component | State | Behaviour | Reduced motion |
|---|---|---|---|
| Accordion | Hovering a trigger | Trigger background transitions over 120ms. | Disabled. |
| Accordion | Opening/closing an item | Content 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. |
| Spinner | Loading | Continuous 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.
<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:
| Preset | Initial | Animated | Exit (for a presence implementation) |
|---|---|---|---|
fade | opacity 0 | opacity 1 | opacity 0 |
fadeUp | opacity 0, y 12px | opacity 1, y 0 | opacity 0, y 8px |
scale | opacity 0, scale 0.96 | opacity 1, scale 1 | opacity 0, scale 0.98 |
menu | opacity 0, y -4px, scale 0.98 | opacity 1, y 0, scale 1 | opacity 0, y -4px, scale 0.98 |
toast | opacity 0, y 12px, scale 0.98 | opacity 1, y 0, scale 1 | opacity 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.
