GODRICH
#css-animation#button#loader#transition#scroll#dashboard#ui#design-trends

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). The card-scale version of that same settling motion lives in 9 CSS Card Hover Effects, and the surface-shine branch of this feedback pillar is 9 CSS Holographic Card Effects.

01Hex byte settle

A hacker text effect css 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.

Hex byte settle
Open in a new tab

01 · Hex byte settle — Measured area 2.084% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

setIntervalJS 10줄color transition

Why a script was needed

CSS alone can't rewrite the text content of an element, so this one needed a short script to drive it.

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

Binary first, then the letter

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.

Binary bit swap
Open in a new tab

02 · Binary bit swap — Measured area 1.574% · moving frames 13/23Original by GODRICH · SCSS and React ship in the zip · See the password

opacity crossfadeJS 9줄animation-delay stagger

A stagger from inherit

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.

.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

Stepping backward through the alphabet

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.

Cipher alphabet cycle
Open in a new tab

03 · Cipher alphabet cycle — Measured area 2.021% · moving frames 10/23Original by GODRICH · SCSS and React ship in the zip · See the password

charCodeAt 연산JS 10줄textContent 교체

Like a combination lock

It reads like turning the dial on a combination lock, and each letter repeats its own fixed offset on a loop.

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

Dots hiding the letters

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.

Password dot morph
Open in a new tab

04 · Password dot morph — Measured area 1.936% · moving frames 10/23Original by GODRICH · SCSS and React ship in the zip · See the password

position:absolute stackopacity+scale@keyframes pd-show

Two layers moving in opposite directions

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.

.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

A scanline clearing the noise

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.

Scanline mask sweep
Open in a new tab

05 · Scanline mask sweep — Measured area 6.767% · moving frames 19/23Original by GODRICH · SCSS and React ship in the zip · See the password

clip-path: inset()repeating-linear-gradient@keyframes sl-beam

A clip-path widening downward

The noise layer's clip-path widens from the top down, uncovering the real text underneath as it goes.

.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

Bars peeling back from the right

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.

Redacted bar lift
Open in a new tab

06 · Redacted bar lift — Measured area 4.215% · moving frames 19/23Original by GODRICH · SCSS and React ship in the zip · See the password

@keyframes rb-liftscaleX transform-originanimation-delay stagger

Why the second word waits

The two words don't lift at the same time — the second word carries a short delay so the first one clears first.

.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

A css text loading effect that counts

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.

Countdown checksum
Open in a new tab

07 · Countdown checksum — Measured area 1.409% · moving frames 16/23Original by GODRICH · SCSS and React ship in the zip · See the password

JS 10줄content 채우기scale pop

Why the number never drifts

The percentage is just the count of filled letters divided by the total, so the number and the text never drift out of sync.

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 negative flashing twice

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.

Negative invert flash
Open in a new tab

08 · Negative invert flash — Measured area 4.139% · moving frames 8/23Original by GODRICH · SCSS and React ship in the zip · See the password

@keyframes ni-flashopacity flicker keyframesposition:absolute overlay

One layer, one property

It's one inverted layer stacked on top of the real letters, with nothing moving but opacity switching on and off a few times.

.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 wedge circling the heading

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.

Radar sweep fill
Open in a new tab

09 · Radar sweep fill — Measured area 10.213% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

conic-gradienttransform: rotateanimation-delay per letter

Delays spaced by an SCSS loop

Every letter gets its own animation-delay, spaced out by an SCSS loop so the brightening lines up with wherever the sweep currently is.

.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?

Only 5 frames of 23 moved

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.

The least and the most movement

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. The scanline panels in HUD Interface Design run into the same blind spot whenever a sweep crosses the screen too briefly to register.

Accessibility — what all nine leave behind under reduced motion

Landing straight on the finished state

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.

What the script checks instead

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.

Where to go next

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

Do these css text transitions 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. If the goal is a sentence that stands still rather than one that decodes itself, the quote boxes in Blockquote CSS are the closer fit.

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.