Maîtriser le stylisme des images en CSS : responsive design, effets visuels, clip-path, filtres, et bien plus.
Les images sont un pilier du web moderne. En CSS, on ne se contente pas de les afficher : on les transforme, on les masque, on les filtre, et on crée des effets interactifs au survol. Ce chapitre couvre tout l'arsenal dont vous disposez.
<img>, <picture>, <figure>background-image, content, mask-image| Capacité | Propriétés CSS | Usage |
|---|---|---|
| Dimensionnement | max-width, width, aspect-ratio |
Images responsives, proportions fixes |
| Ajustement | object-fit, object-position |
Recadrage intelligent dans un conteneur |
| Filtrage | filter |
Flou, luminosité, grayscale, etc. |
| Masquage | clip-path, mask-image |
Formes non-rectangulaires |
| Mélange | mix-blend-mode |
Fusion artistique avec l'arrière-plan |
| Résolution | image-set(), srcset |
Images adaptatives selon la densité d'écran |
Par défaut, <img> est un élément inline (il se comporte comme du texte dans une ligne). Pour le contrôler facilement, on le passe souvent en block.
/* Par défaut, img est inline */
img {
display: inline; /* comportement par défaut */
}
/* Souvent plus utile : mode bloc */
img {
display: block; /* prend toute la largeur disponible */
max-width: 100%; /* ne dépasse jamais son conteneur */
height: auto; /* conserve le ratio d'aspect */
}
img {
max-width: 100%; /* Jamais plus large que le conteneur */
height: auto; /* Ratio préservé */
}
/* Alternative moderne avec aspect-ratio */
img.portrait {
width: 100%;
aspect-ratio: 3 / 4; /* Format portrait */
object-fit: cover; /* Recadre pour remplir */
}
object-fit contrôle comment le contenu d'une image remplit son conteneur — c'est l'équivalent de background-size pour les <img>.
img {
width: 300px;
height: 200px;
object-fit: cover; /* Remplit en conservant le ratio, coupe le surplus */
object-position: center; /* Centre le point focal */
}
| Valeur | Comportement |
|---|---|
fill |
Remplit le conteneur (défaut) — peut déformer l'image |
contain |
Affiche l'image entière dans le conteneur — peut laisser des espaces |
cover |
Remplit le conteneur en conservant le ratio — coupe le surplus |
none |
Aucun ajustement — taille originale |
scale-down |
Comme none ou contain, whichever est le plus petit |
/* Point focal personnalisé */
img {
object-fit: cover;
object-position: 30% 70%; /* 30% depuis la gauche, 70% depuis le haut */
}
/* Valeurs keyword */
img {
object-position: center top;
object-position: left center;
}
img {
width: 100%;
aspect-ratio: 16 / 9; /* Format paysage */
}
img.carré {
aspect-ratio: 1 / 1; /* Carré parfait */
}
img.portrait {
aspect-ratio: 3 / 4; /* Portrait */
}
img.banniere {
aspect-ratio: 21 / 9; /* Ultra-large */
}
/* Fournir différentes résolutions selon l'écran */
.hero {
background-image: image-set(
url("hero-1x.jpg") 1x,
url("hero-2x.jpg") 2x,
url("hero-3x.jpg") 3x
);
}
/* Format et media query */
.hero {
background-image: image-set(
url("hero.webp") type("image/webp"),
url("hero.jpg") type("image/jpeg")
);
}
image-set() fonctionne pour les background-image. Pour les balises <img>, utilisez l'attribut HTML srcset avec <picture>.
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.avif" type="image/avif">
<img src="image.jpg" alt="Description">
</picture>
<!-- Switch selon la taille de l'écran -->
<picture>
<source media="(min-width: 800px)" srcset="large.jpg">
<source media="(min-width: 400px)" srcset="medium.jpg">
<img src="small.jpg" alt="Responsive">
</picture>
filter applique des effets visuels directement au navigateur. C'est l'un des outils les plus puissants du CSS moderne.
img {
filter: none; /* valeur par défaut */
}
/* Fonctions disponibles : */
img.blur { filter: blur(4px); }
img.bright { filter: brightness(1.5); }
img.contrast { filter: contrast(2); }
img.gray { filter: grayscale(100%); }
img.sepia { filter: sepia(100%); }
img.hue { filter: hue-rotate(90deg); }
img.saturate { filter: saturate(2); }
img.invert { filter: invert(100%); }
img.opacity { filter: opacity(0.5); }
img.shadow { filter: drop-shadow(4px 4px 8px rgba(0,0,0,0.3)); }
/* Combinaison de filtres */
img.combined {
filter: grayscale(50%) contrast(1.2) brightness(1.1) saturate(1.3);
}
| Fonction | Valeurs | Description |
|---|---|---|
blur() |
px (rayon) | Flou gaussien |
brightness() |
0% — 200%+ | Luminosité (100% = normal) |
contrast() |
0% — 200%+ | Contraste (100% = normal) |
grayscale() |
0% — 100% | Niveaux de gris |
sepia() |
0% — 100% | Effet sépia vintage |
hue-rotate() |
deg (0° — 360°) | Rotation des teintes |
saturate() |
0% — 200%+ | Saturation des couleurs |
invert() |
0% — 100% | Inversion des couleurs (négatif) |
opacity() |
0% — 100% | Opacité (fonctionne différemment de opacity car dans le filter) |
drop-shadow() |
offset-x offset-y blur radius color | Ombre portée qui suit la forme de l'image |
filter: drop-shadow() suit la forme alpha de l'image (utile pour les PNGs transparents), tandis que box-shadow suit toujours le rectangle. Pour des ombres sur des images non-rectangulaires, utilisez drop-shadow.
img {
mix-blend-mode: multiply; /* Multiplier — assombrit */
mix-blend-mode: screen; /* Écran — éclaircit */
mix-blend-mode: overlay; /* Superposition — contraste */
mix-blend-mode: darken; /* Assombrir — garde le plus sombre */
mix-blend-mode: lighten; /* Éclaircir — garde le plus clair */
mix-blend-mode: color-dodge; /* Éclaircir par couleur */
mix-blend-mode: difference; /* Différence — effet artistique */
}
clip-path découpe un élément selon une forme géométrique.
/* Cercle */
img {
clip-path: circle(50%); /* Rayon = 50% de la largeur */
}
/* Ellipse */
img {
clip-path: ellipse(40% 50%); /* rx ry */
}
/* Losange (polygon) */
img {
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
/* Rectangle avec coins arrondis (inset) */
img {
clip-path: inset(10% round 16px);
}
/* Étoile */
img {
clip-path: polygon(
50% 0%, 61% 35%, 98% 35%,
68% 57%, 79% 91%, 50% 70%,
21% 91%, 32% 57%, 2% 35%, 39% 35%
);
}
/* Dégradé linéaire — fondu vers le bas */
img {
mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
-webkit-mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
}
/* Dégradé radial — cercle central */
img {
mask-image: radial-gradient(circle, black 40%, transparent 70%);
-webkit-mask-image: radial-gradient(circle, black 40%, transparent 70%);
}
/* Avec une image comme masque */
img {
mask-image: url("masque.png");
mask-size: cover;
mask-repeat: no-repeat;
}
-webkit-mask-image pour une meilleure compatibilité, notamment sur Safari.
Comparaison visuelle des valeurs de object-fit
Formes de clip-path : cercle, ellipse, polygon, inset
Démonstration de toutes les fonctions filter avec un dégradé de test
img {
display: block;
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 0 auto;
}
Rendu :
/* cover : remplit le conteneur, coupe le surplus */
.img-cover {
width: 100%;
height: 300px;
object-fit: cover;
object-position: center;
}
/* contain : affiche l'image entière, espaces restants */
.img-contain {
width: 100%;
height: 300px;
object-fit: contain;
background-color: #f1f5f9;
}
Rendu :
object-fit: cover
object-fit: contain
.img-grayscale-hover img {
filter: grayscale(100%);
transition: filter 0.5s ease;
}
.img-grayscale-hover:hover img {
filter: grayscale(0%);
}
Rendu :
.hover-zoom {
overflow: hidden;
border-radius: 8px;
}
.hover-zoom img {
width: 100%;
height: 250px;
object-fit: cover;
transition: transform 0.4s ease;
}
.hover-zoom:hover img {
transform: scale(1.1);
}
Rendu :
.hover-overlay {
position: relative;
overflow: hidden;
border-radius: 8px;
}
.hover-overlay .overlay-text {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.6);
color: white;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.4s ease;
}
.hover-overlay:hover .overlay-text {
opacity: 1;
}
Rendu :
Rendu :
Survolez pour révéler un dégradé et un zoom subtil
/* Cercle simple */
.avatar-circle {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
}
/* Avec anneau coloré */
.avatar-ring {
border-radius: 50%;
border: 4px solid #2563eb;
padding: 2px;
background-color: white;
}
/* Groupe d'avatars (overlapping) */
.avatar-group .avatar-circle {
margin-left: -12px;
border: 3px solid white;
}
.avatar-group .avatar-circle:first-child {
margin-left: 0;
}
Rendu :
Cercle
Anneau
Groupe
<figure>
<img src="photo.jpg" alt="Description de l'image">
<figcaption>Légende sous l'image</figcaption>
</figure>
figure {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
figcaption {
padding: 1rem;
font-size: 0.9rem;
color: #64748b;
font-style: italic;
border-top: 1px solid #e2e8f0;
}
Rendu :
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1rem;
}
.gallery-item {
border-radius: 8px;
overflow: hidden;
position: relative;
aspect-ratio: 4/3;
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
}
.gallery-item:hover img {
transform: scale(1.08);
}
.gallery-caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent, rgba(0,0,0,0.7));
color: white;
padding: 1rem 0.75rem 0.75rem;
opacity: 0;
transition: opacity 0.3s ease;
}
.gallery-item:hover .gallery-caption {
opacity: 1;
}
Rendu :
Rendu :
.hero-image {
position: relative;
height: 400px;
border-radius: 8px;
overflow: hidden;
}
.hero-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.hero-image .hero-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 60%);
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 2rem;
color: white;
}
Rendu :
.comparison-container {
position: relative;
width: 100%;
aspect-ratio: 16/10;
overflow: hidden;
cursor: col-resize;
}
.comparison-after {
position: absolute;
inset: 0;
clip-path: inset(0 50% 0 0); /* Révèle la moitié gauche */
}
.comparison-slider {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 3px;
background: white;
z-index: 2;
}
Rendu :
Rendu :
Les images débordent de leur conteneur si elles sont plus larges. Toujours utiliser max-width: 100% pour un comportement responsive.
/* ❌ MAUVAIS — L'image déborde */
img { /* pas de max-width */ }
/* ✅ BON — L'image reste dans son conteneur */
img { max-width: 100%; height: auto; }
object-fit n'a aucun effet si l'image a sa taille naturelle. Il faut imposer width et height (ou aspect-ratio) sur l'image.
/* ❌ MAUVAIS — object-fit n'aura aucun effet */
img { object-fit: cover; }
/* ✅ BON — Dimensions imposées + object-fit */
img {
width: 300px;
height: 200px;
object-fit: cover;
}
filter: opacity() et opacity donnent le même résultat visuel, mais filter est rendu sur le GPU (meilleures performances pour les animations) tandis que opacity comme propriété de layout est moins coûteux en mode statique.
Safari nécessite -webkit-mask-image. Sans ce préfixe, les masques ne fonctionnent pas sur tous les navigateurs.
/* ❌ MAUVAIS — Ne fonctionne pas sur Safari */
img { mask-image: linear-gradient(black, transparent); }
/* ✅ BON — Compatibilité complète */
img {
-webkit-mask-image: linear-gradient(black, transparent);
mask-image: linear-gradient(black, transparent);
}
box-shadow suit toujours le rectangle de la boîte. Si votre image est découpée avec clip-path ou border-radius: 50%, utilisez filter: drop-shadow() pour une ombre qui suit la forme.
/* ❌ MAUVAIS — Ombre rectangulaire sur un cercle */
img {
border-radius: 50%;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
/* ✅ BON — Ombre qui suit le cercle */
img {
border-radius: 50%;
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
}
Les images décoratives (alt="") et les images informatives (alt="description") doivent être différenciées. Ne jamais omettre l'attribut alt.
WebP et AVIF offrent une meilleure compression que JPEG/PNG. Utilisez <picture> avec <source> pour la rétrocompatibilité.
<picture>
<source srcset="photo.avif" type="image/avif">
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="Description">
</picture>
img { max-width: 100%; height: auto; } est le point de départ de tout site moderne.
Quand vous imposez des dimensions fixes (galeries, avatars, cartes), object-fit: cover garantit un rendu uniforme sans déformation.
drop-shadow suit la forme alpha de l'image, contrairement à box-shadow qui suit toujours le rectangle.
Fournissez WebP/AVIF en priorité avec fallback JPEG/PNG. Le navigateur choisit le meilleur format supporté.
Avant aspect-ratio, on utilisait le trick padding-top: 56.25%. Désormais, aspect-ratio: 16/9 est plus lisible et maintenable.
Les filtres sont animables ! Utilisez transition pour des effets fluides au survol.
/* Transition fluide d'un filtre */
img {
filter: grayscale(100%);
transition: filter 0.4s ease;
}
img:hover {
filter: grayscale(0%);
}
Même avec object-fit: cover, définir les attributs HTML width et height évite les décalages de mise en page (CLS — Cumulative Layout Shift).
Accessibilité obligatoire. Utilisez alt="" (vide) pour les images purement décoratives, et une description concise pour les images informatives.
Défi n°1 — Galerie d'effets de filtre
Objectif : Créer une galerie de 6 images (ou divs colorées) où chaque image a un filtre différent au repos, et passe à none au survol avec une transition fluide :
auto-fillDéfi n°2 — Avatar interactif
Objectif : Créer un composant avatar avec :
clip-path: circle() pour le masquagedrop-shadow au survolDéfi n°3 — Hero section immersive
Objectif : Créer une section hero avec :
object-fit: coverConsigne :
Créez un mini-portfolio d'images qui combine les concepts vus dans ce chapitre :
| Concept | Propriétés | Usage principal |
|---|---|---|
| Responsive | max-width: 100%, height: auto |
Images qui s'adaptent au conteneur |
| Ajustement | object-fit, object-position |
Recadrage intelligent dans un conteneur fixe |
| Proportions | aspect-ratio |
Rapport largeur/hauteur fixe |
| Résolution | image-set(), <picture>, srcset |
Images adaptatives selon la densité d'écran |
| Filtres | filter : blur, brightness, contrast, grayscale, sepia, hue-rotate, saturate, invert, opacity, drop-shadow |
Effets visuels en temps réel |
| Découpe | clip-path : circle, ellipse, polygon, inset |
Formes non-rectangulaires |
| Masquage | mask-image, -webkit-mask-image |
Fondu et formes avec dégradés |
| Fusion | mix-blend-mode |
Mélange artistique avec l'arrière-plan |
| Hover effects | transform, filter, opacity |
Zoom, overlay, révélation, grayscale→couleur |
| Sémantique | <figure>, <figcaption>, <picture> |
Images sémantiques avec légendes |
object-fit: cover et object-fit: contain ?-webkit-mask-image en plus de mask-image ?filter suit la forme alpha d'une image (utile pour les PNGs transparents) ?object-fitcover remplit en coupant le surplus, contain affiche l'image entière (peut laisser des espaces)-webkit- pour mask-imagedrop-shadow() (et mask-image en fait, mais drop-shadow est la fonction la plus pertinente pour les ombres)<picture> et des <source> avec type différent/* Image responsive */
img { max-width: 100%; height: auto; display: block; }
/* Object-fit dans un conteneur fixe */
img { width: 300px; height: 200px; object-fit: cover; }
/* Grayscale → Couleur au survol */
img { filter: grayscale(100%); transition: filter 0.4s ease; }
img:hover { filter: grayscale(0%); }
/* Clip-path cercle */
img { clip-path: circle(50%); }
/* Mask dégradé vers le bas */
img {
-webkit-mask-image: linear-gradient(black 50%, transparent);
mask-image: linear-gradient(black 50%, transparent);
}
/* Avatar anneau */
.avatar {
border-radius: 50%;
border: 4px solid #2563eb;
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2));
}
/* Ombre qui suit la forme */
img { filter: drop-shadow(4px 4px 8px rgba(0,0,0,0.3)); }