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.

Pure CSS MIT licensed
Live Demo Open in tab
Open in playground

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>
.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 translateX values in ta-03-glitch-r and ta-03-glitch-b — try ±8px for a wild broken-screen effect.
  • Change the channel colours by editing the color on ::before (red channel) and ::after (cyan channel) — purple/yellow creates a retro film-error vibe.
  • Reduce glitch frequency by raising the animation-duration from 3s to 8s for a subtle occasional flicker rather than constant corruption.
  • Apply to a logo or monospace display font like Orbitron or Share Tech Mono for maximum sci-fi impact.
  • Wrap the element in a :hover trigger by moving the animation to :hover::before and :hover::after so glitch only plays on interaction.

Watch out for

  • clip-path on pseudo-elements requires the parent to have position: relative and the pseudo-elements to have position: absolute with 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-path can cause repaints on non-composited layers in older browsers; keep the animated element on its own layer with will-change: transform.

Browser support

ChromeSafariFirefoxEdge
55+ 13.1+ 71+ 55+

clip-path on inline elements needs block-level display; Firefox below 71 may have clip-path animation performance issues.

Search CodeFronts

Loading…