15 / 20
All 20 in 20 CSS Neon Text Effects
- 01Neon Glow Button Hover Effect
- 02Neon Flashing "Order Now" Sign Text
- 03Cyberpunk CTA Button Glow
- 04Pulse-Glow Text Notification
- 0580s Retro Synthwave Neon Text
- 06Realistic Flickering Neon Sign
- 07Google-Fonts Neon Pairing
- 08Minimalist Dark-Mode Header Glow
- 09Neon Text Outline Stroke Animation
- 10Neon Digital Clock LED Glow
- 11Synchronized Box-Shadow + Text Glow
- 12Multi-Colour Neon Text Effect
- 13Pure-CSS Neon Text (No JavaScript)
- 14CSS Variables Dynamic Neon Colour
- 15Low-INP Compositor Neon Animation
- 16Tailwind Neon Text-Glow Utility
- 17Neon Glitch Cyberpunk Text
- 18Interactive Neon On/Off Sign Switch
- 19Animated Gradient-Flow Neon Text
- 20Neon Typing / Loading Terminal
Low-INP Compositor Neon Animation
A continuously-pulsing neon headline engineered to keep Interaction-to-Next-Paint low: the glow is a pre-rendered ::after layer that animates only opacity — never blur — so the pulse lives on the compositor, off the main thread.
Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects
ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link
The code
<div class="cnt-15"> <h2 class="cnt-15__h" data-text="LOW INP">LOW INP</h2> <p class="cnt-15__note">pulse animates opacity only — off the main thread</p> </div>
<div class="cnt-15">
<h2 class="cnt-15__h" data-text="LOW INP">LOW INP</h2>
<p class="cnt-15__note">pulse animates opacity only — off the main thread</p>
</div>.cnt-15,
.cnt-15 *,
.cnt-15 *::before,
.cnt-15 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cnt-15 ::selection {
background: oklch(0.86 0.18 175);
color: #03130f;
}
.cnt-15 {
--accent: oklch(0.86 0.18 175);
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
justify-content: center;
padding: 60px 20px;
background: radial-gradient(120% 120% at 50% 30%,#08110f,#040807);
}
.cnt-15__h {
position: relative;
font-size: clamp(46px,12vw,112px);
font-weight: 900;
letter-spacing: .05em;
color: #eafffb;
text-shadow: 0 0 2px #fff;
}
.cnt-15__h::after {
content: attr(data-text);
position: absolute;
inset: 0;
pointer-events: none;
color: var(--accent);
will-change: opacity;
text-shadow: 0 0 6px #fff,0 0 16px var(--accent),0 0 40px var(--accent),0 0 84px color-mix(in oklch,var(--accent) 55%,transparent);
animation: cnt-15-pulse 2s ease-in-out infinite;
}
@keyframes cnt-15-pulse {
0%,
100% {
opacity: .4;
}
50% {
opacity: 1;
}
}
.cnt-15__note {
font: 12px ui-monospace,Menlo,monospace;
letter-spacing: .18em;
color: #5f7a74;
}
@media (prefers-reduced-motion: reduce) {
.cnt-15__h::after {
animation: none;
opacity: 1;
}
}.cnt-15,
.cnt-15 *,
.cnt-15 *::before,
.cnt-15 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cnt-15 ::selection {
background: oklch(0.86 0.18 175);
color: #03130f;
}
.cnt-15 {
--accent: oklch(0.86 0.18 175);
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
justify-content: center;
padding: 60px 20px;
background: radial-gradient(120% 120% at 50% 30%,#08110f,#040807);
}
.cnt-15__h {
position: relative;
font-size: clamp(46px,12vw,112px);
font-weight: 900;
letter-spacing: .05em;
color: #eafffb;
text-shadow: 0 0 2px #fff;
}
.cnt-15__h::after {
content: attr(data-text);
position: absolute;
inset: 0;
pointer-events: none;
color: var(--accent);
will-change: opacity;
text-shadow: 0 0 6px #fff,0 0 16px var(--accent),0 0 40px var(--accent),0 0 84px color-mix(in oklch,var(--accent) 55%,transparent);
animation: cnt-15-pulse 2s ease-in-out infinite;
}
@keyframes cnt-15-pulse {
0%,
100% {
opacity: .4;
}
50% {
opacity: 1;
}
}
.cnt-15__note {
font: 12px ui-monospace,Menlo,monospace;
letter-spacing: .18em;
color: #5f7a74;
}
@media (prefers-reduced-motion: reduce) {
.cnt-15__h::after {
animation: none;
opacity: 1;
}
}/* No JavaScript — the glow is a baked ::after layer; only its opacity animates. */
/* No JavaScript — the glow is a baked ::after layer; only its opacity animates. */Here's a working CSS Neon 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: Low-INP Compositor Neon Animation
Source: https://codefronts.com/motion/css-neon-text-effect/low-inp-compositor-neon-animation/
A continuously-pulsing neon headline engineered to keep Interaction-to-Next-Paint low: the glow is a pre-rendered ::after layer that animates only opacity — never blur — so the pulse lives on the compositor, off the main thread.
## HTML
```html
<div class="cnt-15">
<h2 class="cnt-15__h" data-text="LOW INP">LOW INP</h2>
<p class="cnt-15__note">pulse animates opacity only — off the main thread</p>
</div>
```
## CSS
```css
.cnt-15,
.cnt-15 *,
.cnt-15 *::before,
.cnt-15 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cnt-15 ::selection {
background: oklch(0.86 0.18 175);
color: #03130f;
}
.cnt-15 {
--accent: oklch(0.86 0.18 175);
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
justify-content: center;
padding: 60px 20px;
background: radial-gradient(120% 120% at 50% 30%,#08110f,#040807);
}
.cnt-15__h {
position: relative;
font-size: clamp(46px,12vw,112px);
font-weight: 900;
letter-spacing: .05em;
color: #eafffb;
text-shadow: 0 0 2px #fff;
}
.cnt-15__h::after {
content: attr(data-text);
position: absolute;
inset: 0;
pointer-events: none;
color: var(--accent);
will-change: opacity;
text-shadow: 0 0 6px #fff,0 0 16px var(--accent),0 0 40px var(--accent),0 0 84px color-mix(in oklch,var(--accent) 55%,transparent);
animation: cnt-15-pulse 2s ease-in-out infinite;
}
@keyframes cnt-15-pulse {
0%,
100% {
opacity: .4;
}
50% {
opacity: 1;
}
}
.cnt-15__note {
font: 12px ui-monospace,Menlo,monospace;
letter-spacing: .18em;
color: #5f7a74;
}
@media (prefers-reduced-motion: reduce) {
.cnt-15__h::after {
animation: none;
opacity: 1;
}
}
```
## JavaScript
```js
/* No JavaScript — the glow is a baked ::after layer; only its opacity animates. */
```Here's a working CSS Neon 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: Low-INP Compositor Neon Animation
Source: https://codefronts.com/motion/css-neon-text-effect/low-inp-compositor-neon-animation/
A continuously-pulsing neon headline engineered to keep Interaction-to-Next-Paint low: the glow is a pre-rendered ::after layer that animates only opacity — never blur — so the pulse lives on the compositor, off the main thread.
## HTML
```html
<div class="cnt-15">
<h2 class="cnt-15__h" data-text="LOW INP">LOW INP</h2>
<p class="cnt-15__note">pulse animates opacity only — off the main thread</p>
</div>
```
## CSS
```css
.cnt-15,
.cnt-15 *,
.cnt-15 *::before,
.cnt-15 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cnt-15 ::selection {
background: oklch(0.86 0.18 175);
color: #03130f;
}
.cnt-15 {
--accent: oklch(0.86 0.18 175);
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
justify-content: center;
padding: 60px 20px;
background: radial-gradient(120% 120% at 50% 30%,#08110f,#040807);
}
.cnt-15__h {
position: relative;
font-size: clamp(46px,12vw,112px);
font-weight: 900;
letter-spacing: .05em;
color: #eafffb;
text-shadow: 0 0 2px #fff;
}
.cnt-15__h::after {
content: attr(data-text);
position: absolute;
inset: 0;
pointer-events: none;
color: var(--accent);
will-change: opacity;
text-shadow: 0 0 6px #fff,0 0 16px var(--accent),0 0 40px var(--accent),0 0 84px color-mix(in oklch,var(--accent) 55%,transparent);
animation: cnt-15-pulse 2s ease-in-out infinite;
}
@keyframes cnt-15-pulse {
0%,
100% {
opacity: .4;
}
50% {
opacity: 1;
}
}
.cnt-15__note {
font: 12px ui-monospace,Menlo,monospace;
letter-spacing: .18em;
color: #5f7a74;
}
@media (prefers-reduced-motion: reduce) {
.cnt-15__h::after {
animation: none;
opacity: 1;
}
}
```
## JavaScript
```js
/* No JavaScript — the glow is a baked ::after layer; only its opacity animates. */
```How this works
Naïve neon animates text-shadow blur, which forces the main thread to repaint the whole glow every frame — a measurable INP hit. Here the bright glow is baked once into an ::after pseudo-element (same text, heavy static text-shadow), and the pulse simply eases that layer's opacity up and down. Opacity is a compositor-only property, so animation runs on the GPU and never blocks input.
will-change: opacity promotes the glow layer to its own compositing layer up front. The base text underneath stays fully legible at all times, so the animated layer is pure enhancement.
Make it yours
- Tune the pulse depth by changing the
::afteropacity range (e.g. .35 → 1), never the blur. - Set intensity by editing the static shadow on the
::afteronce — it never re-rasterises. - Slow/speed the pulse via the animation duration; it stays cheap at any tempo.
- Reuse the 'bake the glow, animate opacity' pattern on buttons and badges for site-wide cheap neon.
Gotchas — read before shipping
- The point is to animate ONLY
opacity/transform; the moment you animatetext-shadoworfilter: blur()you're back on the main thread. - Duplicate the text in the
::afterviacontentoraria-hiddenso the glow layer isn't read twice by screen readers. will-changeis a promise, not free — use it on the one animated layer, not everything.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.2+ | 113+ | 111+ |
Floor set by oklch(), color-mix() as this demo is written. The core technique itself goes back further — Chrome 80+.
opacity animation + pseudo-element are universal; the technique is about performance, not new syntax.