18 CSS Play / Pause Button Designs
Ripple on Click
Material-style ink ripple that emanates from the click point on every press. Click feedback that reads as tactile instantly — a subtle but premium detail.
Ripple on Click the 16th of 18 designs in the 18 CSS Play / Pause Button Designs collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
The code
<button
class="pp-ripple"
type="button"
aria-pressed="false"
aria-label="Play"
data-pp
data-pp-ripple
>
<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
<path class="pp-ripple-icon" d="M8 5 L8 19 L19 12 Z" fill="currentColor" />
</svg>
</button> .pp-ripple {
position: relative;
width: 56px;
height: 56px;
border: 0;
padding: 0;
background: linear-gradient(135deg, #a78bfa, #8b5cf6);
border-radius: 50%;
color: white;
cursor: pointer;
display: grid;
place-items: center;
overflow: hidden;
box-shadow: 0 6px 16px -4px rgba(139, 92, 246, 0.55);
transition:
transform 0.15s,
box-shadow 0.25s;
}
.pp-ripple:hover {
transform: translateY(-1px);
box-shadow: 0 10px 22px -4px rgba(139, 92, 246, 0.7);
}
.pp-ripple:focus-visible {
outline: 3px solid rgba(139, 92, 246, 0.5);
outline-offset: 3px;
}
.pp-ripple-wave {
position: absolute;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: translate(-50%, -50%);
pointer-events: none;
animation: ppRippleOut 0.6s ease-out forwards;
}
@keyframes ppRippleOut {
to {
width: 140px;
height: 140px;
opacity: 0;
}
}
.pp-ripple-icon {
position: relative;
z-index: 1;
transition: d 0.3s cubic-bezier(0.5, 0, 0.3, 1.2);
}
.pp-ripple[aria-pressed="true"] .pp-ripple-icon {
d: path("M7 5 H10 V19 H7 Z M14 5 H17 V19 H14 Z");
}
/* ── Drop-in JS for the ripple ──
document.querySelectorAll('[data-pp-ripple]').forEach(btn => {
btn.addEventListener('pointerdown', e => {
const r = btn.getBoundingClientRect();
const wave = document.createElement('span');
wave.className = 'pp-ripple-wave';
wave.style.left = (e.clientX - r.left) + 'px';
wave.style.top = (e.clientY - r.top) + 'px';
btn.appendChild(wave);
wave.addEventListener('animationend', () => wave.remove());
});
});
*/
@media (prefers-reduced-motion: reduce) {
.pp-ripple,
.pp-ripple * {
animation: none !important;
}
}
// ── 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');
});
}); More from 18 CSS Play / Pause Button Designs
Skip HubLiquid WavePulse HaloToggle PillNeon ArcadeMinimal OutlineGradient DiscVoice MemoFloating FABProgress RingMagnetic HoverLiquid Drop
View the full collection →