3. Replace src/main.luau
luastra/motion · stable UI identity · about 15 minutesstate changes once · host animates frames
The card keeps one ID while its changed translateX descriptor deliberately restarts that channel.
When to use it
Replace the complete entry module. Keep the card ID stable: a descriptor change restarts the affected channels without discarding semantic identity.
Focused example
--!strict
local Motion = require("luastra/motion")
local UI = require("luastra/ui")
local App = require("luastra/app")
local Root = {}
local run = 0
local fromLeft = true
local function entrance(): Motion.MotionMap
local offset = if fromLeft then -28 else 28
return {
opacity = Motion.tween {
from = 0,
to = 1,
durationMs = 180,
easing = "easeOutCubic",
},
translateX = Motion.tween {
from = offset,
to = 0,
durationMs = 260,
easing = "easeOutCubic",
},
}
end
function Root.render(): UI.Node
local side = if fromLeft then "left" else "right"
local nextSide = if fromLeft then "right" else "left"
return UI.Screen {
id = "motion-recipe",
width = "content",
padding = "responsive",
UI.Card {
id = "motion/card",
gap = "lg",
padding = "lg",
surface = "elevated",
motion = entrance(),
UI.Text { id = "motion/title", text = "Declarative entrance", variant = "title" },
UI.Text {
id = "motion/status",
text = "Run " .. tostring(run) .. ": from " .. side,
role = "status",
},
UI.Text {
id = "motion/explanation",
text = "Luau changes state once; the host animates the frames.",
tone = "muted",
},
UI.Button {
id = "motion/replay",
text = "Replay from " .. nextSide,
onTap = "motion.replay",
},
},
}
end
function Root.handle(action: string, target: string, _value: string)
if action == "motion.replay" and target == "motion/replay" then
run += 1
fromLeft = not fromLeft
end
end
function Root.snapshot()
return { run = run, fromLeft = fromLeft }
end
return App.compose {
render = function(_outputs) return Root.render() end,
handle = Root.handle,
snapshot = Root.snapshot,
}