npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

solidjs-motion

v0.2.3

Published

An animation library for SolidJS — port of motion/react patterns built on the framework-agnostic motion package

Downloads

1,599

Readme

solidjs-motion

An animation library for SolidJS — a port of motion/react patterns built on the framework-agnostic motion package.

Status: v0.2.0 (pre-1.0). The library covers the canonical motion/react surface; minor versions may still ship breaking changes while the API stabilizes. Live demos: solidjs-motion.github.io/motion.

Install

npm

bun add solidjs-motion motion solid-js
# or: npm i solidjs-motion motion solid-js

JSR

bunx jsr add @solidjs-motion/motion

motion, motion-dom, and solid-js are peer dependencies — install them alongside.

Quick taste

import { useMotion } from "solidjs-motion"

export function Card() {
  const motion = useMotion({
    initial: { opacity: 0, y: 20 },
    animate: { opacity: 1, y: 0 },
    transition: { duration: 0.6 },
    hover: { scale: 1.02 },
    press: { scale: 0.98 },
  })

  return <div {...motion({ class: "card" })}>Hello, motion.</div>
}

useMotion returns a function that merges your props with motion's. User styles deep-merge with motion's initial styles; user refs and motion's ref both fire; the initial style is serialized into SSR HTML so the first paint is flicker-free.

Recipes

1. Reactive options

Every primitive that takes an options bag accepts EITHER a static object OR a Solid Accessor<Options> — i.e. a () => Options function. The accessor form lets you read signals inside the options without per-field accessor boilerplate. This is the canonical reactive escape hatch across the public API:

  • useMotion(opts | () => opts)
  • createMotion(el, opts | () => opts)
  • createScroll(opts | () => opts)
  • createInView(ref, opts | () => opts)
  • createPan(ref, opts | () => opts)
  • createSpring(source, opts | () => opts)
  • createTransform(input, range | () => range, output | () => output, opts?)
import { useMotion } from "solidjs-motion"
import { createSignal } from "solid-js"

export function Toggle() {
  const [open, setOpen] = createSignal(false)
  const motion = useMotion(() => ({
    animate: { rotate: open() ? 180 : 0 },
    transition: { duration: 0.3 },
  }))
  return (
    <button onClick={() => setOpen((p) => !p)} {...motion()}>
      ↑
    </button>
  )
}

Same pattern for a scroll-linked progress bar with a swappable container:

import { createScroll, createTransform, motion } from "solidjs-motion"
import { createSignal } from "solid-js"

export function ProgressIn(props: { children: any }) {
  const [el, setEl] = createSignal<HTMLElement>()
  // Whole options wrapped in an accessor — swapping `el` re-attaches the
  // scroll subscription. Per-field accessors on `container`/`target` were
  // removed in 0.2.0; this is the supported reactive form.
  const { scrollYProgress } = createScroll(() => ({ container: el() }))
  const width = createTransform(scrollYProgress, [0, 1], ["0%", "100%"])
  return (
    <div ref={setEl} style={{ overflow: "auto", height: "300px" }}>
      <motion.div class="progress" style={{ width }} />
      {props.children}
    </div>
  )
}

2. <motion.X> proxy with variants

Every HTML/SVG tag is reachable off motion. Variant labels on the parent cascade to descendants through m.Provider (auto-installed by the proxy).

import { motion } from "solidjs-motion"

const variants = {
  rest: { y: 0, scale: 1 },
  lift: { y: -8, scale: 1.04 },
}

export function Card() {
  return (
    <motion.article animate="rest" hover="lift" variants={variants}>
      <motion.h2 variants={variants}>Inherits lift on hover</motion.h2>
    </motion.article>
  )
}

3. motion.create(Component) HOC

Wrap a custom component to make it motion-aware. The wrapped component must spread its props (including ref) onto a single DOM-element root.

import { motion } from "solidjs-motion"
import type { ComponentProps } from "solid-js"

function Button(props: ComponentProps<"button">) {
  return <button {...props} class={`btn ${props.class ?? ""}`} />
}

const MotionButton = motion.create(Button)

export function Stage() {
  return (
    <MotionButton hover={{ scale: 1.05 }} press={{ scale: 0.95 }}>
      Press me
    </MotionButton>
  )
}

4. MotionValues + createTransform

MotionValues are the source of truth for animated state. They're both Solid Accessors and upstream MotionValues — drop them straight into style.

import { motion, createMotionValue, createTransform } from "solidjs-motion"

export function FadeSlider() {
  const x = createMotionValue(0)
  const opacity = createTransform(x, [-100, 0, 100], [0, 1, 0])
  return (
    <motion.div
      drag="x"
      dragConstraints={{ left: -100, right: 100 }}
      style={{ x, opacity }}
    >
      Drag me
    </motion.div>
  )
}

5. Spring-smoothed pointer

createSpring mirrors any numeric input with physics smoothing.

import { motion, createMotionValue, createSpring } from "solidjs-motion"
import { onCleanup, onMount } from "solid-js"

export function Cursor() {
  const x = createMotionValue(0)
  const y = createMotionValue(0)
  const sx = createSpring(x, { stiffness: 200, damping: 30 })
  const sy = createSpring(y, { stiffness: 200, damping: 30 })

  onMount(() => {
    const move = (e: PointerEvent) => {
      x.set(e.clientX)
      y.set(e.clientY)
    }
    window.addEventListener("pointermove", move)
    onCleanup(() => window.removeEventListener("pointermove", move))
  })

  return <motion.div class="cursor" style={{ x: sx, y: sy }} />
}

6. Scroll-linked progress bar

import { motion, createScroll, createTransform } from "solidjs-motion"

export function ProgressBar() {
  const { scrollYProgress } = createScroll()
  const width = createTransform(scrollYProgress, [0, 1], ["0%", "100%"])
  return <motion.div class="progress" style={{ width }} />
}

7. Viewport-triggered fade-in

import { motion } from "solidjs-motion"

export function FadeInOnce() {
  return (
    <motion.section
      initial={{ opacity: 0, y: 40 }}
      inView={{ opacity: 1, y: 0 }}
      inViewOptions={{ once: true, margin: "0px 0px -10% 0px" }}
    >
      Comes in once, stays.
    </motion.section>
  )
}

8. createTemplate for interpolated strings

Build a MotionValue<string> from interpolated MVs/Accessors — feed it to any string-valued CSS property.

import { motion, createMotionValue, createTemplate } from "solidjs-motion"

export function GradientBox() {
  const angle = createMotionValue(0)
  const background = createTemplate`linear-gradient(${angle}deg, #f0f, #0ff)`
  return (
    <motion.div
      hover={{ rotate: 360 }}
      transition={{ duration: 2 }}
      style={{ background }}
    />
  )
}

9. <Presence> for exit animations

Wrap a conditionally-rendered child to animate its exit before unmount.

import { motion, Presence } from "solidjs-motion"
import { Show, createSignal } from "solid-js"

export function Drawer() {
  const [open, setOpen] = createSignal(false)
  return (
    <>
      <button onClick={() => setOpen((p) => !p)}>Toggle</button>
      <Presence>
        <Show when={open()}>
          <motion.aside
            initial={{ x: -300 }}
            animate={{ x: 0 }}
            exit={{ x: -300 }}
            transition={{ duration: 0.25 }}
          >
            Drawer content
          </motion.aside>
        </Show>
      </Presence>
    </>
  )
}

10. mode="wait" + list exits

mode="wait" plays the outgoing child's exit fully before the incoming child enters. For lists, Presence wraps a <For> and animates add/remove together.

import { motion, Presence } from "solidjs-motion"
import { For, Show, createSignal } from "solid-js"

export function Tabs() {
  const [tab, setTab] = createSignal("a")
  return (
    <Presence mode="wait">
      <Show when={tab()} keyed>
        {(t) => (
          <motion.div
            initial={{ opacity: 0, y: 8 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -8 }}
          >
            Tab {t}
          </motion.div>
        )}
      </Show>
    </Presence>
  )
}

export function Notifications(props: { items: () => string[] }) {
  return (
    <Presence>
      <For each={props.items()}>
        {(msg) => (
          <motion.li
            initial={{ opacity: 0, x: -16 }}
            animate={{ opacity: 1, x: 0 }}
            exit={{ opacity: 0, x: 16 }}
          >
            {msg}
          </motion.li>
        )}
      </For>
    </Presence>
  )
}

11. Drag with constraints

dragConstraints accepts either a numeric rect ({ top, left, right, bottom }) or a container HTMLElement. For a reactive container, wrap the surrounding MotionOptions in an accessor (e.g. useMotion(() => ({ drag: true, dragConstraints: containerEl() }))) — the per-field () => HTMLElement form was removed in 0.2.0. dragElastic controls overshoot.

import { motion } from "solidjs-motion"

export function DraggableCard() {
  let bounds!: HTMLDivElement
  return (
    <div ref={bounds} class="bounds">
      <motion.div
        drag
        dragConstraints={bounds}
        dragElastic={0.2}
        whileDrag={{ scale: 1.05 }}
      >
        Drag inside
      </motion.div>
    </div>
  )
}

12. <MotionConfig> + reduced motion

<MotionConfig> flows defaults (transition, reduced-motion mode, CSP nonce) to descendants. createReducedMotion() reads the system preference directly.

import { MotionConfig, createReducedMotion, motion } from "solidjs-motion"

export function App() {
  const reduced = createReducedMotion()
  return (
    <MotionConfig reducedMotion="user" transition={{ duration: 0.4, ease: "easeOut" }}>
      <motion.div animate={{ x: 100 }}>Honors `prefers-reduced-motion`</motion.div>
      <p>System reduced-motion: {String(reduced())}</p>
    </MotionConfig>
  )
}

13. Layout animations

Add layout to a motion element and it'll FLIP to its new rect on every layout-affecting change — sibling add/remove, content reflow, parent's style/class change, own ResizeObserver firing. The default transition is a critically-damped spring tuned for snappy-but-not-bouncy.

import { motion } from "solidjs-motion"
import { For, createSignal } from "solid-js"

let nextId = 3
const INITIAL = [{ id: "1", label: "Task one" }, { id: "2", label: "Task two" }]

export function StackedCards() {
  const [items, setItems] = createSignal(INITIAL)
  return (
    <>
      <button onClick={() => setItems((p) => [{ id: `${++nextId}`, label: `Task ${nextId}` }, ...p])}>
        + add
      </button>
      <ul>
        <For each={items()}>
          {(item) => (
            <motion.li layout>
              {item.label}
              <button onClick={() => setItems((p) => p.filter((i) => i.id !== item.id))}>×</button>
            </motion.li>
          )}
        </For>
      </ul>
    </>
  )
}

14. Shared-element transitions with layoutId

Two motion elements with the same layoutId in the same <LayoutGroup> scope hand off animation rect across an unmount/mount. Pair with <Presence> so the donor's exit and the consumer's enter FLIP run in parallel.

import { motion, Presence } from "solidjs-motion"
import { Show, createSignal } from "solid-js"

export function ExpandableCard() {
  const [open, setOpen] = createSignal(false)
  return (
    <Presence>
      <Show
        when={open()}
        keyed
        fallback={
          <motion.button layoutId="card" onClick={() => setOpen(true)} class="thumb">
            Open
          </motion.button>
        }
      >
        <motion.div layoutId="card" onClick={() => setOpen(false)} class="hero">
          Hero content
        </motion.div>
      </Show>
    </Presence>
  )
}

15. Reorder list (basic drag-to-reorder)

<Reorder.Group> + <Reorder.Item> for drag-to-reorder. Each Item gets layout: true

  • drag: <axis> automatically. The dragged row tracks the pointer; siblings FLIP into their new slots as it crosses their centers; values is mutated live (no preview state).
import { Reorder } from "solidjs-motion"
import { For, createSignal } from "solid-js"

const INITIAL = [{ id: "1", label: "First" }, { id: "2", label: "Second" }, { id: "3", label: "Third" }]

export function SortableList() {
  const [items, setItems] = createSignal(INITIAL)
  return (
    <Reorder.Group values={items} onReorder={setItems}>
      <For each={items()}>
        {(item) => (
          <Reorder.Item value={item} class="list-item">
            {item.label}
          </Reorder.Item>
        )}
      </For>
    </Reorder.Group>
  )
}

16. Reorder with a drag handle

When item content contains interactive elements (checkboxes, inputs, buttons), full-row drag steals pointer events from them. Scope drag initiation to a dedicated handle with dragListener: false + dragControls={controls} (the same createDragControls factory that's used outside Reorder). One controls instance per row.

import { createDragControls, Reorder } from "solidjs-motion"
import { For, createSignal } from "solid-js"

export function TaskList() {
  const [tasks, setTasks] = createSignal([
    { id: "1", label: "Pick up groceries", done: false },
    { id: "2", label: "Review PR #142", done: true },
  ])
  const toggle = (id: string) =>
    setTasks((p) => p.map((t) => (t.id === id ? { ...t, done: !t.done } : t)))

  return (
    <Reorder.Group values={tasks} onReorder={setTasks}>
      <For each={tasks()}>
        {(task) => {
          const controls = createDragControls()
          return (
            <Reorder.Item value={task} dragListener={false} dragControls={controls}>
              <button
                type="button"
                aria-label={`Drag ${task.label}`}
                onPointerDown={(e) => controls.start(e)}
                style={{ "touch-action": "none", cursor: "grab" }}
              >
                ⋮⋮
              </button>
              <input
                type="checkbox"
                checked={task.done}
                onChange={() => toggle(task.id)}
              />
              <span>{task.label}</span>
            </Reorder.Item>
          )
        }}
      </For>
    </Reorder.Group>
  )
}

17. Reorder with exit animations

Pair Reorder with <Presence> to animate items in/out as they're added/removed. Always pass exitMethod="keep-index" for layout-animated lists — the default ("move-to-end") shuffles the exiting node to the end of the list during its fade, firing the layout-coordinator's parent-MO mid-exit and visibly sliding the item to the bottom instead of letting it fade in place. keep-index holds the slot until exit completes; survivors only FLIP after the slot is released.

import { Presence, Reorder } from "solidjs-motion"
import { For, createSignal } from "solid-js"

let nextId = 3
const INITIAL = [{ id: "1", label: "First" }, { id: "2", label: "Second" }]

export function TaskList() {
  const [items, setItems] = createSignal(INITIAL)
  const add = () =>
    setItems([{ id: `${++nextId}`, label: `Task ${nextId}` }, ...items()])
  const remove = (id: string) => setItems(items().filter((i) => i.id !== id))

  return (
    <>
      <button onClick={add}>+ add</button>
      <Reorder.Group values={items} onReorder={setItems}>
        <Presence exitMethod="keep-index">
          <For each={items()}>
            {(item) => (
              <Reorder.Item
                value={item}
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
              >
                {item.label}
                <button onClick={() => remove(item.id)}>×</button>
              </Reorder.Item>
            )}
          </For>
        </Presence>
      </Reorder.Group>
    </>
  )
}

18. createReorder primitive (custom Reorder UI)

<Reorder.Group> / <Reorder.Item> are thin JSX wrappers over createReorder. Drop down to the primitive when you need a custom container/item structure, or when surrounding UI needs to react to the drag state (e.g., a status bar, a disabled-while-dragging side panel, a drop-zone indicator). createReorder returns { group, item, dragging }:

  • group.ref — spread onto the container element (forward-compat slot).
  • item(value, motionOptions?) — per-row factory. Returns the same callable shape useMotion returns: spread m() onto the JSX element, optionally wrap children in m.Provider for variant cascade.
  • dragging() — Accessor of the value being dragged (or null). Use it anywhere — outside the list, inside it, in a sibling component.
import { createReorder } from "solidjs-motion"
import { For, createSignal } from "solid-js"

export function PrioritisedTaskList() {
  const [tasks, setTasks] = createSignal([
    { id: "1", label: "Draft RFC", priority: "high" },
    { id: "2", label: "Review PRs", priority: "med" },
    { id: "3", label: "Update docs", priority: "low" },
  ])

  const reorder = createReorder(tasks, setTasks, { axis: "y" })

  return (
    <section>
      <header>
        Drag to reorder ·{" "}
        <strong>{reorder.dragging()?.label ?? "(idle)"}</strong>
      </header>
      <ul ref={reorder.group.ref} class="task-list">
        <For each={tasks()}>
          {(task) => {
            const m = reorder.item(task)
            return (
              <li
                {...m({
                  class: `task ${task.priority} ${
                    reorder.dragging() === task ? "is-dragging" : ""
                  }`,
                })}
              >
                {task.label}
              </li>
            )
          }}
        </For>
      </ul>
    </section>
  )
}

createReorder accepts an Accessor<T[]> (createSignal form) OR T[] directly (createStore form — pass store.items and (next) => setStore("items", next)). Both forms track reactivity correctly; mutation-detection uses a re-entrancy flag rather than reference identity so setStore(produce(...)) in-place mutations work too.

Performance

For the mental model on <Reorder.Group> + layout: true at realistic list sizes (N up to ~500), how to measure JS coordination cost vs. real browser paint/composite/GC, and the optimization checklist, see PERFORMANCE.md. Raw numbers live in bench/BASELINES.md.

Roadmap

Shipped

Canonical hook + imperative primitive

  • useMotion(opts | Accessor<opts>) — the public API. Returns a callable motion(userProps?) that merges with motion's ref/style/data attributes, plus a motion.Provider for opt-in variant context propagation.
  • createMotion(el, opts | Accessor<opts>) — the imperative primitive useMotion wraps, for advanced use (drag controls, custom directives).

MotionValue family (every value is a callable MotionValueAccessor<T> = MotionValue<T> & (() => T))

  • createMotionValue(initial) — bound to the current owner; auto-disposed on cleanup.
  • createTransform(input, range, output, opts?) — map a MV/Accessor through a numeric range.
  • createSpring(source, opts?) — physics-smoothed mirror of a numeric input.
  • createTime() — frame-driver advancing each animation frame with elapsed ms.
  • createVelocity(source) — reports the instantaneous velocity of another MV.
  • createTemplate\…`— tagged template producing aMotionValueAccessor` from interpolated MVs/Accessors.
  • createMotionValueEvent(mv, event, cb) — subscribe with automatic cleanup.
  • toSignal(rawMv) — bridge a raw motion MotionValue to a Solid Accessor.

Scroll + viewport

  • createScroll(opts?){ scrollX, scrollY, scrollXProgress, scrollYProgress } MotionValueAccessors.
  • createInView(ref, opts?) — IntersectionObserver wrapper; view.isInView() boolean Accessor, view.entry() Accessor of the raw IntersectionObserverEntry. Accepts amount: number[] for continuous intersectionRatio updates.

Gestures (declarative on useMotion options)

  • hover / press / focus — variant or target object per state. State machine resolves per-property winners across simultaneous states (press > focus > hover > inView > animate).
  • inView — viewport-triggered variant; honors inViewOptions: ViewportOptions (margin, amount, root, once).
  • Callbacks: onHoverStart/onHoverEnd, onPressStart/onPress/onPressCancel, onFocus/onBlur, onViewportEnter/onViewportLeave.

Drag

  • drag: true | "x" | "y" axis lock, dragConstraints (numeric rect or container HTMLElement), dragElastic, dragMomentum, dragSnapToOrigin, dragTransition, whileDrag for sibling-axis visual state.
  • Drag-scrolldragScroll (default true), dragScrollContainer (HTMLElement or accessor; defaults to the nearest scrollable ancestor, falling through to the document scroller), dragScrollThreshold (edge-zone px), dragScrollSpeed (max px/sec). When a drag nears the container's leading/trailing edge along the drag axis, the container auto-scrolls so the drag can continue past the visible viewport — the dragged element tracks the pointer while it scrolls.
  • createPan(ref, opts?) — standalone pan-session primitive. Returns { isPanning, point, delta, offset, velocity } with MotionValueAccessors at the numeric leaves.
  • createDragControls() — drag-handle pattern. One element captures the pointer, another moves.

Variants

  • Named variants on variants: { … }. Parent labels cascade to descendants via motion.Provider.
  • "Controlling variants" rule: a child with its own variant label opts out of the parent cascade (motion-dom parity).
  • Dynamic variants: each variant can be a function of custom for per-instance staggering and per-index timing.

Presence + exit animations

  • <Presence> — coordinator that runs each child's exit target before unmount. Works with <Show>, <For>, and arbitrary mount/unmount toggles.
  • mode: "sync" | "wait" — synchronous swap (default) or sequential swap where the outgoing child exits fully before the incoming child enters.
  • initial: false — suppress the entrance animation for the initial mount; subsequent mounts animate.
  • useAnimatePresence() — imperative escape hatch returning { Provider, exit() } for cases where you need to await exit completion before flipping state yourself.
  • Descendant-walk exit registration: passive children inside m.Provider with a variants map keyed off the parent's exit label exit alongside the parent automatically (the orchestrated-exit cascade).

<motion.X> proxy + motion.create(Component) HOC

  • motion.div, motion.span, motion.path — every HTML/SVG intrinsic element accessed off motion returns a typed Component whose props are that element's native attributes intersected with MotionOptions. SVG namespace handled via <Dynamic>.
  • Each tag-component auto-wraps its rendered output in m.Provider, so variant cascade reaches descendants without manual wrapping for the common case.
  • motion.create(MyComponent) — HOC for custom components. The wrapped component must forward props.ref to a single DOM-element root (Solid's "ref forwarding by convention" reality). A dev-mode warning fires if the wrap is broken.
  • See ADR 0004 for the design rationale.

MV-in-style (<motion.div style={{ scale: mv }}>)

  • Pass any MotionValue directly in style — motion subscribes to it and writes the corresponding DOM property/transform on every change. No React-style re-render cycle.
  • Composes with initial, animate, gestures, and exit on the same key: the MV is the source of truth, and animate dispatches tween THROUGH the MV (matches motion-react fidelity).
  • Static transform shortcuts (x: 10, scale: 1.5) also work — motion composes them into the transform string in canonical order (translate → scale → rotate).
  • SSR-aware: the MV's snapshot value lands in the rendered HTML so client hydration is flicker-free.
  • MotionStyle type widens JSX.CSSProperties to accept MotionValue in every slot, plus motion's transform shortcuts.
  • See ADR 0005 for the per-element value-registry architecture.

Config + reduced motion

  • <MotionConfig transition reducedMotion nonce> — shared defaults for a subtree.
  • createReducedMotion() — reactive system-pref accessor backed by matchMedia("(prefers-reduced-motion)").

SSR

  • useMotion emits a deterministic inline style + data-motion-hydrated="" marker on the server. First paint matches the initial target; the client skips the initial-style application on hydration.

Layout animations (new in 0.2.0)

  • layout: true | "position" | "size" | "preserve-aspect" — per-element FLIP from First to Last rect on every layout-affecting change (parent reorder, sibling add/remove, content reflow, ResizeObserver self, parent style/class change). Critically-damped spring default transition tuned for snappy-but-not-bouncy.
  • layoutId — cross-element handoff. Two elements with the same id (within the same <LayoutGroup> scope) animate seamlessly across an unmount/mount. Pairs with <Presence> for parallel exit + enter FLIP.
  • <LayoutGroup> scopes layoutId namespacing AND broadcasts dependency changes group-wide.
  • layoutScroll, layoutRoot, layoutAnchor, layoutDependency, layoutTransition for finer control.
  • onLayoutAnimationStart / onLayoutAnimationComplete lifecycle callbacks.
  • Target accepts every CSS property via csstype.PropertiesHyphen — hyphenated keys (box-shadow, background-color, --my-custom-prop) are first-class in animate, gesture targets, and style.

Reorder (new in 0.2.0)

  • <Reorder.Group values onReorder> + <Reorder.Item value> — drag-to-reorder. Items get layout: true + drag: <axis> automatically; the dragged row tracks the pointer, siblings FLIP into their new slots, list mutation happens live (no preview state).
  • createReorder(values, setValues, options?) — the primitive that backs the components. Accepts either Accessor<T[]> (signal) or T[] (store).
  • Drag-handle pattern via existing dragListener: false + dragControls={controls} — the row's body stays interactive, drag initiation scoped to a dedicated handle.
  • cancelOnExternalReorder for strict mutation guards.
  • Scrollable lists<Reorder.Group> is a layoutScroll element, so a long list with max-height + overflow: auto keeps sibling FLIPs correct while scrolled. Drag-scroll is on by default: drag a row toward the group's edge and it auto-scrolls. Tune group-wide via dragScroll / dragScrollThreshold / dragScrollSpeed on <Reorder.Group>.
  • Reorder.Item provides variant context to descendants (nested <motion.button> inside a row inherits the item's animate / hover / whileDrag / exit labels).
  • Pair with <Presence exitMethod="keep-index"> when items have exit declared.

Improved revert behavior (new in 0.2.0)

  • Originals tracking — on first paint, the element's computed style is captured for every gesture-target key that has no canonical motion default (anything outside transforms + opacity). When a gesture deactivates and animate doesn't claim the key, the captured original is the revert target. No more redundant animate.box-shadow workaround.
  • whileDrag propagates through variant context — descendants wrapped in m.Provider with a matching label in variants respond to the parent's drag state, same as whileHover / whilePress / whileFocus / whileInView.

Re-exports from upstream motion

  • animate, inView, isMotionValue, motionValue, scroll, spring — for direct use where the framework wrapper isn't needed.

Deferred to v0.3+

  • SVG path drawing (<motion.path pathLength>).
  • useAnimate imperative AnimationControls equivalent.
  • LazyMotion lazy-loaded feature bundles.
  • Generalized shadow-shape normalization (currently only "none"/"" are normalized for box/text-shadow).

License

MIT — copyright the solidjs-motion contributors.