rem и em на одном вложенном примере
rem считается от корневого размера шрифта (элемент <html>, по умолчанию 16px). em считается от текущего размера шрифта, а для font-size это размер родителя. Вложите один и тот же класс три раза, и разница станет видна:
Выведенные размеры вычислил браузер. Каждый 1.25em умножает размер родителя: 20px, затем 25px, затем 31.25px. Каждый 1.25rem смотрит только на корень: 20px, 20px, 20px. Из-за такого перемножения размеры шрифта в em выходят из-под контроля во вложенных списках и меню, и поэтому большинство сайтов задают размеры шрифта в rem.
От чего считается каждая единица
| Единица | Считается от | 1 единица по умолчанию |
|---|---|---|
px | ни от чего (пиксель CSS) | 1px |
rem | размера шрифта корневого элемента (html) | 16px |
em | размера шрифта элемента; для самого font-size от размера родителя | 16px на верхнем уровне |
% | зависит от свойства (смотрите ниже) | |
vw / vh | 1% ширины / высоты окна просмотра | |
dvh, svh, lvh | 1% динамической, малой или большой высоты окна просмотра | |
vmin / vmax | 1% меньшей / большей стороны окна просмотра | |
ch | ширины «0» в текущем шрифте | около половины em |
ex | высоты строчной буквы x в текущем шрифте | около половины em |
Пиксель CSS это не пиксель устройства: на телефоне с экраном 3x 1px занимает 3 пикселя устройства. Визуально он примерно одинаков на разных экранах, поэтому px по-прежнему хорош для рамок и теней.
em для отступов, которые масштабируются с текстом
em нужен не только для размеров шрифта. В padding, margin, width или border-radius он означает «умножить на размер шрифта этого элемента». Так один класс кнопки работает при любом размере:
Padding каждой кнопки по вертикали равен половине ее размера шрифта, а по горизонтали одному размеру шрифта, поэтому три кнопки выглядят как одна и та же кнопка в трех размерах. С padding в px понадобилось бы отдельное правило для каждого размера.
Учтите, что кнопки по умолчанию не наследуют размер шрифта страницы (браузеры дают элементам форм свой), поэтому средняя кнопка здесь в Chrome 13.33px, а не 16px. Добавьте кнопкам и полям font: inherit, если хотите, чтобы они совпадали со страницей.
Проценты: зависит от свойства
У % нет единого смысла. Каждое свойство определяет, от чего берутся его проценты:
| Свойство | % считается от |
|---|---|
width, max-width, left, right | ширины родителя (содержащего блока) |
height, top, bottom | высоты родителя, и только если у родителя задана высота |
padding, margin (все четыре стороны) | ширины родителя |
font-size | размера шрифта родителя (150% = 1.5em) |
line-height | собственного размера шрифта элемента |
transform: translate() | собственного размера элемента |
width: 50% это 200px, половина родителя. padding-top: 5% это 20px: 5% от ширины родителя, а не от высоты. А height: 50% ничего не делает, потому что у родителя нет заданной высоты, и дочерний элемент высотой только с текст плюс этот padding. Это правило подробно разобрано на странице про width и height.
vw, vh и адресная строка на телефонах
1vw это 1% ширины окна просмотра, а 1vh 1% его высоты. Они полезны для полноэкранных секций и плавных размеров (часто внутри clamp()). На телефонах у vh есть подвох: адресная строка браузера выезжает и прячется при прокрутке, а 100vh это высота без строки, поэтому шапка с height: 100vh обрезается снизу, когда строка видна. Новые единицы это исправляют:
| Единица | Какая высота используется |
|---|---|
100vh | большое окно просмотра (панели скрыты) в большинстве мобильных браузеров |
100svh | малое окно просмотра (панели видны): всегда помещается |
100lvh | большое окно просмотра, явно |
100dvh | динамическое окно просмотра: меняется, когда панели двигаются |
.hero {
min-height: 100vh; /* fallback for older browsers */
min-height: 100svh; /* fits the screen even with the address bar visible */
}
У vw свой подвох: 100vw включает ширину вертикальной полосы прокрутки, когда полоса занимает место (как в Windows), поэтому width: 100vw может оказаться шире страницы и добавить горизонтальную прокрутку. Для «всей ширины родителя» используйте width: 100%.
ch для удобной длины строки
ch это ширина символа «0», поэтому max-width: 65ch ограничивает абзац примерно 65 символами в строке, удобной для чтения длиной, при любом размере шрифта:
Какую единицу выбрать
| Для чего | Что использовать | Почему |
|---|---|---|
| Размеры шрифта | rem | Одинаков везде, учитывает настройку размера шрифта читателя |
| Padding и промежутки в компонентах | rem или em | em, когда должен расти вместе с текстом самого компонента |
| Рамки, тонкие линии, тени | px | Должны оставаться тонкими при любом размере шрифта |
| Ширины раскладки | %, fr, max-width в rem или ch | Следуют контейнеру, ограничивают длину строки |
| Полноэкранные секции | svh / dvh (с запасным vh) | Учитывают адресную строку на телефонах |
| Плавная типографика | clamp() с сочетанием rem и vw | Растет вместе с экраном в пределах границ |
Почему rem уважает читателей, а px нет
В браузерах есть настройка размера шрифта по умолчанию, ей пользуются люди, которым нужен более крупный текст. Она меняет корневой размер шрифта, поэтому все в rem и em растет вместе с ней. font-size: 14px остается 14px, что бы ни выбрал читатель. Масштаб браузера увеличивает и px, но многие читатели полагаются именно на настройку размера шрифта. Это главная причина задавать размеры шрифта в rem.
Некоторые сайты задают html { font-size: 62.5%; }, чтобы 1rem равнялся 10px и считать было проще. Это по-прежнему учитывает настройку читателя (62.5% от выбранного им значения), но не забудьте задать удобный для чтения font-size на body, иначе каждый элемент без собственного размера покажет текст 10px.
Частые ошибки
- Размеры шрифта в
emво вложенных элементах. Они перемножаются на каждом уровне. Для размеров шрифта используйтеrem. height: 50%, который ничего не делает. Чтобы процентная высота работала, родителю нужна заданная высота.- Ожидание, что
padding-top: 10%будет считаться от высоты родителя. Проценты вертикальных padding и margin считаются от ширины родителя. width: 100vwвызывает горизонтальную прокрутку. Он включает полосу прокрутки. Используйте100%.height: 100vhобрезается на телефонах. Используйте100svhили100dvh.html { font-size: 12px }. Значение в px на корне перебивает настройку размера шрифта читателя для всей страницы. Не трогайте корень или используйте проценты.
Часто задаваемые вопросы
Чем rem отличается от em?
rem считается от размера шрифта корневого элемента (<html>), поэтому 1.5rem одинаков везде на странице. em считается от размера шрифта самого элемента (для font-size от размера родителя), поэтому при вложении элементов он перемножается.
Сколько пикселей в 1rem?
По умолчанию 16px, потому что браузеры задают корневому элементу размер шрифта 16px. Если пользователь увеличит размер шрифта по умолчанию в настройках браузера или вы зададите html { font-size: ... }, 1rem изменится вместе с ним.
Что использовать для размера шрифта: rem или px?
Для размеров шрифта используйте rem. Он учитывает настройку размера шрифта по умолчанию в браузере читателя, которую px игнорирует. Масштаб браузера увеличивает оба, но не каждый читатель пользуется масштабом.
Когда использовать em, а не rem?
em для отступов, которые должны масштабироваться вместе с текстом самого элемента, например padding кнопки или размер иконки рядом с текстом. Тогда один класс выглядит правильно при любом размере шрифта. rem для размеров шрифта и отступов на уровне страницы, чтобы ничего не перемножалось.
Чем 100vh отличается от 100dvh?
100vh в мобильных браузерах это высота со скрытой адресной строкой, поэтому секция 100vh выше видимого экрана, пока строка показана. 100dvh (динамическая высота окна просмотра) следует за строкой, когда она появляется и исчезает, а 100svh всегда меньшая высота.