Summary#
An animation plays when its element appears. A COUNT wants the opposite: it should move when the number moves, and
never on a page load, when nothing changed. animationKey: value says exactly that — the element's animation plays
again each time value changes, and is held still when the element first appears (server paint included). The kit's
nav badges, the shell's notification count and its pin all nudge this way.
A line that DRAWS itself — the tick that writes itself when a save lands — is two style props and one kit animation:
StrokeDasharray/StrokeDashoffset hide a stroke behind a dash and slide the dash along it, and KitDraw does that
for any line up to 24 units of the icon grid. Done("Saved") is the tick and its word, ready-made.
Signature#
Badge(unread.ToString(), animation: KitNudge, animationKey: unread); // nudges when `unread` changes
Box(animation: KitDraw) { Icon(Icons.Check); } // the tick draws as it appears
Box(animation: KitDraw, animationKey: savedAt) { Icon(Icons.Check); } // …and again on every save
Done("Saved") // a drawn tick and its wordDescription#
Plays on the change, not on the first appearance#
animationKey: takes any value — a number, a string, a bool, an enum — and is accepted on every atom, like
animation: itself. When the value changes between two renders, the element's animation restarts from its first frame;
a re-render that leaves the value alone does nothing. On the element's FIRST appearance the animation is held off —
nothing changed then, and a screen of twenty counts that all nudge on load hides the one that later moves. An element
that should also ENTER is wrapped: the entrance on the outer box, the key on the inner one.
On a component call (Badge, Card) both go on the call, and land on the component's root element
(Placing and naming a component — m:, w:, grow:, alignSelf:, animation:, label: on a component call): Badge(n.ToString(), animation: KitNudge, animationKey: n).
A line that draws itself#
StrokeDasharray sets the dash pattern of every line drawn inside the element and StrokeDashoffset how far along the
line the pattern starts; both INHERIT, so on the box around an Icon they reach the icon's own strokes. A dash as long
as the line, offset by that length, hides it; animating the offset to 0 draws it. KitDraw is exactly that
(24 → 0 over 320ms), sized for the 24-unit icon grid — every tick and circle-tick in the set draws whole.
Done(label = "Saved", tone = Tone.Success) is a tick that draws as it appears beside its word, announced politely
(role: Status). The kit's Checkbox draws its tick when it is ticked — keyed to its value, so a form of ticked boxes
does not draw on load.
Reduced motion#
Nothing to write. A nudge is a scale, which reduced motion drops; a stroke's travel (StrokeDashoffset) is dropped
too, so the tick is simply there. The key still changes; only the movement is gone.
Examples#
[Route("/")]
[AllowAnonymous]
component Queue() {
int waiting = 3;
int saves = 0;
action Arrive() { waiting = waiting + 1; }
action Save() { saves = saves + 1; }
render {
Stack(gap: 3, p: 6) {
Row(gap: 2, align: Align.Center) {
Text("Waiting on you");
// Key it to the count: it nudges when a ticket arrives, never when the page loads.
Badge(waiting.ToString(), tone: Tone.Warning, animation: KitNudge, animationKey: waiting);
}
Button("Simulate a ticket", onPress: Arrive);
Row(gap: 2, align: Align.Center) {
Button("Save", onPress: Save, tone: Tone.Primary);
// A drawn tick, again on every save.
if (saves > 0) {
Box(animation: KitDraw, animationKey: saves, color: Colors.Success) { Icon(Icons.Check, size: 18); }
}
}
if (saves > 0) { Done(); }
}
}
}Testing them#
Text assertions see the words, not the motion: Assert.Visible("4") after the press, Assert.Visible("Saved"). Whether
the motion plays is a real-browser question — osy test --pixels photographs the page, and the platform's own visual
gate checks the replay mechanism.
See also#
- animation — keyframe motion, on entry, on exit, in a loop, or as it scrolls into view — declaring an animation, and the kit's motion vocabulary
- style props — every style prop, the stroke props among them
- Pinned and recent pages, the app switcher, and presence in a shell — the shell's counts and pin, which nudge this way