Единицы размеров в CSS
В CSS существует множество единиц измерения. Обычно мы используем единицы размеров, но также есть единицы для углов — например, deg и turn, единицы времени — s и ms, единицы плотности экрана — например, dpi и dppx и другие.
Здесь будут рассматриваться только единицы размеров, которыми мы пользуемся чаще всего. Подробное описание можно найти в спецификации W3C Distance Units: the <length> type.
Общим для всех единиц длины будет то, что для значения 0, единицы можно не указывать: height: 0px и height: 0 будут работать одинаково, так что единицы измерения можно отбросить. Это позволяет немного быстрее писать код и считывать значения свойств.
Абсолютные единицы измерения
cm |
сантиметр | 1cm = 96px/2.54 ≈ 37,795px |
mm |
миллиметр | 1mm = 1/10 от 1cm |
q |
четверь миллиметра | 1q = 1/40 от 1cm = 0.25mm ≈ 0.945px |
in |
дюйм | 1in = 2.54cm = 96px |
pc |
пика | 1pc = 1/6 от 1in |
pt |
пункт, точка | 1pt = 1/72 от 1in |
px |
пиксель | 1px = 1/96 от 1in, 0.75 от pt |
Абсолютные единицы относительны друг для друга (соотношения в последней колонке), но все они привязаны к конкретным величинам.
Для печатных устройств сантиметры, миллиметры и дюймы должны быть равны своим обычным значениям, но для экранов это будет не так:
Я попыталась разными способами получить полоску длиной 10 сантиметров. Все полоски равны друг другу, но ни одна не равна 10 сантиметрам, если приложить линейку к экрану. Проверила на двух мониторах: на одном они короче, на другом — длиннее.
На веб-страницах физические единицы измерения вроде cm, mm и in не будут работать как ожидается, потому что главная экранная единица измерения — пиксель.
Что такое пиксель и какого он размера? Чем отличаются пиксели px от точек pt? Почему у них такие странные размеры в 1/96 дюйма и 1/72 дюйма соответственно? Как были выбраны эти значения и почему этих единиц измерения две? Попытка разобраться с мелкой точкой на экране вызывает множество вопросов и требует погружения в историю. Я попыталась выяснить как всё было, и вот что узнала.

Когда появились первые программы для предпечатной подготовки, нужно было как-то соотнести размер элементов на экране с тем, чтоб будет напечатано на бумаге. Тогда оказалось, что один дюйм (inch) на экране соответствует 72 точкам. Это соотношение зафиксировано в pt, pt — это 1/72 дюйма. Изначально это была типографская единица измерения, и её значения менялись со временем, но с появлением компьютерных программ для полиграфии значение pt пришлось стандартизировать.
С развитием технологий стало возможно делать экраны с большим разрешением, и Microsoft предложила отображать 96 точек на дюйм, что было зафиксировано в px, то есть пиксель — это 1/96 дюйма. Это позволило увеличить чёткость мелкого текста, потому что для отображения символа теперь можно было использовать больше точек.
Размер px составляет 0.75 от pt. По сути, pt — это более крупный пиксель.
Позже появились ретиновые дисплеи с повышенной плотностью пикселей, и тогда CSS-пиксели, которые мы используем, перестали соответствовать физическим пикселям экрана, теперь это виртуальная единица, размер которой определяется устройством вывода. Для нас это не играет особой роли кроме как при работе с изображениями, когда для ретины нужно подготовить дополнительный набор картинок. Все размеры, заданные в пикселях, будут корректно отображены на экране вне зависимости от того, сколько физических пикселей для этого потребуется.
На сегодняшний момент соотношение физических единиц к точкам экрана утратило прежний смысл, но осталось зафиксированым в спецификации для сохранения обратной совместимости и упрощения конвертации одних единиц в другие.
При этом важно помнить, что раз CSS-пиксели уже не соответствуют физическим точкам экрана, сантиметры и дюймы при выводе на экран тоже не будут не соответствовать своим реальным размерам, об этом есть и в спецификации:
If the anchor unit is the pixel unit, the physical units might not match their physical measurements. Alternatively if the anchor unit is a physical unit, the pixel unit might not map to a whole number of device pixels.
То есть, если использовать дюймы для вёрстки веб-страниц, один дюйм всегда будет равен 96 пикселям, но никогда — реальной физической единице:
Таким образом, главное в абсолютных единицах — контекст использования:
- для вывода на печать экранные пиксели должны быть приведены к физическими единицами, следовательно, для подготовки документов к печати лучше использовать их;
- для вывода на экран дюймы и сантиметры должны быть приведены к пикселям, следовательно, для веб-разработки лучше сразу использовать пиксели.
Пока копалась в истории единиц измерения, выяснилось, что во внутреннем коде Gecko когда-то была поддержка километров, удалили в 2009-м:

Из физических единиц ещё может представлять интерес q: это относительно новая единица, и она поддерживается не всеми браузерами. q — это 0.25mm. Как и другие физические единицы, больше имеет смысл для печати, но также можно попытаться использовать её для уменьшения размера кода: q — это примерно 0.945px, то есть в некоторых случаях вполне можно использовать её вместо пикселей, получается один символ (q) вместо двух (px).
Пример такого использования я подсмотрела в этом демо. Открывайте осторожно, может повиснуть браузер. Попытка посмотреть стили в веб-инспекторе вешает его почти гарантированно, поэтому лучше посмотрите исходный код страницы. Скриншот:
Чистое безумие, конечно. Картина воспроизведена с помощью box-shadow, и мне кажется, тему рисования на CSS на этом можно закрывать.
Вес стилей демо — 4.5Mb, а если бы там вместо q были пиксели, стили весили бы на 300Kb больше.
Относительные единицы измерения
Относительные единицы измерения вычисляются на основе каких-то других величин: размера шрифта или размера экрана, и могут динамически меняться вместе с ними.
Единицы измерения, привязанные к шрифту
em |
размер шрифта элемента |
ex |
высота x в нижнем регистре |
ch |
ширина 0 (ZERO, U+0030) |
rem |
размер шрифта корневого элемента |
em
Для font-size это унаследованный размер шрифта, для остальных свойств — текущий размер шрифта, уже вычисленный для font-size.
Чтобы увидеть это вживую, возьмем такой код:
BODY {
/* Базовый размер шрифта */
font-size: 42px;
}
DIV {
/* Наследуем шрифт родителя и уменьшаем в два раза */
font-size: .5em;
}
/* Какой толщины будет border в каждом случае? */
.box-1 {
border-width: .5em;
}
.box-2 {
border-width: 1em;
}
Получилось вот что:
Розовая полоса — градиент высотой 1em, чтобы было с чем сравнивать.
Оба блока имеют одинаковый размер шрифта, уменьшенный относительно родительского элемента в два раза (font-size: .5em;). И как теперь указать толщину рамки равной размеру шрифта?
border-width: .5em делает рамку в два раза тоньше, чем нужно. Это происходит потому, что родительский размер шрифта использует только font-size, а border получает вычисленное значение из font-size.
Таким образом, если где-то не в font-size нужно использовать текущий размер шрифта, не нужно копировать значение размера, достаточно указать 1em. У правого блока рамка правильной толщины.
Ещё одно демо, для понимания как соотносятся em и символы шрифта. Цветные полосы имеют высоту 1em, поэтому видно, что 1em примерно соответствует высоте символов с учётом заглавных букв и выносных элементов:
Размер em нигде не зафиксирован, и вычисляется в момент использования на основе размера шрифта родителя. Например, если задать размер шрифта вот таким образом:
DIV {
font-size: .75em;
}
а потом вложить несколько дивов один в другой, размер шрифта каждого следующего дива будет меньше предыдущего:
Потому что 1em — это текущий унаследованный размер шрифта, а .75em — унаследованный шрифт, уменьшенный на четверть. Для каждого нового вложенного дива сначала наследуется уменьшенный шрифт родителя, а потом тоже уменьшается заданным образом.
Об этом нужно помнить, если вы захотите задавать в em размеры переиспользуемых компонентов: при вкладывании элементов друг в друга вычисленное значение em может оказаться не тем, что хотелось бы получить.
ex
ex — это высота буквы x в нижнем регистре. Если в шрифте нет подходящей метрики, и в нём нет такого символа, браузер попробует вычислить ex самостоятельно. Если это по каким-то причинам невозможно, ex считается равным .5em.
В демо цветные полосы имеют высоту 1ex, и для выбранных шрифтов 1ex будет равен высоте маленькой x:
Посмотрим, как на ex влияет шрифт, и как ex соотносится с em.
В этом демо квадратикам в левой группе заданы размеры в 1em, в правой — в 2ex, так можно проверить равен ли ex половине em. Также каждому квадратику задан свой шрифт:
В отличие от em, размер ex будет меняться вместе со шрифтом, и во всех случаях 2ex не равно 1em, то есть на соотношение в .5 полагаться нельзя.
ex точно также как и em наследует размер шрифта родителя:
ch
ch — ширина символа 0. Для моноширинных шрифтов это точная ширина любого символа, для остальных — примерная ширина одного узкого символа. Если по каким-то причинам ширину невозможно вычислить, запасным значением будет .5em.
Эрик Мейер предостерегает от попыток использовать ch для задания ширины контейнера в символах, потому что это не работает как ожидается. В демо ниже ширина каждого блока с текстом задана вот таким образом:
DIV {
width: 10ch;
}
Ширина блока будет верной только для моноширинных шрифтов (см. Courier), в некоторых шрифтах будет работать и для цифр (Arial, Comic Sans), в остальных случаях нельзя рассчитывать, что 1ch будет равен ширине символа:
Сопоставим 1em, 2ex и 2ch:
Очевидно, что:
1chбольше1ex(ширина0больше высотыx);1chне равен половинеem;- значение
chможет меняться вместе со шрифтом.
rem
rem — это root em, размер шрифта корневого элемента, для веб-страницы это элемент html. Размер шрифта по умолчанию — 16px. Это значение не зафиксировано в спецификации, но используется всеми браузерами. Про историю вопроса можно почитать в рассылке W3C.
Если пользователь в настройках браузера задаст другое значение, оно переопределит размер шрифта корневого элемента. То есть если нужно сделать интерфейс, который будет масштабироваться под размер шрифта, выбранный пользователем, в качестве единицы измерения удобно использовать именно rem.
Важно понимать, что размер rem можно переопределить только для элемента html. Например, возьмем такие стили:
BODY {
font-size: 24px;
}
DIV {
font-size: 1rem;
}
Если rem можно было бы переопределять в любом месте, текст бы увеличился, но этого не произошло:
Высота цветной полосы 24px, чтобы было с чем сравнивать.
Если переопределить размер шрифта для элемента html, всё сработает:
HTML {
font-size: 24px;
}
DIV {
font-size: 1rem;
}
В отличие от em, rem всегда содержит размер шрифта только корневого элемента, поэтому вложенность ни на что не влияет:
Это позволяет делать компоненты, размеры которых привязаны к базовому размеру шрифта, но не зависят от вложенности элементов друг в друга.
Единицы измерения, привязанные к размерам экрана
vw |
1% ширины вьюпорта |
vh |
1% высоты вьюпорта |
vmin |
1% от меньшего из vw и vh |
vmax |
1% от большего из vw и vh |
Эти единицы предназначены для создания элементов, размер которых должен зависеть от размера окна (вьюпорта).
100vh — это высота вьюпорта, очень удобно для элементов, которые должны растягиваться на всю страницу. Вот пример простой галереи, где каждая картинка будет полностью занимать один экран независимо от размеров окна браузера:
Размеры картинок задаются вот таким образом:
IMG {
height: 100vh;
width: 100%;
object-fit: cover;
}
Ширина вьюпорта — 100vw. Это значение позволяет растянуть на ширину экрана любой элемент при любой вложенность.
Если задать элементу ширину 100%, получится ширина родителя, который, скорее всего, занимает только часть экрана, а 100vw позволяет растянуть именно на ширину окна браузера не обращая внимания на размеры родительских элементов.
Правда, тут есть проблема: 100vw — это ширина всего окна вместе с полосой прокрутки, а доступная для контента ширина окна полосу прокрутки не включает, из-за чего при попытке задать элементам ширину вьюпорта появится горизонтальный скролл:
Если нет возможности изменить вёрстку, чтобы избежать использования 100vw, можно задать overflow-x: hidden ближайшему родителю, растянутому на ширину страницы:
Проблема с полосами прокрутки будет видна только в десктопных браузерах, потому что на мобильных скроллбар размещается поверх страницы и не занимает пространство.
Если вы верстаете на MacOS, и полосы прокрутки исчезают сами по себе, выберите в System Perefences/General опцию «Показывать всегда», это позволит верстать страницы сразу с учетом сколлбаров:

Используя vw и vh можно делать полностью резиновые элементы, которые будут сами подстраиваться под размер окна, например, так:
.text {
padding: 5vh 5vw;
background: paleturquoise;
background-image: repeating-linear-gradient(-45deg,
rgba(255,255,255, .25), rgba(255,255,255, .25) 2vw,
transparent 0, transparent 4vw
);
font-size: 12vw;
}
Лучше всего открыть это демо в отдельной вкладке и порастягивать окно браузера.
С помощью vmin можно сделать элемент, который всегда будет целиком помещаться в экран, сохраняя пропорции:
Это демо тоже лучше смотреть в отдельной вкладке, попробуйте порастягивать окно по вертикали и по горизонтали.
В отличие от единиц, привязанных к шрифту, единицы вьюпорта не реагируют на масштабирование страницы. Откройте это демо и поизменяйте масштаб используя Ctrl+/Ctrl-. Текст, размер которого задан в rem, будет увеличиваться и уменьшаться, текст с размером, заданным в vw, останется неизменным.
Пример с единицами вьюпорта ведёт себя совершенно логично, потому что масштабирование не меняет размер окна. Об этой особенности нужно помнить, если вы захотите использовать единицы вьюпорта для задания размера текста — пользователь не сможет сделать такой текст покрупнее.
В качестве решения предлагается примешивать единицы вьюпорта к относительным единицам измерения, привязанным к размеру шрифта, например, так:
html {
font-size: calc(1em + 0.5vw);
line-height: calc(1.1em + 0.5vw);
}
Вживую можно потестить здесь, пример взят из этой статьи. Больше про управление размером шрифта с помощью единиц вьюпорта можно почитать тут.
За дополнение про особенности масштабирования спасибо @bekharsky.
У единиц, привязанных к размерам вьюпорта, могут быть проблемы с поддержкой в IE включая 11-й, поэтому обязательно проверяйте код в действующем браузере.
В спецификации описано немного больше единиц измерения, например:
cap— высота заглавной буквыlh— высота строкиrlh— высота строки корневого элемента
Но в данный момент они нигде не поддерживаются, поэтому в статье не рассматриваются.
Спасибо Илье Стрельцыну за полезные ссылки.
- Ссылки по теме:
- Distance Units: the <length> type
- CSS <length>
- Point (typography)
- platform-specific font size issues — Mailing lists, 15 Dec 1998
- Making pt a non-physical unit — Mailing lists, 6 Jan 2010
- [CSS21] Issue 149 - px vs. pt — Mailing lists, 16 Jun 2010
- em, px, pt, cm, in…
- What is the CSS ‘ch’ Unit?
- The Lengths of CSS
- CSS Length Explained
- The Myth of DPI
- CSS Units
- Font sizing with rem
- Новые и старые единицы измерения (краткий обзор)
- CSS Viewport Units: A Quick Start
- Fluid Typography
- Fun with Viewport Units
- Simple Little Use Case for `vmin`
- Метки:
- шрифты,
- единицы измерения,
- текст
