Effets Hover & Animations Survol
Hover effects, micro-interactions, éléments interactifs et physique.
Mis à jour le
Qu'est-ce qu'un bon effet au survol, et fonctionne-t-il sur mobile ?
Un effet au survol donne un retour immédiat quand le pointeur atteint un élément : agrandissement, changement de couleur, soulignement, reflet ou révélation. Il se construit avec des transitions CSS sur :hover et :focus-visible, des pseudo-éléments pour les soulignements et les reflets, et un peu de JavaScript pour la physique ou l'inclinaison 3D qui suit le pointeur.
Comment choisir, performances et accessibilité
- Choisissez des effets sobres comme Underline Expand, Color Shift ou Scale & Rotate pour la navigation et les cartes, et des effets ludiques comme Like Heart, Paint Brush ou Gravity Simulation pour les moments de surprise.
- Les écrans tactiles n'ont pas de survol : placez les styles de survol dans
@media (hover: hover), ne cachez jamais une information essentielle derrière un survol et reproduisez chaque effet au focus clavier. - Scale & Rotate, Background Slide, Like Heart, Paint Brush et Gravity Simulation sont gratuits avec leur code complet ; les autres sont inclus dans Premium.
Scale & Rotate
Agrandissement avec légère rotation.
Voir le code source
<div class="demo-preview">
<div class="hover-box hover-scale-rotate">
<span>Scale & Rotate</span>
</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%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
/* Variables du design system du site, dont ce code dépend (valeurs de css/style.css — à adapter librement). */
.demo-preview {
--bg-card: #12121a;
--border: rgba(255, 255, 255, 0.08);
--radius-xl: 1rem;
}
.hover-box {
width: 200px;
height: 200px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
cursor: pointer;
position: relative;
overflow: hidden;
}
.hover-scale-rotate {
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.hover-scale-rotate:hover {
transform: scale(1.1) rotate(5deg);
box-shadow: 0 20px 40px rgba(99, 102, 241, 0.3);
}
.hover-box span {
position: relative;
z-index: 1;
}/* CSS pur — aucun JavaScript requis */Background Slide
Le fond glisse de gauche à droite.
Voir le code source
<div class="demo-preview">
<div class="hover-box hover-bg-slide">
<span>BG Slide</span>
</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%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
/* Variables du design system du site, dont ce code dépend (valeurs de css/style.css — à adapter librement). */
.demo-preview {
--bg-card: #12121a;
--border: rgba(255, 255, 255, 0.08);
--radius-xl: 1rem;
}
.hover-box {
width: 200px;
height: 200px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
cursor: pointer;
position: relative;
overflow: hidden;
}
.hover-bg-slide {
background: linear-gradient(90deg, var(--bg-card) 50%, #6366f1 50%);
background-size: 200% 100%;
background-position: left;
transition: all 0.5s ease;
}
.hover-bg-slide:hover {
background-position: right;
color: white;
}
.hover-box span {
position: relative;
z-index: 1;
}/* CSS pur — aucun JavaScript requis */Border Gradient
Bordure gradient qui apparaît au survol.
Shine Effect
Reflet lumineux qui traverse l'élément.
Underline Expand
Ligne qui s'étend depuis le centre.
Icon Bounce
L'icône rebondit au survol.
Color Shift
Transition de couleur fluide.
Blur Reveal
L'élément flou devient net au survol.
Shadow Inset
Ombre intérieure lumineuse.
Tilt 3D
Inclinaison perspective au survol.
Shutter Reveal
Effet volets qui s'ouvrent au survol.
Glitch Hover
Effet glitch RGB au survol.
Fill Up
Remplissage de bas en haut avec courbe ease-quart. Inspiré de Daily Delights.
Filter Animated
Animation de filtres CSS optimisée GPU avec will-change.
Spotlight Effect
Effet projecteur qui suit le curseur sur l'élément.
Magnetic Pull
L'élément est attiré par le curseur avec effet magnétique.
Elastic Scale
Agrandissement avec courbe élastique et rebond naturel.
Gradient Shift
Gradient animé qui accélère au survol pour un effet dynamique.
Like Heart
Bouton like avec animation pop, anneau de propagation et explosion de particules colorées au clic.
Voir le code source
<div class="demo-preview">
<div class="mi-heart-scene">
<button class="mi-heart-btn" onclick="this.classList.toggle('liked')">
<svg viewBox="0 0 24 24"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
<div class="mi-heart-particles">
<span class="mi-heart-particle"></span>
<span class="mi-heart-particle"></span>
<span class="mi-heart-particle"></span>
<span class="mi-heart-particle"></span>
<span class="mi-heart-particle"></span>
<span class="mi-heart-particle"></span>
<span class="mi-heart-particle"></span>
<span class="mi-heart-particle"></span>
</div>
</button>
<span class="mi-heart-count">423 likes</span>
</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%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.mi-heart-scene {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
.mi-heart-btn {
width: 80px;
height: 80px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
position: relative;
transition: all 0.3s ease;
}
.mi-heart-btn:hover {
background: rgba(239, 68, 68, 0.1);
border-color: rgba(239, 68, 68, 0.3);
}
.mi-heart-btn svg {
width: 36px;
height: 36px;
stroke: rgba(255, 255, 255, 0.5);
fill: none;
stroke-width: 2;
transition: all 0.3s ease;
}
.mi-heart-particles {
position: absolute;
inset: 0;
pointer-events: none;
}
.mi-heart-particle {
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
top: 50%;
left: 50%;
opacity: 0;
}
.mi-heart-particle:nth-child(1) {
background: #ef4444;
--angle: 0deg;
}
.mi-heart-particle:nth-child(2) {
background: #f97316;
--angle: 45deg;
}
.mi-heart-particle:nth-child(3) {
background: #eab308;
--angle: 90deg;
}
.mi-heart-particle:nth-child(4) {
background: #ef4444;
--angle: 135deg;
}
.mi-heart-particle:nth-child(5) {
background: #ec4899;
--angle: 180deg;
}
.mi-heart-particle:nth-child(6) {
background: #f97316;
--angle: 225deg;
}
.mi-heart-particle:nth-child(7) {
background: #eab308;
--angle: 270deg;
}
.mi-heart-particle:nth-child(8) {
background: #ec4899;
--angle: 315deg;
}
.mi-heart-count {
font-size: 0.85rem;
font-weight: 600;
color: rgba(255, 255, 255, 0.5);
transition: all 0.3s ease;
}(function() {
function toggle() {
open = !open;
gift.classList.toggle('guw-open', open);
scene.classList.toggle('guw-done', open);
gift.setAttribute('aria-label', open ? 'Cadeau ouvert (cliquer pour ré-emballer)' : 'Ouvrir le cadeau');
if (open) setTimeout(burst, reduceMotion ? 0 : 1100);
}
})();Bookmark Toggle
Bouton de sauvegarde avec animation de chute, remplissage du fond et transition de couleur.
Submit Button
Bouton envoi avec morphing circulaire, spinner de chargement et animation de check de succès.
Ripple Click
Effet Material Design ripple au clic. L'onde se propage depuis le point de contact et s'estompe.
Pull to Refresh
Simulation pull-to-refresh mobile avec indicateur rotatif, déplacement du contenu et spinner de chargement.
Notification Bell
Cloche avec animation de vibration, badge compteur avec pop-in et toast de notification glissant.
Star Rating
Système de notation avec animation pop et rotation sur chaque étoile. Labels dynamiques selon la note.
Switch with Icons
Interrupteurs avec icônes intégrées dans la poignée, animation bounce et changement de couleur.
Copy to Clipboard
Bouton copier avec transition d'icônes (copie → check), couleur de succès et tooltip « Copié ».
Confetti Button
Bouton qui déclenche une pluie de confettis colorés avec gravité et rotation réalistes.
Paint Brush
Pinceau lumineux qui laisse une traînée colorée.
Voir le code source
<div class="demo-preview">
<canvas id="paintCanvas" class="interactive-zone paint-canvas"></canvas>
<span class="info-label">Dessinez avec la souris</span>
</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;
}
/* Variables du design system du site, dont ce code dépend (valeurs de css/style.css — à adapter librement). */
.demo-preview {
--text-muted: #8b8b93;
}
.paint-canvas {
background: #0a0a0f;
}
.interactive-zone {
width: 100%;
height: 300px;
position: relative;
cursor: none;
}
.info-label {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
font-size: 0.75rem;
color: var(--text-muted);
pointer-events: none;
z-index: 10;
}(function() {
// ========== INTERACTIF ==========
// 1. Paint Brush
(function() {
const paintCanvas = document.getElementById('paintCanvas');
if (!paintCanvas) return;
const paintCtx = paintCanvas.getContext('2d');
paintCanvas.width = paintCanvas.offsetWidth;
paintCanvas.height = 300;
let painting = false;
let hue = 0;
paintCanvas.addEventListener('mousedown', () => painting = true);
paintCanvas.addEventListener('mouseup', () => painting = false);
paintCanvas.addEventListener('mousemove', (e) => {
if (!painting) return;
const rect = paintCanvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
paintCtx.beginPath();
paintCtx.arc(x, y, 15, 0, Math.PI * 2);
paintCtx.fillStyle = `hsl(${hue}, 70%, 60%)`;
paintCtx.fill();
hue = (hue + 2) % 360;
});
// Fade effect
const paintFadeInterval = setInterval(() => {
paintCtx.fillStyle = 'rgba(10, 10, 15, 0.02)';
paintCtx.fillRect(0, 0, paintCanvas.width, paintCanvas.height);
}, 50);
})();
})();Repel Particles
Particules qui fuient le curseur.
Text Scramble
Texte qui se décode au survol.
Elastic Drag
Élément draggable avec retour élastique.
Scratch Reveal
Grattez pour révéler le contenu caché.
Morph on Click
Forme qui se transforme à chaque clic.
Live Typing
Chaque caractère apparaît avec animation.
Sound Visualizer
Barres qui réagissent dynamiquement.
Magnetic Letters
Lettres attirées ou repoussées par le curseur.
Gravity Drag
Éléments qui tombent avec gravité une fois relâchés.
Gravity Simulation
Balles soumises à la gravité avec rebond.
Voir le code source
<div class="demo-preview">
<div class="physics-container" id="gravityContainer">
<span class="info-label">Cliquez pour ajouter des balles</span>
</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%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
/* Variables du design system du site, dont ce code dépend (valeurs de css/style.css — à adapter librement). */
.demo-preview {
--text-muted: #8b8b93;
}
.physics-gravity-ball {
position: absolute;
width: 30px;
height: 30px;
border-radius: 50%;
cursor: grab;
}
.physics-gravity-ball:active {
cursor: grabbing;
}
.info-label {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
font-size: 0.75rem;
color: var(--text-muted);
pointer-events: none;
z-index: 10;
}
.physics-container {
width: 100%;
height: 300px;
position: relative;
}(function() {
// ========== PHYSIQUE ==========
// 1. Gravity Balls
(function() {
const gravityContainer = document.getElementById('gravityContainer');
if (!gravityContainer) return;
const gravityBalls = [];
const gravity = 0.5;
const bounce = 0.7;
const friction = 0.99;
gravityContainer.addEventListener('click', (e) => {
const rect = gravityContainer.getBoundingClientRect();
const ball = document.createElement('div');
ball.className = 'physics-gravity-ball';
ball.style.background = `hsl(${Math.random() * 360}, 70%, 60%)`;
ball.style.left = (e.clientX - rect.left - 15) + 'px';
ball.style.top = (e.clientY - rect.top - 15) + 'px';
gravityContainer.appendChild(ball);
gravityBalls.push({
el: ball,
x: e.clientX - rect.left - 15,
y: e.clientY - rect.top - 15,
vx: (Math.random() - 0.5) * 10,
vy: 0
});
if (gravityBalls.length > 20) {
const old = gravityBalls.shift();
old.el.remove();
}
});
function animateGravity() {
const rect = gravityContainer.getBoundingClientRect();
gravityBalls.forEach(ball => {
ball.vy += gravity;
ball.vx *= friction;
ball.x += ball.vx;
ball.y += ball.vy;
if (ball.y > rect.height - 30) {
ball.y = rect.height - 30;
ball.vy *= -bounce;
}
if (ball.x < 0) {
ball.x = 0;
ball.vx *= -bounce;
}
if (ball.x > rect.width - 30) {
ball.x = rect.width - 30;
ball.vx *= -bounce;
}
ball.el.style.left = ball.x + 'px';
ball.el.style.top = ball.y + 'px';
});
requestAnimationFrame(animateGravity);
}
animateGravity();
})();
})();Elastic String
Corde élastique qui réagit au curseur.
Magnetic Field
Particules attirées par un champ magnétique.
Newton's Cradle
Pendules avec transfert d'énergie cinétique.
Liquid Simulation
Gouttes qui se combinent et se séparent.
Spring Physics
Animation élastique avec effet ressort.
Collision Detection
Balles qui rebondissent les unes sur les autres.
Cloth Simulation
Simulation de tissu interactif.
Newton's Cradle
Pendule de Newton avec transfert d'énergie.
Rope Physics
Corde interactive avec simulation physique.
Fluid X-Ray Reveal
Le curseur « scanne » et révèle une couche cachée — ici, la même personne déguisée. Masque fluide à traînée. Avant/après, normal/déguisé, jour/nuit : la 2e couche peut être n'importe quoi.
💡 Idéal pour : avant/après, « voir le costume », teaser de soirée Halloween, comparaison de visuels, easter-egg de portrait.
Scratch Coupon Reveal
Carte à gratter qui dévoile un code promo, puis propose de le copier. Interaction tactile et ludique qui valorise l'offre. Code et seuil de révélation personnalisables.
💡 Idéal pour : code promo, jeu-concours, fidélité, Black Friday, pop-up d'offre, gamification e-commerce.
Gift Unwrap Reveal
Un cadeau qui tourne sur lui-même, dont le couvercle se soulève et d'où sort un code promo (ou tout contenu) en rebond, avec étincelles. Idéal pour dévoiler une offre de fête de façon mémorable. Code personnalisable.
💡 Idéal pour : code promo de Noël, révélation d'offre, jeu-concours, message de vœux, calendrier surprise, e-commerce festif.
Voir le code source
<div class="demo-preview">
<div class="guw-scene" data-guw data-code="NOEL30">
<button type="button" class="guw-gift" data-guw-box aria-label="Ouvrir le cadeau">
<span class="guw-inner" aria-hidden="true">
<span class="guw-code" data-guw-code>🎄 NOEL30</span>
<span class="guw-box"></span>
<span class="guw-lid"></span>
<span class="guw-bow">🎀</span>
</span>
</button>
<div class="guw-hint" aria-hidden="true">Clique pour déballer</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;
}
.guw-scene {
position: relative;
display: grid;
place-items: center;
min-height: 330px;
border-radius: 14px;
overflow: hidden;
background: radial-gradient(circle at 50% 35%, #241433, #0b0a14 72%);
}
.guw-gift {
position: relative;
width: 220px;
height: 250px;
border: 0;
background: none;
cursor: pointer;
outline-offset: 8px;
}
.guw-gift:focus-visible {
outline: 2px solid #fb7185;
border-radius: 12px;
}
.guw-code {
position: absolute;
left: 50%;
bottom: 58px;
transform: translateX(-50%) translateY(0) scale(.6);
z-index: 3;
background: linear-gradient(135deg, #ffe08a, #f2a900);
color: #5b3a00;
font-weight: 800;
letter-spacing: .06em;
font-size: 16px;
padding: 9px 16px;
border-radius: 10px;
white-space: nowrap;
box-shadow: 0 8px 18px rgba(0, 0, 0, .4);
border: 2px dashed #b97e00;
transition: transform .55s cubic-bezier(.2, 1.55, .35, 1);
transition-delay: 0s;
}
.guw-box {
position: absolute;
left: 50%;
bottom: 40px;
transform: translateX(-50%);
width: 140px;
height: 96px;
border-radius: 9px;
z-index: 4;
background: linear-gradient(135deg, #e01e37, #9c0f20);
box-shadow: inset 0 -10px 16px rgba(0, 0, 0, .32), 0 14px 22px rgba(0, 0, 0, .4);
}
.guw-box::before {
content: "";
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 24px;
height: 100%;
background: linear-gradient(#ffd166, #f2a900);
}
.guw-box::after {
content: "";
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
height: 24px;
background: linear-gradient(90deg, #f2a900, #ffd166);
}
.guw-lid {
position: absolute;
left: 50%;
bottom: 124px;
transform: translateX(-50%) translateY(0) rotate(0);
width: 162px;
height: 40px;
border-radius: 9px;
z-index: 6;
background: linear-gradient(135deg, #f0263f, #b01225);
box-shadow: 0 6px 12px rgba(0, 0, 0, .35);
transition: transform .5s cubic-bezier(.34, 1.3, .5, 1);
transition-delay: .55s;
}
.guw-lid::before {
content: "";
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 24px;
height: 100%;
background: linear-gradient(#ffd166, #f2a900);
}
.guw-bow {
position: absolute;
left: 50%;
bottom: 150px;
transform: translateX(-50%) translateY(0);
font-size: 40px;
z-index: 7;
transition: transform .5s cubic-bezier(.34, 1.3, .5, 1);
transition-delay: .55s;
}
.guw-inner {
position: absolute;
inset: 0;
transform-origin: 50% 70%;
transition: transform .75s cubic-bezier(.45, .05, .2, 1);
transition-delay: .9s;
}
.guw-gift.guw-open .guw-inner {
transform: rotate(360deg);
transition-delay: 0s;
}
.guw-gift.guw-open .guw-lid {
transform: translateX(-50%) translateY(-86px) rotate(-10deg);
transition-delay: .78s;
}
.guw-gift.guw-open .guw-bow {
transform: translateX(-50%) translateY(-104px) rotate(28deg);
transition-delay: .78s;
}
.guw-gift.guw-open .guw-code {
transform: translateX(-50%) translateY(-104px) scale(1);
transition-delay: 1s;
}
.guw-spark {
position: absolute;
width: 9px;
height: 9px;
border-radius: 50%;
background: #ffd166;
z-index: 8;
transform: translate(-50%, -50%);
pointer-events: none;
animation: guw-spark .75s ease-out forwards;
}
.guw-hint {
position: absolute;
bottom: 12px;
left: 50%;
transform: translateX(-50%);
font-size: 12px;
color: #fb7185;
opacity: .6;
pointer-events: none;
transition: opacity .3s;
}
.guw-scene.guw-done .guw-hint {
opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
.guw-inner,
.guw-lid,
.guw-bow,
.guw-code {
transition: none !important;
}
}
@keyframes guw-spark {
to {
transform: translate(-50%, -50%) translate(var(--dx), var(--dy)) scale(.2);
opacity: 0;
}
}(function() {
/* Sprint décembre 2026 — Noël — Gift Unwrap Reveal (fx-0812) */
(function() {
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
document.querySelectorAll('[data-guw]').forEach(function(scene) {
var gift = scene.querySelector('[data-guw-box]');
var code = scene.querySelector('[data-guw-code]');
if (scene.dataset.code) code.textContent = '🎄 ' + scene.dataset.code;
var open = false;
function burst() {
if (reduceMotion) return;
for (var i = 0; i < 12; i++) {
var s = document.createElement('span');
s.className = 'guw-spark';
s.style.left = '50%';
s.style.bottom = '150px';
var a = (i / 12) * 6.2832 + Math.random() * 0.4,
d = 50 + Math.random() * 50;
s.style.setProperty('--dx', Math.cos(a) * d + 'px');
s.style.setProperty('--dy', -Math.abs(Math.sin(a) * d) - 10 + 'px');
gift.appendChild(s);
(function(el) {
setTimeout(function() {
el.remove();
}, 800);
})(s);
}
}
function toggle() {
open = !open;
gift.classList.toggle('guw-open', open);
scene.classList.toggle('guw-done', open);
gift.setAttribute('aria-label', open ? 'Cadeau ouvert (cliquer pour ré-emballer)' : 'Ouvrir le cadeau');
if (open) setTimeout(burst, reduceMotion ? 0 : 1100);
}
gift.addEventListener('click', toggle);
gift.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggle();
}
});
});
})();
})();