Skip to content

Motion

Motion in an interface has one job: to explain what just changed. Something arrived, something left, something moved from here to there, something was pressed. Motion that does not explain a change is decoration, and decoration that moves is the most tiring kind. This page is the motion system MIAN uses, and the rules for adding your own.

Five reasons to move something

PurposeWhat happensHow long
FeedbackThe control acknowledges the touch: it darkens, shrinks slightly, or drops onto its shadow.100–150 ms
State changeA switch slides, a tab indicator moves, a checkbox draws its tick.150–250 ms
Arrival and departureA menu, dialog or toast enters from where it came and leaves the same way, faster.200–350 ms in, 150–200 ms out
Spatial continuityOne screen becomes the next: a cross-fade, or a shared element that travels.250–400 ms
AttentionSomething needs the reader: a single pulse, once.One cycle, then stop

The motion tokens

--mn-dur-fast120 ms. Feedback on press and hover.
--mn-dur200 ms. State changes.
--mn-dur-slow360 ms. Layout changes, large surfaces.
--mn-ease-outcubic-bezier(.16, 1, .3, 1). Anything arriving: fast start, long gentle stop.
--mn-ease-in-outcubic-bezier(.65, 0, .35, 1). Anything moving from one place to another on screen.
--mn-ease-springa linear() spring. A soft overshoot, for playful noodles only.
--mn-enterper noodle. Each noodle’s own entrance. Use it through the mn-enter class.
.my-panel {
  transition: opacity var(--mn-dur) var(--mn-ease-out),
              transform var(--mn-dur) var(--mn-ease-out);
}

Rules

  • Animate transform and opacity. Animating size, position or shadows makes the page stutter.
  • Things leave faster than they arrive.
  • Small things move fast, large things move slower, and nothing takes longer than 400 ms.
  • Ease out for arrivals, ease in for departures, ease in-out for travel. Linear is for spinners and progress only.
  • One moving thing at a time. Stagger a list by 30 to 50 ms per item, for at most eight items.
  • Never animate on scroll to make content appear. The reader came for the content; hiding it until they scroll is the best-known mark of a generated page.
  • Never loop an animation near text, and never animate while someone is typing.
  • Honour prefers-reduced-motion: every MIAN duration becomes zero, and nothing depends on an animation finishing.
  • Bounce and overshoot belong to playful products. In a serious one they look dated.

What MIAN gives you

  • Every noodle defines its own entrance (--mn-enter) and durations. Menus, dialogs, toasts and popovers already use them.
  • mn-enter and mn-stagger apply the noodle’s entrance to anything you add.
  • mn-press and mn-lift give press and hover feedback.
  • Mian.transition(fn) wraps a DOM change in a view transition, so it cross-fades; changing noodle or mode already does this.
  • data-mn-count counts a number up once, when it is first seen.

Try it

Change the noodle with the picker and press the button: the list enters in that noodle’s own way. On this site, moving between pages and switching a preview’s noodle both cross-fade through a view transition.

  • Invoice sent
  • Reminder scheduled
  • Payment received
  • Receipt emailed
Choose a noodle

Minimal

Flat

Pixel

Linear

Thin

Glass

Textured

Bold

Elegant

Rounded

On this page