43 CSS Button Designs

Editorial Ink

A serif, italic button with a thin frame and a hairline underline that draws in on hover. Press and hold and ink floods the button from your finger; release and it retreats.

CSS + JS MIT licensed

Editorial Ink the 11th of 43 designs in the 43 CSS 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

Open in playground

The code

<button class="btn-ink">
  <span class="btn-ink-fill" aria-hidden="true"></span>
  <span class="btn-ink-underline" aria-hidden="true"></span>
  <span class="btn-ink-label">Discover</span>
</button>
.btn-ink {
  position: relative;
  padding: 14px 32px;
  border: none;
  background: transparent;
  color: #f5f0e8;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 16px;
  font-weight: 700;
  font-style: italic;
  letter-spacing: -0.5px;
  cursor: pointer;
  overflow: hidden;
  user-select: none;
}
.btn-ink::before {
  content: '';
  position: absolute;
  inset: 0;
  border: 1.5px solid rgba(245,240,232,0.2);
  transition: border-color 0.3s;
}
.btn-ink:hover::before { border-color: rgba(245,240,232,0.5); }
.btn-ink-underline {
  position: absolute;
  left: 32px;
  right: 32px;
  bottom: 10px;
  height: 1px;
  background: #f5f0e8;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s cubic-bezier(.77,0,.18,1);
}
.btn-ink:hover .btn-ink-underline { transform: scaleX(1); }
.btn-ink-fill {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: #f5f0e8;
  clip-path: circle(0% at var(--ox, 50%) var(--oy, 50%));
}
.btn-ink-label { position: relative; z-index: 1; }
.btn-ink.is-spreading .btn-ink-fill {
  clip-path: circle(150% at var(--ox, 50%) var(--oy, 50%));
  transition: clip-path 0.6s cubic-bezier(.4,0,.2,1);
}
.btn-ink.is-spreading .btn-ink-label { color: #0c0c0f; transition: color 0.2s 0.15s; }
.btn-ink.is-retreating .btn-ink-fill {
  clip-path: circle(0% at var(--ox, 50%) var(--oy, 50%));
  transition: clip-path 0.5s cubic-bezier(.4,0,.2,1) 0.05s;
}
@media (prefers-reduced-motion: reduce) {
  .btn-ink-fill, .btn-ink-underline { transition: none; }
}
document.querySelectorAll('.btn-ink').forEach(function (btn) {
  function retreat() {
    btn.classList.remove('is-spreading');
    btn.classList.add('is-retreating');
    setTimeout(function () { btn.classList.remove('is-retreating'); }, 600);
  }
  btn.addEventListener('mousedown', function (e) {
    var r = btn.getBoundingClientRect();
    btn.style.setProperty('--ox', ((e.clientX - r.left) / r.width * 100) + '%');
    btn.style.setProperty('--oy', ((e.clientY - r.top) / r.height * 100) + '%');
    btn.classList.remove('is-retreating');
    btn.classList.add('is-spreading');
  });
  btn.addEventListener('mouseup', retreat);
  btn.addEventListener('mouseleave', function () {
    if (btn.classList.contains('is-spreading')) retreat();
  });
});

Search CodeFronts

Loading…