25 CSS Text Animations 03 / 25
CSS Glitch Text Effect Animation
A cyberpunk-style glitch animation using CSS pseudo-elements with clip-path and transform skewing to simulate corrupted display artifacts.
The code
<div class="ta-03">
<div class="ta-03__stage">
<h2 class="ta-03__glitch" data-text="GLITCH">GLITCH</h2>
<p class="ta-03__sub">clip-path · pseudo-elements · RGB offset</p>
</div>
</div> <div class="ta-03">
<div class="ta-03__stage">
<h2 class="ta-03__glitch" data-text="GLITCH">GLITCH</h2>
<p class="ta-03__sub">clip-path · pseudo-elements · RGB offset</p>
</div>
</div>.ta-03, .ta-03 *, .ta-03 *::before, .ta-03 *::after {
margin: 0; padding: 0; box-sizing: border-box;
}
.ta-03 ::selection { background: #f43f5e; color: #fff; }
.ta-03 {
--bg: #050510;
--white: #e2e8f0;
min-height: 100vh;
background: var(--bg);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
font-family: 'Orbitron', 'Courier New', monospace;
}
.ta-03__stage { text-align: center; }
.ta-03__glitch {
position: relative;
font-size: clamp(3rem, 10vw, 6rem);
font-weight: 900;
color: var(--white);
letter-spacing: 0.08em;
text-transform: uppercase;
animation: ta-03-main 6s infinite;
}
.ta-03__glitch::before,
.ta-03__glitch::after {
content: attr(data-text);
position: absolute;
top: 0; left: 0;
width: 100%;
height: 100%;
}
.ta-03__glitch::before {
color: #ff003c;
animation: ta-03-glitch-r 3s infinite linear alternate-reverse;
}
.ta-03__glitch::after {
color: #00ffe7;
animation: ta-03-glitch-b 2.5s infinite linear alternate-reverse;
}
.ta-03__sub {
font-size: 0.68rem;
color: #334155;
margin-top: 1.5rem;
letter-spacing: 0.12em;
text-transform: uppercase;
font-family: 'Courier New', monospace;
}
@keyframes ta-03-main {
0%, 90%, 100% { transform: none; opacity: 1; }
92% { transform: skewX(-2deg); opacity: 0.9; }
94% { transform: skewX(1deg); }
96% { transform: none; }
98% { transform: skewX(-1deg) scaleY(1.02); }
}
@keyframes ta-03-glitch-r {
0% { clip-path: polygon(0 2%, 100% 2%, 100% 5%, 0 5%); transform: translate(-2px, 0); }
10% { clip-path: polygon(0 15%, 100% 15%, 100% 18%, 0 18%); transform: translate(2px, 0); }
25% { clip-path: polygon(0 40%, 100% 40%, 100% 44%, 0 44%); transform: translate(-1px, 0); }
40% { clip-path: polygon(0 60%, 100% 60%, 100% 65%, 0 65%); transform: translate(3px, 0); }
55% { clip-path: polygon(0 75%, 100% 75%, 100% 80%, 0 80%); transform: translate(-2px, 0); }
70% { clip-path: polygon(0 88%, 100% 88%, 100% 93%, 0 93%); transform: translate(1px, 0); }
100% { clip-path: polygon(0 30%, 100% 30%, 100% 35%, 0 35%); transform: translate(0, 0); }
}
@keyframes ta-03-glitch-b {
0% { clip-path: polygon(0 55%, 100% 55%, 100% 58%, 0 58%); transform: translate(2px, 0); }
15% { clip-path: polygon(0 10%, 100% 10%, 100% 14%, 0 14%); transform: translate(-3px, 0); }
30% { clip-path: polygon(0 70%, 100% 70%, 100% 74%, 0 74%); transform: translate(1px, 0); }
50% { clip-path: polygon(0 25%, 100% 25%, 100% 30%, 0 30%); transform: translate(-2px, 0); }
70% { clip-path: polygon(0 85%, 100% 85%, 100% 90%, 0 90%); transform: translate(3px, 0); }
100% { clip-path: polygon(0 45%, 100% 45%, 100% 50%, 0 50%); transform: translate(-1px, 0); }
}
@media (prefers-reduced-motion: reduce) {
.ta-03__glitch, .ta-03__glitch::before, .ta-03__glitch::after {
animation: none;
transform: none;
clip-path: none;
}
.ta-03__glitch::before, .ta-03__glitch::after { display: none; }
} .ta-03, .ta-03 *, .ta-03 *::before, .ta-03 *::after {
margin: 0; padding: 0; box-sizing: border-box;
}
.ta-03 ::selection { background: #f43f5e; color: #fff; }
.ta-03 {
--bg: #050510;
--white: #e2e8f0;
min-height: 100vh;
background: var(--bg);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
font-family: 'Orbitron', 'Courier New', monospace;
}
.ta-03__stage { text-align: center; }
.ta-03__glitch {
position: relative;
font-size: clamp(3rem, 10vw, 6rem);
font-weight: 900;
color: var(--white);
letter-spacing: 0.08em;
text-transform: uppercase;
animation: ta-03-main 6s infinite;
}
.ta-03__glitch::before,
.ta-03__glitch::after {
content: attr(data-text);
position: absolute;
top: 0; left: 0;
width: 100%;
height: 100%;
}
.ta-03__glitch::before {
color: #ff003c;
animation: ta-03-glitch-r 3s infinite linear alternate-reverse;
}
.ta-03__glitch::after {
color: #00ffe7;
animation: ta-03-glitch-b 2.5s infinite linear alternate-reverse;
}
.ta-03__sub {
font-size: 0.68rem;
color: #334155;
margin-top: 1.5rem;
letter-spacing: 0.12em;
text-transform: uppercase;
font-family: 'Courier New', monospace;
}
@keyframes ta-03-main {
0%, 90%, 100% { transform: none; opacity: 1; }
92% { transform: skewX(-2deg); opacity: 0.9; }
94% { transform: skewX(1deg); }
96% { transform: none; }
98% { transform: skewX(-1deg) scaleY(1.02); }
}
@keyframes ta-03-glitch-r {
0% { clip-path: polygon(0 2%, 100% 2%, 100% 5%, 0 5%); transform: translate(-2px, 0); }
10% { clip-path: polygon(0 15%, 100% 15%, 100% 18%, 0 18%); transform: translate(2px, 0); }
25% { clip-path: polygon(0 40%, 100% 40%, 100% 44%, 0 44%); transform: translate(-1px, 0); }
40% { clip-path: polygon(0 60%, 100% 60%, 100% 65%, 0 65%); transform: translate(3px, 0); }
55% { clip-path: polygon(0 75%, 100% 75%, 100% 80%, 0 80%); transform: translate(-2px, 0); }
70% { clip-path: polygon(0 88%, 100% 88%, 100% 93%, 0 93%); transform: translate(1px, 0); }
100% { clip-path: polygon(0 30%, 100% 30%, 100% 35%, 0 35%); transform: translate(0, 0); }
}
@keyframes ta-03-glitch-b {
0% { clip-path: polygon(0 55%, 100% 55%, 100% 58%, 0 58%); transform: translate(2px, 0); }
15% { clip-path: polygon(0 10%, 100% 10%, 100% 14%, 0 14%); transform: translate(-3px, 0); }
30% { clip-path: polygon(0 70%, 100% 70%, 100% 74%, 0 74%); transform: translate(1px, 0); }
50% { clip-path: polygon(0 25%, 100% 25%, 100% 30%, 0 30%); transform: translate(-2px, 0); }
70% { clip-path: polygon(0 85%, 100% 85%, 100% 90%, 0 90%); transform: translate(3px, 0); }
100% { clip-path: polygon(0 45%, 100% 45%, 100% 50%, 0 50%); transform: translate(-1px, 0); }
}
@media (prefers-reduced-motion: reduce) {
.ta-03__glitch, .ta-03__glitch::before, .ta-03__glitch::after {
animation: none;
transform: none;
clip-path: none;
}
.ta-03__glitch::before, .ta-03__glitch::after { display: none; }
}How this works
The glitch effect layers two ::before and ::after pseudo-elements on top of the base text, each carrying a data-text attribute copy via content: attr(data-text). The pseudo-elements are coloured differently — red and cyan channel offsets — and run independent clip-path animations that slice them into random horizontal bands, revealing only fragments at a time.
Each pseudo-element also applies a transform: translate() offset that jiggles horizontally at different keyframe percentages, mimicking the RGB channel separation of a damaged CRT or corrupted video signal. The animations use different durations and delays so the two channels glitch out of sync, producing authentic-looking corruption rather than a synchronized pattern.
Customize
- Intensify the glitch by increasing the
translateXvalues inta-03-glitch-randta-03-glitch-b— try±8pxfor a wild broken-screen effect. - Change the channel colours by editing the
coloron::before(red channel) and::after(cyan channel) — purple/yellow creates a retro film-error vibe. - Reduce glitch frequency by raising the
animation-durationfrom3sto8sfor a subtle occasional flicker rather than constant corruption. - Apply to a logo or monospace display font like
OrbitronorShare Tech Monofor maximum sci-fi impact. - Wrap the element in a
:hovertrigger by moving the animation to:hover::beforeand:hover::afterso glitch only plays on interaction.
Watch out for
clip-pathon pseudo-elements requires the parent to haveposition: relativeand the pseudo-elements to haveposition: absolutewith matching dimensions.- The
content: attr(data-text)trick only duplicates text visible in the HTML attribute — if the text contains HTML entities or special characters, they may not render correctly. - Heavy use of animated
clip-pathcan cause repaints on non-composited layers in older browsers; keep the animated element on its own layer withwill-change: transform.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 55+ | 13.1+ | 71+ | 55+ |
clip-path on inline elements needs block-level display; Firefox below 71 may have clip-path animation performance issues.