Единицы измерения в CSS: px, em, rem, %, vw/vh и когда что использовать

Единицы измерения в CSS: px, em, rem, %, vw/vh и когда что использовать Полезное

Единицы измерения в CSS — это суффиксы у чисел (16px, 1.5rem, 50vw), которые говорят браузеру, от какой опорной величины считать размер. Они делятся на две группы. Абсолютные (px, а также cm, mm, in, pt, pc, Q) жестко привязаны к пикселю CSS. Относительные зависят от чего-то еще: em — от размера шрифта, rem — от шрифта корневого элемента, % — от родителя, vw/vh — от окна браузера, ch — от ширины символа.

Короткий ответ, что когда брать: шрифты и отступы — в rem, внутренние отступы компонента — в em, ширины колонок — в %, размеры относительно экрана — в vw/vh/dvh, длину строки текста — в ch, тонкие рамки — в px, плавный шрифт — через clamp(). Ниже — от чего считается каждая единица, примеры с реальными значениями и типичные ошибки. Значения проверены в Chromium 149 (headless) 24.09.2026.

Мини-словарь

  • Длина — число плюс единица: 24px, 1.5em. Для нуля единицу можно не писать: margin: 0.
  • Опорная величина — то, от чего считается относительная единица: шрифт родителя, ширина контейнера, окно браузера.
  • Вычисленное значение — итоговый размер, в который браузер переводит любые единицы. Его видно в DevTools на вкладке Computed или через getComputedStyle().

Сводная таблица: от чего считается каждая единица

Единица От чего считается Типичное применение Подводный камень
px Пиксель CSS (1/96 дюйма) Рамки, тени, мелкие детали Шрифт в px игнорирует размер шрифта из настроек браузера
em В font-size — шрифт родителя, в остальных свойствах — свой шрифт Отступы кнопки, иконки рядом с текстом Накапливается во вложенных элементах
rem Шрифт корневого html (по умолчанию обычно 16px) Шрифты, отступы, сетка отступов В @media считается от шрифта браузера, а не от html
% У каждого свойства своя опора (обычно родитель) Ширина колонок padding-top: 10% считается от ширины, а не высоты
vw, vh 1% ширины / высоты окна Полноэкранные блоки 100vw шире страницы при видимой полосе прокрутки
vmin, vmax 1% меньшей / большей стороны окна Элементы, одинаковые в портрете и альбоме Редко нужны в обычной верстке
dvh, svh, lvh Высота окна с учетом панелей мобильного браузера Первый экран на телефоне В старых браузерах нужен запасной vh
ch Ширина символа «0» текущего шрифта Максимальная длина строки Зависит от шрифта
cqi, cqw 1% размера контейнера Компоненты, которые живут в колонках разной ширины Нужен container-type у предка

Минимальный пример: карточка на rem, em, ch и clamp()

Сохраните как index.html и откройте в браузере:

<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
  html { font-size: 100%; }
  body { margin: 0; font-family: system-ui, sans-serif; line-height: 1.5; }
  .page { max-width: 70rem; margin: 0 auto; padding: 0 1rem; }
  .card { border: 1px solid #ccc; border-radius: 0.5rem; padding: 1.5em; }
  .card h2 { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); margin: 0 0 0.5em; }
  .card p { max-width: 60ch; margin: 0 0 1em; }
  .btn { font-size: 0.875rem; padding: 0.5em 1em; }
</style>
</head>
<body>
  <main class="page">
    <article class="card">
      <h2>Единицы измерения в CSS</h2>
      <p>Абзац не шире 60 символов: такую строку удобно читать.</p>
      <button class="btn">Подробнее</button>
    </article>
  </main>
</body>
</html>

Вычисленные значения при шрифте браузера 16px:

окно 375px:  h2 24px, padding карточки 24px, кнопка: шрифт 14px, отступы 7px 14px
окно 1000px: h2 36px, padding карточки 24px, кнопка: шрифт 14px, отступы 7px 14px
окно 1600px: h2 40px, padding карточки 24px, кнопка: шрифт 14px, отступы 7px 14px

Что здесь происходит. Рамка 1px не должна масштабироваться — это деталь, поэтому пиксели. padding: 1.5em у карточки — это 1.5 x 16 = 24px, а у кнопки 0.5em 1em считается от ее собственного шрифта 14px, поэтому 7px и 14px: отступы растут вместе с текстом кнопки. Заголовок меняется с шириной окна, но не выходит за 24-40px — это работа clamp(), разобранная ниже. 60ch ограничивает абзац примерно 60 символами; в пикселях это зависит от шрифта (для system-ui 16px в этом прогоне — около 605px).

Пиксели и физические единицы

px в CSS — это не физическая точка экрана, а условный пиксель CSS. На экране с плотностью 2x (devicePixelRatio = 2) один пиксель CSS рисуется квадратом 2×2 физических точки. Дробные значения вроде 0.5px допустимы, при отрисовке браузер их округляет или сглаживает.

Физические единицы в CSS пересчитываются через пиксели по фиксированному соотношению:

1in = 96px     1cm ≈ 37.8px    1mm ≈ 3.78px
1pt = 1/72in ≈ 1.33px    1pc = 12pt = 16px    1Q = 0.25mm ≈ 0.94px

Поэтому на мониторе 1cm не обязан совпасть с сантиметром на линейке. Сантиметры и пункты имеют смысл в стилях для печати (@media print), для экрана они ничего не дают по сравнению с px.

em и rem: шрифт родителя против шрифта корня

em ведет себя по-разному в зависимости от свойства. В font-size 1em равен шрифту родителя. Во всех остальных свойствах (padding, margin, width) 1em равен шрифту самого элемента. Проверка: у родителя 20px, у потомка font-size: 1.5em; padding: 1em — шрифт потомка 30px, и отступ тоже 30px, а не 20px.

Из-за первого правила em накапливается во вложенных элементах. Неверно:

body { font-size: 16px; }
.menu li { font-size: 1.2em; }

При трех уровнях вложенных списков размеры получаются такими:

уровень 1: 19.2px   уровень 2: 23.04px   уровень 3: 27.648px

Каждый li умножает шрифт родителя на 1.2. Исправление — rem, он всегда считается от корня:

.menu li { font-size: 1.2rem; }
уровень 1: 19.2px   уровень 2: 19.2px   уровень 3: 19.2px

Почему шрифты лучше задавать в rem, а не в px: если пользователь увеличил базовый шрифт в настройках браузера, все размеры в rem вырастут пропорционально, а font-size в px останется прежним. Масштаб страницы (Ctrl и +) увеличивает размеры в px, em, rem и %, но не размеры, заданные только в vw/vh (почему — в разделе про clamp()), а настройку шрифта уважают только rem, em и %. По той же причине не стоит задавать html { font-size: 16px; } — лучше 100% или вообще ничего.

Отдельный случай — медиазапросы. Внутри @media единицы em и rem считаются от начального шрифта браузера, а не от html. Проверка при html { font-size: 62.5%; } (10px) и окне 500px: 40rem в разметке дает 400px, но @media (min-width: 40em) не срабатывает, потому что для него 40em = 640px.

Проценты: у каждого свойства своя опора

% — единица без общей опоры, у каждого свойства она своя:

  • width, left, right — ширина содержащего блока (обычно родителя).
  • height — высота родителя, но только если она задана явно. При height: auto у родителя height: 50% у потомка не сработает.
  • padding и margin — ширина родителя, даже для верхнего и нижнего отступа. В блоке 500×200 padding-top: 10% дает 50px, а height: 50% — 100px.
  • font-size — шрифт родителя, как em.
  • transform: translate(50%) — размер самого элемента.

С line-height есть ловушка наследования. Проценты вычисляются один раз и наследуются уже в пикселях, а число без единицы наследуется как коэффициент:

.a { font-size: 20px; line-height: 150%; }  /* потомок с шрифтом 40px получит 30px */
.b { font-size: 20px; line-height: 1.5; }   /* потомок с шрифтом 40px получит 60px */

В первом случае крупный текст потомка налезает строками друг на друга, поэтому для line-height обычно пишут число без единицы. Подробно о процентах в width, height и box-sizing — в статье о ширине и высоте блока в CSS.

vw, vh, vmin, vmax и мобильные dvh

1vw — 1% ширины окна браузера, 1vh — 1% высоты. vmin берет меньшую сторону, vmax — большую. В окне 1000×800 10vmin = 80px (от высоты), 10vmax = 100px (от ширины). Если повернуть телефон, опорная сторона поменяется.

Типичная ошибка — width: 100vw для блока на всю ширину. В vw входит полоса прокрутки, если она занимает место (обычно в Windows и Linux, в macOS — при соответствующей настройке). Проверка в окне 500px с полосой 15px:

100vw = 500px, ширина страницы = 485px -> появляется горизонтальная прокрутка
100%  = 485px -> прокрутки нет

Исправление — width: 100% у блока внутри страницы без ограничивающего родителя. В Chrome 145 и новее есть и второй путь: если у корня задано html { scrollbar-gutter: stable; } или overflow-y: scroll, 100vw уже вычитает ширину полосы прокрутки; в браузерах без этой доработки vw по-прежнему ее включает.

Вторая ловушка — 100vh на телефонах. Мобильный браузер прячет и показывает адресную строку, и vh равен высоте окна при скрытых панелях, поэтому низ блока уходит под панель. Для этого есть отдельные единицы: svh — высота при показанных панелях (маленькое окно), lvh — при скрытых (большое), dvh — текущая, меняется на ходу. Надежный вариант с запасом для старых браузеров:

.hero {
  min-height: 100vh;   /* запасной вариант */
  min-height: 100dvh;  /* браузер, который понимает dvh, возьмет это значение */
}

ch, lh и единицы контейнера

ch — ширина символа «0» в текущем шрифте. max-width: 60ch ограничивает абзац примерно 60-70 символами обычного текста: для моноширинного шрифта это точное число символов, для пропорционального — приблизительное, потому что буквы разной ширины.

lh — высота строки элемента: при font-size: 16px и line-height: 1.5 значение 2lh = 48px. Удобно для блока ровно на две или три строки текста.

cqi и cqw — 1% ширины контейнера, а не окна. Они работают, только если у предка указан container-type:

.column { container-type: inline-size; }
.column p { font-size: 5cqi; }  /* в колонке шириной 400px шрифт будет 20px */

Это решает задачу, которую vw решить не может: одна и та же карточка в узкой боковой колонке и в широкой основной меняет размеры по своей колонке, а не по экрану.

clamp(), calc(), min() и max(): смешивание единиц

Функции позволяют сложить разные единицы и поставить границы. calc(1rem + 2vw) складывает шрифт и долю окна. min(100%, 40rem) берет меньшее, max() — большее. clamp(MIN, ЖЕЛАЕМОЕ, MAX) держит значение в коридоре.

Расчет для заголовка из первого примера, clamp(1.5rem, 1rem + 2vw, 2.5rem):

Ширина окна 1rem + 2vw Итог
375px 16 + 7.5 = 23.5px 24px (сработал минимум 1.5rem)
1000px 16 + 20 = 36px 36px
1600px 16 + 32 = 48px 40px (сработал максимум 2.5rem)

Зачем в середине rem, а не просто 5vw. Шрифт только на vw не растет при масштабировании страницы: при зуме CSS-ширина окна уменьшается ровно во столько раз, во сколько растет пиксель CSS, и видимый размер текста остается прежним. Для доступности это проблема: пользователь не может увеличить такой текст зумом. Слагаемое в rem сохраняет реакцию на зум и на настройку шрифта. Раньше ту же задачу решали через calc() и несколько медиазапросов; clamp() делает это одной строкой.

Внутри calc() у нуля единица обязательна: calc(0 + 10px) браузер отбросит как ошибку, calc(0px + 10px) сработает.

Как выбрать единицу: алгоритм

  1. Размер должен реагировать на настройку шрифта пользователя? Шрифт, отступы, высота кнопки — rem.
  2. Отступ должен расти вместе с текстом конкретного компонента? em.
  3. Размер зависит от места, где стоит элемент? Доля родителя — %, доля колонки — cqi.
  4. Размер зависит от экрана? vw, dvh, лучше внутри clamp() с границами.
  5. Это длина строки текста? ch.
  6. Это деталь, которая не должна масштабироваться (рамка, тень, линия)? px.

Если размер получился не тот

  • Текст растет с каждым уровнем вложенности — em в font-size накапливается. Замените на rem.
  • Появилась горизонтальная прокрутка — где-то width: 100vw. Замените на 100%.
  • Блок с height: 50% имеет нулевую высоту — у родителя высота auto. Задайте высоту родителю или используйте flex/grid.
  • Низ первого экрана на телефоне скрыт — 100vh. Используйте 100dvh или 100svh с запасным vh.
  • Медиазапрос срабатывает не на той ширине — em/rem в @media считаются от шрифта браузера, а не от html.
  • Увеличение шрифта в настройках браузера ничего не меняет — шрифты заданы в px.

Проверять лучше не глазами: в DevTools выберите элемент и откройте вкладку Computed — там видно, во сколько пикселей превратилась каждая единица.

Выводы

  • Абсолютная единица для экрана по сути одна — px; cm, pt и остальные пересчитываются через нее и нужны в основном для печати.
  • rem — основа для шрифтов и отступов: не накапливается и уважает настройку шрифта пользователя.
  • em удобен для отступов внутри компонента, но в font-size накапливается во вложенных элементах.
  • У % нет общей опоры: padding-top в процентах считается от ширины, height в процентах требует явной высоты родителя.
  • 100vw ломается на полосе прокрутки, 100vh — на мобильных панелях; есть 100% и dvh, а плавный шрифт надежнее собрать через clamp() с rem.

Где применяется / связь с практикой

Освойте тему на практике

Выбор единиц — это ежедневная работа верстальщика: от него зависит, развалится ли макет на телефоне, будет ли текст читаемым на широком мониторе и учтет ли страница настройки пользователя. На курсе «HTML/CSS» адаптивная верстка разбирается на практических проектах с ревью. Послушать отдельные темы бесплатно можно на открытых уроках Otus.

FAQ

Сколько пикселей в 1rem?
По умолчанию в браузерах 16px, но пользователь может поменять базовый шрифт в настройках, и тогда 1rem изменится. Поэтому на точное «1rem = 16px» в расчетах полагаться нельзя.

Что такое fr — это тоже единица длины?
fr — доля свободного места в CSS Grid, она работает только в grid-template-columns, grid-template-rows и похожих свойствах. Записать width: 1fr нельзя, браузер отбросит такое объявление.

Можно ли смешивать единицы в одном значении?
Да, через calc(), min(), max() и clamp(): например, calc(100% - 2rem) — ширина родителя минус два шрифта корня.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)