18 CSS Scroll Progress Bar17 / 18

Light JSMIT licensed

Cyberpunk / Neon Glow Dark-Mode Progress Bar

A neon tube rather than a rectangle: three stacked shadows produce a real bloom, a brighter nib burns at the leading edge, and a faint scanline layer sits over the whole page. It is the maximalist end of this collection — and it is built from four declarations, not a canvas.

Published

Live Demo
Try it

The code

<section class="sp-17" aria-label="Cyberpunk neon glow scroll progress bar demo">
  <div class="sp-17__scan" aria-hidden="true"></div>
  <div class="sp-17__rail" role="progressbar" aria-label="Reading progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" id="sp-17-rail"><i class="sp-17__fill"><b class="sp-17__nib"></b></i></div>
  <header class="sp-17__bar">
    <a class="sp-17__brand" href="#sp-17-s1"><span class="sp-17__logo" aria-hidden="true"></span>NEON<span class="sp-17__brandthin">//grid</span></a>
    <p class="sp-17__meta" id="sp-17-pct">00%</p>
  </header>
  <div class="sp-17__intro">
    <p class="sp-17__intro-eyebrow">three shadows, one tube</p>
    <h2 class="sp-17__intro-title">Light, not blur</h2>
    <p class="sp-17__intro-sub">Scroll. The rail burns brighter at its leading edge, the scanlines sit over everything, and none of it costs a frame.</p>
  </div>
  <main class="sp-17__main">
    <section class="sp-17__sec" id="sp-17-s1">
      <p class="sp-17__kicker">// 01 bloom</p>
      <h3 class="sp-17__h">Stack the falloff</h3>
      <p class="sp-17__p sp-17__p--lede">Real light fades in layers. Three shadows at 4px, 12px and 34px with decreasing opacity produce a tube; one 30px shadow produces a smudge.</p>
    </section>
    <section class="sp-17__sec" id="sp-17-s2">
      <p class="sp-17__kicker">// 02 nib</p>
      <h3 class="sp-17__h">drop-shadow follows the alpha</h3>
      <p class="sp-17__p">A <code>box-shadow</code> glows in the shape of the box. A <code>drop-shadow</code> filter glows in the shape of what is actually painted — which is why the rounded nib has a rounded halo.</p>
      <figure class="sp-17__fig">
        <img class="sp-17__img" src="https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5?auto=format&amp;fit=crop&amp;w=1400&amp;q=70" alt="Green digital code cascading down a dark screen" loading="lazy" decoding="async">
        <figcaption class="sp-17__cap">Atmosphere is paint. Two gradients and a vignette, no elements.</figcaption>
      </figure>
    </section>
    <section class="sp-17__sec" id="sp-17-s3">
      <p class="sp-17__kicker">// 03 legibility</p>
      <h3 class="sp-17__h">Only the accents glow</h3>
      <p class="sp-17__p">Body copy stays a low-chroma near-white. Glowing paragraphs are the fastest way to make a beautiful dark theme unusable — and neon on black often fails contrast even when it looks bright.</p>
    </section>
    <section class="sp-17__sec" id="sp-17-s4">
      <p class="sp-17__kicker">// 04 respect</p>
      <h3 class="sp-17__h">Let the system win</h3>
      <p class="sp-17__p">Under <code>forced-colors</code> every glow is dropped so the OS high-contrast palette renders cleanly. Under <code>prefers-reduced-motion</code> the bar stops pulsing and simply fills.</p>
      <p class="sp-17__chip">3 shadows · 1 filter · 0 canvas</p>
    </section>
  </main>
  <footer class="sp-17__pagefoot"><p class="sp-17__pagefootin">Scroll progress pattern 17 of 18 · codefronts.com</p></footer>
</section>
.sp-17 {
  position: relative;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --sp-17-neon: oklch(0.78 0.19 195);
  --sp-17-neon2: oklch(0.68 0.25 330);
  --sp-17-bg: oklch(0.13 0.02 275);
  --sp-17-ink: oklch(0.94 0.01 220);
  --sp-17-mut: oklch(0.68 0.02 230);
  --sp-17-line: oklch(0.78 0.19 195/.22);
  --sp-17-p: 0;
  background: radial-gradient(120% 70% at 50% -10%,oklch(0.2 0.07 280),transparent),var(--sp-17-bg);
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--sp-17-ink);
  -webkit-font-smoothing: antialiased;
}

.sp-17 *,
.sp-17 *::before,
.sp-17 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sp-17__scan {
  position: fixed;
  inset: 0;
  z-index: 58;
  pointer-events: none;
  background: repeating-linear-gradient(180deg,oklch(1 0 0/.035) 0 1px,transparent 1px 3px);
  mix-blend-mode: screen;
  opacity: .7;
}

.sp-17__rail {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 4px;
  z-index: 60;
  background: oklch(0.78 0.19 195/.1);
  pointer-events: none;
}

.sp-17__fill {
  position: relative;
  display: block;
  block-size: 100%;
  transform-origin: 0 50%;
  transform: scaleX(var(--sp-17-p));
  background: linear-gradient(90deg,var(--sp-17-neon2),var(--sp-17-neon));
  box-shadow: 0 0 4px var(--sp-17-neon),0 0 12px color-mix(in oklch,var(--sp-17-neon) 70%,transparent),0 0 34px color-mix(in oklch,var(--sp-17-neon) 40%,transparent);
}

.sp-17__nib {
  position: absolute;
  inset-block: -3px;
  inset-inline-end: -1px;
  inline-size: 3px;
  border-radius: 99px;
  background: oklch(0.99 0.02 195);
  filter: drop-shadow(0 0 6px var(--sp-17-neon)) drop-shadow(0 0 14px var(--sp-17-neon));
}

@supports (animation-timeline: scroll()) {
  .sp-17__fill {
    transform: scaleX(0);
    animation: sp-17-fill linear both;
    animation-timeline: scroll(root block);
  }
}

@keyframes sp-17-fill {
  from {
    transform: scaleX(0);
  }

  to {
    transform: scaleX(1);
  }
}

.sp-17__bar {
  position: sticky;
  inset-block-start: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 14px;
  min-block-size: 58px;
  padding-block-start: 4px;
  background: color-mix(in oklch,var(--sp-17-bg) 74%,transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-block-end: 1px solid var(--sp-17-line);
}

.sp-17__brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--sp-17-neon);
  text-decoration: none;
  text-shadow: 0 0 10px color-mix(in oklch,var(--sp-17-neon) 60%,transparent);
}

.sp-17__brandthin {
  color: var(--sp-17-mut);
  text-shadow: none;
  font-weight: 400;
}

.sp-17__brand:focus-visible {
  outline: 2px solid var(--sp-17-neon);
  outline-offset: 3px;
  border-radius: 6px;
}

.sp-17__logo {
  inline-size: 14px;
  block-size: 14px;
  background: var(--sp-17-neon);
  clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%);
  box-shadow: 0 0 12px var(--sp-17-neon);
}

.sp-17__meta {
  margin-inline-start: auto;
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .14em;
  color: var(--sp-17-neon2);
  text-shadow: 0 0 10px color-mix(in oklch,var(--sp-17-neon2) 55%,transparent);
}

.sp-17__intro {
  display: grid;
  gap: 16px;
  padding-block: clamp(50px,11vh,124px) clamp(26px,6vh,58px);
}

.sp-17__intro-eyebrow {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--sp-17-neon);
}

.sp-17__intro-title {
  font-size: clamp(42px,8vw,96px);
  line-height: .94;
  letter-spacing: -.045em;
  font-weight: 760;
  max-inline-size: 12ch;
  text-wrap: balance;
  text-shadow: 0 0 24px color-mix(in oklch,var(--sp-17-neon) 45%,transparent),2px 0 0 color-mix(in oklch,var(--sp-17-neon2) 55%,transparent);
}

.sp-17__intro-sub {
  font-size: clamp(14.5px,1.6vw,18px);
  line-height: 1.55;
  color: var(--sp-17-mut);
  max-inline-size: 52ch;
  text-wrap: pretty;
}

.sp-17__sec {
  scroll-margin-top: 74px;
  display: grid;
  gap: 12px;
  align-content: center;
  min-block-size: 76svh;
  padding-block: clamp(26px,5vw,52px);
  border-block-end: 1px solid oklch(1 0 0/.08);
}

.sp-17__sec:last-child {
  border-block-end: 0;
}

.sp-17__kicker {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--sp-17-neon);
}

.sp-17__h {
  font-size: clamp(23px,3.3vw,36px);
  line-height: 1.08;
  letter-spacing: -.034em;
  font-weight: 730;
  text-wrap: balance;
}

.sp-17__p {
  max-inline-size: 60ch;
  font-size: 15.8px;
  line-height: 1.7;
  color: var(--sp-17-mut);
  text-wrap: pretty;
}

.sp-17__p--lede {
  font-size: 17.8px;
  color: var(--sp-17-ink);
}

.sp-17__p code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .85em;
  background: oklch(0.78 0.19 195/.14);
  padding: 2px 6px;
  border-radius: 5px;
  color: var(--sp-17-neon);
}

.sp-17__fig {
  display: grid;
  gap: 9px;
  max-inline-size: 68ch;
}

.sp-17__img {
  inline-size: 100%;
  aspect-ratio: 16/7;
  object-fit: cover;
  display: block;
  border-radius: 14px;
  border: 1px solid var(--sp-17-line);
  background: linear-gradient(135deg,oklch(0.32 0.1 200),oklch(0.2 0.08 300));
  filter: saturate(1.15);
}

.sp-17__cap {
  font-size: 12.6px;
  color: var(--sp-17-mut);
}

.sp-17__chip {
  justify-self: start;
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  color: var(--sp-17-neon);
  padding: 7px 13px;
  border-radius: 99px;
  background: oklch(0.78 0.19 195/.1);
  border: 1px solid var(--sp-17-line);
  box-shadow: 0 0 18px -6px var(--sp-17-neon);
}

.sp-17__pagefoot {
  display: grid;
  place-items: center;
  padding: clamp(40px,8vh,100px) 24px;
  border-block-start: 1px solid var(--sp-17-line);
}

.sp-17__pagefootin {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 12px;
  letter-spacing: .1em;
  color: var(--sp-17-mut);
  text-align: center;
}

.sp-17__bar,
.sp-17__intro,
.sp-17__sec {
  padding-inline: max(clamp(18px,4vw,36px),calc((100% - 900px)/2));
}

:root:has(.sp-17) {
  scroll-behavior: smooth;
  scroll-padding-top: 74px;
}

@media (max-width: 600px) {
  .sp-17__sec {
    min-block-size: auto;
  }

  .sp-17__scan {
    opacity: .45;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root:has(.sp-17) {
    scroll-behavior: auto;
  }

  .sp-17 * {
    transition-duration: .01ms !important;
  }
}

@media (forced-colors: active) {
  .sp-17__fill,
    .sp-17__nib,
    .sp-17__chip,
    .sp-17__brand,
    .sp-17__meta,
    .sp-17__intro-title {
    box-shadow: none;
    filter: none;
    text-shadow: none;
  }

  .sp-17__scan {
    display: none;
  }
}
(() => {
  const root = document.querySelector('.sp-17');
  if (!root || root.dataset.spWired) return;
  root.dataset.spWired = '1';
  const rail = root.querySelector('#sp-17-rail');
  const pct = root.querySelector('#sp-17-pct');
  const native = CSS.supports('animation-timeline', 'scroll()');
  let ticking = false;
  const paint = () => {
    ticking = false;
    const d = document.documentElement;
    const max = d.scrollHeight - d.clientHeight;
    const p = max > 0 ? Math.min(1, Math.max(0, d.scrollTop / max)) : 0;
    if (!native) root.style.setProperty('--sp-17-p', p.toFixed(4));
    const n = Math.round(p * 100);
    pct.textContent = String(n).padStart(2, '0') + '%';
    rail.setAttribute('aria-valuenow', String(n));
  };
  const onScroll = () => { if (!ticking) { ticking = true; requestAnimationFrame(paint); } };
  addEventListener('scroll', onScroll, { passive: true });
  addEventListener('resize', onScroll, { passive: true });
  paint();
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Scroll Progress Bar 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 Glow Dark-Mode Progress Bar
Source: https://codefronts.com/motion/css-scroll-progress-bar/cyberpunk-neon-glow-dark-mode-progress-bar/

A neon tube rather than a rectangle: three stacked shadows produce a real bloom, a brighter nib burns at the leading edge, and a faint scanline layer sits over the whole page. It is the maximalist end of this collection — and it is built from four declarations, not a canvas.
## HTML
```html
<section class="sp-17" aria-label="Cyberpunk neon glow scroll progress bar demo">
  <div class="sp-17__scan" aria-hidden="true"></div>
  <div class="sp-17__rail" role="progressbar" aria-label="Reading progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" id="sp-17-rail"><i class="sp-17__fill"><b class="sp-17__nib"></b></i></div>
  <header class="sp-17__bar">
    <a class="sp-17__brand" href="#sp-17-s1"><span class="sp-17__logo" aria-hidden="true"></span>NEON<span class="sp-17__brandthin">//grid</span></a>
    <p class="sp-17__meta" id="sp-17-pct">00%</p>
  </header>
  <div class="sp-17__intro">
    <p class="sp-17__intro-eyebrow">three shadows, one tube</p>
    <h2 class="sp-17__intro-title">Light, not blur</h2>
    <p class="sp-17__intro-sub">Scroll. The rail burns brighter at its leading edge, the scanlines sit over everything, and none of it costs a frame.</p>
  </div>
  <main class="sp-17__main">
    <section class="sp-17__sec" id="sp-17-s1">
      <p class="sp-17__kicker">// 01 bloom</p>
      <h3 class="sp-17__h">Stack the falloff</h3>
      <p class="sp-17__p sp-17__p--lede">Real light fades in layers. Three shadows at 4px, 12px and 34px with decreasing opacity produce a tube; one 30px shadow produces a smudge.</p>
    </section>
    <section class="sp-17__sec" id="sp-17-s2">
      <p class="sp-17__kicker">// 02 nib</p>
      <h3 class="sp-17__h">drop-shadow follows the alpha</h3>
      <p class="sp-17__p">A <code>box-shadow</code> glows in the shape of the box. A <code>drop-shadow</code> filter glows in the shape of what is actually painted — which is why the rounded nib has a rounded halo.</p>
      <figure class="sp-17__fig">
        <img class="sp-17__img" src="https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5?auto=format&amp;fit=crop&amp;w=1400&amp;q=70" alt="Green digital code cascading down a dark screen" loading="lazy" decoding="async">
        <figcaption class="sp-17__cap">Atmosphere is paint. Two gradients and a vignette, no elements.</figcaption>
      </figure>
    </section>
    <section class="sp-17__sec" id="sp-17-s3">
      <p class="sp-17__kicker">// 03 legibility</p>
      <h3 class="sp-17__h">Only the accents glow</h3>
      <p class="sp-17__p">Body copy stays a low-chroma near-white. Glowing paragraphs are the fastest way to make a beautiful dark theme unusable — and neon on black often fails contrast even when it looks bright.</p>
    </section>
    <section class="sp-17__sec" id="sp-17-s4">
      <p class="sp-17__kicker">// 04 respect</p>
      <h3 class="sp-17__h">Let the system win</h3>
      <p class="sp-17__p">Under <code>forced-colors</code> every glow is dropped so the OS high-contrast palette renders cleanly. Under <code>prefers-reduced-motion</code> the bar stops pulsing and simply fills.</p>
      <p class="sp-17__chip">3 shadows · 1 filter · 0 canvas</p>
    </section>
  </main>
  <footer class="sp-17__pagefoot"><p class="sp-17__pagefootin">Scroll progress pattern 17 of 18 · codefronts.com</p></footer>
</section>
```
## CSS
```css
.sp-17 {
  position: relative;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  --sp-17-neon: oklch(0.78 0.19 195);
  --sp-17-neon2: oklch(0.68 0.25 330);
  --sp-17-bg: oklch(0.13 0.02 275);
  --sp-17-ink: oklch(0.94 0.01 220);
  --sp-17-mut: oklch(0.68 0.02 230);
  --sp-17-line: oklch(0.78 0.19 195/.22);
  --sp-17-p: 0;
  background: radial-gradient(120% 70% at 50% -10%,oklch(0.2 0.07 280),transparent),var(--sp-17-bg);
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--sp-17-ink);
  -webkit-font-smoothing: antialiased;
}

.sp-17 *,
.sp-17 *::before,
.sp-17 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sp-17__scan {
  position: fixed;
  inset: 0;
  z-index: 58;
  pointer-events: none;
  background: repeating-linear-gradient(180deg,oklch(1 0 0/.035) 0 1px,transparent 1px 3px);
  mix-blend-mode: screen;
  opacity: .7;
}

.sp-17__rail {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 4px;
  z-index: 60;
  background: oklch(0.78 0.19 195/.1);
  pointer-events: none;
}

.sp-17__fill {
  position: relative;
  display: block;
  block-size: 100%;
  transform-origin: 0 50%;
  transform: scaleX(var(--sp-17-p));
  background: linear-gradient(90deg,var(--sp-17-neon2),var(--sp-17-neon));
  box-shadow: 0 0 4px var(--sp-17-neon),0 0 12px color-mix(in oklch,var(--sp-17-neon) 70%,transparent),0 0 34px color-mix(in oklch,var(--sp-17-neon) 40%,transparent);
}

.sp-17__nib {
  position: absolute;
  inset-block: -3px;
  inset-inline-end: -1px;
  inline-size: 3px;
  border-radius: 99px;
  background: oklch(0.99 0.02 195);
  filter: drop-shadow(0 0 6px var(--sp-17-neon)) drop-shadow(0 0 14px var(--sp-17-neon));
}

@supports (animation-timeline: scroll()) {
  .sp-17__fill {
    transform: scaleX(0);
    animation: sp-17-fill linear both;
    animation-timeline: scroll(root block);
  }
}

@keyframes sp-17-fill {
  from {
    transform: scaleX(0);
  }

  to {
    transform: scaleX(1);
  }
}

.sp-17__bar {
  position: sticky;
  inset-block-start: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 14px;
  min-block-size: 58px;
  padding-block-start: 4px;
  background: color-mix(in oklch,var(--sp-17-bg) 74%,transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-block-end: 1px solid var(--sp-17-line);
}

.sp-17__brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--sp-17-neon);
  text-decoration: none;
  text-shadow: 0 0 10px color-mix(in oklch,var(--sp-17-neon) 60%,transparent);
}

.sp-17__brandthin {
  color: var(--sp-17-mut);
  text-shadow: none;
  font-weight: 400;
}

.sp-17__brand:focus-visible {
  outline: 2px solid var(--sp-17-neon);
  outline-offset: 3px;
  border-radius: 6px;
}

.sp-17__logo {
  inline-size: 14px;
  block-size: 14px;
  background: var(--sp-17-neon);
  clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%);
  box-shadow: 0 0 12px var(--sp-17-neon);
}

.sp-17__meta {
  margin-inline-start: auto;
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .14em;
  color: var(--sp-17-neon2);
  text-shadow: 0 0 10px color-mix(in oklch,var(--sp-17-neon2) 55%,transparent);
}

.sp-17__intro {
  display: grid;
  gap: 16px;
  padding-block: clamp(50px,11vh,124px) clamp(26px,6vh,58px);
}

.sp-17__intro-eyebrow {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--sp-17-neon);
}

.sp-17__intro-title {
  font-size: clamp(42px,8vw,96px);
  line-height: .94;
  letter-spacing: -.045em;
  font-weight: 760;
  max-inline-size: 12ch;
  text-wrap: balance;
  text-shadow: 0 0 24px color-mix(in oklch,var(--sp-17-neon) 45%,transparent),2px 0 0 color-mix(in oklch,var(--sp-17-neon2) 55%,transparent);
}

.sp-17__intro-sub {
  font-size: clamp(14.5px,1.6vw,18px);
  line-height: 1.55;
  color: var(--sp-17-mut);
  max-inline-size: 52ch;
  text-wrap: pretty;
}

.sp-17__sec {
  scroll-margin-top: 74px;
  display: grid;
  gap: 12px;
  align-content: center;
  min-block-size: 76svh;
  padding-block: clamp(26px,5vw,52px);
  border-block-end: 1px solid oklch(1 0 0/.08);
}

.sp-17__sec:last-child {
  border-block-end: 0;
}

.sp-17__kicker {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--sp-17-neon);
}

.sp-17__h {
  font-size: clamp(23px,3.3vw,36px);
  line-height: 1.08;
  letter-spacing: -.034em;
  font-weight: 730;
  text-wrap: balance;
}

.sp-17__p {
  max-inline-size: 60ch;
  font-size: 15.8px;
  line-height: 1.7;
  color: var(--sp-17-mut);
  text-wrap: pretty;
}

.sp-17__p--lede {
  font-size: 17.8px;
  color: var(--sp-17-ink);
}

.sp-17__p code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .85em;
  background: oklch(0.78 0.19 195/.14);
  padding: 2px 6px;
  border-radius: 5px;
  color: var(--sp-17-neon);
}

.sp-17__fig {
  display: grid;
  gap: 9px;
  max-inline-size: 68ch;
}

.sp-17__img {
  inline-size: 100%;
  aspect-ratio: 16/7;
  object-fit: cover;
  display: block;
  border-radius: 14px;
  border: 1px solid var(--sp-17-line);
  background: linear-gradient(135deg,oklch(0.32 0.1 200),oklch(0.2 0.08 300));
  filter: saturate(1.15);
}

.sp-17__cap {
  font-size: 12.6px;
  color: var(--sp-17-mut);
}

.sp-17__chip {
  justify-self: start;
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  color: var(--sp-17-neon);
  padding: 7px 13px;
  border-radius: 99px;
  background: oklch(0.78 0.19 195/.1);
  border: 1px solid var(--sp-17-line);
  box-shadow: 0 0 18px -6px var(--sp-17-neon);
}

.sp-17__pagefoot {
  display: grid;
  place-items: center;
  padding: clamp(40px,8vh,100px) 24px;
  border-block-start: 1px solid var(--sp-17-line);
}

.sp-17__pagefootin {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 12px;
  letter-spacing: .1em;
  color: var(--sp-17-mut);
  text-align: center;
}

.sp-17__bar,
.sp-17__intro,
.sp-17__sec {
  padding-inline: max(clamp(18px,4vw,36px),calc((100% - 900px)/2));
}

:root:has(.sp-17) {
  scroll-behavior: smooth;
  scroll-padding-top: 74px;
}

@media (max-width: 600px) {
  .sp-17__sec {
    min-block-size: auto;
  }

  .sp-17__scan {
    opacity: .45;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root:has(.sp-17) {
    scroll-behavior: auto;
  }

  .sp-17 * {
    transition-duration: .01ms !important;
  }
}

@media (forced-colors: active) {
  .sp-17__fill,
    .sp-17__nib,
    .sp-17__chip,
    .sp-17__brand,
    .sp-17__meta,
    .sp-17__intro-title {
    box-shadow: none;
    filter: none;
    text-shadow: none;
  }

  .sp-17__scan {
    display: none;
  }
}
```

## JavaScript
```js
(() => {
  const root = document.querySelector('.sp-17');
  if (!root || root.dataset.spWired) return;
  root.dataset.spWired = '1';
  const rail = root.querySelector('#sp-17-rail');
  const pct = root.querySelector('#sp-17-pct');
  const native = CSS.supports('animation-timeline', 'scroll()');
  let ticking = false;
  const paint = () => {
    ticking = false;
    const d = document.documentElement;
    const max = d.scrollHeight - d.clientHeight;
    const p = max > 0 ? Math.min(1, Math.max(0, d.scrollTop / max)) : 0;
    if (!native) root.style.setProperty('--sp-17-p', p.toFixed(4));
    const n = Math.round(p * 100);
    pct.textContent = String(n).padStart(2, '0') + '%';
    rail.setAttribute('aria-valuenow', String(n));
  };
  const onScroll = () => { if (!ticking) { ticking = true; requestAnimationFrame(paint); } };
  addEventListener('scroll', onScroll, { passive: true });
  addEventListener('resize', onScroll, { passive: true });
  paint();
})();
```

How this works

Convincing neon is a stack of shadows at increasing radius and decreasing opacity, mimicking how light actually falls off:

.fill{
  background: var(--neon);
  box-shadow:
     0 0 4px  var(--neon),          /* the tube */
     0 0 12px color-mix(in oklch, var(--neon) 70%, transparent),
     0 0 34px color-mix(in oklch, var(--neon) 40%, transparent);
}

One large soft shadow reads as a blurry rectangle. Three graduated ones read as light. The color-mix() derivations mean the whole effect re-themes from a single hue token.

The nib is a separate 3px element pinned to the leading edge with a drop-shadow filter rather than a box-shadow, because filter follows the element's alpha — a rounded nib glows in a rounded shape, where a box-shadow would glow as a rectangle.

The atmosphere comes from two cheap layers. Scanlines are a repeating-linear-gradient of 1px translucent stripes on a fixed overlay with pointer-events:none, and a subtle vignette sits behind everything. Both are pure paint, no elements, no cost per frame.

Restraint is what keeps it from being a joke. Body copy is a low-chroma near-white, not neon — glowing text at paragraph size is illegible. Only the accents glow. And every glow drops away under prefers-reduced-motion and honours forced-colors, where the system palette must win.

Make it yours

  • Hue: one token, --sp-17-neon. Cyan reads clinical, magenta reads aggressive, acid green reads retro-terminal — the shadow stack derives from whichever you pick.
  • Bloom intensity: scale the three shadow radii together. Doubling only the largest gives a hazier, foggier look; doubling all three looks like a brighter tube.
  • Scanline density: the gradient's 3px period. Above about 6px it stops reading as a screen and starts reading as stripes.
  • Chromatic aberration on the heading: two offset text-shadows in complementary hues, 1px apart. Use once per page at most.
  • Calm variant: keep the palette, delete the scanlines and halve the bloom — most products want cyberpunk colour without cyberpunk noise.

Gotchas — read before shipping

  • A single large box-shadow looks like a blur, not a glow. Layer at least three at increasing radius.
  • Glowing body text is unreadable. Restrict glow to accents, rules and headings at large sizes.
  • Neon on near-black frequently fails WCAG contrast for small text even though it looks bright — check the actual ratio rather than trusting the vibe.
  • filter: drop-shadow() is more expensive than box-shadow; use it only where the alpha shape matters, as on the nib.
  • Fixed overlays must carry pointer-events:none or they swallow every click on the page.
  • Under forced-colors: active the system palette replaces yours. Do not fight it — drop shadows and let the OS render its own high-contrast treatment.

Browser support

ChromeSafariFirefoxEdge
115+26+144+115+

box-shadow, filter and repeating gradients are universal. color-mix() is Chrome 111+, Safari 16.2+, Firefox 113+. Only the scroll binding needs the modern floor and it falls back to the custom-property path.

Techniques used in this demo

Search CodeFronts

Loading…