AI CSS Animations

Scrolling Terminal Log Text

A live server log that never stops: each new request types in on the bottom row, then the feed scrolls up a line. The first rows repeat at the end, so the 4.5s loop has no seam.

Also known as: live log animation css, hacker terminal text, console output scrolling.

GET /api/users 200 12ms

POST /api/login 201 88ms

GET /api/feed 200 31ms

GET /health 200 2ms

PUT /api/cart 200 45ms

HTML

<div class="txt-scrolling-terminal-log">
  <div class="txt-scrolling-terminal-log-head" aria-hidden="true"><i></i>tail -f access.log</div>
  <div class="txt-scrolling-terminal-log-view">
    <div class="txt-scrolling-terminal-log-feed">
      <p>GET  /api/users  <b>200</b>  12ms</p>
      <p>POST /api/login  <b>201</b>  88ms</p>
      <p>GET  /api/feed   <b>200</b>  31ms</p>
      <p class="txt-scrolling-terminal-log-new" style="--k: 0">GET  /health     <b>200</b>   2ms</p>
      <p class="txt-scrolling-terminal-log-new" style="--k: 1">PUT  /api/cart   <b>200</b>  45ms</p>
      <p class="txt-scrolling-terminal-log-new" style="--k: 2" aria-hidden="true">GET  /api/users  <b>200</b>  12ms</p>
      <p class="txt-scrolling-terminal-log-new" style="--k: 3" aria-hidden="true">POST /api/login  <b>201</b>  88ms</p>
      <p class="txt-scrolling-terminal-log-new" style="--k: 4" aria-hidden="true">GET  /api/feed   <b>200</b>  31ms</p>
      <p class="txt-scrolling-terminal-log-new txt-scrolling-terminal-log-next" aria-hidden="true">GET  /health     <b>200</b>   2ms</p>
    </div>
  </div>
</div>

CSS

.txt-scrolling-terminal-log {
  --c: var(--txt-color, #3b82f6);
  font-size: var(--txt-size, 32px);
  font-family: system-ui, sans-serif;
  font-weight: 700;
  line-height: 1.2;
  display: inline-block;
  overflow: hidden;
  border-radius: 0.3em;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: calc(var(--txt-size, 32px) * 0.48);
  font-weight: 500;
  text-align: left;
  background: color-mix(in srgb, currentColor 5%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 15%, transparent);}
.txt-scrolling-terminal-log-head {
  display: flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.5em 1em;
  font-size: 0.85em;
  color: color-mix(in srgb, currentColor 55%, transparent);
  box-shadow: inset 0 -1px 0 color-mix(in srgb, currentColor 12%, transparent);
}
.txt-scrolling-terminal-log-head i {
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: var(--c);
  animation: txt-scrolling-terminal-log-live 1.5s ease-in-out infinite;
}
/* four rows tall; older rows fade out at the top */
.txt-scrolling-terminal-log-view {
  height: 6em;
  margin: 0.4em 1em 0.5em;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(transparent, #000 35%);
  mask-image: linear-gradient(transparent, #000 35%);
}
.txt-scrolling-terminal-log-feed {
  animation: txt-scrolling-terminal-log-scroll 4.5s infinite;
}
.txt-scrolling-terminal-log p {
  margin: 0;
  line-height: 1.5;
  white-space: pre;
}
.txt-scrolling-terminal-log b {
  color: var(--c);
}
/* row 4+k types during 0.9s beat k of the loop, then stays until the loop wraps */
.txt-scrolling-terminal-log-new {
  animation: txt-scrolling-terminal-log-type 4.5s calc(var(--k) * 0.9s) infinite;
}
.txt-scrolling-terminal-log-next {
  clip-path: inset(0 100% 0 0);
  animation: none;
}
@keyframes txt-scrolling-terminal-log-type {
  0% { clip-path: inset(0 100% 0 0); animation-timing-function: steps(24); }
  12%, 100% { clip-path: inset(0 0 0 0); }
}
/* hold while a row types, then scroll up one row; 100% equals 0% */
@keyframes txt-scrolling-terminal-log-scroll {
  0%, 17% { transform: translateY(0); animation-timing-function: cubic-bezier(0.3, 0.7, 0.3, 1); }
  20%, 37% { transform: translateY(-1.5em); animation-timing-function: cubic-bezier(0.3, 0.7, 0.3, 1); }
  40%, 57% { transform: translateY(-3em); animation-timing-function: cubic-bezier(0.3, 0.7, 0.3, 1); }
  60%, 77% { transform: translateY(-4.5em); animation-timing-function: cubic-bezier(0.3, 0.7, 0.3, 1); }
  80%, 97% { transform: translateY(-6em); animation-timing-function: cubic-bezier(0.3, 0.7, 0.3, 1); }
  100% { transform: translateY(-7.5em); }
}
@keyframes txt-scrolling-terminal-log-live {
  50% { opacity: 0.3; }
}

@media (prefers-reduced-motion: reduce) {
  .txt-scrolling-terminal-log, .txt-scrolling-terminal-log *, .txt-scrolling-terminal-log::before, .txt-scrolling-terminal-log::after, .txt-scrolling-terminal-log *::before, .txt-scrolling-terminal-log *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --txt-color on the element or a parent. Currently previewing #3b82f6.
  • Font size: set --txt-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 40px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar text animations