Interactive states

A graphic can be a live UI element: its timeline cut into named states that page events and your code switch between. The same item renders to video for a product film.

Button

Hover it, then click it.

state: …

Toggle

Click to switch it.

state: …

Like

Click to like, again to unlike.

state: …

Loader

Your code decides how it ends.

state: …

Notification bell

Hover to ring it, click to clear.

state: …

These are the five items in the UI pack, each in a <volito-player>. Each one declares its own states, so they work with no setup: the player plays each state's stretch of the timeline, loops the idle ones and moves on by itself (press hands on to loading). Your code switches the rest.

Use one

<script type="module" src="https://cdn.jsdelivr.net/npm/volito/dist/player.js"></script>

<volito-player item="ui/button" aspect="1000x500" ground="none" brand="studio"
  props='{"label":"Get started"}'></volito-player>

<script type="module">
  const button = document.querySelector("volito-player");
  button.addEventListener("statechange", (e) => console.log(e.detail.to)); // enter → idle → hover → press → loading
  await fetch("/api/signup", { method: "POST" });
  button.state = "done";                                                  // lands on the check
</script>
What it does
stateThe state it is in. Set it to switch: player.state = "done". The attribute sets the first one.
setState(name)The same, as a promise that settles once the state has started.
stateDefs()The states it has, resolved from the item and the attribute.
statechangeAn event on every switch, with { from, to } in its detail.

States on any graphic

Any graphic can have states, not only the UI pack. The states attribute cuts its timeline, and on maps page events to them:

<!-- Waits empty; every click stamps it again. -->
<volito-player item="annotate/stamp" props='{"text":"Approved"}'
  states='{
    "ready": { "from": 0, "play": "hold" },
    "stamp": { "from": 0, "to": "end", "play": "once" }
  }'
  on="click: stamp"></volito-player>
FieldWhat it takes
fromWhere the stretch starts: seconds, one of the item's beat names, start or end. A negative number counts from the end. The start by default.
toWhere it ends, the same way. The end by default.
playonce plays it and stays on its last frame, loop repeats it, hold shows from and stays.
nextThe state to go to when a once stretch finishes.

on takes event: state pairs separated by ;. A guard in brackets limits a pair to some states: pointerenter(idle|unhover): hover only hovers from idle or on the way back. Any DOM event on the element works: pointerenter, pointerleave, click, focus, blur, pointerdown, pointerup.

It starts in the state you ask for, else enter (an entrance that hands on), else idle, else the first one.

In your own items

An item declares its states in item.json, next to its beats, so every player that shows it gets them. Name the beats after the moments, and the states read like the design:

{
  "beats": { "idle": 0.6, "hoverIn": 1.6, "hoverOut": 1.9, "press": 2.2, "loading": 2.6, "done": 3.6 },
  "states": {
    "enter":   { "from": "start",   "to": "idle",     "play": "once", "next": "idle" },
    "idle":    { "from": "idle",    "to": "hoverIn",  "play": "loop" },
    "hover":   { "from": "hoverIn", "to": "hoverOut", "play": "once" },
    "unhover": { "from": "hoverOut","to": "press",    "play": "once", "next": "idle" },
    "press":   { "from": "press",   "to": "loading",  "play": "once", "next": "loading" },
    "loading": { "from": "loading", "to": "done",     "play": "loop" },
    "done":    { "from": "done",    "to": "end",      "play": "once" }
  },
  "on": "pointerenter(idle|unhover): hover; pointerleave(hover): unhover; click(idle|hover|unhover): press"
}

Volito checks them when it loads the item, so volito check, and every other command, stops on a state that names a beat the item does not have, a next that is not a state, or an on pair that points nowhere. Write your own has the rest of the item format.

The same item as video

States are for a page. A render plays the whole timeline, enter to done, so the button that lives in your sign-up form also renders into the launch video, in the same brand:

volito render ui/button --prop label="Get started" --style studio --format webm --out button.webm

Reduced motion

For readers who prefer reduced motion, looping states hold their first frame and a once state shows its last frame at once, then moves on to next. Every state still reads; nothing moves on its own.