16 CSS Glitch Text Effects14 / 16

Pure CSSMIT licensed

Split Color-Channel Glitch Text

The main headline text is color:transparent -- only the red and blue pseudo-element channels are visible, slowly drifting apart at different rates via smooth ease-in-out keyframes, with a violent sync snap that briefly clips the channels to non-overlapping bands.

Published

Live Demo
Try it

The code

<div class="gt-14">
  <span class="gt-14__eyebrow">Chromatic Aberration</span>
  <div class="gt-14__chroma" data-text="ABERRANT">ABERRANT</div>
  <div class="gt-14__legend">
    <div class="gt-14__channel"><div class="gt-14__swatch gt-14__swatch--r"></div>Red Ch.</div>
    <div class="gt-14__channel"><div class="gt-14__swatch gt-14__swatch--b"></div>Blue Ch.</div>
  </div>
</div>
.gt-14,
.gt-14 *,
.gt-14 *::before,
.gt-14 *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.gt-14 ::selection {
  background: #e2e8f0;
  color: #000;
}

.gt-14 {
  --red-ch: #ff2020;
  --blue-ch: #2060ff;
  --bg: #08080f;
  --text: #f0f0ff;
  min-height: 100vh;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 48px 20px;
  font-family: 'Inter', system-ui, sans-serif;
  position: relative;
  overflow: hidden;
}
/* Subtle lens-aberration vignette */

.gt-14::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 80% 60% at 50% 50%, transparent 50%, rgba(0,0,0,0.5) 100%);
  pointer-events: none;
}

.gt-14__eyebrow {
  font-size: 10px;
  letter-spacing: 5px;
  color: rgba(240,240,255,0.3);
  text-transform: uppercase;
}
/* Container: color:transparent so only channel ghosts show */

.gt-14__chroma {
  position: relative;
  font-size: clamp(44px, 9vw, 90px);
  font-weight: 900;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: transparent;
  /* The main layer is invisible */
  user-select: none;
}
/* Red channel — drifts left */

.gt-14__chroma::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  font: inherit;
  letter-spacing: inherit;
  color: var(--red-ch);
  mix-blend-mode: screen;
  animation: gt-14-red-drift 3.5s ease-in-out infinite alternate;
  opacity: 0.85;
}
/* Blue channel — drifts right */

.gt-14__chroma::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  font: inherit;
  letter-spacing: inherit;
  color: var(--blue-ch);
  mix-blend-mode: screen;
  animation: gt-14-blue-drift 3.5s ease-in-out infinite alternate;
  opacity: 0.85;
}
/* Where red + blue overlap → white/violet shows naturally */

@keyframes gt-14-red-drift {
  0% {
    transform: translate(-6px, 0);
  }

  25% {
    transform: translate(-4px, 1px);
  }

  50% {
    transform: translate(-8px, -1px);
  }

  75% {
    transform: translate(-3px, 0);
  }

  100% {
    transform: translate(-10px, 1px);
  }
}

@keyframes gt-14-blue-drift {
  0% {
    transform: translate(6px, 0);
  }

  25% {
    transform: translate(9px, -1px);
  }

  50% {
    transform: translate(4px, 1px);
  }

  75% {
    transform: translate(11px, 0);
  }

  100% {
    transform: translate(5px, -1px);
  }
}
/* Violent snap glitch intermittently */

.gt-14__chroma::before {
  animation: gt-14-red-drift 3.5s ease-in-out infinite alternate, gt-14-red-snap 5s steps(1) infinite;
}

.gt-14__chroma::after {
  animation: gt-14-blue-drift 3.5s ease-in-out infinite alternate, gt-14-blue-snap 5s steps(1) infinite;
}

@keyframes gt-14-red-snap {
  0%,
    88%,
    100% {
    clip-path: none;
  }

  89% {
    transform: translate(-20px, 0);
    clip-path: polygon(0 20%, 100% 20%, 100% 45%, 0 45%);
  }

  90% {
    transform: translate(-14px, 2px);
    clip-path: polygon(0 55%, 100% 55%, 100% 80%, 0 80%);
  }

  91% {
    transform: translate(-6px, 0);
    clip-path: none;
  }
}

@keyframes gt-14-blue-snap {
  0%,
    88%,
    100% {
    clip-path: none;
  }

  89% {
    transform: translate(22px, 0);
    clip-path: polygon(0 20%, 100% 20%, 100% 45%, 0 45%);
  }

  90% {
    transform: translate(15px, -2px);
    clip-path: polygon(0 55%, 100% 55%, 100% 80%, 0 80%);
  }

  91% {
    transform: translate(6px, 0);
    clip-path: none;
  }
}

.gt-14__legend {
  display: flex;
  gap: 24px;
  align-items: center;
}

.gt-14__channel {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  letter-spacing: 2px;
  color: rgba(240,240,255,0.4);
  text-transform: uppercase;
}

.gt-14__swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

.gt-14__swatch--r {
  background: var(--red-ch);
  box-shadow: 0 0 8px var(--red-ch);
}

.gt-14__swatch--b {
  background: var(--blue-ch);
  box-shadow: 0 0 8px var(--blue-ch);
}

@media (prefers-reduced-motion: reduce) {
  .gt-14__chroma::before {
    animation: none;
    transform: translate(-6px,0);
  }

  .gt-14__chroma::after {
    animation: none;
    transform: translate(6px,0);
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Glitch Text Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Split Color-Channel Glitch Text
Source: https://codefronts.com/motion/css-glitch-text-effect/split-color-channel-glitch-text/

The main headline text is color:transparent -- only the red and blue pseudo-element channels are visible, slowly drifting apart at different rates via smooth ease-in-out keyframes, with a violent sync snap that briefly clips the channels to non-overlapping bands.
## HTML
```html
<div class="gt-14">
  <span class="gt-14__eyebrow">Chromatic Aberration</span>
  <div class="gt-14__chroma" data-text="ABERRANT">ABERRANT</div>
  <div class="gt-14__legend">
    <div class="gt-14__channel"><div class="gt-14__swatch gt-14__swatch--r"></div>Red Ch.</div>
    <div class="gt-14__channel"><div class="gt-14__swatch gt-14__swatch--b"></div>Blue Ch.</div>
  </div>
</div>
```
## CSS
```css
.gt-14,
.gt-14 *,
.gt-14 *::before,
.gt-14 *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.gt-14 ::selection {
  background: #e2e8f0;
  color: #000;
}

.gt-14 {
  --red-ch: #ff2020;
  --blue-ch: #2060ff;
  --bg: #08080f;
  --text: #f0f0ff;
  min-height: 100vh;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 48px 20px;
  font-family: 'Inter', system-ui, sans-serif;
  position: relative;
  overflow: hidden;
}
/* Subtle lens-aberration vignette */

.gt-14::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 80% 60% at 50% 50%, transparent 50%, rgba(0,0,0,0.5) 100%);
  pointer-events: none;
}

.gt-14__eyebrow {
  font-size: 10px;
  letter-spacing: 5px;
  color: rgba(240,240,255,0.3);
  text-transform: uppercase;
}
/* Container: color:transparent so only channel ghosts show */

.gt-14__chroma {
  position: relative;
  font-size: clamp(44px, 9vw, 90px);
  font-weight: 900;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: transparent;
  /* The main layer is invisible */
  user-select: none;
}
/* Red channel — drifts left */

.gt-14__chroma::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  font: inherit;
  letter-spacing: inherit;
  color: var(--red-ch);
  mix-blend-mode: screen;
  animation: gt-14-red-drift 3.5s ease-in-out infinite alternate;
  opacity: 0.85;
}
/* Blue channel — drifts right */

.gt-14__chroma::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  font: inherit;
  letter-spacing: inherit;
  color: var(--blue-ch);
  mix-blend-mode: screen;
  animation: gt-14-blue-drift 3.5s ease-in-out infinite alternate;
  opacity: 0.85;
}
/* Where red + blue overlap → white/violet shows naturally */

@keyframes gt-14-red-drift {
  0% {
    transform: translate(-6px, 0);
  }

  25% {
    transform: translate(-4px, 1px);
  }

  50% {
    transform: translate(-8px, -1px);
  }

  75% {
    transform: translate(-3px, 0);
  }

  100% {
    transform: translate(-10px, 1px);
  }
}

@keyframes gt-14-blue-drift {
  0% {
    transform: translate(6px, 0);
  }

  25% {
    transform: translate(9px, -1px);
  }

  50% {
    transform: translate(4px, 1px);
  }

  75% {
    transform: translate(11px, 0);
  }

  100% {
    transform: translate(5px, -1px);
  }
}
/* Violent snap glitch intermittently */

.gt-14__chroma::before {
  animation: gt-14-red-drift 3.5s ease-in-out infinite alternate, gt-14-red-snap 5s steps(1) infinite;
}

.gt-14__chroma::after {
  animation: gt-14-blue-drift 3.5s ease-in-out infinite alternate, gt-14-blue-snap 5s steps(1) infinite;
}

@keyframes gt-14-red-snap {
  0%,
    88%,
    100% {
    clip-path: none;
  }

  89% {
    transform: translate(-20px, 0);
    clip-path: polygon(0 20%, 100% 20%, 100% 45%, 0 45%);
  }

  90% {
    transform: translate(-14px, 2px);
    clip-path: polygon(0 55%, 100% 55%, 100% 80%, 0 80%);
  }

  91% {
    transform: translate(-6px, 0);
    clip-path: none;
  }
}

@keyframes gt-14-blue-snap {
  0%,
    88%,
    100% {
    clip-path: none;
  }

  89% {
    transform: translate(22px, 0);
    clip-path: polygon(0 20%, 100% 20%, 100% 45%, 0 45%);
  }

  90% {
    transform: translate(15px, -2px);
    clip-path: polygon(0 55%, 100% 55%, 100% 80%, 0 80%);
  }

  91% {
    transform: translate(6px, 0);
    clip-path: none;
  }
}

.gt-14__legend {
  display: flex;
  gap: 24px;
  align-items: center;
}

.gt-14__channel {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  letter-spacing: 2px;
  color: rgba(240,240,255,0.4);
  text-transform: uppercase;
}

.gt-14__swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

.gt-14__swatch--r {
  background: var(--red-ch);
  box-shadow: 0 0 8px var(--red-ch);
}

.gt-14__swatch--b {
  background: var(--blue-ch);
  box-shadow: 0 0 8px var(--blue-ch);
}

@media (prefers-reduced-motion: reduce) {
  .gt-14__chroma::before {
    animation: none;
    transform: translate(-6px,0);
  }

  .gt-14__chroma::after {
    animation: none;
    transform: translate(6px,0);
  }
}
```

How this works

Setting color: transparent on the headline makes the base text invisible, leaving only two pseudo-elements -- red (::before) and blue (::after) -- contributing colour. Both use mix-blend-mode: screen so their colours add together: where red and blue overlap the eye perceives violet-white; where they diverge, pure red and blue edges become visible. The slow ease-in-out alternate drift keyframes move the channels in opposite horizontal directions at slightly different amplitudes so their overlap region is always shifting.

A second set of keyframes runs simultaneously on a 5-second steps(1) timer. At 89% they fire a hard displacement to +/-20px and apply a clip-path that isolates only the top band for one frame, then switch to the bottom band for the next, before snapping back to clip-path: none at 91%. The combined result is a chromatic aberration separation that looks like a lens physically shifting out of focus.

Make it yours

  • Add a green channel via a wrapper span coloured #00ff00 with a slight vertical offset instead of horizontal to create a three-channel RGB printing-plate misregistration.
  • Reduce the drift amplitude from +/-10px to +/-3px for a more subtle anaglyph 3D glasses effect suitable for body text.
  • Change the drift animation to use translateY instead of translateX for a vertical chroma-shift resembling a vertically mis-calibrated display.
  • Wrap the snap keyframe trigger in a :hover variant so the violent split only occurs on mouse interaction, keeping the ambient drift as the resting state.
  • Apply to a logo SVG by setting fill: transparent on the base and using absolutely-positioned wrapper pseudo-elements with background-clip on SVG-backed content.

Gotchas — read before shipping

  • mix-blend-mode: screen requires a dark background to be visually effective -- on white or light backgrounds the screen blend washes out. Use mix-blend-mode: multiply on light backgrounds instead.
  • The color: transparent on the main element means the text has no inherent colour contrast for accessibility; ensure the background is dark enough that even a single channel exceeds 3:1 contrast ratio.
  • Safari may render the mix-blend-mode compositing slightly differently due to its colour-managed graphics pipeline; test the overlap region where channels should produce white -- in Safari this may appear slightly violet.

Browser support

ChromeSafariFirefoxEdge
80+14.1+78+80+

mix-blend-mode: screen is supported in all evergreen browsers; test on Safari < 14.1 where blend-mode had known colour-profile issues.

Techniques used in this demo

Search CodeFronts

Loading…