Skip to content

Motion principles

The motion principles behind Youj shown side by side, then a duration and curve for each kind of interaction, reduced motion, and when to leave content still.

Youj uses short, precise motion to show what changed. These principles explain when to animate, how to time it and how to keep the result clear. Further down, the guidelines pick a duration and a curve for each interaction and check how it behaves with reduced motion. Those values are tokens, so they follow the theme's motion settings and Speed; try them in the Motion playground.

01
PreciseFast, confident motion that stops exactly on its mark. No overshoot by default.
02
EarnedOne settle spring with a single overshoot, spent on success and arrival.
03
RestrainedNeutral surfaces and one electric accent. The motion is the ornament.
04
InclusiveReduced motion is a designed state that keeps the meaning, not a missing feature.

Shown, not described

Each pair plays the same moment twice: once in the Youj language, and once the way it usually goes wrong. The "don't" side deliberately uses values that aren't tokens, and it still follows the time scale, so the comparison stays fair.

01

Land, don’t bounce

UI chrome arrives decisively and stops dead on its mark. Springy menus read as imprecise.

Options
Dotween base · enter, exit fast · exit
Options
Don’tbouncy spring, bounce 0.6
02

Quick in, quicker out

Exits are shorter than entrances and accelerate away. Nobody waits for something to leave.

Changes saved
Doenter base · enter, exit fast · exit
Changes saved
Don’tsymmetric 700ms ease-in-out both ways
03

Overshoot is earned

The settle spring’s single overshoot is reserved for arrival and success. Spend it once.

Lint
Test
Deploy
Doonly the completing task pops · settle
Lint
Test
Deploy
Don’teverything wobbles, bounce 0.7
04

Cascade, don’t waterfall

Stagger reveals order, not suspense. A tight step reads as one gesture; a long one reads as lag.

Dostagger base · tween base, enter
Don’t180ms steps, 500ms items
05

Fast, not sluggish

Direct manipulation answers within a couple of hundred milliseconds. Linear, slow motion feels mechanical.

OverviewActivitySettings
Dospring smooth
OverviewActivitySettings
Don’t900ms linear
06

Reduced motion keeps the meaning

Under reduced motion, swap the animation for an instant state change, never for nothing.

Savedstate shown instantly
Doinstant state change, same information
Savemotion removed, so no feedback
Don’tanimation skipped, confirmation lost

Timing lives in tokens

No component hard-codes a duration or a curve. Every value comes from one set of motion tokens, and every component reads it through useYouj(), so tuning a token retunes the whole library. The durations form a short geometric scale, and anything that answers direct input finishes within base. Feedback takes the snappy spring, layout changes smooth, and only success and arrival get settle, the one spring that overshoots.

Motion tokens lists every duration, easing, spring and stagger step, with which one each moment takes and how the time scale and reduced motion reach them.

In code

Timing comes from the hook, never from literals.

import { m } from 'motion/react'
import { MotionFeatures, useYouj } from '@youj/core'

export function Panel({ open }: { open: boolean }) {
  const k = useYouj()
  return (
    <MotionFeatures>
      <m.div
        animate={{ opacity: open ? 1 : 0, y: open ? 0 : 8 }}
        transition={k.reduced ? { duration: 0 } : k.tween('base', open ? 'enter' : 'exit')}
      />
    </MotionFeatures>
  )
}
  • k.tween(duration, ease) and k.spring(token) return ready Motion transitions.
  • k.stagger(token) is a step in seconds, and k.ms(token) a duration in milliseconds for CSS, WAAPI and requestAnimationFrame code.
  • k.reduced is true when motion should give way to an instant or static alternative.
  • k.timeScale multiplies every duration. Loops and shaders divide elapsed time by it.

In CSS, use the yj-duration-* utilities (yj-duration-fast and its siblings) with the ease-standard, ease-enter, ease-exit and ease-emphasized utilities. They follow the provider's speed and reduced motion. The raw var(--yj-duration-fast) follows the speed but not reduced motion, so keep it for loops you stop yourself.

Durations by interaction

How long each kind of interaction takes, in the precise personality's tokens, and the curve it rides
InteractionTokens and curve
Press and hoverbuttons, rows, tints, focus rings80–140 mspress in on instant, out on fast; hover fastCurve: standard
Toggle and selectswitches, checkboxes, tabs, segments200–360 msa thumb on spring.snappy; an indicator on spring.smoothCurve: spring, no overshoot
Tooltipyj-tooltip80–140 msin on fast, out on instantCurve: enter in, exit out
Menu and popovermenus, selects, popovers: yj-pop80–140 msin on fast, out on instantCurve: enter in, exit out
Dialog and sheetyj-dialog, yj-sheet-*, toasts140–340 msa dialog base in, fast out; a sheet or toast slow inCurve: enter in, exit out
List insert, remove, reorderPresenceList rows, cards140–360 msin on base, out on fast, moves on spring.smooth; stagger.base betweenCurve: enter, exit, spring
Page and heroroute changes, a landing reveal340–560 msslow for a route, deliberate for a heroCurve: emphasized
Success and arrivala save lands, a task completes450 msspring.settleCurve: the one overshoot

Three rules hold the table together:

  • Feedback lands within base. The answer to a press, a toggle or a menu shows inside base (220 ms with the precise character, 300 ms with calm, 190 ms with lively), even while an indicator is still gliding. Later than that reads as lag, however good the curve.
  • Farther is longer, never slow. A sheet crossing the screen gets slow; nothing a person waits on gets more than deliberate.
  • Out is shorter than in. An exit takes the next token down and accelerates away on exit. Nobody waits for something to leave.

Springs for anything that can be interrupted or moves with the hand: toggles, indicators, rows that reorder. Tweens for things that come and go on their own: overlays, toasts, entrances. settle's one overshoot is for a success or an arrival, once per moment.

Should this animate?

  1. Does it explain a change? Motion earns its place by showing where something came from, where it went or what changed. Decoration gets one moment per screen, if any.
  2. Will people see it all day? The more often it plays, the shorter it gets. A menu opened fifty times a day pops in on fast; a list that filters as you type doesn't animate its results at all.
  3. Is anyone waiting on it? Motion never delays input or content. Start on the press, not the release, and let the next action interrupt it from where it is.
  4. Does it hold up at ¼ speed and under reduced motion? If a state only makes sense while it's moving, the design is in the motion, not the state. Fix the state.
  5. Is something else already moving? One focal motion at a time. A second competes; a third is noise.

Reduced motion: keep the meaning, remove the travel

Under reduced motion nothing travels, scales or bounces, and nothing is lost: every state still changes, the same words and icons appear, focus moves and announcements are made. It isn't motion switched off. It's a designed state.

Doreduced: the toast is simply there
Don’treduced: the toast still slides in

How Youj does it:

  • In JavaScript, useYouj().reduced is true when the OS asks or a provider sets reducedMotion="always". Components render their end state and swap every transition for { duration: 0 }.
  • In CSS, every overlay preset and yj-duration-* utility multiplies its token by --yj-motion, which is 0 when reduced, so transitions become instant state changes and Base UI unmounts at once.
  • Loops stop on a designed frame; live data still updates, in steps. Pointer and scroll effects switch off.
  • Direct manipulation still follows the finger: a swiped sheet or toast tracks it, because that isn't animation.

Do and don't

Grow from the trigger

An overlay scales from where it was asked for, so the eye never has to find it.

Dogrows from its trigger: yj-pop, fast · enter
Don’tgrows from the middle of the page, 360 ms

Move only what changed

A new row enters and the others make room. Replaying every entrance on every change says everything changed.

Dothe new row enters, the rest make room
Don’tevery row plays its entrance again

Hover keeps up with the pointer

A hover state is where the pointer is, now. A slow fade leaves a trail of half-lit rows.

Dothe highlight is where the pointer is, on fast
Don’ta 400 ms fade trails behind the pointer

To feel every token against its curve, open the Motion playground.