Chercher « animation css exemple » pour booster ses projets web, c’est exactement ce que je faisais il y a encore quelques années. Chaque exemple est actionnable : vous pouvez copier, adapter, tester. Je détaille les propriétés clés, les cas d’usage réels, les impacts sur les Core Web Vitals et comment respecter prefers-reduced-motion. Pas de blabla théorique, juste ce qui fonctionne sur le terrain.

Pourquoi utiliser des animations CSS plutôt que JavaScript ?

Les animations CSS pures offrent une simplicité d’implémentation imbattable : quelques lignes dans votre feuille de style, et vous obtenez des transitions fluides à 60fps. Animer transform et opacity exploite le compositing GPU, sans déclencher de layout ou de paint coûteux. Le navigateur optimise nativement, ce qui réduit le risque de jank et améliore la maintenance.

Côté accessibilité, CSS respecte automatiquement prefers-reduced-motion si vous configurez correctement vos media queries. Pas de logique métier à synchroniser, pas de state management complexe. Pour les micro-interactions (hover, loaders, transitions d’états), CSS suffit 95% du temps.

Mais CSS montre ses limites face aux animations pilotées par l’utilisateur : position du clic pour un ripple, scroll piloté avec parallaxe complexe, timelines interdépendantes, synchronisation audio. Là, JavaScript devient pertinent. Mon approche : partir CSS par défaut, progressive enhancement avec JS léger si nécessaire, et toujours tester la performance dans les DevTools.

Les 30 exemples d’animation CSS qui déchirent

Chaque exemple suit le même format : usage typique, propriétés clés, accessibilité, performance. Les snippets sont courts, classes BEM, variables CSS pour durées et délais. Je fournis toujours une stratégie prefers-reduced-motion pour désactiver ou alléger l’animation. Pas deframework requis, juste du CSS moderne que vous pouvez intégrer dans n’importe quel projet.

1. Texte qui s’écrit lettre par lettre

L’effet typewriter simule une saisie progressive avec un curseur clignotant. Technique : animer la width d’un conteneur overflow: hidden, en utilisant steps() pour sauter de caractère en caractère, et un border-right animé en boucle pour le curseur.

<style>
.typewriter-demo {
  font-family: 'Courier New', monospace;
  display: inline-block;
  overflow: hidden;
  border-right: 2px solid #333;
  white-space: nowrap;
  animation: typing 3s steps(24) forwards, blink 0.7s step-end infinite;
  contain: layout;
}
@keyframes typing {
  from { width: 0; }
  to { width: 100%; }
}
@keyframes blink {
  50% { border-color: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .typewriter-demo {
    animation: none;
    width: 100%;
    border-right: none;
  }
}
</style>

<span class="typewriter-demo">Bienvenue sur mon site</span>

Markup minimal : <span class="typewriter">Texte ici</span> avec police monospace recommandée pour garantir la largeur constante des glyphes. Animer width déclenche du layout, donc gardez l’élément isolé (contain: layout) et la durée courte.

–> Accessibilité : ne pas utiliser aria-live ici, le texte doit être présent dans le DOM dès le départ pour les lecteurs d’écran.

–> Performance : privilégier transform: scaleX sur un wrapper pour éviter le layout, ou accepter le compromis si l’animation ne se répète pas en boucle. Le curseur peut être un pseudo-élément animé en opacity.

Texte surligné dynamique

2. Effet de surbrillance animée

Effet marqueur fluo qui balaye un mot ou une phrase. Technique : linear-gradient en background-image, animé via background-size et background-position. Alternative : box-shadow inset pour un rendu plus épais, mais attention au coût de paint si l’ombre est floue.

<style>
.highlight-demo {
  background: linear-gradient(90deg, transparent 0%, #ffd700 50%, transparent 100%);
  background-size: 200% 100%;
  background-position: -100% 0;
  animation: highlight 0.6s ease-in forwards;
  padding: 0 4px;
}
@keyframes highlight {
  to { background-position: 100% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .highlight-demo {
    animation: none;
    background: #ffd700;
    background-size: 100% 100%;
  }
}
</style>

<h2>Texte <span class="highlight-demo">surligné</span> dynamique</h2>

Je suis développeur designer créatif

3. Alternance de mots dynamique

Plusieurs mots empilés en position: absolute, alternant par fondu enchaîné ou slide vertical. CSS pur : créer autant de @keyframes avec opacity et translateY, en décalant animation-delay pour chaque <span>. JS optionnel si le contenu provient d’une API ou change dynamiquement.

<style>
.word-rotator {
  position: relative;
  display: inline-block;
  min-width: 150px;
  height: 1.2em;
}
.word-rotator__word {
  position: absolute;
  left: 0;
  opacity: 0;
  transform: translateY(20px);
  animation: wordRotate 6s infinite;
}
.word-rotator__word:nth-child(1) { animation-delay: 0s; }
.word-rotator__word:nth-child(2) { animation-delay: 2s; }
.word-rotator__word:nth-child(3) { animation-delay: 4s; }
@keyframes wordRotate {
  0%, 33% { opacity: 1; transform: translateY(0); }
  34%, 100% { opacity: 0; transform: translateY(-20px); }
}
@media (prefers-reduced-motion: reduce) {
  .word-rotator__word {
    animation: none;
    position: static;
    opacity: 1 !important;
    transform: none;
    display: inline;
  }
  .word-rotator__word::after { content: " / "; }
  .word-rotator__word:last-child::after { content: ""; }
}
</style>

<p>Je suis 
  <span class="word-rotator">
    <span class="word-rotator__word">développeur</span>
    <span class="word-rotator__word">designer</span>
    <span class="word-rotator__word">créatif</span>
  </span>
</p>

Accessibilité : conserver tous les mots dans le DOM (masqués via opacity, pas display: none) pour que les lecteurs d’écran les détectent. Performance : animer transform et opacity seulement, durée totale cohérente pour éviter la saccade. Mon conseil : limiter à 3-4 mots pour ne pas surcharger le message.

CYBERPUNK

4. Glitch effect sur texte

Décalages RGB courts, text-shadow colorés, clip-path ou transform: skew avec steps() pour un rendu haché. Usage ponctuel : titres, hero tech/gaming. Propriétés clés : animation courte (200-300ms), bursts rares pour ne pas gêner la lecture.

<style>
.glitch-demo {
  position: relative;
  font-weight: bold;
  font-size: 3em;
  animation: glitchTrigger 5s infinite;
}
.glitch-demo::before,
.glitch-demo::after {
  content: attr(data-text);
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
}
@keyframes glitchTrigger {
  0%, 90%, 100% { opacity: 1; }
  91%, 93%, 95%, 97% { 
    opacity: 0.8;
    transform: skew(0.5deg);
  }
}
.glitch-demo::before {
  animation: glitchBefore 0.3s steps(2) infinite;
  text-shadow: -2px 0 #ff00de;
  clip-path: polygon(0 0, 100% 0, 100% 45%, 0 45%);
}
.glitch-demo::after {
  animation: glitchAfter 0.3s steps(2) infinite;
  text-shadow: 2px 0 #00ffff;
  clip-path: polygon(0 55%, 100% 55%, 100% 100%, 0 100%);
}
@keyframes glitchBefore {
  0%, 100% { transform: translateX(0); opacity: 0; }
  50% { transform: translateX(-3px); opacity: 1; }
}
@keyframes glitchAfter {
  0%, 100% { transform: translateX(0); opacity: 0; }
  50% { transform: translateX(3px); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .glitch-demo, .glitch-demo::before, .glitch-demo::after {
    animation: none;
  }
}
</style>

<h1 class="glitch-demo" data-text="CYBERPUNK">CYBERPUNK</h1>

Accessibilité : impératif de désactiver via @media (prefers-reduced-motion: reduce), l’effet peut déclencher nausées ou migraines. Performance : limiter les layers, pas de blur, animation en boucle espacée (toutes les 5-10s max).

Animations de boutons et interactions

Le feedback visuel au hover/clic guide l’action et rassure l’utilisateur. Rester rapide (<200ms), sobre, accessible au clavier (:focus-visible). Mon approche : transition douce par défaut, état actif marqué, respect de prefers-reduced-motion.

5. Bouton avec effet de bordure animée

Contour qui se dessine progressivement. Technique CSS pure : conic-gradient en background avec mask circulaire, ou pseudo-élément avec border et clip-path animé. Alternative SVG : stroke-dasharray / stroke-dashoffset pour un tracé fluide.

<style>
.btn-border-demo {
  position: relative;
  padding: 12px 32px;
  background: transparent;
  border: 2px solid #6366f1;
  color: #6366f1;
  font-weight: 600;
  cursor: pointer;
  overflow: hidden;
  transition: color 0.4s ease;
}
.btn-border-demo::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #6366f1;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s ease;
  z-index: -1;
}
.btn-border-demo:hover::before,
.btn-border-demo:focus-visible::before {
  transform: scaleX(1);
}
.btn-border-demo:hover,
.btn-border-demo:focus-visible {
  color: white;
}
.btn-border-demo:focus-visible {
  outline: 2px solid #6366f1;
  outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
  .btn-border-demo::before { transition: none; }
}
</style>

<button class="btn-border-demo">Découvrir</button>

États : default, hover, :focus-visible avec anneau visible. Accessibilité : l’anneau de focus doit rester distinct de l’animation décorative. Performance : privilégier CSS pur, éviter box-shadow flou qui déclenche du paint. Mon conseil : durée 300-400ms, easing ease-in-out pour un ressenti naturel.

6. Hover avec rotation 3D

Léger tilt 3D (rotateX / rotateY) sur hover, créant une profondeur subtile. Propriétés : transform, perspective sur le parent, transform-origin: center. États : hover, :focus-visible avec même effet.

<style>
.btn-3d-demo {
  padding: 14px 28px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border: none;
  color: white;
  font-weight: 600;
  cursor: pointer;
  perspective: 1000px;
  transition: transform 0.2s ease;
  transform-style: preserve-3d;
}
.btn-3d-demo:hover {
  transform: rotateX(10deg) rotateY(-10deg) translateY(-2px);
}
.btn-3d-demo:focus-visible {
  outline: 3px solid #667eea;
  outline-offset: 4px;
  transform: rotateX(10deg) rotateY(-10deg) translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
  .btn-3d-demo:hover,
  .btn-3d-demo:focus-visible {
    transform: translateY(-2px);
  }
}
</style>

<button class="btn-3d-demo">En savoir plus</button>

Accessibilité : en cas de prefers-reduced-motion, remplacer par une translation douce (translateY(-2px)) ou un changement de couleur. Performance : will-change: transform uniquement au hover pour économiser la mémoire GPU.

7. Ripple effect au clic

Onde circulaire depuis le point de clic, type Material Design. CSS : pseudo-élément avec radial-gradient, animation scale + opacity. JS minimal : capturer event.clientX/Y, passer les coordonnées en CSS custom properties (--x, --y), positionner le pseudo-élément.

<style>
.btn-ripple-demo {
  position: relative;
  padding: 12px 24px;
  background: #10b981;
  border: none;
  color: white;
  font-weight: 600;
  cursor: pointer;
  overflow: hidden;
}
.btn-ripple-demo::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.6);
  width: 100px;
  height: 100px;
  margin-left: -50px;
  margin-top: -50px;
  transform: scale(0);
  opacity: 1;
  pointer-events: none;
  left: var(--ripple-x, 50%);
  top: var(--ripple-y, 50%);
}
.btn-ripple-demo.active::after {
  animation: ripple 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes ripple {
  to {
    transform: scale(4);
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .btn-ripple-demo.active::after { animation: none; }
}
</style>

<button class="btn-ripple-demo" onclick="this.classList.add('active'); setTimeout(()=>this.classList.remove('active'), 600)">Cliquez-moi</button>

Accessibilité : conserver :focus-visible distinct, ajouter aria-pressed si toggle. Performance : nettoyer le layer après animation (animation-fill-mode: forwards puis reset), éviter plusieurs ripples simultanés.

8. Bouton avec remplissage progressif

Fond coloré qui se remplit de gauche à droite au hover. Technique : pseudo-élément ::before en position: absolute, width: 0 par défaut, scaleX(1) au hover avec transform-origin: left. Alternative : background-size animé.

<style>
.btn-fill-demo {
  position: relative;
  padding: 14px 32px;
  background: transparent;
  border: 2px solid #ef4444;
  color: #ef4444;
  font-weight: 600;
  cursor: pointer;
  overflow: hidden;
  z-index: 1;
  transition: color 0.3s ease;
}
.btn-fill-demo::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #ef4444;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
  z-index: -1;
}
.btn-fill-demo:hover::before,
.btn-fill-demo:focus-visible::before {
  transform: scaleX(1);
}
.btn-fill-demo:hover,
.btn-fill-demo:focus-visible {
  color: white;
}
@media (prefers-reduced-motion: reduce) {
  .btn-fill-demo::before { transition-duration: 0.05s; }
}
</style>

<button class="btn-fill-demo">S'inscrire</button>

États : hover, active (clic). Accessibilité : vérifier le contraste texte constant (couleur texte adaptée au fond initial et final). Performance : animer transform seulement (scaleX), pas width. Durée 250-300ms.

Loaders et indicateurs de chargement

Indiquer l’attente sans agacer : durées courtes, cycles cohérents, feedback clair. Toujours ajouter un label « Chargement… » hors écran (sr-only) pour accessibilité. Mon approche : spinner minimaliste par défaut, barre de progression si durée connue, micro-animation si <1s.

9. Spinner minimaliste

Cercle bordé qui tourne en boucle. Technique : div avec border gris léger, border-top-color coloré, animation: rotate 1s linear infinite. Propriétés : @keyframes rotate { to { transform: rotate(360deg); } }.

<style>
.loader-spinner-demo {
  width: 40px;
  height: 40px;
  border: 4px solid rgba(59, 130, 246, 0.1);
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .loader-spinner-demo {
    animation: pulse 1.5s ease-in-out infinite;
  }
  @keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
  }
}
</style>

<div class="loader-spinner-demo" role="progressbar" aria-label="Chargement en cours"></div>

Accessibilité : role="progressbar", aria-label="Chargement en cours", aria-busy="true" sur le conteneur parent. Performance : 60fps garanti, rotation linéaire sans saccade.

Chargement

10. Points de suspension animés

Trois points qui apparaissent en séquence, cycle infini. Technique : trois span, animation: blink 1.4s steps(1) infinite, animation-delay décalé (0s / 0.2s / 0.4s). Keyframes : opacity de 0 à 1 par steps.

<style>
.loader-dots-demo span {
  animation: blink 1.4s steps(1) infinite;
}
.loader-dots-demo span:nth-child(2) {
  animation-delay: 0.2s;
}
.loader-dots-demo span:nth-child(3) {
  animation-delay: 0.4s;
}
.loader-dots-demo span::before {
  content: '.';
}
@keyframes blink {
  0%, 40% { opacity: 0; }
  41%, 100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .loader-dots-demo span {
    animation: none;
    opacity: 1;
  }
}
</style>

<div class="loader-dots-demo">Chargement<span></span><span></span><span></span></div>

Accessibilité : texte « Chargement » visible pour lecteurs d’écran, points décoratifs en aria-hidden si besoin. Performance : cycles courts (1-1.5s total), pas de layout.

75%

11. Barre de progression circulaire

Cercle qui se remplit selon un pourcentage (0-100%). Technique : conic-gradient avec variable CSS --progress, ou SVG circle avec stroke-dasharray / stroke-dashoffset calculés. JS minimal pour mettre à jour --progress ou l’attribut.

<style>
.progress-circle-demo {
  position: relative;
  width: 120px;
  height: 120px;
}
.progress-circle-demo svg {
  transform: rotate(-90deg);
}
.progress-circle-demo circle.bg {
  fill: none;
  stroke: #e5e7eb;
  stroke-width: 8;
}
.progress-circle-demo circle.bar {
  fill: none;
  stroke: #8b5cf6;
  stroke-width: 8;
  stroke-linecap: round;
  stroke-dasharray: 283;
  stroke-dashoffset: 71; /* 75% = 283 - (283 * 75 / 100) */
  transition: stroke-dashoffset 1s ease;
}
.progress-circle-demo span {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-weight: 700;
  font-size: 1.5em;
}
@media (prefers-reduced-motion: reduce) {
  .progress-circle-demo circle.bar {
    transition-duration: 0.01s;
  }
}
</style>

<div class="progress-circle-demo" role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100">
  <svg viewBox="0 0 100 100">
    <circle class="bg" cx="50" cy="50" r="45"></circle>
    <circle class="bar" cx="50" cy="50" r="45"></circle>
  </svg>
  <span>75%</span>
</div>

Accessibilité : role="progressbar", aria-valuenow, aria-valuemin="0", aria-valuemax="100", label textuel du pourcentage. Performance : transition CSS sur --progress ou offset, pas de re-render massif.

12. Loader avec morphing de formes

Blob qui change de forme en boucle. Technique : border-radius animés (valeurs asymétriques type 60% 40% 30% 70% / 60% 30% 70% 40%) ou clip-path: polygon() avec points qui évoluent. Alternative : SVG avec path animé.

<style>
.loader-morph-demo {
  width: 80px;
  height: 80px;
  background: #ec4899;
  border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
  animation: morph 3s ease-in-out infinite;
}
@keyframes morph {
  0%, 100% {
    border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
  }
  33% {
    border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%;
  }
  66% {
    border-radius: 50% 50% 20% 80% / 25% 75% 75% 25%;
  }
}
@media (prefers-reduced-motion: reduce) {
  .loader-morph-demo {
    animation: pulseMorph 2s ease-in-out infinite;
    border-radius: 50%;
  }
  @keyframes pulseMorph {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(0.9); }
  }
}
</style>

<div class="loader-morph-demo" aria-label="Chargement"></div>

Accessibilité : réduire l’amplitude du morphing si prefers-reduced-motion, ou désactiver. Performance : limiter les points clip-path (8-12 max), durées modérées (2-3s par cycle).

Formulaires interactifs

Feedback clair, rassurant, sans distraction. Priorité à la lisibilité et aux états clavier (:focus-visible). Mon approche : validation temps réel avec animations douces, messages d’erreur explicites, respect WCAG.

13. Validation en temps réel

Bordure, couleur, icône évoluent selon :valid / :invalid. Technique CSS pure : input:valid { border-color: green; }, input:invalid:not(:placeholder-shown) { border-color: red; } pour éviter erreur dès l’affichage. Transition douce sur border-color.

<style>
.input-validate-demo {
  padding: 12px;
  border: 2px solid #d1d5db;
  border-radius: 6px;
  transition: border-color 0.15s ease;
  width: 300px;
}
.input-validate-demo:focus {
  outline: none;
  border-color: #3b82f6;
}
.input-validate-demo:valid:not(:placeholder-shown) {
  border-color: #10b981;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%2310b981' stroke-width='2'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 40px;
}
.input-validate-demo:invalid:not(:placeholder-shown) {
  border-color: #ef4444;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23ef4444' stroke-width='2'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 40px;
}
@media (prefers-reduced-motion: reduce) {
  .input-validate-demo { transition: none; }
}
</style>

<input type="email" class="input-validate-demo" placeholder="votre@email.com" required>

Accessibilité : message d’aide en aria-live="polite" pour annoncer erreur/succès, icône accompagnée de texte (pas seulement couleur). Performance : transitions courtes (150ms).

14. Case à cocher avec animation fluide

Check (✓) qui se dessine progressivement au clic. Technique : checkbox native masquée (opacity: 0, position: absolute), label stylé avec SVG check en stroke-dasharray / stroke-dashoffset, transition de l’offset à 0 quand :checked.

<style>
.checkbox-demo {
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
}
.checkbox-demo input {
  position: absolute;
  opacity: 0;
}
.checkbox-demo .box {
  position: relative;
  width: 24px;
  height: 24px;
  border: 2px solid #d1d5db;
  border-radius: 4px;
  transition: all 0.2s ease;
}
.checkbox-demo svg {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0);
  width: 16px;
  height: 16px;
  fill: none;
  stroke: white;
  stroke-width: 3;
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  transition: all 0.2s ease;
}
.checkbox-demo input:checked + .box {
  background: #6366f1;
  border-color: #6366f1;
}
.checkbox-demo input:checked + .box svg {
  transform: translate(-50%, -50%) scale(1);
  stroke-dashoffset: 0;
}
.checkbox-demo input:focus-visible + .box {
  outline: 2px solid #6366f1;
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .checkbox-demo .box,
  .checkbox-demo svg {
    transition-duration: 0.01s;
  }
}
</style>

<label class="checkbox-demo">
  <input type="checkbox">
  <span class="box">
    <svg viewBox="0 0 24 24">
      <polyline points="20 6 9 17 4 12"></polyline>
    </svg>
  </span>
  <span>J'accepte les conditions</span>
</label>

États : default, :focus-visible (anneau), :checked, :disabled. Accessibilité : conserver la checkbox native pour navigation clavier et lecteurs d’écran. Performance : animation <200ms.

15. Input avec label flottant animé

Label positionné dans l’input, qui remonte et rétrécit au focus ou saisie. Technique : :placeholder-shown pour détecter champ vide, transform: translateY() scale() sur le label. Ordre DOM : input puis label, ou label avec pointer-events: none par-dessus.

<style>
.input-float-demo {
  position: relative;
  margin-top: 20px;
}
.input-float-demo input {
  width: 100%;
  padding: 12px;
  border: 2px solid #d1d5db;
  border-radius: 6px;
  font-size: 16px;
  transition: border-color 0.2s ease;
}
.input-float-demo input:focus {
  outline: none;
  border-color: #3b82f6;
}
.input-float-demo label {
  position: absolute;
  left: 12px;
  top: 12px;
  color: #6b7280;
  pointer-events: none;
  transition: all 0.2s ease;
  background: white;
  padding: 0 4px;
}
.input-float-demo input:focus + label,
.input-float-demo input:not(:placeholder-shown) + label {
  top: -10px;
  left: 8px;
  font-size: 12px;
  color: #3b82f6;
}
@media (prefers-reduced-motion: reduce) {
  .input-float-demo label { transition: none; }
}
</style>

<div class="input-float-demo">
  <input type="text" id="name-demo" placeholder=" " required>
  <label for="name-demo">Votre nom</label>
</div>

Accessibilité : <label for="id"> toujours lié à <input id="id">, pas de placeholder seul (mauvais contraste, disparaît). Performance : animer transform, jamais top / font-size directement.

16. Feedback visuel d’erreur

Shake léger horizontal + couleur rouge + message. Technique : @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }, durée 300ms, appliqué sur le champ en erreur.

<style>
.input-error-demo {
  padding: 12px;
  border: 2px solid #ef4444;
  border-radius: 6px;
  animation: shake 0.3s ease;
  width: 300px;
}
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  50% { transform: translateX(4px); }
  75% { transform: translateX(-4px); }
}
.input-error-demo:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}
@media (prefers-reduced-motion: reduce) {
  .input-error-demo { animation: none; }
}
</style>

<input type="text" class="input-error-demo" placeholder="Champ requis">

Accessibilité : icône d’erreur + texte explicite, ne pas compter sur la couleur seule (WCAG). Message en aria-live="assertive" si erreur bloquante. Performance : animation courte, déclenchée une seule fois.

Navigation et menus

17. Menu burger avec transition smooth

Trois barres horizontales qui deviennent une croix. Technique : trois span dans un <button>, transform: rotate(45deg) et rotate(-45deg) sur barres 1 et 3, opacity: 0 sur barre 2, avec transition.

<style>
.burger-demo {
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.burger-demo span {
  width: 30px;
  height: 3px;
  background: #1f2937;
  border-radius: 2px;
  transition: all 0.2s ease;
}
.burger-demo.active span:nth-child(1) {
  transform: rotate(45deg) translateY(9px);
}
.burger-demo.active span:nth-child(2) {
  opacity: 0;
}
.burger-demo.active span:nth-child(3) {
  transform: rotate(-45deg) translateY(-9px);
}
@media (prefers-reduced-motion: reduce) {
  .burger-demo span { transition-duration: 0.01s; }
}
</style>

<button class="burger-demo" aria-label="Menu" onclick="this.classList.toggle('active')">
  <span></span>
  <span></span>
  <span></span>
</button>

Bouton vrai (<button>), aria-controls="menu-id", aria-expanded="false/true" selon état. Accessibilité : label « Menu » visible ou sr-only, navigation clavier. Performance : transitions <200ms.

18. Navigation à tiroirs latérale

Panneau qui slide depuis le bord (gauche ou droite). Technique : position: fixed, transform: translateX(-100%) par défaut, translateX(0) quand ouvert. Overlay en position: fixed avec opacity animée, z-index géré.

<style>
.drawer-demo-container {
  position: relative;
  height: 300px;
  background: #f3f4f6;
  overflow: hidden;
}
.drawer-demo {
  position: absolute;
  top: 0;
  left: 0;
  width: 250px;
  height: 100%;
  background: white;
  box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
  transform: translateX(-100%);
  transition: transform 0.3s ease-out;
  padding: 24px;
  z-index: 10;
}
.drawer-demo.open {
  transform: translateX(0);
}
.drawer-demo-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
  z-index: 9;
}
.drawer-demo-overlay.open {
  opacity: 1;
  pointer-events: all;
}
.drawer-demo button {
  position: absolute;
  top: 12px;
  right: 12px;
  background: none;
  border: none;
  font-size: 24px;
  cursor: pointer;
}
.drawer-demo ul {
  list-style: none;
  padding: 0;
  margin-top: 48px;
}
.drawer-demo li {
  margin-bottom: 12px;
}
.drawer-demo a {
  display: block;
  padding: 10px;
  color: #1f2937;
  text-decoration: none;
  border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
  .drawer-demo, .drawer-demo-overlay { transition-duration: 0.01s; }
}
</style>

<div class="drawer-demo-container">
  <button onclick="document.querySelector('.drawer-demo').classList.add('open'); document.querySelector('.drawer-demo-overlay').classList.add('open')">Ouvrir menu</button>
  
  <nav class="drawer-demo">
    <button onclick="document.querySelector('.drawer-demo').classList.remove('open'); document.querySelector('.drawer-demo-overlay').classList.remove('open')">×</button>
    <ul>
      <li><a href="#accueil">Accueil</a></li>
      <li><a href="#services">Services</a></li>
      <li><a href="#contact">Contact</a></li>
    </ul>
  </nav>
  
  <div class="drawer-demo-overlay" onclick="document.querySelector('.drawer-demo').classList.remove('open'); this.classList.remove('open')"></div>
</div>

Focus trap via JS (enfermer le focus dans le menu ouvert), scroll-lock sur <body>. Accessibilité : role="dialog" si modal, aria-modal="true", bouton fermeture en haut. Performance : will-change: transform pendant transition seulement. Mon conseil : durée 250-300ms, easing ease-out, fermeture au clic overlay ou Escape.

19. Menu dropdown avec effet cascade

Items du sous-menu qui apparaissent avec léger décalage vertical. Technique : li avec opacity: 0, translateY(-10px), transition-delay incrémenté par item (0.05s, 0.1s, 0.15s…). Classe .is-open sur <ul> pour déclencher.

<style>
.dropdown-demo {
  list-style: none;
  padding: 8px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  max-width: 200px;
}
.dropdown-demo li {
  opacity: 0;
  transform: translateY(-10px);
  transition: all 0.15s ease;
}
.dropdown-demo.open li {
  opacity: 1;
  transform: translateY(0);
}
.dropdown-demo.open li:nth-child(1) { transition-delay: 0.05s; }
.dropdown-demo.open li:nth-child(2) { transition-delay: 0.1s; }
.dropdown-demo.open li:nth-child(3) { transition-delay: 0.15s; }
.dropdown-demo.open li:nth-child(4) { transition-delay: 0.2s; }
.dropdown-demo a {
  display: block;
  padding: 10px 16px;
  color: #1f2937;
  text-decoration: none;
  border-radius: 4px;
  transition: background 0.15s ease;
}
.dropdown-demo a:hover {
  background: #f3f4f6;
}
@media (prefers-reduced-motion: reduce) {
  .dropdown-demo li {
    transition: none;
    opacity: 1 !important;
    transform: none !important;
  }
}
</style>

<button onclick="document.querySelector('.dropdown-demo').classList.toggle('open')">Toggle menu</button>
<ul class="dropdown-demo">
  <li><a href="#">Option 1</a></li>
  <li><a href="#">Option 2</a></li>
  <li><a href="#">Option 3</a></li>
  <li><a href="#">Option 4</a></li>
</ul>

Clavier : navigation fléchée haut/bas via JS si nécessaire (focus management). Accessibilité : prefers-reduced-motion = pas de cascade, apparition immédiate. Performance : limiter à 5-8 items max pour la cascade, sinon l’effet devient lourd.

20. Indicateur d’ancre de page actif

Soulignement ou slider coloré qui se déplace sous l’item de menu actif. Technique : pseudo-élément ::after sur le conteneur <nav>, positionné via transform: translateX() et width calculés en JS. Alternative pure CSS : :target sur ancres, mais limité.

<style>
.tab-nav-demo {
  position: relative;
  display: flex;
  gap: 24px;
  border-bottom: 2px solid #e5e7eb;
  padding-bottom: 0;
}
.tab-nav-demo a {
  padding: 12px 16px;
  color: #6b7280;
  text-decoration: none;
  transition: color 0.15s ease;
}
.tab-nav-demo a.active,
.tab-nav-demo a:hover {
  color: #3b82f6;
}
.tab-nav-demo .slider {
  position: absolute;
  bottom: -2px;
  left: 0;
  height: 2px;
  width: 100px;
  background: #3b82f6;
  transition: transform 0.3s ease;
}
@media (prefers-reduced-motion: reduce) {
  .tab-nav-demo .slider { transition-duration: 0.01s; }
}
</style>

<nav class="tab-nav-demo">
  <a href="#" class="active">Accueil</a>
  <a href="#">Services</a>
  <a href="#">Portfolio</a>
  <a href="#">Contact</a>
  <span class="slider"></span>
</nav>

JS optionnel pour sync au scroll : IntersectionObserver détecte la section visible, met à jour aria-current="page" et la position du slider via CSS variables. Accessibilité : aria-current sur l’item actif. Performance : transition transform, 200-300ms. Mon conseil : fallback sans JS = soulignement statique sur .active.

Images et médias

Image de démonstration

21. Zoom au survol d’image

Scale léger (1.05-1.1) dans un conteneur overflow: hidden. Technique : img { transition: transform 0.3s ease; } img:hover { transform: scale(1.1); }. Container en overflow: hidden pour rogner l’excédent.

<style>
.image-zoom-demo {
  overflow: hidden;
  border-radius: 8px;
  max-width: 400px;
}
.image-zoom-demo img {
  display: block;
  width: 100%;
  height: auto;
  transition: transform 0.3s ease;
}
.image-zoom-demo:hover img {
  transform: scale(1.1);
}
@media (prefers-reduced-motion: reduce) {
  .image-zoom-demo img { transition: none; }
  .image-zoom-demo:hover img { transform: none; }
}
@media (hover: none) {
  .image-zoom-demo img { transform: none; }
}
</style>

<div class="image-zoom-demo">
  <img src="https://picsum.photos/400/300" alt="Image de démonstration">
</div>

Accessibilité : effet réservé au hover (desktop), désactiver sur mobile (pas de hover fiable) ou via tap long. Performance : images optimisées (WebP, tailles adaptées), transition douce.

Effet Parallaxe

22. Effet parallaxe sur background

Couches qui bougent à des vitesses différentes au scroll. CSS pur partiel : background-attachment: fixed (limité, perf variable), ou scroll-timeline (support Chrome/Edge uniquement). JS léger sinon : écouter scroll, décaler translateY via requestAnimationFrame.

<style>
.parallax-demo {
  min-height: 400px;
  background-image: url('https://picsum.photos/1200/600');
  background-attachment: fixed;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 2em;
  font-weight: bold;
  text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
@media (prefers-reduced-motion: reduce) {
  .parallax-demo {
    background-attachment: scroll;
  }
}
</style>

<section class="parallax-demo">
  <h2>Effet Parallaxe</h2>
</section>

Performance : limiter couches (2-3 max), éviter DOM lourd, tester FPS mobile. Accessibilité : prefers-reduced-motion = désactiver parallaxe, images statiques. Mon conseil : réserver aux hero sections, pas sur toute la page. Alternative : IntersectionObserver + animation CSS au trigger (plus performant).

Recto

Survolez-moi

Verso

Contenu caché

23. Card flip 3D

Carte recto/verso qui pivote sur l’axe Y. Technique : conteneur transform-style: preserve-3d, deux faces en position: absolute, backface-visibility: hidden, transform: rotateY(180deg) sur le verso par défaut. Hover/clic déclenche rotateY(180deg) sur le conteneur.

<style>
.card-flip-demo {
  perspective: 1000px;
  width: 300px;
  height: 200px;
  margin: 20px auto;
}
.card-flip-demo-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 0.6s ease;
  transform-style: preserve-3d;
}
.card-flip-demo:hover .card-flip-demo-inner {
  transform: rotateY(180deg);
}
.card-flip-demo-front,
.card-flip-demo-back {
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.card-flip-demo-front {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}
.card-flip-demo-back {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  color: white;
  transform: rotateY(180deg);
}
@media (prefers-reduced-motion: reduce) {
  .card-flip-demo-inner { transition: opacity 0.3s ease; }
  .card-flip-demo:hover .card-flip-demo-inner { transform: none; }
  .card-flip-demo:hover .card-flip-demo-front { opacity: 0; }
  .card-flip-demo-back { transform: none; opacity: 0; }
  .card-flip-demo:hover .card-flip-demo-back { opacity: 1; }
}
</style>

<div class="card-flip-demo">
  <div class="card-flip-demo-inner">
    <div class="card-flip-demo-front">
      <h3>Recto</h3>
      <p>Survolez-moi</p>
    </div>
    <div class="card-flip-demo-back">
      <h3>Verso</h3>
      <p>Contenu caché</p>
    </div>
  </div>
</div>

États : hover, focus + touche Enter pour flip. Accessibilité : alternative sans 3D si prefers-reduced-motion (fondu enchaîné). Performance : textures légères, pas de video/iframe en verso tant que non visible.

Démo overlay

Titre

Description révélée au survol

24. Hover effect avec overlay progressif

Overlay coloré qui monte depuis le bas, révélant texte/CTA. Technique : pseudo-élément ::before en position: absolute, height: 0 ou translateY(100%), transition vers height: 100% ou translateY(0). Texte en z-index supérieur, opacity animée.

<style>
.card-overlay-demo {
  position: relative;
  overflow: hidden;
  border-radius: 12px;
  max-width: 400px;
}
.card-overlay-demo img {
  display: block;
  width: 100%;
  height: auto;
  transition: transform 0.3s ease;
}
.card-overlay-demo-content {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.9), transparent);
  color: white;
  padding: 24px;
  transform: translateY(100%);
  transition: transform 0.3s ease;
}
.card-overlay-demo:hover .card-overlay-demo-content {
  transform: translateY(0);
}
.card-overlay-demo:hover img {
  transform: scale(1.05);
}
@media (prefers-reduced-motion: reduce) {
  .card-overlay-demo-content,
  .card-overlay-demo img {
    transition: none;
  }
}
</style>

<div class="card-overlay-demo">
  <img src="https://picsum.photos/400/300" alt="Démo overlay">
  <div class="card-overlay-demo-content">
    <h3>Titre</h3>
    <p>Description révélée au survol</p>
  </div>
</div>

Accessibilité : contraste texte/overlay suffisant (WCAG AA), texte lisible même sans hover (version mobile). Performance : animer transform, pas height (layout).

Effets décoratifs et ambiances

25. Particules flottantes en arrière-plan

Points ou ronds qui dérivent lentement, créant une ambiance. Technique : peu d’éléments DOM (10-20 max), @keyframes avec translate aléatoires + opacity fade in/out, durées longues (10-30s), variations via CSS custom properties pour éviter la répétition.

<style>
.particles-demo {
  position: relative;
  height: 300px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  overflow: hidden;
}
.particles-demo span {
  position: absolute;
  width: 8px;
  height: 8px;
  background: rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  animation: float-particle ease-in-out infinite;
  opacity: 0;
}
.particles-demo span:nth-child(1) {
  left: 10%;
  top: 20%;
  animation-duration: 15s;
  animation-delay: 0s;
}
.particles-demo span:nth-child(2) {
  left: 80%;
  top: 60%;
  animation-duration: 20s;
  animation-delay: 2s;
}
.particles-demo span:nth-child(3) {
  left: 50%;
  top: 80%;
  animation-duration: 18s;
  animation-delay: 4s;
}
.particles-demo span:nth-child(4) {
  left: 30%;
  top: 40%;
  animation-duration: 22s;
  animation-delay: 1s;
}
.particles-demo span:nth-child(5) {
  left: 70%;
  top: 10%;
  animation-duration: 16s;
  animation-delay: 3s;
}
@keyframes float-particle {
  0%, 100% {
    transform: translate(0, 0);
    opacity: 0;
  }
  10% { opacity: 1; }
  90% { opacity: 1; }
  50% {
    transform: translate(20px, -300px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .particles-demo span {
    animation: none;
    opacity: 0.3;
  }
}
</style>

<div class="particles-demo" aria-hidden="true">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

Performance : éviter gros flous (filter: blur), tailles modestes, pas de box-shadow. Accessibilité : prefers-reduced-motion = off ou statique.

26. Effet de chute de neige

Flocons qui tombent, vitesses et trajectoires variées. Technique : div avec border-radius: 50%, @keyframes translateY du haut vers le bas, animation-duration et animation-delay aléatoires via CSS variables ou JS. Légère oscillation horizontale (translateX) pour naturel.

Performance : limiter nombre (30-50 flocons max), taille petite (4-8px), pas de blur. Accessibilité : prefers-reduced-motion = désactiver.

27. Système solaire animé

Planètes en orbite autour d’un soleil central. Technique : imbrication de div (orbite parent, planète enfant), transform-origin: center sur orbite, animation: rotate infinie avec durées différentes. Planète positionnée en absolute sur le bord de l’orbite.

<style>
.solar-demo {
  position: relative;
  width: 400px;
  height: 400px;
  margin: 50px auto;
  background: radial-gradient(circle, #1a1a2e 0%, #0f0f1e 100%);
  border-radius: 8px;
}
.solar-demo-sun {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  background: radial-gradient(circle, #ffd700, #ff8c00);
  border-radius: 50%;
  box-shadow: 0 0 30px rgba(255, 215, 0, 0.6);
}
.solar-demo-orbit {
  position: absolute;
  top: 50%;
  left: 50%;
  border: 1px dashed rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  transform: translate(-50%, -50%);
}
.solar-demo-orbit:nth-child(2) {
  width: 120px;
  height: 120px;
  animation: rotate-orbit 8s linear infinite;
}
.solar-demo-orbit:nth-child(3) {
  width: 200px;
  height: 200px;
  animation: rotate-orbit 15s linear infinite;
}
.solar-demo-orbit:nth-child(4) {
  width: 280px;
  height: 280px;
  animation: rotate-orbit 25s linear infinite;
}
.solar-demo-planet {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  border-radius: 50%;
}
.solar-demo-orbit:nth-child(2) .solar-demo-planet {
  width: 12px;
  height: 12px;
  background: #4a90e2;
}
.solar-demo-orbit:nth-child(3) .solar-demo-planet {
  width: 16px;
  height: 16px;
  background: #e24a4a;
}
.solar-demo-orbit:nth-child(4) .solar-demo-planet {
  width: 10px;
  height: 10px;
  background: #9b59b6;
}
@keyframes rotate-orbit {
  to { transform: translate(-50%, -50%) rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .solar-demo-orbit { animation: none; }
}
</style>

<div class="solar-demo" aria-hidden="true">
  <div class="solar-demo-sun"></div>
  <div class="solar-demo-orbit">
    <div class="solar-demo-planet"></div>
  </div>
  <div class="solar-demo-orbit">
    <div class="solar-demo-planet"></div>
  </div>
  <div class="solar-demo-orbit">
    <div class="solar-demo-planet"></div>
  </div>
</div>

Accessibilité : décoratif via aria-hidden="true" si fond, sinon décrire via sr-only. Performance : réduire nombre de planètes (3-5), pas de box-shadow lourd, échelles raisonnables.

28. Sous-marin en mouvement

Trajectoire ondulée avec bulles montantes. Technique : @keyframes combinant translateX linéaire et translateY sinusoïdal (approximation via plusieurs étapes), ou offset-path SVG si support suffisant. Bulles : petits div animés en translateY(-100vh) + opacity fade.

<style>
.submarine-demo {
  position: relative;
  width: 100%;
  height: 300px;
  background: linear-gradient(to bottom, #1e3a8a 0%, #0c4a6e 100%);
  overflow: hidden;
}
.submarine-demo-sub {
  position: absolute;
  width: 80px;
  height: 40px;
  background: #fbbf24;
  border-radius: 50% 50% 0 0 / 60% 60% 0 0;
  animation: swim 15s ease-in-out infinite;
}
.submarine-demo-sub::before {
  content: '';
  position: absolute;
  bottom: -10px;
  left: 50%;
  transform: translateX(-50%);
  width: 60px;
  height: 20px;
  background: #f59e0b;
  border-radius: 0 0 50% 50%;
}
.submarine-demo-sub::after {
  content: '';
  position: absolute;
  top: -15px;
  left: 20px;
  width: 15px;
  height: 20px;
  background: #1e40af;
  border-radius: 50% 50% 0 0;
}
@keyframes swim {
  0% {
    left: -100px;
    top: 50%;
  }
  25% { top: 30%; }
  50% {
    left: 50%;
    top: 60%;
  }
  75% { top: 40%; }
  100% {
    left: calc(100% + 100px);
    top: 50%;
  }
}
.submarine-demo span {
  position: absolute;
  bottom: 0;
  width: 6px;
  height: 6px;
  background: rgba(255, 255, 255, 0.6);
  border-radius: 50%;
  animation: rise 4s ease-in infinite;
}
.submarine-demo span:nth-child(2) {
  left: 40%;
  animation-delay: 0s;
}
.submarine-demo span:nth-child(3) {
  left: 45%;
  animation-delay: 1s;
}
.submarine-demo span:nth-child(4) {
  left: 50%;
  animation-delay: 2s;
}
@keyframes rise {
  0% {
    bottom: 0;
    opacity: 1;
  }
  100% {
    bottom: 100%;
    opacity: 0;
    transform: translateX(20px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .submarine-demo-sub,
  .submarine-demo span {
    animation: none;
  }
  .submarine-demo-sub {
    left: 50%;
    top: 50%;
  }
}
</style>

<div class="submarine-demo" aria-hidden="true">
  <div class="submarine-demo-sub"></div>
  <span></span>
  <span></span>
  <span></span>
</div>

JS optionnel pour trajectoire précise (calcul sinus). Performance : cycles lents (10-20s), pas de blur massif sur eau, limiter bulles (5-10). Accessibilité : décoratif, aria-hidden.

SVG et icônes

Icône dessinée

29. Tracé progressif de SVG (stroke animation)

Trait qui se dessine progressivement. Technique : stroke-dasharray égal à la longueur du path (getTotalLength() en JS), stroke-dashoffset animé de cette valeur à 0 en CSS. @keyframes draw { to { stroke-dashoffset: 0; } }.

<style>
.svg-draw-demo {
  display: block;
  margin: 20px auto;
}
.svg-draw-demo path {
  stroke-dasharray: 400;
  stroke-dashoffset: 400;
  animation: draw-path 2s ease-in-out forwards;
}
@keyframes draw-path {
  to { stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .svg-draw-demo path {
    animation: none;
    stroke-dashoffset: 0;
  }
}
</style>

<svg class="svg-draw-demo" width="200" height="200" viewBox="0 0 200 200" role="img">
  <title>Icône dessinée</title>
  <path d="M10,100 Q50,10 100,100 T190,100" fill="none" stroke="#6366f1" stroke-width="3"/>
</svg>

Accessibilité : <title> et <desc> dans le SVG pour lecteurs d’écran, role="img" sur <svg>. Performance : simplifier le path avec SVGO pour réduire points, durée cohérente avec complexité (1-3s).

Paramètres Paramètres

30. Icône animée au hover

Rotation, scale, ou changement de couleur stroke/fill au survol. Technique : svg { transition: transform 0.2s; } svg:hover { transform: rotate(15deg) scale(1.1); }, ou path { transition: fill 0.2s; }. transform-origin: center pour rotation équilibrée.

<style>
.icon-link-demo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: #1f2937;
  padding: 12px 20px;
  border-radius: 8px;
  transition: background 0.2s ease;
}
.icon-link-demo:hover {
  background: #f3f4f6;
}
.icon-link-demo svg {
  transition: transform 0.2s ease;
  transform-origin: center;
}
.icon-link-demo:hover svg {
  transform: rotate(15deg) scale(1.1);
}
.icon-link-demo:hover svg path,
.icon-link-demo:hover svg circle {
  stroke: #6366f1;
}
.icon-link-demo svg path,
.icon-link-demo svg circle {
  transition: stroke 0.2s ease;
}
@media (prefers-reduced-motion: reduce) {
  .icon-link-demo svg { transition: none; }
  .icon-link-demo:hover svg { transform: scale(1.05); }
}
</style>

<a href="#" class="icon-link-demo">
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" role="img">
    <title>Paramètres</title>
    <circle cx="12" cy="12" r="3"></circle>
    <path d="M12 1v6m0 6v6m4.22-13.22l-1.42 1.42M10.2 14.8l-1.42 1.42m8.44 0l-1.42-1.42M10.2 9.2l-1.42-1.42M23 12h-6m-6 0H1"></path>
  </svg>
  Paramètres
</a>

États : hover, :focus-visible si lien/bouton. Accessibilité : icône décorative = aria-hidden, icône sémantique = <title> + role="img". Performance : micro-animation <200ms, pas de filter lourd.

Comment intégrer ces animations CSS dans vos projets ?

Méthode d’intégration propre : variables CSS pour durées/délais, classes BEM pour réutilisabilité, progressive enhancement (animation = bonus, pas bloquante). Tester systématiquement : clavier (Tab, Enter, Espace), contrastes (WCAG AA), prefers-reduced-motion, DevTools Performance (60fps, layers). Mon approche : fichier _animations.css centralisé, tokens de vitesse (fast/normal/slow), composants documentés.

Les propriétés CSS essentielles à maîtriser

transition : changement d’état fluide (hover, focus), syntaxe property duration easing delay. animation + @keyframes : séquences complexes, boucles, déclenchement automatique. transform : translate (déplacements), scale (zoom), rotate (rotation), skew (inclinaison), combinables, performants (compositing GPU).

transform-origin : point pivot des transformations (center par défaut). perspective : profondeur 3D sur parent. opacity : transparence, compositée GPU. filter : blur, brightness, contrast (à dose, coûteux en paint). clip-path / mask : découpes complexes. background gradients : linear-gradient, conic-gradient pour effets colorés.

steps() : animations discrètes (sprite, typewriter). cubic-bezier() : easing personnalisé (cubic-bezier.com). will-change : hint navigateur, à utiliser avec parcimonie (RAM/GPU). contain : isoler render/layout. scroll-timeline : animations pilotées scroll (support partiel, Chrome/Edge).

Performance et optimisation : ce qu’il faut éviter

Éviter absolument : animer top, left, width, height (layout coûteux), gros blur ou box-shadow flous (paint intensif), trop d’éléments animés simultanément (surcharge GPU), animations lourdes au chargement (CLS, LCP).

Techniques d’optimisation : privilégier compositing (transform / opacity), limiter will-change au strict nécessaire (activer au hover, désactiver après), réduire durées (150-600ms suffisent 90% du temps), réduire DOM (moins d’éléments = moins de layers), images optimisées (WebP, srcset), désactiver animations décoratives sur mobile si budget perf serré.

Accessibilité et performance liées : @media (prefers-reduced-motion: reduce) pour désactiver animations non essentielles ou les alléger, tester 60fps dans DevTools Performance, onglet Rendering (paint flashing, layer borders). Mon workflow : mesurer avant/après (Lighthouse, WebPageTest), fallback simple (transition fade si 3D trop lourde), jamais d’animations décoratives sur pages critiques (checkout, formulaires de contact, landing pages à fort enjeu conversions).

Outils et ressources pour aller plus loin avec les animations CSS

Prototypage et génération : CodePen / JSFiddle pour tester rapidement, Animista pour presets prêts à l’emploi (copier/coller), cubic-bezier.com pour créer easings personnalisés, Keyframes.app pour générer animations complexes visuellement.

Optimisation SVG : SVGOMG pour nettoyer/compresser SVG (réduire points path, alléger markup), essentiel avant stroke animation. Compatibilité : Can I use pour vérifier support navigateurs (scroll-timeline, clip-path, backdrop-filter).

DevTools : Chrome/Firefox Performance pour profiler FPS, Rendering tab (paint flashing, layer borders, FPS meter), Animations panel pour ralentir/rejouer animations, inspecter timings. Documentation et guides : MDN (