Glitch text
Loading experiment…
The pure-CSS glitch effect from my 404 page, taken apart. Type your own text and try the variants.
How it works
Pure CSS, no JavaScript. Here are the four steps if you want to use it yourself.
1. The markup
The text goes in a data-text attribute too, for the copies. Screen readers get it once, from a visually hidden span, while the animated one is hidden from them.
<span class="glitch cut" data-text="Glitch" aria-hidden="true">Glitch</span>2. Two copies
::before and ::after draw the text again on top of itself, each cut down to an empty band with clip-path.
.glitch { position: relative; display: inline-block;}
.glitch::before,.glitch::after { content: attr(data-text); position: absolute; inset: 0; /* A band of zero height: nothing shows between bursts. */ clip-path: inset(50% 0 50% 0);}3. The bursts
Each copy opens thin horizontal bands that jump sideways with translate near the end of a 3s loop, while the text itself jolts with a skewX(). steps(1) makes every frame a hard cut, like a bad signal, instead of a smooth slide.
.glitch { animation: glitch-jolt 3s steps(1, end) infinite;}.glitch::before { animation: glitch-top 3s steps(1, end) infinite;}.glitch::after { animation: glitch-bottom 3s steps(1, end) infinite;}
@keyframes glitch-top { 0%, 70%, 100% { clip-path: inset(50% 0 50% 0); translate: 0; } 72% { clip-path: inset(8% 0 72% 0); translate: -0.06em 0; } 76% { clip-path: inset(40% 0 43% 0); translate: 0.05em 0; } 80% { clip-path: inset(15% 0 60% 0); translate: -0.03em 0; } 84% { clip-path: inset(62% 0 20% 0); translate: 0.07em 0; } 88% { clip-path: inset(2% 0 85% 0); translate: -0.04em 0; } 92% { clip-path: inset(50% 0 50% 0); translate: 0; }}
@keyframes glitch-bottom { 0%, 72%, 100% { clip-path: inset(50% 0 50% 0); translate: 0; } 74% { clip-path: inset(70% 0 8% 0); translate: 0.06em 0; } 78% { clip-path: inset(30% 0 55% 0); translate: -0.05em 0; } 82% { clip-path: inset(85% 0 2% 0); translate: 0.04em 0; } 86% { clip-path: inset(48% 0 30% 0); translate: -0.07em 0; } 90% { clip-path: inset(50% 0 50% 0); translate: 0; }}
@keyframes glitch-jolt { 0%, 70%, 100% { transform: none; } 73% { transform: translateX(0.02em) skewX(-6deg); } 79% { transform: translateX(-0.02em); } 85% { transform: skewX(4deg); } 90% { transform: none; }}4. Cut or overlay
cut class gives each band the page background, so it hides the text below: a clean tear, but only on a flat backdrop. overlay class draws coloured bands with no background of their own, so it works anywhere.
:root { --glitch-background: #030707; /* the page behind the text */ --glitch-red: #ff6467; --glitch-green: #5fe3a1;}
/* Cut: each band hides the text below and adds a coloured fringe. */.glitch.cut::before { background: var(--glitch-background); text-shadow: -0.04em 0 var(--glitch-red);}.glitch.cut::after { background: var(--glitch-background); text-shadow: 0.04em 0 var(--glitch-green);}
/* Overlay: coloured bands with no background, for any backdrop. */.glitch.overlay::before { color: var(--glitch-red);}.glitch.overlay::after { color: var(--glitch-green);}