makepad-ports/skills/makepad-ui-craft/EASING.md
andodeki e09f6bbb51 Add makepad-ui-craft skill pack: Emil Kowalski + Jakub Krehel doctrine translated to Makepad 2.0
- skills/README.md: provenance + index
- SKILL.md: merged design-engineering doctrine, Makepad-native (animator, script_mod!)
- EASING.md: CSS cubic-bezier -> Ease.Bezier translation verified against dev@b41e740
- RECIPES.md: press scale 0.96, toast enter/exit, sheet settle w/ velocity handoff, stagger, reduced motion
- PORTS-AUDIT.md: Before/After audit of rider/koboyo/insurance with file:line refs
2026-09-01 18:36:57 +00:00

3.6 KiB
Raw Permalink Blame History

Easing translation: CSS / Motion → Makepad Ease

Verified against widgets/src/animator.rs on the dev branch (b41e740).

The key fact

Makepad's Ease::Bezier {cp0, cp1, cp2, cp3} is CSS cubic-bezier(x1, y1, x2, y2): cp0 = x1, cp1 = y1, cp2 = x2, cp3 = y2 (confirmed in Ease::map — the x-solve uses 3.0 * cp0, the y-evaluation uses 3.0 * cp1). Every named curve Emil ships translates exactly.

Emil's required custom curves

Name CSS Makepad DSL
Strong ease-out (UI default) cubic-bezier(0.23, 1, 0.32, 1) ease: Ease.Bezier {cp0: 0.23, cp1: 1.0, cp2: 0.32, cp3: 1.0}
Strong ease-in-out (on-screen movement) cubic-bezier(0.77, 0, 0.175, 1) ease: Ease.Bezier {cp0: 0.77, cp1: 0.0, cp2: 0.175, cp3: 1.0}
iOS drawer/sheet curve (Ionic/Vaul) cubic-bezier(0.32, 0.72, 0, 1) ease: Ease.Bezier {cp0: 0.32, cp1: 0.72, cp2: 0.0, cp3: 1.0}
Jakub's polish curve cubic-bezier(0.2, 0, 0, 1) ease: Ease.Bezier {cp0: 0.2, cp1: 0.0, cp2: 0.0, cp3: 1.0}

Built-in equivalents (cheaper to type, slightly weaker)

Intent CSS-ish Makepad built-in Strength
Enter/exit default ease-out OutQuadOutCubicOutQuartOutQuintOutExp pick further right for more punch
Movement/morph ease-in-out InOutQuadInOutCubicInOutQuart
Hover/color ease OutQuad (upstream check_box.rs uses this)
Constant motion linear Linear
Overshoot (rare, gesture-born only) spring w/ bounce OutBack (subtle) — avoid OutElastic/OutBounce in product UI

Banned in UI: InQuad/InCubic/InQuart/InQuint/InExp/InSine/InCirc/InElastic/InBack/InBounce as the ease of anything entering, exiting, or responding to the user. (In* is only legitimate for an element accelerating away as part of a choreographed hand-off, e.g. upstream slide_panel.rs uses InQuad for the outgoing panel.)

The spring stand-in: ExpDecay

Makepad has no parametric spring in the animator, but Ease.ExpDecay {d1, d2} is an exponential-decay settle that reads like a critically-damped spring and is what upstream widgets ship for "alive" motion:

Upstream use Values
file_tree.rs open/close Ease.ExpDecay {d1: 0.80, d2: 0.97} / {d1: 0.82, d2: 0.95}
fold_button.rs / fold_header.rs ExpDecay {d1: 0.96, d2: 0.97} open, {d1: 0.98, d2: 0.95} close
Default {d1: 0.82, d2: 0.97}

Rules of thumb: lower d1 = faster initial move (snappier); d2 shapes the tail. Stay inside d1 0.800.98, d2 0.950.97 unless you can defend the value. No overshoot ever comes out of ExpDecay — for gesture-released momentum with a real bounce use scroll_motion::rubber_band_bounce driven by NextFrame (see RECIPES §4).

Duration reference (Play::Forward {duration} is seconds)

Element Seconds
Press feedback release 0.10.16
Hover 0.00.1
Tooltip/popover 0.1250.2
Dropdown/select/side drawer 0.150.25
Modal/bottom sheet/toast 0.20.5
UI ceiling 0.3 (sheets/modals may reach 0.5)

Play variants: Forward {duration}, Snap (instant — use for press-down), Reverse, Loop {duration, end} (spinners: keep them fast — a faster spinner makes the same load feel shorter), ReverseLoop, BounceLoop.

Keyframes inside a state: timeline(t0 v0 t1 v1 …) / [{time: 0.0, value: 0.0}, …] — reserve for one-shot choreography (the CSS-keyframes analog); plain state targets retarget smoothly and are what interactive toggles should use.