Back to CSS Play/Pause Buttons Cassette Tape Light JS
Share
HTML
<button class="pp-tape" type="button" aria-pressed="false" aria-label="Play" data-pp>
  <span class="pp-tape-body" aria-hidden="true">
    <span class="pp-tape-reel pp-tape-reel-l">
      <span class="pp-tape-spoke"></span>
      <span class="pp-tape-spoke"></span>
      <span class="pp-tape-spoke"></span>
    </span>
    <span class="pp-tape-reel pp-tape-reel-r">
      <span class="pp-tape-spoke"></span>
      <span class="pp-tape-spoke"></span>
      <span class="pp-tape-spoke"></span>
    </span>
    <span class="pp-tape-label">SIDE A</span>
  </span>
</button>
CSS
.pp-tape {
  width: 110px;
  height: 70px;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  border-radius: 10px;
}
.pp-tape:focus-visible {
  outline: 3px solid rgba(180, 83, 9, 0.5);
  outline-offset: 3px;
}
.pp-tape-body {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  background: linear-gradient(180deg, #92400e 0%, #78350f 100%);
  border-radius: 8px;
  border: 1px solid rgba(0, 0, 0, 0.4);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    inset 0 -2px 6px rgba(0, 0, 0, 0.35),
    0 8px 20px -6px rgba(0, 0, 0, 0.55);
}
.pp-tape-reel {
  position: absolute;
  top: 50%;
  width: 32px;
  height: 32px;
  margin-top: -16px;
  border-radius: 50%;
  background: radial-gradient(circle, #1f1f1f 28%, #444 30%, #444 60%, #1f1f1f 62%);
  border: 1.5px solid #1a1a1a;
  display: grid;
  place-items: center;
}
.pp-tape-reel-l {
  left: 12px;
}
.pp-tape-reel-r {
  right: 12px;
}
.pp-tape-spoke {
  position: absolute;
  width: 2px;
  height: 100%;
  background: #888;
  border-radius: 1px;
}
.pp-tape-spoke:nth-child(2) {
  transform: rotate(60deg);
}
.pp-tape-spoke:nth-child(3) {
  transform: rotate(-60deg);
}
.pp-tape[aria-pressed="true"] .pp-tape-reel {
  animation: ppTapeSpin 1.6s linear infinite;
}
@keyframes ppTapeSpin {
  to {
    transform: rotate(360deg);
  }
}
.pp-tape-label {
  position: absolute;
  left: 50%;
  bottom: 6px;
  transform: translateX(-50%);
  font-family: "JetBrains Mono", monospace;
  font-size: 8px;
  font-weight: 700;
  color: #fed7aa;
  letter-spacing: 0.2em;
  background: rgba(0, 0, 0, 0.4);
  padding: 1px 8px;
  border-radius: 2px;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .pp-tape,
  .pp-tape * {
    animation: none !important;
  }
}
JS
// ── Drop this on every page where you render a play/pause button ──
// Toggles aria-pressed + aria-label on click. The CSS handles all visuals.
document.querySelectorAll('[data-pp]').forEach(function (btn) {
  btn.addEventListener('click', function () {
    var playing = btn.getAttribute('aria-pressed') === 'true';
    btn.setAttribute('aria-pressed', String(!playing));
    btn.setAttribute('aria-label', !playing ? 'Pause' : 'Play');
  });
});