Timeline
Also called activity feed, history, changelog, audit trail
A sequence of events shown in chronological order, each with its time and description.
Example
Release history — newest first
- Deployed
v2.4.0 released to production by the release workflow.
- Approved
Two reviewers signed off; one comment left unresolved.
- Opened
Pull request #482 — “Replace the colour scale generator”.
- Branched
Created from main at commit 2ba527d.
What the markup carries
<ol> — the order is the content, so not <ul> <li> <time datetime="2026-08-08T09:14:00Z">Today, 09:14</time> the rail and the dots are ::before, never in the tree
“3 days ago” is for reading; the datetime attribute and the tooltip carry the absolute value, so a page cached for a week still says exactly when this happened. Each event also states its status in words — the green dot is not carrying that on its own.
When to use it
- Events have a real order and the order is what the reader is here for — order history, deployments, an audit trail.
- The gap between events matters: “three weeks later” is part of the story.
- Each entry is short. A timeline is a spine, not a container for articles.
When not to
- The items are a hierarchy. Nesting under a rail implies containment; that is a tree.
- The list is long and the reader wants the newest only. A timeline of four hundred entries is a table wearing decoration.
- There is no time dimension. A vertical line does not make an unordered list into a narrative.
Trade-offs
- Order and rhythm are visible at once — bursts and gaps read straight off the rail.
- Mostly semantic HTML, so it degrades to a perfectly usable list with no CSS at all.
- Extends naturally: a new event is one more list item.
- Costs a lot of vertical space per event, so it scales badly past a screenful.
- The decorative rail is often built with elements that end up in the accessibility tree as noise.
- Relative times (“2 days ago”) go stale in cached HTML and mislead anyone reading later.
Accessibility
What this pattern needs in order to work for everyone.
- Keyboard
- Nothing to operate unless entries link somewhere; then each link is a normal tab stop with a name that says where it goes.
- Do not add `tabindex` to entries so they can be “navigated”. A list is read, not driven.
- Roles and state
- Use `<ol>` — the order is the content, and an unordered list throws that away. Newest-first is fine; say so in the heading.
- Wrap each timestamp in `<time datetime="…">` with a machine-readable value, so “2 days ago” still resolves to a real date.
- The rail, the dots and the connecting lines are decoration: `aria-hidden`, or draw them in CSS so they are never in the tree at all.
- Icons that carry status need a text equivalent — a green tick beside “Deployed” is fine, a green tick alone is not.
- Focus
- If entries load as you scroll, do not move focus. Append below and let the reader arrive at their own pace.
In the wild
- GitHub pull request timeline — Commits, reviews and comments in one ordered stream, each with a real timestamp on hover.
- Parcel tracking — The clearest use of the pattern: a short list of events where the order is the entire message.