Chromatic Aberration Text
Two CSS ::before and ::after pseudo-elements duplicate the text via content: attr(data-text) and offset in opposite directions on hover, simulating the chromatic dispersion of a prism.
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. Backgrounds has 50 effects, including 6 free. Explore the category →
3 usage examples



How it works
The effect relies on two CSS pseudo-elements (::before and ::after) that replicate the text using content: attr(data-text). The data-text attribute on .aberration-text must match the visible text exactly — this is the only markup requirement.
The pseudo-elements are positioned absolute with z-index: -1 (behind the white main text) and start at opacity: 0. On hover of the .aberration-container, CSS transitions move ::before (red) to translate(-4px, -2px) and ::after (cyan) to translate(4px, 2px) with an opacity of 0.7 — the two channels diverge in opposite directions, recreating the wavelength separation of an optical prism.
Simultaneously, an aberrationFlicker animation is applied to the main text via text-shadow: it alternates between slightly offset red and cyan shadows (plus a magenta touch #f0f on the final keyframe) at 0.15 s in alternate infinite mode. This rapid shimmer on the text edges creates the impression of an unstable video signal.
The JS file is empty — the effect is 100% CSS, zero dependencies. Entry/exit transitions (transform 0.3s, opacity 0.3s) ensure smooth appearance and disappearance. No requestAnimationFrame, no canvas, no library.
Accessibility
- No prefers-reduced-motion in the code: the
aberrationFlickeranimation and CSS transitions run even when the OS requests reduced motion. Recommended addition:@media (prefers-reduced-motion: reduce) { .aberration-text { animation: none; } .aberration-text::before, .aberration-text::after { transition: none; } } - The
::beforeand::afterpseudo-elements exposecontent: attr(data-text)— some screen readers (NVDA, VoiceOver) announce the text up to three times. Workaround: addaria-hidden="true"to.aberration-textand an explicitaria-labelon the parent container. - The effect is hover only — no keyboard interaction is exposed. If the text is a link or button, add
:focus-visiblewith a visible focus style. - Main text contrast (white on dark background): ratio ≥ 7:1 in the default state. The red and cyan channels are purely decorative layers behind the white text and carry no independent content.
- No
aria-*attributes in the effect source code. Depending on context (heading, link, menu), add the appropriate ARIA role and label to the parent container.
Browser compatibility
CSS pseudo-elements, transitions, animations and content: attr() — universal support since Chrome 26 / Firefox 4 / Safari 6. Zero JS, zero dependencies.
Without hover support (mobile, keyboard) or without CSS animations, the white text displays normally without RGB channels — no JS errors, content remains fully readable.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="aberration-container">
<!-- data-text must match the visible text exactly -->
<span class="aberration-text" 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 (HTML attribute) | PRISM | Text duplicated by pseudo-elements. Must match the span's visible text exactly — the only required markup configuration. |
| translate(-4px, -2px) on ::before hover | -4px, -2px | Red channel offset on hover. Increase (±6px, ±8px) for a stronger effect; decrease (±2px) for subtlety. |
| translate(4px, 2px) on ::after hover | 4px, 2px | Cyan channel offset in the opposite direction. Keep symmetric with ::before for balanced output. |
| opacity: 0.7 on hover pseudo-elements | 0.7 | Intensity of the visible RGB channels. Between 0.4 (subtle) and 1 (fully saturated). Applies to both ::before and ::after. |
| color: red / color: #0ff | red / cyan | Colors of the ::before and ::after channels. Replace with any contrasting pair — purple+green, orange+blue, etc. |
| transition: transform .3s, opacity .3s | 0.3s | Entry and exit duration of channels on hover. Reduce to 0.15s for snappiness, increase to 0.5s for a slow fade. |
| animation: .15s aberrationFlicker infinite | 0.15s | Shimmer speed during hover. 0.08s = aggressive glitch, 0.3s = soft vibration. |
| font-size: 2rem on .aberration-text | 2rem | Text size. The effect reads well from 1.5 rem up — below that, a 4px offset is barely noticeable. |
FAQ
::before and ::after pseudo-elements cannot access their parent's text content directly. The property content: attr(data-text) reads the HTML attribute and copies it into the pseudo-element — the standard technique for duplicating text in pure CSS, without JS. The text must be identical in data-text and in the visible span content.<span class="aberration-text" data-text="Your text"> inside its own <div class="aberration-container"> works independently, without selector conflicts. No JS initialization, no unique ID required. Place as many instances as needed.