Glitch Effect
Two CSS pseudo-elements clipped into horizontal bands with magenta/cyan chromatic aberration create a cyberpunk digital distortion — pure CSS, no canvas, no JavaScript.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Text has 56 effects, including 4 free. Explore the category →
3 usage examples



How it works
The effect relies on two ::before and ::after pseudo-elements that duplicate the text via content: attr(data-text). Positioned as absolute on the parent (position: relative), they sit exactly over the original text. Each is then clipped to a horizontal band using clip-path: polygon(…): ::before exposes only the top slice (0–35% height), ::after only the bottom slice (65–100%).
Each copy receives a distinct chromatic aberration color: magenta (#f0f) for the top, cyan (#0ff) for the bottom. Two sets of @keyframes — glitch1 and glitch2 — apply XY translations of ±3 px in five irregular steps. The horizontal offset simulates the tearing of a corrupted video signal; the vertical jumps amplify the loss-of-sync impression.
The two animations deliberately run at different speeds (2 s for glitch1, 3 s for glitch2) in alternate-reverse mode, permanently desynchronising them. This natural phase shift prevents any mechanical repetition: the distortion appears random without a single line of JavaScript.
The source text (white, .text-glitch itself) stays still — only the colored copies move, ensuring permanent readability between glitch frames. The .demo-text class sets base typography (size, weight, spacing) independently of the glitch, allowing .text-glitch to be applied to any existing text element by simply adding the data-text attribute.
Accessibility
- No prefers-reduced-motion support in this code: animations run continuously regardless of system preference. For an accessible deployment, add
@media (prefers-reduced-motion: reduce) { .text-glitch::before, .text-glitch::after { animation: none; } }. - The pseudo-elements use
content: attr(data-text): some screen readers (NVDA, JAWS) may read the text two or three times. Addaria-hidden="true"to the.text-glitchelement and provide a separate<span class="sr-only">for accessible text. - Main text contrast: white (
#fff) on#0a0a0fbackground — ratio > 15:1, well above WCAG AA (4.5:1) and AAA (7:1). - The effect is purely decorative: no interaction, no focus, no keyboard handling required.
- Compatible with forced color preferences (
forced-colors: active): pseudo-element colors are replaced by the system color — the original white text remains readable.
Browser compatibility
Requires clip-path polygon and CSS animations — available in all modern browsers since 2018.
Without <code>clip-path</code> support (IE 11, old Android < 5), pseudo-elements render without clipping — magenta and cyan text appears stacked without glitch animation. The original white text remains readable in all cases.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="demo-preview">
<span class="demo-text text-glitch" data-text="YOUR TEXT">YOUR TEXT</span>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| data-text="…" | GLITCH | Text displayed by pseudo-elements — must match the element's textContent exactly to align the three layers. |
| .text-glitch::before { color } | #f0f | Top band color (upper chromatic aberration). Replace with any neon or brand color. |
| .text-glitch::after { color } | #0ff | Bottom band color. Typically complementary to ::before for the chromatic aberration effect. |
| clip-path ::before | polygon(0 0, 100% 0, 100% 35%, 0 35%) | Top band height. Increase 35% to 45–50% for a wider band, or reduce to 20% for a thin tearing line. |
| clip-path ::after | polygon(0 65%, 100% 65%, 100% 100%, 0 100%) | Bottom band start point. The 35–65% gap is the glitch-free zone — shrink it for more aggressive distortion (e.g. 45/55%). |
| animation-duration ::before | 2s | Top glitch speed. 0.5–1s = jittery, 4–6s = slow and dreamlike. |
| animation-duration ::after | 3s | Bottom glitch speed. The duration gap between ::before and ::after is key: the larger it is, the more random the phase shift appears. |
| translate in @keyframes glitch1/2 | ±3px | XY offset amplitude in pixels. 1–2px = subtle, 5–8px = extreme video-corruption-style glitch. |
| .demo-text { font-size } | 2.5rem | Text size. The effect is most striking at 3–4rem and above on a dark full-width background. |
FAQ
@keyframes apply transform: translate() only to the ::before and ::after pseudo-elements, never to the parent element. The original white text (.text-glitch itself) stays perfectly fixed — only the colored copies move, creating the illusion of tearing without affecting the layout or document flow.text-glitch class to as many elements as needed. Each element must carry a data-text attribute matching its textContent — that is what content: attr(data-text) reads. The CSS animations apply automatically to every instance, with no JavaScript initialisation required.clip-path values (35% / 65%) apply to the element's total height. On a multi-line block, the bands cover the first and last lines but leave middle lines untouched. For best results, apply .text-glitch to a single-line <span>, or split the text into separate elements.