Timeline

Chronological event display.

Release timeline

  1. Design spec finalised

    Layout, tokens and component states signed off by the team.

  2. Beta released to testers

    Rolled out to 40 opt-in accounts for early feedback.

  3. General availability

    Launched to all customers with a migration guide.

html

<div class="timeline timeline--vertical" style="--accent:#BFD9F2;--gap:28px;">
  <h3 class="timeline__heading">Release timeline</h3>
  <ol class="timeline__list">
    <li class="timeline__item">
      <time class="timeline__time" datetime="2024-01-15">15 Jan 2024</time>
      <p class="timeline__event">Design spec finalised</p>
      <p class="timeline__detail">Layout, tokens and component states signed off by the team.</p>
    </li>
    <li class="timeline__item">
      <time class="timeline__time" datetime="2024-02-03">3 Feb 2024</time>
      <p class="timeline__event">Beta released to testers</p>
      <p class="timeline__detail">Rolled out to 40 opt-in accounts for early feedback.</p>
    </li>
    <li class="timeline__item">
      <time class="timeline__time" datetime="2024-03-01">1 Mar 2024</time>
      <p class="timeline__event">General availability</p>
      <p class="timeline__detail">Launched to all customers with a migration guide.</p>
    </li>
  </ol>
</div>
<style>
.timeline { font: 16px/1.5 system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 20px; box-sizing: border-box; }
.timeline__heading { margin: 0 0 16px; font-size: 16px; font-weight: 700; }
.timeline__list { list-style: none; margin: 0; padding: 0; display: flex; }
.timeline--vertical .timeline__list { flex-direction: column; gap: var(--gap); }
.timeline--horizontal .timeline__list { flex-direction: row; gap: var(--gap); overflow-x: auto; }
.timeline__item { position: relative; padding-left: 22px; }
.timeline--horizontal .timeline__item { padding-left: 0; padding-top: 22px; min-width: 160px; }
.timeline__item::before { content: ""; position: absolute; left: 4px; top: 4px; width: 9px; height: 9px; border-radius: 50%; background: var(--accent); }
.timeline--horizontal .timeline__item::before { left: 0; top: 4px; }
.timeline__item::after { content: ""; position: absolute; left: 8px; top: 14px; width: 1.5px; height: calc(100% + var(--gap) - 14px); background: #24143022; }
.timeline--horizontal .timeline__item::after { left: 14px; top: 8px; width: calc(100% + var(--gap)); height: 1.5px; }
.timeline__item:last-child::after { display: none; }
.timeline__time { display: block; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em; color: #24143099; }
.timeline__event { margin: 2px 0 0; font-size: 14px; font-weight: 600; }
.timeline__detail { margin: 4px 0 0; font-size: 13px; color: #241430b3; }
</style>

tailwind

<div class="timeline-tw timeline-tw--vertical font-sans text-[#241430] bg-[#FAF7F2] p-5 box-border" style="--accent:#BFD9F2;--gap:28px;">
  <h3 class="m-0 mb-4 text-base font-bold">Release timeline</h3>
  <ol class="timeline-tw__list list-none m-0 p-0 flex">
    <li class="timeline-tw__item relative pl-[22px]">
      <time class="block text-[11px] font-semibold uppercase tracking-wide text-[#24143099]" datetime="2024-01-15">15 Jan 2024</time>
      <p class="mt-0.5 mb-0 text-sm font-semibold">Design spec finalised</p>
      <p class="mt-1 mb-0 text-[13px] text-[#241430]/70">Layout, tokens and component states signed off by the team.</p>
    </li>
    <li class="timeline-tw__item relative pl-[22px]">
      <time class="block text-[11px] font-semibold uppercase tracking-wide text-[#24143099]" datetime="2024-02-03">3 Feb 2024</time>
      <p class="mt-0.5 mb-0 text-sm font-semibold">Beta released to testers</p>
      <p class="mt-1 mb-0 text-[13px] text-[#241430]/70">Rolled out to 40 opt-in accounts for early feedback.</p>
    </li>
    <li class="timeline-tw__item relative pl-[22px]">
      <time class="block text-[11px] font-semibold uppercase tracking-wide text-[#24143099]" datetime="2024-03-01">1 Mar 2024</time>
      <p class="mt-0.5 mb-0 text-sm font-semibold">General availability</p>
      <p class="mt-1 mb-0 text-[13px] text-[#241430]/70">Launched to all customers with a migration guide.</p>
    </li>
  </ol>
</div>
<style>
.timeline-tw--vertical .timeline-tw__list { flex-direction: column; gap: var(--gap); }
.timeline-tw--horizontal .timeline-tw__list { flex-direction: row; gap: var(--gap); overflow-x: auto; }
.timeline-tw--horizontal .timeline-tw__item { padding-left: 0; padding-top: 22px; min-width: 160px; }
.timeline-tw__item::before { content: ""; position: absolute; left: 4px; top: 4px; width: 9px; height: 9px; border-radius: 50%; background: var(--accent); }
.timeline-tw--horizontal .timeline-tw__item::before { left: 0; top: 4px; }
.timeline-tw__item::after { content: ""; position: absolute; left: 8px; top: 14px; width: 1.5px; height: calc(100% + var(--gap) - 14px); background: #24143022; }
.timeline-tw--horizontal .timeline-tw__item::after { left: 14px; top: 8px; height: 1.5px; width: calc(100% + var(--gap)); }
.timeline-tw__item:last-child::after { display: none; }
</style>

react

export function Timeline({
  heading = "Release timeline",
  orientation = "vertical",
  accent = "#BFD9F2",
  showDetail = true,
  spacing = 28,
}) {
  const horizontal = orientation === "horizontal";
  const events = [
    { date: "2024-01-15", label: "15 Jan 2024", title: "Design spec finalised", detail: "Layout, tokens and component states signed off by the team." },
    { date: "2024-02-03", label: "3 Feb 2024", title: "Beta released to testers", detail: "Rolled out to 40 opt-in accounts for early feedback." },
    { date: "2024-03-01", label: "1 Mar 2024", title: "General availability", detail: "Launched to all customers with a migration guide." },
  ];
  return (
    <div style={{ fontFamily: "system-ui, sans-serif", fontSize: 16, lineHeight: 1.5, color: "#241430", background: "#FAF7F2", padding: 20, boxSizing: "border-box" }}>
      <h3 style={{ margin: "0 0 16px", fontSize: 16, fontWeight: 700 }}>{heading}</h3>
      <ol style={{ listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: horizontal ? "row" : "column", gap: spacing, overflowX: horizontal ? "auto" : "visible" }}>
        {events.map((e) => (
          <li key={e.date} style={{ position: "relative", paddingLeft: horizontal ? 0 : 22, paddingTop: horizontal ? 22 : 0, minWidth: horizontal ? 160 : "auto" }}>
            <span aria-hidden="true" style={{ position: "absolute", left: horizontal ? 0 : 4, top: 4, width: 9, height: 9, borderRadius: "50%", background: accent }} />
            <time dateTime={e.date} style={{ display: "block", fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: ".03em", color: "#24143099" }}>{e.label}</time>
            <p style={{ margin: "2px 0 0", fontSize: 14, fontWeight: 600 }}>{e.title}</p>
            {showDetail && <p style={{ margin: "4px 0 0", fontSize: 13, color: "#241430b3" }}>{e.detail}</p>}
          </li>
        ))}
      </ol>
    </div>
  );
}