16 / 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
Cyberpunk Neon Palette and Token Set
A token system where one hue value drives the core, halo and bloom layers across a small component set, so retinting is a one-value edit rather than a search-and-replace. Three hue presets switch the entire set with no JavaScript, using :has() on radio inputs, and the token table shows exactly what is derived from what.
Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time
Untitled UI · Affiliate link
The code
<section class="nn-16" aria-labelledby="nn-16-heading">
<div class="nn-16__stage">
<div class="nn-16__theme">
<input class="nn-16__tin" type="checkbox" id="nn-16-theme">
<label class="nn-16__tlab" for="nn-16-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-16__head">
<p class="nn-16__kicker">One token, four components</p>
<h2 class="nn-16__heading" id="nn-16-heading">Retinting a neon set should be one edit</h2>
<p class="nn-16__sub">Core, halo and bloom are all derived from a single hue value. Switch it below — radio inputs and <span class="nn-16__code">:has()</span>, no script.</p>
</header>
<fieldset class="nn-16__switch">
<legend class="nn-16__legend">Hue preset</legend>
<div class="nn-16__opts">
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h1" checked>
<label class="nn-16__opt" for="nn-16-h1">248 · electric blue</label>
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h2">
<label class="nn-16__opt" for="nn-16-h2">158 · acid green</label>
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h3">
<label class="nn-16__opt" for="nn-16-h3">330 · hot pink</label>
</div>
</fieldset>
<div class="nn-16__set">
<div class="nn-16__card">
<p class="nn-16__badge">Live</p>
<p class="nn-16__ctitle">Talon gateway</p>
<p class="nn-16__cbody">Edge nodes in three regions. Median response 41ms over the last hour.</p>
<div class="nn-16__meter"><span class="nn-16__fill"></span></div>
<button class="nn-16__btn" type="button">Open channel</button>
</div>
<table class="nn-16__tokens">
<caption class="nn-16__caption">Derivation chain</caption>
<thead><tr><th scope="col">Token</th><th scope="col">Derived from</th></tr></thead>
<tbody>
<tr><th scope="row">--nn-16-hue</th><td>the one value you edit</td></tr>
<tr><th scope="row">--nn-16-base</th><td>oklch(78% .19 hue)</td></tr>
<tr><th scope="row">--nn-16-core</th><td>mix(base 85%, transparent)</td></tr>
<tr><th scope="row">--nn-16-halo</th><td>mix(base 45%, transparent)</td></tr>
<tr><th scope="row">--nn-16-bloom</th><td>mix(base 24%, transparent)</td></tr>
<tr><th scope="row">--nn-16-edge</th><td>mix(base 55%, line)</td></tr>
</tbody>
</table>
</div>
</div>
</section><section class="nn-16" aria-labelledby="nn-16-heading">
<div class="nn-16__stage">
<div class="nn-16__theme">
<input class="nn-16__tin" type="checkbox" id="nn-16-theme">
<label class="nn-16__tlab" for="nn-16-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-16__head">
<p class="nn-16__kicker">One token, four components</p>
<h2 class="nn-16__heading" id="nn-16-heading">Retinting a neon set should be one edit</h2>
<p class="nn-16__sub">Core, halo and bloom are all derived from a single hue value. Switch it below — radio inputs and <span class="nn-16__code">:has()</span>, no script.</p>
</header>
<fieldset class="nn-16__switch">
<legend class="nn-16__legend">Hue preset</legend>
<div class="nn-16__opts">
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h1" checked>
<label class="nn-16__opt" for="nn-16-h1">248 · electric blue</label>
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h2">
<label class="nn-16__opt" for="nn-16-h2">158 · acid green</label>
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h3">
<label class="nn-16__opt" for="nn-16-h3">330 · hot pink</label>
</div>
</fieldset>
<div class="nn-16__set">
<div class="nn-16__card">
<p class="nn-16__badge">Live</p>
<p class="nn-16__ctitle">Talon gateway</p>
<p class="nn-16__cbody">Edge nodes in three regions. Median response 41ms over the last hour.</p>
<div class="nn-16__meter"><span class="nn-16__fill"></span></div>
<button class="nn-16__btn" type="button">Open channel</button>
</div>
<table class="nn-16__tokens">
<caption class="nn-16__caption">Derivation chain</caption>
<thead><tr><th scope="col">Token</th><th scope="col">Derived from</th></tr></thead>
<tbody>
<tr><th scope="row">--nn-16-hue</th><td>the one value you edit</td></tr>
<tr><th scope="row">--nn-16-base</th><td>oklch(78% .19 hue)</td></tr>
<tr><th scope="row">--nn-16-core</th><td>mix(base 85%, transparent)</td></tr>
<tr><th scope="row">--nn-16-halo</th><td>mix(base 45%, transparent)</td></tr>
<tr><th scope="row">--nn-16-bloom</th><td>mix(base 24%, transparent)</td></tr>
<tr><th scope="row">--nn-16-edge</th><td>mix(base 55%, line)</td></tr>
</tbody>
</table>
</div>
</div>
</section>.nn-16 {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
}
@media (prefers-color-scheme: light) {
.nn-16 {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
}
}
@media (prefers-color-scheme: dark) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
}
.nn-16[data-theme="light"] {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
.nn-16 {
--nn-16-spec-ink: #f0ecfa;
--nn-16-spec-dim: #a196bd;
}
.nn-16__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-16__tin {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-16__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-16__tlab svg {
width: .9rem;
height: .9rem;
flex: none;
}
.nn-16__tlab:hover {
opacity: 1;
}
.nn-16__tin:focus-visible + .nn-16__tlab {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
}
}
@media (prefers-color-scheme: dark) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
}
.nn-16 {
--nn-16-hue: 248;
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
--nn-16-base: #7b8cff;
--nn-16-core: rgba(123, 140, 255, .85);
--nn-16-halo: rgba(123, 140, 255, .45);
--nn-16-bloom: rgba(123, 140, 255, .24);
--nn-16-edge: #4a4f96;
--nn-16-font-display: system-ui, "Helvetica Neue", Arial, sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: block;
box-sizing: border-box;
background: var(--nn-16-bg);
color: var(--nn-16-ink);
font-family: system-ui, sans-serif;
}
.nn-16 *,
.nn-16 *::before,
.nn-16 *::after {
box-sizing: border-box;
}
@supports (color: oklch(50% 0.1 200)) {
.nn-16 {
--nn-16-bg: oklch(17% 0.035 295);
--nn-16-panel: oklch(21% 0.045 295);
--nn-16-line: oklch(31% 0.05 295);
--nn-16-ink: oklch(96% 0.014 295);
--nn-16-muted: oklch(73% 0.035 295);
--nn-16-base: oklch(78% 0.19 var(--nn-16-hue));
}
}
@supports (color: color-mix(in oklab, red, blue)) {
.nn-16 {
--nn-16-core: color-mix(in oklab, var(--nn-16-base) 85%, transparent);
--nn-16-halo: color-mix(in oklab, var(--nn-16-base) 45%, transparent);
--nn-16-bloom: color-mix(in oklab, var(--nn-16-base) 24%, transparent);
--nn-16-edge: color-mix(in oklab, var(--nn-16-base) 55%, var(--nn-16-line));
}
}
@supports selector(:has(*)) {
.nn-16:has(#nn-16-h2:checked) {
--nn-16-hue: 158;
}
.nn-16:has(#nn-16-h3:checked) {
--nn-16-hue: 330;
}
}
@media (prefers-color-scheme: light) {
.nn-16 {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
}
.nn-16[data-theme="light"] {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
.nn-16[data-theme="dark"] {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
}
.nn-16__stage {
max-width: 66rem;
margin: 0 auto;
padding: clamp(1.25rem, 4vw, 1.75rem) clamp(1rem, 4vw, 1.75rem) clamp(1.75rem, 5vw, 2.25rem);
display: grid;
gap: clamp(1.1rem, 3vw, 1.5rem);
}
.nn-16__kicker {
margin: 0 0 .4rem;
font-size: .72rem;
font-weight: 600;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--nn-16-base);
}
.nn-16__heading {
margin: 0;
font-family: var(--nn-16-font-display);
font-size: clamp(1.3rem, 4.6vw, 1.95rem);
font-weight: 640;
letter-spacing: -.025em;
text-wrap: balance;
}
.nn-16__sub {
margin: .5rem 0 0;
max-width: 40rem;
font-size: .89rem;
line-height: 1.6;
color: var(--nn-16-muted);
text-wrap: pretty;
}
.nn-16__code {
font-family: ui-monospace, Menlo, monospace;
font-size: .9em;
color: var(--nn-16-base);
}
.nn-16__switch {
margin: 0;
padding: .75rem .85rem;
border: 1px solid var(--nn-16-line);
border-radius: .5rem;
background: var(--nn-16-panel);
}
.nn-16__legend {
padding: 0 .35rem;
font-size: .68rem;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--nn-16-muted);
}
.nn-16__opts {
display: flex;
gap: .4rem;
flex-wrap: wrap;
}
.nn-16__radio {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-16__opt {
display: flex;
align-items: center;
min-height: 44px;
padding: 0 .85rem;
border: 1px solid var(--nn-16-line);
border-radius: .35rem;
font-size: .8rem;
color: var(--nn-16-muted);
cursor: pointer;
}
.nn-16__opt:hover {
color: var(--nn-16-ink);
border-color: var(--nn-16-edge);
}
.nn-16__radio:checked + .nn-16__opt {
color: var(--nn-16-ink);
border-color: var(--nn-16-base);
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1rem var(--nn-16-halo);
}
.nn-16__radio:focus-visible + .nn-16__opt {
outline: 2px solid var(--nn-16-ink);
outline-offset: 2px;
}
.nn-16__set {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
align-items: start;
}
@media (min-width: 54rem) {
.nn-16__set {
grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
}
}
.nn-16__card {
display: grid;
gap: .6rem;
justify-items: start;
padding: clamp(1rem, 3.5vw, 1.35rem);
border: 1px solid var(--nn-16-edge);
border-radius: .6rem;
background: var(--nn-16-panel);
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1.1rem var(--nn-16-halo), 0 0 clamp(1.75rem, 8vw, 3rem) var(--nn-16-bloom);
}
.nn-16__badge {
margin: 0;
padding: .2rem .5rem;
border-radius: 999px;
border: 1px solid var(--nn-16-base);
color: var(--nn-16-base);
font-size: .66rem;
font-weight: 650;
letter-spacing: .12em;
text-transform: uppercase;
box-shadow: 0 0 .3rem var(--nn-16-halo);
}
.nn-16__ctitle {
margin: 0;
font-family: var(--nn-16-font-display);
font-size: 1.05rem;
font-weight: 640;
letter-spacing: -.02em;
}
.nn-16__cbody {
margin: 0;
font-size: .84rem;
line-height: 1.55;
color: var(--nn-16-muted);
text-wrap: pretty;
}
.nn-16__meter {
width: 100%;
height: .4rem;
border-radius: 999px;
background: color-mix(in srgb, var(--nn-16-line) 70%, transparent);
overflow: hidden;
}
.nn-16__fill {
display: block;
width: 68%;
height: 100%;
background: var(--nn-16-base);
box-shadow: 0 0 .35rem var(--nn-16-core), 0 0 .9rem var(--nn-16-halo);
}
.nn-16__btn {
min-height: 44px;
padding: 0 1rem;
border: 1px solid var(--nn-16-base);
border-radius: .35rem;
background: color-mix(in srgb, var(--nn-16-base) 14%, transparent);
color: var(--nn-16-ink);
font-family: inherit;
font-size: .84rem;
font-weight: 550;
cursor: pointer;
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1rem var(--nn-16-halo);
}
.nn-16__btn:hover {
background: color-mix(in srgb, var(--nn-16-base) 22%, transparent);
}
.nn-16__btn:focus-visible {
outline: 2px solid var(--nn-16-ink);
outline-offset: 2px;
}
.nn-16__tokens {
width: 100%;
border-collapse: collapse;
font-size: .78rem;
min-inline-size: 0;
}
.nn-16__caption {
padding-bottom: .5rem;
text-align: left;
font-size: .68rem;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--nn-16-muted);
}
.nn-16__tokens th,
.nn-16__tokens td {
padding: .5rem .5rem;
text-align: left;
border-bottom: 1px solid var(--nn-16-line);
}
.nn-16__tokens thead th {
font-size: .66rem;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--nn-16-muted);
font-weight: 400;
}
.nn-16__tokens tbody th {
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
font-weight: 400;
color: var(--nn-16-base);
}
.nn-16__tokens td {
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
color: var(--nn-16-muted);
}.nn-16 {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
}
@media (prefers-color-scheme: light) {
.nn-16 {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
}
}
@media (prefers-color-scheme: dark) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
}
.nn-16[data-theme="light"] {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
.nn-16 {
--nn-16-spec-ink: #f0ecfa;
--nn-16-spec-dim: #a196bd;
}
.nn-16__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-16__tin {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-16__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-16__tlab svg {
width: .9rem;
height: .9rem;
flex: none;
}
.nn-16__tlab:hover {
opacity: 1;
}
.nn-16__tin:focus-visible + .nn-16__tlab {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
}
}
@media (prefers-color-scheme: dark) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
}
.nn-16 {
--nn-16-hue: 248;
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
--nn-16-base: #7b8cff;
--nn-16-core: rgba(123, 140, 255, .85);
--nn-16-halo: rgba(123, 140, 255, .45);
--nn-16-bloom: rgba(123, 140, 255, .24);
--nn-16-edge: #4a4f96;
--nn-16-font-display: system-ui, "Helvetica Neue", Arial, sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: block;
box-sizing: border-box;
background: var(--nn-16-bg);
color: var(--nn-16-ink);
font-family: system-ui, sans-serif;
}
.nn-16 *,
.nn-16 *::before,
.nn-16 *::after {
box-sizing: border-box;
}
@supports (color: oklch(50% 0.1 200)) {
.nn-16 {
--nn-16-bg: oklch(17% 0.035 295);
--nn-16-panel: oklch(21% 0.045 295);
--nn-16-line: oklch(31% 0.05 295);
--nn-16-ink: oklch(96% 0.014 295);
--nn-16-muted: oklch(73% 0.035 295);
--nn-16-base: oklch(78% 0.19 var(--nn-16-hue));
}
}
@supports (color: color-mix(in oklab, red, blue)) {
.nn-16 {
--nn-16-core: color-mix(in oklab, var(--nn-16-base) 85%, transparent);
--nn-16-halo: color-mix(in oklab, var(--nn-16-base) 45%, transparent);
--nn-16-bloom: color-mix(in oklab, var(--nn-16-base) 24%, transparent);
--nn-16-edge: color-mix(in oklab, var(--nn-16-base) 55%, var(--nn-16-line));
}
}
@supports selector(:has(*)) {
.nn-16:has(#nn-16-h2:checked) {
--nn-16-hue: 158;
}
.nn-16:has(#nn-16-h3:checked) {
--nn-16-hue: 330;
}
}
@media (prefers-color-scheme: light) {
.nn-16 {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
}
.nn-16[data-theme="light"] {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
.nn-16[data-theme="dark"] {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
}
.nn-16__stage {
max-width: 66rem;
margin: 0 auto;
padding: clamp(1.25rem, 4vw, 1.75rem) clamp(1rem, 4vw, 1.75rem) clamp(1.75rem, 5vw, 2.25rem);
display: grid;
gap: clamp(1.1rem, 3vw, 1.5rem);
}
.nn-16__kicker {
margin: 0 0 .4rem;
font-size: .72rem;
font-weight: 600;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--nn-16-base);
}
.nn-16__heading {
margin: 0;
font-family: var(--nn-16-font-display);
font-size: clamp(1.3rem, 4.6vw, 1.95rem);
font-weight: 640;
letter-spacing: -.025em;
text-wrap: balance;
}
.nn-16__sub {
margin: .5rem 0 0;
max-width: 40rem;
font-size: .89rem;
line-height: 1.6;
color: var(--nn-16-muted);
text-wrap: pretty;
}
.nn-16__code {
font-family: ui-monospace, Menlo, monospace;
font-size: .9em;
color: var(--nn-16-base);
}
.nn-16__switch {
margin: 0;
padding: .75rem .85rem;
border: 1px solid var(--nn-16-line);
border-radius: .5rem;
background: var(--nn-16-panel);
}
.nn-16__legend {
padding: 0 .35rem;
font-size: .68rem;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--nn-16-muted);
}
.nn-16__opts {
display: flex;
gap: .4rem;
flex-wrap: wrap;
}
.nn-16__radio {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-16__opt {
display: flex;
align-items: center;
min-height: 44px;
padding: 0 .85rem;
border: 1px solid var(--nn-16-line);
border-radius: .35rem;
font-size: .8rem;
color: var(--nn-16-muted);
cursor: pointer;
}
.nn-16__opt:hover {
color: var(--nn-16-ink);
border-color: var(--nn-16-edge);
}
.nn-16__radio:checked + .nn-16__opt {
color: var(--nn-16-ink);
border-color: var(--nn-16-base);
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1rem var(--nn-16-halo);
}
.nn-16__radio:focus-visible + .nn-16__opt {
outline: 2px solid var(--nn-16-ink);
outline-offset: 2px;
}
.nn-16__set {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
align-items: start;
}
@media (min-width: 54rem) {
.nn-16__set {
grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
}
}
.nn-16__card {
display: grid;
gap: .6rem;
justify-items: start;
padding: clamp(1rem, 3.5vw, 1.35rem);
border: 1px solid var(--nn-16-edge);
border-radius: .6rem;
background: var(--nn-16-panel);
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1.1rem var(--nn-16-halo), 0 0 clamp(1.75rem, 8vw, 3rem) var(--nn-16-bloom);
}
.nn-16__badge {
margin: 0;
padding: .2rem .5rem;
border-radius: 999px;
border: 1px solid var(--nn-16-base);
color: var(--nn-16-base);
font-size: .66rem;
font-weight: 650;
letter-spacing: .12em;
text-transform: uppercase;
box-shadow: 0 0 .3rem var(--nn-16-halo);
}
.nn-16__ctitle {
margin: 0;
font-family: var(--nn-16-font-display);
font-size: 1.05rem;
font-weight: 640;
letter-spacing: -.02em;
}
.nn-16__cbody {
margin: 0;
font-size: .84rem;
line-height: 1.55;
color: var(--nn-16-muted);
text-wrap: pretty;
}
.nn-16__meter {
width: 100%;
height: .4rem;
border-radius: 999px;
background: color-mix(in srgb, var(--nn-16-line) 70%, transparent);
overflow: hidden;
}
.nn-16__fill {
display: block;
width: 68%;
height: 100%;
background: var(--nn-16-base);
box-shadow: 0 0 .35rem var(--nn-16-core), 0 0 .9rem var(--nn-16-halo);
}
.nn-16__btn {
min-height: 44px;
padding: 0 1rem;
border: 1px solid var(--nn-16-base);
border-radius: .35rem;
background: color-mix(in srgb, var(--nn-16-base) 14%, transparent);
color: var(--nn-16-ink);
font-family: inherit;
font-size: .84rem;
font-weight: 550;
cursor: pointer;
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1rem var(--nn-16-halo);
}
.nn-16__btn:hover {
background: color-mix(in srgb, var(--nn-16-base) 22%, transparent);
}
.nn-16__btn:focus-visible {
outline: 2px solid var(--nn-16-ink);
outline-offset: 2px;
}
.nn-16__tokens {
width: 100%;
border-collapse: collapse;
font-size: .78rem;
min-inline-size: 0;
}
.nn-16__caption {
padding-bottom: .5rem;
text-align: left;
font-size: .68rem;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--nn-16-muted);
}
.nn-16__tokens th,
.nn-16__tokens td {
padding: .5rem .5rem;
text-align: left;
border-bottom: 1px solid var(--nn-16-line);
}
.nn-16__tokens thead th {
font-size: .66rem;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--nn-16-muted);
font-weight: 400;
}
.nn-16__tokens tbody th {
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
font-weight: 400;
color: var(--nn-16-base);
}
.nn-16__tokens td {
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
color: var(--nn-16-muted);
}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: Cyberpunk Neon Palette and Token Set
Source: https://codefronts.com/design-styles/css-neon/neon-cyberpunk-palette/
A token system where one hue value drives the core, halo and bloom layers across a small component set, so retinting is a one-value edit rather than a search-and-replace. Three hue presets switch the entire set with no JavaScript, using :has() on radio inputs, and the token table shows exactly what is derived from what.
## HTML
```html
<section class="nn-16" aria-labelledby="nn-16-heading">
<div class="nn-16__stage">
<div class="nn-16__theme">
<input class="nn-16__tin" type="checkbox" id="nn-16-theme">
<label class="nn-16__tlab" for="nn-16-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-16__head">
<p class="nn-16__kicker">One token, four components</p>
<h2 class="nn-16__heading" id="nn-16-heading">Retinting a neon set should be one edit</h2>
<p class="nn-16__sub">Core, halo and bloom are all derived from a single hue value. Switch it below — radio inputs and <span class="nn-16__code">:has()</span>, no script.</p>
</header>
<fieldset class="nn-16__switch">
<legend class="nn-16__legend">Hue preset</legend>
<div class="nn-16__opts">
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h1" checked>
<label class="nn-16__opt" for="nn-16-h1">248 · electric blue</label>
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h2">
<label class="nn-16__opt" for="nn-16-h2">158 · acid green</label>
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h3">
<label class="nn-16__opt" for="nn-16-h3">330 · hot pink</label>
</div>
</fieldset>
<div class="nn-16__set">
<div class="nn-16__card">
<p class="nn-16__badge">Live</p>
<p class="nn-16__ctitle">Talon gateway</p>
<p class="nn-16__cbody">Edge nodes in three regions. Median response 41ms over the last hour.</p>
<div class="nn-16__meter"><span class="nn-16__fill"></span></div>
<button class="nn-16__btn" type="button">Open channel</button>
</div>
<table class="nn-16__tokens">
<caption class="nn-16__caption">Derivation chain</caption>
<thead><tr><th scope="col">Token</th><th scope="col">Derived from</th></tr></thead>
<tbody>
<tr><th scope="row">--nn-16-hue</th><td>the one value you edit</td></tr>
<tr><th scope="row">--nn-16-base</th><td>oklch(78% .19 hue)</td></tr>
<tr><th scope="row">--nn-16-core</th><td>mix(base 85%, transparent)</td></tr>
<tr><th scope="row">--nn-16-halo</th><td>mix(base 45%, transparent)</td></tr>
<tr><th scope="row">--nn-16-bloom</th><td>mix(base 24%, transparent)</td></tr>
<tr><th scope="row">--nn-16-edge</th><td>mix(base 55%, line)</td></tr>
</tbody>
</table>
</div>
</div>
</section>
```
## CSS
```css
.nn-16 {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
}
@media (prefers-color-scheme: light) {
.nn-16 {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
}
}
@media (prefers-color-scheme: dark) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
}
.nn-16[data-theme="light"] {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
.nn-16 {
--nn-16-spec-ink: #f0ecfa;
--nn-16-spec-dim: #a196bd;
}
.nn-16__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-16__tin {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-16__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-16__tlab svg {
width: .9rem;
height: .9rem;
flex: none;
}
.nn-16__tlab:hover {
opacity: 1;
}
.nn-16__tin:focus-visible + .nn-16__tlab {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
}
}
@media (prefers-color-scheme: dark) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
}
.nn-16 {
--nn-16-hue: 248;
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
--nn-16-base: #7b8cff;
--nn-16-core: rgba(123, 140, 255, .85);
--nn-16-halo: rgba(123, 140, 255, .45);
--nn-16-bloom: rgba(123, 140, 255, .24);
--nn-16-edge: #4a4f96;
--nn-16-font-display: system-ui, "Helvetica Neue", Arial, sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: block;
box-sizing: border-box;
background: var(--nn-16-bg);
color: var(--nn-16-ink);
font-family: system-ui, sans-serif;
}
.nn-16 *,
.nn-16 *::before,
.nn-16 *::after {
box-sizing: border-box;
}
@supports (color: oklch(50% 0.1 200)) {
.nn-16 {
--nn-16-bg: oklch(17% 0.035 295);
--nn-16-panel: oklch(21% 0.045 295);
--nn-16-line: oklch(31% 0.05 295);
--nn-16-ink: oklch(96% 0.014 295);
--nn-16-muted: oklch(73% 0.035 295);
--nn-16-base: oklch(78% 0.19 var(--nn-16-hue));
}
}
@supports (color: color-mix(in oklab, red, blue)) {
.nn-16 {
--nn-16-core: color-mix(in oklab, var(--nn-16-base) 85%, transparent);
--nn-16-halo: color-mix(in oklab, var(--nn-16-base) 45%, transparent);
--nn-16-bloom: color-mix(in oklab, var(--nn-16-base) 24%, transparent);
--nn-16-edge: color-mix(in oklab, var(--nn-16-base) 55%, var(--nn-16-line));
}
}
@supports selector(:has(*)) {
.nn-16:has(#nn-16-h2:checked) {
--nn-16-hue: 158;
}
.nn-16:has(#nn-16-h3:checked) {
--nn-16-hue: 330;
}
}
@media (prefers-color-scheme: light) {
.nn-16 {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
}
.nn-16[data-theme="light"] {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
.nn-16[data-theme="dark"] {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
}
.nn-16__stage {
max-width: 66rem;
margin: 0 auto;
padding: clamp(1.25rem, 4vw, 1.75rem) clamp(1rem, 4vw, 1.75rem) clamp(1.75rem, 5vw, 2.25rem);
display: grid;
gap: clamp(1.1rem, 3vw, 1.5rem);
}
.nn-16__kicker {
margin: 0 0 .4rem;
font-size: .72rem;
font-weight: 600;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--nn-16-base);
}
.nn-16__heading {
margin: 0;
font-family: var(--nn-16-font-display);
font-size: clamp(1.3rem, 4.6vw, 1.95rem);
font-weight: 640;
letter-spacing: -.025em;
text-wrap: balance;
}
.nn-16__sub {
margin: .5rem 0 0;
max-width: 40rem;
font-size: .89rem;
line-height: 1.6;
color: var(--nn-16-muted);
text-wrap: pretty;
}
.nn-16__code {
font-family: ui-monospace, Menlo, monospace;
font-size: .9em;
color: var(--nn-16-base);
}
.nn-16__switch {
margin: 0;
padding: .75rem .85rem;
border: 1px solid var(--nn-16-line);
border-radius: .5rem;
background: var(--nn-16-panel);
}
.nn-16__legend {
padding: 0 .35rem;
font-size: .68rem;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--nn-16-muted);
}
.nn-16__opts {
display: flex;
gap: .4rem;
flex-wrap: wrap;
}
.nn-16__radio {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-16__opt {
display: flex;
align-items: center;
min-height: 44px;
padding: 0 .85rem;
border: 1px solid var(--nn-16-line);
border-radius: .35rem;
font-size: .8rem;
color: var(--nn-16-muted);
cursor: pointer;
}
.nn-16__opt:hover {
color: var(--nn-16-ink);
border-color: var(--nn-16-edge);
}
.nn-16__radio:checked + .nn-16__opt {
color: var(--nn-16-ink);
border-color: var(--nn-16-base);
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1rem var(--nn-16-halo);
}
.nn-16__radio:focus-visible + .nn-16__opt {
outline: 2px solid var(--nn-16-ink);
outline-offset: 2px;
}
.nn-16__set {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
align-items: start;
}
@media (min-width: 54rem) {
.nn-16__set {
grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
}
}
.nn-16__card {
display: grid;
gap: .6rem;
justify-items: start;
padding: clamp(1rem, 3.5vw, 1.35rem);
border: 1px solid var(--nn-16-edge);
border-radius: .6rem;
background: var(--nn-16-panel);
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1.1rem var(--nn-16-halo), 0 0 clamp(1.75rem, 8vw, 3rem) var(--nn-16-bloom);
}
.nn-16__badge {
margin: 0;
padding: .2rem .5rem;
border-radius: 999px;
border: 1px solid var(--nn-16-base);
color: var(--nn-16-base);
font-size: .66rem;
font-weight: 650;
letter-spacing: .12em;
text-transform: uppercase;
box-shadow: 0 0 .3rem var(--nn-16-halo);
}
.nn-16__ctitle {
margin: 0;
font-family: var(--nn-16-font-display);
font-size: 1.05rem;
font-weight: 640;
letter-spacing: -.02em;
}
.nn-16__cbody {
margin: 0;
font-size: .84rem;
line-height: 1.55;
color: var(--nn-16-muted);
text-wrap: pretty;
}
.nn-16__meter {
width: 100%;
height: .4rem;
border-radius: 999px;
background: color-mix(in srgb, var(--nn-16-line) 70%, transparent);
overflow: hidden;
}
.nn-16__fill {
display: block;
width: 68%;
height: 100%;
background: var(--nn-16-base);
box-shadow: 0 0 .35rem var(--nn-16-core), 0 0 .9rem var(--nn-16-halo);
}
.nn-16__btn {
min-height: 44px;
padding: 0 1rem;
border: 1px solid var(--nn-16-base);
border-radius: .35rem;
background: color-mix(in srgb, var(--nn-16-base) 14%, transparent);
color: var(--nn-16-ink);
font-family: inherit;
font-size: .84rem;
font-weight: 550;
cursor: pointer;
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1rem var(--nn-16-halo);
}
.nn-16__btn:hover {
background: color-mix(in srgb, var(--nn-16-base) 22%, transparent);
}
.nn-16__btn:focus-visible {
outline: 2px solid var(--nn-16-ink);
outline-offset: 2px;
}
.nn-16__tokens {
width: 100%;
border-collapse: collapse;
font-size: .78rem;
min-inline-size: 0;
}
.nn-16__caption {
padding-bottom: .5rem;
text-align: left;
font-size: .68rem;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--nn-16-muted);
}
.nn-16__tokens th,
.nn-16__tokens td {
padding: .5rem .5rem;
text-align: left;
border-bottom: 1px solid var(--nn-16-line);
}
.nn-16__tokens thead th {
font-size: .66rem;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--nn-16-muted);
font-weight: 400;
}
.nn-16__tokens tbody th {
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
font-weight: 400;
color: var(--nn-16-base);
}
.nn-16__tokens td {
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
color: var(--nn-16-muted);
}
```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: Cyberpunk Neon Palette and Token Set
Source: https://codefronts.com/design-styles/css-neon/neon-cyberpunk-palette/
A token system where one hue value drives the core, halo and bloom layers across a small component set, so retinting is a one-value edit rather than a search-and-replace. Three hue presets switch the entire set with no JavaScript, using :has() on radio inputs, and the token table shows exactly what is derived from what.
## HTML
```html
<section class="nn-16" aria-labelledby="nn-16-heading">
<div class="nn-16__stage">
<div class="nn-16__theme">
<input class="nn-16__tin" type="checkbox" id="nn-16-theme">
<label class="nn-16__tlab" for="nn-16-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-16__head">
<p class="nn-16__kicker">One token, four components</p>
<h2 class="nn-16__heading" id="nn-16-heading">Retinting a neon set should be one edit</h2>
<p class="nn-16__sub">Core, halo and bloom are all derived from a single hue value. Switch it below — radio inputs and <span class="nn-16__code">:has()</span>, no script.</p>
</header>
<fieldset class="nn-16__switch">
<legend class="nn-16__legend">Hue preset</legend>
<div class="nn-16__opts">
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h1" checked>
<label class="nn-16__opt" for="nn-16-h1">248 · electric blue</label>
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h2">
<label class="nn-16__opt" for="nn-16-h2">158 · acid green</label>
<input class="nn-16__radio" type="radio" name="nn-16-hue" id="nn-16-h3">
<label class="nn-16__opt" for="nn-16-h3">330 · hot pink</label>
</div>
</fieldset>
<div class="nn-16__set">
<div class="nn-16__card">
<p class="nn-16__badge">Live</p>
<p class="nn-16__ctitle">Talon gateway</p>
<p class="nn-16__cbody">Edge nodes in three regions. Median response 41ms over the last hour.</p>
<div class="nn-16__meter"><span class="nn-16__fill"></span></div>
<button class="nn-16__btn" type="button">Open channel</button>
</div>
<table class="nn-16__tokens">
<caption class="nn-16__caption">Derivation chain</caption>
<thead><tr><th scope="col">Token</th><th scope="col">Derived from</th></tr></thead>
<tbody>
<tr><th scope="row">--nn-16-hue</th><td>the one value you edit</td></tr>
<tr><th scope="row">--nn-16-base</th><td>oklch(78% .19 hue)</td></tr>
<tr><th scope="row">--nn-16-core</th><td>mix(base 85%, transparent)</td></tr>
<tr><th scope="row">--nn-16-halo</th><td>mix(base 45%, transparent)</td></tr>
<tr><th scope="row">--nn-16-bloom</th><td>mix(base 24%, transparent)</td></tr>
<tr><th scope="row">--nn-16-edge</th><td>mix(base 55%, line)</td></tr>
</tbody>
</table>
</div>
</div>
</section>
```
## CSS
```css
.nn-16 {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
}
@media (prefers-color-scheme: light) {
.nn-16 {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
}
}
@media (prefers-color-scheme: dark) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
}
.nn-16[data-theme="light"] {
--nn-16-bad: #a12a20;
--nn-16-warn: #78490a;
}
.nn-16 {
--nn-16-spec-ink: #f0ecfa;
--nn-16-spec-dim: #a196bd;
}
.nn-16__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-16__tin {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-16__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-16__tlab svg {
width: .9rem;
height: .9rem;
flex: none;
}
.nn-16__tlab:hover {
opacity: 1;
}
.nn-16__tin:focus-visible + .nn-16__tlab {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (prefers-color-scheme: light) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
}
}
@media (prefers-color-scheme: dark) {
.nn-16:has(#nn-16-theme:checked) {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
}
.nn-16 {
--nn-16-hue: 248;
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
--nn-16-base: #7b8cff;
--nn-16-core: rgba(123, 140, 255, .85);
--nn-16-halo: rgba(123, 140, 255, .45);
--nn-16-bloom: rgba(123, 140, 255, .24);
--nn-16-edge: #4a4f96;
--nn-16-font-display: system-ui, "Helvetica Neue", Arial, sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: block;
box-sizing: border-box;
background: var(--nn-16-bg);
color: var(--nn-16-ink);
font-family: system-ui, sans-serif;
}
.nn-16 *,
.nn-16 *::before,
.nn-16 *::after {
box-sizing: border-box;
}
@supports (color: oklch(50% 0.1 200)) {
.nn-16 {
--nn-16-bg: oklch(17% 0.035 295);
--nn-16-panel: oklch(21% 0.045 295);
--nn-16-line: oklch(31% 0.05 295);
--nn-16-ink: oklch(96% 0.014 295);
--nn-16-muted: oklch(73% 0.035 295);
--nn-16-base: oklch(78% 0.19 var(--nn-16-hue));
}
}
@supports (color: color-mix(in oklab, red, blue)) {
.nn-16 {
--nn-16-core: color-mix(in oklab, var(--nn-16-base) 85%, transparent);
--nn-16-halo: color-mix(in oklab, var(--nn-16-base) 45%, transparent);
--nn-16-bloom: color-mix(in oklab, var(--nn-16-base) 24%, transparent);
--nn-16-edge: color-mix(in oklab, var(--nn-16-base) 55%, var(--nn-16-line));
}
}
@supports selector(:has(*)) {
.nn-16:has(#nn-16-h2:checked) {
--nn-16-hue: 158;
}
.nn-16:has(#nn-16-h3:checked) {
--nn-16-hue: 330;
}
}
@media (prefers-color-scheme: light) {
.nn-16 {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
}
.nn-16[data-theme="light"] {
--nn-16-bg: #edeaf3;
--nn-16-panel: #ffffff;
--nn-16-line: #ccc5da;
--nn-16-ink: #150f22;
--nn-16-muted: #544a68;
}
.nn-16[data-theme="dark"] {
--nn-16-bad: #f4a4a4;
--nn-16-warn: #f0c88a;
--nn-16-bg: #120e1c;
--nn-16-panel: #191327;
--nn-16-line: #2c2342;
--nn-16-ink: #f0ecfa;
--nn-16-muted: #a196bd;
}
.nn-16__stage {
max-width: 66rem;
margin: 0 auto;
padding: clamp(1.25rem, 4vw, 1.75rem) clamp(1rem, 4vw, 1.75rem) clamp(1.75rem, 5vw, 2.25rem);
display: grid;
gap: clamp(1.1rem, 3vw, 1.5rem);
}
.nn-16__kicker {
margin: 0 0 .4rem;
font-size: .72rem;
font-weight: 600;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--nn-16-base);
}
.nn-16__heading {
margin: 0;
font-family: var(--nn-16-font-display);
font-size: clamp(1.3rem, 4.6vw, 1.95rem);
font-weight: 640;
letter-spacing: -.025em;
text-wrap: balance;
}
.nn-16__sub {
margin: .5rem 0 0;
max-width: 40rem;
font-size: .89rem;
line-height: 1.6;
color: var(--nn-16-muted);
text-wrap: pretty;
}
.nn-16__code {
font-family: ui-monospace, Menlo, monospace;
font-size: .9em;
color: var(--nn-16-base);
}
.nn-16__switch {
margin: 0;
padding: .75rem .85rem;
border: 1px solid var(--nn-16-line);
border-radius: .5rem;
background: var(--nn-16-panel);
}
.nn-16__legend {
padding: 0 .35rem;
font-size: .68rem;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--nn-16-muted);
}
.nn-16__opts {
display: flex;
gap: .4rem;
flex-wrap: wrap;
}
.nn-16__radio {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
}
.nn-16__opt {
display: flex;
align-items: center;
min-height: 44px;
padding: 0 .85rem;
border: 1px solid var(--nn-16-line);
border-radius: .35rem;
font-size: .8rem;
color: var(--nn-16-muted);
cursor: pointer;
}
.nn-16__opt:hover {
color: var(--nn-16-ink);
border-color: var(--nn-16-edge);
}
.nn-16__radio:checked + .nn-16__opt {
color: var(--nn-16-ink);
border-color: var(--nn-16-base);
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1rem var(--nn-16-halo);
}
.nn-16__radio:focus-visible + .nn-16__opt {
outline: 2px solid var(--nn-16-ink);
outline-offset: 2px;
}
.nn-16__set {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
align-items: start;
}
@media (min-width: 54rem) {
.nn-16__set {
grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
}
}
.nn-16__card {
display: grid;
gap: .6rem;
justify-items: start;
padding: clamp(1rem, 3.5vw, 1.35rem);
border: 1px solid var(--nn-16-edge);
border-radius: .6rem;
background: var(--nn-16-panel);
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1.1rem var(--nn-16-halo), 0 0 clamp(1.75rem, 8vw, 3rem) var(--nn-16-bloom);
}
.nn-16__badge {
margin: 0;
padding: .2rem .5rem;
border-radius: 999px;
border: 1px solid var(--nn-16-base);
color: var(--nn-16-base);
font-size: .66rem;
font-weight: 650;
letter-spacing: .12em;
text-transform: uppercase;
box-shadow: 0 0 .3rem var(--nn-16-halo);
}
.nn-16__ctitle {
margin: 0;
font-family: var(--nn-16-font-display);
font-size: 1.05rem;
font-weight: 640;
letter-spacing: -.02em;
}
.nn-16__cbody {
margin: 0;
font-size: .84rem;
line-height: 1.55;
color: var(--nn-16-muted);
text-wrap: pretty;
}
.nn-16__meter {
width: 100%;
height: .4rem;
border-radius: 999px;
background: color-mix(in srgb, var(--nn-16-line) 70%, transparent);
overflow: hidden;
}
.nn-16__fill {
display: block;
width: 68%;
height: 100%;
background: var(--nn-16-base);
box-shadow: 0 0 .35rem var(--nn-16-core), 0 0 .9rem var(--nn-16-halo);
}
.nn-16__btn {
min-height: 44px;
padding: 0 1rem;
border: 1px solid var(--nn-16-base);
border-radius: .35rem;
background: color-mix(in srgb, var(--nn-16-base) 14%, transparent);
color: var(--nn-16-ink);
font-family: inherit;
font-size: .84rem;
font-weight: 550;
cursor: pointer;
box-shadow: 0 0 .3rem var(--nn-16-core), 0 0 1rem var(--nn-16-halo);
}
.nn-16__btn:hover {
background: color-mix(in srgb, var(--nn-16-base) 22%, transparent);
}
.nn-16__btn:focus-visible {
outline: 2px solid var(--nn-16-ink);
outline-offset: 2px;
}
.nn-16__tokens {
width: 100%;
border-collapse: collapse;
font-size: .78rem;
min-inline-size: 0;
}
.nn-16__caption {
padding-bottom: .5rem;
text-align: left;
font-size: .68rem;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--nn-16-muted);
}
.nn-16__tokens th,
.nn-16__tokens td {
padding: .5rem .5rem;
text-align: left;
border-bottom: 1px solid var(--nn-16-line);
}
.nn-16__tokens thead th {
font-size: .66rem;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--nn-16-muted);
font-weight: 400;
}
.nn-16__tokens tbody th {
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
font-weight: 400;
color: var(--nn-16-base);
}
.nn-16__tokens td {
font-family: ui-monospace, Menlo, monospace;
font-size: .72rem;
color: var(--nn-16-muted);
}
```How this works
One source value, three derived layers. The chain here is --nn-16-hue to a base colour, then three color-mix(in oklab, base X%, transparent) results for core, halo and bloom. Nothing downstream names a hue. That is the whole point: a retint touches one number, and the relationships between the layers — the falloff curve from demo 01 — survive the change intact.
Hand-written per-layer colours are the defect. The usual neon palette hard-codes three rgba values per component. Retinting means finding every one of them, and the moment one is missed a component glows a different colour than its border. Worse, the alpha relationships drift as people tweak individual values, so the falloff stops matching across the set.
Mix in oklab, not sRGB. Fading a saturated colour toward transparent in sRGB shifts its hue as it fades, so a halo derived that way is a slightly different colour from its core. color-mix(in oklab, …) keeps the hue stable through the fade, which is why the three layers read as one light source rather than as three tinted shadows.
Switching with no script. The hue presets are radio inputs; the scope root uses :has(:checked) to set --nn-16-hue. Radios keep the state single-select and keyboard-navigable for free, the labels are the 44px targets, and because the whole system is one token the switch is three rules rather than three palettes.
Make it yours
- Change the base
--nn-16-hueto retint the entire component set in one edit. - Adjust the three mix percentages to change the falloff curve for every component at once.
- Add a fourth preset by copying one radio and one rule — no new colours are needed.
- Raise the base chroma for a harsher set, or lower it for an industrial, half-lit panel look.
- Swap
in oklabforin srgbto see the halo drift away from the core hue. - Point a second token at a complementary hue for accents that should not follow the main retint.
Gotchas — read before shipping
- Hard-coded per-layer colours mean a retint is a search-and-replace, and one missed value shows up as a border that does not match its glow.
- Mixing toward transparent in sRGB shifts hue as alpha falls, so the halo is a different colour from the core.
- A hue token used without a lightness policy reproduces demo 03's problem — some presets will read muddy unless lightness is tuned per hue.
- Custom property names must be scoped: an unqualified
--huewill inherit from, or clobber, a host page's own tokens.
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 Chrome and Safari floors (111 and 16.2) and :has() sets Firefox at 121, because the no-script hue switch depends on it. Without :has() the set renders in its default hue and the presets simply do nothing; without color-mix, the rgba() triples declared first keep every component lit with the hue written out per layer. 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.