# 09 · Motion Five behaviours cover everything that moves, each with a curve, a duration band and the properties it may touch. One rule sits above all five: nothing bounces. *Updated 2026-08-09. Source: https://brand.haux.studio/motion* ## Nothing bounces One prohibition governs the whole system, and it has no exception band. Read it before the five behaviours, because it constrains all of them. Nothing bounces. No elastic, no overshoot, no back-easing. An overshoot reads as a visual second guess: the element arrives, changes its mind, and corrects. The claim this studio makes is that the thinking happened before the work shipped, and a bounce contradicts it every time it plays. There is no exception band on this rule. If a transition feels flat, it gets a shorter duration or a sharper curve, never a wobble. That constraint is also why the five curves below are all monotonic. Every one of them arrives and stops. > Arrives fast. Lands soft. Holds still. > > — Settle, which is the shape of the whole system ## Settle, Strike, Hold, Track, Trim Anything that moves is one of these five. A transition that fits none of them is a transition that has not been decided yet. #### Settle Every entrance. ##### Specification Curve: cubic-bezier(0.16, 1, 0.3, 1) Duration: 620ms for a hero, 420ms for a section, 280ms for a card May touch: transform and opacity only, 24px maximum travel, 45ms converging stagger ##### How it behaves Arrives fast, lands soft, holds still. Most of the distance is covered in the first third of the duration, and the remainder is the part that reads as considered. Travel is capped at 24px. Beyond that the element stops arriving and starts flying in, which is a different and much cheaper effect. The stagger converges: 45ms between the first items, tightening across the sequence so a list of twenty does not take four times longer to resolve than a list of five. *The tell: If it is still moving when the eye reaches it, the duration is wrong.* #### Strike State change and feedback. ##### Specification Curve: cubic-bezier(0.55, 0, 0.1, 1) Duration: 180ms standard, 120ms pressed May touch: colour, transform, opacity. The only behaviour allowed to touch orange. ##### How it behaves No wind up. The response starts on frame one, because anything else is read as lag rather than as animation. Strike is the only behaviour permitted to bring in the accent. A hover, a press, a selection and a validation error are the moments orange is earned. At 120ms the pressed state is nearly instant on purpose. Feedback that waits is feedback that gets pressed twice. *The tell: Frame one has already changed, or it is not Strike.* #### Hold Ambient, background, always running. ##### Specification Curve: linear, or a sine loop Duration: 12 to 30 seconds per cycle May touch: transform and opacity, at very low amplitude ##### How it behaves The loop has no visible join. A loop point that can be spotted becomes the only thing on the page anyone looks at. Amplitude is set so low that two screenshots taken a second apart look identical. The motion should only be perceptible over the time it takes to read a paragraph. Hold is the behaviour most often specified too strongly. If a reviewer says the background is moving, it is moving too much. *The tell: Two screenshots a second apart are indistinguishable.* #### Track Continuous response to pointer and scroll. ##### Specification Curve: critically damped lerp, never 1:1 Duration: continuous, no fixed duration May touch: transform only, 40px maximum parallax ##### How it behaves The lag is the character. An element pinned exactly to the pointer feels attached to the mouse; one that trails and settles feels like it has mass. Critically damped means it catches up and stops, with no oscillation on arrival. That constraint is what keeps Track inside the no-bounce rule. Parallax is capped at 40px of travel across the whole scroll range. Past that the depth stops reading as depth and starts reading as drift. *The tell: Nothing follows the pointer exactly, and nothing overshoots on arrival.* #### Trim Every exit. ##### Specification Curve: cubic-bezier(0.4, 0, 1, 1) Duration: 160ms, and always shorter than the matching entrance May touch: transform and opacity only ##### How it behaves Removed, not dismissed. An exit that takes as long as its entrance makes the interface feel reluctant to let go of something the person has already finished with. The curve accelerates out, so the element leaves rather than fades in place. Attention returns to the page before the animation has finished. One number to remember: if the entrance was 280ms, the exit is 160ms, not 280ms played backwards. *The tell: The exit is shorter than the entrance, every time.* *Curves are written as CSS values so they can be pasted rather than retyped. Retyping a bezier is how a system ends up with four curves that are almost the same.* ## The contract, published rather than implied A reduced-motion setting that quietly disables an interface is worse than one that keeps every control responding. This is what each behaviour becomes when the preference is set. ### What each behaviour becomes Settle becomes a 120ms opacity fade. No travel, no stagger, no transform. Strike keeps its full timing. It is feedback rather than decoration, and removing it removes the confirmation that a control was pressed. Hold stops. An ambient loop has no function beyond atmosphere, so it is the first thing to go. Track goes static. Elements sit at their resting position and nothing follows the pointer. Trim becomes instant. There is no value in animating a removal that the person has already decided on. WebGL renders one frame and freezes. The scene still appears, and it stops being a scene. Publishing the contract is the point of this module. Most systems implement a reduced-motion branch and never write down what it does, so nobody can check it and nobody can rely on it. Keeping Strike at full timing is the decision most often got wrong. Stripping feedback along with decoration leaves an interface that appears not to respond, which is a worse outcome than the motion it removed. *Every behaviour above is testable in a browser with the preference on. If it has not been tested with the preference on, it has not been tested.* ## The four ways this system gets broken Each pair below is a default that arrives with a library rather than a choice anyone made. Defaults are where a motion system leaks. **Nothing bounces, including at small amplitudes.** - Do: Settle at 420ms on cubic-bezier(0.16, 1, 0.3, 1). It arrives, lands and holds. - Don't: A spring with 8% overshoot on the same element. Small enough to argue about, large enough to read as a correction. **Exits are shorter than entrances.** - Do: Trim at 160ms against a 280ms card entrance. Attention returns to the page immediately. - Don't: A 280ms fade out mirroring the entrance. The interface reads as reluctant to close. **Strike starts on frame one.** - Do: Press state at 120ms, with the change already visible on the first frame. - Don't: A 60ms wind up before the state changes. It is imperceptible as animation and perceptible as lag. **Track lags on purpose.** - Do: A critically damped lerp, capped at 40px of parallax across the full scroll range. - Don't: An element pinned 1:1 to the pointer. It feels attached to the mouse rather than to the page. ## What is not specified yet Five things are undecided, and three of them will be decided by whoever builds the next project unless they are settled first. The curves are not in the token file. They are retyped per project, which is how a system ends up with four beziers that are nearly identical and none of them canonical. Route transitions are unspecified. Nothing here says what happens between two pages, and that is the most visible motion decision on any site. Loading and skeleton behaviour is undecided. It is the one case where an ambient loop has a job, which makes it a genuine exception to Hold rather than an oversight. Video and WebGL carry no rules beyond the reduced-motion freeze. Autoplay, looping and first-frame posters all need a position. Sound has never been discussed. That is a decision, not an omission, and it should be written down as one. *The first two gaps are worth closing before the next build. Both get decided by default the moment someone opens an editor.*