До aspect-ratio задача «сделай блок с пропорцией 16:9» решалась через нелепый трюк: пустой блок-обёртка с padding-bottom: 56.25% (это 9 / 16 в процентах), внутрь абсолютно позиционированный реальный контент. Работает, но смотреть страшно: padding-bottom в процентах считается от ширины родителя, и эту магию надо помнить, искать в гугле или копировать из старого проекта.
Свойство aspect-ratio закрывает эту боль ровно одной строчкой: задаём пропорцию — браузер сам вычисляет недостающую сторону по заданной. Никаких обёрток, никакого position: absolute для содержимого. Разберём, как им пользоваться без сюрпризов.
Синтаксис: какие значения принимает
Значения у aspect-ratio три:
- auto — пропорция не задана. Для обычных блоков это просто «нет соотношения», для замещаемых элементов вроде <img> и <video> — их собственное соотношение сторон (если оно известно).
- <ratio> — два числа через слеш: 16 / 9, 4 / 3, 1 / 1. Это привычная запись пропорции «ширина к высоте».
- auto <ratio> — комбинация двух предыдущих: пропорция-резерв, которую браузер использует, пока у замещаемого элемента нет собственной пропорции (картинка ещё не загрузилась). Для обычных блоков работает как обычный <ratio>. Об этой форме отдельный раздел ниже — она и есть самая полезная.
Несколько практических нюансов записи:
.card {
/* классическое 16:9 */
aspect-ratio: 16 / 9;
}
.tile {
/* квадрат — можно так */
aspect-ratio: 1 / 1;
/* или короче — без слеша */
aspect-ratio: 1;
}
.banner {
/* можно дробным числом — это эквивалент 0.5 / 1, то есть 1:2 */
aspect-ratio: 0.5;
}
Когда пишем без слеша — второе число неявно равно 1. То есть aspect-ratio: 2 — это 2 / 1 (вдвое шире, чем выше), а не 1 / 2. Если вы любитель чистоты — пишите со слешем всегда, читается понятнее.
Пробелы вокруг слеша свободные: 16/9, 16 / 9 и 16 /9 — всё одно и то же.
Главное правило: одна сторона должна быть auto
Это правило, на которое жалуются чаще всего, и звучит оно нелогично: если задать одновременно и width, и height фиксированными значениями, aspect-ratio молча игнорируется. Никакой ошибки в консоли — просто свойство ничего не делает.
/* работает: высота вычислится автоматически как 100/16*9 = 56.25px */
.ok {
width: 100px;
aspect-ratio: 16 / 9;
}
/* работает: ширина вычислится автоматически */
.ok-too {
height: 100px;
aspect-ratio: 16 / 9;
}
/* НЕ работает: обе размерности заданы, противоречие, aspect-ratio проигрывает */
.broken {
width: 100px;
height: 200px;
aspect-ratio: 16 / 9;
}
Логика тут такая: aspect-ratio — это правило вычисления, а не более приоритетное значение. Если вы уже задали обе стороны вручную, вычислять нечего и пропорция ничего не делает. Чтобы свойство работало, одна из размерностей должна оставаться auto (это значение по умолчанию, его можно просто не писать).
То же относится к комбинациям с min-*/max-*: если жёсткие ограничители заставят браузер выйти за пропорцию, побеждают они, а не aspect-ratio. Об этом подробнее в разделе про подводные камни.
Главная фишка: auto <ratio> для img и video
Это та форма, ради которой стоит запомнить aspect-ratio, даже если в остальном вёрстка обходится без него.
Когда браузер видит <img>, у него есть два момента, определяющие - знает ли он пропорцию картинки:
- До загрузки — не знает. Зарезервированной высоты нет, страница в этом месте занимает 0 пикселей.
- После загрузки — знает: пропорция берётся из самой картинки.
Если в момент между этими двумя точками пользователь начал скроллить, контент под картинкой подпрыгнет на её высоту, как только она дорисуется. Это и есть тот самый CLS (Cumulative Layout Shift, накопленный сдвиг макета) — одна из метрик Core Web Vitals, по которой Google штрафует за неудобство для пользователя.
Комбинация auto <ratio> решает это в одну строчку:
img {
width: 100%;
height: auto;
aspect-ratio: auto 16 / 9;
}
Читается так: «используй собственную пропорцию картинки, а пока её не знаешь — считай, что 16:9». До загрузки браузер резервирует ровно столько места, сколько нужно под 16:9-картинку выбранной ширины. Как только src загрузился — реальная пропорция перекрывает резерв, и блок подстраивается. Если реальные пропорции совпали с резервом — никакого сдвига вовсе.
Без auto поведение жёстче: aspect-ratio: 16/9 на <img> заставит браузер натянуть на блок ровно 16:9, даже если реальная картинка 4:3 — она исказится. Чтобы этого не произошло, либо ставьте auto в начале, либо добавляйте object-fit: cover/contain (см. отдельную статью про object-fit).
Полезно знать: атрибуты width и height на самом теге. Современные браузеры (Chrome 79+, Firefox 71+, Safari 14+) автоматически выводят aspect-ratio из HTML-атрибутов width и height у картинки, даже если CSS их перетирает на width: 100%; height: auto. То есть для типичной адаптивной картинки достаточно просто проставить размеры в атрибутах:
<img src="cover.jpg" width="1200" height="675" alt="">
Это эквивалентно aspect-ratio: auto 1200/675 и тоже спасает от CLS. Свойство aspect-ratio в CSS нужно, когда атрибуты по какой-то причине не подходят: размеры неизвестны заранее, картинка приходит из CMS без габаритов, или внутри блока не <img>, а <iframe>/<div>.
Где это реально пригождается
Адаптивный video-эмбед
Раньше для встраивания YouTube-плеера приходилось делать обёртку с padding-bottom: 56.25% и position: absolute на <iframe>. Сейчас — одна строка:
.video-embed {
width: 100%;
aspect-ratio: 16 / 9;
}
.video-embed iframe {
width: 100%;
height: 100%;
border: 0;
}
На любой ширине родителя плеер останется в правильной пропорции. Никакой обёртки и магических процентов.
Карточки одинаковой пропорции в гриде
Грид-плитка из карточек, которые должны быть одинаково квадратными, — типовой случай:
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 16px;
}
.grid .card {
aspect-ratio: 1;
border-radius: 12px;
background: #f1f5f9;
}
Карточки сами подстраивают высоту под ширину ячейки. Если карточек разная ширина — высоты тоже разные, но каждая остаётся квадратом.
Превью-аватарки и миниатюры
Список комментариев или каталог авторов, где у каждого аватарка должна быть строго квадратной независимо от исходной картинки:
.avatar {
width: 64px;
aspect-ratio: 1;
border-radius: 50%;
object-fit: cover;
}
Здесь aspect-ratio диктует форму, а object-fit: cover — как картинка обрезается внутри неё. Без него непропорциональное изображение растянулось бы в овал.
Hero-баннеры с фиксированной пропорцией
Большая верхняя картинка-обложка на странице обычно требует одинакового соотношения сторон на десктопе и мобайле, чтобы не ломать композицию. Раньше это делали через vh-единицы и медиа-запросы, теперь — одной строкой:
.hero {
width: 100%;
aspect-ratio: 21 / 9;
background: url('/storage/hero.jpg') center/cover;
}
На широком экране — кинематографичный баннер 21:9, на мобайле — тот же кадр в той же пропорции, просто меньшего размера.
Подводные камни
Инлайн-элементы и табличные коробки
Свойство применяется ко всем элементам, кроме инлайновых (по умолчанию — <span>, <a>, <em>) и внутренних табличных коробок (display: table-cell, table-row и т. п.). Если хочется задать пропорцию инлайн-ссылке — сначала переведите её в display: inline-block или block, иначе свойство тихо проигнорируется.
Контент длиннее, чем влезает в пропорцию
Самый коварный случай. Задали width: 300px; aspect-ratio: 1 — ожидаете квадрат 300×300. А внутри лежит длинный текст в десять строк. Что будет?
.card {
width: 300px;
aspect-ratio: 1;
border: 1px solid #ccc;
padding: 16px;
}
По умолчанию — контент вылезет за нижнюю границу. Внешне блок остался 300×300, но текст торчит. Если хочется, чтобы блок рос, как только контент не влез, добавляем min-height: auto (это не значение по умолчанию для блоков с явной высотой):
.card {
width: 300px;
aspect-ratio: 1;
min-height: auto; /* блок может стать выше пропорции */
}
А если, наоборот, хочется обрезать лишнее — добавляем overflow: hidden. Третьего, «незаметно ужать контент», в CSS не предусмотрено — для этого есть отдельные приёмы вроде обрезки многострочного текста.
box-sizing и какой блок считается
Пропорция считается от того блока, который задаёт box-sizing. Если у элемента box-sizing: content-box (значение по умолчанию по спецификации, но в большинстве проектов глобально перекрытое на border-box) — пропорция считается без учёта padding и border. С border-box — включая их. Звучит абстрактно, на практике значит вот что:
* { box-sizing: border-box; }
.card {
width: 200px;
padding: 20px;
aspect-ratio: 1;
}
/* border-box: внешняя коробка 200x200, контент внутри 160x160 */
.card {
box-sizing: content-box;
width: 200px;
padding: 20px;
aspect-ratio: 1;
}
/* content-box: контент 200x200, внешняя коробка 240x240 — больше, чем хотели */
Если в проекте глобально стоит box-sizing: border-box (а так в подавляющем большинстве проектов) — никаких сюрпризов, пропорция применяется к внешней коробке, которая и есть видимый блок.
object-fit нужен, если внутри картинка
Этот пункт повторим отдельно, потому что забывают часто. aspect-ratio на <img> диктует форму контейнера картинки. А как сама картинка внутри неё себя ведёт — растягивается, обрезается или вписывается — решает object-fit. По умолчанию object-fit: fill, что значит «растяни как угодно», и без коррекции непропорциональная картинка станет визуально кривой.
.avatar {
width: 64px;
aspect-ratio: 1;
object-fit: cover; /* обрезать по центру, не искажая */
}
Это — повторюсь — та же тема, что у комбинации auto <ratio>, но с другого ракурса: auto <ratio> отдаёт пропорцию реальной картинке, а object-fit: cover навязывает форму и подгоняет картинку под неё.
А «padding-bottom»-хак ещё нужен?
Короткий ответ — нет.
Хак с padding-bottom: 56.25% жил в кодовых базах ради одной причины: Internet Explorer. С июня 2022 года IE окончательно мёртв, поддержка aspect-ratio у живых браузеров — 93.9% по caniuse, последний живой браузер без поддержки исчез в 2021 году с релизом Safari 15. Никакой фолбэк не нужен.
Если вы видите padding-bottom-хак в существующем коде — это либо легаси, которое не успели переписать, либо след копипасты со старого Stack Overflow. Переписывать на aspect-ratio можно смело, поведение будет идентичным, а кода вдвое меньше.
С 20 марта 2024 года фича стабильно работает во всех актуальных браузерах. По свежести спеки тоже без сюрпризов: с 2021 года — когда Safari 15 закрыл круг поддержки — синтаксис не менялся, новых форм записи не добавлялось.
Комментарии (0)