Files
app/frontend/src/lib/motion.ts
T
Melvin Strobl 037df4c75f Give the pulse its own duration, and let the editor fill the screen
The shared duration tokens had been stretched to slow the pulses down, which
also slowed every panel and dock animation and left `motion.ts` out of step
with the CSS. Both are back to 200/300 ms, and the pulses use a new
`--duration-pulse` (500 ms): a message arriving is a signal, not a state
change, and it is the only thing here that should linger. The edge pulse read
its own 300 ms constant, so it now takes the token through `motion.ts` and the
two sides cannot drift again.

The code editor also expands: the same node panel fills the content region
beside the sidebar, the canvas chrome steps aside rather than floating on top,
and the settings above the editor keep a readable width.
2026-08-15 20:03:21 +02:00

77 lines
2.8 KiB
TypeScript

/**
* Shared motion presets (Fluksio design system).
*
* Pure data: variants and transitions consumed by `motion/react` components
* across the app. Mirrors the `--ease-*` / `--duration-*` timing tokens defined
* in `index.css`, so CSS transitions and JS animations stay in sync.
*
* This file is duplicated verbatim in `app` and `index` (the shadcn model,
* see ../../DESIGN.md / the root DESIGN-GUIDELINES.md). Keep the two copies
* identical; `make design-check` at the workspace root verifies it.
*
* Reduced motion: wrap each app root in `<MotionConfig reducedMotion="user">` so
* transform/layout animations are disabled automatically when the OS requests it.
*/
import type { Transition, Variants } from "motion/react"
/** Easing curves, mirroring the `--ease-*` tokens. */
export const easeEmphasized: [number, number, number, number] = [0.2, 0, 0, 1]
export const easeStandard: [number, number, number, number] = [0.4, 0, 0.2, 1]
/** Durations in seconds, mirroring the `--duration-*` tokens (ms). */
export const duration = {
fast: 0.15,
base: 0.2,
slow: 0.3,
/** One-shot feedback, not a state change. See `--duration-pulse`. */
pulse: 0.5,
} as const
export const transitions = {
/** Enter / expressive moves (decelerate). */
emphasized: { duration: duration.base, ease: easeEmphasized },
/** Neutral state changes. */
standard: { duration: duration.base, ease: easeStandard },
/** Springy, for hover tilt / direct-manipulation feedback. */
spring: { type: "spring", stiffness: 250, damping: 20, mass: 0.5 },
} satisfies Record<string, Transition>
const exitFast: Transition = { duration: duration.fast, ease: easeStandard }
/** Opacity fade. */
export const fadeIn: Variants = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: transitions.standard },
exit: { opacity: 0, transition: exitFast },
}
/** Fade + subtle scale: popovers, dialogs, floating panels. */
export const scaleIn: Variants = {
hidden: { opacity: 0, scale: 0.96 },
visible: { opacity: 1, scale: 1, transition: transitions.emphasized },
exit: { opacity: 0, scale: 0.96, transition: exitFast },
}
/** Fade + rise: list/grid items, toolbars, content blocks. */
export const slideUp: Variants = {
hidden: { opacity: 0, y: 8 },
visible: { opacity: 1, y: 0, transition: transitions.emphasized },
exit: { opacity: 0, y: 8, transition: exitFast },
}
/** Dialog / drawer scrim. */
export const overlayFade: Variants = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: exitFast },
exit: { opacity: 0, transition: exitFast },
}
/**
* Stagger container for lists/grids. Children animate with `slideUp` / `fadeIn`
* via `variants` inheritance (set the same `initial`/`animate` state names).
*/
export const listStagger: Variants = {
hidden: {},
visible: { transition: { staggerChildren: 0.05, delayChildren: 0.02 } },
}