Effets CSS Boutons & Hover
Boutons animés, interactifs et modernes pour vos interfaces.
Mis à jour le
Comment animer un bouton pour qu'il donne envie de cliquer ?
Un bouton animé utilise le mouvement pour montrer qu'il est cliquable et pour confirmer le clic. Les techniques fiables sont les transitions CSS sur transform, box-shadow et background-position, les pseudo-éléments pour les reflets et les bordures, et quelques lignes de JavaScript pour l'onde au clic ou l'attraction magnétique vers le pointeur.
Comment choisir, performances et accessibilité
- Pour l'appel à l'action principal, préférez un seul signal clair, comme Glow Pulse, Shine Sweep ou Gradient Shift, plutôt que plusieurs à la fois ; gardez Magnetic Button ou Liquid Fill pour les sites créatifs.
- Faites court (environ 150 à 300 ms), donnez le même retour aux utilisateurs du clavier avec
:focus-visibleet n'exprimez jamais un état par le seul mouvement. - Gradient Shift et Ripple Effect sont gratuits avec leur code complet ; les autres boutons de la page sont inclus dans Premium.
Gradient Shift
Le gradient se déplace au survol créant un effet de mouvement fluide.
Page de l’effet →Voir le code source
<div class="demo-preview">
<button class="demo-btn btn-gradient-shift">Gradient Shift</button>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.demo-btn {
padding: 16px 32px;
font-size: 1rem;
font-weight: 600;
border: none;
border-radius: 12px;
cursor: pointer;
font-family: inherit;
position: relative;
overflow: hidden;
}
.btn-gradient-shift {
background: linear-gradient(135deg, #6366f1, #8b5cf6, #d946ef);
background-size: 200% 200%;
color: white;
transition: all 0.5s ease;
}
.btn-gradient-shift:hover {
background-position: 100% 100%;
transform: translateY(-3px);
box-shadow: 0 15px 35px rgba(99, 102, 241, 0.4);
}/* CSS pur — aucun JavaScript requis */Voir le code source
<div class="demo-preview">
<button class="demo-btn btn-ripple" onclick="createRipple(event)">Ripple Effect</button>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.demo-btn {
padding: 16px 32px;
font-size: 1rem;
font-weight: 600;
border: none;
border-radius: 12px;
cursor: pointer;
font-family: inherit;
position: relative;
overflow: hidden;
}
.btn-ripple {
background: #6366f1;
color: white;
transition: all 0.3s ease;
}
.btn-ripple .ripple-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: scale(0);
animation: ripple 0.6s linear;
pointer-events: none;
}
@keyframes ripple {
to {
transform: scale(4);
opacity: 0;
}
}(function() {
// Ripple Effect
function createRipple(e) {
const btn = e.currentTarget;
const rect = btn.getBoundingClientRect();
const circle = document.createElement('span');
const diameter = Math.max(btn.clientWidth, btn.clientHeight);
const radius = diameter / 2;
circle.style.width = circle.style.height = `${diameter}px`;
circle.style.left = `${e.clientX - rect.left - radius}px`;
circle.style.top = `${e.clientY - rect.top - radius}px`;
circle.classList.add('ripple-circle');
const ripple = btn.querySelector('.ripple-circle');
if (ripple) ripple.remove();
circle.addEventListener('animationend', () => circle.remove());
btn.appendChild(circle);
}
window.createRipple = createRipple;
})();Fill Slide Up
Remplissage de bas en haut avec courbe ease-quart. Inspiré de Daily Delights.
Page de l’effet →Press Feedback
Micro-interaction tactile avec compression au clic pour un feedback utilisateur optimal.
Page de l’effet →Bezier Elastic
Animation avec courbe cubic-bezier élastique pour un effet rebond naturel.
Page de l’effet →CTA Gradient Border
Bordure gradient animée parfaite pour les call-to-action premium. Inspiré de CTA Gallery.
Page de l’effet →Neon Pulse
Box-shadow neon pulsant avec animation continue pour un effet cyberpunk intense.
Page de l’effet →Gummy Elastic
Effet élastique au clic avec courbe cubic-bezier pour un rebond naturel et fun.
Page de l’effet →Glitch Effect
Effet glitch avec pseudo-éléments et décalage chromatique style rétro gaming.
Page de l’effet →RGB Split
Séparation des canaux RGB au hover pour un effet aberration chromatique.
Page de l’effet →Gradient Border Animated
Bordure dégradée tournante avec animation continue utilisant @property CSS.
Page de l’effet →Slide Reveal Icon
Icône qui apparaît progressivement au hover avec expansion fluide.
Page de l’effet →Casque Studio Pro
149 €Magnetic Add to Cart
Micro-interaction d'ajout au panier : bouton magnétique, burst de particules, vol de l'article jusqu'au panier et compteur animé. La satisfaction qui pousse à l'achat.
💡 Idéal pour : e-commerce, page produit, Black Friday, panier, tout CTA « ajouter / sauvegarder / aimer ».
Voir le code source
<div class="demo-preview">
<div class="atc-scene" data-atc data-icon="🎧">
<div class="atc-cart-wrap">
<span class="atc-cart" aria-hidden="true">🛒</span>
<span class="atc-count" data-atc-count aria-label="Articles dans le panier">0</span>
</div>
<div class="atc-product">
<div class="atc-thumb" aria-hidden="true">🎧</div>
<div class="atc-info">
<h3>Casque Studio Pro</h3>
<span class="atc-price">149 €</span>
<div><button type="button" class="atc-btn" data-atc-btn>Ajouter au panier</button></div>
</div>
</div>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 200px;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.atc-scene {
position: relative;
width: 100%;
min-height: 240px;
border-radius: 14px;
overflow: hidden;
isolation: isolate;
background: radial-gradient(circle at 70% 20%, #1c1330, #0b0a14 75%);
padding: 22px;
box-sizing: border-box;
}
.atc-cart {
position: absolute;
top: 16px;
right: 18px;
font-size: 26px;
line-height: 1;
}
.atc-count {
position: absolute;
top: -8px;
right: -10px;
min-width: 18px;
height: 18px;
padding: 0 4px;
box-sizing: border-box;
background: #ef4444;
color: #fff;
font-size: 11px;
font-weight: 700;
border-radius: 999px;
display: grid;
place-items: center;
}
.atc-cart-wrap {
position: absolute;
top: 16px;
right: 18px;
}
.atc-product {
display: flex;
align-items: center;
gap: 16px;
max-width: 360px;
margin-top: 34px;
}
.atc-thumb {
width: 78px;
height: 78px;
border-radius: 14px;
background: linear-gradient(135deg, #6366f1, #d946ef);
display: grid;
place-items: center;
font-size: 38px;
flex: none;
}
.atc-info h3 {
margin: 0 0 2px;
font-size: 16px;
color: #fff;
}
.atc-info .atc-price {
color: #a78bfa;
font-weight: 700;
}
.atc-btn {
margin-top: 14px;
border: 0;
cursor: pointer;
background: linear-gradient(135deg, #f59e0b, #ef4444);
color: #fff;
font-weight: 700;
font-size: 14px;
padding: 11px 20px;
border-radius: 10px;
transition: transform .12s, box-shadow .2s;
will-change: transform;
}
.atc-btn:hover {
box-shadow: 0 8px 24px rgba(245, 158, 11, .35);
}
.atc-btn:active {
transform: scale(.96);
}
.atc-particle {
position: absolute;
width: 7px;
height: 7px;
border-radius: 50%;
background: #fbbf24;
pointer-events: none;
transform: translate(-50%, -50%);
animation: atc-burst .6s ease-out forwards;
z-index: 4;
}
.atc-fly {
position: absolute;
font-size: 30px;
pointer-events: none;
z-index: 5;
transform: translate(-50%, -50%);
}
.atc-pop {
animation: atc-pop .32s ease;
}
.atc-bump {
animation: atc-bump .4s ease;
}
@media (prefers-reduced-motion: reduce) {
.atc-btn {
transition: none;
}
}
@keyframes atc-burst {
to {
transform: translate(-50%, -50%) translate(var(--dx), var(--dy)) scale(.2);
opacity: 0;
}
}
@keyframes atc-pop {
0% {
transform: scale(1);
}
40% {
transform: scale(1.6);
}
100% {
transform: scale(1);
}
}
@keyframes atc-bump {
0%,
100% {
transform: translateY(0);
}
30% {
transform: translateY(-7px) rotate(-8deg);
}
}(function() {
(function() {
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
document.querySelectorAll('[data-atc]').forEach(function(scene) {
var btn = scene.querySelector('[data-atc-btn]');
var cart = scene.querySelector('.atc-cart');
var countEl = scene.querySelector('[data-atc-count]');
var icon = scene.dataset.icon || '🛍️';
var n = 0;
if (!reduceMotion) {
btn.addEventListener('pointermove', function(e) {
var r = btn.getBoundingClientRect();
var mx = e.clientX - (r.left + r.width / 2),
my = e.clientY - (r.top + r.height / 2);
btn.style.transform = 'translate(' + (mx * 0.22) + 'px,' + (my * 0.35) + 'px)';
});
btn.addEventListener('pointerleave', function() {
btn.style.transform = '';
});
}
function bump() {
n++;
countEl.textContent = n;
countEl.classList.remove('atc-pop');
void countEl.offsetWidth;
countEl.classList.add('atc-pop');
cart.classList.remove('atc-bump');
void cart.offsetWidth;
cart.classList.add('atc-bump');
}
btn.addEventListener('click', function() {
if (reduceMotion) {
bump();
return;
}
var br = btn.getBoundingClientRect(),
cr = cart.getBoundingClientRect(),
sr = scene.getBoundingClientRect();
var x0 = br.left + br.width / 2 - sr.left,
y0 = br.top + br.height / 2 - sr.top;
var x1 = cr.left + cr.width / 2 - sr.left,
y1 = cr.top + cr.height / 2 - sr.top;
for (var i = 0; i < 14; i++) {
var p = document.createElement('span');
p.className = 'atc-particle';
p.style.left = x0 + 'px';
p.style.top = y0 + 'px';
var a = Math.random() * 6.2832,
d = 24 + Math.random() * 46;
p.style.setProperty('--dx', Math.cos(a) * d + 'px');
p.style.setProperty('--dy', Math.sin(a) * d + 'px');
scene.appendChild(p);
(function(el) {
setTimeout(function() {
el.remove();
}, 650);
})(p);
}
var chip = document.createElement('span');
chip.className = 'atc-fly';
chip.textContent = icon;
chip.style.left = x0 + 'px';
chip.style.top = y0 + 'px';
scene.appendChild(chip);
var t0 = performance.now(),
dur = 620,
cx = (x0 + x1) / 2,
cy = Math.min(y0, y1) - 70;
(function anim(t) {
var k = Math.min((t - t0) / dur, 1);
var e = k < .5 ? 2 * k * k : 1 - Math.pow(-2 * k + 2, 2) / 2,
m = 1 - e;
var x = m * m * x0 + 2 * m * e * cx + e * e * x1;
var y = m * m * y0 + 2 * m * e * cy + e * e * y1;
chip.style.transform = 'translate(-50%,-50%) translate(' + (x - x0) + 'px,' + (y - y0) + 'px) scale(' + (1 - 0.55 * e) + ')';
chip.style.opacity = String(1 - 0.25 * e);
if (k < 1) requestAnimationFrame(anim);
else {
chip.remove();
bump();
}
})(t0);
});
});
})();
})();