03 / 20
All 20 in 20 CSS Neon Effects
- 01CSS Glow Anatomy with Layered Box Shadow
- 02Drop Shadow Glow for Irregular Shapes
- 03Neon Color Choice and Perceived Luminance
- 04Inner Glow and Faked Light Emission
- 05Animating Glow Intensity with at-property
- 06Glow on SVG Paths and Stroke Elements
- 07Neon Glow Paint Cost and What to Animate
- 08Accessible Neon on Dark Backgrounds
- 09Neon Glow Button Construction Reference
- 10Neon Heading and Display Type Glow
- 11Neon Pricing Table with Glow Tiers
- 12Layered Glow Scene with Depth Falloff
- 13Neon Form Inputs and Focus Glow
- 14Neon Navigation with Active Glow State
- 15Flicker Timing and Failing Tube Effect
- 16Cyberpunk Neon Palette and Token Set
- 17Tailwind Neon Glow Utility Recipe
- 18Neon Icon Glow with SVG Filters
- 19Infinite Neon Grid and Perspective Floor
- 20Multi Hue Neon Gradient Glow
Neon Color Choice and Perceived Luminance
Why cyan and magenta glow convincingly while a deep blue or a brown reads as mud at identical shadow values. Perceived luminance varies enormously by hue at the same nominal lightness, so one fixed alpha and blur produces wildly different results across a palette. One recipe is applied to eight hues, the failures are labelled, then the same eight are corrected with oklch().
Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time
Untitled UI · Affiliate link
The code
<section class="nn-03" aria-labelledby="nn-03-heading">
<div class="nn-03__stage">
<div class="nn-03__theme">
<input class="nn-03__tin" type="checkbox" id="nn-03-theme">
<label class="nn-03__tlab" for="nn-03-theme">
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.4 3.4l1.4 1.4M11.2 11.2l1.4 1.4M12.6 3.4l-1.4 1.4M4.8 11.2l-1.4 1.4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path><circle cx="8" cy="8" r="2.6" fill="none" stroke="currentColor" stroke-width="1.5"></circle></svg>
Flip light / dark
</label>
</div>
<header class="nn-03__head">
<p class="nn-03__kicker">One recipe · eight hues</p>
<h2 class="nn-03__heading" id="nn-03-heading">Some hues glow. Others go muddy.</h2>
<p class="nn-03__sub">Identical blur, identical alpha, identical ground. Only the hue changes — and that is enough to break the effect.</p>
</header>
<section class="nn-03__row" aria-labelledby="nn-03-naive">
<h3 class="nn-03__rtitle" id="nn-03-naive">
<span class="nn-03__tag nn-03__tag--bad">Naive</span>
hsl(h 100% 50%) at a fixed alpha
</h3>
<ul class="nn-03__grid">
<li class="nn-03__item" style="--nn-03-h: 195;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Cyan</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 150;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Spring</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 95;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Lime</p><p class="nn-03__verdict nn-03__verdict--ok">too hot, blows out</p></li>
<li class="nn-03__item" style="--nn-03-h: 48;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Amber</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 22;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Warm red</p><p class="nn-03__verdict nn-03__verdict--warn">dull, needs lift</p></li>
<li class="nn-03__item" style="--nn-03-h: 322;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Magenta</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 272;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Violet</p><p class="nn-03__verdict nn-03__verdict--warn">dim halo</p></li>
<li class="nn-03__item" style="--nn-03-h: 245;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Deep blue</p><p class="nn-03__verdict nn-03__verdict--bad">muddy — no headroom</p></li>
</ul>
</section>
<section class="nn-03__row" aria-labelledby="nn-03-fixed">
<h3 class="nn-03__rtitle" id="nn-03-fixed">
<span class="nn-03__tag nn-03__tag--good">Corrected</span>
oklch(85% 0.16 h) — lightness held constant
</h3>
<ul class="nn-03__grid">
<li class="nn-03__item" style="--nn-03-h: 195;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Cyan</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 150;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Spring</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 95;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Lime</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 48;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Amber</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 22;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Warm red</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 322;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Magenta</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 272;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Violet</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 245;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Deep blue</p><p class="nn-03__verdict nn-03__verdict--ok">chroma clamped, still lit</p></li>
</ul>
</section>
<p class="nn-03__rule">When a hue looks weak, lift its lightness — not the shadow alpha. Alpha flattens the falloff and turns a glow back into a smudge.</p>
</div>
</section><section class="nn-03" aria-labelledby="nn-03-heading">
<div class="nn-03__stage">
<div class="nn-03__theme">
<input class="nn-03__tin" type="checkbox" id="nn-03-theme">
<label class="nn-03__tlab" for="nn-03-theme">
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.4 3.4l1.4 1.4M11.2 11.2l1.4 1.4M12.6 3.4l-1.4 1.4M4.8 11.2l-1.4 1.4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path><circle cx="8" cy="8" r="2.6" fill="none" stroke="currentColor" stroke-width="1.5"></circle></svg>
Flip light / dark
</label>
</div>
<header class="nn-03__head">
<p class="nn-03__kicker">One recipe · eight hues</p>
<h2 class="nn-03__heading" id="nn-03-heading">Some hues glow. Others go muddy.</h2>
<p class="nn-03__sub">Identical blur, identical alpha, identical ground. Only the hue changes — and that is enough to break the effect.</p>
</header>
<section class="nn-03__row" aria-labelledby="nn-03-naive">
<h3 class="nn-03__rtitle" id="nn-03-naive">
<span class="nn-03__tag nn-03__tag--bad">Naive</span>
hsl(h 100% 50%) at a fixed alpha
</h3>
<ul class="nn-03__grid">
<li class="nn-03__item" style="--nn-03-h: 195;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Cyan</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 150;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Spring</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 95;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Lime</p><p class="nn-03__verdict nn-03__verdict--ok">too hot, blows out</p></li>
<li class="nn-03__item" style="--nn-03-h: 48;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Amber</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 22;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Warm red</p><p class="nn-03__verdict nn-03__verdict--warn">dull, needs lift</p></li>
<li class="nn-03__item" style="--nn-03-h: 322;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Magenta</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 272;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Violet</p><p class="nn-03__verdict nn-03__verdict--warn">dim halo</p></li>
<li class="nn-03__item" style="--nn-03-h: 245;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Deep blue</p><p class="nn-03__verdict nn-03__verdict--bad">muddy — no headroom</p></li>
</ul>
</section>
<section class="nn-03__row" aria-labelledby="nn-03-fixed">
<h3 class="nn-03__rtitle" id="nn-03-fixed">
<span class="nn-03__tag nn-03__tag--good">Corrected</span>
oklch(85% 0.16 h) — lightness held constant
</h3>
<ul class="nn-03__grid">
<li class="nn-03__item" style="--nn-03-h: 195;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Cyan</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 150;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Spring</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 95;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Lime</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 48;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Amber</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 22;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Warm red</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 322;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Magenta</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 272;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Violet</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 245;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Deep blue</p><p class="nn-03__verdict nn-03__verdict--ok">chroma clamped, still lit</p></li>
</ul>
</section>
<p class="nn-03__rule">When a hue looks weak, lift its lightness — not the shadow alpha. Alpha flattens the falloff and turns a glow back into a smudge.</p>
</div>
</section>.nn-03 {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
}
@media (prefers-color-scheme: light) {
.nn-03 {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
}
}
@media (prefers-color-scheme: dark) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
}
.nn-03[data-theme="light"] {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
.nn-03 {
--nn-03-spec-ink: #f5efe9;
--nn-03-spec-dim: #a2968d;
}
.nn-03__theme {
position: absolute;
inset-block-start: clamp(10px, 2vw, 18px);
inset-inline-end: clamp(10px, 2vw, 18px);
z-index: 5;
display: flex;
justify-content: flex-end;
}
.nn-03__tin {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-03__tlab {
display: inline-flex;
align-items: center;
gap: .4rem;
min-height: 44px;
padding: 0 .8rem;
border: 1px solid currentColor;
font-size: .7rem;
letter-spacing: .12em;
text-transform: uppercase;
cursor: pointer;
opacity: .7;
}
.nn-03__tlab svg {
width: .9rem;
height: .9rem;
flex: none;
}
.nn-03__tlab:hover {
opacity: 1;
}
.nn-03__tin:focus-visible + .nn-03__tlab {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
}
}
@media (prefers-color-scheme: dark) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
}
.nn-03 {
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
--nn-03-font-display: "Helvetica Neue", "Inter Tight", system-ui, sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: block;
box-sizing: border-box;
background: var(--nn-03-bg);
color: var(--nn-03-ink);
font-family: system-ui, sans-serif;
}
.nn-03 *,
.nn-03 *::before,
.nn-03 *::after {
box-sizing: border-box;
}
@supports (color: oklch(50% 0.1 200)) {
.nn-03 {
--nn-03-bg: oklch(17% 0.008 45);
--nn-03-well: oklch(21% 0.01 45);
--nn-03-line: oklch(31% 0.012 45);
--nn-03-ink: oklch(96% 0.012 60);
--nn-03-muted: oklch(73% 0.014 55);
--nn-03-accent: oklch(91% 0.03 70);
}
}
@media (prefers-color-scheme: light) {
.nn-03 {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
}
.nn-03[data-theme="light"] {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
.nn-03[data-theme="dark"] {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
}
.nn-03__stage {
max-width: 72rem;
margin: 0 auto;
padding: clamp(1rem, 3.5vw, 1.5rem) clamp(.75rem, 3.5vw, 1.5rem) clamp(1.5rem, 4vw, 2rem);
display: grid;
gap: clamp(1rem, 3vw, 1.5rem);
}
.nn-03__kicker {
margin: 0 0 .35rem;
font-family: var(--nn-03-font-display);
font-size: .66rem;
letter-spacing: .28em;
text-transform: uppercase;
color: var(--nn-03-muted);
}
.nn-03__heading {
margin: 0;
font-family: var(--nn-03-font-display);
font-size: clamp(1.3rem, 4.5vw, 1.95rem);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
text-wrap: balance;
}
.nn-03__sub {
margin: .45rem 0 0;
max-width: 40rem;
font-size: .85rem;
line-height: 1.55;
color: var(--nn-03-muted);
text-wrap: pretty;
}
.nn-03__row {
display: grid;
gap: .6rem;
padding: .75rem;
border: 1px solid var(--nn-03-line);
background: var(--nn-03-well);
}
.nn-03__rtitle {
margin: 0;
display: flex;
align-items: center;
gap: .5rem;
flex-wrap: wrap;
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
font-weight: 400;
color: var(--nn-03-spec-dim);
min-inline-size: 0;
}
.nn-03__tag {
padding: .15rem .4rem;
border: 1px solid var(--nn-03-line);
font-family: var(--nn-03-font-display);
font-size: .6rem;
letter-spacing: .16em;
text-transform: uppercase;
}
.nn-03__tag--bad {
color: #ff9f9f;
border-color: #4b2a2a;
}
.nn-03__tag--good {
color: #a8f0c0;
border-color: #234634;
}
.nn-03__grid {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: .5rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 26rem) {
.nn-03__grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
@media (min-width: 60rem) {
.nn-03__grid {
grid-template-columns: repeat(8, minmax(0, 1fr));
}
}
.nn-03__item {
display: grid;
gap: .3rem;
justify-items: center;
padding: .75rem .25rem .5rem;
min-inline-size: 0;
}
.nn-03__swatch {
width: 100%;
max-width: 3.5rem;
aspect-ratio: 1;
border-radius: 2px;
}
.nn-03__swatch--naive {
background: hsl(var(--nn-03-h) 100% 50%);
box-shadow: 0 0 .3rem hsl(var(--nn-03-h) 100% 50% / .9), 0 0 clamp(.75rem, 3vw, 1.4rem) hsl(var(--nn-03-h) 100% 50% / .45);
}
.nn-03__swatch--fixed {
background: #cccccc;
box-shadow: 0 0 .3rem rgba(204, 204, 204, .9), 0 0 clamp(.75rem, 3vw, 1.4rem) rgba(204, 204, 204, .45);
}
@supports (color: oklch(50% 0.1 200)) {
.nn-03__swatch--fixed {
background: oklch(85% 0.16 var(--nn-03-h));
}
}
@supports (color: color-mix(in oklab, red, blue)) {
.nn-03__swatch--fixed {
box-shadow: 0 0 .3rem color-mix(in oklab, oklch(85% 0.16 var(--nn-03-h)) 90%, transparent), 0 0 clamp(.75rem, 3vw, 1.4rem) color-mix(in oklab, oklch(85% 0.16 var(--nn-03-h)) 45%, transparent);
}
}
.nn-03__name {
margin: .2rem 0 0;
font-size: .72rem;
font-weight: 600;
text-align: center;
color: var(--nn-03-spec-ink);
}
.nn-03__verdict {
margin: 0;
font-family: ui-monospace, Menlo, monospace;
font-size: .6rem;
line-height: 1.35;
text-align: center;
color: var(--nn-03-spec-dim);
}
.nn-03__verdict--warn {
color: #f0c88a;
}
.nn-03__verdict--bad {
color: #ff9f9f;
}
.nn-03__rule {
margin: 0;
padding: .9rem 1rem;
border: 1px solid var(--nn-03-line);
border-left: 2px solid var(--nn-03-accent);
font-size: .84rem;
line-height: 1.6;
color: var(--nn-03-ink);
text-wrap: pretty;
}.nn-03 {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
}
@media (prefers-color-scheme: light) {
.nn-03 {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
}
}
@media (prefers-color-scheme: dark) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
}
.nn-03[data-theme="light"] {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
.nn-03 {
--nn-03-spec-ink: #f5efe9;
--nn-03-spec-dim: #a2968d;
}
.nn-03__theme {
position: absolute;
inset-block-start: clamp(10px, 2vw, 18px);
inset-inline-end: clamp(10px, 2vw, 18px);
z-index: 5;
display: flex;
justify-content: flex-end;
}
.nn-03__tin {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-03__tlab {
display: inline-flex;
align-items: center;
gap: .4rem;
min-height: 44px;
padding: 0 .8rem;
border: 1px solid currentColor;
font-size: .7rem;
letter-spacing: .12em;
text-transform: uppercase;
cursor: pointer;
opacity: .7;
}
.nn-03__tlab svg {
width: .9rem;
height: .9rem;
flex: none;
}
.nn-03__tlab:hover {
opacity: 1;
}
.nn-03__tin:focus-visible + .nn-03__tlab {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
}
}
@media (prefers-color-scheme: dark) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
}
.nn-03 {
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
--nn-03-font-display: "Helvetica Neue", "Inter Tight", system-ui, sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: block;
box-sizing: border-box;
background: var(--nn-03-bg);
color: var(--nn-03-ink);
font-family: system-ui, sans-serif;
}
.nn-03 *,
.nn-03 *::before,
.nn-03 *::after {
box-sizing: border-box;
}
@supports (color: oklch(50% 0.1 200)) {
.nn-03 {
--nn-03-bg: oklch(17% 0.008 45);
--nn-03-well: oklch(21% 0.01 45);
--nn-03-line: oklch(31% 0.012 45);
--nn-03-ink: oklch(96% 0.012 60);
--nn-03-muted: oklch(73% 0.014 55);
--nn-03-accent: oklch(91% 0.03 70);
}
}
@media (prefers-color-scheme: light) {
.nn-03 {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
}
.nn-03[data-theme="light"] {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
.nn-03[data-theme="dark"] {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
}
.nn-03__stage {
max-width: 72rem;
margin: 0 auto;
padding: clamp(1rem, 3.5vw, 1.5rem) clamp(.75rem, 3.5vw, 1.5rem) clamp(1.5rem, 4vw, 2rem);
display: grid;
gap: clamp(1rem, 3vw, 1.5rem);
}
.nn-03__kicker {
margin: 0 0 .35rem;
font-family: var(--nn-03-font-display);
font-size: .66rem;
letter-spacing: .28em;
text-transform: uppercase;
color: var(--nn-03-muted);
}
.nn-03__heading {
margin: 0;
font-family: var(--nn-03-font-display);
font-size: clamp(1.3rem, 4.5vw, 1.95rem);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
text-wrap: balance;
}
.nn-03__sub {
margin: .45rem 0 0;
max-width: 40rem;
font-size: .85rem;
line-height: 1.55;
color: var(--nn-03-muted);
text-wrap: pretty;
}
.nn-03__row {
display: grid;
gap: .6rem;
padding: .75rem;
border: 1px solid var(--nn-03-line);
background: var(--nn-03-well);
}
.nn-03__rtitle {
margin: 0;
display: flex;
align-items: center;
gap: .5rem;
flex-wrap: wrap;
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
font-weight: 400;
color: var(--nn-03-spec-dim);
min-inline-size: 0;
}
.nn-03__tag {
padding: .15rem .4rem;
border: 1px solid var(--nn-03-line);
font-family: var(--nn-03-font-display);
font-size: .6rem;
letter-spacing: .16em;
text-transform: uppercase;
}
.nn-03__tag--bad {
color: #ff9f9f;
border-color: #4b2a2a;
}
.nn-03__tag--good {
color: #a8f0c0;
border-color: #234634;
}
.nn-03__grid {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: .5rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 26rem) {
.nn-03__grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
@media (min-width: 60rem) {
.nn-03__grid {
grid-template-columns: repeat(8, minmax(0, 1fr));
}
}
.nn-03__item {
display: grid;
gap: .3rem;
justify-items: center;
padding: .75rem .25rem .5rem;
min-inline-size: 0;
}
.nn-03__swatch {
width: 100%;
max-width: 3.5rem;
aspect-ratio: 1;
border-radius: 2px;
}
.nn-03__swatch--naive {
background: hsl(var(--nn-03-h) 100% 50%);
box-shadow: 0 0 .3rem hsl(var(--nn-03-h) 100% 50% / .9), 0 0 clamp(.75rem, 3vw, 1.4rem) hsl(var(--nn-03-h) 100% 50% / .45);
}
.nn-03__swatch--fixed {
background: #cccccc;
box-shadow: 0 0 .3rem rgba(204, 204, 204, .9), 0 0 clamp(.75rem, 3vw, 1.4rem) rgba(204, 204, 204, .45);
}
@supports (color: oklch(50% 0.1 200)) {
.nn-03__swatch--fixed {
background: oklch(85% 0.16 var(--nn-03-h));
}
}
@supports (color: color-mix(in oklab, red, blue)) {
.nn-03__swatch--fixed {
box-shadow: 0 0 .3rem color-mix(in oklab, oklch(85% 0.16 var(--nn-03-h)) 90%, transparent), 0 0 clamp(.75rem, 3vw, 1.4rem) color-mix(in oklab, oklch(85% 0.16 var(--nn-03-h)) 45%, transparent);
}
}
.nn-03__name {
margin: .2rem 0 0;
font-size: .72rem;
font-weight: 600;
text-align: center;
color: var(--nn-03-spec-ink);
}
.nn-03__verdict {
margin: 0;
font-family: ui-monospace, Menlo, monospace;
font-size: .6rem;
line-height: 1.35;
text-align: center;
color: var(--nn-03-spec-dim);
}
.nn-03__verdict--warn {
color: #f0c88a;
}
.nn-03__verdict--bad {
color: #ff9f9f;
}
.nn-03__rule {
margin: 0;
padding: .9rem 1rem;
border: 1px solid var(--nn-03-line);
border-left: 2px solid var(--nn-03-accent);
font-size: .84rem;
line-height: 1.6;
color: var(--nn-03-ink);
text-wrap: pretty;
}Here's a working CSS Neon 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: Neon Color Choice and Perceived Luminance
Source: https://codefronts.com/design-styles/css-neon/neon-color-choice-and-perceived-luminance/
Why cyan and magenta glow convincingly while a deep blue or a brown reads as mud at identical shadow values. Perceived luminance varies enormously by hue at the same nominal lightness, so one fixed alpha and blur produces wildly different results across a palette. One recipe is applied to eight hues, the failures are labelled, then the same eight are corrected with oklch().
## HTML
```html
<section class="nn-03" aria-labelledby="nn-03-heading">
<div class="nn-03__stage">
<div class="nn-03__theme">
<input class="nn-03__tin" type="checkbox" id="nn-03-theme">
<label class="nn-03__tlab" for="nn-03-theme">
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.4 3.4l1.4 1.4M11.2 11.2l1.4 1.4M12.6 3.4l-1.4 1.4M4.8 11.2l-1.4 1.4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path><circle cx="8" cy="8" r="2.6" fill="none" stroke="currentColor" stroke-width="1.5"></circle></svg>
Flip light / dark
</label>
</div>
<header class="nn-03__head">
<p class="nn-03__kicker">One recipe · eight hues</p>
<h2 class="nn-03__heading" id="nn-03-heading">Some hues glow. Others go muddy.</h2>
<p class="nn-03__sub">Identical blur, identical alpha, identical ground. Only the hue changes — and that is enough to break the effect.</p>
</header>
<section class="nn-03__row" aria-labelledby="nn-03-naive">
<h3 class="nn-03__rtitle" id="nn-03-naive">
<span class="nn-03__tag nn-03__tag--bad">Naive</span>
hsl(h 100% 50%) at a fixed alpha
</h3>
<ul class="nn-03__grid">
<li class="nn-03__item" style="--nn-03-h: 195;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Cyan</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 150;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Spring</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 95;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Lime</p><p class="nn-03__verdict nn-03__verdict--ok">too hot, blows out</p></li>
<li class="nn-03__item" style="--nn-03-h: 48;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Amber</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 22;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Warm red</p><p class="nn-03__verdict nn-03__verdict--warn">dull, needs lift</p></li>
<li class="nn-03__item" style="--nn-03-h: 322;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Magenta</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 272;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Violet</p><p class="nn-03__verdict nn-03__verdict--warn">dim halo</p></li>
<li class="nn-03__item" style="--nn-03-h: 245;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Deep blue</p><p class="nn-03__verdict nn-03__verdict--bad">muddy — no headroom</p></li>
</ul>
</section>
<section class="nn-03__row" aria-labelledby="nn-03-fixed">
<h3 class="nn-03__rtitle" id="nn-03-fixed">
<span class="nn-03__tag nn-03__tag--good">Corrected</span>
oklch(85% 0.16 h) — lightness held constant
</h3>
<ul class="nn-03__grid">
<li class="nn-03__item" style="--nn-03-h: 195;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Cyan</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 150;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Spring</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 95;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Lime</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 48;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Amber</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 22;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Warm red</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 322;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Magenta</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 272;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Violet</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 245;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Deep blue</p><p class="nn-03__verdict nn-03__verdict--ok">chroma clamped, still lit</p></li>
</ul>
</section>
<p class="nn-03__rule">When a hue looks weak, lift its lightness — not the shadow alpha. Alpha flattens the falloff and turns a glow back into a smudge.</p>
</div>
</section>
```
## CSS
```css
.nn-03 {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
}
@media (prefers-color-scheme: light) {
.nn-03 {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
}
}
@media (prefers-color-scheme: dark) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
}
.nn-03[data-theme="light"] {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
.nn-03 {
--nn-03-spec-ink: #f5efe9;
--nn-03-spec-dim: #a2968d;
}
.nn-03__theme {
position: absolute;
inset-block-start: clamp(10px, 2vw, 18px);
inset-inline-end: clamp(10px, 2vw, 18px);
z-index: 5;
display: flex;
justify-content: flex-end;
}
.nn-03__tin {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-03__tlab {
display: inline-flex;
align-items: center;
gap: .4rem;
min-height: 44px;
padding: 0 .8rem;
border: 1px solid currentColor;
font-size: .7rem;
letter-spacing: .12em;
text-transform: uppercase;
cursor: pointer;
opacity: .7;
}
.nn-03__tlab svg {
width: .9rem;
height: .9rem;
flex: none;
}
.nn-03__tlab:hover {
opacity: 1;
}
.nn-03__tin:focus-visible + .nn-03__tlab {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
}
}
@media (prefers-color-scheme: dark) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
}
.nn-03 {
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
--nn-03-font-display: "Helvetica Neue", "Inter Tight", system-ui, sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: block;
box-sizing: border-box;
background: var(--nn-03-bg);
color: var(--nn-03-ink);
font-family: system-ui, sans-serif;
}
.nn-03 *,
.nn-03 *::before,
.nn-03 *::after {
box-sizing: border-box;
}
@supports (color: oklch(50% 0.1 200)) {
.nn-03 {
--nn-03-bg: oklch(17% 0.008 45);
--nn-03-well: oklch(21% 0.01 45);
--nn-03-line: oklch(31% 0.012 45);
--nn-03-ink: oklch(96% 0.012 60);
--nn-03-muted: oklch(73% 0.014 55);
--nn-03-accent: oklch(91% 0.03 70);
}
}
@media (prefers-color-scheme: light) {
.nn-03 {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
}
.nn-03[data-theme="light"] {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
.nn-03[data-theme="dark"] {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
}
.nn-03__stage {
max-width: 72rem;
margin: 0 auto;
padding: clamp(1rem, 3.5vw, 1.5rem) clamp(.75rem, 3.5vw, 1.5rem) clamp(1.5rem, 4vw, 2rem);
display: grid;
gap: clamp(1rem, 3vw, 1.5rem);
}
.nn-03__kicker {
margin: 0 0 .35rem;
font-family: var(--nn-03-font-display);
font-size: .66rem;
letter-spacing: .28em;
text-transform: uppercase;
color: var(--nn-03-muted);
}
.nn-03__heading {
margin: 0;
font-family: var(--nn-03-font-display);
font-size: clamp(1.3rem, 4.5vw, 1.95rem);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
text-wrap: balance;
}
.nn-03__sub {
margin: .45rem 0 0;
max-width: 40rem;
font-size: .85rem;
line-height: 1.55;
color: var(--nn-03-muted);
text-wrap: pretty;
}
.nn-03__row {
display: grid;
gap: .6rem;
padding: .75rem;
border: 1px solid var(--nn-03-line);
background: var(--nn-03-well);
}
.nn-03__rtitle {
margin: 0;
display: flex;
align-items: center;
gap: .5rem;
flex-wrap: wrap;
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
font-weight: 400;
color: var(--nn-03-spec-dim);
min-inline-size: 0;
}
.nn-03__tag {
padding: .15rem .4rem;
border: 1px solid var(--nn-03-line);
font-family: var(--nn-03-font-display);
font-size: .6rem;
letter-spacing: .16em;
text-transform: uppercase;
}
.nn-03__tag--bad {
color: #ff9f9f;
border-color: #4b2a2a;
}
.nn-03__tag--good {
color: #a8f0c0;
border-color: #234634;
}
.nn-03__grid {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: .5rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 26rem) {
.nn-03__grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
@media (min-width: 60rem) {
.nn-03__grid {
grid-template-columns: repeat(8, minmax(0, 1fr));
}
}
.nn-03__item {
display: grid;
gap: .3rem;
justify-items: center;
padding: .75rem .25rem .5rem;
min-inline-size: 0;
}
.nn-03__swatch {
width: 100%;
max-width: 3.5rem;
aspect-ratio: 1;
border-radius: 2px;
}
.nn-03__swatch--naive {
background: hsl(var(--nn-03-h) 100% 50%);
box-shadow: 0 0 .3rem hsl(var(--nn-03-h) 100% 50% / .9), 0 0 clamp(.75rem, 3vw, 1.4rem) hsl(var(--nn-03-h) 100% 50% / .45);
}
.nn-03__swatch--fixed {
background: #cccccc;
box-shadow: 0 0 .3rem rgba(204, 204, 204, .9), 0 0 clamp(.75rem, 3vw, 1.4rem) rgba(204, 204, 204, .45);
}
@supports (color: oklch(50% 0.1 200)) {
.nn-03__swatch--fixed {
background: oklch(85% 0.16 var(--nn-03-h));
}
}
@supports (color: color-mix(in oklab, red, blue)) {
.nn-03__swatch--fixed {
box-shadow: 0 0 .3rem color-mix(in oklab, oklch(85% 0.16 var(--nn-03-h)) 90%, transparent), 0 0 clamp(.75rem, 3vw, 1.4rem) color-mix(in oklab, oklch(85% 0.16 var(--nn-03-h)) 45%, transparent);
}
}
.nn-03__name {
margin: .2rem 0 0;
font-size: .72rem;
font-weight: 600;
text-align: center;
color: var(--nn-03-spec-ink);
}
.nn-03__verdict {
margin: 0;
font-family: ui-monospace, Menlo, monospace;
font-size: .6rem;
line-height: 1.35;
text-align: center;
color: var(--nn-03-spec-dim);
}
.nn-03__verdict--warn {
color: #f0c88a;
}
.nn-03__verdict--bad {
color: #ff9f9f;
}
.nn-03__rule {
margin: 0;
padding: .9rem 1rem;
border: 1px solid var(--nn-03-line);
border-left: 2px solid var(--nn-03-accent);
font-size: .84rem;
line-height: 1.6;
color: var(--nn-03-ink);
text-wrap: pretty;
}
```Here's a working CSS Neon 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: Neon Color Choice and Perceived Luminance
Source: https://codefronts.com/design-styles/css-neon/neon-color-choice-and-perceived-luminance/
Why cyan and magenta glow convincingly while a deep blue or a brown reads as mud at identical shadow values. Perceived luminance varies enormously by hue at the same nominal lightness, so one fixed alpha and blur produces wildly different results across a palette. One recipe is applied to eight hues, the failures are labelled, then the same eight are corrected with oklch().
## HTML
```html
<section class="nn-03" aria-labelledby="nn-03-heading">
<div class="nn-03__stage">
<div class="nn-03__theme">
<input class="nn-03__tin" type="checkbox" id="nn-03-theme">
<label class="nn-03__tlab" for="nn-03-theme">
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.4 3.4l1.4 1.4M11.2 11.2l1.4 1.4M12.6 3.4l-1.4 1.4M4.8 11.2l-1.4 1.4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path><circle cx="8" cy="8" r="2.6" fill="none" stroke="currentColor" stroke-width="1.5"></circle></svg>
Flip light / dark
</label>
</div>
<header class="nn-03__head">
<p class="nn-03__kicker">One recipe · eight hues</p>
<h2 class="nn-03__heading" id="nn-03-heading">Some hues glow. Others go muddy.</h2>
<p class="nn-03__sub">Identical blur, identical alpha, identical ground. Only the hue changes — and that is enough to break the effect.</p>
</header>
<section class="nn-03__row" aria-labelledby="nn-03-naive">
<h3 class="nn-03__rtitle" id="nn-03-naive">
<span class="nn-03__tag nn-03__tag--bad">Naive</span>
hsl(h 100% 50%) at a fixed alpha
</h3>
<ul class="nn-03__grid">
<li class="nn-03__item" style="--nn-03-h: 195;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Cyan</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 150;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Spring</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 95;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Lime</p><p class="nn-03__verdict nn-03__verdict--ok">too hot, blows out</p></li>
<li class="nn-03__item" style="--nn-03-h: 48;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Amber</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 22;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Warm red</p><p class="nn-03__verdict nn-03__verdict--warn">dull, needs lift</p></li>
<li class="nn-03__item" style="--nn-03-h: 322;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Magenta</p><p class="nn-03__verdict nn-03__verdict--ok">reads as light</p></li>
<li class="nn-03__item" style="--nn-03-h: 272;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Violet</p><p class="nn-03__verdict nn-03__verdict--warn">dim halo</p></li>
<li class="nn-03__item" style="--nn-03-h: 245;"><span class="nn-03__swatch nn-03__swatch--naive"></span><p class="nn-03__name">Deep blue</p><p class="nn-03__verdict nn-03__verdict--bad">muddy — no headroom</p></li>
</ul>
</section>
<section class="nn-03__row" aria-labelledby="nn-03-fixed">
<h3 class="nn-03__rtitle" id="nn-03-fixed">
<span class="nn-03__tag nn-03__tag--good">Corrected</span>
oklch(85% 0.16 h) — lightness held constant
</h3>
<ul class="nn-03__grid">
<li class="nn-03__item" style="--nn-03-h: 195;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Cyan</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 150;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Spring</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 95;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Lime</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 48;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Amber</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 22;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Warm red</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 322;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Magenta</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 272;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Violet</p><p class="nn-03__verdict nn-03__verdict--ok">matched</p></li>
<li class="nn-03__item" style="--nn-03-h: 245;"><span class="nn-03__swatch nn-03__swatch--fixed"></span><p class="nn-03__name">Deep blue</p><p class="nn-03__verdict nn-03__verdict--ok">chroma clamped, still lit</p></li>
</ul>
</section>
<p class="nn-03__rule">When a hue looks weak, lift its lightness — not the shadow alpha. Alpha flattens the falloff and turns a glow back into a smudge.</p>
</div>
</section>
```
## CSS
```css
.nn-03 {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
}
@media (prefers-color-scheme: light) {
.nn-03 {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
}
}
@media (prefers-color-scheme: dark) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
}
.nn-03[data-theme="light"] {
--nn-03-bad: #a12a20;
--nn-03-warn: #78490a;
}
.nn-03 {
--nn-03-spec-ink: #f5efe9;
--nn-03-spec-dim: #a2968d;
}
.nn-03__theme {
position: absolute;
inset-block-start: clamp(10px, 2vw, 18px);
inset-inline-end: clamp(10px, 2vw, 18px);
z-index: 5;
display: flex;
justify-content: flex-end;
}
.nn-03__tin {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-03__tlab {
display: inline-flex;
align-items: center;
gap: .4rem;
min-height: 44px;
padding: 0 .8rem;
border: 1px solid currentColor;
font-size: .7rem;
letter-spacing: .12em;
text-transform: uppercase;
cursor: pointer;
opacity: .7;
}
.nn-03__tlab svg {
width: .9rem;
height: .9rem;
flex: none;
}
.nn-03__tlab:hover {
opacity: 1;
}
.nn-03__tin:focus-visible + .nn-03__tlab {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
}
}
@media (prefers-color-scheme: dark) {
.nn-03:has(#nn-03-theme:checked) {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
}
.nn-03 {
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
--nn-03-font-display: "Helvetica Neue", "Inter Tight", system-ui, sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: block;
box-sizing: border-box;
background: var(--nn-03-bg);
color: var(--nn-03-ink);
font-family: system-ui, sans-serif;
}
.nn-03 *,
.nn-03 *::before,
.nn-03 *::after {
box-sizing: border-box;
}
@supports (color: oklch(50% 0.1 200)) {
.nn-03 {
--nn-03-bg: oklch(17% 0.008 45);
--nn-03-well: oklch(21% 0.01 45);
--nn-03-line: oklch(31% 0.012 45);
--nn-03-ink: oklch(96% 0.012 60);
--nn-03-muted: oklch(73% 0.014 55);
--nn-03-accent: oklch(91% 0.03 70);
}
}
@media (prefers-color-scheme: light) {
.nn-03 {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
}
.nn-03[data-theme="light"] {
--nn-03-bg: #edeae6;
--nn-03-well: #26211f;
--nn-03-line: #cbc4bd;
--nn-03-ink: #191614;
--nn-03-muted: #5a514b;
--nn-03-accent: #3d3530;
}
.nn-03[data-theme="dark"] {
--nn-03-bad: #f4a4a4;
--nn-03-warn: #f0c88a;
--nn-03-bg: #131110;
--nn-03-well: #1a1716;
--nn-03-line: #322c29;
--nn-03-ink: #f5efe9;
--nn-03-muted: #a2968d;
--nn-03-accent: #f0dcc8;
}
.nn-03__stage {
max-width: 72rem;
margin: 0 auto;
padding: clamp(1rem, 3.5vw, 1.5rem) clamp(.75rem, 3.5vw, 1.5rem) clamp(1.5rem, 4vw, 2rem);
display: grid;
gap: clamp(1rem, 3vw, 1.5rem);
}
.nn-03__kicker {
margin: 0 0 .35rem;
font-family: var(--nn-03-font-display);
font-size: .66rem;
letter-spacing: .28em;
text-transform: uppercase;
color: var(--nn-03-muted);
}
.nn-03__heading {
margin: 0;
font-family: var(--nn-03-font-display);
font-size: clamp(1.3rem, 4.5vw, 1.95rem);
font-weight: 600;
letter-spacing: .06em;
text-transform: uppercase;
text-wrap: balance;
}
.nn-03__sub {
margin: .45rem 0 0;
max-width: 40rem;
font-size: .85rem;
line-height: 1.55;
color: var(--nn-03-muted);
text-wrap: pretty;
}
.nn-03__row {
display: grid;
gap: .6rem;
padding: .75rem;
border: 1px solid var(--nn-03-line);
background: var(--nn-03-well);
}
.nn-03__rtitle {
margin: 0;
display: flex;
align-items: center;
gap: .5rem;
flex-wrap: wrap;
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
font-weight: 400;
color: var(--nn-03-spec-dim);
min-inline-size: 0;
}
.nn-03__tag {
padding: .15rem .4rem;
border: 1px solid var(--nn-03-line);
font-family: var(--nn-03-font-display);
font-size: .6rem;
letter-spacing: .16em;
text-transform: uppercase;
}
.nn-03__tag--bad {
color: #ff9f9f;
border-color: #4b2a2a;
}
.nn-03__tag--good {
color: #a8f0c0;
border-color: #234634;
}
.nn-03__grid {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: .5rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 26rem) {
.nn-03__grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
@media (min-width: 60rem) {
.nn-03__grid {
grid-template-columns: repeat(8, minmax(0, 1fr));
}
}
.nn-03__item {
display: grid;
gap: .3rem;
justify-items: center;
padding: .75rem .25rem .5rem;
min-inline-size: 0;
}
.nn-03__swatch {
width: 100%;
max-width: 3.5rem;
aspect-ratio: 1;
border-radius: 2px;
}
.nn-03__swatch--naive {
background: hsl(var(--nn-03-h) 100% 50%);
box-shadow: 0 0 .3rem hsl(var(--nn-03-h) 100% 50% / .9), 0 0 clamp(.75rem, 3vw, 1.4rem) hsl(var(--nn-03-h) 100% 50% / .45);
}
.nn-03__swatch--fixed {
background: #cccccc;
box-shadow: 0 0 .3rem rgba(204, 204, 204, .9), 0 0 clamp(.75rem, 3vw, 1.4rem) rgba(204, 204, 204, .45);
}
@supports (color: oklch(50% 0.1 200)) {
.nn-03__swatch--fixed {
background: oklch(85% 0.16 var(--nn-03-h));
}
}
@supports (color: color-mix(in oklab, red, blue)) {
.nn-03__swatch--fixed {
box-shadow: 0 0 .3rem color-mix(in oklab, oklch(85% 0.16 var(--nn-03-h)) 90%, transparent), 0 0 clamp(.75rem, 3vw, 1.4rem) color-mix(in oklab, oklch(85% 0.16 var(--nn-03-h)) 45%, transparent);
}
}
.nn-03__name {
margin: .2rem 0 0;
font-size: .72rem;
font-weight: 600;
text-align: center;
color: var(--nn-03-spec-ink);
}
.nn-03__verdict {
margin: 0;
font-family: ui-monospace, Menlo, monospace;
font-size: .6rem;
line-height: 1.35;
text-align: center;
color: var(--nn-03-spec-dim);
}
.nn-03__verdict--warn {
color: #f0c88a;
}
.nn-03__verdict--bad {
color: #ff9f9f;
}
.nn-03__rule {
margin: 0;
padding: .9rem 1rem;
border: 1px solid var(--nn-03-line);
border-left: 2px solid var(--nn-03-accent);
font-size: .84rem;
line-height: 1.6;
color: var(--nn-03-ink);
text-wrap: pretty;
}
```How this works
Equal lightness in hsl() is not equal brightness to the eye. hsl(60 100% 50%) and hsl(240 100% 50%) claim the same 50% lightness, but the yellow is roughly six times more luminous than the blue in the eye's terms. A glow recipe is tuned against the ground it sits on, so when a palette is recoloured to a low-luminance hue the same blur and alpha produce a dim, muddy bruise instead of light.
Blue and brown fail for different reasons. Deep blue fails on luminance: there is almost no headroom between it and a near-black ground, so the halo cannot climb. Brown and olive fail on chroma: they are low-saturation dark oranges, and a glow at low chroma reads as a dirty smear because the eye has no hue signal to attribute the brightness to. Both look like a broken shadow, not light.
oklch() fixes it because its lightness axis is perceptual. Setting oklch(85% 0.16 h) and varying only h gives eight hues that genuinely match in brightness, so one blur and alpha works across all of them. Chroma has to be clamped per hue — some hues simply cannot reach 0.2 chroma inside sRGB — but the failure mode is a slightly duller swatch, not a muddy one.
The practical rule: raise lightness, do not raise alpha. When a hue looks weak the instinct is to push the shadow alpha up, which flattens the falloff and turns the stack into a smudge. The fix is to lift the layer colour's lightness in oklch and leave the alphas alone. That keeps the falloff curve from demo 01 intact while giving the halo something bright enough to climb into.
Make it yours
- Edit any swatch's
--nn-03-hto slot your own brand hue into the row and see where it lands. - Change the corrected row's lightness from
85%to78%to watch every hue dim together, evenly. - Raise the chroma to
0.22— hues that cannot reach it inside sRGB clamp visibly, which is the useful signal. - Swap
color-mix(in oklab, …)forin srgbto see the halo layer shift hue as it fades. - Add a ninth swatch: nothing but a hue number is needed, both rows derive everything else.
- Set the naive row's alpha to
0.9to prove that more alpha does not rescue a low-luminance hue.
Gotchas — read before shipping
- A palette recoloured to a deep blue or a brown at the same shadow values goes muddy, and the fix is lightness, not alpha.
- Fixed
hsl()lightness across a palette guarantees uneven glow strength, because hsl lightness is not perceptual. - Pushing alpha to rescue a weak hue destroys the falloff curve and gives you a coloured smudge with a bright middle.
- Some hues cannot hold high chroma in sRGB, so an ambitious
oklch()chroma silently clamps and one swatch reads flatter than its neighbours.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by :has(), oklch(), color-mix(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
color-mix(in oklab) sets the floor — it derives the halo and bloom alphas from one hue token, which is what makes the corrected row a single-value edit per swatch. Older engines get the hand-written rgba() pair declared first, so the naive and corrected rows still render and still differ; you edit two colours per swatch instead of one. The script-free theme toggle uses :has() (Chrome 105 / Safari 15.4 / Firefox 121) after the @media rules, so without it the demo follows the system colour scheme and the control does nothing.