Interpolated motion

Updated: Sep 24, 2026
Use interpolated motion for a discrete change with known endpoints, such as a fade, icon rotation, or state change. It moves a value over a fixed duration and easing curve, and the browser can run it more cheaply than a spring.
Reference the toolkit’s named timing vocabulary: AnimationDurations for durations, Interpolators for easing curves, and createTransition for composing a CSS transition value. Literal timing values drift out of step when the system is tuned.

Usage

import {useMemo} from 'react';
import type {ReactNode} from 'react';
import {
  AnimationDurations,
  Interpolators,
  createTransition,
  reducedMotionDuration,
  usePrefersReducedMotion,
} from '@wearables-ui-toolkit/mrbd';

export function DetailReveal({
  isExpanded,
  children,
}: {
  isExpanded: boolean;
  children: ReactNode;
}) {
  const prefersReducedMotion = usePrefersReducedMotion();

  const transition = useMemo(
    () =>
      createTransition(
        ['opacity', 'transform'],
        reducedMotionDuration(
          AnimationDurations.TEXT_OPACITY,
          prefersReducedMotion,
        ),
        Interpolators.TEXT_OPACITY,
        reducedMotionDuration(
          AnimationDurations.TEXT_OPACITY_EXPAND_DELAY,
          prefersReducedMotion,
        ),
      ),
    [prefersReducedMotion],
  );

  return (
    <div
      className="detail-reveal"
      style={{transition, opacity: isExpanded ? 1 : 0}}
    >
      {children}
    </div>
  );
}
.detail-reveal {
  padding: var(--uit-spacing-medium);
  color: var(--uit-color-text-primary);
}
Run the delay through reducedMotionDuration as well as the duration. Otherwise a reduced-motion change still waits before snapping, which reads as a lag rather than as an instant update.
When the transition lives entirely in CSS rather than in a computed string, guard it with @media (prefers-reduced-motion: reduce) instead — see reduced motion.

API

createTransition(properties, duration, interpolator?, delay?)

ParameterTypeDefaultDescription
properties
string \| string[]
—
One CSS property name, or several. An array produces one comma-separated entry per property, all with the same timing.
duration
number
—
Duration in milliseconds. The helper appends the unit.
interpolator
string
Interpolators.CONTAINER_SCALE
A CSS timing function.
delay
number
0
Delay in milliseconds. Emitted only when greater than zero.
Returns a string suitable for the CSS transition property, shaped as "<property> <duration>ms <timing-function>", with " <delay>ms" appended when a delay was given. Compose several groups with different timings by calling it more than once and joining the results with ", ".

AnimationDurations

Durations in milliseconds. Reach for the entry whose name matches what you are animating; the relationships between them are deliberate.
ConstantWhat it times
CONTAINER_STATE_CHANGE
The baseline state change between the default and focused appearance.
CONTAINER_PRESS_IN
Any state into pressed. The shortest state-change duration, so press feedback feels immediate.
CONTAINER_PRESS_OUT
Pressed back to the previous state. Slightly longer than press-in, so the release reads as a settle rather than a snap.
CONTAINER_STATE_CHANGE_FAST_FOCUS
A focus change while the user is scrolling quickly. Shorter than the baseline so focus keeps up with the movement.
CONTAINER_FAST_MOVEMENT_EXPANSION_HESITATION_DELAY
How long a container waits before expanding while focus is moving fast, so items that are merely passed over never expand.
TEXT_OPACITY
A text opacity fade.
TEXT_OPACITY_EXPAND_DELAY
The delay before the text fade when content expands, so the text arrives after the surface has started to open.
TEXT_OPACITY_COLLAPSE_DELAY
The matching delay when content collapses. Collapse adds none, so the fade starts immediately.
ICON_ROTATION
An icon rotating in place.
CONTAINER_RUBBERBAND
One segment of the two-stage rubber-band response shown when focus cannot move further.

Interpolators

CSS timing-function strings. They work anywhere a timing function is accepted: the transition shorthand, animation-timing-function, and the easing option of the Web Animations API.
ConstantCurve
CONTAINER_SCALE
Smooth ease-in-out for state changes. The default used by createTransition.
CONTAINER_PRESS_IN
Fast start for responsive press feedback.
CONTAINER_PRESS_OUT
Softer release curve.
ICON_AVATAR
Icon and avatar transitions, such as a chip exchanging its leading element.
CONTAINER_RUBBERBAND_STAGE_ONE
The outward segment of the rubber-band response.
CONTAINER_RUBBERBAND_STAGE_TWO
The return segment of the rubber-band response.
TEXT_OPACITY
Standard ease for subtle opacity changes.
LINEAR
No easing — a constant rate.

Pairing durations with curves

Durations and interpolators are designed in pairs. Unless you have a reason to break them apart:
ChangeDurationInterpolator
Default to or from focused
CONTAINER_STATE_CHANGE
CONTAINER_SCALE
Focus change during fast scrolling
CONTAINER_STATE_CHANGE_FAST_FOCUS
CONTAINER_SCALE
Into pressed
CONTAINER_PRESS_IN
CONTAINER_PRESS_IN
Out of pressed
CONTAINER_PRESS_OUT
CONTAINER_PRESS_OUT
Text fade
TEXT_OPACITY
TEXT_OPACITY
Rubber-band segment
CONTAINER_RUBBERBAND
CONTAINER_RUBBERBAND_STAGE_ONE, then CONTAINER_RUBBERBAND_STAGE_TWO

Behavior

An interpolated transition is bound to its endpoints. If the target changes mid-flight the browser restarts the interpolation from the current computed value, which is correct for discrete changes but reads as a stutter when the target moves repeatedly. If that is the situation you are in, the value wants spring motion instead.
The same distinction is built into Container: press-in and press-out drive the content scale with a spring, because a press can be released at any point, while every other state change follows these curves. The press durations above still time the material’s visual-state transition for those changes; only the content scale is spring-driven, and it settles on the spring’s own timing.
Interpolated transitions are also where the reduced-motion helpers do their work. reducedMotionDuration collapses the duration to zero, which makes the property jump to its new value on the next frame while still going through the same code path.
  • Reference AnimationDurations and Interpolators by name and keep the designed pairings so related motion remains synchronized.
  • Build transition strings with createTransition so property lists, units, and delays stay consistent.
  • Memoize the transition string when it depends only on the reduced-motion preference.
  • Transition opacity and transform, which the browser can composite.

Avoid

  • Use named constants instead of literal durations or easing curves in CSS and TypeScript.
  • Prefer composited properties over layout properties such as width, height, or margin around live content; layout properties reflow the subtree.
  • Let the toolkit components own properties they already animate.
  • Choose a spring for a target that changes every frame; fixed interpolation can stutter under repeated retargeting.