47 CSS Toggles & Switches16 / 47

Pure CSSMIT licensed

Neon Cyberpunk Toggle

A clipped-corner, neon-tube toggle for dark dashboards and game UIs: magenta standby glow snaps to cyan when engaged, the status text flickers to life like a striplight warming up, and every glow is pure layered box-shadow and text-shadow.

Published

Live Demo
Try it

The code

<section class="tgl-16" aria-label="Neon cyberpunk toggle">
  <label class="tgl-16__unit" for="tgl-16-sw">
    <span class="tgl-16__word" aria-hidden="true">PWR</span>
    <span class="tgl-16__frame">
      <input type="checkbox" id="tgl-16-sw" class="tgl-16__sw" role="switch" aria-label="Power">
      <span class="tgl-16__thumb" aria-hidden="true"></span>
    </span>
    <span class="tgl-16__status" aria-hidden="true"><i class="tgl-16__stby">STBY</i><i class="tgl-16__live">LIVE</i></span>
  </label>
  <p class="tgl-16__cap">Magenta standby → cyan engage</p>
</section>
.tgl-16,
.tgl-16 *,
.tgl-16 *::before,
.tgl-16 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tgl-16 {
  --off-neon: oklch(0.7 0.24 340);
  --on-neon: oklch(0.82 0.15 200);
  font-family: ui-monospace,'SF Mono',Menlo,monospace;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 60px 20px;
  background: #0a0812;
}

.tgl-16__unit {
  display: flex;
  align-items: center;
  gap: 22px;
  cursor: pointer;
  padding: 20px 26px;
  background: #120f1d;
  clip-path: polygon(10px 0,100% 0,100% calc(100% - 10px),calc(100% - 10px) 100%,0 100%,0 10px);
}

.tgl-16__word {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .3em;
  color: #4c4761;
}

.tgl-16__frame {
  position: relative;
  width: 92px;
  height: 40px;
  background: #0c0a15;
  border: 1px solid var(--off-neon);
  box-shadow: 0 0 1px var(--off-neon),0 0 8px color-mix(in oklab,var(--off-neon) 60%,transparent),inset 0 0 10px color-mix(in oklab,var(--off-neon) 25%,transparent);
  clip-path: polygon(8px 0,100% 0,100% calc(100% - 8px),calc(100% - 8px) 100%,0 100%,0 8px);
  transition: border-color .3s,box-shadow .3s;
}

.tgl-16__frame:has(.tgl-16__sw:checked) {
  border-color: var(--on-neon);
  box-shadow: 0 0 1px var(--on-neon),0 0 10px color-mix(in oklab,var(--on-neon) 65%,transparent),inset 0 0 12px color-mix(in oklab,var(--on-neon) 30%,transparent);
}

.tgl-16__sw {
  appearance: none;
  -webkit-appearance: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: pointer;
  z-index: 1;
  background: transparent;
}

.tgl-16__thumb {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 28px;
  height: 26px;
  background: var(--off-neon);
  box-shadow: 0 0 8px var(--off-neon);
  clip-path: polygon(5px 0,100% 0,100% calc(100% - 5px),calc(100% - 5px) 100%,0 100%,0 5px);
  transition: transform .3s cubic-bezier(.6,0,.3,1.2),background-color .3s,box-shadow .3s;
  pointer-events: none;
}

.tgl-16__sw:checked ~ .tgl-16__thumb {
  transform: translateX(50px);
  background: var(--on-neon);
  box-shadow: 0 0 10px var(--on-neon);
}

.tgl-16__status {
  position: relative;
  width: 52px;
  height: 18px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .18em;
}

.tgl-16__status i {
  position: absolute;
  inset: 0;
  font-style: normal;
  transition: opacity .25s;
}

.tgl-16__stby {
  color: var(--off-neon);
  text-shadow: 0 0 6px var(--off-neon),0 0 16px color-mix(in oklab,var(--off-neon) 55%,transparent);
}

.tgl-16__live {
  color: var(--on-neon);
  text-shadow: 0 0 6px var(--on-neon),0 0 16px color-mix(in oklab,var(--on-neon) 55%,transparent);
  opacity: 0;
}

.tgl-16__unit:has(.tgl-16__sw:checked) .tgl-16__stby {
  opacity: 0;
}

.tgl-16__unit:has(.tgl-16__sw:checked) .tgl-16__live {
  opacity: 1;
  animation: tgl-16-strike .5s steps(1,end);
}

@keyframes tgl-16-strike {
  0% {
    opacity: 0;
  }

  20% {
    opacity: 1;
  }

  30% {
    opacity: .2;
  }

  45% {
    opacity: 1;
  }

  55% {
    opacity: .4;
  }

  70% {
    opacity: 1;
  }
}

.tgl-16__sw:focus-visible ~ .tgl-16__thumb {
  outline: 3px solid #fff;
  outline-offset: 6px;
}

.tgl-16__cap {
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #3f3a52;
}

@media (prefers-reduced-motion: reduce) {
  .tgl-16__unit:has(.tgl-16__sw:checked) .tgl-16__live {
    animation: none;
  }

  .tgl-16__thumb {
    transition: transform .15s,background-color .15s;
  }
}
/* No JavaScript — layered box/text shadows build the neon bloom, one custom-property swap rehues the tube, and a steps() opacity keyframe strikes the LIVE text. */
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 Toggles & Switche 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 Cyberpunk Toggle
Source: https://codefronts.com/components/css-toggles-switches/neon-cyberpunk-toggle/

A clipped-corner, neon-tube toggle for dark dashboards and game UIs: magenta standby glow snaps to cyan when engaged, the status text flickers to life like a striplight warming up, and every glow is pure layered box-shadow and text-shadow.
## HTML
```html
<section class="tgl-16" aria-label="Neon cyberpunk toggle">
  <label class="tgl-16__unit" for="tgl-16-sw">
    <span class="tgl-16__word" aria-hidden="true">PWR</span>
    <span class="tgl-16__frame">
      <input type="checkbox" id="tgl-16-sw" class="tgl-16__sw" role="switch" aria-label="Power">
      <span class="tgl-16__thumb" aria-hidden="true"></span>
    </span>
    <span class="tgl-16__status" aria-hidden="true"><i class="tgl-16__stby">STBY</i><i class="tgl-16__live">LIVE</i></span>
  </label>
  <p class="tgl-16__cap">Magenta standby → cyan engage</p>
</section>
```
## CSS
```css
.tgl-16,
.tgl-16 *,
.tgl-16 *::before,
.tgl-16 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tgl-16 {
  --off-neon: oklch(0.7 0.24 340);
  --on-neon: oklch(0.82 0.15 200);
  font-family: ui-monospace,'SF Mono',Menlo,monospace;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 60px 20px;
  background: #0a0812;
}

.tgl-16__unit {
  display: flex;
  align-items: center;
  gap: 22px;
  cursor: pointer;
  padding: 20px 26px;
  background: #120f1d;
  clip-path: polygon(10px 0,100% 0,100% calc(100% - 10px),calc(100% - 10px) 100%,0 100%,0 10px);
}

.tgl-16__word {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .3em;
  color: #4c4761;
}

.tgl-16__frame {
  position: relative;
  width: 92px;
  height: 40px;
  background: #0c0a15;
  border: 1px solid var(--off-neon);
  box-shadow: 0 0 1px var(--off-neon),0 0 8px color-mix(in oklab,var(--off-neon) 60%,transparent),inset 0 0 10px color-mix(in oklab,var(--off-neon) 25%,transparent);
  clip-path: polygon(8px 0,100% 0,100% calc(100% - 8px),calc(100% - 8px) 100%,0 100%,0 8px);
  transition: border-color .3s,box-shadow .3s;
}

.tgl-16__frame:has(.tgl-16__sw:checked) {
  border-color: var(--on-neon);
  box-shadow: 0 0 1px var(--on-neon),0 0 10px color-mix(in oklab,var(--on-neon) 65%,transparent),inset 0 0 12px color-mix(in oklab,var(--on-neon) 30%,transparent);
}

.tgl-16__sw {
  appearance: none;
  -webkit-appearance: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: pointer;
  z-index: 1;
  background: transparent;
}

.tgl-16__thumb {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 28px;
  height: 26px;
  background: var(--off-neon);
  box-shadow: 0 0 8px var(--off-neon);
  clip-path: polygon(5px 0,100% 0,100% calc(100% - 5px),calc(100% - 5px) 100%,0 100%,0 5px);
  transition: transform .3s cubic-bezier(.6,0,.3,1.2),background-color .3s,box-shadow .3s;
  pointer-events: none;
}

.tgl-16__sw:checked ~ .tgl-16__thumb {
  transform: translateX(50px);
  background: var(--on-neon);
  box-shadow: 0 0 10px var(--on-neon);
}

.tgl-16__status {
  position: relative;
  width: 52px;
  height: 18px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .18em;
}

.tgl-16__status i {
  position: absolute;
  inset: 0;
  font-style: normal;
  transition: opacity .25s;
}

.tgl-16__stby {
  color: var(--off-neon);
  text-shadow: 0 0 6px var(--off-neon),0 0 16px color-mix(in oklab,var(--off-neon) 55%,transparent);
}

.tgl-16__live {
  color: var(--on-neon);
  text-shadow: 0 0 6px var(--on-neon),0 0 16px color-mix(in oklab,var(--on-neon) 55%,transparent);
  opacity: 0;
}

.tgl-16__unit:has(.tgl-16__sw:checked) .tgl-16__stby {
  opacity: 0;
}

.tgl-16__unit:has(.tgl-16__sw:checked) .tgl-16__live {
  opacity: 1;
  animation: tgl-16-strike .5s steps(1,end);
}

@keyframes tgl-16-strike {
  0% {
    opacity: 0;
  }

  20% {
    opacity: 1;
  }

  30% {
    opacity: .2;
  }

  45% {
    opacity: 1;
  }

  55% {
    opacity: .4;
  }

  70% {
    opacity: 1;
  }
}

.tgl-16__sw:focus-visible ~ .tgl-16__thumb {
  outline: 3px solid #fff;
  outline-offset: 6px;
}

.tgl-16__cap {
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #3f3a52;
}

@media (prefers-reduced-motion: reduce) {
  .tgl-16__unit:has(.tgl-16__sw:checked) .tgl-16__live {
    animation: none;
  }

  .tgl-16__thumb {
    transition: transform .15s,background-color .15s;
  }
}
```

## JavaScript
```js
/* No JavaScript — layered box/text shadows build the neon bloom, one custom-property swap rehues the tube, and a steps() opacity keyframe strikes the LIVE text. */
```

How this works

The track is a dark slab with clip-path:polygon() cutting angled corners. Neon is stacked shadows: a tight 1px ring, a mid 8px bloom and a wide 22px wash of the same hue — on the border via box-shadow and on the STATUS text via text-shadow. Checked state swaps the whole hue set from magenta to cyan by flipping one custom property.

The signature flicker runs only on engage: a steps-based opacity keyframe (on-off-on-off-on) plays once on the checked label, imitating a gas tube striking. The thumb is a squared indicator that slides with transform and carries its own glow.

Make it yours

  • Rehue both states via --off-neon / --on-neon (any oklch hue works).
  • Sharpen corners: deepen the clip-path insets from 10px to 16px.
  • Loop a faint idle flicker (2% opacity dips every 4s) for a busier scene.
  • Swap STATUS words to EN/GAGED, ARM/SAFE etc. — keep them short.

Gotchas — read before shipping

  • Neon needs dark: below ~15% background lightness the bloom reads as glow, above it as blur.
  • Flicker via opacity keyframes with steps(), not display toggles; and gate it behind prefers-reduced-motion (strobing is a vestibular trigger).
  • clip-path clips box-shadow too — put the glow on an inner element, not the clipped wrapper.

Browser support

ChromeSafariFirefoxEdge
111+16.2+121+111+

Floor set by :has(), oklch(), color-mix() as this demo is written. The core technique itself goes back further — Chrome 55+.

clip-path polygon + layered shadows; universal in modern browsers.

Techniques used in this demo

Search CodeFronts

Loading…