Timeline

An ordered list turned into a vertical track of events. Each item gets a dot and a connector down to the next one, so the line stops at the last item on its own.

Works with

Loading
Spinner in place of a dot

Standalone

<link rel="stylesheet" href="https://esm.sh/@erikt/ui/timeline.css" />

Default

Add .timeline to an <ol>. The item holds whatever you want.

  1. Create a services site
  2. Solve initial network problems
  3. Technical testing
  4. Network problems being solved
<ol class="timeline">
  <li><time datetime="2015-09-01">2015-09-01</time> Create a services site</li>
  <li>
    <time datetime="2015-09-01">2015-09-01</time> Solve initial network problems
  </li>
  <li><time datetime="2015-09-01">2015-09-01</time> Technical testing</li>
</ol>

Filled dots

Put data-filled on the list to fill every dot instead of leaving it as a ring.

  1. Create a services site
  2. Solve initial network problems
  3. Technical testing
  4. Network problems being solved
<ol class="timeline" data-filled>
  <li><time datetime="2015-09-01">2015-09-01</time> Create a services site</li>
  <li>
    <time datetime="2015-09-01">2015-09-01</time> Solve initial network problems
  </li>
</ol>

Marking progress

The same attribute works on a single <li>. Fill the steps that are done and leave the rest as rings.

  1. Order placed
  2. Packed
  3. In transit
  4. Delivered
<ol class="timeline">
  <li data-filled>Order placed</li>
  <li data-filled>Packed</li>
  <li>In transit</li>
  <li>Delivered</li>
</ol>

Richer items

An item can hold any markup. The dot stays centred on the first line, so a heading of any size lines up with it.

Tune the track with --ui-timeline-dot, --ui-timeline-ring, --ui-timeline-line, --ui-timeline-gap, and --ui-timeline-color.

  1. Deployed to production

    Version 2.4.0 went out to all regions.

  2. Staging sign off

    Two reviewers approved the release candidate.

  3. Release cut

    Branched from main at commit b653bff.

<ol class="timeline" data-filled style="--ui-timeline-gap: 2rem">
  <li>
    <strong>Deployed to production</strong><br />
    <time datetime="2026-09-11">11 September 2026</time>
    <p>Version 2.4.0 went out to all regions.</p>
  </li>
  <li>
    <strong>Staging sign off</strong><br />
    <time datetime="2026-09-09">9 September 2026</time>
    <p>Two reviewers approved the release candidate.</p>
  </li>
</ol>

Driven by a control

The marker follows the item's attributes, so anything that can set an attribute can drive it. This toggle group switches the last item between four states.

  • Gone. The item is taken out of the list, and the connector above it goes with it.
  • Idle. A plain ring.
  • Loading. aria-busy on the <li> swaps the dot for the Loading spinner.
  • Done. data-filled fills the dot.
  1. Order placed
  2. Packed
  3. In transit
  4. Delivery
<div id="timeline-state">
  <ol class="timeline">
    <li data-filled>Order placed</li>
    <li data-filled>Packed</li>
    <li data-filled>In transit</li>
    <li>Delivery</li>
  </ol>
</div>

API reference

Type Classes / attributes What it does
Filled [data-filled] On the list or an item, fills the dot to mark it done
Loading [aria-busy] On an item, swaps the dot for a spinner
Color --ui-timeline-color: var(--ui-primary) Color of the dots
Dot size --ui-timeline-dot: 0.75rem Size of each dot
Ring --ui-timeline-ring: 2px Border width of each dot
Line --ui-timeline-line: 1px Width of the connecting line
Gap --ui-timeline-gap: var(--ui-spacing-6) Space between items

Search 5021 icons

Type a name to find icons from the Tabler icon set.

      Move Open Esc Close