21 CSS Button Hover Effects
100% original, hand-coded from scratch. Pure CSS where possible. Copy any snippet and drop it straight into your project — no libraries, no frameworks, no dependencies.
.btn {
position: relative;
color: #7c6cff; background: transparent;
border: 2px solid #7c6cff; overflow: hidden;
transition: color 0.4s; z-index: 0;
}
.btn::before {
content: '';
position: absolute;
bottom: -2px; left: -2px; right: -2px; height: 0;
background: #7c6cff;
transition: height 0.45s cubic-bezier(0.23,1,0.32,1);
z-index: -1;
}
.btn:hover { color: #fff; }
.btn:hover::before { height: calc(100% + 4px); } <button class="btn">Hover Me</button>
.btn-wrap {
position: relative; padding: 2px;
border: none; background: none; cursor: pointer;
border-radius: 8px; display: inline-block;
}
.btn-wrap::before {
content: '';
position: absolute; inset: -2px; border-radius: 10px;
background: conic-gradient(
#7c6cff, #ff6c8a, #3de8f5, #1ed98a, #7c6cff);
opacity: 0; transition: opacity .3s;
animation: border-spin 3s linear infinite paused;
}
.btn-wrap:hover::before {
opacity: 1;
animation-play-state: running;
}
@keyframes border-spin { to { transform: rotate(360deg); } }
.btn-inner {
display: block; padding: 12px 32px; border-radius: 7px;
background: #111118; color: #fff;
font-size: 14px; font-weight: 600;
transition: transform .3s, box-shadow .3s;
}
.btn-wrap:hover .btn-inner {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(124,108,255,.3);
} <button class="btn-wrap"> <span class="btn-inner">Magnetic Border</span> </button>
.btn { position: relative; overflow: hidden; }
.btn::before, .btn::after {
content: attr(data-text);
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
opacity: 0;
}
.btn::before {
background: #ff6c8a; color: #fff;
clip-path: polygon(0 30%,100% 30%,100% 50%,0 50%);
}
.btn::after {
background: #7c6cff; color: #fff;
clip-path: polygon(0 55%,100% 55%,100% 75%,0 75%);
}
.btn:hover::before { animation: glitch1 0.3s steps(1) infinite; }
.btn:hover::after { animation: glitch2 0.3s steps(1) infinite 0.05s; }
@keyframes glitch1 { 50% { opacity:1; transform:translateX(-4px); } }
@keyframes glitch2 { 50% { opacity:1; transform:translateX(4px); } } <button class="btn" data-text="GLITCH">GLITCH</button>
.btn {
color: #2ecc8a; border: 2px solid #2ecc8a;
background: transparent; text-transform: uppercase;
letter-spacing: 0.08em; transition: all 0.3s;
}
.btn:hover {
background: rgba(46,204,138,0.1);
animation: neonpulse 1.2s ease-in-out infinite;
}
@keyframes neonpulse {
0%,100% { box-shadow: 0 0 12px #2ecc8a, 0 0 28px rgba(46,204,138,0.35); }
50% { box-shadow: 0 0 20px #2ecc8a, 0 0 50px rgba(46,204,138,0.55),
0 0 80px rgba(46,204,138,0.2); }
} <button class="btn">NEON</button>
.btn {
position: relative; overflow: hidden;
border: 2px solid rgba(255,255,255,.15);
background: #17171f; color: #fff;
font-size: 14px; font-weight: 600; cursor: pointer;
}
.btn-top {
display: block;
clip-path: polygon(0 0, 100% 0, 100% 50%, 0 50%);
transition: transform .35s cubic-bezier(.23,1,.32,1);
}
.btn-bottom {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
color: #7c6cff;
clip-path: polygon(0 50%, 100% 50%, 100% 100%, 0 100%);
transform: translateY(50%);
transition: transform .35s cubic-bezier(.23,1,.32,1);
}
.btn:hover .btn-top { transform: translateY(-50%); }
.btn:hover .btn-bottom { transform: translateY(0); } <button class="btn"> <span class="btn-top">Split Reveal</span> <span class="btn-bottom" aria-hidden="true">Split Reveal</span> </button>
.btn {
position: relative; overflow: hidden; z-index: 0;
border: 1px solid rgba(255,255,255,0.18);
transition: color 0.3s, border-color 0.3s;
}
.btn::before {
content: '';
position: absolute; top: 0; left: -105%; width: 100%; height: 100%;
background: linear-gradient(135deg, #7c6cff 0%, #ff6c8a 100%);
transition: left 0.4s cubic-bezier(0.23,1,0.32,1);
z-index: -1; transform: skewX(-15deg);
}
.btn:hover::before { left: 0; }
.btn:hover { border-color: transparent; } <button class="btn">Hover Me</button>
.btn {
background: #7c6cff; color: #fff; border: none;
box-shadow: 0 4px 0 #4a3aad; transform: translateY(0);
transition: transform 0.12s cubic-bezier(0.34,1.56,0.64,1),
box-shadow 0.12s;
}
.btn:hover { transform: translateY(-4px); box-shadow: 0 8px 0 #4a3aad; }
.btn:active { transform: translateY(2px); box-shadow: 0 2px 0 #4a3aad; } <button class="btn">Click Me</button>
.btn { position: relative; background: transparent; border: none; }
.btn::after {
content: '';
position: absolute; bottom: 5px;
left: 50%; right: 50%; height: 2px;
background: linear-gradient(90deg, #7c6cff, #ff6c8a);
transition: left 0.4s cubic-bezier(0.23,1,0.32,1),
right 0.4s cubic-bezier(0.23,1,0.32,1);
border-radius: 2px;
}
.btn:hover::after { left: 32px; right: 32px; } <button class="btn">Hover Me</button>
.wrapper { perspective: 600px; display: inline-block; }
.btn {
position: relative; display: block;
transform-style: preserve-3d;
transition: transform 0.5s cubic-bezier(0.23,1,0.32,1);
background: transparent; border: none;
}
.front, .back {
display: block; padding: 12px 32px;
border-radius: 8px; backface-visibility: hidden;
}
.front { background: #ff6c8a; color: #fff; }
.back {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
background: #7c6cff; color: #fff; transform: rotateX(180deg);
}
.wrapper:hover .btn { transform: rotateX(180deg); } <div class="wrapper">
<button class="btn">
<span class="front">Hover Me</span>
<span class="back">Flipped!</span>
</button>
</div> .btn { position: relative; overflow: visible; }
.dot {
position: absolute; width: 6px; height: 6px; border-radius: 50%;
top: 50%; left: 50%; margin: -3px 0 0 -3px; opacity: 0;
}
.btn:hover .dot { animation: burst 0.65s ease-out forwards; }
.dot:nth-child(1){background:#7c6cff;--dx:-52px;--dy:-42px;}
.dot:nth-child(2){background:#ff6c8a;--dx:52px;--dy:-42px;animation-delay:0.05s;}
.dot:nth-child(3){background:#2ecc8a;--dx:-62px;--dy:2px;animation-delay:0.02s;}
.dot:nth-child(4){background:#f5a623;--dx:62px;--dy:2px;animation-delay:0.07s;}
.dot:nth-child(5){background:#7c6cff;--dx:-36px;--dy:46px;animation-delay:0.04s;}
.dot:nth-child(6){background:#ff6c8a;--dx:36px;--dy:46px;animation-delay:0.03s;}
@keyframes burst {
0% { opacity: 1; }
100% { transform:translate(var(--dx),var(--dy)) scale(0); opacity:0; }
} <button class="btn"> <span>Burst!</span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </button>
.btn { position: relative; overflow: hidden; }
.btn::before, .btn::after {
content: ''; position: absolute; top: 0;
width: 50%; height: 100%; background: #7c6cff;
transition: transform 0.4s cubic-bezier(0.23,1,0.32,1);
}
.btn::before { left: 0; transform: translateX(-101%); }
.btn::after { right: 0; transform: translateX(101%); }
.btn .label { position: relative; z-index: 1; transition: color 0.4s; }
.btn:hover::before { transform: translateX(0); }
.btn:hover::after { transform: translateX(0); }
.btn:hover .label { color: #fff; } <button class="btn"><span class="label">Slide Doors</span></button>
.btn { font-family: monospace; }
.cursor {
display: inline-block; width: 2px; height: 13px;
background: currentColor; margin-left: 2px;
vertical-align: middle; opacity: 0;
}
.btn:hover .cursor { animation: blink 0.6s step-end infinite; }
@keyframes blink { 0%,100%{ opacity:1; } 50%{ opacity:0; } } <button class="btn"> <span class="text">console.log()</span> <span class="cursor"></span> </button>
const btn = document.querySelector('.btn');
const txt = btn.querySelector('.text');
const full = txt.textContent;
btn.addEventListener('mouseenter', () => {
let i = full.length;
const erase = setInterval(() => {
txt.textContent = full.slice(0, --i);
if (i === 0) { clearInterval(erase); retype(); }
}, 55);
function retype() {
let j = 0;
const write = setInterval(() => {
txt.textContent = full.slice(0, ++j);
if (j === full.length) clearInterval(write);
}, 55);
}
}); .btn {
color: #0a0a0f; background: #f0eeff;
border: 2px solid #0a0a0f;
text-transform: uppercase; letter-spacing: 0.07em; font-weight: 700;
box-shadow: 4px 4px 0 #0a0a0f;
transition: box-shadow 0.15s, transform 0.15s;
}
.btn:hover { box-shadow: 8px 8px 0 #7c6cff; transform: translate(-2px,-2px); }
.btn:active { box-shadow: 2px 2px 0 #0a0a0f; transform: translate(2px,2px); } <button class="btn">CLICK ME</button>
.btn { position: relative; overflow: hidden; }
.ripple {
position: absolute; border-radius: 50%;
background: rgba(255,255,255,0.32);
transform: scale(0); animation: rwave 0.7s linear;
pointer-events: none;
}
@keyframes rwave { to { transform: scale(4); opacity: 0; } } <button class="btn">Click Me</button>
btn.addEventListener('click', (e) => {
const c = document.createElement('span');
const d = Math.max(btn.offsetWidth, btn.offsetHeight);
const r = btn.getBoundingClientRect();
c.className = 'ripple';
c.style.cssText = `width:${d}px;height:${d}px;
left:${e.clientX - r.left - d/2}px;
top:${e.clientY - r.top - d/2}px`;
btn.appendChild(c);
c.addEventListener('animationend', () => c.remove());
}); .btn { position: relative; border: none; }
.btn::before {
content: '';
position: absolute; inset: 0;
border-radius: 8px; padding: 2px;
background: conic-gradient(#7c6cff,#ff6c8a,#2ecc8a,#f5a623,#7c6cff);
-webkit-mask: linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor; mask-composite: exclude;
opacity: 0; transition: opacity 0.3s;
}
.btn:hover::before { opacity: 1; animation: spin 2s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } } <button class="btn">Hover Me</button>
.btn {
background: #ff6c8a; color: #fff; border: none;
transition: border-radius 0.4s cubic-bezier(0.23,1,0.32,1),
background 0.3s, transform 0.2s;
}
.btn:hover {
border-radius: 50px;
background: #7c6cff;
transform: scaleX(0.92);
} <button class="btn">Hover Me</button>
.btn {
position: relative; overflow: hidden;
transition: padding 0.35s cubic-bezier(0.23,1,0.32,1);
}
.icon {
position: absolute; right: -24px; top: 50%;
transform: translateY(-50%); opacity: 0;
transition: right 0.35s cubic-bezier(0.23,1,0.32,1), opacity 0.3s;
}
.btn:hover { padding-right: 52px; }
.btn:hover .icon { right: 16px; opacity: 1; } <button class="btn">Get Started<span class="icon">→</span></button>
.btn { position: relative; overflow: hidden; }
.glow {
position: absolute; width: 100px; height: 100px; border-radius: 50%;
background: radial-gradient(circle, rgba(124,108,255,0.28) 0%, transparent 70%);
pointer-events: none; opacity: 0; transition: opacity 0.3s;
transform: translate(-50%, -50%);
}
.btn:hover .glow { opacity: 1; } <button class="btn"> <span class="glow"></span> Hover Me </button>
btn.addEventListener('mousemove', (e) => {
const r = btn.getBoundingClientRect();
glow.style.left = (e.clientX - r.left) + 'px';
glow.style.top = (e.clientY - r.top) + 'px';
}); .btn { position: relative; overflow: hidden; }
.seg {
position: absolute; top: 0; width: 20%; height: 100%;
background: #7c6cff; transform: scaleY(0); transform-origin: bottom;
transition: transform 0.3s cubic-bezier(0.23,1,0.32,1);
}
.seg:nth-child(1){ left: 0%; transition-delay: 0s; }
.seg:nth-child(2){ left: 20%; transition-delay: 0.04s; }
.seg:nth-child(3){ left: 40%; transition-delay: 0.08s; }
.seg:nth-child(4){ left: 60%; transition-delay: 0.12s; }
.seg:nth-child(5){ left: 80%; transition-delay: 0.16s; }
.label { position: relative; z-index: 1; transition: color 0.4s 0.1s; }
.btn:hover .seg { transform: scaleY(1); }
.btn:hover .label { color: #fff; } <button class="btn"> <span class="seg"></span> <span class="seg"></span> <span class="seg"></span> <span class="seg"></span> <span class="seg"></span> <span class="label">Hover Me</span> </button>
.btn {
position: relative; overflow: hidden;
padding: 12px 40px; border-radius: 50px; border: none;
background: #f5a84a; color: #0a0a0f;
font-size: 14px; font-weight: 700; cursor: pointer;
transition: transform .3s, box-shadow .3s;
}
.btn:hover {
transform: scale(1.06);
box-shadow: 0 8px 24px rgba(245,168,74,.5);
}
.btn-rays {
position: absolute; inset: -40%;
background: conic-gradient(
transparent 0deg, rgba(255,255,255,.2) 10deg,
transparent 20deg, rgba(255,255,255,.2) 30deg,
transparent 40deg, rgba(255,255,255,.2) 50deg,
transparent 60deg, rgba(255,255,255,.2) 70deg,
transparent 80deg, rgba(255,255,255,.2) 90deg,
transparent 100deg, rgba(255,255,255,.2) 110deg,
transparent 120deg, rgba(255,255,255,.2) 130deg,
transparent 140deg, rgba(255,255,255,.2) 150deg,
transparent 160deg, rgba(255,255,255,.2) 170deg,
transparent 180deg, rgba(255,255,255,.2) 190deg,
transparent 200deg, rgba(255,255,255,.2) 210deg,
transparent 220deg, rgba(255,255,255,.2) 230deg,
transparent 240deg, rgba(255,255,255,.2) 250deg,
transparent 260deg, rgba(255,255,255,.2) 270deg,
transparent 280deg, rgba(255,255,255,.2) 290deg,
transparent 300deg, rgba(255,255,255,.2) 310deg,
transparent 320deg, rgba(255,255,255,.2) 330deg,
transparent 340deg, rgba(255,255,255,.2) 350deg);
opacity: 0; transition: opacity .3s;
}
.btn:hover .btn-rays {
opacity: 1;
animation: sunburst-spin 6s linear infinite;
}
@keyframes sunburst-spin { to { transform: rotate(360deg); } }
.btn-label { position: relative; z-index: 1; } <button class="btn"> <div class="btn-rays"></div> <span class="btn-label">Sunburst</span> </button>
.btn {
background: #7c6cff; color: #fff; border: none;
border-radius: 8px;
box-shadow:
0 6px 0 #4a3ab0,
0 8px 16px rgba(74,58,176,.4);
transition:
transform .15s cubic-bezier(.23,1,.32,1),
box-shadow .15s cubic-bezier(.23,1,.32,1);
}
.btn:hover {
transform: translateY(-3px);
box-shadow:
0 9px 0 #4a3ab0,
0 14px 24px rgba(74,58,176,.45);
}
.btn:active {
transform: translateY(4px);
box-shadow:
0 2px 0 #4a3ab0,
0 4px 8px rgba(74,58,176,.3);
} <button class="btn">3D Press</button>
Related collections
27 CSS Card Hover Effects
27 original CSS card hover effects — elastic lift, 3D tilt, holographic shimmer, spotlight, aurora and more. Pure CSS with 4 vanilla JS enhancements. Copy-paste ready.
20 CSS Link Hover Effect Designs
20 free CSS link hover effects — animated underlines, glitches, neon flickers, marker highlights, 3D flips and more, with copy-paste code.
15 Pure CSS Loading Animations
15 hand-coded CSS loading animations — DNA helix, liquid blob, orbit system, signal bars, clock sweep, bouncing chain, neon ring draw, pixel dissolve, hourglass flip, heartbeat line, House Unlock, Listing Card Skeleton, semantic Progress Path, Heat-Map Compass and a Floor-by-Floor building loader. Pure CSS, accessible, drop-in ready.