30 CSS Text Animations12 / 30

CSS + JSMIT licensed

CSS Text Unscramble Animation on Load

Text decoding out of noise, twice: a 14-line JS scrambler that locks characters left-to-right out of a glyph storm, and a zero-JS slot-machine build where every letter rides a stepped reel of decoy glyphs into place.

Published Updated

Live Demo
Try it

The code

<div class="cat-12-group">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400..800&display=swap" rel="stylesheet">
<section class="cat-12a" aria-label="Text unscramble decode effect">
  <div class="cat-12a__stage">
    <p class="cat-12a__status" aria-hidden="true"><i></i>DECRYPTING PAYLOAD</p>
    <p class="cat-12a__text" id="cat-12a-text" data-value="ACCESS GRANTED" aria-label="ACCESS GRANTED"></p>
    <button class="cat-12a__btn" id="cat-12a-btn" type="button">Run again</button>
  </div>
</section>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400..800&display=swap" rel="stylesheet">
<section class="cat-12b" aria-label="Slot reel decode effect">
  <div class="cat-12b__stage">
    <h1 class="cat-12b__word" aria-label="DECODED">
      <span class="cat-12b__slots" aria-hidden="true"><span class="cat-12b__slot" style="--i:0"><span class="cat-12b__reel"><i>#</i><i>X</i><i>9</i><i>D</i></span></span><span class="cat-12b__slot" style="--i:1"><span class="cat-12b__reel"><i>$</i><i>3</i><i>K</i><i>E</i></span></span><span class="cat-12b__slot" style="--i:2"><span class="cat-12b__reel"><i>%</i><i>W</i><i>7</i><i>C</i></span></span><span class="cat-12b__slot" style="--i:3"><span class="cat-12b__reel"><i>&amp;</i><i>2</i><i>Z</i><i>O</i></span></span><span class="cat-12b__slot" style="--i:4"><span class="cat-12b__reel"><i>+</i><i>Q</i><i>5</i><i>D</i></span></span><span class="cat-12b__slot" style="--i:5"><span class="cat-12b__reel"><i>?</i><i>8</i><i>V</i><i>E</i></span></span><span class="cat-12b__slot" style="--i:6"><span class="cat-12b__reel"><i>*</i><i>J</i><i>4</i><i>D</i></span></span></span>
    </h1>
    <p class="cat-12b__sub" aria-hidden="true">zero JS — each letter is a steps(3) reel of decoy glyphs</p>
  </div>
</section>
</div>
.cat-12-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.cat-12-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.cat-12a,
.cat-12a *,
.cat-12a *::before,
.cat-12a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cat-12a {
  --green: oklch(0.88 0.2 150);
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: radial-gradient(100% 90% at 50% 10%,#0c1512,#060a08 70%);
  font-family: 'JetBrains Mono',ui-monospace,Menlo,monospace;
}

.cat-12a__stage {
  text-align: center;
  display: grid;
  gap: 22px;
  justify-items: center;
}

.cat-12a__status {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 10.5px;
  letter-spacing: .32em;
  color: rgba(150,220,180,.55);
}

.cat-12a__status i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  animation: cat-12a-blink 1s step-end infinite;
}

.cat-12a__text {
  font-size: clamp(26px,5.4vw,58px);
  font-weight: 800;
  letter-spacing: .12em;
  color: var(--green);
  text-shadow: 0 0 22px color-mix(in oklab,var(--green) 55%,transparent);
  min-height: 1.2em;
}

.cat-12a__btn {
  font: inherit;
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: rgba(150,220,180,.7);
  background: transparent;
  border: 1px solid rgba(150,220,180,.3);
  border-radius: 999px;
  padding: 9px 20px;
  cursor: pointer;
  transition: border-color .25s,color .25s;
}

.cat-12a__btn:hover {
  border-color: var(--green);
  color: var(--green);
}

.cat-12a__btn:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

@keyframes cat-12a-blink {
  50% {
    opacity: .25;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cat-12a__status i {
    animation: none;
  }
}

.cat-12b,
.cat-12b *,
.cat-12b *::before,
.cat-12b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cat-12b {
  --cyan: oklch(0.86 0.14 200);
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: #080d14;
  font-family: 'JetBrains Mono',ui-monospace,Menlo,monospace;
}

.cat-12b__stage {
  text-align: center;
}

.cat-12b__word {
  font-size: clamp(40px,8vw,96px);
  font-weight: 800;
  color: var(--cyan);
}

.cat-12b__slots {
  display: inline-flex;
  gap: .06em;
}

.cat-12b__slot {
  display: inline-block;
  height: 1.05em;
  line-height: 1.05;
  overflow: hidden;
}

.cat-12b__reel {
  display: flex;
  flex-direction: column;
  animation: cat-12b-drop .9s steps(3) calc(var(--i)*100ms + .35s) forwards;
}

.cat-12b__reel i {
  font-style: normal;
  height: 1.05em;
}

.cat-12b__reel i:not(:last-child) {
  opacity: .45;
}

.cat-12b__sub {
  margin-top: 20px;
  font-size: 11px;
  letter-spacing: .16em;
  color: rgba(160,200,230,.4);
}

@keyframes cat-12b-drop {
  to {
    transform: translateY(-3.15em);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cat-12b__reel {
    animation-duration: .01s;
    animation-delay: 0s;
  }
}
(() => {
  const el = document.querySelector('#cat-12a-text'); if (!el) return;
  const FINAL = el.dataset.value, POOL = '!<>-_/[]{}=+*^?#@$%&';
  const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
  let timer;
  const run = () => {
    if (reduce) { el.textContent = FINAL; return; }
    clearInterval(timer); let frame = 0;
    timer = setInterval(() => {
      const done = Math.floor(frame / 2);
      el.textContent = FINAL.split('').map((c, i) =>
        c === ' ' ? ' ' : i < done ? c : POOL[Math.floor(Math.random() * POOL.length)]).join('');
      if (done >= FINAL.length) clearInterval(timer);
      frame++;
    }, 34);
  };
  run();
  const btn = document.querySelector('#cat-12a-btn');
  if (btn) btn.addEventListener('click', run);
})();

/* No JavaScript — every slot is a 1em overflow:hidden window over a 4-glyph column; steps(3) hops it down one glyph at a time until the real letter parks. */
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 Text Animation 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: CSS Text Unscramble Animation on Load
Source: https://codefronts.com/motion/css-text-animations/css-word-scramble-animation/

Text decoding out of noise, twice: a 14-line JS scrambler that locks characters left-to-right out of a glyph storm, and a zero-JS slot-machine build where every letter rides a stepped reel of decoy glyphs into place.
## HTML
```html
<div class="cat-12-group">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400..800&display=swap" rel="stylesheet">
<section class="cat-12a" aria-label="Text unscramble decode effect">
  <div class="cat-12a__stage">
    <p class="cat-12a__status" aria-hidden="true"><i></i>DECRYPTING PAYLOAD</p>
    <p class="cat-12a__text" id="cat-12a-text" data-value="ACCESS GRANTED" aria-label="ACCESS GRANTED"></p>
    <button class="cat-12a__btn" id="cat-12a-btn" type="button">Run again</button>
  </div>
</section>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400..800&display=swap" rel="stylesheet">
<section class="cat-12b" aria-label="Slot reel decode effect">
  <div class="cat-12b__stage">
    <h1 class="cat-12b__word" aria-label="DECODED">
      <span class="cat-12b__slots" aria-hidden="true"><span class="cat-12b__slot" style="--i:0"><span class="cat-12b__reel"><i>#</i><i>X</i><i>9</i><i>D</i></span></span><span class="cat-12b__slot" style="--i:1"><span class="cat-12b__reel"><i>$</i><i>3</i><i>K</i><i>E</i></span></span><span class="cat-12b__slot" style="--i:2"><span class="cat-12b__reel"><i>%</i><i>W</i><i>7</i><i>C</i></span></span><span class="cat-12b__slot" style="--i:3"><span class="cat-12b__reel"><i>&amp;</i><i>2</i><i>Z</i><i>O</i></span></span><span class="cat-12b__slot" style="--i:4"><span class="cat-12b__reel"><i>+</i><i>Q</i><i>5</i><i>D</i></span></span><span class="cat-12b__slot" style="--i:5"><span class="cat-12b__reel"><i>?</i><i>8</i><i>V</i><i>E</i></span></span><span class="cat-12b__slot" style="--i:6"><span class="cat-12b__reel"><i>*</i><i>J</i><i>4</i><i>D</i></span></span></span>
    </h1>
    <p class="cat-12b__sub" aria-hidden="true">zero JS — each letter is a steps(3) reel of decoy glyphs</p>
  </div>
</section>
</div>
```
## CSS
```css
.cat-12-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.cat-12-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.cat-12a,
.cat-12a *,
.cat-12a *::before,
.cat-12a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cat-12a {
  --green: oklch(0.88 0.2 150);
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: radial-gradient(100% 90% at 50% 10%,#0c1512,#060a08 70%);
  font-family: 'JetBrains Mono',ui-monospace,Menlo,monospace;
}

.cat-12a__stage {
  text-align: center;
  display: grid;
  gap: 22px;
  justify-items: center;
}

.cat-12a__status {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 10.5px;
  letter-spacing: .32em;
  color: rgba(150,220,180,.55);
}

.cat-12a__status i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  animation: cat-12a-blink 1s step-end infinite;
}

.cat-12a__text {
  font-size: clamp(26px,5.4vw,58px);
  font-weight: 800;
  letter-spacing: .12em;
  color: var(--green);
  text-shadow: 0 0 22px color-mix(in oklab,var(--green) 55%,transparent);
  min-height: 1.2em;
}

.cat-12a__btn {
  font: inherit;
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: rgba(150,220,180,.7);
  background: transparent;
  border: 1px solid rgba(150,220,180,.3);
  border-radius: 999px;
  padding: 9px 20px;
  cursor: pointer;
  transition: border-color .25s,color .25s;
}

.cat-12a__btn:hover {
  border-color: var(--green);
  color: var(--green);
}

.cat-12a__btn:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

@keyframes cat-12a-blink {
  50% {
    opacity: .25;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cat-12a__status i {
    animation: none;
  }
}

.cat-12b,
.cat-12b *,
.cat-12b *::before,
.cat-12b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cat-12b {
  --cyan: oklch(0.86 0.14 200);
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: #080d14;
  font-family: 'JetBrains Mono',ui-monospace,Menlo,monospace;
}

.cat-12b__stage {
  text-align: center;
}

.cat-12b__word {
  font-size: clamp(40px,8vw,96px);
  font-weight: 800;
  color: var(--cyan);
}

.cat-12b__slots {
  display: inline-flex;
  gap: .06em;
}

.cat-12b__slot {
  display: inline-block;
  height: 1.05em;
  line-height: 1.05;
  overflow: hidden;
}

.cat-12b__reel {
  display: flex;
  flex-direction: column;
  animation: cat-12b-drop .9s steps(3) calc(var(--i)*100ms + .35s) forwards;
}

.cat-12b__reel i {
  font-style: normal;
  height: 1.05em;
}

.cat-12b__reel i:not(:last-child) {
  opacity: .45;
}

.cat-12b__sub {
  margin-top: 20px;
  font-size: 11px;
  letter-spacing: .16em;
  color: rgba(160,200,230,.4);
}

@keyframes cat-12b-drop {
  to {
    transform: translateY(-3.15em);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cat-12b__reel {
    animation-duration: .01s;
    animation-delay: 0s;
  }
}
```

## JavaScript
```js
(() => {
  const el = document.querySelector('#cat-12a-text'); if (!el) return;
  const FINAL = el.dataset.value, POOL = '!<>-_/[]{}=+*^?#@$%&';
  const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
  let timer;
  const run = () => {
    if (reduce) { el.textContent = FINAL; return; }
    clearInterval(timer); let frame = 0;
    timer = setInterval(() => {
      const done = Math.floor(frame / 2);
      el.textContent = FINAL.split('').map((c, i) =>
        c === ' ' ? ' ' : i < done ? c : POOL[Math.floor(Math.random() * POOL.length)]).join('');
      if (done >= FINAL.length) clearInterval(timer);
      frame++;
    }, 34);
  };
  run();
  const btn = document.querySelector('#cat-12a-btn');
  if (btn) btn.addEventListener('click', run);
})();

/* No JavaScript — every slot is a 1em overflow:hidden window over a 4-glyph column; steps(3) hops it down one glyph at a time until the real letter parks. */
```

How this works

The JS variant keeps the real sentence in data-value (and in the accessible name). On an interval it rebuilds the string: characters left of the reveal cursor are real, the rest are random picks from a symbol pool — the cursor advances every second tick, so the lock-in sweeps left to right.

The pure-CSS variant needs no text mutation at all: each letter is a 1em-tall overflow:hidden slot containing a vertical reel of four glyphs (three decoys + the real one). One keyframe slides every reel up 3em with steps(3); staggered delays make the word assemble like tumblers landing:

.reel{animation:drop .9s steps(3) forwards;
  animation-delay:calc(var(--i)*80ms)}
@keyframes drop{to{transform:translateY(-3em)}}

Make it yours

  • Tune chaos with the symbol pool — box-drawing and katakana read very cyberpunk.
  • Reveal speed: JS cursor advances every 2 ticks at 34ms; raise either for drama.
  • Reel variant: add reel rows for a longer spin (keep steps = rows − 1).
  • Wire the JS re-run to a button click, hover, or IntersectionObserver entry.

Gotchas — read before shipping

  • Never scramble the DOM's only copy of the text — crawlers and screen readers must see the real string (data-value + aria-label here).
  • Monospace fonts prevent width jitter while glyphs shuffle.
  • Clear the interval when done and skip the effect entirely under prefers-reduced-motion.

Browser support

ChromeSafariFirefoxEdge
111+16.2+113+111+

Floor set by oklch(), color-mix() as this demo is written. The core technique itself goes back further — Chrome 43+.

Both variants degrade to the plain readable string. The JS scrambler is ~14 lines, dependency-free, and exits early under reduced motion.

Search CodeFronts

Loading…