The four token groups
1
Explicit overrides
--tm-duration, --tm-delay, --tm-easing, --tm-iteration-count,
--tm-exit-duration.Deliberately unset, so each motion class keeps its own tuned default.
Set them per element with tm-duration-*, tm-delay-*, tm-ease-*,
tm-repeat-*, the Tailwind plugin, or an inline style. An explicit value
always wins.2
Profile factors
--tm-duration-scale, --tm-emphasis, --tm-overshoot.Inherited multipliers. A motion personality retunes a whole subtree by
changing these, and each animation keeps its relative character because it
multiplies its own default rather than being replaced by a global one.3
Shared scalars
--tm-distance, --tm-stagger-step.One value each, already shared by every class that uses them, so a profile
sets them outright rather than scaling them.4
Role easings
--tm-ease-entrance, --tm-ease-exit, --tm-ease-interaction,
--tm-ease-morph, --tm-ease-emphasis.Classes opt into a role; a profile retunes the role. Animations with a
character of their own — linear spins, ambient drifts — stay out of this and
keep their literal curve.How a class reads its duration
Every animation in the library is written this way:- With nothing set, the class runs at its own tuned default — 260ms here.
- An inherited profile changes
--tm-duration-scale, so the class scales.tm-popat 380ms andtm-fade-inat 250ms stay in proportion. tm-duration-300sets--tm-durationon the element, which replaces the wholecalc(). Exactly 300ms, not 300ms times anything.
:root never carries a value for --tm-duration, --tm-delay, --tm-easing
or --tm-iteration-count. A root value would shadow every per-class default in
the library — which is exactly the bug 0.7 fixed, and which npm run check
now guards against.The override order
From weakest to strongest:
Profiles are plain inherited custom properties, so “nearest wins” is just CSS
inheritance — there is no resolution logic to learn.
Element-level utilities beat a profile on the same element too, because they
are declared later in the stylesheet than the profile classes and carry
!important when generated by the plugin.
Easing roles
Entrances and exits both use an ease-out family. An ease-in exit delays the
visual feedback and reads as sluggish; the exit is made quieter by being shorter
and travelling less, not by starting slowly.
tm-ease-* on an element overrides whatever role a class uses.
Emphasis and overshoot
Two separate factors, because “smaller” and “no bounce” are different requests.tm-motion-calm sets --tm-overshoot: 0 and --tm-emphasis: 0.55: the pop
still grows into place, it just stops springing.
Which one do I want?
Keyframes or transitions?
The distinction decides whether a class can be interrupted.
That is why presence exists as its own category rather than being a pair of
entrance and exit keyframes: a menu the user changes their mind about halfway
has to reverse from where it is, not snap back to where it started.
Two keyframe classes cannot be applied to the same element — whichever comes
later in the stylesheet wins
animation-name, not both. That is a property of
CSS animations, not of this library. Transitions do stack, so tm-press and
tm-hover-lift together are fine. See Transform safety.