Motion
Interface parts arrive with an ease-out, and things that fall, fall with gravity. Nothing moves just to be seen.
In this chapter 7 pages
Two curves
--ply-ease-out, cubic-bezier(0.16, 1, 0.3, 1). Fast start, long settle. For menus, cards, tooltips, anything arriving.
--ply-ease-gravity, cubic-bezier(0.55, 0, 1, 0.45). Slow start, speeding up. For balls dropping through the machine and anything else that falls.
Durations
| Token | Value | Use |
|---|---|---|
--ply-duration-fast | 120ms | Hover and press feedback, color changes. |
--ply-duration-base | 200ms | Switches, tooltips, small reveals. |
--ply-duration-slow | 320ms | Toasts and panels entering. |
--ply-duration-drop | 900ms | One ball falling the height of the machine. |
The signature moment
Each page gets at most one moment of motion, and it shows a process. On the home page it is the complex growing from bare points into edges and triangles; on a chapter landing it is that chapter's model, such as the probability machine filling on Principles. The machine, small, is also the loading indicator: Loading example
Reduced motion
When the reader asks their system for reduced motion, every animation jumps to its end state. The complex is drawn finished, the machine draws its settled bell curve, the loading indicator shows a resting ball, and transitions take effect at once. The CSS reset handles this globally; scripts check prefers-reduced-motion before animating. Motion the reader starts and needs, such as a step-through animation, can opt out: mark its container data-ply-motion="keep".
The reset does this by giving every change a transition of 0.01ms. A script that changes a style and reads it back at once gets the old value during that transition. Mark an element your script reads that way data-ply-motion="keep", as the Atlas does with the probe it reads its colors from, or finish its transitions before reading (el.getAnimations().forEach((a) => a.finish())), as the top bar does before it measures.