Skip to content
Назад к блогу
Руководства

PX vs REM vs EM: полное руководство по единицам CSS

px vs rem vs em: что означает каждая единица CSS, когда использовать rem для доступности, ловушка вложенных em и шпаргалка по свойствам.

12 мин чтения

PX vs REM vs EM: полное руководство по единицам CSS

Короткий ответ на вопрос px vs rem vs em, ещё до всяких объяснений. Используйте rem почти для всех размеров: размеров шрифта, отступов, полей, промежутков, скруглений и контрольных точек. Rem масштабируется вместе с настройкой размера шрифта в браузере читателя. Используйте px для тех немногих вещей, которые не должны масштабироваться никогда, — например, для границы в 1px или точного смещения тени. Используйте em для редкого локального случая, когда значение должно расти вместе с собственным размером шрифта элемента: скажем, для внутренних отступов кнопки, привязанных к её тексту.

Это правило покрывает 90% решений. На остальные 10% и приходится большинство проблем: математика вложенных em, которая удивляет каждого при первой встрече; баг медиазапроса, ломающий вёрстку при масштабировании; случаи, где px на самом деле оказывается более доступным выбором. Дальше я разбираю все три темы с готовым CSS для каждой и даю шпаргалку по свойствам, которую можно держать открытой, пока вы пишете стили.

Что на самом деле означают px, rem и em

У трёх единиц три разные точки отсчёта, и в этом всё различие.

px — это абсолютная единица. Один px — это один пиксель CSS, и он остаётся такого же размера независимо от окружения. border: 1px solid — это один пиксель, и точка. Подвох в том, что «абсолютная» означает ещё и «игнорирующая» предпочтения пользователя — почему это важно, поговорим ниже.

rem отсчитывается от размера шрифта корневого элемента. Корень — это <html>, и браузеры по умолчанию задают ему размер шрифта 16px. Поэтому 1rem равен 16px при стандартных настройках, везде на странице и независимо от вложенности. Ради этой предсказуемости rem и выбирают: одно опорное значение и никаких сюрпризов.

em отсчитывается от размера шрифта текущего элемента (или его родителя — для свойств, отличных от font-size). Поскольку эта точка отсчёта меняется по мере вложения элементов, значения em сдвигаются в зависимости от контекста. Один и тот же 1.5em может превратиться в 24px в одном месте и в 30px — в другом.

Запомнить стоит одно: 16px = 1rem. Если усвоите из всего только это, уже хватит. Когда нужно перевести конкретное значение, конвертер px в rem выполнит деление на любую выбранную вами базу.

px vs rem vs em — кратко

ЕдиницаОтносительно чегоМасштабируется с размером шрифта пользователя?Типичное применениеПоведение при вложенности
pxНичего (абсолютная)НетГраницы, смещения теней, тонкие линииВсегда одного размера
remРазмер шрифта корня <html>ДаРазмер шрифта, отступы, контрольные точкиВсегда одного размера
emРазмер шрифта текущего элементаДаЛокальные значения, привязанные к компонентуНакапливается — может «уплывать»

Главные тут два столбца: «масштабируется с размером шрифта пользователя» и «поведение при вложенности». rem выигрывает по обоим: он уважает предпочтение читателя и остаётся предсказуемым. em разделяет первое преимущество, но жертвует вторым.

Как вычисляется каждая единица

Математика тут — обычная арифметика. Людей сбивает с толку то, на какое число делить или умножать.

rem использует размер шрифта корня:

rem = px ÷ root-font-size

При корне в 16px по умолчанию 24px ÷ 16 = 1.5rem. Чтобы вернуться обратно, умножаем: 1.5rem × 16 = 24px. Каждый rem на странице использует одно и то же значение 16 (или то, что вы задали корню), — именно поэтому rem предсказуем.

em использует собственный размер шрифта текущего элемента:

em = px ÷ current-element-font-size

Если у элемента font-size равен 20px, то 1em для этого элемента — это 20px, 0.5em — это 10px, а отступ 1.5em — это 30px. Измените размер шрифта элемента, и каждое привязанное к нему значение em изменится вместе с ним. В этой локальной связанности и смысл em, и его ловушка.

Ловушка накопления em

Именно здесь em чаще всего и подводит. Когда вы вкладываете элементы, которые все используют em для размера шрифта, значения перемножаются вниз по дереву. Каждый уровень наследует вычисленный размер шрифта своего родителя и применяет поверх собственный коэффициент em.

.menu      { font-size: 1.2em; } /* родитель 16px → 19.2px */
.menu .item { font-size: 1.2em; } /* родитель 19.2px → 23.04px */
.menu .item .sub { font-size: 1.2em; } /* родитель 23.04px → 27.648px */

Каждый уровень — это «120% от своего родителя», что звучит безобидно. Но поскольку родитель уже вырос, третий уровень составляет 1.2 × 1.2 × 1.2 = 1.728em относительно исходных 16px — около 27.6px, а не 19.2px, которые вы могли бы вывести из правила, рассматривая его изолированно. Вложите список внутрь списка внутри компонента — и текст раздуется так, что отследить причину будет непросто.

rem обходит это полностью. 1.2rem — это 19.2px, стоит ли он в начале документа или на двенадцатом уровне вложенности, потому что rem всегда отмеряется от корня, а не от родителя. Когда значение разрешается в неожиданный размер, первый вопрос — это em (относительно родителя, накапливается) или rem (относительно корня, стабильно)? Если вы отлаживаете «заблудившийся» rem и хотите быстро увидеть его размер в пикселях, конвертер rem в px разрешит его мгновенно.

Когда использовать rem

По умолчанию тянитесь за rem. Это правильная единица для размеров шрифта, отступов, полей, промежутков, скруглений и контрольных точек медиазапросов — для всего, что должно масштабироваться, когда читатель меняет размер текста.

Последняя оговорка — это и есть аргумент о доступности, и он не гипотетический. Опросы WebAIM среди пользователей скринридеров и людей со слабым зрением стабильно показывают, что значительная доля пользователей меняет размер шрифта по умолчанию в браузере или ОС, причём многие — заметно выше стандартных 16px. Вёрстка с размерами в rem уважает это изменение: поднимите значение по умолчанию до 20px, и каждое значение на основе rem вырастет пропорционально. Вёрстка с размерами в px игнорирует его полностью — текст остаётся прикованным к жёстко заданному размеру, как бы сильно читателю ни требовался размер крупнее.

:root {
  font-size: 16px; /* 1rem = 16px */
}

h1   { font-size: 2rem;     } /* 32px, масштабируется с настройкой пользователя */
p    { font-size: 1rem;     } /* 16px */
.card { padding: 1.5rem;    } /* 24px */
.card { border-radius: 0.5rem; } /* 8px */

Поскольку каждое значение здесь привязано к одному и тому же корню, единственное изменение размера корневого шрифта пропорционально пересчитывает весь интерфейс. Так и держится цельность дизайн-системы: отступы и типографика двигаются вместе, а не расходятся врозь.

Трюк с 62.5%

Есть популярный приём, чтобы сделать арифметику rem тривиальной. Задайте размер корневого шрифта равным 62.5%, то есть 62.5% × 16px = 10px:

html {
  font-size: 62.5%; /* теперь 1rem = 10px */
}

body {
  font-size: 1.6rem; /* восстанавливаем читаемые 16px текста */
}

h1 { font-size: 2.4rem; } /* 24px */
p  { font-size: 1.6rem; } /* 16px */

С корнем в 10px устный счёт сводится к «раздели значение в пикселях на 10»: 24px → 2.4rem, 12px → 1.2rem. Единственная тонкость — восстановить читаемый размер текста через body { font-size: 1.6rem }, поскольку при сырой базе 10px текст по умолчанию слишком мелкий. Использование 62.5% как процента, а не 10px, сохраняет относительность — так что читатель, который масштабирует значение браузера по умолчанию, всё равно получает пропорциональный рост. Если вы берёте эту базу, задайте размер корневого шрифта в конвертере равным 10, чтобы он совпадал с вашей таблицей стилей.

Когда использовать em

Используйте em, когда хотите, чтобы значение масштабировалось вместе с собственным размером шрифта элемента, а не корня. Классический случай — кнопка:

.btn {
  font-size: 1rem;      /* размер относительно корня */
  padding: 0.75em 1.5em; /* отступы привязаны к тексту кнопки */
}

.btn--large {
  font-size: 1.25rem;   /* одно изменение меняет размер всего */
}

Поскольку отступы заданы в em, модификатор .btn--large меняет размер текста и его отступов вместе из одного объявления — кнопка остаётся пропорциональной при любом размере. Та же логика применима к иконке с размером в em, чтобы она совпадала со строкой текста, рядом с которой стоит, или к межбуквенному интервалу, который должен расти вместе со шрифтом.

На практике работает так: rem для глобального каркаса, em для локальных пропорций. Задавайте размер шрифта в rem, чтобы он отвечал корню и предпочтению пользователя; ту горстку значений, которые должны следовать за этим элементом, задавайте в em. Только держите em подальше от всего, что глубоко вкладывается, иначе ловушка накопления из предыдущего раздела вернётся.

Когда использовать px

Некоторые значения действительно не должны масштабироваться, и px для них правилен: тонкая граница в 1px, точное смещение box-shadow, фокусное кольцо в 2px. Это детали отрисовки, а не контент. Граница, «масштабирующаяся» до 1.25px, когда пользователь увеличивает текст, ничего не выигрывает и может отрисоваться как размытая линия. px сохраняет её чёткой.

.divider { border-bottom: 1px solid; }     /* должна остаться 1px */
.card    { box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* фиксированное смещение */
.input:focus { outline: 2px solid; }       /* чёткое фокусное кольцо */

Неожиданный нюанс — когда px доступнее

А вот часть, которую советы «всегда используйте rem» обычно опускают. Доступный выбор — это не «rem везде», а «масштабируй то, что должно масштабироваться, и фиксируй то, что не должно».

Граница в 1px — это фиксированная деталь. Если втиснуть её в rem, чтобы она росла, когда пользователь увеличивает текст, читаемости это не добавит, а тонкую линию сделает размытой. Для таких свойств px — более доступный выбор именно потому, что остаётся на месте.

Реальная ошибка, как правило, обратная: использование px для того, что должно реагировать, например для размеров шрифта и контрольных точек. Вот где px вредит доступности. Так что правило не про единицу, а про свойство. Спросите себя: это значение — контент, с которым взаимодействует читатель (масштабируй — rem), или фиксированная деталь отрисовки (фиксируй — px)? Единица вытекает из ответа.

Ловушка медиазапроса

Эта ловушка ломает реальную вёрстку, а предупреждают о ней редко. Контрольные точки медиазапросов, написанные в px, не реагируют на масштабирование размера шрифта в браузере так, как вам бы хотелось.

Представьте контрольную точку width: 600px, на которой сворачивается боковая панель. Пользователь со слабым зрением задаёт размер браузера по умолчанию в 24px, чтобы читать комфортно. Теперь вашему контенту нужно больше горизонтального места — более крупный текст хочет перетечь раньше. Но контрольная точка в px не знает, что текст вырос; она всё так же срабатывает ровно на 600px ширины окна, поэтому вёрстка переключается не в тот момент, и контент сжимается или накладывается.

Сравните два подхода:

/* контрольная точка в px — игнорирует настройку размера шрифта пользователя */
@media (min-width: 600px) {
  .sidebar { display: block; }
}

/* контрольная точка в em/rem — реагирует на размер шрифта пользователя */
@media (min-width: 37.5em) {
  .sidebar { display: block; }
}

37.5em — это 600px при стандартных 16px (600 ÷ 16 = 37.5). Различие в поведении: когда пользователь удваивает размер шрифта по умолчанию, контрольная точка в em фактически тоже удваивается, так что вёрстка переключается при ширине окна, пропорциональной тексту, — ровно тогда, когда это нужно контенту. Контрольная точка в px остаётся замороженной.

Одна тонкость, о которой стоит знать: в условиях медиазапросов em и rem оба отсчитываются от размера шрифта браузера по умолчанию, а не от какого-либо переопределения в html, поэтому там они ведут себя одинаково. Любая из единиц исправляет баг; px его вызывает.

Таблица решений по свойствам

Когда вы сомневаетесь, эта таблица даёт ответ, не заставляя выводить логику заново каждый раз.

СвойствоРекомендуемая единицаПочему
font-sizeremМасштабируется с настройкой размера шрифта пользователя
padding / marginremОтступы масштабируются вместе с текстом
borderpxТонкие линии должны оставаться чёткими и фиксированными
смещение box-shadowpxТочная деталь отрисовки, а не контент
border-radiusremСохраняет скругление углов пропорциональным масштабу
медиазапросem / remКонтрольные точки должны реагировать на масштаб шрифта
width / max-widthrem (часто ch для текста)Масштабируемая ширина вёрстки; ch ограничивает длину строки
line-heightбез единицыБезразмерный множитель наследуется правильно

Строка line-height заслуживает примечания, потому что это частый баг. Всегда пишите line-height: 1.5, без единицы. Безразмерное значение — это множитель, который каждый элемент вычисляет относительно собственного размера шрифта, поэтому вложенные элементы остаются читаемыми. Напишите line-height: 1.5em или 24px — и наследуется вычисленная длина, а значит дочерний элемент с более крупным шрифтом сохранит межстрочный интервал родителя, и его текст начнёт налезать. Безразмерное значение снимает всю проблему.

Перевод между px и rem

Арифметика достаточно мала, чтобы делать её в уме, как только вы держите якорь: 16px = 1rem. Делите на 16, чтобы перейти к rem, умножайте на 16, чтобы вернуться к px.

pxrem (база 16px)
8px0.5rem
12px0.75rem
16px1rem
24px1.5rem
32px2rem

Если вы используете трюк с 62.5%, база становится 10px, и математика ещё проще — просто делите или умножайте на 10, так что 24px = 2.4rem. Единственное правило — всегда переводите относительно той базы, которую ваша таблица стилей действительно задаёт.

Для всего остального — нестандартных значений, кастомного корня или пакетного перевода экспорта из Figma — пропустите устный счёт и используйте конвертер px в rem или конвертер rem в px. Оба позволяют задать любой размер корневого шрифта и переводить в любом направлении в реальном времени. А если потом вы приводите в порядок таблицу стилей с перемешанными единицами, форматтер CSS нормализует за вас пробелы и отступы.

Частые ошибки

Большую часть проблем с единицами вызывают несколько повторяющихся шаблонов:

Задание размера корневого шрифта в px. Запись html { font-size: 16px } (вместо того чтобы оставить значение по умолчанию или использовать 100% / процент) напрямую переопределяет настройку размера шрифта в браузере пользователя. Значения rem по-прежнему вычисляются относительно него, но читатель больше не может масштабировать всю страницу. Оставьте корень по умолчанию или используйте процент.

Смешивание px и rem без системы. Одни размеры шрифта в px, другие в rem, отступы поделены между обоими — в результате вёрстка масштабируется неравномерно, когда пользователь меняет размер текста. Выберите rem по умолчанию и приберегите px для осознанных исключений из таблицы решений.

Использование em для глобальных отступов. Em на широко вложенных контейнерах вновь вводит ловушку накопления, так что padding глубоко в дереве разрешается во что-то, чего никто не задумывал. Держите глобальные отступы в rem; приберегите em для локальных значений в рамках компонента.

Указание единицы у line-height. Как описано выше, line-height: 24px или 1.5em наследуется как вычисленная длина и ломается на элементах с другим размером шрифта. Всегда используйте безразмерный множитель.

FAQ

rem лучше, чем px?

Для большинства размеров — да: rem лучше px, потому что масштабируется с настройкой размера шрифта в браузере пользователя, которую px игнорирует. Но «лучше» зависит от свойства: px — правильный выбор для фиксированных деталей вроде границ в 1px и смещений теней, которые должны оставаться чёткими. Используйте rem для размеров контента, px — для деталей отрисовки.

Чему равен 1rem в пикселях?

1rem равен размеру корневого шрифта в пикселях, который по умолчанию составляет 16px практически во всех браузерах. Значит, 1rem = 16px, 1.5rem = 24px, а 2rem = 32px при стандартных настройках. Если таблица стилей переопределяет html { font-size } — например, до 10px через трюк с 62.5%, — то 1rem равен уже этому значению.

Использовать rem или em для font-size?

Используйте rem для font-size почти в каждом случае. Rem отмеряется от корня, поэтому остаётся предсказуемым независимо от глубины вложенности элемента. Em отмеряется от размера шрифта родителя, что накапливается вниз по дереву и заставляет вложенный текст раздуваться неожиданно. Приберегите em для локальных значений, привязанных к одному компоненту.

Когда использовать px вместо rem?

Используйте px для значений, которые не должны масштабироваться с размером шрифта пользователя: границ в 1px, точных смещений box-shadow, контуров фокусных колец и других фиксированных деталей отрисовки. Это чёткие детали оформления, а не контент, поэтому фиксировать их в px — более доступный выбор. Всё, что относится к контенту, всё равно должно использовать rem.

Почему медиазапросы ломаются, когда я использую px?

Контрольные точки медиазапросов в px не реагируют на масштабирование размера шрифта в браузере. Когда пользователь увеличивает шрифт по умолчанию, его контенту нужно больше места, но контрольная точка в px всё так же срабатывает на той же ширине окна — поэтому вёрстка переключается не в тот момент. Используйте контрольные точки в em или rem, которые масштабируются с размером шрифта пользователя.

Что такое трюк с font-size 62.5%?

Трюк с 62.5% задаёт html { font-size: 62.5% }, делая размер корневого шрифта равным 10px (62.5% от 16). С базой в 10px математика rem становится «делением на 10»: 24px = 2.4rem, 12px = 1.2rem. Затем разработчики задают body { font-size: 1.6rem }, чтобы восстановить читаемые 16px текста.

Можно ли смешивать px, rem и em?

Да, смешивать px, rem и em правильно, когда каждая единица следует подходящему ей свойству: rem для типографики и отступов, px для фиксированных деталей, em для локальных значений в рамках компонента. Проблемы вызывает смешивание без системы — например, когда одни размеры шрифта в px, а другие в rem. Выберите rem по умолчанию, а px и em трактуйте как осознанные исключения.

Какую единицу использовать для padding и margin?

Используйте rem для padding и margin, чтобы отступы масштабировались вместе с текстом, когда пользователь меняет размер шрифта. Это сохраняет вёрстку пропорциональной и доступной. Приберегите em для отступов, которые должны следовать за собственным размером шрифта элемента, — например, кнопки, у которой отступы растут вместе с её текстом, — и избегайте em на глубоко вложенных контейнерах, где он накапливается.

Теги: css rem em frontend accessibility responsive-design

Похожие статьи

Все статьи