Iteratively refine UI in live sessions with a focus on motion, polish, and interaction detail.
Real-world examples
Live HTML demos for this skill — rendered directly in the page. 4 examples.
- 01
Small refinements → motion tokens
Toggle hand-rolled 400ms ease-out vs refine-live Small nudges: 250ms Fast and Smooth ease out cubic-bezier(0.22, 1, 0.36, 1) on a resize + color lane.
- 02
Modal open / close asymmetry
Modal recipe timings: open at 250ms Fast with scale 0.96 and Smooth ease out; close quieter at 150ms Quick — the polish open/close asymmetry Small jobs apply.
- 03
Dropdown phases (scan grouping)
Open vs close on one panel — different durations and easings from cssRules — the structure a Scan job returns as component phases with propertyTimings.
- 04
Replace → Notification badge
Hand-rolled opacity fade vs a Replace suggestion for the Notification badge recipe: scale pop with cubic-bezier(0.34, 1.36, 0.64, 1) and 500ms Very slow.
Skill markdown
# Refine Live
## Two modes
**Persistent (recommended — run and forget)**
Run `npx transitions-refine live` from your project. The CLI starts the relay and wires `REFINE_AGENT_CMD` so the relay spawns your agent CLI **per Refine click**. No chat loop; idle = zero credit burn. Works hours later as long as the relay process keeps running. Stop with Ctrl-C (or `npx transitions-refine stop`).
**In-chat loop (fallback — this skill)**
Run `/refine live` in Cursor/Claude/Codex when the relay is up but has **no** `REFINE_AGENT_CMD`. **You** become the poller via `GET /jobs/next`. The Agent tab stays available only while you keep polling — **each idle poll cycle consumes chat turns/credits**. Say "stop refine" to exit.
Use the in-chat loop only when you cannot wire a persistent agent CLI.
---
Turn yourself into the LLM behind the Timeline Inspector's **Refine** button (**in-chat fallback mode**). While
this loop runs, the panel's **LLM** tab is "available": each click sends one
transition here, you reason about it, and your suggestions appear in the panel.
You are the poller. Nothing is installed — you just talk to a small local relay
(default `http://localhost:7331`) that the `npx` injector already started.
## How it works
```
Browser (Refine, LLM tab) ──POST /jobs──► relay ──GET /jobs/next──► YOU
◄──GET /jobs/:id── relay ◄──POST /jobs/:id/result── YOU
```
## The loop — stay live, but don't burn credits forever
Keep polling so the panel's LLM tab stays "available", but this loop costs chat
turns/credits even while idle, so it is **not** truly run-and-forget — it has
three exits, in priority order:
1. **Relay stop signal (authoritative).** `GET /jobs/next` may return `200` with
`{"stop": true}`. The relay sends this when the user clicks **Stop** in the
panel, or automatically after ~10 min with no jobs. **Always honor it: stop
looping immediately**, tell the user the LLM tab will go unavailable and how to
resume (`/refine live`), and end your turn. Never re-poll after a stop signal.
2. **The user says so** — "stop refine", "exit live", etc.
3. **Your own idle backoff (safety net).** A long stretch of `204`s is normal —
it just means no one has clicked Refine yet — but to avoid spending credits on
a forgotten loop, **back off as idle grows** instead of hammering immediately:
re-poll right away for the first few empty cycles, then pause ~5s between polls,
and after ~10 min of unbroken idle stop on your own (same as the relay's
auto-stop) and tell the user how to resume. Any real job resets the backoff.
The relay reports the agent as "available" for ~120s after your last poll, so
short pauses keep you live. A successful job always resets idle, so an active
session never backs off.
0. **Announce yourself once, before the first poll.** The relay keeps a *sticky*
Stop latch: after a panel **Stop** (or the idle auto-stop) it answers every
`GET /jobs/next` with `{"stop": true}` until a new agent explicitly resumes —
so a stopped session can't silently come back. Clear the latch a single time
at startup, then begin polling:
```bash
curl -s -X POST http://localhost:7331/poller/start
```
Do **not** call this again mid-loop (it would defeat a user's Stop). Only on a
fresh `/refine live`.
1. **Claim the next job (long-poll).** This call blocks up to ~25s, then returns.
```bash
curl -s http://localhost:7331/jobs/next
```
- HTTP `204` / empty body → no work yet. Poll again, applying the idle
backoff above (immediate at first, then ~5s pauses, then stop after ~10 min).
- HTTP `200` with `{"stop": true}` → **the loop must end.** Stop polling, tell
the user the LLM tab is now unavailable and that `/refine live` resumes it,
and end your turn. Do not treat it as a job.
- HTTP `200` with a job JSON → work to do. Shape:
```json
{
"id": "uuid",
"request": {
"label": "Resize + Color",
"selector": ".box-resize",
"mode": "llm",
"refineType": "small",
"timings": [
{ "property": "width", "durationMs": 400, "delayMs": 0, "easing": "ease-out" },
{ "property": "background", "durationMs": 400, "delayMs": 0, "easing": "ease-out" }
]
}
}
```
- **If `request.kind === "scan"`** this is not a suggestion job — the panel is
asking you to group the page's transitions by reading the source. Jump to
[`## Scan jobs`](#scan-jobs-group-from-source) and return `groups` instead of
suggestions.
- **If `request.kind === "apply"`** this is not a suggestion job — the user
pressed **Accept** to write changes to their code. Jump to
[`## Apply jobs`](#apply-jobs-write-to-source) and edit the source instead of
posting suggestions. Everything below (refineType, steps 3–4) is for the
normal Refine flow.
- `refineType` chooses what kinds of suggestions to make (it mirrors the
…