Morphos & Kosmesis are here.Explore
PraxisJS

Motion

@praxisjs/motion — signal-driven animations via @Tween and @Spring field decorators. Assigning a value triggers a smooth animated transition.

Motion

Animated field decorators. Assign a new value and the transition plays automatically — no imperative animation API, no requestAnimationFrame loops, no timeline management.

npm install @praxisjs/motion
pnpm add @praxisjs/motion
yarn add @praxisjs/motion
bun add @praxisjs/motion

@Tween(options?)

Animates a numeric field from its current value to a new one using a duration-based easing curve. Reading the field returns the current interpolated value. A field starts at rest — creating the component schedules no animation frame — and a running animation is stopped when the component unmounts.

import { Tween } from '@praxisjs/motion'

@Component()
class ProgressBar extends StatefulComponent {
  @Tween({ duration: 600, easing: 'easeOut' })
  progress = 0

  load() {
    this.progress = 100  // smoothly animates from current value to 100
  }

  render() {
    return (
      <div
        class="bar"
        style={() => ({ width: `${this.progress}%` })}
      />
    )
  }
}

Assigning a new value starts a new animation from wherever the field currently is — even if a previous animation is still in progress.

OptionTypeDefaultDescription
durationnumber300Animation duration in ms
easingEasing'easeOut'Easing function name or custom (t: number) => number
delaynumber0Delay before the animation starts in ms

Available easings

'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'easeInCubic' | 'bounce' | 'elastic'

Custom easing — any function from [0, 1] to [0, 1]:

@Tween({ easing: (t) => t * t * t, duration: 400 })
scale = 1
Storybook
Live demo — @Tween

@Spring(options?)

Physics-based spring animation. The field settles toward its target value with natural momentum and configurable bounce. Assign a value and let the physics do the work.

import { Spring } from '@praxisjs/motion'

@Component()
class DragHandle extends StatefulComponent {
  @Spring({ stiffness: 0.2, damping: 0.7 })
  x = 0

  @Spring()
  y = 0

  onPointerMove(e: PointerEvent) {
    this.x = e.clientX
    this.y = e.clientY
  }

  render() {
    return (
      <div
        class="handle"
        style={() => ({ transform: `translate(${this.x}px, ${this.y}px)` })}
        onPointermove={(e) => this.onPointerMove(e)}
      />
    )
  }
}
OptionTypeDefaultDescription
stiffnessnumber0.15Spring strength — higher = snappier. Must be > 0.
dampingnumber0.8Resistance — lower = more bounce and overshoot
massnumber1Object mass — higher = more inertia
precisionnumber0.001Settlement threshold — how close to the target before animation stops

Validation

stiffness must be greater than 0. Passing 0 or a negative value throws immediately.

Storybook
Live demo — @Spring

createTransition(options?)

Creates CSS-class transitions for elements entering and leaving the DOM. enter(el) and leave(el) return a Promise that resolves when the transition is over. Pass the result to the transition prop of For and Index to animate added and removed rows: a removed row stays in place until leave resolves.

import { createTransition } from '@praxisjs/motion'
import { For } from '@praxisjs/runtime'

const fade = createTransition({ name: 'fade', duration: 200 })

<For each={() => this.toasts} by="id" transition={fade}>
  {(toast) => <div class="toast">{() => toast().message}</div>}
</For>
.toast { transition: opacity .2s ease, transform .2s ease; }
.fade-enter-from, .fade-leave-to { opacity: 0; transform: translateY(-4px); }

On enter, ${name}-enter-from is added, swapped for ${name}-enter-to on the next frame, and removed after duration. leave does the same with ${name}-leave-from and ${name}-leave-to.

OptionTypeDefaultDescription
namestring"transition"Prefix of the classes added during the transition
durationnumber300Milliseconds until the Promise resolves and the classes are removed
onEnter(el: HTMLElement) => void—Called before the enter classes are added. Throwing rejects the Promise.
onLeave(el: HTMLElement) => void—Called before the leave classes are added. Throwing rejects the Promise.
Storybook
Live demo — list transitions

On this page