Navigation

v0.0.4
Data display

Timeline

Events in the order they happened.

On this page

A timeline sets events in the order they happened, on one line: an activity feed, a deployment's history, a changelog. It is an ordered list. Each event has a marker on a column and its words beside it, and a hairline joins the markers.

Timeline

<ol class="timeline max-w(360) mx-auto">  <li class="timeline-item" aria-current="true">    <span class="timeline-marker"></span>    <div class="timeline-body">      <p class="timeline-title">Deployed to production</p>      <p class="timeline-description">atlas.dev is live in 14 regions.</p>      <time class="timeline-time" datetime="2026-11-11T09:42">2 minutes ago</time>    </div>  </li>  <li class="timeline-item">    <span class="timeline-marker"></span>    <div class="timeline-body">      <p class="timeline-title">Build completed</p>      <p class="timeline-description">38 routes compiled in 48 seconds.</p>      <time class="timeline-time" datetime="2026-11-11T09:41">3 minutes ago</time>    </div>  </li>  <li class="timeline-item">    <span class="timeline-marker"></span>    <div class="timeline-body">      <p class="timeline-title">Commit pushed to main</p>      <time class="timeline-time" datetime="2026-11-11T09:40">4 minutes ago</time>    </div>  </li></ol>

#Markers

An empty timeline-marker is a small dot. The event marked aria-current takes ink and a faint halo: the newest, or the one a reader is at. A marker that holds an icon becomes a small well around it, which :has() decides, so a feed can mix both.

With icons

<ol class="timeline max-w(360) mx-auto">  <li class="timeline-item">    <span class="timeline-marker">      <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">        <path d="m6 12.5 4 4 8-9" />      </svg>    </span>    <div class="timeline-body">      <p class="timeline-title">Ada approved the change</p>      <time class="timeline-time">Today at 09:12</time>    </div>  </li>  <li class="timeline-item">    <span class="timeline-marker">      <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">        <path d="M5 6h14v10H9l-4 3z" />      </svg>    </span>    <div class="timeline-body">      <p class="timeline-title">Alan left a comment</p>      <p class="timeline-description">The focus ring is cut by the mask. Move it to the group.</p>      <time class="timeline-time">Yesterday at 17:40</time>    </div>  </li>  <li class="timeline-item">    <span class="timeline-marker"></span>    <div class="timeline-body">      <p class="timeline-title">Pull request opened</p>      <time class="timeline-time">Yesterday at 16:05</time>    </div>  </li></ol>

#Timeline or steps

A timeline records what happened; its length is open and every event is done. Steps show a fixed sequence a reader is moving through, with one step current and the rest ahead. Work under way, whose steps may be running or have failed and open to show what stands behind them, is a trace.

#Accessibility

  • Use an <ol> when the order matters, which it nearly always does, and a <time> with datetime for each moment.
  • The markers and the line are drawn by the stylesheet and are not announced. An icon in a marker is decoration: say what happened in the title.
  • A feed that updates while it is on screen should be a live region with aria-live="polite".

#Classes

ClassDescription
timelineThe list
timeline-itemAn event; a hairline joins its marker to the next
timeline-markerA small dot when empty, or a small well around an icon
timeline-bodyThe words beside the marker
timeline-titleWhat happened, in strong ink
timeline-descriptionA quieter line of detail
timeline-timeWhen it happened, in tabular figures

#Custom properties

PropertyDefaultDescription
--timeline-marker1.5remWidth of the marker column

#Source

@master/ui/components/timeline.css
/* * Timeline: events in the order they happened, as a list on one line. * Each event has a marker on a column and its words beside it; a hairline joins the markers. A bare marker * is a small dot, and a marker that holds an icon becomes a small well around it. */@layer components {  .timeline {    --timeline-marker: 1.5rem;    display: flex;    flex-direction: column;    min-width: 0;    margin: 0;    padding: 0;    color: var(--color-text-body);    font-size: .875rem;    line-height: 1.5;    list-style: none;  }  .timeline-item {    position: relative;    display: flex;    align-items: flex-start;    gap: .75rem;    min-width: 0;    padding-bottom: 1.25rem;    &:last-child {      padding-bottom: 0;    }    /* The line runs from under this marker to the next one. */    &:not(:last-child)::before {      content: '';      position: absolute;      top: calc(var(--timeline-marker) + .125rem);      bottom: .125rem;      inset-inline-start: calc(var(--timeline-marker) / 2 - .5px);      width: 1px;      background: var(--color-line-divider);    }    @media (forced-colors: active) {      &::before {        background: CanvasText;      }    }  }  /* The marker's column is as tall as the first line of text, so the dot sits on that line. */  .timeline-marker {    --icon-size: .875rem;    --icon-color: var(--color-text-muted);    display: grid;    flex: none;    place-items: center;    width: var(--timeline-marker);    height: var(--timeline-marker);    margin-block: calc((1lh - var(--timeline-marker)) / 2);    border-radius: 999px;    /* Bare: a dot. */    &:empty::before {      content: '';      width: .4375rem;      height: .4375rem;      border-radius: 999px;      background: var(--color-line-mark);    }    /* With an icon: a small well around it. */    &:has(.icon) {      background: var(--color-surface-inset);      box-shadow: var(--shadow-pressed);    }    @media (forced-colors: active) {      &:empty::before {        background: CanvasText;      }      &:has(.icon) {        outline: 1px solid CanvasText;        outline-offset: -1px;      }    }  }  /* The newest or current event: its dot takes ink. */  .timeline-item[aria-current] .timeline-marker:empty::before {    background: var(--color-text-strong);    box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-text-strong) 12%, transparent);  }  .timeline-body {    display: grid;    flex: 1 1 0;    gap: .125rem;    min-width: 0;  }  .timeline-title {    margin: 0;    color: var(--color-text-strong);    font-weight: var(--font-weight-control);    text-wrap: balance;    overflow-wrap: anywhere;  }  .timeline-description {    margin: 0;    color: var(--color-text-muted);    font-size: .8125rem;    text-wrap: pretty;    overflow-wrap: anywhere;  }  /* When it happened: a quiet line in tabular figures. */  .timeline-time {    color: var(--color-text-muted);    font-size: .75rem;    font-variant-numeric: tabular-nums;  }}