Every specimen on this page has to be operated. A screenshot shows a frame and never a movement, so this is the one foundation the kit cannot print. Movement here does exactly three jobs, and a moment that cannot be given one of the three does not get a moment. The product had five durations before this stage and not one of them was a literal, so the work was never tidying loose numbers. It was finding one job wearing four of them.
The job is named before the animation is chosen. A moment for which none of the three can be named does not enter the inventory, and so it never gets movement. "It livens up the interface" is not a job. This is the same rule that throws an orphan feature out of a To-Be map and an orphan block out of the IA bank.
Connection: where did that come from
The surface arrives from the edge it will leave by, so the direction is
the answer to the question. This is sheet-rise at stand scale, and in the
product it carries 337 dialogs on 105 screens.
the page the sheet is over
Response: I was heard
A real control, taking its transition from the system rather than from
this page. Hover it and hold it down. 18 of the product's 20 response declarations
cross-fade a colour, a border or a ground, and two move something. The second specimen
wore btn-quiet until 2026-08-18 and no such face exists, so it took the
browser's own default button, grey 239 in Chromium and 192 in WebKit, and answered nothing:
rest, hover and press read identical on every channel. The transition was there the whole
time, five properties at .16s off .btn, which is why no reading of movement
could see it. It is btn-secondary now, the face the product stands beside a
primary 196 times, 530 counting the provider buttons that add to it.
Status: it is still working
It was not in the product until 2026-08-15, and this section said so
for one step. The skeleton stood 482 times on 19 screens and every one was a flat box, so
the only job about waiting was performed nowhere while nineteen screens were about waiting. It
is sk-pulse now, an opacity cycle in skeleton.css, and
the specimen below is the product's own rule rather than the stand's. A pulse is also the
one thing a shorter duration makes worse: 1ms per period at infinite repeats is a flicker,
so less motion REPLACES the cycle with a still tone instead of speeding it up. That is the
first case the token override cannot reach, and it was found by writing the rule rather than
by auditing for it.
The stage asks for three, named by job. The inventory found two jobs with movement in them. The middle one, a change inside a component already on screen, has exactly one member: a chevron turning over when its market opens, which is feedback for a click and therefore a response. A third rung would have sat 20ms from the one above it. It arrives the day a row asks for it, said out loud, and not before.
Run them together. Separately they read the same; the difference only exists in the comparison, which is why they are on one button.
.16s is 10ms over the guideline and it was taken
knowingly. A response slower than roughly 150ms stops reading as an answer and starts
reading as lag, but that ceiling is about a control that MOVES under a finger, and 18 of the 20
here are a colour fade. The other direction was one line in tokens.css and
9,528 slots following it, which is the whole argument for the number living in one file.
What the product rendered before this stage
One role wore four of them. A hover is 160ms on a button, 180 on a photo tile, 250 on a trust plate and 300 on a card. Nobody decided that, and reading the files one at a time never shows it: the readings have to be grouped by job before the drift has a shape.
Same comparison, one duration, three shapes. The third lane is
linear and it is here as the control: if the two above it looked alike you would
have no way to know whether the page was working.
And linear is the one bare keyword the system keeps, in one
place, with its reason beside it. The scroll edge fade binds a mask to distance rather than
to time, so the identity is the only correct function there and anything else runs the fade
ahead of or behind the thumb. A --ease-linear read once, whose value can never be
anything else, is a name for a constant. The stage published "0 bare keywords" and the true
number is 1: its own check read transition: and animation: and
never looked at the longhand. The second instrument is what found it.
95.6 per cent of the product's easing read no
token at all. 12,821 of 13,406 rendered slots are the bare keyword ease, typed
out 54 times in the source and defaulted the rest. That is where this stage found its drift,
and not in the durations. --ease-standard keeps the same value on purpose: renaming
a curve is free, redrawing one changes how every hover in the product feels, and no row of the
inventory asked for that.
There is no --ease-exit. Nothing in this product animates
a departure: a dialog closes at once, the condensed category band collapses through the rule
that opened it, and no state anywhere in the painted tree has a leaving face. A token with no
reader fails the idle control as loudly as an undeclared case. It arrives with its first exit.
No springs and no overshoot, and that is a default rather than a taste: they read as "something went wrong" in precisely the states where a person least wants to be asked a question. One arrives with a row of the inventory that asks for it.
The stage asks for --move-sm and --move-md. This
system answered that question on 2026-08-13 with a census and a different shape, and adding the
pair now would be a fourth answer to a settled question.
Of the system's 20 transform declarations, only 5 are
movement. Nine are resting geometry, a chevron drawn by rotating a square and a knob centred
on itself; two are a state that carries meaning rather than motion; the rest are the harness
drawer and two keyframes. So *:hover{transform:none} under reduced motion would
have flattened .market-chevron for exactly as long as a pointer rested on it, and
an open market would have pointed the wrong way.
When a family cannot be selected, put the switch in the value the
declaration already reads. The five distances are 3px on a card, 2 on a badge and 1 on a
provider button, which are three decisions and not a ladder, and each stays in the file that
makes it. Every one multiplies itself by --motion, which is 1 normally and 0 under
reduced motion, so a distance that no selector could reach is turned off by a number.
A state is a PAGE here, so state transitions do not exist. Both trees hold one document per state, 105 each, by a convention five stages older than this one. Loading, empty, error and success are not conditions a screen moves into, so there is no before and after inside one document to move between. The one state family that does live inside a document is the dialog, which makes the tone check a check of five surfaces rather than of twenty.
The tone check: four quantities against the line the screen says
The tone is set by voice.md and this stage does not get to choose it. Duration, curve, amplitude, direction, each named separately and put beside the microcopy of the same state.
0 disagreements, and the microcopy was not edited. The row that could have gone wrong is the win, and it did not, because the rule that would have broken it was written five stages ago and the outcome dialog never had a face of its own.
Patterns: 0 of 6, and the zero is what the rung IS
Read from the comment-stripped source of all six files: not one declares a selector that toggles at run time. Five have no state selector at all, and the sixth has a structural presence check rather than a state that changes while a person looks at it. The composition never recomposes, so an arrangement that never changes has nothing to move between. This is the invariant that keeps colour out of the rung, seen from the other side.
The crossing between documents: A, and it was measured before it was chosen
Every screen is a separate document, so the move from a feed card to its
detail is a navigation and an ordinary transition cannot see it. Two test documents
carrying @view-transition { navigation: auto }, navigated for real, asking whether
pagereveal arrived with a transition attached:
The answer is A, do not animate the crossing. Four reasons, in order. A cross-fade points at no source, and the connection job is defined as showing what came from what, so it would be movement with no inventory row behind it, which is the one thing this stage cuts. Doing it properly is a markup change in two trees: a shared name on the card and on the detail head, unique per document, on a feed that ships up to nine cards, so the name has to be written at click time. It would add an obligation the tokens cannot reach, since a view transition reads no duration token. And on the path this repository actually reads from, one engine of two would not show it, so half the reviews would be of a feature the reviewer cannot see. The line is one line, the measurement is banked, and the decision does not have to start from zero if the named pair is ever built.
Two more rulings, both no
A rung does not animate its own rebuild. Two surfaces qualify for the exception, the bet panel arriving at DETAIL and the sub-category rail at RAIL, and both are refused on the same ground: they arrive only while somebody drags a window edge. A person on a desk never sees the arrival and a person on a phone never has it. An entrance nobody can be present for is not a connection, it is a frame that happens to differ.
The scroll edge fade stays, and stays out of the register. It binds a mask to the element's own scroll rather than to time, reads no duration token, and is driven by the reader's own hand: it changes only while they scroll and by exactly as much. Measured at three scroll positions in both engines and both settings, the left edge closes from 0 to 32px and the right opens to 100 per cent, including under reduced motion where the blanket net forces the duration to 1e-05s, because a scroll timeline takes its progress from distance and not from that duration. Movement a person makes themselves is not movement done to them, so it needs no reduced-motion treatment and gets none.
And the first reading of it was wrong in this stage's signature way.
The probe set scrollLeft and read the mask in the same task, and reported it
identical at every position in both engines, which reads exactly like a feature that does not
work. Two frames later the same probe reports it moving in all four combinations. A scroll is
not a paint, any more than a resize is a layout.
transform and opacity are the two properties a
browser animates without laying the page out again. Everything else makes it do the work on every
frame, and the device that can least afford it is the one most people are holding.
transition: all is 0 and will-change is 0, and both were
0 before this stage as well. Eight expensive properties were found: two converted, one refused
with its reason, five kept with a count.
leftnow translateX(16px). 22 minus 6 is 16, and the geometry is identical to the pixel: the knob's left edge reads 6 off and 22 on, in both engineswidthnow scaleX(1.75), because 28 over 16 is exactly 1.75, so two dots of every three are drawn undistorted. The cost is named: the selected dot's end caps go from a 2px radius to 3.5 on a bar 4px tallmax-heightthere is no transform that removes a box from the flow, and this band's whole job is to stop taking room. The cost is one element, once: one band per document, relaid for 250msbox-shadow on button, card, hero, icon button, inputit loads painting rather than layout, and the case against it is twenty cards painting at once. All five sit on the element's own hover or focus, so at most ONE element in the document animates a shadow at a time. A pointer is in one placeBoth conversions were misread once before they were believed. The
toggle read translateX(0) in both faces, then 0.56px in one engine and 3.82 in the
other, because the probe was reading a transition in flight; finished first, both read exactly
16. The dot read transform: none on all nine of its placements on the how-it-works
screen, which looks exactly like a rule that failed to apply, and those nine render at no
width at all: they live inside the shut dialog. Where six of them do render, the selected one
reads matrix(1.75, 0, 0, 1, 0, 0). A computed value on a box that was never laid
out is not a reading of the rule.
Not a product setting and not a preference: the operating system already
carries the answer, and for some people this is nausea and migraine rather than taste. The
mechanism is one block that redeclares the tokens. Every component reading a
var() obeys without knowing the block exists, and so does the component nobody has
written yet.
Reducing motion never cancels a state. What appears still appears; the movement goes and the arrival stays. An element that stops appearing at all under the setting is a worse defect than any amount of unnecessary animation.
less motion is on: the sheet still arrives, the pulse is a still tone
the sheet arrives either way
This switch is a demonstration and not the check. It redeclares the two tokens on a scope instead of on the root, so what you see is the mechanism working. It cannot tell you about a rule that reads no token, and a rule that reads no token is the entire thing the real reading is looking for. That reading is taken with the browser emulating the system setting, on every element of every document.
1ms and not 0s, because a duration of zero removes the transition
rather than shortening it, so transitionend never fires and a script waiting on it
stalls. 1ms keeps the event and removes the movement, and it is the value the check looks for:
anything above 1ms is a rule that is not reading these tokens.
@media works here and could not at Responsive, and that is
not a contradiction. A width query cannot READ a custom property, which is why the rung
ladder is kept as a registry. This query does not read one, it redeclares one inside itself.
Asking a media query for a variable's value and redefining a variable within a media query are
opposite operations, and confusing them is expensive.
The blanket net on * was hiding this check, and it is
gone. base.css carried one from before this stage. Measured with it in place
over 163 documents in two engines: 6,555 moving elements in the normal pass and 115,028 under
the setting, the difference being every element in the document taking a duration from that
one rule, with 0 rows above 1ms of 230,056. Under !important on *, a
component that reads no token is indistinguishable from one that reads every token. A sweep
that cannot fail is not a sweep.
Taken off and re-measured, control first. An injected element carrying
transition: opacity 999ms linear, which reads no token of ours, read back
999ms in both engines, so the probe can see a rule that does not obey. Then the same 163
documents with the setting emulated: 1,392 elements above 1ms, and 0 of them in the
system. Every one was the STAND, a .15s literal in _page.css
reaching 690 elements per engine over ten kit pages, plus the demonstration pulse on this page.
Both were repaired at source rather than covered, and the second reading is 0. Every
rule in the system obeyed on its own, which is the fact the net had been standing on top of
for as long as it existed. It is not put back: its only remaining argument was code that does not
exist yet.
And the state survives, measured. Three sheets opened at 390px, settled,
both engines, both settings: in the normal pass they are climbing at 21 and 25 per cent 60ms in;
under the setting they read translate: 0 0%, arrived, opacity 1. 0 states lost.
The skeleton is the same answer from the other side: the marks stop moving and stay fully opaque
rather than disappearing.
What the tokens cannot reach is three things and they are written down. A cycle, which needs REPLACING rather than shortening because 1ms per period is a flicker. The grey tree, which links no stylesheet at all, so no token declared anywhere reaches it: measured with the setting on, 105 elements above 1ms over 105 of 105 documents, and the honest answer there is a declared boundary difference rather than 105 copies of a media query. And a cross-document view transition, which is one of the four reasons there is not one.
Corpus: all 105 grey screens, the flows, and every state selector in the system. Each moment carries exactly one job, and a second rule applies on top of the first: the state has to already exist. Movement is laid on the states the Tokens and Components stage built; it does not invent them. A moment whose state is missing is an order on that stage, not a class drawn here that the product will never wear.
Two rows say the state is missing, the toast arriving and Load more appending, and both are orders on another stage rather than work for this one. 35 moments over 47 components is under one each, so nothing here is decoration wearing a job's name.
The full record, both halves of the transcript and the reasons behind every number, is in docs/motion.md. Width has its own foundation next door: responsive, and the ruling about whether a rung animates its own rebuild is taken there and recorded here.
text-transform, and 14 matches for "animation" there are 0 declarations. The
scroll fade read identical at every position until it was given two frames. The toggle knob read
0 in both faces, then 0.56 and 3.82, because the probe was reading a transition in flight. And the
step dot read transform: none on nine placements that render at no width at
all.transition at all contributes no rows.
Reported by a person looking at the category strip. The inverse question, does the element that
CHANGES a painted property carry that property in its own transition-property, gave
77 declarations and 20,864 property-on-placement readings changing with nothing in
between, and the repeating shape was an ATOM writing no response while some of its FACES wrote
their own: chip answered on two faces of five, iconbtn on four of six,
and .btn-provider re-declared the list to add a transform and lost
color and box-shadow, because a transition declaration replaces the
one it out-specifies rather than extending it. One declaration on the atom, carrying the
union of everything any face changes.::details-content fades
from opacity:0, and a box under full opacity IS a stacking context, so for the
length of the arrival the filter panel's --z-menu was resolved inside that group
instead of against the page. 14 of 14 panels on 6 screens opened UNDER the first card, in
Chromium and WebKit both, and jumped on top when the fade landed. Reported by a person on
a screenshot, which is the fifth time. The header's two menus were already clean and that is
the fix: they open inside a sticky header that is its own lifted group, 0 of 5 each. The
lift belongs to the element the fade turns into a context, not to the panel inside it.