---
name: neon-text
description: Use when you want "Nightlife, cyber, dramatic" - Adds glowing outlines or shadows to create a neon sign look.
---

# Neon Text

> **Category:** Text  -  **Personality:** Nightlife, cyber, dramatic
>
> **Best use:** Gaming, events, cyberpunk UI
>
> **Ratings:** Professional 2/5 - Casual 4/5 - Exotic 5/5 - Visual impact 5/5 - Difficulty 2/5 - Performance cost 2/5

## What it is

Neon Text fakes a glowing glass neon sign using nothing but stacked `text-shadow`s: a tiny white blur sits right on the glyph to read as the white-hot core of the tube, then progressively larger coloured blurs bloom outward as the gas glow. It only convinces on a **dark** background, where the colour can actually "spill" light onto the surface. Add a quick opacity flicker on one word and one dead/unlit letter and it stops looking like a CSS demo and starts looking like a real, slightly-broken bar sign. Great for nightlife, gaming, events and cyberpunk UIs; too loud for buttoned-up corporate work.

## Dependencies / CDN

None - pure CSS (the optional power switch is vanilla JS). The demo loads two display fonts - optional:

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;500;600;700&family=Yellowtail&display=swap" rel="stylesheet">
```

## HTML

A few glowing lines on a dark "wall". One word flickers (`.open`); one letter is a dead tube (`.dead`):

```html
<div class="stage">
  <div class="glow" aria-hidden="true"></div>
  <div class="sign">
    <p class="eyebrow">
      <span class="dot" aria-hidden="true"></span>
      <span class="neon open">Open</span>
      <span class="neon hours">&rsquo;til 4&nbsp;am &middot; est. 1986</span>
    </p>
    <h2 class="neon hero">Voltage</h2>
    <div class="neon arcade" aria-label="Arcade">AR<span class="dead">C</span>ADE</div>
    <p class="neon tag">Cocktails &middot; Pinball &middot; Synthwave</p>
  </div>
</div>
```

## CSS

```css
/* THE RECIPE: white-hot core (tight #fff blur) + widening colour blooms */
.hero{
  font-family:'Yellowtail',cursive;
  font-size:clamp(64px,13.5vw,168px); line-height:.86; color:#fff;
  text-shadow:
    0 0 6px  #fff,        /* 1) tight white core      */
    0 0 16px #ff8fe6,     /* 2) soft inner colour      */
    0 0 32px #ff2bd6,
    0 0 62px #ff2bd6,     /* 3) wide outer bloom       */
    0 0 110px #ff2bd6;
}

/* a second tube colour (cyan), wide-tracked like real sign lettering */
.arcade{
  font-family:'Chakra Petch',sans-serif; font-weight:700; color:#fff;
  letter-spacing:.46em; padding-left:.46em; text-transform:uppercase;
  text-shadow:0 0 4px #fff,0 0 11px #9af6ff,0 0 24px #19e6ff,0 0 48px #19e6ff;
}

/* a broken / unlit tube: kill the glow, dim the glass, give it a faint rim */
.arcade .dead{
  color:#352d3d; text-shadow:none;
  -webkit-text-stroke:1px rgba(150,140,165,.28);
  animation:buzz 8s steps(1,end) infinite;     /* rare dying-tube stutter */
}

/* flicker ONE word */
.open{
  color:#fff;
  text-shadow:0 0 4px #fff,0 0 12px #7dffc0,0 0 24px #39ff9e,0 0 44px #19c46e;
  animation:flicker 5.5s infinite;
}

/* the dark wall is what makes the glow read */
.stage{position:relative; overflow:hidden; border-radius:24px; isolation:isolate;
  min-height:620px; display:grid; place-items:center; background:#0b0810}

@keyframes flicker{                              /* mostly lit, brief abrupt dips */
  0%,42%{opacity:1} 42.6%{opacity:.18} 43.2%{opacity:1} 43.9%{opacity:.5} 44.5%{opacity:1}
  71.6%{opacity:1} 72%{opacity:.22} 72.5%{opacity:1}
  87%{opacity:1} 87.5%{opacity:.55} 88%,100%{opacity:1}
}
@keyframes buzz{
  0%,92%,100%{color:#352d3d;text-shadow:none}
  92.5%{color:#9fe0ff;text-shadow:0 0 6px rgba(120,220,255,.55)}
  94%{color:#c2ecff;text-shadow:0 0 8px rgba(150,225,255,.6)}
}

/* steady, no flicker for motion-sensitive users */
@media (prefers-reduced-motion:reduce){
  .open,.dead{animation:none!important} .open{opacity:1!important}
}
```

## JavaScript

Not required - the glow, flicker and dead tube are pure CSS. Optional flourish: a power switch that turns every tube dark (toggle a `.off` class that sets `color` to a dim grey and `text-shadow:none`), replaying a start-up stutter on power-on (skipped under reduced motion):

```js
var stage = document.querySelector('.stage');
var btn   = stage && stage.querySelector('.power-btn');
function reduce(){ return !!(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches); }
btn && btn.addEventListener('click', function(){
  var turningOn = stage.classList.contains('off');
  stage.classList.toggle('off');
  btn.setAttribute('aria-pressed', String(turningOn));
  stage.classList.remove('powering');
  if (turningOn && !reduce()){
    void stage.offsetWidth;                 // restart the animation
    stage.classList.add('powering');        // .powering .sign { animation:power-on .9s }
    setTimeout(function(){ stage.classList.remove('powering'); }, 950);
  }
});
```

## How it works

- **Layered `text-shadow` = the whole trick.** Each comma-separated shadow is another blurred copy of the glyph. Order matters: a near-zero-blur **white** shadow first gives the bright tube core, then 2-3 increasingly large **coloured** shadows fake the gas halo. Keep the fill `#fff` so the letter centre stays white-hot.
- **Dark surface is mandatory.** Glow is additive light; on a pale background the coloured blur just looks like a muddy drop shadow. The demo also paints faint magenta/cyan radial gradients (`.glow`) so the wall behind the sign looks lit.
- **Flicker** is just an `opacity` animation that sits at `1` almost the whole time and snaps to `~.18` for a few milliseconds at irregular keyframes - dimming the glyph and its glow together, exactly like a tube losing power. Applied to **one** word so it reads as a fault, not a theme.
- **Dead tube** drops `text-shadow` entirely, dims the fill to a grey "unlit glass" colour, and adds a hairline `-webkit-text-stroke` rim. The text stays in the DOM (and in an `aria-label`) so the word is still legible to assistive tech.

## Customization

- **Colour:** change the colour used in shadows 2-5 (`#ff2bd6` magenta, `#19e6ff` cyan, `#39ff9e` green here). Keep the first shadow white for the hot core.
- **Intensity:** add/remove bloom layers or widen the last blur (`0 0 110px`) for a brighter sign; drop it to `0 0 40px` for a subtle, classy glow.
- **Tube weight:** thin scripts (Yellowtail, Sacramento) read as delicate bent glass; heavy uppercase (Chakra Petch, Monoton) reads as chunky channel letters. Wide `letter-spacing` sells the "individual tubes" look.
- **Flicker feel:** edit the keyframe percentages - more dips = a sicker tube; a single shallow dip = a gentle buzz.
- **Off state:** reuse the `.dead` styling (grey fill, no shadow) on every tube for a convincing "sign switched off".

## Accessibility & performance

- **Respect `prefers-reduced-motion`** - the demo sets `animation:none` and forces full opacity, so reduced-motion users get a steady, readable sign with no flashing (important: flicker can be a seizure / vestibular trigger).
- Cost is low: `text-shadow` and `opacity` are cheap and GPU-friendly. Animate **`opacity`** (as here), never the `text-shadow` of a large body of text every frame.
- Keep the real text as text (never bake it into an image) and give "broken" words an `aria-label` so the glow gimmick never costs you legibility.
- Contrast is excellent (near-white glyph on near-black); just keep the dead/unlit letters out of anything a user must read.

## Gotchas

- **Light backgrounds kill it.** No dark surface, no glow - it just looks like a blurry shadow.
- **`-webkit-text-stroke` is unprefixed-unsupported** in some engines; treat the dead-tube rim as a progressive enhancement, not load-bearing.
- **Huge blur radii can be clipped** by an ancestor `overflow:hidden` - give the stage generous padding (the demo uses 64px) so the bloom isn't sliced at the edges.
- **Don't flicker everything.** Animating every word at once looks like a strobe/bug; flicker exactly one word (and dead-tube exactly one letter) for a believable, characterful sign.
- The global reduced-motion reset in a shared stylesheet may set `animation-duration` to near-zero, which can *freeze* a flickering element on an "off" frame - override it explicitly with `animation:none` and a lit base state (as the media query above does).
