GODRICH

9 CSS Text Reveal Effects — Hex, Cipher, Radar

A css text reveal effect is a short animation for text appearing on screen, ready to paste in with no coding needed.

Nothing here requires writing a single line of CSS yourself — open the demo, copy the file, drop it into your page, and the words on screen start decoding the same way they do below.

Auto-plays · click a tile to jump to its section · all nine in one zip

These nine aren't ordered by popularity — they follow the order in which hidden information actually gets revealed. First, random-looking code settles into real letters (01–03). Then dots or dark bars that were hiding something peel away (04–06). Last come the moments that feel like a check passing or a secret getting caught (07–09).

01Hex byte settle

Each letter slot flickers through a random two-digit hex code, then locks into the real character one at a time, starting from the right. The color shifts from amber while it's still guessing to green once a letter is settled. CSS alone can't rewrite the text content of an element, so this one needed a short script to drive it.

setIntervalJS 10줄color transition
var target = "ACCESS", hex = "0123456789ABCDEF";
var spans = document.querySelectorAll(".hb-c");
var n = 0;
setInterval(function () {
  n++;
  var settled = Math.max(0, Math.floor((n % 44) / 5) - 1);
  spans.forEach(function (s, i) {
    var on = i >= target.length - settled;
    s.textContent = on ? target[i] : hex[Math.random() * 16 | 0];
    s.className = on ? "hb-c hb-on" : "hb-c hb-off";
  });
}, 90);

02Binary bit swap

Each letter first shows up as an eight-digit binary string like 01001000, then briefly vanishes as the real character cross-fades in over the top. The letters flip left to right in sequence, and that stagger isn't run by JavaScript at all — a parent element sets an inline animation-delay, and every child inherits it with animation-delay: inherit.

opacity crossfadeJS 9줄animation-delay stagger
.bn-ch {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(.85);
}

@keyframes bn-ch-in {
  0%, 40% { opacity: 0; transform: scale(.85); }
  55%, 90% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(.85); }
}

.bn-word.is-demo .bn-ch {
  animation: bn-ch-in 2s ease-out infinite;
  animation-delay: inherit;
}

03Cipher alphabet cycle

Every letter starts a few spots ahead in the alphabet and steps backward one position at a time until it lands on the real character. It reads like turning the dial on a combination lock, and each letter repeats its own fixed offset on a loop.

charCode 연산JS 10줄textContent 교체
var offs = [5, 6, 4, 7, 5, 6], cyc = 40;
var n = 0;
setInterval(function () {
  n++;
  spans.forEach(function (s, i) {
    var t = ((n - i * 2) % cyc + cyc) % cyc;
    var left = Math.max(0, offs[i] - Math.floor(t / 2));
    var base = target.charCodeAt(i) - 65;
    s.textContent = String.fromCharCode(65 + (base + left) % 26);
  });
}, 130);

04Password dot morph

Letters start out hidden behind round dots, the way a password field hides what you type, then each word grows slightly larger as the dots fade and the real letters show through underneath. A dot layer and a letter layer sit stacked on top of each other, and the only thing moving is opacity and scale running in opposite directions.

position:absolute stackopacity+scale@keyframes pd-show
.pd-real {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(.9);
}

@keyframes pd-show {
  0%, 18% { opacity: 0; transform: scale(.9); }
  48%, 78% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(.9); }
}

.pd-wrap.is-demo .pd-real { animation: pd-show 2s cubic-bezier(.2, .8, .2, 1) infinite; }

05Scanline mask sweep

Staticky noise blocks sit over the text until a bright scanline sweeps from top to bottom, clearing the noise and leaving crisp letters behind as it passes. The noise layer's clip-path widens from the top down, uncovering the real text underneath as it goes.

clip-path: inset()repeating-linear-gradient@keyframes sl-beam
.sl-noise {
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, .4) 0 2px, transparent 2px 4px);
  mix-blend-mode: screen;
}

@keyframes sl-clear {
  0%, 8% { clip-path: inset(0 0 0 0); }
  55%, 78% { clip-path: inset(100% 0 0 0); }
  100% { clip-path: inset(0 0 0 0); }
}

.sl-wrap.is-demo .sl-noise { animation: sl-clear 2s ease-out infinite; }

06Redacted bar lift

Black bars cover each word the way a classified document gets blacked out, then peel back from the right so the letters show up in order. The two words don't lift at the same time — the second word carries a short delay so the first one clears first.

@keyframes rb-liftscaleX transform-originanimation-delay stagger
.rb-bar {
  position: absolute;
  inset: -8% -6%;
  background: $stage-ink;
  transform-origin: right center;
}

@keyframes rb-lift {
  0%, 12% { transform: scaleX(1); }
  50%, 78% { transform: scaleX(0); }
  100% { transform: scaleX(1); }
}

.rb-wrap.is-demo .rb-word .rb-bar { animation: rb-lift 2s ease-out infinite; }
.rb-wrap.is-demo .rb-word--2 .rb-bar { animation-delay: .3s; }

07Countdown checksum

The heading starts as a row of underscores, and as a percentage counter next to it climbs, letters fill in one by one from the left, each popping slightly into place as it lands. The percentage is just the count of filled letters divided by the total, so the number and the text never drift out of sync.

JS 10줄content 채우기scale pop
var n = 0;
setInterval(function () {
  n++;
  var filled = Math.min(target.length, Math.floor((n % 50) / 5));
  spans.forEach(function (s, i) {
    s.textContent = i < filled ? target[i] : "_";
    s.classList.toggle("cc-pop", i === filled - 1);
  });
  pct.textContent = Math.round(filled / target.length * 100) + "%";
}, 90);

08Negative invert flash

A color-inverted, negative-film version of the heading flashes two or three times, then fades out for good and leaves the normal-colored real text sitting underneath. It's one inverted layer stacked on top of the real letters, with nothing moving but opacity switching on and off a few times.

@keyframes ni-flashopacity flicker keyframesposition:absolute overlay
.ni-negative {
  position: absolute;
  inset: 0;
  color: $stage-paper;
  background: $color;
}

@keyframes ni-flash {
  0%, 4% { opacity: 1; }
  9% { opacity: 0; }
  14% { opacity: 1; }
  19% { opacity: 0; }
  24% { opacity: 1; }
  32%, 100% { opacity: 0; }
}

.ni-wrap.is-demo .ni-negative { animation: ni-flash 2s ease-out infinite; }

09Radar sweep fill

A bright wedge of light circles the heading once, radar-screen style, and each letter locks into full color right as the sweep passes over it. Every letter gets its own animation-delay, spaced out by an SCSS loop so the brightening lines up with wherever the sweep currently is.

conic-gradienttransform: rotateanimation-delay per letter
.rs-sweep {
  position: absolute;
  inset: -60%;
  background: conic-gradient(from 0deg, transparent 0deg, rgba(61, 220, 132, .5) 10deg, transparent 34deg);
  mix-blend-mode: screen;
}

@keyframes rs-lock {
  0%, 4% { opacity: .16; }
  14%, 88% { opacity: 1; }
  100% { opacity: .16; }
}

@for $i from 1 through 8 {
  .rs-wrap.is-demo .rs-text span:nth-child(#{$i}) {
    animation-delay: calc(2s / 8 * #{$i - 1});
  }
}

Where do CSS text reveal effects break?

Item 04 was the first one that actually broke. The dots and the real letters were stacked as a cross-fade, and the first pass squeezed the transition into a narrow 42–58% slice of the loop — only 5 of 23 frames actually had pixels changing, short of the 6-frame minimum this site measures every demo against. It looked fine to the eye, spinning along smoothly, but stepping through the frames one at a time showed most of them were pixel-for-pixel identical. Widening that transition window to 18–48% fixed it: 10 of 23 frames now carry real movement, comfortably past the bar. Because the renderer decides "did this move" by comparing pixels between frames, a cross-fade that finishes in an instant can look perfectly smooth to a person while reading as a frozen image to a measurement script. Item 08's negative flash runs into a milder version of the same thing — its flicker is so quick that only 8 frames out of 23 show any change, the lowest count of the nine, though it still clears the 6-frame floor with room to spare. Item 09's radar sweep sits at the opposite end: all 23 frames move, tied with item 01's hex settle for the longest continuously-moving effect here. The password that opens this zip is 8y6rrjcm, and you'll come across it naturally by the time you've read through all nine items above.

Accessibility — what all nine leave behind under reduced motion

Anyone who has their system set to reduce motion sees all nine of these land straight on their finished state under prefers-reduced-motion: reduce, with no scrambling in between. Items 02, 04, 05, 06, 08, and 09 run on SCSS @keyframes, so their final look — letters fully revealed, bars lifted, the sweep already finished — gets pinned in place with !important and shows up immediately. Items 01, 03, and 07 are driven by JavaScript instead of CSS, so the script itself checks matchMedia directly and skips the scrambling loop entirely, rendering the finished word right away.

If you like this kind of entrance effect, 9 CSS Text Animation Effects for Reveals covers a different set of nine, and 9 CSS Typing Animation, One Line, Copy-Paste is worth a look if a blinking cursor is closer to what you want. For everything else on this site, the CSS category hub is the place to browse. The exact values clip-path accepts are documented on MDN if you want to bend item 05's sweep into a different shape.

FAQ — frequently asked questions

Does a css text reveal effect need JavaScript?

Most of them don't. Six of the nine here run entirely on SCSS @keyframes with opacity, transform, and clip-path — only items 01, 03, and 07 need a short script, because those three have to randomize or fill in the actual text content, which CSS on its own can't touch.

Should I use item 01's hex settle or item 03's cipher cycle?

If you want the feeling of a code unlocking or a login succeeding, item 01 reads as more technical. If it's a puzzle or an unlock moment where pieces click into place one at a time, item 03's combination-lock dial fits better.

Are the vanilla and React code in the zip actually different?

The motion itself is identical between the vanilla and React versions. The only difference is when it plays: the vanilla demo loops forever using an is-demo class, while the React component uses an IntersectionObserver to play once, the moment it scrolls into view.

Enter the archive password

The password is inside this article. You will find it as you read.