# 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` | `OutQuad` → `OutCubic` → `OutQuart` → `OutQuint` → `OutExp` | pick further right for more punch | | Movement/morph | `ease-in-out` | `InOutQuad` → `InOutCubic` → `InOutQuart` | | | 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.80–0.98, d2 0.95–0.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.1–0.16 | | Hover | 0.0–0.1 | | Tooltip/popover | 0.125–0.2 | | Dropdown/select/side drawer | 0.15–0.25 | | Modal/bottom sheet/toast | 0.2–0.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.