-
Notifications
You must be signed in to change notification settings - Fork 9
Guide Animation
Every stateful composite (SelectState, ModalState, PopoverState, TooltipState, DropdownMenuState, ComboBoxState) owns an AnimatedVisibility field. The renderer reads its progress; the orchestrator drives the timer.
You don't usually write animation code yourself. Just call state.update(cx, |s, _| s.open()) / close() / toggle() — the renderer handles the rest.
state.update(cx, |s, _| s.open()) // user clicks trigger
→ AnimatedVisibility::show() // target = true
→ orchestrator advances progress 0 → 1 over enter_config.duration
→ renderer paints enter animation (fade / slide / scale)
state.update(cx, |s, _| s.close()) // user picks / presses Escape
→ AnimatedVisibility::hide() // target = false
→ orchestrator advances progress 1 → 0 over exit_config.duration
→ renderer paints exit animation
A composite stays mounted while is_visible() is true, even during the exit phase — the renderer paints the fading-out visual until progress reaches 0.
| Composite | Methods |
|---|---|
Select |
.open() / .close() / .toggle()
|
ComboBox |
.open() / .close() / .toggle()
|
Modal |
.open() / .close() + .invoke_close(reason, window, cx)
|
Popover |
.open() / .close() / .toggle()
|
DropdownMenu |
.open() / .close() / .toggle()
|
Tooltip |
.open() / .close() + .set_delay_ms(ms)
|
ModalCloseReason is Escape / ScrimClick / Programmatic.
let state: gpui::Entity<SelectState> = SelectState::new(&mut *cx);
let state_for_btn = state.clone();
button("select-trigger", cx)
.on_click(move |_, _, cx| state_for_btn.update(cx, |s, _| s.toggle()))
.render(cx)
.child("Pick…");
select("select-id", state).render(cx)Renderers read tokens.motion.duration_normal and tokens.motion.easing_standard from the theme so theme switching retimes every animation.
For custom painter elements, use AnimationConfig directly:
use std::time::Duration;
use yororen_ui::animation::{AnimationConfig, ease_out_cubic};
let cfg = AnimationConfig::from_motion(Duration::from_millis(200), ease_out_cubic);Presets are also available: FadeIn / FadeOut, ScaleIn / ScaleOut, fade_slide_in_right, bounce_in_*, pulse, etc. Theme tokens are the source of truth in real apps; presets stay convenient for prototyping.
For a one-off animation that doesn't fit a composite, use gpui's with_animation directly:
div.with_animation(
"card-enter",
Animation::new(Duration::from_millis(200)).with_easing(ease_out_cubic),
fade_slide_in_right(px(16.0)),
)The MaterialRippleElement in crates/yororen-ui-demos/layers_demo/src/material_button.rs is the canonical example of a hand-rolled element using request_animation_frame to advance its own f32 progress.
-
Default to
AnimatedVisibility. Every composite already has it. -
Use
tokens.motion.*for durations so theme switching retimes animations. - Keep animations short. 100–300 ms covers almost every case.
-
Pick easing by intent:
ease_out_*for entrance,ease_in_*for exit,ease_in_out_*for equal emphasis,ease_out_backfor attention. -
Clamp overshoot easings with the
*_clampedvariants when passing to gpui'sAnimation::with_easing. - Don't run side effects in render. Spawning animation timers goes in event handlers.
-
Notification guide — the toast host uses
with_animation. - Composing UI §3 — modal-pinned layout.
Yororen UI v0.3.0 · repository · Apache-2.0 · This wiki documents Yororen UI v0.3.0.
This wiki documents Yororen UI v0.3.0 — the headless-core, swappable-renderer build.