AI CSS Animations

Select and Retype Typing Effect

Types a phrase, selects it with a highlight that sweeps back right to left, deletes it and types the next one, forever. Both phrases share one set of keyframes, offset by a delay.

Also known as: typing loop with highlight, text selection animation css, retype text effect.

HTML

<div class="txt-select-and-retype-loop" aria-label="Write faster. Build better.">
  <span class="txt-select-and-retype-loop-phrase" aria-hidden="true"><span style="--i: 0">W</span><span style="--i: 1">r</span><span style="--i: 2">i</span><span style="--i: 3">t</span><span style="--i: 4">e</span><span style="--i: 5">&nbsp;</span><span style="--i: 6">f</span><span style="--i: 7">a</span><span style="--i: 8">s</span><span style="--i: 9">t</span><span style="--i: 10">e</span><span style="--i: 11">r</span><span style="--i: 12">.</span></span>
  <span class="txt-select-and-retype-loop-phrase" aria-hidden="true"><span style="--i: 0">B</span><span style="--i: 1">u</span><span style="--i: 2">i</span><span style="--i: 3">l</span><span style="--i: 4">d</span><span style="--i: 5">&nbsp;</span><span style="--i: 6">b</span><span style="--i: 7">e</span><span style="--i: 8">t</span><span style="--i: 9">t</span><span style="--i: 10">e</span><span style="--i: 11">r</span><span style="--i: 12">.</span></span>
</div>

CSS

.txt-select-and-retype-loop {
  --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-grid;
  justify-items: start;
  white-space: nowrap;}
/* each phrase owns 3s of a 6s loop */
.txt-select-and-retype-loop-phrase {
  position: relative;
  isolation: isolate;
  grid-area: 1 / 1;
  animation: txt-select-and-retype-loop-life 6s step-end infinite;
}
/* delays are negative (slot + beat - 6s) so every element starts mid-cycle and
   the base styles are the readable state that reduced motion falls back to */
.txt-select-and-retype-loop-phrase + .txt-select-and-retype-loop-phrase {
  --slot: 3s;
  visibility: hidden;
  animation-delay: -3s;
}
/* the selection highlight sweeps from the end back to the start */
.txt-select-and-retype-loop-phrase::before {
  content: "";
  position: absolute;
  inset: 0.05em -0.04em;
  z-index: -1;
  border-radius: 0.1em;
  background: color-mix(in srgb, var(--c) 30%, transparent);
  transform: scaleX(0);
  transform-origin: right;
  animation: txt-select-and-retype-loop-select 6s var(--slot, 0s) infinite;
}
.txt-select-and-retype-loop-phrase span {
  position: relative;
  animation: txt-select-and-retype-loop-key 6s step-end calc(var(--slot, 0s) + 0.2s + var(--i) * 80ms - 6s) infinite;
}
.txt-select-and-retype-loop-phrase span::after {
  content: "";
  position: absolute;
  top: 0.12em;
  bottom: 0.08em;
  right: -0.05em;
  width: 0.07em;
  background: var(--c);
  opacity: 0;
  animation: txt-select-and-retype-loop-caret 6s step-end calc(var(--slot, 0s) + 0.2s + var(--i) * 80ms - 6s) infinite;
}
.txt-select-and-retype-loop-phrase span:last-child::after {
  animation-name: txt-select-and-retype-loop-wait;
}
@keyframes txt-select-and-retype-loop-life {
  0% { visibility: visible; }
  43.33% { visibility: hidden; }
}
@keyframes txt-select-and-retype-loop-key {
  0% { opacity: 1; }
  40% { opacity: 0; }
}
@keyframes txt-select-and-retype-loop-caret {
  0% { opacity: 1; }
  1.33% { opacity: 0; }
}
/* the last letter keeps the caret, blinking, until the selection starts */
@keyframes txt-select-and-retype-loop-wait {
  0% { opacity: 1; }
  6% { opacity: 0; }
  9% { opacity: 1; }
  12.33% { opacity: 0; }
}
@keyframes txt-select-and-retype-loop-select {
  0%, 31.67% { transform: scaleX(0); animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }
  38.33%, 43.33% { transform: scaleX(1); }
  43.34%, 100% { transform: scaleX(0); }
}

@media (prefers-reduced-motion: reduce) {
  .txt-select-and-retype-loop, .txt-select-and-retype-loop *, .txt-select-and-retype-loop::before, .txt-select-and-retype-loop::after, .txt-select-and-retype-loop *::before, .txt-select-and-retype-loop *::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