Skip to content

Timelines & steps

Things that move through stages: event timelines, trackers, pipelines, traces, rollouts, gates, paths and roadmaps.

Installation & usage

npx @youj/cli add app-event-timeline

Also installs async-button, badge, button, card, dropdown-menu, icon-more-horizontal, severity-timeline, share-bar, typography, use-announcer, use-controllable-state, use-now, use-visible, youj-core, youj-presets, youj-ui-core.

The CLI copies the block to blocks/app/event-timeline.tsx with every component it uses, so it’s yours to edit.

Every prop is optional and falls back to the demo, so it renders as you see it until you pass your own. Give it your data, rewrite or translate its strings through copy, and connect its actions:

eventTimeline-view.tsx
'use client'

import { EventTimeline, type EventTimelineProps } from '@/blocks/app/event-timeline'

export function EventTimelineView({ event, phases, severities, statuses }: Pick<EventTimelineProps, 'event' | 'phases' | 'severities' | 'statuses'>) {
  return (
    <EventTimeline
      event={event}
      phases={phases}
      severities={severities}
      statuses={statuses}
      copy={{ more: 'More actions' }}
      onPhaseChange={(phase) => {
        // your code
      }}
      onSeverityChange={(severity) => {
        // your code
      }}
      onAdvance={async (phase) => {
        // await your API here; the block shows progress until it settles
      }}
    />
  )
}

Reference

Props30 props, 5 types

Props

EventTimeline props
PropType
themeThemeInput

Wraps the block in this theme. Default: the theme around it.

–
embeddedboolean

Inside another app: a section named by an h2, without the page around the card.

default true

true
fillboolean

Take the whole width it's given instead of stopping at its natural width (736px at 100% scale).

default false

false
headingLevel1 | 2 | 3

The title's heading level; the block's own sections sit one under it. Default 1, or 2 when embedded.

–
localestring

Formats durations and clock times. Default: copy.locale, else the app's locale (YoujProvider), else en-US.

–
copyPartial<EventTimelineCopy>

Any of the block's UI strings, to rewrite or translate. The event's own words are in event.

–
eventEventTimelineEvent

The event: its id, title, subject, lead and start.

default the demo's INC-2817 on checkout-api

the demo's INC-2817 on checkout-api
phasesEventTimelinePhase[]

The stages it passes through, in order: the first is where it starts, the last closes it.

default triggered, acknowledged, mitigated, resolved

triggered, acknowledged, mitigated, resolved
severitiesEventTimelineSeverity[]

Levels of urgency, most urgent first (level 1), at most four.

default SEV1 to SEV4

SEV1 to SEV4
statusesEventTimelineStatus[]

How an item reads from its rate, worst first.

default down, degraded, operational

down, degraded, operational
scalenumber

The rate (0–1) at which an item's bar is full.

default 0.4

0.4
format{ rate?: (rate: number) => string }

Writes an item's rate (0–1). Default: a percentage with one decimal in the locale.

–
phasestring

The id of the phase it is in, when your app owns it; pair with onPhaseChange. The block adds milestone entries only when its own action moves it.

–
defaultPhasestring

The id of the phase it starts in, when the block owns it. Default: the first phase.

–
onPhaseChange(phase: string) => void

Called with the new phase's id after the action that moves it has run.

–
severitySeverityLevel
1234

Its severity, 1–4, when your app owns it; pair with onSeverityChange.

–
defaultSeveritySeverityLevel
1234

Its severity at first, 1–4.

default 2

2
onSeverityChange(severity: SeverityLevel) => void

Called with the new severity after Escalate, or when a phase eases it.

–
entriesSeverityEvent[]

What has happened so far, read when the block mounts (give it a new key to replace them). Default: the demo's four, opened 12 minutes before now.

–
itemsEventTimelineItem[]

What it touches, each with its rate per phase.

default the demo's three

the demo's three
nowDate | number

The time the block first renders at, in ms, which the server and the browser agree on; the clock ticks on from it once mounted. Default: the demo's fixed time with the demo events; with your own entries, the browser's clock once mounted.

–
timeZonestring

Clock times on the timeline in this zone, e.g. "UTC"; set it when you render on the server. Default: the browser's.

–
liveboolean

Keep the demo alive: new demo alerts arrive until someone acknowledges (never with your own entries).

default true

true
demoMarkerboolean

Shows a dashed "Demo data" badge while the block runs on the demo's entries (no entries passed).

default true

true
onAdvance(phase: EventTimelinePhase) => Promise<unknown> | unknown

Moves it into the next phase, which it receives; resolve when done, reject to show the message. Default: a short demo wait.

–
onEscalate(severity: SeverityLevel) => Promise<unknown> | unknown–
onCopyLink() => void

The menu's Copy link. Default: nothing.

–
onOpenRunbook() => void

The menu's Open runbook. Default: nothing.

–
onPostmortem() => void

Start postmortem, once resolved. Default: nothing.

–
classNames{ card?: string }

Classes for its parts: card holds the block's size (717px tall at 100%; the timeline scrolls inside it, so alerts, escalations and milestones never resize it).

–

EventTimelineEvent

EventTimelineEvent props
PropType
idRequiredstring

Its reference, set in mono: "INC-2817".

–
titleRequiredstring

What's wrong, as the heading: "Checkout errors".

–
subjectRequiredstring

What it is filed against, set in mono beside the id.

–
leadRequiredstring

Who's in charge, as people read it: "M. Oyelaran".

–
leadHandlestring

The lead's handle, the actor on the events the block adds.

default `lead`

`lead`
startedAtnumber

When it began, in ms since the epoch. Default: its first entry.

–
milestonesRecord<string, string>

Titles for the milestones the actions add, by phase id. Default: the phases' own.

–

EventTimelineItem

EventTimelineItem props
PropType
idRequiredstring–
nameRequiredstring

Set in mono, as your tooling names it: "checkout-api".

–
ratesRequiredRecord<string, number>

Its rate (0–1) in each phase, by phase id: it falls as the event is worked.

–

EventTimelinePhase

EventTimelinePhase props
PropType
idRequiredstring–
labelRequiredstring

The status pill while it is here, and the milestone's word on the timeline: "Acknowledged".

–
toneTone
'accent''neutral''success''warning''danger'

The pill's tone. Default accent.

–
actionstring

The button that moves it into this phase: "Acknowledge". Not on the first phase.

–
pendingstring

The button while that runs: "Acknowledging…". Default: the action.

–
milestonestring

The milestone event's title; {lead} is the lead's name. Default: the phase's label.

–
announcestring

Announced to screen readers on arrival. Default: the phase's label.

–
stat{ label: string; name: string }

The time-to-reach figure in the row under the pill: its short label ("TTA") and spoken name. Not on the first phase.

–
easesEventTimelineSeverityLevel
1234

Moving into this phase eases the severity down to this level, if it is higher.

–

EventTimelineSeverity

EventTimelineSeverity props
PropType
idRequiredstring–
labelRequiredstring

The chip's text: "SEV1".

–
namestring

The spoken name. Default: the label.

–
toneRequiredTone
'accent''neutral''success''warning''danger'

Its tone, on the chip beside the pill and on the timeline's nodes.

–

EventTimelineStatus

EventTimelineStatus props
PropType
idRequiredstring–
labelRequiredstring–
toneRequired'success' | 'warning' | 'danger'–
fromnumber

The rate (0–1) from which an item is in this status.

default 0

0
Strings22 keys for copy

Strings

copy takes any of these keys; the rest keep their defaults. A key whose text holds a value (a count, a name) takes a function.

subtitlemorecopyLinkdemoDatarunbookopenclosedAfternotYetaffectednoItemsratetimelineentriesleadescalateescalatingescalatedescalatedDetailpostmortemfailedannounceEventlocale
Built from7 components
Accessibilitysteps, listboxes, meters, motion

Accessibility

Event timeline reads out its level of urgency (hidden text beside the badge), the timeline is a labelled list, and failed actions use role="alert". The open time is not announced as it ticks.

The step tracker lists its stages as an ordered list named for the item. Each stage is a button with a full spoken label, aria-pressed for the one you picked and aria-current="step" for where the parcel is; the arrow keys move along the road, and the stage details panel is linked with aria-controls. The item tabs and the Replay button are named, and the decorative dots are aria-hidden.

In Stage pipeline each stage is a labelled toggle with its name, state and duration, and the arrow keys, Home and End move between stages. The log is role="log" named by its title; it is a polite live region while it follows the newest line and goes silent once you scroll up or pause.

Step trace's steps are one tab stop, arrow keys follow the flow direction, and the detail's close button is labelled.

Case file shows its stage on a Stepper whose current step is aria-current="step" and whose every step says its state. The signals bar is a meter whose value text names every signal with its share, and the key under it repeats them as text. The chain of events is a named list, oldest first, each step with its clock time, how long after the first it came, its source and what it touched; the step still running says Ongoing. Contain, Close and Dismiss announce the new stage in a polite live region, and a failed action says why in its button.

Staged rollout puts a named meter on each ruler, with the stage spoken (for example "Internal at 1%"). The playhead steps with the keyboard, the environment switch is labelled, and each item in the list is a button with aria-pressed. The stories show other worlds, with their own names and words.

Check gate draws its history as an image with a text alternative that gives the candidate count, how many fell under the bar and the candidate's pass rate; the lollipops themselves are aria-hidden. The All / Blocking filter is a named control.

Roadmap timeline draws its year as a named listbox of items, one tab stop: the arrow keys walk the pins, Home and End go to the ends, and selection follows focus. Every pin is named with its title, day and status in words ("SSO and audit log, Oct 28, At risk"), a shared pin names all of its items, and the one-line description is the pin's description; the callout repeats it visually. Status is a word and a solid, hollow or amber dot, never color alone, and the foot's figures are a description list.

In both stories the progress path is a labelled group with one tab stop, and arrow keys move between steps, with a hidden hint. The points and daily goal meters are named, the week's flames are a labelled ordered list with each day's points written out, and finishing a step is announced in a status region.

Interval timer's ring is a labelled group, and the timer is role="timer" with live announcements off so it does not chatter every second; the ring announces each interval and ticks through the last three seconds. Add 15 seconds and Skip rest have explicit labels.

Under reduced motion numbers, rings and the trail are drawn in place.

Each card is a section named by its title. Under reduced motion cells and rows change in place.