Introduction aux animations CSS avec @keyframes
Les animations CSS permettent de créer des transitions visuelles riches et engageantes sans JavaScript. Grâce à la règle @keyframes, vous pouvez définir des séquences d'animation complexes qui s'exécutent de manière fluide et performante, directement gérées par le moteur de rendu du navigateur.
En 2026, les animations CSS sont devenues incontournables pour offrir une expérience utilisateur moderne. Chez Tourak Digital, nous les utilisons pour donner vie à nos interfaces tout en préservant les performances.
Syntaxe de @keyframes
Définir une animation
/* Syntaxe avec from/to */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Syntaxe avec pourcentages (plus de contrôle) */
@keyframes bounce {
0% { transform: translateY(0); }
25% { transform: translateY(-30px); }
50% { transform: translateY(0); }
75% { transform: translateY(-15px); }
100% { transform: translateY(0); }
}
/* Animation complexe multi-propriétés */
@keyframes slideInRotate {
0% {
opacity: 0;
transform: translateX(-100px) rotate(-10deg);
}
60% {
opacity: 1;
transform: translateX(10px) rotate(2deg);
}
100% {
opacity: 1;
transform: translateX(0) rotate(0deg);
}
}
Appliquer une animation
/* Propriétés individuelles */
.element {
animation-name: fadeIn;
animation-duration: 0.6s;
animation-timing-function: ease-out;
animation-delay: 0.2s;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: forwards;
animation-play-state: running;
}
/* Raccourci animation */
.element {
animation: fadeIn 0.6s ease-out 0.2s 1 normal forwards;
}
/* Animations multiples */
.element {
animation:
fadeIn 0.6s ease-out forwards,
slideUp 0.8s ease-out 0.2s forwards;
}
Les propriétés d'animation en détail
| Propriété | Valeurs | Description |
|---|---|---|
animation-name | nom du @keyframes | Référence l'animation définie |
animation-duration | 0.5s, 300ms | Durée de l'animation |
animation-timing-function | ease, linear, ease-in, ease-out, ease-in-out, cubic-bezier() | Courbe de vitesse |
animation-delay | 0.2s, -0.5s | Délai avant le début (négatif = commence au milieu) |
animation-iteration-count | 1, 3, infinite | Nombre de répétitions |
animation-direction | normal, reverse, alternate, alternate-reverse | Direction de l'animation |
animation-fill-mode | none, forwards, backwards, both | État avant/après l'animation |
animation-play-state | running, paused | Pause/reprise de l'animation |
Fonctions de timing (easing) détaillées
/* Fonctions prédéfinies */
.linear { animation-timing-function: linear; }
.ease { animation-timing-function: ease; } /* Défaut */
.ease-in { animation-timing-function: ease-in; } /* Lent au début */
.ease-out { animation-timing-function: ease-out; } /* Lent à la fin */
.ease-io { animation-timing-function: ease-in-out; } /* Lent début + fin */
/* Cubic-bezier personnalisé */
.custom { animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }
/* Effet rebond */
.bouncy { animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }
/* Steps (animation image par image) */
.typewriter { animation-timing-function: steps(20, end); }
.sprite { animation-timing-function: steps(8); }
Animations pratiques prêtes à l'emploi
Fade In avec mouvement
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in-up {
animation: fadeInUp 0.6s ease-out forwards;
opacity: 0;
}
/* Délai en cascade pour plusieurs éléments */
.fade-in-up:nth-child(1) { animation-delay: 0.1s; }
.fade-in-up:nth-child(2) { animation-delay: 0.2s; }
.fade-in-up:nth-child(3) { animation-delay: 0.3s; }
Loader spinner
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #e0e0e0;
border-top-color: #3498db;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
Effet de pulsation
@keyframes pulse {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.4); }
70% { transform: scale(1.05); box-shadow: 0 0 0 15px rgba(52, 152, 219, 0); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(52, 152, 219, 0); }
}
.pulse-btn {
animation: pulse 2s ease-in-out infinite;
}
Effet de frappe machine (typewriter)
@keyframes typing {
from { width: 0; }
to { width: 100%; }
}
@keyframes blink {
50% { border-color: transparent; }
}
.typewriter {
font-family: monospace;
overflow: hidden;
white-space: nowrap;
border-right: 3px solid #333;
width: 0;
animation:
typing 3s steps(30) forwards,
blink 0.75s step-end infinite;
}
Effet de survol élégant sur les cartes
@keyframes shimmer {
0% { background-position: -200% center; }
100% { background-position: 200% center; }
}
.card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
}
.card::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
background-size: 200% 100%;
opacity: 0;
transition: opacity 0.3s;
}
.card:hover::after {
opacity: 1;
animation: shimmer 1.5s linear infinite;
}
Notification badge
@keyframes notifBounce {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.2); }
}
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
background: #e74c3c;
color: white;
border-radius: 50%;
font-size: 0.75rem;
animation: notifBounce 0.6s ease-in-out;
}
Animation au scroll avec Intersection Observer
/* CSS */
.animate-on-scroll {
opacity: 0;
transform: translateY(30px);
transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.animate-on-scroll.visible {
opacity: 1;
transform: translateY(0);
}
/* JavaScript */
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.animate-on-scroll').forEach(el => {
observer.observe(el);
});
Performances des animations CSS
Toutes les propriétés CSS ne s'animent pas avec les mêmes performances :
| Performance | Propriétés | Raison |
|---|---|---|
| Excellente (GPU) | transform, opacity | Compositing uniquement, pas de repaint |
| Moyenne | color, background-color, box-shadow | Repaint nécessaire |
| Mauvaise | width, height, margin, padding, top, left | Reflow + repaint (layout recalculation) |
/* MAUVAIS : anime width (déclenche un reflow) */
@keyframes expand-bad {
from { width: 0; }
to { width: 300px; }
}
/* BON : anime transform (GPU, pas de reflow) */
@keyframes expand-good {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
/* Forcer l'accélération GPU */
.animated-element {
will-change: transform, opacity;
transform: translateZ(0); /* Hack pour créer un layer GPU */
}
Accessibilité des animations
/* Respecter les préférences utilisateur */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Alternative : proposer des animations subtiles */
@media (prefers-reduced-motion: reduce) {
.fade-in { animation: none; opacity: 1; }
.slide-in { animation: none; transform: none; }
}
Conclusion
Les animations CSS avec @keyframes sont un outil puissant pour enrichir l'expérience utilisateur. En vous concentrant sur les propriétés performantes (transform et opacity), en respectant les préférences d'accessibilité, et en dosant subtilement vos effets, vous créerez des interfaces vivantes et professionnelles.
Tourak Digital crée des interfaces animées, performantes et accessibles. Discutons de votre projet.