CSS + JS Data & tools #generated

Flip clock in CSS + JavaScript

Real time needs JS. Each pair that changes re-triggers a CSS rotateX flip.

  • 33 lines of CSS
  • 3 lines of HTML
  • 18 lines of JS
  • No dependencies
  • MIT licensed
GitHub

How it works

  1. CSS has no idea what time it is — JS reads the clock and writes the digits.
  2. The flip itself is a CSS keyframe: rotateX(-90deg)0.
  3. To replay a CSS animation on the same element: remove the class, force a reflow (void el.offsetWidth), add the class back.
  4. Only the pairs whose value actually changed are flipped.

Key techniques

  • setInterval for the time
  • animation restart via reflow
  • rotateX flip keyframes

Copy-paste code

HTML 3 lines

<div class="scene">
  <div class="clock"><span></span><em>:</em><span></span><em>:</em><span></span></div>
</div>

CSS 33 lines

.scene {
  perspective: 700px;
}

.clock {
  display: flex;
  align-items: center;
  gap: 8px;
  font: 800 3.5rem ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  transform: rotateY(-14deg) rotateX(6deg);
  transform-style: preserve-3d;
}

.clock span {
  min-width: 2.2ch;
  padding: 10px 14px;
  border-radius: 12px;
  text-align: center;
  color: #fff;
  background: linear-gradient(#2a2f52 49%, #0c0e1d 49% 52%, #1d2140 52%);
  box-shadow: 0 14px 22px -12px #000;
}

.clock em { color: #ffb547; font-style: normal; }

.clock .is-flipping {
  animation: flip 0.55s cubic-bezier(0.3, 1.5, 0.5, 1);
}

@keyframes flip {
  from { transform: rotateX(-90deg); }
}

JS 18 lines

const parts = [...document.querySelectorAll('.clock span')];

function tick() {
  const d = new Date();
  [d.getHours(), d.getMinutes(), d.getSeconds()].forEach((n, i) => {
    const text = String(n).padStart(2, '0');
    const el = parts[i];
    if (el.textContent === text) return;

    el.textContent = text;
    el.classList.remove('is-flipping');
    void el.offsetWidth;              // force reflow so the animation restarts
    el.classList.add('is-flipping');
  });
}

tick();
setInterval(tick, 250);

Sass source 44 lines

.d-clock {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono);
  font-size: 40px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  transform-style: preserve-3d;
  transform: rotateY(-14deg) rotateX(6deg);

  span {
    position: relative;
    min-width: 2.2ch;
    padding: 8px 10px;
    border-radius: 10px;
    background: linear-gradient(#2a2f52 49%, #0c0e1d 49% 52%, #1d2140 52%);
    color: #fff;
    text-align: center;
    box-shadow: 0 14px 22px -12px #000;
    transform-origin: center;

    // JS re-adds this class whenever the value changes.
    &.is-flipping {
      animation: d-clock-flip 0.55s cubic-bezier(0.3, 1.5, 0.5, 1);

      // With animations paused the first frame would leave the digits edge-on and unreadable.
      :root[data-paused] & {
        animation: none;
      }
    }
  }

  em {
    color: var(--warm);
    font-style: normal;
  }
}

@keyframes d-clock-flip {
  from {
    transform: rotateX(-90deg);
  }
}
Standalone snippet · plain CSS, no build step, no dependencies.