Every canonical GLEE motion primitive, rendered live, beside the ad-hoc values it replaces. This page is the decision surface: a primitive becomes canon by being judged here, not by appearing in a diff.
gm-hover-lift means this surface is interactable and comes toward you,
not "translateY looks nice". It may not write inline motion on an interactive state,
across any of the four channels: transform:, the standalone
scale:/translate:/rotate: properties, transitioning
layout geometry to fake a scale, or JS assigning motion at commit time. Need something
new? Prototype it here, get it judged, add it to glee-motion.css with its
contract — then use it. Adding a primitive is a deliberate change to the design
system. Inventing one inline is drift.
.entry.nudge { scale: 1.09 } on an overshooting spring, held. Stage 2,
550 ms later in JS: animate width/height/top/left
toward ~1.85× and shove the neighbours. Two magnitudes for one gesture, two owners of size
on one element, spread across three channels chosen specifically to avoid fighting the
entrance animation for transform. Every decision defensible; the result is
"a slight enlargement followed by another excessive zoom."
Every one of these was a locally reasonable edit. Together they are six answers to one question. Hover each.
6 vocabularies for one gesture · 22 hardcoded durations across two stacks · 0 shared motion tokens between them.
And the compound failure — an ancestor that scales wrapping a descendant that also scales. The one transform owner rule exists for this:
Captain's test: a −3px lift that feels excellent on a project card can feel absurd on a 28px control and invisible on a 400px scene object. Hover every cell in both rows. Row 1 is one magnitude everywhere. Row 2 is the same semantic primitive with a magnitude register. The numbers under each cell are the lift as a share of the element's own height — that ratio is the measurable part of the argument.
| tiny control · 28px | card / post · 142px | scene object · 400px | |
|---|---|---|---|
| one magnitude gm-hover-lift everywhere |
a post |
scene / media object |
|
| magnitude register same meaning, sized to fit |
a post |
scene / media object |
If row 1 reads consistently across all three,
the canon stays one magnitude and .gm-subtle / .gm-emphatic come
back out. If it does not, the registers stay — and the thing that must never come
back is a component picking its own number.
gm-camera-live to drop the
transition and track input exactly.Both stacks already carry a blanket
transition-duration:0.001ms!important under this query, so timing is
handled — everything lands instantly. What a blanket rule cannot do is remove the
displacement: translateY(-3px) still moves the element, just instantly,
which is a jump rather than a motion and is worse. That is the one thing the canon adds
here. State stays fully visible — shadow, border, colour and progress width all still
change, they just no longer travel. The binding test is your OS setting, captured in the
UVP run; this page does not simulate it, because a simulation would be a second copy of the
rule and would drift from it.
| Primitive | Reduced-motion equivalent |
|---|---|
gm-hover-lift / gm-hover-object | no displacement; shadow + border still respond, instantly |
gm-press | no displacement; the press is still felt through :active colour |
gm-focus-ring | unchanged — it never moved |
gm-expand | opens instantly; content still reveals |
gm-enter / gm-exit / gm-route-in | animation none, opacity 1 — content appears, does not travel |
gm-drop-settle | animation none — the drop is instant and final |
gm-pending | steady 0.7 opacity, no pulse |
gm-progress | width still changes — it is information, not decoration |
gm-camera | the jump is instantaneous |
gm-dragging | exempt — carry must keep tracking the pointer or dragging breaks |
| Token | Value | Job |
|---|---|---|
--gm-instant | 80 ms | state flips: colour, border, focus ring |
--gm-quick | 140 ms | pointer feedback: hover, press |
--gm-standard | 280 ms | structure: open/close, expand, dock, route |
--gm-slow | 450 ms | space: camera, semantic zoom, lock-in settle |
--gm-ease-precise | cubic-bezier(.2,0,.1,1) | mechanical — the default |
--gm-ease-alive | cubic-bezier(.23,1,.32,1) | decelerating reveal |
--gm-ease-exit | cubic-bezier(.4,0,1,1) | accelerating leave |
--gm-lift-subtle | −2px | register: controls and dense rows under ~48px (.gm-subtle) |
--gm-lift | −3px | register: cards, posts, panels — the default |
--gm-lift-emphatic | −6px | register: media and scene objects over ~280px (.gm-emphatic) |
--gm-nudge | 2px | directional hints, list-row shift |
--gm-rise | 6px | entrance travel |
--gm-scale-up | 1.015 | grabbable objects only |
--gm-scale-down | 0.985 | press |
--gm-scale-pickup | 1.02 | held for drag — reads bigger than hover on purpose |
--gm-shadow-lift / --gm-shadow-drag | elevation | paired with lift and pickup |
140 ms is the median of the two dominant legacy values (120 ms, 12 uses; 160 ms, 15 uses), so converting either way is a sub-perceptual change. Character is taste; responsiveness, target size, transform stacking, accidental layout movement and consistency across component sizes are measurable — ratification is judgment plus the adversarial tests, not a demo that looks good. Retune the values here and the whole estate follows; the architecture does not move.
This page is the executable specimen of a capability; an agent
needs the same vocabulary without eyes. capability.json
is generated from glee-motion.css by the same command that builds the
platform mirror, so it cannot drift — ship lint blocks if it is stale, or if any
primitive lacks a stated meaning. It answers the six questions a capability must answer
about itself:
| question | field |
|---|---|
| What are you? | what_are_you |
| What can I do with you? | what_can_i_do — every primitive with its meaning |
| How do I invoke you? | how_do_i_invoke — per stack, plus the magnitude registers |
| What happens when I do? | what_happens — the resolved token values |
| What are your limits? | limits — composition rules, the four channels that count as inventing motion, reduced-motion behaviour, how to add a primitive |
| How do I prove I used you correctly? | how_do_i_prove_i_used_you_correctly — the specimen, the lint rule that blocks drift, and the publication verifier |
It also carries "ratified", read from
the canon rather than asserted — an agent learns the values are still candidate without
having to trust a human's summary. Doctrine:
doctrine/EXECUTABLE_CAPABILITY.md.