32 CSS Timelines
27 / 32

All 32 in 32 CSS Timelines

  1. 01GitHub-Style Commit History Timeline
  2. 02CI/CD Deploy Pipeline Timeline — Status Steps with Live Spinner
  3. 03Resume / Work Experience Timeline with Subgrid Alignment
  4. 04Order Tracking Timeline — Delivery Progress Stepper
  5. 05Fitness Activity Feed Timeline — Workout Cards with Metric Grids
  6. 06Scroll-Driven Reveal Timeline — animation-timeline: view() Entrances
  7. 07Team Swimlane Gantt Timeline — Grid-Line Placement, No Libraries
  8. 08Conference Agenda Timeline — Hour Grid with Parallel Tracks
  9. 09Stratigraphy Timeline — Geological Layer Blocks with Depth Scale
  10. 10Concert Tour Timeline — Ticket Stub Cards with Perforated Edges
  11. 11Patient Vitals Timeline — Medical Dashboard with CSS Sparklines
  12. 12Film Reel Timeline — Sprocket-Hole Border History Strip
  13. 13Mission Control Timeline — T-Minus Event Log with Status Dots
  14. 14Fermentation Calendar Timeline — Process Stages as Duration Bars
  15. 15Conversation Thread Timeline — Nested Replies with Curved Connectors
  16. 16Accordion Timeline — Expandable Milestones with Exclusive Open
  17. 17Cinematic Filmstrip Timeline — Scroll-Snap Career Frames
  18. 18Nature Trail Timeline — Winding Dashed Path with Waypoint Badges
  19. 19Terminal Log Timeline — Command-Line Session with Typing Caret
  20. 20Reading-Progress Timeline — animation-timeline: scroll() Article Tracker
  21. 21Zigzag Alternating Timeline — Container Query Responsive
  22. 22Tabbed Era Timeline — Radio-Button Period Switcher, Zero JS
  23. 23Anchor-Nav Timeline — :target Deep-Linkable Milestones
  24. 24Scrollytelling Timeline — Sticky Chapter Panel with Scrolling Beats
  25. 25Product Roadmap Timeline — Quarter Columns with Status Chips
  26. 26Horizontal Panorama Timeline — Vertical Scroll Drives Sideways Travel
  27. 27SaaS Changelog Timeline — Version Badges with Sticky Month Rail
  28. 28Editorial History Timeline — Sticky Era Headers in Serif
  29. 29Comparison Dual Timeline — Two Histories on One Spine
  30. 30Metro Map Timeline — Transit Lines with Interchange Stations
  31. 31Flight Itinerary Timeline — Segments, Layover and Timezone Footnotes
  32. 32Live Activity Feed Timeline — Real-Time Events with :has() Filter
Pure CSSMIT licensed

SaaS Changelog Timeline — Version Badges with Sticky Month Rail

The docs-site staple: releases on a left rail with semver badges, category chips (New / Improved / Fixed) double-coded with glyphs, and month labels that stick in the gutter as their releases scroll by — the layout Linear, Stripe and every serious devtool converged on for 'What's new' pages.

Live Demo
Try it

Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects

ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link

10% off at checkout with code
Check ZTM Course

The code

<section class="tls-27" aria-label="Changelog timeline demo">
  <div class="tls-27__inner">
    <h3 class="tls-27__title">What's new</h3>
    <div class="tls-27__month">
      <p class="tls-27__mlabel">Jul<br>2026</p>
      <div class="tls-27__entries">
        <article class="tls-27__rel tls-27__rel--major">
          <header class="tls-27__rhead"><code class="tls-27__ver">v3.0.0</code><time datetime="2026-07-14">Jul 14</time></header>
          <h4 class="tls-27__rtitle">The big one: workspaces</h4>
          <ul class="tls-27__list" role="list">
            <li><span class="tls-27__kind" data-kind="new">New</span>Multi-workspace accounts with per-workspace billing</li>
            <li><span class="tls-27__kind" data-kind="new">New</span>Granular roles: admin, editor, commenter, guest</li>
            <li><span class="tls-27__kind" data-kind="improved">Improved</span>Search is 6× faster on large orgs</li>
          </ul>
        </article>
        <article class="tls-27__rel">
          <header class="tls-27__rhead"><code class="tls-27__ver">v2.14.1</code><time datetime="2026-07-03">Jul 3</time></header>
          <ul class="tls-27__list" role="list">
            <li><span class="tls-27__kind" data-kind="fixed">Fixed</span>Exported CSVs no longer double-escape quotes</li>
            <li><span class="tls-27__kind" data-kind="fixed">Fixed</span>Safari: date picker closes on first tap again</li>
          </ul>
        </article>
      </div>
    </div>
    <div class="tls-27__month">
      <p class="tls-27__mlabel">Jun<br>2026</p>
      <div class="tls-27__entries">
        <article class="tls-27__rel">
          <header class="tls-27__rhead"><code class="tls-27__ver">v2.14.0</code><time datetime="2026-06-19">Jun 19</time></header>
          <h4 class="tls-27__rtitle">Automations, round two</h4>
          <ul class="tls-27__list" role="list">
            <li><span class="tls-27__kind" data-kind="new">New</span>Scheduled triggers (cron syntax supported)</li>
            <li><span class="tls-27__kind" data-kind="improved">Improved</span>Run history keeps 90 days on all plans</li>
            <li><span class="tls-27__kind" data-kind="fixed">Fixed</span>Webhook retries respect Retry-After headers</li>
          </ul>
        </article>
        <article class="tls-27__rel">
          <header class="tls-27__rhead"><code class="tls-27__ver">v2.13.2</code><time datetime="2026-06-05">Jun 5</time></header>
          <ul class="tls-27__list" role="list">
            <li><span class="tls-27__kind" data-kind="improved">Improved</span>Keyboard nav: J/K move between rows everywhere</li>
          </ul>
        </article>
      </div>
    </div>
  </div>
</section>
.tls-27,
.tls-27 *,
.tls-27 *::before,
.tls-27 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-27 {
  min-height: 100vh;
  width: 100%;
  --tl-accent: #4f46e5;
  --tl-line: #e5e7eb;
  --tl-bg: #ffffff;
  --tl-ink: #111827;
  --tl-mut: #6b7280;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: var(--tl-bg);
  padding: 64px 24px;
  display: grid;
  place-items: center;
}

.tls-27__inner {
  width: min(620px,100%);
}

.tls-27__title {
  font-size: clamp(22px,3.5vw,28px);
  font-weight: 800;
  color: var(--tl-ink);
  letter-spacing: -.02em;
  margin-bottom: 32px;
}

.tls-27__month {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 20px;
}

.tls-27__mlabel {
  position: sticky;
  top: 24px;
  align-self: start;
  font-size: 13px;
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tl-mut);
  text-align: right;
  padding-top: 2px;
}

.tls-27__entries {
  position: relative;
  padding: 0 0 36px 26px;
  border-left: 2px solid var(--tl-line);
}

.tls-27__rel {
  position: relative;
  padding-bottom: 28px;
}

.tls-27__rel:last-child {
  padding-bottom: 0;
}

.tls-27__rel::before {
  content: '';
  position: absolute;
  left: -33px;
  top: 5px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--tl-bg);
  border: 3px solid var(--tl-accent);
  box-shadow: 0 0 0 4px var(--tl-bg);
}

.tls-27__rel--major::before {
  left: -35px;
  width: 14px;
  height: 14px;
  background: var(--tl-accent);
}

.tls-27__rhead {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 8px;
}

.tls-27__ver {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 13px;
  font-weight: 700;
  color: var(--tl-accent);
  background: #eef2ff;
  border-radius: 6px;
  padding: 2px 8px;
}

.tls-27__rel--major .tls-27__ver {
  color: #fff;
  background: var(--tl-accent);
}

.tls-27__rhead time {
  font-size: 12.5px;
  color: var(--tl-mut);
}

.tls-27__rtitle {
  font-size: 16.5px;
  font-weight: 750;
  color: var(--tl-ink);
  letter-spacing: -.01em;
  margin-bottom: 10px;
}

.tls-27__list {
  list-style: none;
  display: grid;
  gap: 8px;
}

.tls-27__list li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 14px;
  line-height: 1.55;
  color: #374151;
  text-wrap: pretty;
}

.tls-27__kind {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  border-radius: 999px;
  padding: 2px 9px;
  min-width: 64px;
  justify-content: center;
}

.tls-27__kind::before {
  font-weight: 800;
}

.tls-27__kind[data-kind="new"] {
  color: #166534;
  background: #f0fdf4;
}

.tls-27__kind[data-kind="new"]::before {
  content: '+';
}

.tls-27__kind[data-kind="improved"] {
  color: #1d4ed8;
  background: #eff6ff;
}

.tls-27__kind[data-kind="improved"]::before {
  content: '↑';
}

.tls-27__kind[data-kind="fixed"] {
  color: #9a3412;
  background: #fff7ed;
}

.tls-27__kind[data-kind="fixed"]::before {
  content: '✓';
}

@media (max-width: 520px) {
  .tls-27__month {
    grid-template-columns: 1fr;
  }

  .tls-27__mlabel {
    position: static;
    text-align: left;
    padding-bottom: 10px;
  }

  .tls-27__mlabel br {
    display: none;
  }
}
/* No JavaScript — month labels are position:sticky inside their month group; category chips are data-kind attributes rendering glyph + text (never color alone). */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Timeline from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: SaaS Changelog Timeline — Version Badges with Sticky Month Rail
Source: https://codefronts.com/layouts/css-timelines/ticker-tape-changelog/

The docs-site staple: releases on a left rail with semver badges, category chips (New / Improved / Fixed) double-coded with glyphs, and month labels that stick in the gutter as their releases scroll by — the layout Linear, Stripe and every serious devtool converged on for 'What's new' pages.
## HTML
```html
<section class="tls-27" aria-label="Changelog timeline demo">
  <div class="tls-27__inner">
    <h3 class="tls-27__title">What's new</h3>
    <div class="tls-27__month">
      <p class="tls-27__mlabel">Jul<br>2026</p>
      <div class="tls-27__entries">
        <article class="tls-27__rel tls-27__rel--major">
          <header class="tls-27__rhead"><code class="tls-27__ver">v3.0.0</code><time datetime="2026-07-14">Jul 14</time></header>
          <h4 class="tls-27__rtitle">The big one: workspaces</h4>
          <ul class="tls-27__list" role="list">
            <li><span class="tls-27__kind" data-kind="new">New</span>Multi-workspace accounts with per-workspace billing</li>
            <li><span class="tls-27__kind" data-kind="new">New</span>Granular roles: admin, editor, commenter, guest</li>
            <li><span class="tls-27__kind" data-kind="improved">Improved</span>Search is 6× faster on large orgs</li>
          </ul>
        </article>
        <article class="tls-27__rel">
          <header class="tls-27__rhead"><code class="tls-27__ver">v2.14.1</code><time datetime="2026-07-03">Jul 3</time></header>
          <ul class="tls-27__list" role="list">
            <li><span class="tls-27__kind" data-kind="fixed">Fixed</span>Exported CSVs no longer double-escape quotes</li>
            <li><span class="tls-27__kind" data-kind="fixed">Fixed</span>Safari: date picker closes on first tap again</li>
          </ul>
        </article>
      </div>
    </div>
    <div class="tls-27__month">
      <p class="tls-27__mlabel">Jun<br>2026</p>
      <div class="tls-27__entries">
        <article class="tls-27__rel">
          <header class="tls-27__rhead"><code class="tls-27__ver">v2.14.0</code><time datetime="2026-06-19">Jun 19</time></header>
          <h4 class="tls-27__rtitle">Automations, round two</h4>
          <ul class="tls-27__list" role="list">
            <li><span class="tls-27__kind" data-kind="new">New</span>Scheduled triggers (cron syntax supported)</li>
            <li><span class="tls-27__kind" data-kind="improved">Improved</span>Run history keeps 90 days on all plans</li>
            <li><span class="tls-27__kind" data-kind="fixed">Fixed</span>Webhook retries respect Retry-After headers</li>
          </ul>
        </article>
        <article class="tls-27__rel">
          <header class="tls-27__rhead"><code class="tls-27__ver">v2.13.2</code><time datetime="2026-06-05">Jun 5</time></header>
          <ul class="tls-27__list" role="list">
            <li><span class="tls-27__kind" data-kind="improved">Improved</span>Keyboard nav: J/K move between rows everywhere</li>
          </ul>
        </article>
      </div>
    </div>
  </div>
</section>
```
## CSS
```css
.tls-27,
.tls-27 *,
.tls-27 *::before,
.tls-27 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-27 {
  min-height: 100vh;
  width: 100%;
  --tl-accent: #4f46e5;
  --tl-line: #e5e7eb;
  --tl-bg: #ffffff;
  --tl-ink: #111827;
  --tl-mut: #6b7280;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: var(--tl-bg);
  padding: 64px 24px;
  display: grid;
  place-items: center;
}

.tls-27__inner {
  width: min(620px,100%);
}

.tls-27__title {
  font-size: clamp(22px,3.5vw,28px);
  font-weight: 800;
  color: var(--tl-ink);
  letter-spacing: -.02em;
  margin-bottom: 32px;
}

.tls-27__month {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 20px;
}

.tls-27__mlabel {
  position: sticky;
  top: 24px;
  align-self: start;
  font-size: 13px;
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tl-mut);
  text-align: right;
  padding-top: 2px;
}

.tls-27__entries {
  position: relative;
  padding: 0 0 36px 26px;
  border-left: 2px solid var(--tl-line);
}

.tls-27__rel {
  position: relative;
  padding-bottom: 28px;
}

.tls-27__rel:last-child {
  padding-bottom: 0;
}

.tls-27__rel::before {
  content: '';
  position: absolute;
  left: -33px;
  top: 5px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--tl-bg);
  border: 3px solid var(--tl-accent);
  box-shadow: 0 0 0 4px var(--tl-bg);
}

.tls-27__rel--major::before {
  left: -35px;
  width: 14px;
  height: 14px;
  background: var(--tl-accent);
}

.tls-27__rhead {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 8px;
}

.tls-27__ver {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 13px;
  font-weight: 700;
  color: var(--tl-accent);
  background: #eef2ff;
  border-radius: 6px;
  padding: 2px 8px;
}

.tls-27__rel--major .tls-27__ver {
  color: #fff;
  background: var(--tl-accent);
}

.tls-27__rhead time {
  font-size: 12.5px;
  color: var(--tl-mut);
}

.tls-27__rtitle {
  font-size: 16.5px;
  font-weight: 750;
  color: var(--tl-ink);
  letter-spacing: -.01em;
  margin-bottom: 10px;
}

.tls-27__list {
  list-style: none;
  display: grid;
  gap: 8px;
}

.tls-27__list li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 14px;
  line-height: 1.55;
  color: #374151;
  text-wrap: pretty;
}

.tls-27__kind {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  border-radius: 999px;
  padding: 2px 9px;
  min-width: 64px;
  justify-content: center;
}

.tls-27__kind::before {
  font-weight: 800;
}

.tls-27__kind[data-kind="new"] {
  color: #166534;
  background: #f0fdf4;
}

.tls-27__kind[data-kind="new"]::before {
  content: '+';
}

.tls-27__kind[data-kind="improved"] {
  color: #1d4ed8;
  background: #eff6ff;
}

.tls-27__kind[data-kind="improved"]::before {
  content: '↑';
}

.tls-27__kind[data-kind="fixed"] {
  color: #9a3412;
  background: #fff7ed;
}

.tls-27__kind[data-kind="fixed"]::before {
  content: '✓';
}

@media (max-width: 520px) {
  .tls-27__month {
    grid-template-columns: 1fr;
  }

  .tls-27__mlabel {
    position: static;
    text-align: left;
    padding-bottom: 10px;
  }

  .tls-27__mlabel br {
    display: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — month labels are position:sticky inside their month group; category chips are data-kind attributes rendering glyph + text (never color alone). */
```

How this works

The page is a two-column grid: a narrow gutter column for month labels and a main column of release entries. Month labels are position: sticky; top: 24px INSIDE their month group, so as a month's releases scroll, its label rides along in the gutter, then hands off when the next month begins — the same handoff physics as demo 20, rotated into a side rail.

Each release is an <article> headed by a semver badge (v2.14.0) in monospace and a <time> date. Change categories are data-kind chips where the glyph is drawn per kind (+ for New, ↑ for Improved, ✕→✓ wrench-free 'Fixed' dot) so category survives grayscale — and each chip's text IS its category, never color alone.

The rail dot for each release is ring-punched over the rail line with a box-shadow of the page background, and major releases (x.0.0) get a larger, filled marker via a --major class — visual versioning hierarchy at a glance. Everything themes from four tokens.

Techniques used: sticky gutter labels with per-group containment handoff · semver badges in monospace with major-release marker hierarchy · data-kind category chips (glyph + text, never color-only) · ring-punched rail markers · two-column grid with narrow sticky gutter · time[datetime] machine dates

Make it yours

  • Sticky offset: top:24px on the month label — align it with your site header.
  • Major-release emphasis: the --major marker size/fill; add a subtle card border tint for x.0.0 entries if launches deserve more.
  • Categories: each is one data-kind ruleset — add 'Security' or 'Deprecated' without touching layout.
  • RSS-friendly: each article already has a heading + time; add id="v2-14-0" anchors for deep-linkable releases.

Gotchas — read before shipping

  • The sticky label needs its PARENT (the month group) to be tall enough to scroll within — sticking happens inside the parent's box, never beyond it.
  • Chips must keep their text labels; icon-only category chips fail WCAG 1.1.1 unless you add hidden text.
  • Monospace semver badges: proportional fonts make v1.1.1 and v9.9.9 different widths and the rail alignment wobbles.
  • Don't reverse-order releases oldest-first — changelogs are newest-first by convention and users bounce if v1.0 greets them.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by text-wrap as this demo is written. The core technique itself goes back further — Chrome all.

Sticky positioning, grid and attribute selectors — everything is baseline-everywhere CSS.

Techniques used in this demo

Search CodeFronts

Loading…