Open demo

CSS code

HTML
<div><span class="lbl">Accent glow</span><div class="row">
<label class="dk"><input type="checkbox" role="switch" aria-label="Focus mode" checked><i></i></label>
<label class="dk"><input type="checkbox" role="switch" aria-label="Do not disturb"><i></i></label></div></div>
<div><span class="lbl">Ghost outline</span><div class="row">
<label class="dk gh"><input type="checkbox" role="switch" aria-label="Auto-play" checked><i></i></label>
<label class="dk gh"><input type="checkbox" role="switch" aria-label="Captions"><i></i></label></div></div>
<div><span class="lbl">Status switch</span><div class="row">
<label class="dk st"><input type="checkbox" role="switch" aria-labelledby="svc-a" checked><i></i></label><span class="cap" id="svc-a">Service enabled</span></div>
<div class="row" style="margin-top:14px">
<label class="dk st"><input type="checkbox" role="switch" aria-labelledby="svc-b"><i></i></label><span class="cap" id="svc-b">Service paused</span></div></div>
CSS
/* Dark switches: a visually hidden checkbox (role="switch") followed by an <i> that draws track and knob. */
.dk{position:relative;display:inline-block;width:54px;height:30px;cursor:pointer}
.dk input{position:absolute;opacity:0;width:0;height:0;margin:0}
.dk i{box-sizing:border-box;position:absolute;inset:0;border-radius:999px;background:#232a36;border:1px solid #56617a;
  transition:background .22s,border-color .22s,box-shadow .22s}
.dk i::before{content:"";position:absolute;top:4px;left:4px;width:20px;height:20px;border-radius:50%;
  background:#8b93a7;transition:transform .22s,background .22s}
.dk input:checked+i{background:#1d4ed8;border-color:#3b82f6;box-shadow:0 0 12px rgba(59,130,246,.5)}
.dk input:checked+i::before{transform:translateX(24px);background:#fff}
.dk input:focus-visible+i{outline:2px solid #7dd3fc;outline-offset:3px}
/* ghost outline */
.gh i{background:transparent;border:1px solid #56617a}
.gh input:checked+i{background:transparent;border-color:#7dd3fc;box-shadow:none}
.gh input:checked+i::before{background:#7dd3fc}
/* status: red when off, green when on */
.st i{background:#3f1d1d;border-color:#7f1d1d}
.st i::before{background:#f87171}
.st input:checked+i{background:#14351f;border-color:#166534;box-shadow:none}
.st input:checked+i::before{background:#4ade80}
.cap{font-size:.85rem;color:#8b93a7;margin-left:14px}
@media (prefers-reduced-motion:reduce){.dk i,.dk i::before{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Accent glow</span><div class="demo-row">
<div class="form-check form-switch dk"><input class="form-check-input" type="checkbox" role="switch" aria-label="Focus mode" checked></div>
<div class="form-check form-switch dk"><input class="form-check-input" type="checkbox" role="switch" aria-label="Do not disturb"></div></div></div>
<div><span class="lbl">Ghost outline</span><div class="demo-row">
<div class="form-check form-switch dk gh"><input class="form-check-input" type="checkbox" role="switch" aria-label="Auto-play" checked></div>
<div class="form-check form-switch dk gh"><input class="form-check-input" type="checkbox" role="switch" aria-label="Captions"></div></div></div>
<div><span class="lbl">Status switch</span><div class="demo-row">
<div class="form-check form-switch dk st"><input class="form-check-input" type="checkbox" role="switch" aria-labelledby="svc-a" checked></div><span class="cap" id="svc-a">Service enabled</span></div>
<div class="demo-row" style="margin-top:14px">
<div class="form-check form-switch dk st"><input class="form-check-input" type="checkbox" role="switch" aria-labelledby="svc-b"></div><span class="cap" id="svc-b">Service paused</span></div></div>
CSS
/* Bootstrap .form-switch for dark interfaces. Track colours are CSS variables; the knob is --bs-form-switch-bg. */
.dk{--dk-track:#232a36;--dk-border:#56617a;--dk-on-track:#1d4ed8;--dk-on-border:#3b82f6;--dk-glow:0 0 12px rgba(59,130,246,.5);
  --dk-knob:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-14 -14 28 28'><circle r='10' fill='%238b93a7'/></svg>");
  --dk-knob-on:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-14 -14 28 28'><circle r='10' fill='%23fff'/></svg>");
  display:inline-block;min-height:0;margin:0;padding:0;line-height:0}
.dk .form-check-input{width:54px;height:30px;margin:0;float:none;border:1px solid var(--dk-border);border-radius:999px;cursor:pointer;
  background-color:var(--dk-track);transition:background-position .22s,background-color .22s,border-color .22s,box-shadow .22s}
.dk .form-check-input,.dk .form-check-input:focus{--bs-form-switch-bg:var(--dk-knob);border-color:var(--dk-border);box-shadow:none}
.dk .form-check-input:checked{--bs-form-switch-bg:var(--dk-knob-on);background-color:var(--dk-on-track);
  border-color:var(--dk-on-border);box-shadow:var(--dk-glow)}
.dk .form-check-input:focus-visible{outline:2px solid #7dd3fc;outline-offset:3px}
/* ghost outline */
.dk.gh{--dk-track:transparent;--dk-on-track:transparent;--dk-on-border:#7dd3fc;--dk-glow:none;
  --dk-knob-on:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-14 -14 28 28'><circle r='10' fill='%237dd3fc'/></svg>")}
/* status: red when off, green when on */
.dk.st{--dk-track:#3f1d1d;--dk-border:#7f1d1d;--dk-on-track:#14351f;--dk-on-border:#166534;--dk-glow:none;
  --dk-knob:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-14 -14 28 28'><circle r='10' fill='%23f87171'/></svg>");
  --dk-knob-on:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-14 -14 28 28'><circle r='10' fill='%234ade80'/></svg>")}
.cap{font-size:.85rem;color:#8b93a7;margin-left:14px}
@media (prefers-reduced-motion:reduce){.dk .form-check-input{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-dk-knob uppercase">Accent glow</span><div class="flex flex-wrap items-center gap-[18px]">
<label class="relative inline-block h-[30px] w-[54px] cursor-pointer"><input type="checkbox" role="switch" aria-label="Focus mode" class="peer sr-only" checked><span class="absolute inset-0 rounded-full border border-dk-line bg-dk-track transition-[background-color,border-color,box-shadow] duration-[220ms] peer-checked:border-dk-accent-line peer-checked:bg-dk-accent peer-checked:shadow-[0_0_12px_rgba(59,130,246,.5)] peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-dk-cyan motion-reduce:transition-none"></span><span class="pointer-events-none absolute top-[5px] left-[5px] size-5 rounded-full bg-dk-knob transition-[translate,background-color] duration-[220ms] peer-checked:translate-x-6 peer-checked:bg-white motion-reduce:transition-none"></span></label>
<label class="relative inline-block h-[30px] w-[54px] cursor-pointer"><input type="checkbox" role="switch" aria-label="Do not disturb" class="peer sr-only"><span class="absolute inset-0 rounded-full border border-dk-line bg-dk-track transition-[background-color,border-color,box-shadow] duration-[220ms] peer-checked:border-dk-accent-line peer-checked:bg-dk-accent peer-checked:shadow-[0_0_12px_rgba(59,130,246,.5)] peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-dk-cyan motion-reduce:transition-none"></span><span class="pointer-events-none absolute top-[5px] left-[5px] size-5 rounded-full bg-dk-knob transition-[translate,background-color] duration-[220ms] peer-checked:translate-x-6 peer-checked:bg-white motion-reduce:transition-none"></span></label></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-dk-knob uppercase">Ghost outline</span><div class="flex flex-wrap items-center gap-[18px]">
<label class="relative inline-block h-[30px] w-[54px] cursor-pointer"><input type="checkbox" role="switch" aria-label="Auto-play" class="peer sr-only" checked><span class="absolute inset-0 rounded-full border border-dk-line transition-[border-color] duration-[220ms] peer-checked:border-dk-cyan peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-dk-cyan motion-reduce:transition-none"></span><span class="pointer-events-none absolute top-[5px] left-[5px] size-5 rounded-full bg-dk-knob transition-[translate,background-color] duration-[220ms] peer-checked:translate-x-6 peer-checked:bg-dk-cyan motion-reduce:transition-none"></span></label>
<label class="relative inline-block h-[30px] w-[54px] cursor-pointer"><input type="checkbox" role="switch" aria-label="Captions" class="peer sr-only"><span class="absolute inset-0 rounded-full border border-dk-line transition-[border-color] duration-[220ms] peer-checked:border-dk-cyan peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-dk-cyan motion-reduce:transition-none"></span><span class="pointer-events-none absolute top-[5px] left-[5px] size-5 rounded-full bg-dk-knob transition-[translate,background-color] duration-[220ms] peer-checked:translate-x-6 peer-checked:bg-dk-cyan motion-reduce:transition-none"></span></label></div></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-dk-knob uppercase">Status switch</span><div class="flex flex-wrap items-center gap-[18px]">
<label class="relative inline-block h-[30px] w-[54px] cursor-pointer"><input type="checkbox" role="switch" aria-labelledby="svc-a" class="peer sr-only" checked><span class="absolute inset-0 rounded-full border border-dk-red-line bg-dk-red-track transition-[background-color,border-color] duration-[220ms] peer-checked:border-dk-green-line peer-checked:bg-dk-green-track peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-dk-cyan motion-reduce:transition-none"></span><span class="pointer-events-none absolute top-[5px] left-[5px] size-5 rounded-full bg-dk-red transition-[translate,background-color] duration-[220ms] peer-checked:translate-x-6 peer-checked:bg-dk-green motion-reduce:transition-none"></span></label><span id="svc-a" class="ml-3.5 text-[.85rem] text-dk-knob">Service enabled</span></div>
<div class="mt-3.5 flex flex-wrap items-center gap-[18px]">
<label class="relative inline-block h-[30px] w-[54px] cursor-pointer"><input type="checkbox" role="switch" aria-labelledby="svc-b" class="peer sr-only"><span class="absolute inset-0 rounded-full border border-dk-red-line bg-dk-red-track transition-[background-color,border-color] duration-[220ms] peer-checked:border-dk-green-line peer-checked:bg-dk-green-track peer-focus-visible:outline-2 peer-focus-visible:outline-offset-3 peer-focus-visible:outline-dk-cyan motion-reduce:transition-none"></span><span class="pointer-events-none absolute top-[5px] left-[5px] size-5 rounded-full bg-dk-red transition-[translate,background-color] duration-[220ms] peer-checked:translate-x-6 peer-checked:bg-dk-green motion-reduce:transition-none"></span></label><span id="svc-b" class="ml-3.5 text-[.85rem] text-dk-knob">Service paused</span></div></div>
CSS
@theme {
  --color-dk-track: #232a36;
  --color-dk-line: #56617a;
  --color-dk-knob: #8b93a7;
  --color-dk-accent: #1d4ed8;
  --color-dk-accent-line: #3b82f6;
  --color-dk-cyan: #7dd3fc;
  --color-dk-red-track: #3f1d1d;
  --color-dk-red-line: #7f1d1d;
  --color-dk-red: #f87171;
  --color-dk-green-track: #14351f;
  --color-dk-green-line: #166534;
  --color-dk-green: #4ade80;
}

About this set

Switches designed for dark interfaces, where a switch made for a light theme loses its edges. Accent glow is a dark track with a grey knob that turns blue with a soft glow when on, for primary settings in dark apps. Ghost outline is a transparent track with a thin border that switches to cyan, for dense lists where filled tracks would be heavy. Status switch is red when off and green when on, with a caption beside it such as Service enabled, for monitoring screens and admin panels where the state matters more than the setting. Each switch is a checkbox with role=”switch”, visually hidden inside a label, followed by an element that draws the track and knob, with every colour change handled by :checked. The track borders are lightened to #56617a so the off state still shows its outline on the #0e1116 page. The Bootstrap version restyles .form-switch with CSS variables, and the Tailwind version keeps the palette in theme colours.

What’s included

  • Accent glow, ghost outline and red to green status variants
  • Glow and ghost track borders lightened to reach 3:1 on a near-black page
  • Status switch named by its visible caption through aria-labelledby
  • Cyan focus outline that stands out on dark backgrounds
  • Colours kept in variables or theme tokens for easy re-theming

Accessibility

The glow and ghost switches have an aria-label, and the status switches take their name from the caption beside them through aria-labelledby. All use role="switch", respond to Tab and Space, and show a 2px cyan focus outline. The status variant also moves its knob, so red and green are not the only cue. Transitions are removed under prefers-reduced-motion.

Customise it

Edit the track, border and knob colours in .dk, .gh and .st (plain CSS), the --dk-* variables on the .dk wrapper (Bootstrap) or the --color-dk-* theme colours (Tailwind). The glow is the box-shadow on the checked track.