32 CSS Timelines11 / 32

Pure CSSMIT licensed

Patient Vitals Timeline — Medical Dashboard with CSS Sparklines

A clinical monitoring strip: timestamped vitals readings (HR, SpO₂, BP) on a shared time axis, each with a pure-CSS sparkline drawn from custom properties via clip-path — plus threshold flags that mark out-of-range readings with icon + text, in the restrained visual language of medical dashboards.

Published Updated

Live Demo
Try it

The code

<section class="tls-11" aria-label="Patient vitals timeline demo">
  <div class="tls-11__monitor">
    <header class="tls-11__head">
      <p class="tls-11__pt">Bed 12 · J. Okafor</p>
      <p class="tls-11__range">Last 6 hours · <time datetime="2026-07-18T09:00">09:00</time>–<time datetime="2026-07-18T15:00">15:00</time></p>
    </header>
    <div class="tls-11__axis" aria-hidden="true"><span>09</span><span>10</span><span>11</span><span>12</span><span>13</span><span>14</span></div>
    <dl class="tls-11__rows">
      <div class="tls-11__row">
        <dt class="tls-11__metric">Heart rate <span class="tls-11__unit">bpm</span></dt>
        <dd class="tls-11__val">86</dd>
        <dd class="tls-11__spark" aria-hidden="true"><span class="tls-11__line" style="--c:#38bdf8;--p1:42;--p2:48;--p3:45;--p4:60;--p5:68;--p6:58"></span></dd>
        <dd class="tls-11__flag" data-flag="ok">In range</dd>
      </div>
      <div class="tls-11__row">
        <dt class="tls-11__metric">SpO₂ <span class="tls-11__unit">%</span></dt>
        <dd class="tls-11__val">93</dd>
        <dd class="tls-11__spark" aria-hidden="true"><span class="tls-11__line" style="--c:#a78bfa;--p1:80;--p2:76;--p3:70;--p4:62;--p5:55;--p6:48"></span></dd>
        <dd class="tls-11__flag" data-flag="low"><b aria-hidden="true">▼</b> Low</dd>
      </div>
      <div class="tls-11__row">
        <dt class="tls-11__metric">Systolic BP <span class="tls-11__unit">mmHg</span></dt>
        <dd class="tls-11__val">142</dd>
        <dd class="tls-11__spark" aria-hidden="true"><span class="tls-11__line" style="--c:#fbbf24;--p1:50;--p2:52;--p3:58;--p4:64;--p5:72;--p6:78"></span></dd>
        <dd class="tls-11__flag" data-flag="high"><b aria-hidden="true">▲</b> High</dd>
      </div>
      <div class="tls-11__row">
        <dt class="tls-11__metric">Temp <span class="tls-11__unit">°C</span></dt>
        <dd class="tls-11__val">37.1</dd>
        <dd class="tls-11__spark" aria-hidden="true"><span class="tls-11__line" style="--c:#34d399;--p1:48;--p2:50;--p3:49;--p4:52;--p5:51;--p6:50"></span></dd>
        <dd class="tls-11__flag" data-flag="ok">In range</dd>
      </div>
    </dl>
  </div>
</section>
.tls-11,
.tls-11 *,
.tls-11 *::before,
.tls-11 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-11 {
  min-height: 100vh;
  width: 100%;
  --tl-alert: #f59e0b;
  --tl-low: #60a5fa;
  --tl-line: #1e2733;
  --tl-ink: #e6edf5;
  --tl-mut: #7d8b9d;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: #0b1017;
  padding: 56px 24px;
  display: grid;
  place-items: center;
}

.tls-11__monitor {
  width: min(640px,100%);
  background: #0f1620;
  border: 1px solid var(--tl-line);
  border-radius: 14px;
  padding: 20px 24px 24px;
}

.tls-11__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.tls-11__pt {
  font-size: 14px;
  font-weight: 700;
  color: var(--tl-ink);
}

.tls-11__range {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 11px;
  color: var(--tl-mut);
}

.tls-11__axis {
  display: grid;
  grid-template-columns: 16ch 6ch 1fr 9ch;
  margin-bottom: 6px;
}

.tls-11__axis span {
  display: none;
}

.tls-11__axis::before {
  content: '';
  grid-column: 1 / 3;
}

.tls-11__axis {
  position: relative;
}

.tls-11__rows {
  display: grid;
  gap: 2px;
}

.tls-11__row {
  display: grid;
  grid-template-columns: 16ch 6ch 1fr 9ch;
  align-items: center;
  gap: 10px;
  padding: 10px 8px;
  border-radius: 8px;
}

.tls-11__row:hover {
  background: #131c28;
}

.tls-11__metric {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--tl-ink);
}

.tls-11__unit {
  font-size: 10px;
  color: var(--tl-mut);
  margin-left: 3px;
}

.tls-11__val {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 18px;
  font-weight: 700;
  color: var(--tl-ink);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.tls-11__spark {
  height: 34px;
  position: relative;
}

.tls-11__line {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,var(--c),transparent 130%);
  opacity: .9;
  clip-path: polygon(0 calc(100% - var(--p1) * 1%),20% calc(100% - var(--p2) * 1%),40% calc(100% - var(--p3) * 1%),60% calc(100% - var(--p4) * 1%),80% calc(100% - var(--p5) * 1%),100% calc(100% - var(--p6) * 1%),100% 100%,0 100%);
}

.tls-11__spark::before {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(90deg,#1a2430 0 1px,transparent 1px 20%);
}

.tls-11__flag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-align: right;
  color: var(--tl-mut);
}

.tls-11__flag[data-flag="high"] {
  color: var(--tl-alert);
}

.tls-11__flag[data-flag="low"] {
  color: var(--tl-low);
}

.tls-11__flag b {
  font-weight: 400;
  margin-right: 2px;
}

@media (max-width: 560px) {
  .tls-11__row,
    .tls-11__axis {
    grid-template-columns: 12ch 5ch 1fr 7ch;
  }
}
/* No JavaScript — each sparkline is a clip-path:polygon() whose vertex heights come from inline --p1…--p6 tokens; flags pair a glyph AND text with the color. */
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: Patient Vitals Timeline — Medical Dashboard with CSS Sparklines
Source: https://codefronts.com/layouts/css-timelines/vitals-timeline/

A clinical monitoring strip: timestamped vitals readings (HR, SpO₂, BP) on a shared time axis, each with a pure-CSS sparkline drawn from custom properties via clip-path — plus threshold flags that mark out-of-range readings with icon + text, in the restrained visual language of medical dashboards.
## HTML
```html
<section class="tls-11" aria-label="Patient vitals timeline demo">
  <div class="tls-11__monitor">
    <header class="tls-11__head">
      <p class="tls-11__pt">Bed 12 · J. Okafor</p>
      <p class="tls-11__range">Last 6 hours · <time datetime="2026-07-18T09:00">09:00</time>–<time datetime="2026-07-18T15:00">15:00</time></p>
    </header>
    <div class="tls-11__axis" aria-hidden="true"><span>09</span><span>10</span><span>11</span><span>12</span><span>13</span><span>14</span></div>
    <dl class="tls-11__rows">
      <div class="tls-11__row">
        <dt class="tls-11__metric">Heart rate <span class="tls-11__unit">bpm</span></dt>
        <dd class="tls-11__val">86</dd>
        <dd class="tls-11__spark" aria-hidden="true"><span class="tls-11__line" style="--c:#38bdf8;--p1:42;--p2:48;--p3:45;--p4:60;--p5:68;--p6:58"></span></dd>
        <dd class="tls-11__flag" data-flag="ok">In range</dd>
      </div>
      <div class="tls-11__row">
        <dt class="tls-11__metric">SpO₂ <span class="tls-11__unit">%</span></dt>
        <dd class="tls-11__val">93</dd>
        <dd class="tls-11__spark" aria-hidden="true"><span class="tls-11__line" style="--c:#a78bfa;--p1:80;--p2:76;--p3:70;--p4:62;--p5:55;--p6:48"></span></dd>
        <dd class="tls-11__flag" data-flag="low"><b aria-hidden="true">▼</b> Low</dd>
      </div>
      <div class="tls-11__row">
        <dt class="tls-11__metric">Systolic BP <span class="tls-11__unit">mmHg</span></dt>
        <dd class="tls-11__val">142</dd>
        <dd class="tls-11__spark" aria-hidden="true"><span class="tls-11__line" style="--c:#fbbf24;--p1:50;--p2:52;--p3:58;--p4:64;--p5:72;--p6:78"></span></dd>
        <dd class="tls-11__flag" data-flag="high"><b aria-hidden="true">▲</b> High</dd>
      </div>
      <div class="tls-11__row">
        <dt class="tls-11__metric">Temp <span class="tls-11__unit">°C</span></dt>
        <dd class="tls-11__val">37.1</dd>
        <dd class="tls-11__spark" aria-hidden="true"><span class="tls-11__line" style="--c:#34d399;--p1:48;--p2:50;--p3:49;--p4:52;--p5:51;--p6:50"></span></dd>
        <dd class="tls-11__flag" data-flag="ok">In range</dd>
      </div>
    </dl>
  </div>
</section>
```
## CSS
```css
.tls-11,
.tls-11 *,
.tls-11 *::before,
.tls-11 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-11 {
  min-height: 100vh;
  width: 100%;
  --tl-alert: #f59e0b;
  --tl-low: #60a5fa;
  --tl-line: #1e2733;
  --tl-ink: #e6edf5;
  --tl-mut: #7d8b9d;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: #0b1017;
  padding: 56px 24px;
  display: grid;
  place-items: center;
}

.tls-11__monitor {
  width: min(640px,100%);
  background: #0f1620;
  border: 1px solid var(--tl-line);
  border-radius: 14px;
  padding: 20px 24px 24px;
}

.tls-11__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.tls-11__pt {
  font-size: 14px;
  font-weight: 700;
  color: var(--tl-ink);
}

.tls-11__range {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 11px;
  color: var(--tl-mut);
}

.tls-11__axis {
  display: grid;
  grid-template-columns: 16ch 6ch 1fr 9ch;
  margin-bottom: 6px;
}

.tls-11__axis span {
  display: none;
}

.tls-11__axis::before {
  content: '';
  grid-column: 1 / 3;
}

.tls-11__axis {
  position: relative;
}

.tls-11__rows {
  display: grid;
  gap: 2px;
}

.tls-11__row {
  display: grid;
  grid-template-columns: 16ch 6ch 1fr 9ch;
  align-items: center;
  gap: 10px;
  padding: 10px 8px;
  border-radius: 8px;
}

.tls-11__row:hover {
  background: #131c28;
}

.tls-11__metric {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--tl-ink);
}

.tls-11__unit {
  font-size: 10px;
  color: var(--tl-mut);
  margin-left: 3px;
}

.tls-11__val {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 18px;
  font-weight: 700;
  color: var(--tl-ink);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.tls-11__spark {
  height: 34px;
  position: relative;
}

.tls-11__line {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,var(--c),transparent 130%);
  opacity: .9;
  clip-path: polygon(0 calc(100% - var(--p1) * 1%),20% calc(100% - var(--p2) * 1%),40% calc(100% - var(--p3) * 1%),60% calc(100% - var(--p4) * 1%),80% calc(100% - var(--p5) * 1%),100% calc(100% - var(--p6) * 1%),100% 100%,0 100%);
}

.tls-11__spark::before {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(90deg,#1a2430 0 1px,transparent 1px 20%);
}

.tls-11__flag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-align: right;
  color: var(--tl-mut);
}

.tls-11__flag[data-flag="high"] {
  color: var(--tl-alert);
}

.tls-11__flag[data-flag="low"] {
  color: var(--tl-low);
}

.tls-11__flag b {
  font-weight: 400;
  margin-right: 2px;
}

@media (max-width: 560px) {
  .tls-11__row,
    .tls-11__axis {
    grid-template-columns: 12ch 5ch 1fr 7ch;
  }
}
```

## JavaScript
```js
/* No JavaScript — each sparkline is a clip-path:polygon() whose vertex heights come from inline --p1…--p6 tokens; flags pair a glyph AND text with the color. */
```

How this works

The sparklines are the trick worth stealing: each is a fixed-height div whose clip-path: polygon() vertices come from inline custom properties — --p1 through --p6 hold percentage heights, and the polygon reads polygon(0 calc(100% - var(--p1)), 20% calc(100% - var(--p2)) …, 100% 100%, 0 100%), clipping a gradient fill into a tiny area chart. Values live in the markup as data, the shape derives in CSS, and no canvas or SVG library ever loads.

Each vitals row is a grid: time column (sticky-ready), metric label, current value in tabular figures, sparkline, and a status cell. Out-of-range readings set data-flag="high" — which colors the value, adds a ▲ glyph AND the word 'High', because in a medical context color-only coding isn't a style choice, it's a safety failure.

The shared time axis across the top uses the same 6-column rhythm as the sparkline points, so readings visually align with their timestamps. Everything is calm grays with one alert hue — restraint IS the aesthetic in clinical UI.

Techniques used: clip-path:polygon() sparklines with vertices from --p* custom properties · data-flag threshold states with glyph + text (never color-only) · tabular-nums value column · shared 6-slot time axis aligned to sparkline points · dl/dt/dd semantics per reading · calm-palette clinical restraint

Make it yours

  • Feed real data: render the six --p* tokens per row (normalize your series to 0–100) — the polygon needs no other changes.
  • Resolution: add vertices for more points; 6–12 reads well at this size.
  • Thresholds: data-flag="high"/"low" both styled; add "crit" with a stronger treatment for escalation tiers.
  • The alert hue is one token (--tl-alert) — keep everything else neutral or the flags stop popping.

Gotchas — read before shipping

  • clip-path polygons don't animate between different vertex COUNTS — keep the point count constant across updates if you transition.
  • Normalize each metric to its own clinical range (HR 40–180, SpO₂ 85–100) before mapping to --p* — raw values on a shared scale flatten every line.
  • The sparkline is decoration over data that must exist as text — the current value + flag text carry the information; never ship the chart alone.
  • tabular-nums AND a fixed ch width on the value cell — live-updating vitals that shift width read as glitches.

Browser support

ChromeSafariFirefoxEdge
allallallall

clip-path:polygon() with calc()/custom-property vertices is baseline everywhere modern (2021+); no gating features.

Techniques used in this demo

Search CodeFronts

Loading…