Niveau 22 — Animations

Les Animations

Donnez vie à vos interfaces avec les animations CSS. Des micro-interactions aux animations complexes, maîtrisez l'art de créer des expériences dynamiques et performantes.

1. Comprendre les animations CSS

En une phrase : Les animations CSS permettent de modifier progressivement les propriétés d'un élément sur une durée définie, en utilisant des keyframes (images clés) pour décrire les états intermédiaires.

Contrairement aux transitions (qui nécessitent un changement d'état déclenché), les animations sont autonomes : elles se déclenchent, se répètent et se contrôlent entièrement via CSS, sans JavaScript.

TRANSITION vs ANIMATION
═══════════════════════════════════════════════════════════

  TRANSITION                              ANIMATION
  ──────────                              ─────────
  • Nécessite un trigger                  • Autonome (pas de trigger)
  • 2 états (avant / après)               • Autant d'états que nécessaire
  • Pas de répétition                     • Répétition illimitée
  • Pas de pause                          • Pause possible
  • Pas de delay complexe                 • Delays multiples

  État A ──────transition────── État B    @keyframes {
    │                                        0%  { État A }
  hover/click                                 50% { État B }
    │                                       100% { État A }
                                           }

Différences fondamentales entre transitions et animations

Pourquoi les animations sont essentielles

Besoin Sans animation Avec animation
Indicateur de chargement Texte "Chargement..." statique Spinner animé
Feedback utilisateur Aucun retour visuel Bouton pulse, ripple effect
Transition de page Changement brutal Slide / fade fluide
Attention utilisateur Élément statique ignoré Bounce / pulse pour attirer l'œil
Narration visuelle Tout apparaît d'un coup Apparition séquentielle (stagger)
État en cours Barre de progression statique Progression animée
Compatibilité : Les animations CSS sont supportées par 97%+ des navigateurs modernes. Les préfixes vendor ne sont plus nécessaires sauf pour des cas très spécifiques.

2. Syntaxe détaillée

2.1 @keyframes — Définir les états de l'animation

Définition : La rule @keyframes définit les états intermédiaires d'une animation. Chaque état est identifié par un pourcentage (0% à 100%) ou par les mots-clés from (0%) et to (100%).

Syntaxe from/to

/* Simple : depuis → vers */
@keyframes monAnimation {
  from {
    transform: translateX(0);
    opacity: 1;
  }
  to {
    transform: translateX(100px);
    opacity: 0;
  }
}

Syntaxe pourcentage

/* Détaillé : autant d'états que nécessaire */
@keyframes monAnimation {
  0% {
    transform: translateX(0);
    opacity: 1;
  }
  25% {
    transform: translateX(25px);
    background: #2563eb;
  }
  50% {
    transform: translateX(50px);
    background: #7c3aed;
  }
  75% {
    transform: translateX(75px);
    background: #ec4899;
  }
  100% {
    transform: translateX(100px);
    opacity: 0;
  }
}

Convention de nommage

/* Les conventions courantes pour les noms */
@keyframes slide-in-left { /* snake_case */ }
@keyframes slideInLeft {   /* camelCase */ }
@keyframes SLIDE-IN-LEFT { /* kebab-case (non conventionnel) */ }

/* Recommandation : utiliser kebab-case ou snake-case */
@keyframes slide-in-left { ... }
@keyframes fade-out { ... }
@keyframes scale-up { ... }
@keyframes rotate-cw { ... }

Quelles propriétés peuvent être animées ?

PROPRIÉTÉS ANIMABLES (sélection)
═══════════════════════════════════

  ✓ TRANSFORMS          transform, rotate, scale, skew, translate
  ✓ COULEURS            color, background-color, border-color
  ✓ OPACITÉ             opacity
  ✓ TAILLES             width, height, max-width, min-width
  ✓ MARGES/PADDING      margin, padding (tous les côtés)
  ✓ BORDURES            border-width, border-radius
  ✓ POLICES             font-size, font-weight, letter-spacing
  ✓ OMBRES              box-shadow, text-shadow
  ✓ POSITION            top, right, bottom, left
  ✓ Z-INDEX             z-index
  ✓ FILTRES             filter, backdrop-filter
  ✓ CLIP-PATH           clip-path
  ✓ OBJECT-FIT          object-fit

  ✗ display             (ne s'anime pas directement)
  ✗ visibility          (découpe, pas de transition fluide)
  ✗ font-family         (changement brutal)
  ✗ overflow            (pas d'animation fluide)
  ✗ position            (sauf top/right/bottom/left)

Guide des propriétés animables vs non-animables

2.2 Propriétés d'animation

animation-name — Nommer l'animation

Définition : Associe un élément à un ensemble de keyframes défini par @keyframes.
.element {
  animation-name: mon-animation;
  /* ou plusieurs animations : */
  animation-name: pulse, slide-in;
}

animation-duration — Durée de l'animation

Définition : Définit la durée d'un cycle complet de l'animation. Sans cette propriété, l'animation dure 0 seconde et n'est pas visible.
.element {
  animation-duration: 2s;      /* 2 secondes */
  animation-duration: 500ms;   /* 500 millisecondes */
  animation-duration: 1.5s;    /* 1.5 secondes */
}

animation-timing-function — Courbe de vitesse

Définition : Contrôle la vitesse de progression de l'animation. C'est le même principe que pour les transitions.
.element {
  animation-timing-function: linear;           /* Vitesse constante */
  animation-timing-function: ease;             /* Lent → rapide → lent (défaut) */
  animation-timing-function: ease-in;          /* Lent → rapide */
  animation-timing-function: ease-out;         /* Rapide → lent */
  animation-timing-function: ease-in-out;      /* Lent → rapide → lent (plus marqué) */
  animation-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55); /* Personnalisée */
  animation-timing-function: steps(4, end);    /* 4 étapes */
}

Visualiseur de timing functions

linear
ease
ease-in
ease-out
ease-in-out
cubic-bezier

animation-delay — Délai avant le démarrage

Définition : Définit le temps d'attente avant le début de l'animation. Une valeur négative démarre l'animation depuis un point avancé.
.element {
  animation-delay: 0s;        /* Démarre immédiatement (défaut) */
  animation-delay: 1s;        /* Attend 1 seconde */
  animation-delay: -0.5s;     /* Démarre à 50% pour une animation de 1s */
  animation-delay: 200ms;     /* Attend 200ms */
}

animation-iteration-count — Nombre de répétitions

Définition : Définit combien de fois l'animation se répète. La valeur infinite crée une boucle infinie.
.element {
  animation-iteration-count: 1;       /* Une fois (défaut) */
  animation-iteration-count: 3;       /* 3 fois */
  animation-iteration-count: 0.5;     /* Demi-tour (s'arrête à 50%) */
  animation-iteration-count: infinite; /* Boucle infinie */
}

animation-direction — Direction de la répétition

Définition : Contrôle la direction de l'animation lors des répétitions successives.
.element {
  animation-direction: normal;             /* 0%→100% à chaque fois (défaut) */
  animation-direction: reverse;            /* 100%→0% à chaque fois */
  animation-direction: alternate;          /* Aller-retour (0→100, 100→0, 0→100...) */
  animation-direction: alternate-reverse;  /* Retour-alterner (100→0, 0→100...) */
}
animation-direction visualisée (2 cycles)
══════════════════════════════════════════

  normal:           0%───►100%  │  0%───►100%
                              → → →

  reverse:          100%◄───0%  │  100%◄───0%
                              ← ← ←

  alternate:        0%───►100%  │  100%◄───0%
                          → → → → → ← ← ←

  alternate-reverse: 100%◄───0% │  0%───►100%
                          ← ← ← ← ← → → →

Les 4 directions d'animation sur 2 cycles

animation-fill-mode — État avant/après l'animation

Définition : Contrôle quel état les propriétés animées conservent avant (délai) et après l'animation terminée.
.element {
  animation-fill-mode: none;      /* Pas de remplissage (défaut) */
  animation-fill-mode: forwards;  /* Garde l'état final (100%) */
  animation-fill-mode: backwards; /* Applique l'état initial (0%) pendant le delay */
  animation-fill-mode: both;      /* forwards + backwards combinés */
}
animation-fill-mode visualisé
═════════════════════════════

          ◄── delay ──►◄── animation ──►◄── après ──►

none:      [état naturel]  [animation]   [état naturel]
           Le style "saute" au début et à la fin

forwards:  [état naturel]  [animation]   [état final (100%)]
           Garde le dernier état

backwards: [état initial]  [animation]   [état naturel]
           Le 0% s'applique pendant le delay

both:      [état initial]  [animation]   [état final (100%)]
           Les deux combinés

L'effet de animation-fill-mode sur le rendu

animation-play-state — Pause / Lecture

Définition : Permet de mettre en pause ou de reprendre une animation, utile pour les interactions utilisateur.
.element {
  animation-play-state: running;  /* En cours (défaut) */
  animation-play-state: paused;   /* En pause */
}

/* Pause au survol */
.element:hover {
  animation-play-state: paused;
}

animation — Le raccourci complet

Définition : La propriété animation est un raccourci qui combine toutes les propriétés d'animation en une seule ligne.
/* Syntaxe : animation: [name] [duration] [timing] [delay] [iteration] [direction] [fill-mode] [play-state] */
.element {
  animation: pulse 1s ease-in-out 0.5s infinite alternate forwards running;
}

/* Exemples pratiques */
.fade-in     { animation: fade-in 0.5s ease-out; }
.loop        { animation: spin 2s linear infinite; }
.bounce      { animation: bounce 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) infinite; }
.pulse       { animation: pulse 2s ease-in-out infinite; }
.shake       { animation: shake 0.5s ease-in-out; }

/* Plusieurs animations séparées par des virgules */
.multi {
  animation: pulse 2s ease infinite,
             glow 3s ease-in-out infinite;
}

/* Valeur prédéfinie */
.element {
  animation: none;  /* Annule toutes les animations */
}

Démonstration : Les propriétés d'animation

linear
ease
ease-in
ease-out
ease-in-out
cubic-bezier
direction: normal
direction: reverse
direction: alternate
direction: alt-reverse

2.3 Transform (en détail)

Définition : La propriété transform applique des transformations 2D (et 3D) à un élément : translations, rotations, échelles, déformations. C'est la base de la plupart des animations performantes.

translate() — Déplacement

/* Translation 2D */
.element { transform: translateX(50px); }    /* Déplacement horizontal */
.element { transform: translateY(-30px); }   /* Déplacement vertical */
.element { transform: translate(50px, -30px); } /* Les deux axes */

/* Translation 3D */
.element { transform: translate3d(50px, -30px, 100px); } /* Ajoute la profondeur */

/* Pourcentages (relatifs à la taille de l'élément) */
.element { transform: translate(-50%, -50%); } /* Centrage classique */

rotate() — Rotation

/* Rotation 2D */
.element { transform: rotate(45deg); }    /* Rotation horaire */
.element { transform: rotate(-90deg); }   /* Rotation anti-horaire */
.element { transform: rotate(0.5turn); }  /* Demi-tour */

/* Rotation 3D */
.element { transform: rotateX(45deg); }   /* Rotation autour de l'axe X */
.element { transform: rotateY(45deg); }   /* Rotation autour de l'axe Y */
.element { transform: rotateZ(45deg); }   /* Même que rotate() 2D */

scale() — Échelle

/* Échelle uniforme */
.element { transform: scale(1.5); }      /* Agrandi de 50% */
.element { transform: scale(0.8); }      /* Réduit de 20% */
.element { transform: scale(1); }        /* Taille originale */

/* Échelle non uniforme */
.element { transform: scaleX(2); }       /* Étiré en largeur */
.element { transform: scaleY(0.5); }     /* Aplati en hauteur */
.element { transform: scale(1.5, 0.8); } /* Les deux axes */

/* Échelle 3D */
.element { transform: scale3d(1.5, 1.5, 2); } /* Ajoute la profondeur */

skew() — Déformation

/* Skew 2D */
.element { transform: skewX(15deg); }    /* Inclinaison horizontale */
.element { transform: skewY(-10deg); }   /* Inclinaison verticale */
.element { transform: skew(15deg, -5deg); } /* Les deux axes */

matrix() — La transformation complète (mention)

/* matrix(a, b, c, d, tx, ty) — Combinaison de toutes les transforms 2D */
.element { transform: matrix(1, 0, 0, 1, 50, 0); } /* = translateX(50px) */

/* Utile pour des calculs programmatiques, rarement écrit à la main */

transform-origin — Point d'origine

/* Définit le point autour duquel les transforms s'appliquent */
.element {
  transform-origin: center;        /* Centre (défaut) */
  transform-origin: top left;      /* Coin supérieur gauche */
  transform-origin: 50% 50%;       /* Centre (en pourcentage) */
  transform-origin: 0 50%;         /* Gauche, centre vertical */
  transform-origin: bottom right;  /* Coin inférieur droit */
  transform-origin: 20px 40px;     /* Position précise en px */
  transform-origin: bottom;        /* Centre horizontal, bas */
}

Transformations 3D

/* Profondeur perspective */
.scene {
  perspective: 800px;         /* Distance de la caméra */
}

.card {
  transform-style: preserve-3d;   /* Les enfants sont en 3D */
  backface-visibility: hidden;    /* Masque le dos de l'élément */
  transform: rotateY(180deg);     /* Retourne la carte */
}

/* Exemple : carte qui se retourne */
.card:hover {
  transform: rotateY(180deg);
}
.card .front { backface-visibility: hidden; }
.card .back {
  backface-visibility: hidden;
  transform: rotateY(180deg);
}

Démonstration : Transformations (survol pour voir l'effet)

translateX(15px)
rotate(45°)
scale(1.3)
+
skewX(-15°)
/
perspective + rotateY
3D
Transform Syntaxe Description
translate() translate(x, y) Déplacement horizontal et vertical
rotate() rotate(angle) Rotation dans le plan 2D
scale() scale(x, y) Agrandissement ou réduction
skew() skew(x, y) Déformation (cisaillement)
matrix() matrix(a,b,c,d,tx,ty) Transformation complète 2D
perspective() perspective(px) Profondeur 3D
transform-origin center, top left Point d'origine des transformations
transform-style preserve-3d, flat Style des enfants (3D ou plat)
backface-visibility visible, hidden Visibilité du dos de l'élément

3. Visualisation

3.1 Résumé des propriétés d'animation

╔═══════════════════════════════════════════════════════════════════╗
║                    PROPRIÉTÉS D'ANIMATION                        ║
╠═══════════════════════════════════════════════════════════════════╣
║  animation-name:         nom-du-keyframes                       ║
║  animation-duration:     2s | 500ms | 1.5s                     ║
║  animation-timing:       ease | linear | ease-in-out | steps()  ║
║  animation-delay:        0s | 1s | -0.5s                       ║
║  animation-iteration:    1 | 3 | infinite                       ║
║  animation-direction:    normal | reverse | alternate            ║
║  animation-fill-mode:    none | forwards | backwards | both      ║
║  animation-play-state:   running | paused                       ║
╠═══════════════════════════════════════════════════════════════════╣
║  SHORTHAND:                                                     ║
║  animation: name duration timing delay iteration                ║
║             direction fill-mode play-state;                     ║
╠═══════════════════════════════════════════════════════════════════╣
║  VALEURS PAR DÉFAUT:                                            ║
║  name: none | duration: 0s | timing: ease | delay: 0s          ║
║  iteration: 1 | direction: normal | fill: none | play: running ║
╚═══════════════════════════════════════════════════════════════════╝

╔═══════════════════════════════════════════════════════════════════╗
║                    PROPRIÉTÉS TRANSFORM                          ║
╠═══════════════════════════════════════════════════════════════════╣
║  translate(x, y)     translateX()   translateY()   translate3d()║
║  rotate(angle)       rotateX()      rotateY()      rotateZ()   ║
║  scale(x, y)         scaleX()       scaleY()       scale3d()   ║
║  skew(x, y)          skewX()        skewY()                     ║
║  matrix(a,b,c,d,tx,ty)                                         ║
║  transform-origin:   center | top left | Xpx Ypx               ║
║  transform-style:    flat | preserve-3d                        ║
║  backface-visibility: visible | hidden                         ║
╚═══════════════════════════════════════════════════════════════════╝

Résumé visuel de toutes les propriétés d'animation et transform

3.2 Avant / Après : Impact des animations

Sans animation

Un élément statique. Pas de feedback visuel, pas de dynamisme.

Avec animation

L'élément se déplace et pulse. L'interface prend vie, l'utilisateur est guidé.

3.3 Démonstrations de @keyframes

Les différentes syntaxes de @keyframes

from → to
Pulse (0%, 50%, 100%)
Scale bounce
Rotation infinie
Couleur progressive

4. Exemples progressifs

4.1 Loading spinner — Plusieurs styles

/* Spinner 1 : Anneau rotatif */
@keyframes spin {
  to { transform: rotate(360deg); }
}

.spinner-ring {
  width: 40px;
  height: 40px;
  border: 4px solid #e2e8f0;
  border-top-color: #2563eb;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* Spinner 2 : Points rebondissants */
@keyframes dot-bounce {
  0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
  40% { transform: scale(1); opacity: 1; }
}

.spinner-dots span {
  animation: dot-bounce 1.4s ease-in-out infinite;
}
.spinner-dots span:nth-child(2) { animation-delay: 0.2s; }
.spinner-dots span:nth-child(3) { animation-delay: 0.4s; }

/* Spinner 3 : Barres qui grandissent */
@keyframes bar-grow {
  0%, 100% { height: 10px; }
  50% { height: 35px; }
}

.spinner-bars span {
  animation: bar-grow 1.2s ease-in-out infinite;
}

Rendu : Trois styles de spinner

Anneau
Points
Barres

4.2 Animations d'usage courant

/* Bounce */
@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-20px); }
}

/* Shake (secousse) */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
  20%, 40%, 60%, 80% { transform: translateX(5px); }
}

/* Slide in from left */
@keyframes slide-in-left {
  from { transform: translateX(-60px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

/* Fade in/out */
@keyframes fade-in-out {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}

/* Floating (flottaison) */
@keyframes floating {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  33% { transform: translateY(-8px) rotate(2deg); }
  66% { transform: translateY(4px) rotate(-1deg); }
}

/* Heartbeat (battement de cœur) */
@keyframes heartbeat {
  0%, 100% { transform: scale(1); }
  14% { transform: scale(1.2); }
  28% { transform: scale(1); }
  42% { transform: scale(1.2); }
}

/* Wave (vague) */
@keyframes wave {
  0%, 100% { transform: rotate(0deg); }
  25% { transform: rotate(15deg); }
  50% { transform: rotate(-10deg); }
  75% { transform: rotate(5deg); }
}

Rendu : Animations pratiques

Bounce
Shake
Slide in
Fade
Float
Heartbeat
Wave

4.3 Typewriter — Effet machine à écrire

@keyframes typewrite {
  from { width: 0; }
  to { width: 30ch; }  /* ch = largeur du caractère "0" */
}

@keyframes blink-caret {
  0%, 100% { border-color: transparent; }
  50% { border-color: #2563eb; }
}

.typewriter {
  overflow: hidden;
  border-right: 2px solid #2563eb;
  white-space: nowrap;
  animation:
    typewrite 3s steps(30) 1s forwards,
    blink-caret 0.75s step-end infinite;
  width: 0;
}

Rendu : Effet Typewriter

Bonjour, bienvenue dans le CSS !

4.4 Ripple effect — Onde de clic

@keyframes ripple-effect {
  to {
    transform: scale(4);
    opacity: 0;
  }
}

.ripple-btn {
  position: relative;
  overflow: hidden;
}

.ripple-btn .ripple {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.4);
  transform: scale(0);
  animation: ripple-effect 0.6s ease-out;
  pointer-events: none;
}

/* JavaScript nécessaire pour créer l'élément .ripple au clic */

Rendu : Bouton avec ripple effect (cliquez)

4.5 Menu hamburger ouverture/fermeture

/* Menu hamburger animé en croix */
.hamburger {
  width: 30px;
  height: 20px;
  position: relative;
  cursor: pointer;
}

.hamburger span {
  position: absolute;
  width: 100%;
  height: 3px;
  background: #1e293b;
  border-radius: 2px;
  transition: all 0.3s ease;
}

.hamburger span:nth-child(1) { top: 0; }
.hamburger span:nth-child(2) { top: 50%; transform: translateY(-50%); }
.hamburger span:nth-child(3) { bottom: 0; }

/* État ouvert → transforme en X */
.hamburger.active span:nth-child(1) {
  transform: translateY(9px) rotate(45deg);
}
.hamburger.active span:nth-child(2) {
  opacity: 0;
}
.hamburger.active span:nth-child(3) {
  transform: translateY(-9px) rotate(-45deg);
}

4.6 Modal — Entrée / Sortie

@keyframes fade-in {
  to { opacity: 1; }
}

@keyframes modal-enter {
  from {
    transform: scale(0.9) translateY(20px);
    opacity: 0;
  }
  to {
    transform: scale(1) translateY(0);
    opacity: 1;
  }
}

.modal-overlay {
  display: none;
  opacity: 0;
}
.modal-overlay.active {
  display: flex;
  animation: fade-in 0.3s ease forwards;
}

.modal-content {
  animation: modal-enter 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  transform: scale(0.9) translateY(20px);
  opacity: 0;
}

Rendu : Modal animée

4.7 Toast notification — Slide-in

@keyframes toast-slide {
  from { transform: translateX(110%); }
  to { transform: translateX(0); }
}

.toast {
  animation: toast-slide 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

Rendu : Toast notifications

4.8 Page transition

@keyframes page-slide-in {
  0% { transform: translateX(100%); opacity: 0; }
  20% { transform: translateX(0); opacity: 1; }
  80% { transform: translateX(0); opacity: 1; }
  100% { transform: translateX(-100%); opacity: 0; }
}

.page {
  animation: page-slide-in 6s ease-in-out infinite;
}

Rendu : Transition de page

Page 1 → Page 2 → Page 3

4.9 Staggered list — Apparition séquentielle

@keyframes stagger-in {
  from { opacity: 0; transform: translateX(-20px); }
  to { opacity: 1; transform: translateX(0); }
}

.stagger-item {
  opacity: 0;
  animation: stagger-in 0.5s ease forwards;
}

/* Décalage progressif avec nth-child */
.stagger-item:nth-child(1) { animation-delay: 0.1s; }
.stagger-item:nth-child(2) { animation-delay: 0.2s; }
.stagger-item:nth-child(3) { animation-delay: 0.3s; }
.stagger-item:nth-child(4) { animation-delay: 0.4s; }
.stagger-item:nth-child(5) { animation-delay: 0.5s; }

Rendu : Liste avec apparition séquentielle

  • Premier élément — apparaît en premier
  • Deuxième élément — décalage de 100ms
  • Troisième élément — décalage de 200ms
  • Quatrième élément — décalage de 300ms
  • Cinquième élément — décalage de 400ms

4.10 @keyframes en détail — Les 5 types

/* Type 1 : from/to (le plus simple) */
@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Type 2 : Pourcentages (contrôle total) */
@keyframes color-cycle {
  0%   { background: red; }
  25%  { background: yellow; }
  50%  { background: green; }
  75%  { background: blue; }
  100% { background: red; }
}

/* Type 3 : Points multiples pour easing complexe */
@keyframes bounce-complex {
  0%   { transform: translateY(0); }
  20%  { transform: translateY(-30px); }
  40%  { transform: translateY(0); }
  60%  { transform: translateY(-15px); }
  80%  { transform: translateY(0); }
  100% { transform: translateY(0); }
}

/* Type 4 : Animation multi-propriétés */
@keyframes complex-anim {
  0%   { transform: translateX(0) rotate(0deg); opacity: 1; }
  50%  { transform: translateX(100px) rotate(180deg); opacity: 0.5; background: #7c3aed; }
  100% { transform: translateX(0) rotate(360deg); opacity: 1; }
}

/* Type 5 : Animation infinie avec timing personnalisé */
@keyframes float {
  0%, 100% {
    transform: translateY(0) rotate(0deg);
    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
  }
  50% {
    transform: translateY(-10px) rotate(2deg);
    box-shadow: 0 15px 30px rgba(0,0,0,0.15);
  }
}

4.11 Animations avant/après

Effets avant/après avec pseudo-éléments

Checkmark animé
Soulignement animé
Texte souligné
Cercle rotatif

5. Erreurs fréquentes

Erreur 1 : Oublier animation-duration
/* FAUX — l'animation n'est pas visible */
.element {
  animation-name: slide-in;
  /* animation-duration non défini = 0s */
}

/* CORRECT */
.element {
  animation-name: slide-in;
  animation-duration: 0.5s;  /* Obligatoire ! */
}
Erreur 2 : Animer des propriétés coûteuses (layout/paint)
/* MAUVAIS — déclenche layout + paint à chaque frame */
@keyframes bad-anim {
  from { width: 100px; margin-left: 0; top: 0; }
  to { width: 200px; margin-left: 50px; top: 100px; }
}

/* BON — utilise transform + opacity (compositing uniquement) */
@keyframes good-anim {
  from { transform: translateX(0) scale(1); opacity: 1; }
  to { transform: translateX(100px) scale(1.2); opacity: 0.5; }
}
Erreur 3 : Ne pas respecter prefers-reduced-motion
/* FAUX — pas de respect de l'accessibilité */
.animé {
  animation: pulse 2s infinite;
}

/* CORRECT — désactiver pour les utilisateurs sensibles */
@media (prefers-reduced-motion: reduce) {
  .animé {
    animation: none;
    /* ou animation-duration: 0.01s; */
  }
}
Erreur 4 : Abuser de animation: infinite sans fin
/* RISQUE — consomme des ressources en continu */
.fantaisie {
  animation: rainbow 3s infinite;
}

/* BON — limiter aux animations utiles */
.utile {
  animation: loading-spin 1s linear infinite; /* Justifié */
}
.decoratif {
  animation: float 3s ease-in-out 5; /* Limiter le nombre de cycles */
}
Erreur 5 : Oublier will-change pour les animations complexes
/* FAUX — le navigateur prépare à chaque frame */
.animé {
  transform: translateX(0);
}
.animé.animé {
  transform: translateX(100px);
}

/* BON — indiquer les propriétés qui vont changer */
.animé {
  will-change: transform;
  transform: translateX(0);
}
Erreur 6 : Mélanger transform et propriétés de layout dans la même animation
/* FAUX — performance dégradée */
@keyframes mixed {
  0% { transform: translateX(0); width: 100px; }
  100% { transform: translateX(100px); width: 200px; }
}

/* BON — séparer ou tout mettre dans transform */
@keyframes performant {
  0% { transform: translateX(0) scaleX(1); }
  100% { transform: translateX(100px) scaleX(2); }
}
Erreur 7 : Utiliser animation shorthand sans valeur duration
/* FAUX — duration = 0s par défaut dans le shorthand */
.element {
  animation: slide-in ease 1s;  /* slide-in = name, ease = timing, 1s = duration */
}

/* CORRECT — toujours inclure la duration */
.element {
  animation: slide-in 0.5s ease;  /* name, duration, timing */
}
Erreur 8 : Nommer les keyframes avec des mots réservés
/* FAUX — conflits potentiels */
@keyframes initial { ... }  /* "initial" est une valeur CSS */
@keyframes inherit { ... }  /* "inherit" est une valeur CSS */

/* CORRECT — noms descriptifs et uniques */
@keyframes slide-in-left { ... }
@keyframes fade-out-up { ... }
@keyframes pulse-scale { ... }

6. Bonnes pratiques

Les règles d'or des animations CSS :
  • Animer uniquement transform et opacity pour des animations à 60fps fluides.
  • Toujours inclure animation-duration — sans elle, l'animation est invisible.
  • Utiliser will-change pour prévenir le navigateur des changements à venir.
  • Respecter prefers-reduced-motion pour l'accessibilité.
  • Utiliser cubic-bezier personnalisé pour des animations naturelles.
  • Ne pas abuser de animation: infinite — consomme des ressources.
  • Nommer les keyframes de manière descriptive (slide-in-left, fade-out-up).
  • Combiner animation et transition : animations pour les effets continus, transitions pour les interactions.

Performance : Propriétés à privilégier

Propriété Layout Paint Composite Recommandation
transform Non Non Oui Privilégier
opacity Non Non Oui Privilégier
width Oui Oui Oui Éviter
height Oui Oui Oui Éviter
top / left Oui Oui Oui Utiliser transform
margin Oui Oui Oui Éviter
padding Oui Oui Oui Éviter
font-size Oui Oui Oui Si nécessaire
box-shadow Non Oui Oui Acceptable
La règle d'or de la performance :
/* 60fps = 16ms par frame. Pour rester fluide : */
/* ✅ ANIMER : transform, opacity (composite uniquement) */
/* ⚠️ ACCEPTABLE : box-shadow, filter, clip-path */
/* ❌ ÉVITER : width, height, margin, padding, top, left */

/* Exemple performant */
.animé {
  will-change: transform, opacity;
}

.animé.entrer {
  animation: slide-in 0.3s ease forwards;
}

@keyframes slide-in {
  from { transform: translateY(20px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
Template de base pour une animation :
/* 1. Définir les keyframes */
@keyframes my-animation {
  from { transform: ...; opacity: ...; }
  to { transform: ...; opacity: ...; }
}

/* 2. Appliquer avec le shorthand */
.mon-element {
  animation: my-animation 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* 3. Ajouter will-change */
.mon-element {
  will-change: transform, opacity;
}

/* 4. Respecter prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
  .mon-element { animation: none; }
}

7. Mini-défi

Défi 1 : Spinner personnalisé

Créez un spinner avec 3 cercles concentriques qui tournent à des vitesses différentes.

@keyframes spin { to { transform: rotate(360deg); } }

.spinner-multi {
  position: relative;
  width: 60px;
  height: 60px;
}

.spinner-multi span {
  position: absolute;
  inset: 0;
  border: 3px solid transparent;
  border-radius: 50%;
}

.spinner-multi span:nth-child(1) {
  border-top-color: #2563eb;
  animation: spin 1s linear infinite;
}
.spinner-multi span:nth-child(2) {
  inset: 6px;
  border-top-color: #7c3aed;
  animation: spin 1.5s linear infinite reverse;
}
.spinner-multi span:nth-child(3) {
  inset: 12px;
  border-top-color: #ec4899;
  animation: spin 2s linear infinite;
}

Défi 2 : Carte flip 3D

Créez une carte qui se retourne au survol pour afficher son verso, en utilisant transform-style: preserve-3d et backface-visibility: hidden.

.card-3d {
  perspective: 1000px;
  width: 200px;
  height: 120px;
}

.card-3d-inner {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.6s ease;
}

.card-3d:hover .card-3d-inner {
  transform: rotateY(180deg);
}

.card-3d-front, .card-3d-back {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.card-3d-back {
  transform: rotateY(180deg);
}

Défi 3 : Animation de chargement de page

Créez une barre de progression animée qui se remplit de gauche à droite, puis se réinitialise en boucle.

@keyframes progress-fill {
  0% { width: 0%; }
  100% { width: 100%; }
}

.progress-bar {
  height: 4px;
  background: #e2e8f0;
  border-radius: 2px;
  overflow: hidden;
}

.progress-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, #2563eb, #7c3aed);
  border-radius: 2px;
  animation: progress-fill 2s ease-in-out infinite;
}

Défi 4 : Notification slide-in

Créez une notification qui slide depuis la droite, reste 3 secondes, puis disparaît. Utilisez animation-fill-mode: both.

@keyframes notify-in {
  from { transform: translateX(100%); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

@keyframes notify-out {
  from { transform: translateX(0); opacity: 1; }
  to { transform: translateX(100%); opacity: 0; }
}

.notification {
  animation:
    notify-in 0.3s ease forwards,
    notify-out 0.3s ease 3s forwards;
}

Défi 5 : Bouton avec pulse attention

Créez un bouton CTA avec une animation pulse qui attire l'attention (scale + ombre), qui s'arrête au survol.

@keyframes pulse-attention {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(37, 99, 235, 0.4); }
  50% { transform: scale(1.05); box-shadow: 0 0 0 10px rgba(37, 99, 235, 0); }
}

.cta-pulse {
  animation: pulse-attention 2s ease-in-out infinite;
}

.cta-pulse:hover {
  animation-play-state: paused;
  transform: scale(1.05);
}

8. Projets pratiques

Projet 1 : Landing page avec animations

Créez une page de lancement avec :

  • Hero section avec stagger-in sur le titre, sous-titre et boutons
  • Cartes features avec float ou fade-in au scroll
  • Spinner de chargement personnalisé
  • Bouton CTA avec pulse attention
  • Respect de prefers-reduced-motion

Projet 2 : Système de notifications animé

Créez un système de notifications avec :

  • Toast slide-in depuis la droite
  • Animation de disparition après 5 secondes
  • Empilement avec animation stagger
  • Boutons d'action avec ripple effect
  • 3 types : succès (vert), info (bleu), erreur (rouge)

Projet 3 : Portfolio avec animations

Créez un mini-portfolio avec :

  • Header avec menu hamburger animé (→ croix)
  • Cartes projets avec card flip 3D au survol
  • Liste de compétences avec animation stagger
  • Typewriter pour l'intro
  • Modal pour les détails de projet

9. Révision

Récapitulatif des concepts clés

Concept Propriété Valeurs courantes
Définir les états @keyframes from/to, 0%, 50%, 100%
Nommer l'animation animation-name Identifiant de keyframes
Durée animation-duration 0.5s, 1s, 2s
Courbe de vitesse animation-timing-function ease, linear, cubic-bezier()
Délai animation-delay 0s, 1s, -0.5s
Répétitions animation-iteration-count 1, 3, infinite
Direction animation-direction normal, reverse, alternate
Remplissage animation-fill-mode none, forwards, both
Lecture/pause animation-play-state running, paused
Raccourci animation name duration timing delay iteration direction fill-mode
Transformation transform translate(), rotate(), scale(), skew()
Point d'origine transform-origin center, top left, Xpx Ypx
Performance will-change transform, opacity
Accessibilité prefers-reduced-motion reduceanimation: none

Quiz rapide

  1. Quelle est la différence fondamentale entre une transition et une animation ?
  2. Quelle propriété est obligatoire pour qu'une animation soit visible ?
  3. Quelles sont les 2 propriétés CSS à privilégier pour des animations performantes ?
  4. Que fait animation-fill-mode: both ?
  5. Comment répéter une animation indéfiniment ?
  6. Qu'est-ce que will-change et quand l'utiliser ?
  7. Comment désactiver les animations pour les utilisateurs sensibles au mouvement ?
  8. Quelle valeur de animation-delay fait démarrer l'animation à mi-chemin ?
  9. Comment changer le point d'origine d'une rotation ?
  10. Quelle est la différence entre alternate et alternate-reverse ?
Réponses :
  1. Les transitions nécessitent un changement d'état déclenché (hover, classe…), les animations sont autonomes via @keyframes.
  2. animation-duration — sans elle, la durée est 0s et l'animation est invisible.
  3. transform et opacity — elles n'activent que le compositeur (pas de layout ni paint).
  4. both = forwards + backwards : applique l'état initial pendant le delay ET garde l'état final après.
  5. animation-iteration-count: infinite;
  6. will-change indique au navigateur quelles propriétés vont changer, lui permettant de préparer l'accélération matérielle. À utiliser avec parcimonie.
  7. @media (prefers-reduced-motion: reduce) { .animé { animation: none; } }
  8. animation-delay: -1s; pour une animation de 2s (la négativité fait démarrer à 50%).
  9. transform-origin: top left; ou transform-origin: 50% 50%;
  10. alternate = 0→100, 100→0, 0→100… alternate-reverse = 100→0, 0→100, 100→0…

Cheat sheet rapide

/* Animation de base */
.element { animation: nom 0.5s ease; }

/* Animation infinie */
.element { animation: spin 2s linear infinite; }

/* Aller-retour infini */
.element { animation: bounce 1s ease-in-out infinite alternate; }

/* Avec delay + fill-mode */
.element { animation: fade-in 0.3s ease 0.5s forwards; }

/* Pause au survol */
.element:hover { animation-play-state: paused; }

/* Rotation */
@keyframes spin { to { transform: rotate(360deg); } }

/* Bounce */
@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-20px); }
}

/* Pulse */
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* Slide in from left */
@keyframes slide-in {
  from { transform: translateX(-100%); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

/* Respect a11y */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; }
}