CSS Neon Text Effect

Glowing neon text using layered text-shadow values and a flicker CSS animation.

Published June 18, 2026 Beginner 7 min read

CSS neon text is a stack of text-shadow layers at increasing blur radii, all in the same color family, drawn behind bright type on a dark background. One shadow gives a soft edge. Five of them, running from a 4 pixel blur out to 80, give the falloff a real tube has: intense right at the glass, weaker in the air around it, and a wide dim wash on the wall behind.

The flicker comes from a @keyframes animation that drops opacity and filter: brightness at a handful of irregular moments. Spacing those moments unevenly is what makes it read as a failing tube rather than a pulse. The variants below cover a sign that lights up on hover instead of glowing constantly, and a hollow tube built with -webkit-text-stroke.

Three glowing signs

PURE CSS

NEON GLOW

no javascript

HTML

<div class="neon-stage">
  <p class="neon neon-green">PURE CSS</p>
  <p class="neon neon-blue">NEON GLOW</p>
  <p class="neon neon-pink">no javascript</p>
</div>

CSS

.neon-stage {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  align-items: center;
  padding: 1rem;
  background: #0a0a0b;
  border-radius: 12px;
}

.neon {
  margin: 0;
  font-family: system-ui, sans-serif;
  font-size: 2.5rem;
  font-weight: 800;
  letter-spacing: .02em;
  animation: neon-flicker 4s infinite;
}

.neon-green {
  color: #b8ff57;
  text-shadow:
    0 0 4px #b8ff57,
    0 0 10px #b8ff57,
    0 0 20px #b8ff57,
    0 0 40px #6ddf00,
    0 0 80px #6ddf00;
}

.neon-blue {
  color: #38bdf8;
  text-shadow:
    0 0 4px #38bdf8,
    0 0 10px #38bdf8,
    0 0 20px #38bdf8,
    0 0 40px #0ea5e9,
    0 0 80px #0ea5e9;
  animation-delay: .5s;
}

.neon-pink {
  color: #f472b6;
  font-size: 1.5rem;
  text-shadow:
    0 0 4px #f472b6,
    0 0 10px #f472b6,
    0 0 20px #f472b6,
    0 0 40px #ec4899,
    0 0 80px #ec4899;
  animation-delay: 1s;
}

@keyframes neon-flicker {
  0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% {
    opacity: 1;
  }
  20%, 24%, 55% {
    opacity: .7;
    filter: brightness(.8);
  }
}

Other ways to build it

A sign that switches on

The off state is the same text in a dark, desaturated color with no shadow at all, and hovering restores the full stack. Because the whole glow lives in one property, the transition is one line. Every layer has to be present in both states for the browser to interpolate between them, so the off state lists five shadows too, all at zero blur in a color that disappears against the background. A run of none would make the change a jump instead of a fade.

HOVER TO LIGHT

HTML

<div class="neon-stage">
  <p class="neon neon-switch">HOVER TO LIGHT</p>
</div>

CSS

/* the same five layers, all at zero blur, so the browser has
   something to interpolate from */
.neon-switch {
  color: #3d4a2b;
  animation: none;
  text-shadow:
    0 0 0 transparent,
    0 0 0 transparent,
    0 0 0 transparent,
    0 0 0 transparent,
    0 0 0 transparent;
  transition: color .35s ease, text-shadow .35s ease;
}

.neon-switch:hover,
.neon-switch:focus-visible {
  color: #b8ff57;
  text-shadow:
    0 0 4px #b8ff57,
    0 0 10px #b8ff57,
    0 0 20px #b8ff57,
    0 0 40px #6ddf00,
    0 0 80px #6ddf00;
}

@media (prefers-reduced-motion: reduce) {
  .neon-switch { transition: none; }
}

Hollow tube with text-stroke

Emptying the fill with color: transparent and drawing the letter with -webkit-text-stroke gives the glass tube rather than the light inside it. The glow still works, because text-shadow is cast from the glyph outline and not from its fill, so a hollow letter throws exactly the same halo a solid one does. Give the rule a real color before the transparent one if the stroke property matters to you, since a browser without it renders transparent letters and nothing else.

OPEN

HTML

<div class="neon-stage">
  <p class="neon neon-tube">OPEN</p>
</div>

CSS

.neon-tube {
  /* the fallback for anything without text-stroke */
  color: #ff6b6b;
  -webkit-text-stroke: 2px #ffd7d7;
  color: transparent;
  letter-spacing: .12em;
  text-shadow:
    0 0 6px #ff6b6b,
    0 0 14px #ff6b6b,
    0 0 30px #ff2d2d,
    0 0 60px #ff2d2d;
}

How it works

Neon glow is created by stacking multiple text-shadow layers with increasing blur radii and matching colors. The innermost shadows are tight and bright (small blur), while outer shadows spread the glow. A flicker animation using opacity and filter: brightness at specific keyframe points, at 20%, 24% and 55%, mimics the irregular flicker of real neon signs without JavaScript.

Each text-shadow layer is offset-x offset-y blur color, and neon uses zero for both offsets so the glow sits centered on the letter rather than falling to one side. The blur radii here climb 4, 10, 20, 40, 80. Doubling roughly each time is what produces a smooth falloff; five layers at 4, 8, 12, 16 and 20 pixels would stack up into one hard edged halo instead of fading out.

The last two layers change color as well as size. The inner shadows repeat the text color and the outer ones use a deeper, more saturated version of it, which is how real neon behaves: the tube itself reads almost white at the center and the color is what you see in the air around it. Keeping every layer the same color gives a flatter, more sticker like glow.

The effect only works on a dark background, and that is a physical constraint rather than a stylistic one. A glow is additive light, and text-shadow cannot add light to a surface that is already near white. On a pale background the layers read as a muddy drop shadow. The stage element here sets its own near black background for exactly that reason.

The flicker animation holds opacity: 1 for most of its cycle and drops to .7 with filter: brightness(.8) at 20, 24 and 55 percent. Two of those are close together and one is a long way off, which is what stops it reading as a rhythm. animation-delay on the second and third signs offsets them by half a second and a second, so three signs in a row never flicker in unison.

A large blur radius is expensive to paint, and five of them on a heading at 2.5rem is fine while forty of them across a page of body copy is not. Where a glow has to animate, animate opacity or filter rather than the shadow itself: changing a blur radius forces the browser to re-rasterise the whole shadow stack on every frame, and the difference is visible on a mid range laptop.

CSS properties used

text-shadow
The whole effect. Multiple comma separated layers at zero offset and increasing blur, painted from the last layer forward.
color
The tube itself. Neon reads best when this is the lightest value in the set and the outer shadows are deeper.
filter
brightness() dims every layer at once during a flicker frame, which opacity alone cannot do as convincingly.
opacity
Dropped a little at flicker keyframes. Combined with brightness it reads as a tube losing power rather than fading out.
animation-delay
Offsets each sign so a row of them flickers independently instead of in unison.
-webkit-text-stroke
Draws an outline on the glyph. With a transparent fill it gives the hollow glass tube look in the second variant.

Browser support

FeatureChromeFirefoxSafariEdge
text-shadow43.5412
animation455.112
filter1835679
-webkit-text-stroke4493.115
prefers-reduced-motion746310.179

Nothing here has a meaningful support gap. -webkit-text-stroke is the one oddity: it is a non-standard property that every engine implements under the prefix, and Can I Use tracks it under CSS text-stroke and text-fill. The standard replacement is text-stroke, which is not yet something to rely on, so the prefixed form is still what shipping code uses. Where it is missing the letters simply fill with their color value, so give the tube variant a fallback color rather than leaving it transparent.

Accessibility notes

Glowing text is harder to read than plain text, and contrast checkers do not measure it. The blur bleeds color outward past the glyph edge, softening the boundary between the letter and the background in a way that a ratio calculated from two flat colors does not capture. Treat neon as display type: a heading, a short label, never a paragraph, and never anything a reader has to get right the first time.

The flicker is motion and it should stop when the system asks for less. A prefers-reduced-motion block that sets animation: none leaves the sign lit and steady, which keeps the entire visual effect and removes the part that causes trouble. That is a better answer here than removing the glow, because the glow is the design and the flicker is the decoration.

Flashing is a seizure risk when it is fast enough and covers enough of the screen. The cycle here is 4 seconds with three brief dips, well under the three flashes per second threshold. Speeding the animation up to make a sign look more broken is the change that pushes it into unsafe territory, so vary the pattern rather than the rate.

What you can build with it

  • Retro and arcade landing pages. A single glowing headline over a dark hero, which is the look the effect exists for.
  • Bar, club and event sites. An open sign or a venue name, usually paired with a deliberately imperfect flicker.
  • Portfolio and demo headers. One glowing line over a dark page, often alongside the glitch effect for a second layer of the same aesthetic.
  • Status indicators. A small glowing label that reads as live or on air, where the glow carries the state and the text carries the meaning.
  • Game interfaces. Score numbers and menu labels, where a dark interface makes the glow read the way it is meant to.

Mistakes worth avoiding

  • Putting neon on a light background. A glow is additive, and there is nothing to add to a pale surface, so the layers read as a smudged drop shadow.
  • Spacing the blur radii evenly. The layers stack into one hard edged halo rather than fading out, which looks like a bad outline instead of a light.
  • Using the same color for every layer. The tube loses its bright center and the whole thing flattens into a sticker.
  • Animating the blur radius. The browser re-rasterises the full shadow stack every frame. Animate opacity or filter instead.
  • Setting a flicker fast enough to read as strobing. Anything past three flashes per second is a seizure risk, and it does not look more broken, only more uncomfortable.

Frequently asked questions

How do you make neon text in CSS?
Stack several text-shadow layers at zero offset with blur radii that roughly double, such as 4px, 10px, 20px, 40px and 80px. Use the text color for the inner layers and a deeper version of it for the outer ones, and put the whole thing on a dark background.
Why does my neon text look flat or muddy?
Two causes. The background is too light, so the glow has nothing to add light to, or the blur radii are too close together, which stacks the layers into a single hard halo. Widen the gaps between radii and darken the surface behind the text.
How do you animate a neon flicker?
Write a @keyframes block that holds full opacity for most of the cycle and drops opacity and filter: brightness() at a few irregular percentages. Even spacing reads as a pulse, uneven spacing reads as a fault. Keep it under three flashes per second.
Is neon text bad for performance?
One heading is fine. A large blur radius is expensive to paint, so the cost scales with how much text carries the effect and how big it is. The change that actually hurts is animating a blur radius, since it forces the whole shadow stack to be redrawn on every frame.
Can you make hollow neon tube text?
Yes. Set -webkit-text-stroke to draw the outline, color: transparent to empty the fill, and keep the glow layers on text-shadow. The shadow is cast from the glyph shape rather than the fill, so the glow is still there behind the hollow letters. The second variant below does it.