Maîtrisez aspect-ratio et object-fit pour des images et vidéos parfaitement responsives : ratios, recadrage intelligent, anti-CLS et patterns Bootstrap 5.
Pourquoi maîtriser les ratios ?
Une page web moderne moyenne charge entre 15 et 40 médias : photos, vignettes, avatars, logos, vidéos embed, illustrations SVG. Si chacun s'affiche avec ses dimensions natives, le layout saute, les grilles s'écrasent, le Cumulative Layout Shift (CLS) explose et Google rétrograde la page. La solution moderne tient en deux propriétés CSS : aspect-ratio impose un format au conteneur, et object-fit dicte comment le média se comporte à l'intérieur.
Avant 2021, l'astuce historique consistait à utiliser padding-top: 56.25% (= 9÷16) sur un conteneur, puis à positionner l'image en absolute à l'intérieur. Cette technique fonctionne encore mais reste verbeuse, peu lisible, et ne s'adapte pas bien aux ratios dynamiques. Avec aspect-ratio, une seule ligne suffit.
Comparaison rapide : ancien vs moderne
/* ❌ ANCIEN — Hack padding-top */
.video-wrap {
position: relative;
padding-top: 56.25%; /* 16:9 */
height: 0;
}
.video-wrap iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
/* ✅ MODERNE — aspect-ratio */
.video-wrap {
aspect-ratio: 16 / 9;
}
.video-wrap iframe {
width: 100%;
height: 100%;
}
aspect-ratio en profondeur
aspect-ratio accepte un ratio sous 3 formes : 16 / 9 (le plus lisible), 1.7777 (décimal), ou auto pour revenir au comportement natif.
Syntaxes valides
.thumbnail { aspect-ratio: 16 / 9; } /* paysage standard */
.cover-photo { aspect-ratio: 21 / 9; } /* ultrawide cinéma */
.avatar { aspect-ratio: 1 / 1; } /* carré pur */
.portrait { aspect-ratio: 3 / 4; } /* photo verticale */
.story-card { aspect-ratio: 9 / 16; } /* TikTok / stories */
.banner { aspect-ratio: 4 / 1; } /* bannière fine */
.golden { aspect-ratio: 1.618; } /* nombre d'or */
.dynamic { aspect-ratio: auto; } /* ratio natif du contenu */
Comment aspect-ratio calcule la dimension manquante
Si vous spécifiez width, la height est calculée. Si vous spécifiez height, la width est calculée. Si vous fixez les deux, aspect-ratio est ignoré.
/* Largeur 100% du parent → hauteur calculée automatiquement */
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
/* height auto-calculée = 56.25% de la largeur */
}
/* Hauteur fixe → largeur calculée */
.icon-square {
height: 48px;
aspect-ratio: 1 / 1;
/* width = 48px */
}
/* Conflit : aspect-ratio est ignoré si width ET height sont fixés */
.broken {
width: 200px;
height: 100px; /* prend le pas */
aspect-ratio: 1 / 1; /* ignoré */
}
Avec min-height ou max-height
/* Idéal : ratio garanti mais hauteur min de sécurité */
.hero-banner {
width: 100%;
aspect-ratio: 21 / 9;
min-height: 240px; /* sur mobile, ratio relâché */
max-height: 640px; /* sur très large écran, plafonné */
}
aspect-ratio avec min-height protège contre ces extrêmes sans casser le ratio sur desktop.
object-fit : 5 modes essentiels
object-fit contrôle comment un <img>, <video> ou <iframe> remplit son conteneur lorsque ses dimensions ne correspondent pas exactement à celles du média.
Les 5 valeurs en détail
| Valeur | Comportement | Cas d'usage |
|---|---|---|
fill (défaut) |
Étire le média pour remplir le conteneur — déforme le ratio natif | À éviter — produit des images écrasées |
cover |
Remplit le conteneur, conserve le ratio, rogne les débordements | Photos décoratives, héros, vignettes uniformes |
contain |
Affiche le média entier, conserve le ratio, ajoute des bandes vides | Logos, screenshots, schémas — rien ne doit être coupé |
none |
Affiche en taille native, sans aucun redimensionnement | Icônes pixel-perfect, sprites |
scale-down |
Comme contain si trop grand, comme none sinon |
Vignettes utilisateur de tailles variables |
Exemples concrets
/* Photo de héros — remplit, recadre intelligemment */
.hero img {
width: 100%;
height: 480px;
object-fit: cover;
}
/* Logo client — visible en entier */
.partner-logo {
width: 160px;
height: 80px;
object-fit: contain;
background: #fff;
padding: 0.5rem;
}
/* Avatar utilisateur uniformisé */
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
}
object-position : recadrer précisément
Quand object-fit: cover rogne, où coupe-t-il ? Par défaut au centre. object-position permet de déplacer le point d'ancrage. Indispensable pour des portraits où le visage doit rester visible après recadrage.
Syntaxes courantes
/* Mots-clés (les plus lisibles) */
.thumb-top { object-fit: cover; object-position: top; }
.thumb-bottom { object-fit: cover; object-position: bottom; }
.thumb-left { object-fit: cover; object-position: left center; }
/* Pourcentages : 0% 0% = top-left, 50% 50% = centre, 100% 100% = bottom-right */
.face-thumb { object-fit: cover; object-position: 50% 25%; } /* visage en haut */
/* Pixels (relatif au conteneur) */
.precise { object-fit: cover; object-position: 30px 20px; }
Cas réel : galerie de portraits
<div class="row g-3">
<div class="col-md-3">
<img src="alice.webp" alt="Alice" class="portrait">
</div>
<div class="col-md-3">
<img src="bob.webp" alt="Bob" class="portrait">
</div>
</div>
.portrait {
width: 100%;
aspect-ratio: 3 / 4;
object-fit: cover;
/* Privilégier le haut où se trouvent les visages */
object-position: center 20%;
border-radius: 0.5rem;
}
Combiner aspect-ratio + object-fit
Le pattern le plus puissant : aspect-ratio sur le conteneur ou directement sur l'image, object-fit: cover pour remplir sans déformer. Tous les médias d'une grille s'alignent parfaitement, quelles que soient leurs dimensions sources.
Pattern grille uniforme
<div class="gallery">
<img src="paysage-large.jpg" alt="">
<img src="portrait-vertical.jpg" alt="">
<img src="carre.jpg" alt="">
<img src="ultra-wide.jpg" alt="">
</div>
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1rem;
}
/* Chaque image : ratio 4:3 imposé, contenu rogné en cover */
.gallery img {
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
border-radius: 0.5rem;
display: block;
}
Pattern card produit
<article class="product-card">
<div class="product-card__media">
<img src="produit.webp" alt="Sneakers">
</div>
<h3>Sneakers Trail</h3>
<p class="price">89,00 €</p>
</article>
.product-card__media {
aspect-ratio: 1 / 1;
background: #f5f5f5;
overflow: hidden;
border-radius: 0.5rem 0.5rem 0 0;
}
.product-card__media img {
width: 100%;
height: 100%;
object-fit: contain; /* ne pas rogner — produit visible en entier */
transition: transform 0.4s ease;
}
.product-card:hover .product-card__media img {
transform: scale(1.06);
}
Pattern vidéo embed responsive
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/xxx"
title="Présentation produit"
allowfullscreen
loading="lazy"></iframe>
</div>
.video-embed {
aspect-ratio: 16 / 9;
width: 100%;
border-radius: 0.5rem;
overflow: hidden;
}
.video-embed iframe {
width: 100%;
height: 100%;
border: 0;
}
Performance et CLS
Le Cumulative Layout Shift est l'un des trois Core Web Vitals (avec LCP et INP). Une page perd des points si des images chargent et poussent le contenu vers le bas. aspect-ratio et les attributs width/height HTML résolvent ce problème.
Toujours déclarer les dimensions HTML
<!-- ✅ BON : navigateur réserve l'espace dès le HTML parsé -->
<img src="hero.webp" alt="Hero"
width="1280" height="720"
fetchpriority="high"
decoding="async">
Les attributs width et height indiquent au navigateur le ratio intrinsèque (1280÷720 = 16:9), même avant que l'image charge. Combinés à un CSS width: 100%; height: auto;, l'espace est réservé instantanément.
CSS minimal anti-CLS
/* Reset global pour toutes les images */
img, video {
max-width: 100%;
height: auto; /* préserve le ratio intrinsèque */
display: block; /* supprime le baseline gap */
}
Lazy loading + aspect-ratio
<img src="vignette.webp"
alt="Article"
loading="lazy"
decoding="async"
class="article-thumb">
.article-thumb {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
background: linear-gradient(135deg, #f5f5f5, #e8e8e8); /* placeholder visible avant chargement */
}
aspect-ratio ni width/height, une image lazy-loaded provoque un CLS au chargement. Avec, le navigateur réserve la place exacte dès le premier rendu.
Patterns Bootstrap 5
Bootstrap 5 ne fournit pas nativement aspect-ratio sur ses composants. Voici comment l'intégrer proprement avec les classes utilitaires.
Card Bootstrap avec image au format imposé
<div class="card h-100">
<div class="ratio ratio-16x9">
<img src="article.webp" alt="" class="card-img-top object-fit-cover">
</div>
<div class="card-body">
<h5 class="card-title">Titre article</h5>
<p class="card-text">Lorem ipsum dolor sit amet</p>
</div>
</div>
Bootstrap 5.3+ propose les classes utilitaires .object-fit-{cover|contain|fill|scale|none} qui appliquent object-fit sans CSS supplémentaire. La classe .ratio avec .ratio-16x9, .ratio-4x3, .ratio-1x1 applique aspect-ratio via une variable CSS.
Ratio personnalisé Bootstrap
<!-- Ratio 21:9 personnalisé via --bs-aspect-ratio -->
<div class="ratio" style="--bs-aspect-ratio: 42.85%">
<iframe src="https://example.com/embed"></iframe>
</div>
Hero responsive avec aspect-ratio
<section class="hero">
<img src="hero-2400.webp"
alt=""
width="2400" height="900"
class="hero__bg">
<div class="hero__content container">
<h1 class="display-4 fw-bold">Bienvenue</h1>
</div>
</section>
.hero {
position: relative;
aspect-ratio: 21 / 9;
min-height: 320px;
max-height: 720px;
overflow: hidden;
}
.hero__bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center 30%;
z-index: 0;
}
.hero__content {
position: relative;
z-index: 1;
padding-block: 4rem;
color: white;
}
Fallbacks pour navigateurs anciens
Avec 96%+ de couverture, le fallback n'est nécessaire que pour des contextes B2B avec navigateurs internes vieillissants. La requête @supports sépare les comportements.
Pattern progressive enhancement
/* Fallback : hack padding-top */
.video-embed {
position: relative;
padding-top: 56.25%; /* 16/9 */
height: 0;
}
.video-embed iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
/* Override : navigateurs modernes */
@supports (aspect-ratio: 1) {
.video-embed {
aspect-ratio: 16 / 9;
padding-top: 0;
height: auto;
}
.video-embed iframe {
position: static;
}
}
object-fit : fallback IE11
IE11 (encore présent dans certains intranets) ne supporte pas object-fit. La librairie object-fit-images (3KB gzip) ajoute le support via une astuce CSS + JS.
<script src="https://cdn.jsdelivr.net/npm/object-fit-images@3.2.4/dist/ofi.min.js"></script>
<script>
// Active le polyfill — utilise font-family hack en CSS
objectFitImages('img.cover');
</script>
/* Pour le polyfill, ajouter font-family avec valeur object-fit */
img.cover {
width: 100%;
height: 100%;
object-fit: cover;
font-family: 'object-fit: cover;'; /* lu par le polyfill */
}
Conclusion
aspect-ratio et object-fit remplacent une décennie de hacks CSS pour gérer les médias responsives. Avec aspect-ratio sur le conteneur, object-fit sur le média et les attributs width/height en HTML, vous éliminez le CLS, alignez parfaitement vos grilles et clarifiez votre code.
Adoptez ces patterns dans tous vos composants : cards, héros, galeries, vignettes blog, avatars, logos partenaires. Bootstrap 5.3+ fournit déjà des classes utilitaires (.ratio, .object-fit-cover) — tirez-en parti pour garder votre code DRY. Vos Core Web Vitals s'amélioreront immédiatement.
aspect-ratio: 16/9impose un ratio sans hack padding-topobject-fit: coverremplit en rognant — photos décorativesobject-fit: containaffiche en entier — logos, screenshotsobject-positionrecadre intelligemment (visages, focus)- Toujours ajouter
width/heightHTML pour anti-CLS - Combiner avec
min-height/max-heightsur très petits/grands écrans - Bootstrap 5.3+ : utiliser
.ratioet.object-fit-* - Fallback :
@supports (aspect-ratio: 1)+ padding-top hack