px, rem, em и vw: как выбрать единицу и не сломать вёрстку
Когда шрифт вдруг вырастает вдвое, а padding уезжает на мобильном — обычно виноват не CSS, а неверно выбранная единица. Разбираем, где px, где rem, где em и когда vw мешает.

Задача: сверстать карточку с заголовком, отступами и адаптивным блоком так, чтобы при увеличении шрифта в настройках браузера макет не рассыпался, а на широком мониторе текст не растягивался в одну строку. Очевидное решение — всё указать в px — даёт две проблемы сразу. Пользователь, который поднял базовый размер шрифта в браузере до 20px ради читаемости, не увидит изменений: px его игнорирует. А блок с width: 40vw на мониторе 3440px превратится в строку длиной 1376px, где строка текста тянется от края до края и глаз теряет начало следующей.
Разберём четыре ходовые единицы — px, rem, em, vw — с точки зрения того, от чего каждая считается и чем вы платите за выбор. Тему ch, %, vh и функций clamp() трогаем только там, где без них не объяснить основную. Всё актуально для современных браузеров (Chrome, Firefox, Safari 2023 года и новее).
От чего считается каждая единица
Единицы делятся на абсолютные и относительные. Разница не в написании, а в том, что берётся за базу.
- px — абсолютная единица. В CSS один
pxне равен физическому пикселю экрана: это условная величина, привязанная к эталонному расстоянию просмотра. На Retina один CSS-пиксель может занимать 2 или 3 аппаратных. Для вёрстки важно другое:pxни от чего не зависит и не реагирует на настройки пользователя. - rem — доля от размера шрифта корневого элемента
<html>. По умолчанию браузер ставит на<html>16px, значит1rem = 16px. Если пользователь в настройках браузера выбрал крупный шрифт, база меняется, и все размеры вremедут вместе с ней. - em — доля от размера шрифта того элемента, на котором свойство применяется (а для самого
font-size— от родителя). Это единственная из четырёх, чья база меняется от места к месту. - vw — 1% ширины окна просмотра (viewport).
50vw— половина ширины окна, независимо ни от каких шрифтов. Есть братьяvh,vmin,vmaxи новыеdvw/dvhдля мобильных с исчезающей адресной строкой.
Почему em коварнее остальных
С em легко получить каскадное умножение. Если у родителя font-size: 1.2em и у вложенного списка тоже 1.2em, реальный размер на втором уровне — уже 1.44em относительно деда. Классический баг — вложенные <ul>, где каждый уровень мельче предыдущего, хотя в коде везде одно и то же значение.
/* Ловушка каскада em */
ul { font-size: 0.9em; } /* каждый вложенный ul умножает на 0.9 */
/* Три уровня вложенности: 0.9 → 0.81 → 0.729 от базы */
/* Как избежать — считать от корня */
ul { font-size: 0.9rem; } /* всегда 0.9 от html, вложенность не влияет */
Сравнение по задачам
| Единица | База | Реагирует на шрифт браузера | Где уместна | Где вредит |
|---|---|---|---|---|
px | ничего | нет | границы, тени, тонкие линии, размеры иконок с фикс. графикой | шрифты и вертикальные отступы — ломает доступность |
rem | font-size у html | да | шрифты, отступы, размеры компонентов — рабочая лошадка | там, где нужна связь с локальным шрифтом (иконка внутри текста) |
em | font-size элемента/родителя | да | padding кнопки, размер иконки рядом с текстом — масштабируются вместе с ним | вложенные структуры — каскадное умножение |
vw | ширина окна | нет | полноэкранные секции, фон-геройблоки | размер шрифта в чистом виде — либо крошечный на телефоне, либо гигантский на мониторе |
Рабочая раскладка
Практика, к которой пришло большинство команд, сводится к простому распределению ролей.
- Не трогайте font-size у html. Соблазн написать
html { font-size: 62.5%; }, чтобы1remстал равен 10px и было удобно считать, живёт с 2010-х. Проблема: вы уменьшаете базу, которую пользователь мог осознанно увеличить. Оставьте браузерные 16px по умолчанию — тогдаremчестно уважает настройки доступности. - Шрифты и отступы — в rem.
font-size: 1rem,margin: 1.5rem,gap: 0.5rem. Пользователь увеличил шрифт — весь блок вырос пропорционально, ничего не наехало. - Внутренние отступы кнопок и бейджей — в em. Тогда padding привязан к размеру текста самой кнопки: сделали кнопку крупнее через
font-size— отступы выросли сами. - Границы, тени, radius — в px. Линия в 1px должна оставаться линией в 1px при любом шрифте. Здесь абсолютность — плюс.
- vw — только для действительно вьюпорт-зависимого: полноширинные секции, отрицательные margin для вырыва блока за контейнер, изредка — размер шрифта героя, но через
clamp(), а не голымvw.
Простое правило на каждый день: если размер должен масштабироваться с текстом — rem или em, если должен оставаться пиксель-в-пиксель — px, если должен зависеть от окна — vw. Смешивать в одном свойстве без clamp() почти всегда ошибка.
Почему голый vw для шрифта — плохо
Соблазн написать font-size: 4vw, чтобы заголовок «сам» масштабировался, разбивается о края. На телефоне 375px это 15px — почти нечитаемо. На мониторе 2560px — 102px, заголовок в пол-экрана. И главное: vw не реагирует на масштаб страницы теми же путями, что и шрифты, — вы отбираете у пользователя контроль над размером текста.
Решение — clamp(min, желаемое, max), где среднее значение содержит и rem, и vw:
/* Заголовок: не меньше 1.5rem, не больше 3rem,
между ними плавно растёт с шириной окна */
h1 {
font-size: clamp(1.5rem, 1rem + 2.5vw, 3rem);
}
Слагаемое 1rem внутри сохраняет связь с настройками пользователя, vw добавляет плавность, а границы не дают уехать в крайности. Это то, ради чего vw вообще стоит применять к тексту.
Что ломается на практике
- Медиазапросы в px при базе не 16px. Единицы медиазапросов считаются от исходного размера шрифта браузера, а не от вашего
html { font-size }. Если вы всё же переопределили базу, breakpoint@media (min-width: 48em)сработает не там, где вы ждёте. Ещё аргумент оставить html в покое. - line-height в единицах вместо числа.
line-height: 1.5emвычисляется один раз и наследуется как готовое значение — вложенные элементы с другим шрифтом получат чужую высоту строки. Пишитеline-height: 1.5без единицы: множитель пересчитается от локального font-size. - vh на мобильных. Классический
100vhна телефоне не учитывает адресную строку — блок оказывается выше видимой области, низ уезжает под панель браузера. Для этого появилисьdvh(динамическая высота) иsvh/lvh. Проверьте поддержку в целевых браузерах, прежде чем менятьvhнаdvh. - Смешение em и rem без системы. Когда в одном компоненте padding в em, margin в rem, а font-size в px, изменение одного значения даёт непредсказуемый сдвиг остального. Заведите правило по типам свойств и держитесь его во всём проекте.
Что настроить дальше
Соберите значения в CSS-переменные и задайте шкалу: --space-1: 0.25rem, --space-2: 0.5rem и так далее. Это убирает магические числа и делает раскладку единиц частью дизайн-системы, а не решением каждого разработчика на месте. Проверьте вёрстку в двух режимах: увеличьте шрифт браузера до 200% (настройки доступности) и масштаб страницы через Ctrl+ — это разные механизмы, и rem-вёрстка должна пережить оба. Следующий шаг после единиц — функции clamp/min/max для адаптивной типографики без груды медиазапросов.
Источники: CSS values and units — MDN, CSS clamp() — MDN
Частые вопросы
Если я хочу, чтобы 1rem был равен 10px для удобства расчётов — так можно?
Что выбрать для отступов — em или rem?
Почему у меня вложенные списки становятся всё мельче, хотя font-size везде одинаковый?
А если clamp() не поддерживается в старом браузере?
Vh перестал работать корректно на телефоне — блок уезжает под адресную строку. Что делать?
Стоит ли вообще использовать px в 2024 году?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.