Les bases du CSS
Syntaxe, sélecteurs, modèle de boîte
Ouvrir le cours
Une interface qui bouge se comprend mieux qu’une interface qui saute. Transitions et keyframes rendent visibles les changements d’état — à condition de rester sobres.
Une animation n’est pas un ornement : elle dit ce qui vient de changer, et où regarder.
Le mouvement relie l’action à son effet. Sans lui, l’interface saute d’un état à l’autre et l’œil doit deviner ce qui a bougé.
Animer transform et opacity coûte presque rien : le navigateur les confie à la carte graphique. Animer width ou top le force à tout recalculer.
La façon dont une interface bouge se reconnaît autant que ses couleurs. Une courbe d’accélération est une signature.
D’un état à un autre, déclenchée par quelque chose — un survol, un clic, une classe ajoutée.
css
.bouton {
transition: all 0.3s ease;
}
.bouton:hover {
transform: translateY(-4px);
}
Une suite d’étapes décrites à l’avance, qui peut tourner seule et se répéter.
css
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.element {
animation: pulse 2s infinite;
}
Quatre réglages suffisent : ce qui change, en combien de temps, selon quelle courbe, et après quel délai.
css
.element {
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
transition-delay: 0s;
}
/* Raccourci */
.element {
transition: all 0.3s ease 0s;
}
transition-duration
transition-duration: ;
Sous 0,1 s le mouvement passe inaperçu ; au-delà d’une seconde il se fait attendre. 0,3 s est le réglage courant.
transition-timing-function
transition-timing-function: ;
linear avance à vitesse constante — c’est ce qui fait mécanique. ease part vite et ralentit : c’est le défaut, et c’est le plus naturel.
transition-property
transition-property: ;
all est commode mais anime tout, y compris ce qui coûte cher. Nommer la propriété est plus sûr.
transition-delay
transition-delay: ;
Un délai sert à échelonner plusieurs éléments. Au-delà de 0,3 s, l’interface semble ne pas répondre.
transform
transform: ;
Les transformations se combinent dans une seule déclaration, et s’appliquent de droite à gauche.
Une transition attend un déclencheur. Une animation décrit ses étapes une fois pour toutes et tourne d’elle-même.
@keyframes
Un mouvement qui se répète sans fin doit pouvoir être arrêté — c’est le critère 2.2.2 des règles d’accessibilité. Votre système peut déjà le demander : si vous avez réduit les animations, la galerie arrive figée.
bounce
animation: bounce 1s infinite;
pulse
animation: pulse 2s infinite;
shake
animation: shake 0.5s infinite;
rotate
animation: rotate 2s linear infinite;
fadeInUp
animation: fadeInUp 1s ease-out infinite;
slideInRight
animation: slideInRight 1s ease-out infinite;
zoomIn
animation: zoomIn 0.5s ease-out infinite;
heartbeat
animation: heartbeat 1.5s infinite;
Chaque vignette porte la déclaration qui la met en mouvement. Les dix règles @keyframes correspondantes sont plus bas.
css
/* Définition des keyframes */
@keyframes slideIn {
0% {
transform: translateX(-100%);
opacity: 0;
}
50% {
transform: translateX(10px);
opacity: 0.8;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
/* Application de l'animation */
.element {
animation: slideIn 1s ease-out;
}
css
/* Syntaxe complète */
.element {
animation-name: slideIn;
animation-duration: 1s;
animation-timing-function: ease-out;
animation-delay: 0.2s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards;
animation-play-state: running;
}
/* Raccourci */
.element {
animation: slideIn 1s ease-out 0.2s infinite alternate forwards;
}
C’est la propriété qui rend le bouton de pause possible. Un mouvement qui se répète sans fin doit pouvoir être arrêté — critère 2.2.2. La galerie ci-dessus en est l’exemple : un bouton, et le respect de prefers-reduced-motion.
Le cas le plus courant d’animation qui tourne en boucle — et donc celui où la pause compte le plus.
css
/* Spinner simple */
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes
Un mouvement qui se répète sans fin doit pouvoir être arrêté — c’est le critère 2.2.2 des règles d’accessibilité. Votre système peut déjà le demander : si vous avez réduit les animations, la galerie arrive figée.
spin
animation: spin 1s linear infinite;
dotPulse
animation: dotPulse 1.4s ease-in-out infinite both;
pulse
animation: pulse 2s infinite;
bounce
animation: bounce 1s infinite;
Les trois tournent en boucle : ce sont exactement les cas visés par le critère 2.2.2.
Les mêmes propriétés, appliquées à ce qu’on rencontre sur de vrais sites.
Un bouton qui réagit au survol et au clic. Le reflet qui traverse vient d’un pseudo-élément décalé, animé par sa seule position.
css
.btn-shimmer {
position: relative;
overflow: hidden;
background: #3b82f6;
transition: transform 0.1s ease;
}
.btn-shimmer::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s ease;
}
.btn-shimmer:hover::before {
left: 100%;
}
.btn-shimmer:active {
transform: scale(0.95);
}
Le contenu entre en glissant, et les éléments d’une liste se suivent au lieu d’arriver ensemble. Le décalage se règle avec animation-delay.
css
.page-enter {
opacity: 0;
transform: translateY(30px);
animation: pageEnter 0.6s ease-out forwards;
}
@keyframes pageEnter {
to {
opacity: 1;
transform: translateY(0);
}
}
/* Décalage en cascade */
.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; }
Au-delà des cinq courbes nommées, cubic-bezier() en décrit n’importe laquelle. Une valeur hors de [0, 1] fait dépasser la cible — c’est ce qui donne l’effet élastique.
css
/* Carte qui se soulève */
.card-effect {
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
/* Rebond élastique */
.elastique {
transition: all 0.3s cubic-bezier(0.68, -0.6, 0.32, 1.6);
}
/* Reflet qui traverse */
.reflet {
transition: transform 0.5s ease;
opacity: 0.7;
}
/* Raccourci complet */
.element {
transition: all 0.3s ease 0s;
}
Deux propriétés seulement sont confiées à la carte graphique : transform et opacity. Toutes les autres forcent le navigateur à recalculer la mise en page à chaque image.
css
/* ✅ Animez transform et opacity */
.rapide {
transform: translateX(100px);
opacity: 0.5;
}
/* Propriétés coûteuses */
.expensive {
left: 100px; /* ❌ Layout */
width: 200px; /* ❌ Layout */
height: 100px; /* ❌ Layout */
background: #ff0000; /* ❌ Paint */
box-shadow: 0 0 10px red; /* ❌ Paint */
}
/* Prévenir le navigateur */
.gpu-accelerated {
will-change: transform, opacity;
transform: translateZ(0);
}
Créez une interface de jeu mobile complète avec des animations fluides et des micro-interactions engageantes.
html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Game UI - Animation Exercise</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #1a1a2e, #16213e, #0f3460);
color: white;
min-height: 100vh;
overflow-x: hidden;
}
/* Splash Screen */
.splash-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
/* TODO: Animation d'apparition et disparition */
}
.logo {
font-size: 3rem;
font-weight: bold;
text-transform: uppercase;
/* TODO: Animation de logo (scale + rotate + glow) */
}
/* Game Container */
.game-container {
max-width: 400px;
margin: 0 auto;
padding: 2rem;
/* TODO: Animation d'entrée de page */
}
/* Header avec titre */
.game-title {
text-align: center;
font-size: 2rem;
font-weight: bold;
margin-bottom: 2rem;
background: linear-gradient(45deg, #f1c40f, #e67e22);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
/* TODO: Animation de pulsation et brillance */
}
/* Barre de vie */
.health-container {
margin-bottom: 2rem;
}
.health-label {
font-size: 0.9rem;
margin-bottom: 0.5rem;
opacity: 0.8;
}
.health-bar {
background: rgba(255,255,255,0.1);
height: 20px;
border-radius: 10px;
overflow: hidden;
position: relative;
}
.health-fill {
height: 100%;
background: linear-gradient(90deg, #e74c3c, #f39c12, #2ecc71);
border-radius: 10px;
transition: width 0.5s ease;
/* TODO: Animation de pulsation basée sur le niveau */
}
/* Score animé */
.score-container {
text-align: center;
margin-bottom: 2rem;
}
.score-value {
font-size: 2.5rem;
font-weight: bold;
color: #f1c40f;
/* TODO: Animation de comptage + bounce lors des gains */
}
/* Power-ups */
.powerups-container {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 2rem;
}
.powerup {
width: 60px;
height: 60px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
cursor: pointer;
transition: all 0.3s ease;
/* TODO: Animations différentes pour chaque power-up */
}
.powerup-lightning {
background: linear-gradient(135deg, #3498db, #2980b9);
/* TODO: Animation de rotation */
}
.powerup-fire {
background: linear-gradient(135deg, #e74c3c, #c0392b);
/* TODO: Animation de flamme (scale + glow) */
}
.powerup-gem {
background: linear-gradient(135deg, #9b59b6, #8e44ad);
/* TODO: Animation de brillance */
}
/* Boutons de jeu */
.game-buttons {
display: flex;
flex-direction: column;
gap: 1rem;
}
.btn {
padding: 1rem;
border: none;
border-radius: 12px;
font-size: 1.1rem;
font-weight: bold;
cursor: pointer;
position: relative;
overflow: hidden;
transition: all 0.2s ease;
/* TODO: Micro-interactions (hover + active) */
}
.btn-primary {
background: linear-gradient(135deg, #2ecc71, #27ae60);
color: white;
}
.btn-secondary {
background: linear-gradient(135deg, #3498db, #2980b9);
color: white;
}
/* Notifications */
.notification {
position: fixed;
top: 2rem;
right: 2rem;
background: linear-gradient(135deg, #f39c12, #e67e22);
color: white;
padding: 0.8rem 1.2rem;
border-radius: 25px;
font-weight: bold;
transform: translateX(100%);
/* TODO: Animation slide-in/slide-out */
}
/* Loading spinner */
.loading {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 999;
}
.spinner {
width: 50px;
height: 50px;
border: 4px solid rgba(255,255,255,0.3);
border-top: 4px solid #f1c40f;
border-radius: 50%;
/* TODO: Animation de rotation */
}
/* Keyframes à définir */
/* TODO: @keyframes pour splash screen */
/* TODO: @keyframes pour logo animation */
/* TODO: @keyframes pour health bar pulse */
/* TODO: @keyframes pour score counting */
/* TODO: @keyframes pour power-ups effects */
/* TODO: @keyframes pour notifications */
/* TODO: @keyframes pour celebration effects */
/* Responsive */
@media (max-width: 480px) {
.game-container {
padding: 1rem;
}
.game-title {
font-size: 1.5rem;
}
.powerup {
width: 50px;
height: 50px;
font-size: 1.2rem;
}
}
/* Accessibility */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<!-- Splash Screen -->
<div class="splash-screen" id="splashScreen">
<div class="logo" id="logo">Super Game</div>
</div>
<!-- Game UI -->
<div class="game-container" id="gameContainer">
<h1 class="game-title">Super Game</h1>
<!-- Health Bar -->
<div class="health-container">
<div class="health-label">Health:</div>
<div class="health-bar">
<div class="health-fill" id="healthFill" style="width: 75%;"></div>
</div>
</div>
<!-- Score -->
<div class="score-container">
<div class="score-value" id="scoreValue">0</div>
<div style="font-size: 0.9rem; opacity: 0.7;">Score</div>
</div>
<!-- Power-ups -->
<div class="powerups-container">
<div class="powerup powerup-lightning" id="powerupLightning">⚡</div>
<div class="powerup powerup-fire" id="powerupFire">🔥</div>
<div class="powerup powerup-gem" id="powerupGem">💎</div>
</div>
<!-- Game Buttons -->
<div class="game-buttons">
<button class="btn btn-primary" id="playBtn">PLAY GAME</button>
<button class="btn btn-secondary" id="settingsBtn">Settings</button>
<button class="btn btn-secondary" id="leaderboardBtn">Leaderboard</button>
</div>
</div>
<!-- Notification -->
<div class="notification" id="notification">+100 XP Earned!</div>
<!-- Loading -->
<div class="loading" id="loading">
<div class="spinner"></div>
</div>
<script>
// TODO: JavaScript pour déclencher les animations
// Splash screen animation
function hideSplashScreen() {
// TODO: Implémenter la disparition du splash screen
}
// Score animation
function animateScore(targetScore) {
// TODO: Animation de comptage du score
}
// Health bar animation
function updateHealth(percentage) {
// TODO: Animer la barre de vie
}
// Notification system
function showNotification(message) {
// TODO: Afficher une notification animée
}
// Power-up effects
function activatePowerup(type) {
// TODO: Animation d'activation des power-ups
}
// Initialize game
setTimeout(hideSplashScreen, 3000);
setTimeout(() => animateScore(12450), 4000);
</script>
</body>
</html>
Vous savez faire réagir un élément, décrire une suite d’étapes, choisir une courbe — et rendre tout cela arrêtable. C’est la dernière des cinq pages CSS de référence.
transform et opacity d’abord : le reste force un recalcul
prefers-reduced-motion, et un moyen d’arrêter le mouvement
Une animation qui se remarque a déjà échoué
Syntaxe, sélecteurs, modèle de boîte
Ouvrir le coursAligner sur un axe
Ouvrir le coursComposer en deux dimensions
Ouvrir le coursS’adapter à chaque écran
Ouvrir le cours