
An animated page has five motion layers, and a brief that names none of them gets the same default on all five: fade in, slide up 20px, cubic-bezier(0.4, 0, 0.2, 1), 150ms, on every section, forever.
That treatment is not ugly. It is anonymous, which is worse, because a reader who has seen it forty times this month stops reading the page and starts categorising it.
Specify the layers separately and three of the five turn out to be empty. That emptiness is the design.
The five layers#
Motion on a page is not one decision, it is five, and they have almost nothing to do with each other. Generated pages treat them as one, which is why the same curve ends up on a button and a hero.
- Entrance. What happens the first time an element enters the viewport. This is the layer that gets overused.
- State. Hover, focus, pressed, disabled, loading. Short, functional, and the only layer that must exist on every interactive element.
- Transition. What happens between two views: a route change, a tab, an accordion, a modal.
- Ambient. Anything that moves without being asked: a gradient drift, a marquee, a canvas field, a looping video.
- Scroll driven. Progress tied to scroll position, pinned or not.
A directed page usually fills two of those five properly, keeps the third minimal, and leaves two empty. The ambient layer in particular is empty on most good pages and full on most generated ones, because a moving background is the cheapest way to make an undecided design look like something is happening.
The useful exercise is to write the five down and then say, for each, what it will contain. Three quarters of the time the honest answer for the ambient layer is nothing at all, and writing nothing next to it is what stops a model inventing a drifting mesh gradient to fill the space. Absence has to be stated to survive, which is the part of a directed brief that almost nobody writes.
The animation inventory#
Here is each layer with the mechanism and the values, at the level of detail a model can act on without guessing.
| Layer | Mechanism | Values |
|---|---|---|
| Entrance, headline | clip-path inset mask rising, per line | 680ms, cubic-bezier(0.22, 1, 0.36, 1), stagger 0.08 |
| Entrance, sections | opacity and 16px rise, once only | 520ms, cubic-bezier(0.25, 1, 0.5, 1), trigger top 75% |
| State, button | translateY(-2px) and border lightens | 140ms, cubic-bezier(0.4, 0, 0.2, 1) |
| State, input focus | 2px ring, no movement | 120ms, linear |
| Transition, modal | 12px rise, scale 0.98 to 1 | 260ms, cubic-bezier(0.34, 1.56, 0.64, 1) |
| Transition, tab | content crossfade, no layout change | 180ms, both directions |
| Ambient | nothing | no marquee, no drifting gradient, no autoplay loop |
| Scroll driven | one pinned sequence, hero only | +=240%, scrub: 1, ease: "none" |
Read the ambient row as the most important one in the table. It is the row that keeps a page from looking like a screensaver, and it is the only row whose value is a refusal. A page with a still background and one moving headline reads as more expensive than a page where four things drift.
Two rows carry a detail worth copying. The input focus row moves nothing at all, because a field that jumps when you click into it fights the act of typing, and a 2px ring at 120ms communicates the same state without touching layout. And the tab row says no layout change on purpose: a crossfade between two panels of different heights is the most common cause of a page that jitters while a reader is looking straight at it. Fix the container height, then fade.
One signature moment beats six effects#
The mechanism that carries a page should be a single thing, described precisely enough that a model builds it rather than approximating it. A masked line reveal is a good default because it is cheap, it works on text of any length, and it does not move layout.
.line { overflow: clip; }
.line > span {
display: block;
transform: translateY(105%);
transition: transform 680ms cubic-bezier(0.22, 1, 0.36, 1);
}
.is-in .line > span { transform: translateY(0); }
.line:nth-child(2) > span { transition-delay: 80ms; }
.line:nth-child(3) > span { transition-delay: 160ms; }105% rather than 100% is the detail that makes it work: descenders on a g or a y sit below the baseline, and at exactly 100% a sliver of them shows above the mask before the line moves. That is the kind of value a brief has to carry, because no adjective produces it and every reader sees the result.

Six effects on one page cost more than one effect and buy less, because a reader cannot tell what matters when everything is competing. Pick the moment that belongs to the product, spend the specification there, and hold the rest of the page still. That is the same principle as spending the whole design budget on one section.
Budgets, so it still feels fast#
Five constraints keep animation from costing what it usually costs, and all five belong in the brief as values.
- Transform and opacity only. Animating
width,height,toporbox-shadowforces layout on every frame. A shadow that must animate becomes a stacked pseudo-element whose opacity changes instead. will-change: transformadded on the element that is about to move and removed when it stops. Left on permanently it holds a compositor layer for every element that has ever animated.- A
16.7msframe budget, checked with the CPU throttled four times. A sequence that holds sixty frames unthrottled on a laptop tells you nothing. - Reserved space.
aspect-ratioon every image and an explicit height on the canvas, so no entrance animation is competing with a layout shift. - One reduced motion branch.
prefers-reduced-motion: reducesets final states and disables the pin, rather than being ignored.
The throttling line is the one that changes decisions. Under a four times CPU throttle a page with eight simultaneous entrance animations drops frames on the first scroll, and the fix is never a faster animation: it is fewer of them. Most performance problems in generated motion are quantity problems wearing a technical disguise.
The reserved space line pays for itself differently. An entrance animation on an element whose image has not sized itself yet produces two movements at once, the intended one and a layout shift, and a reader reads the pair as the page being broken. An aspect-ratio on the wrapper costs one line and removes the second movement entirely, which makes the first one legible.
The verdict on animated pages#
Name the five layers, fill two, refuse two, keep the third short. That is the whole method, and it produces pages that look unrelated to each other even when the underlying values came from the same place, because the refusals differ even when the mechanisms do not.
Two tests tell you whether a brief is a specification or a mood.
- Count what moves. Open the page, sit still, and count the things moving without being touched. One is a signature. Two is a style. Four or more is a default that nobody chose, and readers spot it from the first screen, often before they have read a single word of the page.
- Substitute the product. Take the motion description out of the brief and put any other company's name in it. If the description still fits, it was never a specification, and it will return the same page for everybody who writes one.
A brief that survives both has numbers in it. There is no third way to pass.
Thanor is a subscription library of these briefs: full art direction per design, in exact values, covering motion layers, three dimensional scenes, animated backgrounds, page sections and app interfaces. $89 for three months, $189 a year, $299 once, and free accounts open the designs marked free.
Browse the Thanor library and read one design's motion inventory, the rows that say nothing included, before you write your own five layers. For the per element numbers there is the motion brief, and one free design shows how the refusals are written.
Questions this raises
What does an AI animated website get wrong by default?
It animates everything. Every section fades and slides up on entry at the framework's stock 150ms curve, which means the effect was applied by nobody and carries no meaning. A directed page moves two or three things and holds the rest still.
How many animations should one page have?
One signature moment, a consistent entrance treatment for headings, and hover states on interactive elements. That is three layers. Pages with six read as a demonstration of a library rather than as a product.
Is CSS or JavaScript better for website animation?
CSS transitions for state changes and hovers, because they run off the main thread when they only touch transform and opacity. A timeline library for anything sequenced or scroll driven, where you need ordering and progress rather than a single change.
How do I keep animation from slowing the page?
Animate transform and opacity only, never width, height, top or box shadow. Set will-change on the element right before it animates and remove it after. Reserve the space with aspect-ratio so nothing shifts.
Part of
Build it with AI
Three dimensional scenes, scroll driven motion and animated pages, built by handing a model a specification instead of an adjective. What to write, and what comes back.
