Timeline
A read-only chronology — activity feed, audit log, order/shipment history, changelog, or status thread. A toned-marker rail of ordered events with terse or fully-composed items, grouped day headers, a live pending tail, alternating layout, and a collapsible body.
@garn/ui/timelineDefault
Terse items — title + time + tone, the order-activity feed.
- Success:Order placed
- Info:Payment confirmed
- Packed and ready
- Awaiting carrier pickup
Tones
The full palette — neutral · brand · info · success · warning · danger · muted.
- Success:Deploy succeeded
- Info:Release published
- Warning:High memory usage
- Error:Build failed
- Commit pushed
- Marked stale
Markers
Marker geometry — dot · outline · icon.
- Success:Dot · success
- Warning:Dot · warning
- Error:Dot · danger
- Success:Outline · success
- Info:Outline · info
- Outline · brand
- Success:Icon · check
- Icon · truck
- Info:Icon · package
Sizes
sm · md · lg marker ladders.
- Success:Small marker
- Info:Small marker
- Success:Medium marker
- Info:Medium marker
- Success:Large marker
- Info:Large marker
Current
A `current` item with a tonal halo — a live shipment tracker.
- Success:Order placed
- Success:Packed
- Out for delivery
- Delivered
Pending
A loading tail — spinner marker + a fading connector.
- Awaiting carrier confirmation…
- Success:Label created
- Info:Shipment requested
Rich
Composed items — avatar markers, a collapsible body, and an actions row.
- Reland: tighten the seam shadowReverts the revert. The scroll-aware seam shadow now reads the pinned column offset from the table context instead of a ref, which removes the flash on first paint and the off-by-one on RTL. Adds a regression test for the pinned + RTL combination and a story to the lab.
- Ari approved these changes
- Requested a review from the platform team
Grouped
Timeline.Group day headers (Today / Yesterday) over nested lists.
- Today
- Error:Failed sign-in from a new device
- Info:Password changed
- Yesterday
- Success:Two-factor enabled
- Email verified
Alternating
side="alternating" — a zig-zag roadmap.
- Success:Discovery
- Success:Design system audit
- Beta launch
- GA
End Side
side="end" — the rail on the trailing edge.
- Success:Order placed
- Info:Payment confirmed
- Shipped
Dense
dense + sm — a tight system-event log.
- Info:Cache warmed (1.2s)
- Info:Health check OK
- Warning:Latency spike on /api/feed
- Success:Deploy v4.2.1 live
- Worker scaled 3 → 5
Skeleton
Timeline.Skeleton — the loading preset in the rail's geometry.
Empty
An Empty state inside a Timeline with no events.
When something happens, it will show up here in order.
Installation
garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.
npx garn-ui add timeline- Registry
timeline- Source
- timeline.tsx
- Deps
class-variance-authority@radix-ui/react-slot- Registry deps
utilsspinner
Import
import { Timeline } from "@garn/ui/timeline";Props
Generated from the component's TypeScript types.
side- Description
- Marker/content placement: left rail (default), right rail, or zig-zag.
- Type
"alternating" | "end" | "start"- Default
start
size- Description
- Marker geometry ladder (identity — does not shrink with density).
- Type
"lg" | "md" | "sm"- Default
md
tone- Description
- Default marker tone inherited by every item (each item may override).
- Type
"brand" | "danger" | "info" | "muted" | "neutral" | "success" | "warning"- Default
neutral
variant- Description
- No description.
- Type
"dot" | "icon" | "outline"- Default
dot
dense- Description
- Tighten type + line-height for log-style feeds (spacing already follows density).
- Type
boolean
label- Description
- Convenience accessible name for the list (the `<ol>`).
- Type
React.ReactNode
Plus 283 inherited native <ol> attributes.
Timeline.Item
current- Description
- Emphasise this entry as the live/active one (a tonal halo, like Stepper's current step).
- Type
boolean
dateTime- Description
- Machine-readable ISO timestamp for the `<time datetime>` attribute.
- Type
string
loading- Description
- This entry is in progress — the marker shows a spinner, the connector fades out.
- Type
boolean
marker- Description
- Custom marker content (icon / avatar / glyph). Overrides the default dot.
- Type
React.ReactNode
side- Description
- Per-item side override (ignored unless the layout is alternating).
- Type
TimelineResolvedSide
time- Description
- Terse mode: the timestamp (rendered in a `<time>`).
- Type
React.ReactNode
title- Description
- Terse mode: the event title.
- Type
React.ReactNode
tone- Description
- Marker/text tone. Overrides the Timeline default.
- Type
"brand" | "danger" | "info" | "muted" | "neutral" | "success" | "warning"
value- Description
- Stable key (reserved for future selection/anchoring).
- Type
string
variant- Description
- Marker geometry: a plain dot (default), a tonal icon ring, or an outline ring.
- Type
"dot" | "icon" | "outline"
Plus 278 inherited native HTML attributes.
Timeline.Group
label- Description
- Section header text (e.g. "Today", "Yesterday", a date).
- Type
React.ReactNode
sticky- Description
- Stick the header to the top of the scroll container while its run is visible.
- Type
boolean
Plus 281 inherited native HTML attributes.
Timeline.Title
asChild- Description
- Render as the child element (e.g. a heading or a link), merging props.
- Type
boolean
Plus 280 inherited native HTML attributes.
Timeline.Time
dateTime- Description
- Machine-readable ISO value for the `datetime` attribute.
- Type
string
Plus 279 inherited native HTML attributes.
Timeline.Body
collapsible- Description
- Clamp to N lines with a "Show more" disclosure (uncontrolled).
- Type
boolean
lines- Description
- Number of lines to show when collapsed (default 2).
- Type
number
showLessLabel- Description
- Label for the collapse control (default "Show less").
- Type
string
showMoreLabel- Description
- Label for the expand control (default "Show more").
- Type
string
Plus 280 inherited native HTML attributes.
Timeline.Skeleton
dense- Description
- No description.
- Type
boolean
rows- Description
- How many placeholder rows to render (default 3).
- Type
number
size- Description
- No description.
- Type
TimelineSize
Plus 283 inherited native HTML attributes.
Styling
Target these data-slots and remap these tokens to restyle without forking the component.
[data-slot="timeline"][data-slot="timeline-label"]| Property | Token | Tier |
|---|---|---|
gap | --garn-gap-stack | semantic · air |
markerFill | --garn-badge-neutral-boldbrandable | component |
Private instance vars (never externalize): --m-bg
Overview
Timeline shows a sequence of events along a single axis — an activity feed, an order's status history, an audit trail, a changelog. Timeline.Item is one event (terse props or composed children, reflecting loading / current / complete), Timeline.Group is a dated/section run, and the Separator / Marker / Connector keep every marker aligned on one line. Use it for chronological events; for ordered steps the user completes use a Stepper, and for plain term→value facts use DataList.
Guidelines
Use it for events in time, not for steps to take. A timeline records what happened (or is happening); a Stepper drives a process forward. Order consistently — usually newest-first for feeds, oldest-first for histories — and stick to it.
Give each event a clear marker and timestamp. Use the marker variant/tone to convey state (done, current, error) and show a human-readable, locale-aware time; the current/loading states highlight what's live. Group long runs by date with Timeline.Group.
Keep it scannable and accessible. Lead each item with what changed, keep detail terse, and let the marker column stay decorative (aria-hidden) while the text carries the meaning to assistive tech. Don't overload one axis with unrelated streams.
Best practices
- Give the list an accessible name (label / aria-label).
- Pass
dateTime(ISO) on each item so the <time> carries a machine value beside the human text. - Use
tonefor event status — it announces an sr-only word AND colours the marker from the shared badge vocabulary. - Use Timeline.Group for day/section runs (optionally
sticky); useloadingfor the live pending tail. - Order items the way they should read (the component preserves the consumer's array order — no auto-sort).
- Don't use Timeline for a process the user steps through — that's Stepper (it has aria-current, focusable triggers, and gated movement).
- Don't rely on marker colour alone for meaning — keep the tone (it carries the sr-only status word).
- Don't strip the focus ring on item actions or the collapsible-body toggle.
- Don't hardcode marker colours — tones resolve through the --garn-badge-* tokens (edit dark mode in the token layer).
Content guidelines
- Keep titles short and factual; put detail in Timeline.Body (make it
collapsiblefor long audit payloads). - Write timestamps human-readable but always pass the ISO
dateTimefor the machine value.
Accessibility
- Role
list- Focus
- The list and items aren't focusable; only their actions and the collapsible-body toggle are, each with a 2px --garn-ring focus ring.
Enforceable contracts
Give the Timeline an accessible name via `label`, `aria-label`, or `aria-labelledby`.
when a Timeline has no `label`, `aria-label`, or `aria-labelledby`
- • A real <ol> of <li> events — an ordered list of facts, NOT a navigation. There is deliberately no aria-current, no roving focus, and no panels (the Stepper distinction).
- • Each timestamp is a <time datetime> carrying a machine ISO value beside the human text (pass `dateTime`; otherwise the visible text is used as a best-effort value).
- • The marker is decorative (aria-hidden); its status reaches AT via an sr-only tone word ('Error: …', 'Success: …') so colour is never the sole signal (WCAG 1.4.1). neutral/brand/muted carry no status word.
- • A Timeline.Group renders an <li> heading over a nested <ol>, so AT announces a sub-list under the section header without faking flat numbering.
- • Give the list an accessible name via `label`, `aria-label`, or `aria-labelledby` (dev-warns if missing). Items' actions and the collapsible-body toggle are normal focusable controls.
- Status
- Stable
- Version
v0.3.0- Introduced
v0.1.0
garn doesn't yet record a per-release version history. This reflects the component's current lifecycle state; full changelog entries will appear once the metadata pipeline emits a lifecycle.history field.