32 CSS Timelines19 / 32

Pure CSSMIT licensed

Terminal Log Timeline — Command-Line Session with Typing Caret

Developer-docs native: a dark terminal session where each timeline event is a timestamped log line with severity-colored level tags, a blinking block caret typing the newest entry via steps() animation, and a window-chrome header — the format for deploy logs, system journals, incident timelines and CLI tutorials.

Published Updated

Live Demo
Try it

The code

<section class="tls-19" aria-label="Terminal log timeline demo">
  <div class="tls-19__term">
    <div class="tls-19__chrome" aria-hidden="true"><span></span><span></span><span></span><p>deploy · production · ssh</p></div>
    <samp class="tls-19__screen">
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:02:01">14:02:01</time><b class="tls-19__lv" data-lv="ok">[ ok ]</b><span class="tls-19__msg">build completed in 42s · 0 warnings</span></span>
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:02:44">14:02:44</time><b class="tls-19__lv" data-lv="ok">[ ok ]</b><span class="tls-19__msg">integration suite: 214 passed</span></span>
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:03:10">14:03:10</time><b class="tls-19__lv" data-lv="warn">[warn]</b><span class="tls-19__msg">bundle +4.2 kB over budget (icons)</span></span>
      <span class="tls-19__ln tls-19__ln--fail"><time class="tls-19__ts" datetime="2026-07-18T14:03:58">14:03:58</time><b class="tls-19__lv" data-lv="fail">[FAIL]</b><span class="tls-19__msg">canary check: p99 latency 840ms &gt; 500ms</span></span>
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:04:02">14:04:02</time><b class="tls-19__lv" data-lv="info">[info]</b><span class="tls-19__msg">rollback initiated automatically</span></span>
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:04:31">14:04:31</time><b class="tls-19__lv" data-lv="ok">[ ok ]</b><span class="tls-19__msg tls-19__msg--type">rollback complete · traffic 100%</span></span>
    </samp>
  </div>
</section>
.tls-19,
.tls-19 *,
.tls-19 *::before,
.tls-19 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-19 {
  min-height: 100vh;
  width: 100%;
  --tl-ok: #34d399;
  --tl-warn: #fbbf24;
  --tl-fail: #f87171;
  --tl-info: #60a5fa;
  --tl-ink: #c8d3dc;
  --tl-mut: #55616d;
  font-family: ui-monospace,'Cascadia Code',Menlo,Consolas,monospace;
  background: #11151a;
  padding: 56px 24px;
  display: grid;
  place-items: center;
}

.tls-19__term {
  width: min(640px,100%);
  background: #0a0e12;
  border: 1px solid #232a33;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 24px 60px -30px rgba(0,0,0,.7);
}

.tls-19__chrome {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px 14px;
  border-bottom: 1px solid #1a212a;
  background: #0e1319;
}

.tls-19__chrome span {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #2d3742;
}

.tls-19__chrome span:nth-child(1) {
  background: #f87171;
}

.tls-19__chrome span:nth-child(2) {
  background: #fbbf24;
}

.tls-19__chrome span:nth-child(3) {
  background: #34d399;
}

.tls-19__chrome p {
  margin-left: 8px;
  font-size: 11px;
  color: var(--tl-mut);
  letter-spacing: .04em;
}

.tls-19__screen {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 18px 20px 22px;
  font-size: 12.5px;
  line-height: 1.5;
}

.tls-19__ln {
  display: grid;
  grid-template-columns: 9ch 7ch 1fr;
  gap: 12px;
  align-items: baseline;
}

.tls-19__ln--fail {
  background: rgba(248,113,113,.07);
  margin-inline: -20px;
  padding-inline: 20px;
  border-left: 2px solid var(--tl-fail);
}

.tls-19__ts {
  color: var(--tl-mut);
  font-variant-numeric: tabular-nums;
}

.tls-19__lv {
  font-weight: 700;
}

.tls-19__lv[data-lv="ok"] {
  color: var(--tl-ok);
}

.tls-19__lv[data-lv="warn"] {
  color: var(--tl-warn);
}

.tls-19__lv[data-lv="fail"] {
  color: var(--tl-fail);
}

.tls-19__lv[data-lv="info"] {
  color: var(--tl-info);
}

.tls-19__msg {
  color: var(--tl-ink);
}

.tls-19__msg--type {
  position: relative;
  display: inline-block;
}

.tls-19__msg--type::after {
  content: '';
  display: inline-block;
  width: 8px;
  height: 15px;
  background: var(--tl-ok);
  vertical-align: -2px;
  margin-left: 2px;
}

@media (prefers-reduced-motion: no-preference) {
  .tls-19__msg--type {
    overflow: hidden;
    white-space: nowrap;
    width: 30ch;
    animation: tls-19-type 2.1s steps(30) .4s backwards;
  }

  @keyframes tls-19-type {
    from {
      width: 0;
    }
  }

  .tls-19__msg--type::after {
    animation: tls-19-blink 1s steps(1) infinite;
  }

  @keyframes tls-19-blink {
    50% {
      opacity: 0;
    }
  }
}
/* No JavaScript — typing is width:0→30ch with steps(30) on a monospace span (1 step = 1 character); the caret blinks with steps(1); both gated behind no-preference. */
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: Terminal Log Timeline — Command-Line Session with Typing Caret
Source: https://codefronts.com/layouts/css-timelines/terminal-log/

Developer-docs native: a dark terminal session where each timeline event is a timestamped log line with severity-colored level tags, a blinking block caret typing the newest entry via steps() animation, and a window-chrome header — the format for deploy logs, system journals, incident timelines and CLI tutorials.
## HTML
```html
<section class="tls-19" aria-label="Terminal log timeline demo">
  <div class="tls-19__term">
    <div class="tls-19__chrome" aria-hidden="true"><span></span><span></span><span></span><p>deploy · production · ssh</p></div>
    <samp class="tls-19__screen">
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:02:01">14:02:01</time><b class="tls-19__lv" data-lv="ok">[ ok ]</b><span class="tls-19__msg">build completed in 42s · 0 warnings</span></span>
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:02:44">14:02:44</time><b class="tls-19__lv" data-lv="ok">[ ok ]</b><span class="tls-19__msg">integration suite: 214 passed</span></span>
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:03:10">14:03:10</time><b class="tls-19__lv" data-lv="warn">[warn]</b><span class="tls-19__msg">bundle +4.2 kB over budget (icons)</span></span>
      <span class="tls-19__ln tls-19__ln--fail"><time class="tls-19__ts" datetime="2026-07-18T14:03:58">14:03:58</time><b class="tls-19__lv" data-lv="fail">[FAIL]</b><span class="tls-19__msg">canary check: p99 latency 840ms &gt; 500ms</span></span>
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:04:02">14:04:02</time><b class="tls-19__lv" data-lv="info">[info]</b><span class="tls-19__msg">rollback initiated automatically</span></span>
      <span class="tls-19__ln"><time class="tls-19__ts" datetime="2026-07-18T14:04:31">14:04:31</time><b class="tls-19__lv" data-lv="ok">[ ok ]</b><span class="tls-19__msg tls-19__msg--type">rollback complete · traffic 100%</span></span>
    </samp>
  </div>
</section>
```
## CSS
```css
.tls-19,
.tls-19 *,
.tls-19 *::before,
.tls-19 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-19 {
  min-height: 100vh;
  width: 100%;
  --tl-ok: #34d399;
  --tl-warn: #fbbf24;
  --tl-fail: #f87171;
  --tl-info: #60a5fa;
  --tl-ink: #c8d3dc;
  --tl-mut: #55616d;
  font-family: ui-monospace,'Cascadia Code',Menlo,Consolas,monospace;
  background: #11151a;
  padding: 56px 24px;
  display: grid;
  place-items: center;
}

.tls-19__term {
  width: min(640px,100%);
  background: #0a0e12;
  border: 1px solid #232a33;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 24px 60px -30px rgba(0,0,0,.7);
}

.tls-19__chrome {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px 14px;
  border-bottom: 1px solid #1a212a;
  background: #0e1319;
}

.tls-19__chrome span {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #2d3742;
}

.tls-19__chrome span:nth-child(1) {
  background: #f87171;
}

.tls-19__chrome span:nth-child(2) {
  background: #fbbf24;
}

.tls-19__chrome span:nth-child(3) {
  background: #34d399;
}

.tls-19__chrome p {
  margin-left: 8px;
  font-size: 11px;
  color: var(--tl-mut);
  letter-spacing: .04em;
}

.tls-19__screen {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 18px 20px 22px;
  font-size: 12.5px;
  line-height: 1.5;
}

.tls-19__ln {
  display: grid;
  grid-template-columns: 9ch 7ch 1fr;
  gap: 12px;
  align-items: baseline;
}

.tls-19__ln--fail {
  background: rgba(248,113,113,.07);
  margin-inline: -20px;
  padding-inline: 20px;
  border-left: 2px solid var(--tl-fail);
}

.tls-19__ts {
  color: var(--tl-mut);
  font-variant-numeric: tabular-nums;
}

.tls-19__lv {
  font-weight: 700;
}

.tls-19__lv[data-lv="ok"] {
  color: var(--tl-ok);
}

.tls-19__lv[data-lv="warn"] {
  color: var(--tl-warn);
}

.tls-19__lv[data-lv="fail"] {
  color: var(--tl-fail);
}

.tls-19__lv[data-lv="info"] {
  color: var(--tl-info);
}

.tls-19__msg {
  color: var(--tl-ink);
}

.tls-19__msg--type {
  position: relative;
  display: inline-block;
}

.tls-19__msg--type::after {
  content: '';
  display: inline-block;
  width: 8px;
  height: 15px;
  background: var(--tl-ok);
  vertical-align: -2px;
  margin-left: 2px;
}

@media (prefers-reduced-motion: no-preference) {
  .tls-19__msg--type {
    overflow: hidden;
    white-space: nowrap;
    width: 30ch;
    animation: tls-19-type 2.1s steps(30) .4s backwards;
  }

  @keyframes tls-19-type {
    from {
      width: 0;
    }
  }

  .tls-19__msg--type::after {
    animation: tls-19-blink 1s steps(1) infinite;
  }

  @keyframes tls-19-blink {
    50% {
      opacity: 0;
    }
  }
}
```

## JavaScript
```js
/* No JavaScript — typing is width:0→30ch with steps(30) on a monospace span (1 step = 1 character); the caret blinks with steps(1); both gated behind no-preference. */
```

How this works

The typing effect is the canonical CSS trick done right: the last line's text span has width animated from 0 to 26ch with steps(26) — one step per character, which only works because the font is monospace (1 character = 1ch exactly). overflow:hidden; white-space:nowrap clips the unrevealed text; the caret is a ::after block cursor blinking with steps(1). Both animations sit behind prefers-reduced-motion: no-preference; reduced-motion users see the complete line instantly with a steady caret.

Log lines are a grid of 9ch timestamp · 7ch level · message — ch tracks again, because terminal alignment IS character alignment. Levels are double-coded (color + the bracketed text itself: [ ok ], [warn], [FAIL]) and the FAIL line carries a full-width tint bar so scanning for failures works at a glance.

The session is wrapped in <samp> (sample output) — the semantically correct element for computer output, which also cues assistive tech to switch verbosity. The window chrome is three dot spans and a title; pure costume, marked aria-hidden.

Techniques used: steps(N)-per-character typing animation on a ch-width (monospace-exact) · steps(1) block-caret blink · ch-track grid alignment · double-coded severity ([text] + color) · samp element semantics for computer output · motion gated behind prefers-reduced-motion:no-preference

Make it yours

  • Type a different line: set the animation width to the new string's character count in ch, and steps() to the same number — the two MUST match.
  • Typing speed: duration ÷ steps ≈ 45–90ms per character reads human; 20ms reads machine.
  • Severity palette: --tl-ok/--tl-warn/--tl-fail — keep FAIL the only saturated red on screen so it stays findable.
  • Add lines: each is one grid row; the typing animation belongs only on the LAST line.

Gotchas — read before shipping

  • ch-based typing requires monospace — in proportional fonts 1ch is the width of '0' only and the clip lands mid-letter.
  • Count the characters EXACTLY (including spaces) for the ch width and the steps() — off by one and the caret parks inside the final word.
  • Blinking carets are motion: gate them; a steady caret under reduced-motion still reads 'terminal'.
  • Don't fake logs with divs — samp/code elements make screen readers announce the content appropriately and help docs-site search index it as code.

Browser support

ChromeSafariFirefoxEdge
allallallall

steps() easing, ch units and samp are baseline-everywhere; the whole demo is universal CSS.

Techniques used in this demo

Search CodeFronts

Loading…