AI CSS Animations

VHS Camcorder REC Timestamp

A camcorder viewfinder: REC blinks in red, the date sits in the corner and the clock ticks every second, while a tracking band rolls through and jolts the OSD. A stepped integer and gradients.

Also known as: vhs timestamp css, camcorder rec text, vhs effect css, retro video overlay text.

HTML

<div class="txt-camcorder-rec-overlay" aria-label="Recording, Oct 28 1996, 11:58 PM">
  <span class="txt-camcorder-rec-overlay-osd" aria-hidden="true">
    <span class="txt-camcorder-rec-overlay-rec"><i></i>REC</span>
    <span class="txt-camcorder-rec-overlay-sp">SP <i></i></span>
    <span class="txt-camcorder-rec-overlay-date">OCT 28 1996</span>
    <span class="txt-camcorder-rec-overlay-time"></span>
  </span>
  <span class="txt-camcorder-rec-overlay-band" aria-hidden="true"></span>
</div>

CSS

@property --txt-camcorder-rec-overlay-s {
  syntax: "<integer>";
  initial-value: 7;
  inherits: false;
}
.txt-camcorder-rec-overlay {
  --c: var(--txt-color, #3b82f6);
  font-size: calc(var(--txt-size, 32px) * 0.5);
  font-family: system-ui, sans-serif;
  font-weight: 700;
  line-height: 1.2;
  position: relative;
  display: inline-block;
  width: 27em;
  height: 12.5em;
  overflow: hidden;
  border-radius: 0.4em;
  background:
    radial-gradient(60% 70% at 70% 40%, rgb(255 190 120 / 0.28), transparent 70%),
    radial-gradient(50% 60% at 20% 80%, color-mix(in srgb, var(--c) 30%, transparent), transparent 70%),
    linear-gradient(160deg, #3a3f3c, #1d211f 55%, #121413);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-weight: 700;
  color: #f4f4f4;
  --t: 5s;}
/* scanlines over the picture */
.txt-camcorder-rec-overlay::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgb(0 0 0 / 0.22) 0 1px, transparent 1px 0.2em);
  pointer-events: none;
}
.txt-camcorder-rec-overlay-osd {
  position: absolute;
  inset: 1em 1.3em;
  display: grid;
  grid-template: auto 1fr auto / 1fr auto;
  font-size: 1.35em;
  letter-spacing: 0.08em;
  text-shadow: 0.08em 0.08em 0 rgb(0 0 0 / 0.7), -0.05em 0 rgb(255 60 60 / 0.45), 0.05em 0 rgb(60 200 255 / 0.45);
  animation: txt-camcorder-rec-overlay-jolt var(--t) infinite;
}
.txt-camcorder-rec-overlay-rec {
  display: flex;
  align-items: center;
  gap: 0.35em;
}
.txt-camcorder-rec-overlay-rec i {
  width: 0.7em;
  height: 0.7em;
  border-radius: 50%;
  background: #ff3b30;
  box-shadow: 0 0 0.4em #ff3b30;
  animation: txt-camcorder-rec-overlay-blink 1s steps(1) infinite;
}
.txt-camcorder-rec-overlay-sp {
  display: flex;
  align-items: center;
  gap: 0.4em;
}
/* a battery icon */
.txt-camcorder-rec-overlay-sp i {
  width: 1.2em;
  height: 0.6em;
  border: 0.1em solid currentColor;
  background: linear-gradient(90deg, currentColor 66%, transparent 0) 0.08em 0.08em / calc(100% - 0.16em) calc(100% - 0.16em) no-repeat;
  box-shadow: 0.08em 0.08em 0 rgb(0 0 0 / 0.7);
}
.txt-camcorder-rec-overlay-date { grid-area: 3 / 1; align-self: end; }
.txt-camcorder-rec-overlay-time {
  grid-area: 3 / 2;
  align-self: end;
  counter-reset: txt-camcorder-rec-overlay-s var(--txt-camcorder-rec-overlay-s);
  animation: txt-camcorder-rec-overlay-s 60s steps(60) -7s infinite;
}
.txt-camcorder-rec-overlay-time::before {
  content: "PM 11:58:" counter(txt-camcorder-rec-overlay-s, decimal-leading-zero);
}
/* the tracking band */
.txt-camcorder-rec-overlay-band {
  position: absolute;
  left: -5%;
  right: -5%;
  top: 0;
  height: 1.8em;
  background:
    repeating-linear-gradient(90deg, rgb(255 255 255 / 0.85) 0 3.1em, transparent 0 3.6em, rgb(255 255 255 / 0.5) 0 4.4em, transparent 0 6.9em) 0 0.35em / 100% 0.12em no-repeat,
    repeating-linear-gradient(90deg, transparent 0 1.2em, rgb(255 255 255 / 0.7) 0 5.8em, transparent 0 6.5em, rgb(255 255 255 / 0.35) 0 8em) 0 0.8em / 100% 0.1em no-repeat,
    repeating-linear-gradient(90deg, rgb(255 255 255 / 0.4) 0 1.5em, transparent 0 2.3em, rgb(255 255 255 / 0.75) 0 4.1em, transparent 0 5.4em) 0 1.25em / 100% 0.14em no-repeat,
    linear-gradient(transparent, rgb(255 255 255 / 0.14) 40%, rgb(255 255 255 / 0.2) 60%, transparent);
  filter: blur(0.03em);
  mix-blend-mode: screen;
  opacity: 0;
  animation: txt-camcorder-rec-overlay-band var(--t) linear infinite;
}
@keyframes txt-camcorder-rec-overlay-blink {
  50% { opacity: 0; }
}
@keyframes txt-camcorder-rec-overlay-s {
  from { --txt-camcorder-rec-overlay-s: 0; }
  to { --txt-camcorder-rec-overlay-s: 60; }
}
@keyframes txt-camcorder-rec-overlay-band {
  0%, 60% { opacity: 0; transform: translateY(-2em); }
  61% { opacity: 1; transform: translateY(-1.5em); }
  80% { opacity: 1; transform: translateY(12em); }
  81%, 100% { opacity: 0; transform: translateY(13em); }
}
@keyframes txt-camcorder-rec-overlay-jolt {
  0%, 64%, 78%, 100% { transform: none; }
  66% { transform: translateX(0.25em); }
  69% { transform: translateX(-0.15em) skewX(-4deg); }
  72% { transform: translateX(0.1em); }
  75% { transform: translate(-0.05em, 0.05em); }
}
@media (prefers-reduced-motion: reduce) {
  .txt-camcorder-rec-overlay, .txt-camcorder-rec-overlay *, .txt-camcorder-rec-overlay *::before, .txt-camcorder-rec-overlay *::after { animation: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  .txt-camcorder-rec-overlay, .txt-camcorder-rec-overlay *, .txt-camcorder-rec-overlay::before, .txt-camcorder-rec-overlay::after, .txt-camcorder-rec-overlay *::before, .txt-camcorder-rec-overlay *::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