Hewn

Motion

Duration and easing tokens, MotionProvider modes, and the transform-and-opacity contract.

stablefree

Hewn treats motion as a global contract, not a per-component prop. Tokens set timing, MotionProvider sets the mode, and sources may only animate transform, opacity, and filter.

Live mode

Preference full · Effective full

Sync68%

Full mode: springs, press scale, and chart draws run.

Tokens

CSS custom properties live in @hewnui/themes/motion.css (imported by tokens.css). Theme packs still tune --kp-motion-duration for character; the scale below is shared.

TokenRoleDefault
--kp-motion-duration-instantImmediate / disabled0ms
--kp-motion-duration-fastMicro feedback120ms
--kp-motion-durationPack base (theme character)pack-specific
--kp-motion-duration-slowPresence / draws360ms
--kp-motion-ease-standardDefault CSS easingcubic-bezier(0.2, 0, 0, 1)
--kp-motion-ease-emphasizedEmphasized settlecubic-bezier(0.16, 1, 0.3, 1)
--kp-motion-ease-exitExit curvescubic-bezier(0.4, 0, 1, 1)
--kp-motion-ease-linearProgress / meterslinear

TypeScript mirrors for Motion transitions ship from @hewnui/motion:

TSX
import { durations, easings, springs } from "@hewnui/motion";

durations.fast; // 0.12
easings.standard; // [0.2, 0, 0, 1]
springs.snappy; // { type: "spring", stiffness: 420, … }

Use CSS vars in class strings (duration-[var(--kp-motion-duration-fast)], ease-[var(--kp-motion-ease-standard)]) and the TS constants in transition={{ … }} props. Do not hard-code millisecond or bezier values in components.

Modes

MotionProvider exposes three modes:

ModeBehavior
fullSprings, press scale, chart draws, layout presence
reducedMotionConfig reducedMotion="always"; CSS durations collapse to 0
offSame hard stop as reduced for Motion + CSS; static fallbacks only

Resolution rules:

  1. Default preference is full.
  2. prefers-reduced-motion: reduce caps effective mode at reduced — a user preference of full cannot win.
  3. Users may only reduce motion further (full → reduced → off).
  4. The effective mode is written to document.documentElement.dataset.motion so CSS-only surfaces can key off [data-motion="reduced"] / "off".
TSX
import { MotionProvider, useMotionMode } from "@hewnui/ui";

<MotionProvider defaultMotion="full">{children}</MotionProvider>

const { motion, effectiveMode, motionEnabled, reducedMotion, setMotion } =
  useMotionMode();
FieldMeaning
motionStored user preference
effectiveModePreference after OS clamp
motionEnabledeffectiveMode === "full"
reducedMotionOS preference is active
setMotionUpdate preference

MotionConfig receives reducedMotion="never" in full mode and "always" otherwise.

Rules

  • Animate transform, opacity, and filter only. Never animate layout properties (width, height, top, left, margin, …).
  • Never use transition: all or Tailwind transition-all.
  • Press feedback uses scale(0.96).
  • Reduced motion always wins over product defaults and user “more motion” choices.
  • Infinite motion must be isolated, memoized, and disabled when motionEnabled is false.
  • pnpm check:motion fails the build if packages/ui sources violate the transition contract.

Appearance controls

Docs and Theme Lab appearance docks include the motion mode control next to theme, mode, material, and radius. Choices persist in localStorage (kp-motion). Legacy on values migrate to full.

On this page