easeul

Reference

Every attribute and every token in one place, generated from the same manifest entry the TypeScript types are.

Two attributes and 7 tokens. That is the whole surface. Anything that typechecks is listed here, and anything listed here has a rule behind it.

Attributes

AttributeAcceptsRequiredWhat it is for
data-motionOne preset id, or two composed ids separated by a space.yesThe whole API. Names the preset that animates the element. The other half of the match is the state attribute the host already writes, so this is the only thing you add.
<Dialog.Content data-motion="scale-fade" />
data-motion-state"open" or "closed".noThe escape hatch for a host that publishes no open and closed pair of its own. Write it from the app and the transition path matches on it exactly as it matches Base UI ending styles. Base UI scroll area needs it, and so does any element you keep mounted and hide yourself.
<ScrollArea.Scrollbar data-motion="fade" data-motion-state={shown ? "open" : "closed"} />

Tokens

Every token is declared on :root inside @layer easeful. Override one in your own unlayered CSS and it wins without !important, which restyles every preset at once rather than one of them.

TokenDefaultWhat it controls
--motion-duration200msEnter duration.
--motion-duration-exit150msExit duration. Faster than enter feels better.
--motion-easecubic-bezier(0.32, 0.72, 0, 1)Enter easing, decelerating.
--motion-ease-exitcubic-bezier(0.4, 0, 1, 1)Exit easing, accelerating.
--motion-distance8pxSmall travel distance for slide presets.
--motion-scale0.96Scale factor for scale-in presets.
--motion-origincenterDefault transform-origin.

Composition

Two presets on one element, separated by a space. The manifest declares which pairs compose and which fight over the same property, and the types accept a pair, so data-motion="fade nonsense" fails to compile.

tsx
<Dialog.Content data-motion="fade slide-up" />

Writing direction

No preset moves along the inline axis today. slide-up is vertical, so nothing in the current vocabulary changes meaning in a right-to-left document.

The decision, recorded before a slide-start or slide-end preset exists rather than after it ships: an inline-axis preset follows writing direction. It will set an inline-axis variable rather than --motion-from-x, and one rule will flip its sign under :dir(rtl). Physical slide-left and slide-right names stay out of the vocabulary, because a name that means "toward the trigger" in one document and "away from it" in another is the ambiguity a closed vocabulary exists to prevent.

The rule itself ships with the first preset that needs it, and not before. A custom property nothing reads is the same broken promise as an attribute that typechecks and matches nothing.