16 CSS Glitch Text Effects14 / 16
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
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><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);
}
}.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);
}
}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);
}
}
```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
#00ff00with 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
translateYinstead oftranslateXfor a vertical chroma-shift resembling a vertically mis-calibrated display. - Wrap the snap keyframe trigger in a
:hovervariant 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: transparenton the base and using absolutely-positioned wrapper pseudo-elements with background-clip on SVG-backed content.
Gotchas — read before shipping
mix-blend-mode: screenrequires a dark background to be visually effective -- on white or light backgrounds the screen blend washes out. Usemix-blend-mode: multiplyon light backgrounds instead.- The
color: transparenton 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-modecompositing 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
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 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.