Inkwell Ripple
Click triggers a dark ink drop that ripples outward and swallows the X for a moment. The X re-emerges when the ink drains.
Inkwell Ripple the 8th of 18 designs in the 18 CSS Close Buttons 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="ccb-ink" aria-label="Close"><span></span><span></span></button>
.ccb-ink {
width: 40px; height: 40px;
border: none; border-radius: 50%;
background: #f0eeff;
position: relative; cursor: pointer;
overflow: hidden;
}
.ccb-ink span {
position: absolute; top: 50%; left: 50%;
width: 16px; height: 2px;
background: #1a1a28; border-radius: 2px;
z-index: 2;
transition: background 0.3s;
}
.ccb-ink span:nth-child(1) { transform: translate(-50%,-50%) rotate(45deg); }
.ccb-ink span:nth-child(2) { transform: translate(-50%,-50%) rotate(-45deg); }
.ccb-ink-drop {
position: absolute; top: 50%; left: 50%;
width: 8px; height: 8px;
background: #1a1a28; border-radius: 50%;
transform: translate(-50%,-50%) scale(0);
z-index: 1; pointer-events: none;
}
.ccb-ink.is-poured .ccb-ink-drop { animation: ccb-ink-pour 0.9s ease-out forwards; }
.ccb-ink.is-poured span { background: #f0eeff; }
@keyframes ccb-ink-pour {
0% { transform: translate(-50%,-50%) scale(0); }
40% { transform: translate(-50%,-50%) scale(8); }
60% { transform: translate(-50%,-50%) scale(8); }
100% { transform: translate(-50%,-50%) scale(0); }
}
@media (prefers-reduced-motion: reduce) {
.ccb-ink,
.ccb-ink * {
animation: none !important;
}
}
document.querySelectorAll('.ccb-ink').forEach(function(btn) {
btn.addEventListener('click', function() {
if (btn.classList.contains('is-poured')) return;
var drop = document.createElement('span');
drop.className = 'ccb-ink-drop';
btn.appendChild(drop);
btn.classList.add('is-poured');
setTimeout(function() {
btn.classList.remove('is-poured');
drop.remove();
}, 900);
});
});