Updated 2026-10-14. 6 effects.
A CSS glitch text effect layers two copies of a word in offset colours with pseudo-elements, then shifts thin horizontal slices of them using clip-path in short, irregular keyframes. Good glitches are brief and rare. These stay readable, flash well below three times a second, and stop completely for reduced motion.
Glitches suit music, games, security products and anything about signals or interference. Use the occasional-burst styles on a headline people will read, and the hover and focus versions on links and buttons where the effect rewards interaction instead of running constantly.
Each version keeps one real, readable word in the markup. The colour-shifted copies are drawn from the same text through pseudo-elements or aria-hidden layers, so screen readers announce the word once and nothing else.
| Effect | Built with | Size (gzipped) | Running cost |
|---|---|---|---|
| RGB Channel Split Glitch Headline | CSS only | 1.7 KB | Two pseudo elements animate clip-path and transform in short stepped bursts; no JavaScript beyond an off-screen pause. |
| Broken Signal Word Glitch | CSS only | 1.6 KB | One word with two pseudo copies that only change during a brief stepped burst, so the page is idle most of each cycle. |
| CRT Phosphor Flicker Headline | CSS only | 1.7 KB | Opacity flicker and one translated gradient band; the layered text shadows are painted once and then only faded. |
| Data Corruption Text Resolving | CSS only | 2 KB | A throttled loop rewrites about thirty single-character spans roughly eighteen times a second, and stops off screen. |
| Hover and Focus Glitch Menu Links | CSS only | 2.2 KB | Nothing runs at rest; each hover plays one half-second clip-path burst, and the demo timer stops off screen. |
| VHS Tracking Wobble Text | CSS only | 1.9 KB | Three small CSS loops on transform and clip-path over one headline, so paint work stays light and only an off-screen pause runs in JavaScript. |
A dark hero headline whose red and cyan colour channels sit two pixels apart, like a slightly misaligned monitor, and tear into horizontal slices during two brief bursts in every cycle. Between bursts the type rests, so the line stays easy to read. It is pure CSS: two pseudo elements copy the text from a data attribute, and clip-path cuts the slices. On phones the headline wraps and the copies wrap with it.
<section class="w10rgb" data-paused="false">
<div class="w10rgb-glow" aria-hidden="true"></div>
<div class="w10rgb-lines" aria-hidden="true"></div>
<div class="w10rgb-inner">
<p class="w10rgb-eyebrow">Nightshift Radio · 98.4 FM</p>
<h1 class="w10rgb-title">
<span class="w10rgb-sr">Static after midnight</span>
<span class="w10rgb-shift" aria-hidden="true"><span class="w10rgb-word" data-text="Static after midnight">Static after midnight</span></span>
</h1>
<p class="w10rgb-copy">Independent late-night radio from a converted bakery basement. New mixes every Friday from eleven.</p>
<a class="w10rgb-btn" href="#">Listen live</a>
</div>
</section>
<style>
/* Change these: the two channel colours and how often a burst happens (two short bursts per cycle). */
.w10rgb { --w10rgb-red: #ff4766; --w10rgb-cyan: #2fd3ff; --w10rgb-cycle: 4.6s; --w10rgb-bg: #0f1113; }
.w10rgb { position: relative; overflow: hidden; background: var(--w10rgb-bg); padding: 112px 20px; font-family: ui-sans-serif, system-ui, sans-serif; }
.w10rgb-glow, .w10rgb-lines { position: absolute; inset: 0; pointer-events: none; }
.w10rgb-glow { background: radial-gradient(60% 55% at 50% 42%, rgba(70, 92, 110, 0.35), transparent 70%), radial-gradient(40% 40% at 80% 90%, rgba(255, 71, 102, 0.08), transparent 70%); }
.w10rgb-lines { background: repeating-linear-gradient(to bottom, rgba(255,255,255,0.035) 0 1px, transparent 1px 3px); }
.w10rgb-inner { position: relative; max-width: 1024px; margin: 0 auto; text-align: center; }
.w10rgb-eyebrow { margin: 0; font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.3em; text-transform: uppercase; color: #9aa6b0; }
.w10rgb-title { margin: 24px 0 0; font-size: clamp(44px, 9vw, 112px); font-weight: 800; line-height: 0.95; letter-spacing: -0.025em; color: #eeece6; }
.w10rgb-copy { max-width: 36rem; margin: 24px auto 0; font-size: 18px; line-height: 1.6; color: #b9c1c8; }
.w10rgb-btn { display: inline-flex; align-items: center; height: 48px; margin-top: 36px; padding: 0 28px; border-radius: 999px; background: #eeece6; color: var(--w10rgb-bg); font-size: 14px; font-weight: 600; text-decoration: none; }
.w10rgb-btn:hover { background: #fff; }
.w10rgb-btn:focus-visible { outline: 2px solid var(--w10rgb-cyan); outline-offset: 3px; }
.w10rgb-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.w10rgb-word { position: relative; display: inline-block; }
.w10rgb-word::before, .w10rgb-word::after { content: attr(data-text); position: absolute; inset: 0; pointer-events: none; mix-blend-mode: screen; }
.w10rgb-word::before { color: var(--w10rgb-red); transform: translate(-2px, 0); animation: w10rgb-red var(--w10rgb-cycle) steps(1, end) infinite; }
.w10rgb-word::after { color: var(--w10rgb-cyan); transform: translate(2px, 0); animation: w10rgb-cyan var(--w10rgb-cycle) steps(1, end) infinite; }
.w10rgb-shift { display: inline-block; animation: w10rgb-shift var(--w10rgb-cycle) steps(1, end) infinite; }
@keyframes w10rgb-red {
0% { clip-path: inset(0 0 0 0); transform: translate(-2px, 0); }
3% { clip-path: inset(14% 0 62% 0); transform: translate(-11px, 0); }
5% { clip-path: inset(61% 0 21% 0); transform: translate(8px, 0); }
7% { clip-path: inset(36% 0 44% 0); transform: translate(-6px, 0); }
9% { clip-path: inset(0 0 0 0); transform: translate(-2px, 0); }
55% { clip-path: inset(72% 0 8% 0); transform: translate(12px, 0); }
57% { clip-path: inset(0 0 0 0); transform: translate(-2px, 0); }
}
@keyframes w10rgb-cyan {
0% { clip-path: inset(0 0 0 0); transform: translate(2px, 0); }
3% { clip-path: inset(48% 0 30% 0); transform: translate(10px, 0); }
5% { clip-path: inset(8% 0 74% 0); transform: translate(-9px, 0); }
7% { clip-path: inset(66% 0 12% 0); transform: translate(5px, 0); }
9% { clip-path: inset(0 0 0 0); transform: translate(2px, 0); }
55% { clip-path: inset(20% 0 64% 0); transform: translate(-10px, 0); }
57% { clip-path: inset(0 0 0 0); transform: translate(2px, 0); }
}
@keyframes w10rgb-shift {
0% { transform: none; }
4% { transform: translateX(3px) skewX(-4deg); }
6% { transform: translateX(-2px); }
8% { transform: none; }
}
.w10rgb[data-paused="true"] .w10rgb-word::before, .w10rgb[data-paused="true"] .w10rgb-word::after, .w10rgb[data-paused="true"] .w10rgb-shift { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
.w10rgb-word::before, .w10rgb-word::after, .w10rgb-shift { animation: none; }
}
</style>
<script>
(function () {
var root = document.querySelector(".w10rgb");
if (!root || !("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
root.setAttribute("data-paused", entries[0].isIntersecting ? "false" : "true");
}).observe(root);
})();
</script>
With reduced motion: The headline stays still with a fixed two pixel red and cyan offset and no slicing.
Running cost: Two pseudo elements animate clip-path and transform in short stepped bursts; no JavaScript beyond an off-screen pause.
Full page for RGB Channel Split Glitch Headline
An editorial serif headline on warm paper where only one word misbehaves: about once every five seconds, the highlighted word jolts sideways and splits into rose and teal slices for a third of a second, then sits still again. The rest of the sentence never moves. It makes a point with a single word, which suits repair, support and rescue services.
<section class="w10sig" data-paused="false">
<div class="w10sig-inner">
<p class="w10sig-eyebrow">Patchwork Web Repair</p>
<h1 class="w10sig-title">
Your website is
<span class="w10sig-mark"><span class="w10sig-sr">broken.</span><span class="w10sig-word" data-text="broken" aria-hidden="true">broken</span><span aria-hidden="true">.</span></span>
<span class="w10sig-soft">We will mend it by Friday.</span>
</h1>
<p class="w10sig-copy">Dead links, a contact form that stopped sending, a checkout that hangs on phones. Send us the address and we will send back a fixed site and a plain list of what was wrong.</p>
<div class="w10sig-row">
<a class="w10sig-btn" href="#">Get a repair quote</a>
<span class="w10sig-note">Fixed price, quoted within a day</span>
</div>
</div>
</section>
<style>
/* Change these: text colour, the two glitch colours, and the time between glitches (one short burst per cycle). */
.w10sig { --w10sig-ink: #1d1b18; --w10sig-rose: #d1495b; --w10sig-teal: #1f8a82; --w10sig-every: 5s; }
.w10sig { background: #f3efe6; padding: 112px 20px; font-family: ui-sans-serif, system-ui, sans-serif; }
.w10sig-inner { max-width: 896px; margin: 0 auto; }
.w10sig-eyebrow { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: #6b5f52; }
.w10sig-title { margin: 20px 0 0; font-family: ui-serif, Georgia, serif; font-size: clamp(40px, 7.4vw, 92px); font-weight: 500; line-height: 1.02; letter-spacing: -0.02em; color: var(--w10sig-ink); }
.w10sig-soft { color: #5b5248; }
.w10sig-copy { max-width: 36rem; margin: 32px 0 0; font-size: 18px; line-height: 1.65; color: #4a433b; }
.w10sig-row { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin-top: 40px; }
.w10sig-btn { display: inline-flex; align-items: center; height: 48px; padding: 0 24px; border-radius: 8px; background: var(--w10sig-ink); color: #f3efe6; font-size: 14px; font-weight: 600; text-decoration: none; }
.w10sig-btn:hover { background: #33302b; }
.w10sig-btn:focus-visible { outline: 2px solid var(--w10sig-rose); outline-offset: 3px; }
.w10sig-note { font-size: 14px; color: #5b5248; }
.w10sig-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.w10sig-mark { white-space: nowrap; background: linear-gradient(transparent 62%, rgba(209,73,91,0.22) 62% 92%, transparent 92%); }
.w10sig-word { position: relative; display: inline-block; animation: w10sig-jolt var(--w10sig-every) steps(1, end) infinite; }
.w10sig-word::before, .w10sig-word::after { content: attr(data-text); position: absolute; inset: 0; pointer-events: none; mix-blend-mode: multiply; opacity: 0; }
.w10sig-word::before { color: var(--w10sig-rose); animation: w10sig-a var(--w10sig-every) steps(1, end) infinite; }
.w10sig-word::after { color: var(--w10sig-teal); animation: w10sig-b var(--w10sig-every) steps(1, end) infinite; }
@keyframes w10sig-jolt { 0% { transform: none; } 30% { transform: translateX(-3px) skewX(6deg); } 32% { transform: translateX(4px); } 34% { transform: translateX(-1px) skewX(-3deg); } 36% { transform: none; } }
@keyframes w10sig-a {
0% { opacity: 0; clip-path: inset(0 0 0 0); transform: none; }
30% { opacity: 1; clip-path: inset(10% 0 58% 0); transform: translateX(-12px); }
32% { opacity: 1; clip-path: inset(55% 0 20% 0); transform: translateX(9px); }
34% { opacity: 1; clip-path: inset(30% 0 42% 0); transform: translateX(-5px); }
36% { opacity: 0; clip-path: inset(0 0 0 0); transform: none; }
}
@keyframes w10sig-b {
0% { opacity: 0; clip-path: inset(0 0 0 0); transform: none; }
30% { opacity: 1; clip-path: inset(60% 0 12% 0); transform: translateX(10px); }
32% { opacity: 1; clip-path: inset(18% 0 60% 0); transform: translateX(-8px); }
34% { opacity: 1; clip-path: inset(70% 0 4% 0); transform: translateX(6px); }
36% { opacity: 0; clip-path: inset(0 0 0 0); transform: none; }
}
.w10sig[data-paused="true"] .w10sig-word, .w10sig[data-paused="true"] .w10sig-word::before, .w10sig[data-paused="true"] .w10sig-word::after { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
.w10sig-word, .w10sig-word::before, .w10sig-word::after { animation: none; }
}
</style>
<script>
(function () {
var root = document.querySelector(".w10sig");
if (!root || !("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
root.setAttribute("data-paused", entries[0].isIntersecting ? "false" : "true");
}).observe(root);
})();
</script>
With reduced motion: The headline is calm and still, with the key word sitting on its soft rose highlighter mark.
Running cost: One word with two pseudo copies that only change during a brief stepped burst, so the page is idle most of each cycle.
Amber monospace type glowing on a curved, vignetted tube screen set inside a warm bezel. The phosphor dims by a few percent at uneven moments, a faint bright band rolls slowly down the glass, and a block cursor ticks after the headline. It is a quiet, believable monitor rather than a loud malfunction, and fits repair shops, archives and developer tools.
<section class="w10crt" data-paused="false">
<div class="w10crt-bezel">
<div class="w10crt-screen">
<div class="w10crt-rollwrap" aria-hidden="true"><div class="w10crt-roll"></div></div>
<div class="w10crt-content">
<p class="w10crt-text w10crt-eyebrow">Halvorsen TV & Radio Repair · Bench 2</p>
<h1 class="w10crt-text w10crt-title">We still fix the old sets.<span class="w10crt-caret" aria-hidden="true"></span></h1>
<p class="w10crt-text w10crt-copy">Tube televisions, reel decks and valve radios, repaired by hand with a six month warranty on every job.</p>
<a class="w10crt-btn" href="#">Book a bench slot</a>
</div>
<div class="w10crt-scan" aria-hidden="true"></div>
</div>
</div>
</section>
<style>
/* Change these: phosphor colour (try #7dff9b for green), its glow, and how slowly the bright band rolls. */
.w10crt { --w10crt-phosphor: #ffb547; --w10crt-glow: rgba(255, 170, 60, 0.55); --w10crt-roll: 7s; }
.w10crt { background: #17130f; padding: 80px 20px; }
.w10crt-bezel { max-width: 1024px; margin: 0 auto; padding: 18px; border-radius: 28px; background: #2b241d; box-shadow: inset 0 2px 0 rgba(255,255,255,0.06); }
.w10crt-screen { position: relative; overflow: hidden; padding: 80px clamp(20px, 6vw, 56px); border-radius: 22px; background: radial-gradient(120% 90% at 50% 45%, #2a1c0c 0%, #120c06 62%, #070503 100%); box-shadow: inset 0 0 90px rgba(0,0,0,0.85), inset 0 0 0 1px rgba(255,190,110,0.08), 0 30px 60px -20px rgba(0,0,0,0.7); }
.w10crt-rollwrap, .w10crt-scan { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.w10crt-scan { background: repeating-linear-gradient(to bottom, rgba(0,0,0,0.35) 0 1px, transparent 1px 3px); }
.w10crt-roll { position: absolute; left: 0; right: 0; top: 0; height: 30%; background: linear-gradient(to bottom, transparent, rgba(255,190,110,0.06) 50%, transparent); transform: translateY(-110%); animation: w10crt-roll var(--w10crt-roll) linear infinite; }
.w10crt-content { position: relative; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.w10crt-text { color: var(--w10crt-phosphor); text-shadow: 0 0 2px #ffd28a, 0 0 10px var(--w10crt-glow), 0 0 28px rgba(255,140,30,0.3); animation: w10crt-flicker 3.4s linear infinite; }
.w10crt-eyebrow { margin: 0; font-size: 13px; letter-spacing: 0.25em; text-transform: uppercase; }
.w10crt-title { margin: 24px 0 0; font-size: clamp(34px, 6.4vw, 76px); font-weight: 700; line-height: 1.05; letter-spacing: -0.02em; }
.w10crt-copy { max-width: 42rem; margin: 24px 0 0; font-size: 16px; line-height: 1.65; }
.w10crt-caret { display: inline-block; width: 0.5em; height: 0.85em; margin-left: 0.12em; vertical-align: -0.08em; background: var(--w10crt-phosphor); box-shadow: 0 0 12px var(--w10crt-glow); animation: w10crt-blink 1.1s steps(1, end) infinite; }
.w10crt-btn { display: inline-flex; align-items: center; height: 48px; margin-top: 36px; padding: 0 24px; border: 1px solid var(--w10crt-phosphor); color: var(--w10crt-phosphor); font-size: 14px; letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none; }
.w10crt-btn:hover { background: var(--w10crt-phosphor); color: #120c06; }
.w10crt-btn:focus-visible { outline: 2px solid #ffd28a; outline-offset: 3px; }
@keyframes w10crt-roll { to { transform: translateY(440%); } }
@keyframes w10crt-flicker { 0%, 100% { opacity: 1; } 8% { opacity: 0.93; } 9% { opacity: 1; } 47% { opacity: 0.96; } 48% { opacity: 1; } 71% { opacity: 0.94; } 73% { opacity: 1; } }
@keyframes w10crt-blink { 0% { opacity: 1; } 50% { opacity: 0; } }
.w10crt[data-paused="true"] .w10crt-roll, .w10crt[data-paused="true"] .w10crt-text, .w10crt[data-paused="true"] .w10crt-caret { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
.w10crt-roll, .w10crt-text, .w10crt-caret { animation: none; }
.w10crt-roll { transform: translateY(120%); }
}
@media (max-width: 640px) { .w10crt { padding: 56px 16px; } .w10crt-bezel { padding: 12px; } .w10crt-screen { padding: 56px 20px; } }
</style>
<script>
(function () {
var root = document.querySelector(".w10crt");
if (!root || !("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
root.setAttribute("data-paused", entries[0].isIntersecting ? "false" : "true");
}).observe(root);
})();
</script>
With reduced motion: A steady amber screen at full brightness with the scan band parked near the top and a solid cursor.
Running cost: Opacity flicker and one translated gradient band; the layered text shadows are painted once and then only faded.
A monospace headline on a deep navy grid that arrives as scrambled symbols and repairs itself from left to right in about a second and a half. It then holds clean for several seconds before a scatter of characters breaks again and the repair repeats. Because every glyph is the same width, the line never shifts while it changes, which makes it a good fit for backup, security and data tools.
<section class="w10dcr">
<div class="w10dcr-grid" aria-hidden="true"></div>
<div class="w10dcr-inner">
<p class="w10dcr-eyebrow">Coldvault · offsite backup</p>
<h1 class="w10dcr-title"><span class="w10dcr-sr">Your files, restored intact.</span><span class="w10dcr-live" aria-hidden="true">Your files, restored intact.</span></h1>
<p class="w10dcr-copy">Encrypted nightly copies of every laptop in the office, checked byte for byte each week so a restore is never a gamble.</p>
<div class="w10dcr-row">
<a class="w10dcr-btn" href="#">Run a test restore</a>
<span class="w10dcr-note">Last check passed 06:00</span>
</div>
</div>
</section>
<style>
/* Change these: background, clean text colour, and the dim colour of corrupted characters. */
.w10dcr { --w10dcr-bg: #0b1322; --w10dcr-clean: #b9f5dc; --w10dcr-noise: #3f8f78; }
.w10dcr { position: relative; overflow: hidden; background: var(--w10dcr-bg); padding: 112px 20px; font-family: ui-sans-serif, system-ui, sans-serif; }
.w10dcr-grid { position: absolute; inset: 0; pointer-events: none; background-image: linear-gradient(rgba(148,233,200,0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(148,233,200,0.05) 1px, transparent 1px); background-size: 32px 32px; mask-image: radial-gradient(70% 70% at 50% 45%, #000, transparent); -webkit-mask-image: radial-gradient(70% 70% at 50% 45%, #000, transparent); }
.w10dcr-inner { position: relative; max-width: 896px; margin: 0 auto; }
.w10dcr-eyebrow, .w10dcr-note { margin: 0; font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.3em; text-transform: uppercase; color: #7fb8a6; }
.w10dcr-title { margin: 24px 0 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: clamp(32px, 6.2vw, 72px); font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; color: var(--w10dcr-clean); }
.w10dcr-copy { max-width: 36rem; margin: 28px 0 0; font-size: 18px; line-height: 1.6; color: #a9c4bb; }
.w10dcr-row { display: flex; flex-wrap: wrap; align-items: center; gap: 20px; margin-top: 40px; }
.w10dcr-btn { display: inline-flex; align-items: center; height: 48px; padding: 0 24px; border-radius: 6px; background: var(--w10dcr-clean); color: var(--w10dcr-bg); font: 600 14px ui-monospace, SFMono-Regular, Menlo, monospace; text-decoration: none; }
.w10dcr-btn:hover { background: #fff; }
.w10dcr-btn:focus-visible { outline: 2px solid var(--w10dcr-clean); outline-offset: 3px; }
.w10dcr-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.w10dcr-c[data-state="noise"] { color: var(--w10dcr-noise); }
.w10dcr-c[data-state="clean"] { color: var(--w10dcr-clean); text-shadow: 0 0 18px rgba(120, 240, 190, 0.25); }
@media (prefers-reduced-motion: reduce) { .w10dcr-c { transition: none; } }
</style>
<script>
(function () {
/* Timing in milliseconds: settle left to right, hold the clean line, then let corruption spread. */
var RESOLVE_MS = 1500, HOLD_MS = 4200, BREAK_MS = 700;
var NOISE = "#%&*+=/<>?!01$@{}[]";
var root = document.querySelector(".w10dcr");
if (!root) return;
var holder = root.querySelector(".w10dcr-live");
var text = holder.textContent;
holder.textContent = "";
var spans = [];
for (var i = 0; i < text.length; i++) {
var s = document.createElement("span");
s.className = "w10dcr-c";
s.setAttribute("data-state", "clean");
s.textContent = text[i];
holder.appendChild(s);
spans.push(s);
}
function show(i, clean) {
if (text[i] === " ") return;
spans[i].textContent = clean ? text[i] : NOISE[Math.floor(Math.random() * NOISE.length)];
spans[i].setAttribute("data-state", clean ? "clean" : "noise");
}
var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduce) return;
var n = text.length, cycle = RESOLVE_MS + HOLD_MS + BREAK_MS;
var breakAt = []; for (var k = 0; k < n; k++) breakAt.push(Math.random());
var start = performance.now(), last = 0, frame = 0;
function tick(now) {
frame = requestAnimationFrame(tick);
if (now - last < 55) return;
last = now;
var t = (now - start) % cycle;
for (var i = 0; i < n; i++) {
var clean;
if (t < RESOLVE_MS) clean = t > 150 + (i / n) * (RESOLVE_MS - 350);
else if (t < RESOLVE_MS + HOLD_MS) clean = true;
else clean = (t - RESOLVE_MS - HOLD_MS) / BREAK_MS < breakAt[i];
show(i, clean);
}
}
function play() { if (!frame) { start = performance.now(); frame = requestAnimationFrame(tick); } }
function pause() { cancelAnimationFrame(frame); frame = 0; for (var i = 0; i < n; i++) show(i, true); }
if ("IntersectionObserver" in window) {
new IntersectionObserver(function (entries) { if (entries[0].isIntersecting) play(); else pause(); }).observe(root);
} else play();
})();
</script>
With reduced motion: The clean headline is shown from the start, with no scrambled characters at any point.
Running cost: A throttled loop rewrites about thirty single-character spans roughly eighteen times a second, and stops off screen.
A large typographic studio menu on pale sea-glass green. Pointing at a link, or tabbing to it, fires a single half-second glitch: the label jolts, splits into red and blue slices, and an underline sweeps across the row. Until someone interacts, the links take turns showing the effect so the menu never looks inert.
<section class="w10hov">
<div class="w10hov-inner">
<div class="w10hov-head">
<h2 class="w10hov-brand">Fernhill Type & Print</h2>
<p class="w10hov-hint">Hover or tab through the menu</p>
</div>
<nav aria-label="Studio">
<ul class="w10hov-list">
<li><a class="w10hov-link" href="#"><span class="w10hov-left"><span class="w10hov-num" aria-hidden="true">01</span><span class="w10hov-big"><span class="w10hov-sr">Selected work</span><span class="w10hov-txt" data-text="Selected work" aria-hidden="true">Selected work</span></span></span><span class="w10hov-note">Identity and packaging, 2019 to now</span><span class="w10hov-rule" aria-hidden="true"></span></a></li>
<li><a class="w10hov-link" href="#"><span class="w10hov-left"><span class="w10hov-num" aria-hidden="true">02</span><span class="w10hov-big"><span class="w10hov-sr">Studio</span><span class="w10hov-txt" data-text="Studio" aria-hidden="true">Studio</span></span></span><span class="w10hov-note">Four designers in a former print shop</span><span class="w10hov-rule" aria-hidden="true"></span></a></li>
<li><a class="w10hov-link" href="#"><span class="w10hov-left"><span class="w10hov-num" aria-hidden="true">03</span><span class="w10hov-big"><span class="w10hov-sr">Journal</span><span class="w10hov-txt" data-text="Journal" aria-hidden="true">Journal</span></span></span><span class="w10hov-note">Notes from the bench</span><span class="w10hov-rule" aria-hidden="true"></span></a></li>
<li><a class="w10hov-link" href="#"><span class="w10hov-left"><span class="w10hov-num" aria-hidden="true">04</span><span class="w10hov-big"><span class="w10hov-sr">Start a project</span><span class="w10hov-txt" data-text="Start a project" aria-hidden="true">Start a project</span></span></span><span class="w10hov-note">Replies within two working days</span><span class="w10hov-rule" aria-hidden="true"></span></a></li>
</ul>
</nav>
</div>
</section>
<style>
/* Change these: text colour, the two slice colours, and how long one burst lasts. */
.w10hov { --w10hov-ink: #13221d; --w10hov-a: #d43d51; --w10hov-b: #2259b8; --w10hov-burst: 0.5s; }
.w10hov { background: #e3ebe7; padding: 80px 20px; font-family: ui-sans-serif, system-ui, sans-serif; color: var(--w10hov-ink); }
.w10hov-inner { max-width: 1024px; margin: 0 auto; }
.w10hov-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding-bottom: 20px; border-bottom: 1px solid rgba(19,34,29,0.2); }
.w10hov-brand { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: 0.22em; text-transform: uppercase; }
.w10hov-hint { margin: 0; font-size: 14px; color: #40534b; }
.w10hov-list { list-style: none; margin: 0; padding: 0; }
.w10hov-list li { border-bottom: 1px solid rgba(19,34,29,0.2); }
.w10hov-link { position: relative; display: flex; align-items: center; justify-content: space-between; gap: 8px 24px; flex-wrap: wrap; padding: 24px 0; color: inherit; text-decoration: none; outline: none; }
.w10hov-link:focus-visible { box-shadow: 0 0 0 2px #e3ebe7, 0 0 0 4px var(--w10hov-b); }
.w10hov-left { display: flex; align-items: baseline; gap: 16px; }
.w10hov-num { width: 28px; font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; color: #40534b; }
.w10hov-big { font-size: clamp(32px, 6vw, 64px); font-weight: 600; line-height: 1; letter-spacing: -0.02em; }
.w10hov-note { font-size: 14px; color: #40534b; }
.w10hov-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.w10hov-rule { position: absolute; left: 0; bottom: -1px; width: 100%; height: 2px; background: var(--w10hov-ink); transform: scaleX(0); transform-origin: left; transition: transform 0.45s cubic-bezier(0.2, 0.8, 0.2, 1); }
.w10hov-txt { position: relative; display: inline-block; }
.w10hov-txt::before, .w10hov-txt::after { content: attr(data-text); position: absolute; inset: 0; pointer-events: none; mix-blend-mode: multiply; opacity: 0; }
.w10hov-txt::before { color: var(--w10hov-a); }
.w10hov-txt::after { color: var(--w10hov-b); }
.w10hov-link:hover .w10hov-txt, .w10hov-link:focus-visible .w10hov-txt, .w10hov-link[data-demo="on"] .w10hov-txt { animation: w10hov-jolt var(--w10hov-burst) steps(1, end) 1; }
.w10hov-link:hover .w10hov-txt::before, .w10hov-link:focus-visible .w10hov-txt::before, .w10hov-link[data-demo="on"] .w10hov-txt::before { animation: w10hov-a var(--w10hov-burst) steps(1, end) 1; }
.w10hov-link:hover .w10hov-txt::after, .w10hov-link:focus-visible .w10hov-txt::after, .w10hov-link[data-demo="on"] .w10hov-txt::after { animation: w10hov-b var(--w10hov-burst) steps(1, end) 1; }
.w10hov-link:hover .w10hov-rule, .w10hov-link:focus-visible .w10hov-rule, .w10hov-link[data-demo="on"] .w10hov-rule { transform: scaleX(1); }
@keyframes w10hov-jolt { 0% { transform: translateX(-3px) skewX(8deg); } 20% { transform: translateX(3px); } 40% { transform: skewX(-4deg); } 60%, 100% { transform: none; } }
@keyframes w10hov-a { 0% { opacity: 1; clip-path: inset(8% 0 60% 0); transform: translateX(-10px); } 20% { opacity: 1; clip-path: inset(52% 0 22% 0); transform: translateX(8px); } 40% { opacity: 1; clip-path: inset(30% 0 46% 0); transform: translateX(-4px); } 60%, 100% { opacity: 0; transform: none; } }
@keyframes w10hov-b { 0% { opacity: 1; clip-path: inset(62% 0 10% 0); transform: translateX(9px); } 20% { opacity: 1; clip-path: inset(14% 0 64% 0); transform: translateX(-7px); } 40% { opacity: 1; clip-path: inset(72% 0 6% 0); transform: translateX(5px); } 60%, 100% { opacity: 0; transform: none; } }
@media (max-width: 640px) { .w10hov-hint { display: none; } .w10hov-note { padding-left: 44px; } .w10hov-link { padding: 20px 0; } }
@media (prefers-reduced-motion: reduce) {
.w10hov-link .w10hov-txt, .w10hov-link .w10hov-txt::before, .w10hov-link .w10hov-txt::after { animation: none !important; }
.w10hov-rule { transition: none; }
}
</style>
<script>
(function () {
/* Demo cycle: one link glitches every DEMO_EVERY ms until the visitor points at or tabs into the menu. */
var DEMO_EVERY = 2400;
var root = document.querySelector(".w10hov");
if (!root) return;
var items = root.querySelectorAll(".w10hov-link");
var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
var visible = true, touched = false, i = 0, timer = 0, off = 0;
function clear() { for (var k = 0; k < items.length; k++) items[k].setAttribute("data-demo", "off"); }
function run() {
if (!visible || touched) return;
clear();
items[i % items.length].setAttribute("data-demo", "on");
off = window.setTimeout(clear, 600);
i += 1;
}
function stop() { window.clearInterval(timer); window.clearTimeout(off); timer = 0; clear(); }
function start() { if (!reduce && !touched && !timer) { window.setTimeout(run, 1450); timer = window.setInterval(run, DEMO_EVERY); } }
root.addEventListener("pointerenter", function () { touched = true; stop(); });
root.addEventListener("focusin", function () { touched = true; stop(); });
if ("IntersectionObserver" in window) {
new IntersectionObserver(function (entries) {
visible = entries[0].isIntersecting;
if (visible) start(); else stop();
}).observe(root);
} else start();
})();
</script>
With reduced motion: Links show a plain underline on hover or focus, with no jolt, slices or idle demo.
Running cost: Nothing runs at rest; each hover plays one half-second clip-path burst, and the demo timer stops off screen.
An italic serif headline on a warm tape-brown screen that sways a fraction of a pixel, while a soft tracking band rolls down the frame and drags a torn, colour-fringed strip of the words sideways as it passes. Scanlines and a blinking PLAY marker finish the rental-shop look. It suits retro shops, film nights and anything nostalgic.
<section class="w10vhs" data-paused="false">
<div class="w10vhs-bg" aria-hidden="true"></div>
<div class="w10vhs-bandwrap" aria-hidden="true"><div class="w10vhs-band"></div></div>
<div class="w10vhs-osd-l" aria-hidden="true"><span class="w10vhs-osd">PLAY ▶</span></div>
<div class="w10vhs-osd-r" aria-hidden="true">SP 0:04:12</div>
<div class="w10vhs-inner">
<p class="w10vhs-eyebrow">Corner Video Club · since 1991</p>
<h1 class="w10vhs-title">
<span class="w10vhs-sr">Be kind, rewind.</span>
<span class="w10vhs-stack" aria-hidden="true">
<span class="w10vhs-base">Be kind, rewind.</span>
<span class="w10vhs-tear">Be kind, rewind.</span>
</span>
</h1>
<p class="w10vhs-copy">Four thousand tapes, two working VCRs to borrow, and a late return box that never closes.</p>
<a class="w10vhs-btn" href="#">Browse the shelves</a>
</div>
<div class="w10vhs-scan" aria-hidden="true"></div>
</section>
<style>
/* Change these: text colour, the two fringe colours, and how long the tracking band takes to roll down. */
.w10vhs { --w10vhs-ink: #f4e6cf; --w10vhs-warm: #ff6f59; --w10vhs-cool: #4fd1c5; --w10vhs-speed: 6s; }
.w10vhs { position: relative; overflow: hidden; background: #1a1315; padding: 104px 20px 96px; font-family: ui-sans-serif, system-ui, sans-serif; }
.w10vhs-bg, .w10vhs-bandwrap, .w10vhs-scan { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.w10vhs-bg { background: radial-gradient(80% 70% at 50% 40%, #3a2526 0%, #1a1315 70%); }
.w10vhs-scan { background: repeating-linear-gradient(to bottom, rgba(0,0,0,0.28) 0 2px, transparent 2px 4px); }
.w10vhs-band { position: absolute; left: 0; right: 0; height: 18%; top: 0; transform: translateY(-120%); background: linear-gradient(to bottom, transparent, rgba(255,240,220,0.07) 40%, rgba(255,240,220,0.12) 50%, rgba(255,240,220,0.07) 60%, transparent); animation: w10vhs-band var(--w10vhs-speed) linear infinite; }
.w10vhs-osd-l, .w10vhs-osd-r { position: absolute; top: 22px; font: 14px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.15em; color: var(--w10vhs-ink); }
.w10vhs-osd-l { left: 22px; }
.w10vhs-osd-r { right: 22px; }
.w10vhs-osd { animation: w10vhs-osd 1.6s steps(1, end) infinite; }
.w10vhs-inner { position: relative; max-width: 900px; margin: 0 auto; text-align: center; }
.w10vhs-eyebrow { margin: 0; font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.28em; text-transform: uppercase; color: #d9b99a; }
.w10vhs-title { margin: 24px 0 0; font-family: ui-serif, Georgia, serif; font-style: italic; font-size: clamp(46px, 9vw, 108px); font-weight: 700; line-height: 1; letter-spacing: -0.02em; }
.w10vhs-copy { max-width: 32rem; margin: 28px auto 0; font-size: 18px; line-height: 1.6; color: #e3d3bd; }
.w10vhs-btn { display: inline-flex; align-items: center; height: 48px; margin-top: 36px; padding: 0 24px; border: 1px solid rgba(244,230,207,0.6); border-radius: 6px; color: var(--w10vhs-ink); font: 14px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none; }
.w10vhs-btn:hover { background: var(--w10vhs-ink); color: #1a1315; }
.w10vhs-btn:focus-visible { outline: 2px solid var(--w10vhs-cool); outline-offset: 3px; }
.w10vhs-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.w10vhs-stack { position: relative; display: inline-grid; }
.w10vhs-stack > span { grid-area: 1 / 1; }
.w10vhs-base { color: var(--w10vhs-ink); text-shadow: -2px 0 rgba(255,111,89,0.55), 2px 0 rgba(79,209,197,0.5), 0 0 18px rgba(255,200,160,0.25); animation: w10vhs-wobble 2.8s ease-in-out infinite; }
.w10vhs-tear { color: var(--w10vhs-ink); text-shadow: -5px 0 var(--w10vhs-warm), 5px 0 var(--w10vhs-cool); clip-path: inset(0 0 100% 0); animation: w10vhs-tear var(--w10vhs-speed) linear infinite; }
@keyframes w10vhs-wobble { 0%, 100% { transform: translateX(0) skewX(0deg); } 30% { transform: translateX(1.5px) skewX(-0.6deg); } 60% { transform: translateX(-1px) skewX(0.4deg); } }
@keyframes w10vhs-band { from { transform: translateY(-120%); } to { transform: translateY(580%); } }
@keyframes w10vhs-tear {
0% { clip-path: inset(0 0 100% 0); transform: translateX(0); }
20% { clip-path: inset(0 0 82% 0); transform: translateX(9px); }
40% { clip-path: inset(30% 0 52% 0); transform: translateX(-7px); }
60% { clip-path: inset(58% 0 24% 0); transform: translateX(11px); }
80% { clip-path: inset(84% 0 0 0); transform: translateX(-5px); }
100% { clip-path: inset(100% 0 0 0); transform: translateX(0); }
}
@keyframes w10vhs-osd { 0% { opacity: 1; } 50% { opacity: 0.35; } }
.w10vhs[data-paused="true"] .w10vhs-band, .w10vhs[data-paused="true"] .w10vhs-base, .w10vhs[data-paused="true"] .w10vhs-tear, .w10vhs[data-paused="true"] .w10vhs-osd { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
.w10vhs-band, .w10vhs-base, .w10vhs-osd { animation: none; }
.w10vhs-band { transform: translateY(210%); }
.w10vhs-tear { animation: none; clip-path: inset(46% 0 40% 0); transform: translateX(6px); }
}
</style>
<script>
(function () {
var root = document.querySelector(".w10vhs");
if (!root || !("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
root.setAttribute("data-paused", entries[0].isIntersecting ? "false" : "true");
}).observe(root);
})();
</script>
With reduced motion: A still frame: the tracking band rests mid-screen and one strip of the headline sits offset with colour fringes.
Running cost: Three small CSS loops on transform and clip-path over one headline, so paint work stays light and only an off-screen pause runs in JavaScript.
Slice glitches change clip-path and transform on two extra layers, which is light enough to run on any device. The cost comes from running all the time, so most of these sit still for several seconds and glitch for a fraction of one.
Rapid flashing can trigger seizures. WCAG 2.3.1 sets a limit of three flashes in any one second; every effect here stays well below it, avoids large saturated red flashes and turns off entirely when the visitor asks for reduced motion.
Duplicate the text with ::before and ::after using content: attr(data-text), tint and offset each copy, and animate clip-path inset values in steps so random slices of each copy jump sideways.
Make the keyframes long, for example six seconds, and put all the glitch movement in a small slice of that time, such as 92 to 96 percent. The rest of the cycle the text stays still.
It can be if it never flashes more than three times in one second, avoids big bright red flashes and stops for reduced motion. Keep bursts small in area and short in time.
If the copies are real text elements, they are read too. Use pseudo-elements, or mark the copies aria-hidden, so only the original word is announced.
More collections: Text Reveal and Scramble Animations, CSS Text Animations, Text Animations for Websites. All text effects.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.