Expanding card
animateTo tweens each property from where it is now to the value you give, and commits it — so state lives in the style, not a timeline. The body reveals by animating its own height while each line fades in on a staggered delay; tapping again mid-flight simply retargets everything.
// animateTo tweens an element from where it is now to the values you give, per property, and
// commits them — state lives in the style, not a timeline. The body reveals by animating its
// own height while each line fades in on a staggered `delay`; a second tap retargets everything
// mid-flight instead of queueing. The card is content-sized, so it grows with the body.
const LINES = [
"Height, opacity and offset each tween on their own.",
"delay staggers the lines into place.",
"Tap mid-flight — the next animateTo takes over.",
]
const HIDDEN = { opacity: 0, transform: "translateY(8px)" }
const plus = UIText("+").style({ fontSize: 30, fontWeight: 700, color: "#FF4032" })
const lines = LINES.map(text =>
UIText(text).style({ fontSize: 14, lineHeight: "1.45em", color: "#8b90a0", ...HIDDEN }))
const body = UIColumn(lines).style({ height: 0, gap: 14, overflow: "hidden" })
let open = false
function toggle() {
open = !open
body.animateTo({ height: open ? 150 : 0, duration: 420 })
plus.animateTo({ transform: open ? "rotate(45deg)" : "rotate(0deg)", duration: 320 })
lines.forEach((line, i) => line.animateTo({
...(open ? { opacity: 1, transform: "translateY(0px)" } : HIDDEN),
duration: 260, delay: open ? 90 + i * 70 : 0,
}))
}
const card = UIButton([
UIRow([
UIText("Declarative motion").style({ fontSize: 19, fontWeight: 700, color: "white" }),
UISpacer(),
plus,
]).style({ height: 76, alignItems: "center" }),
body,
]).style({
flexDirection: "column", alignItems: "stretch", justifyContent: "flex-start",
px: 20, pb: 4, borderRadius: 20, bgColor: "#151922", overflow: "hidden",
}).onClick(toggle)
const screen = UIScreen([ card ]).style({ bgColor: "#0b0d12", px: 16, pt: "max(safe-top, 60px)" })
screen.open()
Press Run to launch the floating simulator. It stays open as you browse — opening another example just swaps the app inside it.