{ CF }

20 Pure CSS Toggles & Switches

Waveform Toggle

Static audio bars spring to life with staggered breathing animations when toggled on. The track border and indicator dot pulse with an ice-cyan glow. Audio-visual data physicality — toggle = sound on/off.

Pure CSS MIT licensed

Waveform Toggle the 2nd of 20 designs in the 20 Pure CSS Toggles & Switches collection. The design is implemented in pure CSS — no JavaScript required. Copy the HTML and CSS panels below into your project. Because the demo is pure CSS, it works in any framework or templating engine you happen to use. The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.

Live preview

Open in playground

The code

<label class="tg-wav">
  <input class="tg-wav-input" type="checkbox" checked>
  <span class="tg-wav-body" aria-hidden="true">
    <span class="tg-wav-bars">
      <span></span><span></span><span></span><span></span><span></span>
      <span></span><span></span><span></span><span></span>
    </span>
    <span class="tg-wav-text">Microphone</span>
    <span class="tg-wav-dot"></span>
  </span>
</label>
.tg-wav {
  --tg-wav-rim: #14141e;
  --tg-wav-wire: #1e1e2e;
  --tg-wav-fog: #3a3a52;
  --tg-wav-ash: #7a7a98;
  --tg-wav-ice: #00e5ff;
  display: inline-block;
  cursor: pointer;
  font-family: "Inter", "Segoe UI", system-ui, sans-serif;
  font-size: 14px;
  user-select: none;
}
.tg-wav-input {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}
.tg-wav-body {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 12px;
  background: var(--tg-wav-rim);
  border: 1px solid var(--tg-wav-wire);
  min-width: 240px;
  transition: border-color 0.3s ease, background 0.3s ease;
}
.tg-wav-bars {
  display: flex;
  align-items: center;
  gap: 2.5px;
  height: 28px;
}
.tg-wav-bars span {
  display: block;
  width: 3px;
  border-radius: 2px;
  background: var(--tg-wav-fog);
  transition: background 0.3s ease, height 0.3s ease;
}
/* Static heights (off state) — each bar has its own resting height. */
.tg-wav-bars span:nth-child(1) { height: 8px; }
.tg-wav-bars span:nth-child(2) { height: 14px; }
.tg-wav-bars span:nth-child(3) { height: 20px; }
.tg-wav-bars span:nth-child(4) { height: 26px; }
.tg-wav-bars span:nth-child(5) { height: 18px; }
.tg-wav-bars span:nth-child(6) { height: 24px; }
.tg-wav-bars span:nth-child(7) { height: 12px; }
.tg-wav-bars span:nth-child(8) { height: 20px; }
.tg-wav-bars span:nth-child(9) { height: 8px; }
.tg-wav-text {
  font-size: 13px;
  color: var(--tg-wav-ash);
  transition: color 0.3s ease;
}
.tg-wav-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--tg-wav-wire);
  margin-left: auto;
  flex-shrink: 0;
  transition: background 0.3s ease, box-shadow 0.3s ease;
}
/* Each bar oscillates between its resting height and ~30% of it. The
   varied durations + delays keep the pattern from looking robotic. */
@keyframes tg-wav-breathe {
  0%, 100% { height: var(--tg-wav-h); }
  50%      { height: calc(var(--tg-wav-h) * 0.3 + 4px); }
}
.tg-wav-input:checked ~ .tg-wav-body {
  border-color: rgba(0,229,255,0.3);
  background: rgba(0,229,255,0.04);
}
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span {
  background: var(--tg-wav-ice);
}
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(1) { animation: tg-wav-breathe 0.9s ease-in-out infinite;       --tg-wav-h: 8px;  }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(2) { animation: tg-wav-breathe 0.7s ease-in-out 0.1s infinite;  --tg-wav-h: 14px; }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(3) { animation: tg-wav-breathe 1.1s ease-in-out 0.2s infinite;  --tg-wav-h: 20px; }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(4) { animation: tg-wav-breathe 0.8s ease-in-out 0.05s infinite; --tg-wav-h: 26px; }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(5) { animation: tg-wav-breathe 1.0s ease-in-out 0.15s infinite; --tg-wav-h: 18px; }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(6) { animation: tg-wav-breathe 0.6s ease-in-out 0.25s infinite; --tg-wav-h: 24px; }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(7) { animation: tg-wav-breathe 0.9s ease-in-out 0.08s infinite; --tg-wav-h: 12px; }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(8) { animation: tg-wav-breathe 1.2s ease-in-out 0.18s infinite; --tg-wav-h: 20px; }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span:nth-child(9) { animation: tg-wav-breathe 0.75s ease-in-out 0.3s infinite; --tg-wav-h: 8px;  }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-text { color: var(--tg-wav-ice); }
.tg-wav-input:checked ~ .tg-wav-body .tg-wav-dot {
  background: var(--tg-wav-ice);
  box-shadow: 0 0 8px var(--tg-wav-ice);
}
.tg-wav-input:focus-visible ~ .tg-wav-body {
  outline: 2px solid var(--tg-wav-ice);
  outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
  .tg-wav-body,
  .tg-wav-bars span,
  .tg-wav-dot,
  .tg-wav-text { transition: none; }
  .tg-wav-input:checked ~ .tg-wav-body .tg-wav-bars span { animation: none; }
}

Search CodeFronts

Loading…