animate() and triggers
widget.animate(...) gives a widget its own motion. It takes preset names, pz.Animation objects
(or lists of them), property values and options:
pz.text("Saved").animate("fade_in_up") # plays when it appearscard.animate("lift", on="hover") # forward on hover, back on leaveicon.animate("pulse", repeat="infinite") # loopsfield.animate("shake", key=self.errors) # replays when key changesfield.animate("shake", on=self.wrong) # plays on self.wrong.fire()button.animate(on="press", scale=0.96, duration=90) # your own valuesanimate() returns the widget, so you can chain it after style() or call it several times. Each
call adds an animation that keeps its own trigger:
ui.button("Buy", self.buy).animate("fade_in_up").animate("lift", on="hover").animate("shrink", on="press")Triggers
Section titled “Triggers”The on option says what plays the animation:

on |
Plays | Typical use |
|---|---|---|
"mount" (default) |
Once, when the widget appears on screen. | Entrances: a page, a toast, a list item. |
"hover" |
Forward while the pointer is over the widget, back when it leaves. | Cards and buttons that react. |
"press" |
Forward while the mouse button is down on it, back on release. | A button that sinks in. |
"focus" |
Forward while it has the keyboard focus, back when it loses it. | Inputs that light up. |
"click" |
From the start on every click. | A “liked” pop, a celebration. |
"active" |
Forward while active=True, back when it becomes False. |
A recording light, a selected state. |
"manual" |
From the start each time key changes, never on its own. |
A pulse when a counter goes up. |
a pz.Trigger |
From the start each time your code calls fire(). |
A shake on a wrong password, a “saved” check. |
ui.button("Record", self.toggle).animate("glow_pulse", active=self.recording)Playing from your code with pz.Trigger
Section titled “Playing from your code with pz.Trigger”A pz.Trigger starts an animation from a handler. Create it once (in __init__, or in a component’s
setup()), give it to on= and call fire() whenever the animation should play:
class Login(pz.App): def __init__(self): self.wrong = pz.Trigger()
def view(self): return ui.input(self.password, self.set_password).animate("shake", on=self.wrong)
def submit(self): if not self.check(): self.wrong.fire() # shakes now; again from the start if it was still shakingThe widget doesn’t move when it appears, only on fire(). trigger.count says how many times it was
fired, and fire ignores its arguments, so ui.button("Again", self.wrong.fire) works.
Replaying with key
Section titled “Replaying with key”A mount animation plays once. Give it a key and it plays again every time the key changes:
password.animate("shake", key=self.failed_attempts) # shakes on each wrong passwordui.button("Copy", self.copy).animate("pulse", key=self.copies)As a mount animation it also plays when the widget first appears. With on="manual" it only plays
when the key changes (this is what pz.Trigger does underneath):
ui.badge(str(self.unread)).animate("pulse", on="manual", key=self.unread)Timing options
Section titled “Timing options”These options override the timing that an animation (or a preset) brings:

| Option | Values | What it does |
|---|---|---|
duration |
Milliseconds, or "0.3s" / "300ms". |
How long one run takes. |
delay |
The same units. | How long to wait before starting. |
easing |
A curve name, a Bézier (x1, y1, x2, y2) or "steps(4)". |
The shape of the motion: see easing. |
repeat |
A number of times, or "infinite". |
How many runs in a row. |
direction |
"normal", "reverse", "alternate", "alternate_reverse". |
reverse plays backwards; alternate goes there and back on every other run. |
fill |
"both" (default), "forwards", "backwards", "none". |
What shows before the delay and after the end, as in CSS. With "none" the widget goes back to its own look when the animation ends. |
key |
Any value. | Replays a mount or manual animation when it changes. |
active |
True or False. |
Drives an "active" animation. |
on_start, on_end |
A callable or a message. | Sent when the animation starts and when it ends. |
badge.animate("pulse", repeat=3, delay=400)arrow.animate(pz.Animation({0: {"x": 0}, 1: {"x": 6}}, duration=600), direction="alternate", repeat="infinite")toast.animate("fade_in_up", on_end=self.toast_shown)Ready-made or your own
Section titled “Ready-made or your own”- A preset name is the quickest: see the preset gallery.
- Property values describe a simple animation in place:
animate(opacity=0, y=12)appears from below. See Animatable properties. - A
pz.Animationgives you keyframes, per-frame easing and a name you can reuse anywhere. See Keyframes and easing. - A timeline target from an animation player (
self.player["card"]) lets one clip move many widgets. See Timeline animations.