20 CSS Neon Text Effects15 / 20

Pure CSSMIT licensed

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.

Published

Live Demo
Try it

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>
.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. */
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 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 ::after opacity range (e.g. .35 → 1), never the blur.
  • Set intensity by editing the static shadow on the ::after once — 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 animate text-shadow or filter: blur() you're back on the main thread.
  • Duplicate the text in the ::after via content or aria-hidden so the glow layer isn't read twice by screen readers.
  • will-change is a promise, not free — use it on the one animated layer, not everything.

Browser support

ChromeSafariFirefoxEdge
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.

Techniques used in this demo

Search CodeFronts

Loading…