47 CSS Toggles & Switches45 / 47

Pure CSSMIT licensed

Outline

A pure-stroke toggle — track and thumb are outlines only, no fills. When toggled on, the thumb fills with brand color. Minimalist; pairs with the brutalist demo.

Published Updated

Live Demo
Try it

The code

<label class="tgl-45">
  <input class="tgl-45__input" type="checkbox" checked>
  <span class="tgl-45__track" aria-hidden="true">
    <span class="tgl-45__thumb"></span>
  </span>
  <span class="tgl-45__label">Sync</span>
</label>
.tgl-45 {
  font-family: "Inter", "Segoe UI", system-ui, sans-serif;
  font-size: 14px;
  color: #f0eeff;
  min-height: 100vh;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  padding: 48px 20px;
  background: #14141e;
  box-sizing: border-box;
}

.tgl-45__input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

.tgl-45__track {
  position: relative;
  width: 56px;
  height: 28px;
  background: transparent;
  border: 1px solid #6a6a7a;
  border-radius: 999px;
  transition: border-color 0.2s ease;
  box-sizing: border-box;
}

.tgl-45__thumb {
  position: absolute;
  top: 50%;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid #6a6a7a;
  transform: translateY(-50%);
  transition: left 0.2s cubic-bezier(0.4, 0, 0.2, 1), background 0.2s ease, border-color 0.2s ease;
}

.tgl-45__input:checked ~ .tgl-45__track {
  border-color: #7c6cff;
}

.tgl-45__input:checked ~ .tgl-45__track .tgl-45__thumb {
  /* Inner width: 56 - 2 = 54. Thumb 20. Max left: 54-20-3 = 31. */
  left: 31px;
  background: #7c6cff;
  border-color: #7c6cff;
}

.tgl-45__input:focus-visible ~ .tgl-45__track {
  outline: 2px solid #7c6cff;
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .tgl-45__track,
    .tgl-45__thumb {
    transition: none;
  }
}
/* No JavaScript — hollow 1px-stroke track and thumb; :checked fills the thumb solid and recolors both strokes. */
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: Outline
Source: https://codefronts.com/components/css-toggles-switches/outline/

A pure-stroke toggle — track and thumb are outlines only, no fills. When toggled on, the thumb fills with brand color. Minimalist; pairs with the brutalist demo.
## HTML
```html
<label class="tgl-45">
  <input class="tgl-45__input" type="checkbox" checked>
  <span class="tgl-45__track" aria-hidden="true">
    <span class="tgl-45__thumb"></span>
  </span>
  <span class="tgl-45__label">Sync</span>
</label>
```
## CSS
```css
.tgl-45 {
  font-family: "Inter", "Segoe UI", system-ui, sans-serif;
  font-size: 14px;
  color: #f0eeff;
  min-height: 100vh;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  padding: 48px 20px;
  background: #14141e;
  box-sizing: border-box;
}

.tgl-45__input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

.tgl-45__track {
  position: relative;
  width: 56px;
  height: 28px;
  background: transparent;
  border: 1px solid #6a6a7a;
  border-radius: 999px;
  transition: border-color 0.2s ease;
  box-sizing: border-box;
}

.tgl-45__thumb {
  position: absolute;
  top: 50%;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid #6a6a7a;
  transform: translateY(-50%);
  transition: left 0.2s cubic-bezier(0.4, 0, 0.2, 1), background 0.2s ease, border-color 0.2s ease;
}

.tgl-45__input:checked ~ .tgl-45__track {
  border-color: #7c6cff;
}

.tgl-45__input:checked ~ .tgl-45__track .tgl-45__thumb {
  /* Inner width: 56 - 2 = 54. Thumb 20. Max left: 54-20-3 = 31. */
  left: 31px;
  background: #7c6cff;
  border-color: #7c6cff;
}

.tgl-45__input:focus-visible ~ .tgl-45__track {
  outline: 2px solid #7c6cff;
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .tgl-45__track,
    .tgl-45__thumb {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — hollow 1px-stroke track and thumb; :checked fills the thumb solid and recolors both strokes. */
```

How this works

Stroke-only at rest: a 1px-bordered transparent 56×28 track and a 1px-bordered transparent 20px thumb, both in neutral gray. Checking fills the thumb solid brand-violet and recolors both borders — so state is double-encoded as position AND fill density, which keeps it legible in grayscale.

The slide math is annotated in-source (54px inner − 20px thumb − 3px inset = left:31px). At 1px strokes this is the lightest-touch toggle in the collection; it disappears into text-heavy UI and dense tables where a filled switch would shout.

Make it yours

  • Thicken to 2px strokes for standalone use outside dense UI.
  • Inherit theme color automatically by switching the grays to currentColor (see demo 24).
  • Square the radii for a technical/wireframe look.
  • Fill the track too on :checked if you need a louder on-state.

Gotchas — read before shipping

  • 1px borders can shimmer at fractional zoom levels — 2px is safer if your users zoom.
  • Keep the fill change alongside the position change; outline-only state fails color-independence.
  • Transparent backgrounds mean the surface shows through — check contrast of the gray strokes on your actual background.

Browser support

ChromeSafariFirefoxEdge
1+1+1+1+

Borders + transitions; works everywhere.

Techniques used in this demo

Search CodeFronts

Loading…