Timeline
Show timeline with phase bands, a scrubbable playhead, cue markers, and a GO LIVE return action — promoted from the demo's status-bar timeline.
Import
Timeline is a registry composite — you own the source after installing:
npx ploxum add timelineimport { Timeline, type TimelineMarker, type TimelinePhase } from "@/components/timeline";Depends on @roger-emerson/ploxum-primitives (StatusPip) and @roger-emerson/ploxum-utils (cn).
Usage
const phases: TimelinePhase[] = [
{ id: "doors", label: "Doors", startMinutes: 0, endMinutes: 90, tone: "info" },
{ id: "show", label: "Show", startMinutes: 90, endMinutes: 210, tone: "amber" },
{ id: "egress", label: "Egress", startMinutes: 210, endMinutes: 270, tone: "warning" },
];
const markers: TimelineMarker[] = [
{ id: "cue-12", label: "Pyro cue 12", atMinutes: 132, severity: "warning" },
{ id: "cue-47", label: "Headliner walk-on", atMinutes: 150, severity: "live" },
];
function ShowTimeline() {
const [playhead, setPlayhead] = useState(164);
const [live, setLive] = useState(true);
return (
<Timeline
live={live}
markers={markers}
max={270}
onGoLive={() => {
setLive(true);
setPlayhead(currentShowMinute());
}}
onScrub={(minutes) => {
setLive(false);
setPlayhead(minutes);
}}
phases={phases}
value={playhead}
/>
);
}Fully controlled: value is the playhead position in minutes, onScrub fires as the operator drags the native range input. When live is false and onGoLive is provided, a GO LIVE button renders to snap back to real time.
Props
Timeline
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | — | Current playhead position in minutes. |
max | number | — | Total show length in minutes. |
onScrub | (minutes: number) => void | — | Fires as the playhead is dragged. |
phases | TimelinePhase[] | [] | Tinted bands behind the playhead, plus a legend row. |
markers | TimelineMarker[] | [] | Cue markers rendered as StatusPips above the track. |
live | boolean | true | When false and onGoLive is provided, a GO LIVE button renders. |
onGoLive | () => void | — | Return-to-live handler. |
formatTime | (minutes: number) => string | T+HH:MM | Format the elapsed readout. |
className | string | — | Extra classes on the root. |
ref | Ref<HTMLDivElement> | — | Forwarded to the root. |
TimelinePhase
| Field | Type | Description |
|---|---|---|
id | string | Stable key. |
label | string | Phase name; shown in the legend, highlighted while the playhead is inside the phase. |
startMinutes | number | Band start. |
endMinutes | number | Band end. |
tone | "info" | "amber" | "warning" | Band color, drawn at 35% opacity behind the progress fill. |
TimelineMarker
| Field | Type | Description |
|---|---|---|
id | string | Stable key. |
label | string | Accessible label + title tooltip. |
atMinutes | number | Position on the track. |
severity | StatusPipStatus? | Pip shape + color; defaults to "unknown". |
Design rules
- The scrubber is a native
<input type="range">(styled thumb, amber glow) — keyboard arrow-key scrubbing and screen-reader value reporting come for free. Don't replace it with a pointer-only drag surface. - The elapsed readout (
T+HH:MM) is monospace + tabular numerals; it must not shift layout as it ticks. - Phase bands sit at 0.35 opacity under the amber progress fill — they're context, not data. Don't raise their opacity to compete with the playhead.
- Markers are StatusPips (shape + color), never bare colored dots.
