em или rem — в чём разница и что выбрать

Есть факт, который переворачивает спор «em или rem» с ног на голову: в медиазапросах обе единицы работают одинаково — и обе игнорируют то, что ты написал в html. Так и записано в спецификации медиазапросов.
То есть половина различий, из-за которых их сравнивают, там просто не существует. А там, где существует, разница вполне конкретная. Разберём по пунктам.
Коротко: от чего считается каждая
em считается от размера шрифта «в этом месте». Тонкость: для свойства font-size это шрифт родителя, для всего остального (padding, margin, width) — шрифт самого элемента.
rem считается от размера шрифта корневого элемента, то есть html. Одна точка отсчёта на весь документ. По умолчанию это обычно 16px, но пользователь может поменять значение в настройках браузера — и тогда поменяется твой 1rem.
Сравнение по тем критериям, которые реально решают
Точка отсчёта. У rem она одна и глобальная. У em — локальная и своя у каждого элемента. Отсюда всё остальное.
Предсказуемость. 1.5rem — это всегда одно и то же, где бы ты его ни написал. 1.5em придётся считать в уме, зная контекст. В большом проекте это разница между «посмотрел и понял» и «открыл инспектор».
Вложенность. Тут em кусается. Задай li { font-size: 0.9em } — и в списке внутри списка получится 0.81em, на третьем уровне 0.729em. Текст тает с каждым уровнем. Это называется компаундингом, и это самая частая причина «почему у меня где-то текст мелкий». У rem такого не бывает по определению.
Масштабирование компонента целиком. А тут выигрывает em. Опиши кнопку через padding: 0.6em 1.2em — и когда ты поменяешь только её font-size, отступы вырастут пропорционально. Одна переменная вместо трёх. Для rem пришлось бы править каждое значение.
Уважение к пользователю. Обе единицы в итоге опираются на настройку размера шрифта в браузере, поэтому обе — честный выбор с точки зрения доступности. А вот px эту настройку игнорирует: человек увеличил шрифт в браузере, а твой текст остался прежним.
Медиазапросы. Тот самый сюрприз. По спецификации относительные единицы внутри медиазапроса считаются от начального значения font-size, а не от того, что ты задал в стилях. Написал html { font-size: 62.5% } (то есть 10px) — в правилах 1rem станет 10px, а в @media (min-width: 40rem) порог останется 640-ю пикселями, как будто ты ничего не менял. Сюда же и em: в медиазапросах они эквивалентны.
Как быстро считать в уме
При стандартных 16 пикселях шкала запоминается за минуту:
0.75rem— 12px, мелкие подписи;0.875rem— 14px, вторичный текст;1rem— 16px, основной текст;1.25rem— 20px, подзаголовок;2rem— 32px, заголовок.
Дальше арифметика простая: нужное число пикселей делишь на 16. И не гонись за круглыми значениями — 0.9375rem ничем не хуже, просто выглядит непривычно.
С em такой шкалы не построить: пока не знаешь шрифт родителя, число ни о чём не говорит. Отсюда и практический совет — держи em там, где кегль задан рядом, буквально в соседней строке того же правила.
Кому что подойдёт
Без увиливаний:
rem— для всего, что задаёт масштаб страницы: размеры шрифтов, вертикальные отступы между блоками, ширины контейнеров. Предсказуемо и не накапливает ошибку.em— внутри компонента: отступы кнопки, поля, бейджа. Тогда компонент остаётся «резиновым» относительно собственного кегля, и его легко сделать крупнее одной строчкой.px— для того, что не должно расти: рамка в 1 пиксель, толщина тени, иконка фиксированного размера.
Если сомневаешься — бери rem. Ошибка с ним стоит дешевле: текст будет чуть не того размера. Ошибка с em прячется во вложенности и вылезает через месяц.
Можно ли поставить html font-size: 10px, чтобы считать было проще?
Так делают, чтобы 1.6rem читалось как «16 пикселей». Но абсолютное значение в html затирает настройку пользователя: человек выбрал в браузере крупный шрифт, а ты вернул ему 10px. Если очень хочется — используй 62.5%: проценты считаются от пользовательского значения и масштабируются вместе с ним. И помни про медиазапросы — на них этот трюк не действует.
А px вообще нельзя?
Можно, просто не для текста. Для рамок, теней и мелкой графики пиксели удобнее и честнее. Проблема только там, где значение должно расти вместе с настройками человека.
Чему равен 1rem по умолчанию?
В большинстве браузеров 16px — но это именно значение по умолчанию, а не константа. Пользователь может задать другое, и тогда весь макет на rem вырастет вместе с ним. Проверить, что получилось в итоге, всегда можно в инспекторе: вкладка Computed показывает конечные пиксели. Кстати, 16px — не случайное число и в другой истории: почему iPhone увеличивает страницу на поле ввода. А высота строки — единственное место, где единицы лучше не писать вовсе: как работает line-height. Если же значения вроде заданы, а в браузере всё по-старому — начни с базовых причин: почему CSS не применяется.
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.





