32 CSS Timelines
09 / 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

Stratigraphy Timeline — Geological Layer Blocks with Depth Scale

Deep time as sediment: stacked era blocks whose heights are proportional to duration, textured with pure-CSS grain gradients, flanked by a depth scale — the layered-block metaphor that fits geology, archaeology dig reports, company history 'eras' and any dataset where duration should be FELT, not just labeled.

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-09" aria-label="Stratigraphy timeline demo">
  <div class="tls-09__frame">
    <header class="tls-09__head"><h3 class="tls-09__title">Deep time, to scale</h3><p class="tls-09__sub">Layer height = era duration (Ma = million years ago)</p></header>
    <div class="tls-09__dig">
      <div class="tls-09__scale" aria-hidden="true"><span>0</span><span>50</span><span>100</span><span>150</span><span>200 Ma</span></div>
      <ol class="tls-09__col" role="list">
        <li class="tls-09__layer" style="--span:23;--c:oklch(.82 .09 95)">
          <div class="tls-09__chip"><b>Neogene &amp; Quaternary</b><span>23 Ma – now</span></div>
          <p class="tls-09__note">Grasslands spread; hominins appear</p>
        </li>
        <li class="tls-09__layer" style="--span:43;--c:oklch(.75 .1 70)">
          <div class="tls-09__chip"><b>Paleogene</b><span>66 – 23 Ma</span></div>
          <p class="tls-09__note">Mammals radiate into vacant niches</p>
          <span class="tls-09__seam"><i aria-hidden="true"></i>K–Pg extinction · 66 Ma</span>
        </li>
        <li class="tls-09__layer" style="--span:79;--c:oklch(.68 .1 145)">
          <div class="tls-09__chip"><b>Cretaceous</b><span>145 – 66 Ma</span></div>
          <p class="tls-09__note">First flowering plants; T. rex at the very end</p>
        </li>
        <li class="tls-09__layer" style="--span:56;--c:oklch(.6 .09 230)">
          <div class="tls-09__chip"><b>Jurassic</b><span>201 – 145 Ma</span></div>
          <p class="tls-09__note">Giant sauropods; first birds</p>
        </li>
      </ol>
    </div>
  </div>
</section>
.tls-09,
.tls-09 *,
.tls-09 *::before,
.tls-09 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-09 {
  min-height: 100vh;
  width: 100%;
  --tl-ink: #2b2419;
  --tl-mut: #8c8172;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: #f3efe7;
  padding: 56px 24px;
  display: grid;
  place-items: center;
}

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

.tls-09__head {
  margin-bottom: 18px;
}

.tls-09__title {
  font-family: Georgia,'Times New Roman',serif;
  font-size: clamp(20px,3vw,26px);
  font-weight: 700;
  color: var(--tl-ink);
  letter-spacing: -.01em;
}

.tls-09__sub {
  font-size: 12.5px;
  color: var(--tl-mut);
  margin-top: 4px;
}

.tls-09__dig {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 14px;
}

.tls-09__scale {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
  font-family: ui-monospace,Menlo,monospace;
  font-size: 10.5px;
  color: var(--tl-mut);
  padding-block: 2px;
  font-variant-numeric: tabular-nums;
}

.tls-09__col {
  list-style: none;
  height: 520px;
  display: flex;
  flex-direction: column;
  border: 2px solid #3d3426;
  border-radius: 6px;
  overflow: hidden;
}

.tls-09__layer {
  position: relative;
  flex-grow: var(--span);
  min-height: 34px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 12px 18px;
  background: linear-gradient(180deg,color-mix(in oklab,var(--c),#fff 8%),var(--c)),var(--c);
  background-blend-mode: normal;
}

.tls-09__layer::before {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(180deg,rgba(40,30,10,.06) 0 1px,transparent 1px 4px),repeating-linear-gradient(180deg,transparent 0 22px,rgba(40,30,10,.05) 22px 26px);
  pointer-events: none;
}

.tls-09__layer + .tls-09__layer {
  border-top: 2px dashed rgba(40,30,10,.28);
}

.tls-09__chip {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  align-self: flex-start;
  background: rgba(255,253,248,.92);
  border-radius: 8px;
  padding: 5px 12px;
}

.tls-09__chip b {
  font-family: Georgia,serif;
  font-size: 14.5px;
  color: var(--tl-ink);
}

.tls-09__chip span {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 10.5px;
  color: var(--tl-mut);
  font-variant-numeric: tabular-nums;
}

.tls-09__note {
  position: relative;
  font-size: 12px;
  color: rgba(30,22,8,.75);
  opacity: 0;
  translate: 0 4px;
  transition: opacity .2s,translate .2s;
  text-wrap: pretty;
}

.tls-09__layer:hover .tls-09__note {
  opacity: 1;
  translate: 0 0;
}

.tls-09__seam {
  position: absolute;
  top: 0;
  left: 18px;
  translate: 0 -50%;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  color: #7f1d1d;
  background: #fef2f2;
  border: 1px solid #fca5a5;
  border-radius: 999px;
  padding: 3px 10px;
}

.tls-09__seam i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #dc2626;
}

@media (prefers-reduced-motion: reduce) {
  .tls-09__note {
    opacity: 1;
    translate: 0 0;
    transition: none;
  }
}
/* No JavaScript — layer heights are flex-grow:var(--span) proportions inside a fixed-height column; sediment grain is stacked repeating-linear-gradients. */
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: Stratigraphy Timeline — Geological Layer Blocks with Depth Scale
Source: https://codefronts.com/layouts/css-timelines/stratigraphy-timeline/

Deep time as sediment: stacked era blocks whose heights are proportional to duration, textured with pure-CSS grain gradients, flanked by a depth scale — the layered-block metaphor that fits geology, archaeology dig reports, company history 'eras' and any dataset where duration should be FELT, not just labeled.
## HTML
```html
<section class="tls-09" aria-label="Stratigraphy timeline demo">
  <div class="tls-09__frame">
    <header class="tls-09__head"><h3 class="tls-09__title">Deep time, to scale</h3><p class="tls-09__sub">Layer height = era duration (Ma = million years ago)</p></header>
    <div class="tls-09__dig">
      <div class="tls-09__scale" aria-hidden="true"><span>0</span><span>50</span><span>100</span><span>150</span><span>200 Ma</span></div>
      <ol class="tls-09__col" role="list">
        <li class="tls-09__layer" style="--span:23;--c:oklch(.82 .09 95)">
          <div class="tls-09__chip"><b>Neogene &amp; Quaternary</b><span>23 Ma – now</span></div>
          <p class="tls-09__note">Grasslands spread; hominins appear</p>
        </li>
        <li class="tls-09__layer" style="--span:43;--c:oklch(.75 .1 70)">
          <div class="tls-09__chip"><b>Paleogene</b><span>66 – 23 Ma</span></div>
          <p class="tls-09__note">Mammals radiate into vacant niches</p>
          <span class="tls-09__seam"><i aria-hidden="true"></i>K–Pg extinction · 66 Ma</span>
        </li>
        <li class="tls-09__layer" style="--span:79;--c:oklch(.68 .1 145)">
          <div class="tls-09__chip"><b>Cretaceous</b><span>145 – 66 Ma</span></div>
          <p class="tls-09__note">First flowering plants; T. rex at the very end</p>
        </li>
        <li class="tls-09__layer" style="--span:56;--c:oklch(.6 .09 230)">
          <div class="tls-09__chip"><b>Jurassic</b><span>201 – 145 Ma</span></div>
          <p class="tls-09__note">Giant sauropods; first birds</p>
        </li>
      </ol>
    </div>
  </div>
</section>
```
## CSS
```css
.tls-09,
.tls-09 *,
.tls-09 *::before,
.tls-09 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-09 {
  min-height: 100vh;
  width: 100%;
  --tl-ink: #2b2419;
  --tl-mut: #8c8172;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: #f3efe7;
  padding: 56px 24px;
  display: grid;
  place-items: center;
}

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

.tls-09__head {
  margin-bottom: 18px;
}

.tls-09__title {
  font-family: Georgia,'Times New Roman',serif;
  font-size: clamp(20px,3vw,26px);
  font-weight: 700;
  color: var(--tl-ink);
  letter-spacing: -.01em;
}

.tls-09__sub {
  font-size: 12.5px;
  color: var(--tl-mut);
  margin-top: 4px;
}

.tls-09__dig {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 14px;
}

.tls-09__scale {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
  font-family: ui-monospace,Menlo,monospace;
  font-size: 10.5px;
  color: var(--tl-mut);
  padding-block: 2px;
  font-variant-numeric: tabular-nums;
}

.tls-09__col {
  list-style: none;
  height: 520px;
  display: flex;
  flex-direction: column;
  border: 2px solid #3d3426;
  border-radius: 6px;
  overflow: hidden;
}

.tls-09__layer {
  position: relative;
  flex-grow: var(--span);
  min-height: 34px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 12px 18px;
  background: linear-gradient(180deg,color-mix(in oklab,var(--c),#fff 8%),var(--c)),var(--c);
  background-blend-mode: normal;
}

.tls-09__layer::before {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(180deg,rgba(40,30,10,.06) 0 1px,transparent 1px 4px),repeating-linear-gradient(180deg,transparent 0 22px,rgba(40,30,10,.05) 22px 26px);
  pointer-events: none;
}

.tls-09__layer + .tls-09__layer {
  border-top: 2px dashed rgba(40,30,10,.28);
}

.tls-09__chip {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  align-self: flex-start;
  background: rgba(255,253,248,.92);
  border-radius: 8px;
  padding: 5px 12px;
}

.tls-09__chip b {
  font-family: Georgia,serif;
  font-size: 14.5px;
  color: var(--tl-ink);
}

.tls-09__chip span {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 10.5px;
  color: var(--tl-mut);
  font-variant-numeric: tabular-nums;
}

.tls-09__note {
  position: relative;
  font-size: 12px;
  color: rgba(30,22,8,.75);
  opacity: 0;
  translate: 0 4px;
  transition: opacity .2s,translate .2s;
  text-wrap: pretty;
}

.tls-09__layer:hover .tls-09__note {
  opacity: 1;
  translate: 0 0;
}

.tls-09__seam {
  position: absolute;
  top: 0;
  left: 18px;
  translate: 0 -50%;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  color: #7f1d1d;
  background: #fef2f2;
  border: 1px solid #fca5a5;
  border-radius: 999px;
  padding: 3px 10px;
}

.tls-09__seam i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #dc2626;
}

@media (prefers-reduced-motion: reduce) {
  .tls-09__note {
    opacity: 1;
    translate: 0 0;
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — layer heights are flex-grow:var(--span) proportions inside a fixed-height column; sediment grain is stacked repeating-linear-gradients. */
```

How this works

Each layer's height encodes its duration: an inline --span (millions of years) feeds flex-grow: var(--span) on the column's children, so the Cretaceous is literally taller than the Neogene, proportionally, with zero measurement code. Add a layer, and the column redistributes automatically.

The sediment texture is two stacked repeating-linear-gradients — a fine 3px grain plus a broad tonal band — per layer, tinted by the layer's --c. No images, no noise PNGs; the texture costs nothing and prints correctly.

Layer labels use a two-tier type scheme (era name + <time>-style bounds in tabular figures) and the boundary between layers carries a named 'event' marker (extinction, first flowering plants) positioned on the seam — timelines are often ABOUT the boundaries, and giving the seam its own element makes those annotatable. The depth scale on the left is a plain flex column of ticks matched by the same --span values.

Techniques used: duration-proportional heights via flex-grow:var(--span) · repeating-linear-gradient sediment grain (no image requests) · seam-anchored boundary event markers · dual-column scale + layers driven by the same tokens · oklch layer tints for even perceptual steps · hover reveal of per-layer detail text

Make it yours

  • Change the dataset: any per-item magnitude works — years at a company, sprint lengths, funding eras. Set --span per layer and heights redistribute.
  • Grain intensity: the alpha in the repeating gradient's dark stop (.06 here); 0 gives flat color-block strata.
  • Boundary events: any seam can carry a marker — position more by copying the seam span into other layers.
  • Tints: layers step through oklch lightness for even perceptual spacing — regenerate with a different hue for other palettes.

Gotchas — read before shipping

  • flex-grow distributes FREE space — give the column a fixed height (520px here) or every layer collapses to content height and the proportions vanish.
  • Keep at least 34px min-height per layer (min-height guard included) or thin eras become unlabelable slivers.
  • Text on textured fills needs a contrast guard — the label chips sit on a solid tint chip, not raw texture, for AA contrast.
  • Don't rotate labels vertical to save space — writing-mode text in thin strata is unreadable; let thin layers push labels outside with a leader line instead.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by oklch(), color-mix(), text-wrap as this demo is written. The core technique itself goes back further — Chrome all.

flex-grow proportions and repeating gradients are ancient CSS; oklch() tints are baseline 2023 with hex fallbacks trivial to add.

Techniques used in this demo

Search CodeFronts

Loading…