Skip to main content

CSS одиниці: px, rem, em

CSS одиниці px, rem та em визначають точку відліку для розмірів: фіксований піксель, font-size батька або font-size кореневого елемента.

Теорія

TL;DR

  • px - цегла: фіксований розмір, не залежить ні від батька, ні від root
  • em відносний до font-size батьківського елемента і накопичується при вкладенні
  • rem відносний до font-size кореневого html і однаковий скрізь
  • Проблема em: три рівні по 1.2em = 1.728em (1.2³), а не 1.2em
  • Правило вибору: rem майже скрізь, px для рамок і тіней, em коли відступ має масштабуватись разом зі шрифтом компонента

Швидкий приклад

css
html { font-size: 16px; } /* база для rem */ .parent { font-size: 2rem; } /* 32px */ .child-em { font-size: 1.5em; } /* 48px - множиться на 32px батька */ .child-rem { font-size: 1.5rem; } /* 24px - множиться тільки на 16px root */ .child-px { font-size: 20px; } /* 20px - завжди 20px */

.parent має 2rem (32px). Його child-em при 1.5em дає 48px, а не 24px. rem ігнорує батька і бере значення прямо з кореневого елемента.

Ключова різниця

Все вирішує точка відліку. px її не має - завжди те саме число. em бере обчислений font-size батька і множить: глибоке вкладення дає накопичувальний ріст. rem завжди читає обчислений font-size html, тому 1.5rem - це 24px незалежно від того, наскільки глибоко вкладений елемент.

Коли що використовувати

  • Типографіка і відступи (більшість випадків): rem масштабується з налаштуваннями браузера і ОС
  • Рамки (border), box-shadow, outline: px тримає краї чіткими, масштабування тут не потрібне
  • Padding або margin пропорційний шрифту компонента: em, наприклад padding: 0.75em на кнопці, що росте і зменшується разом зі своїм текстом
  • Глибоко вкладені меню або компоненти: rem замість em, щоб уникнути накопичувальної математики
  • Лінії в 1px та дрібні іконки: тільки px

Таблиця порівняння

Аспектpxemrem
Точка відлікуФіксована (1/96 дюйма)font-size батькаfont-size кореневого html
Ефект вкладенняНемаєНакопичується (1.2em x 1.2em = 1.44em)Однакова скрізь
Зум користувачаФіксованийМасштабується від батькаМасштабується від root
ДоступністьІгнорує налаштування шрифтуСлідує за батьківським ланцюгомВраховує налаштування root
Найкраще дляРамки, тініЛокальні пропорції компонентаТипографіка, відступи, макет

Як браузер обчислює одиниці

Браузер вирішує одиниці під час cascade. Спочатку обчислюється font-size кореневого html (за замовчуванням 16px, якщо не перевизначено). rem читає це значення напряму. em проходить вгору по дереву стилів до батька і множить. Обидві одиниці перетворюються на абсолютні пікселі перед layout. px минає все це.

Типові помилки

Використовувати em для загальних відступів

css
/* ламається при вкладенні */ .menu { font-size: 1.2em; } .menu li { font-size: 1.2em; } /* вже 1.44em */ .menu li a { font-size: 1.2em; } /* 1.728em - не те, що задумувалось */ /* рішення */ .menu li a { font-size: 1rem; }

Три рівні по 1.2em дають 1.728 від базового розміру. Такі проблеми важко відстежити у великих проєктах.

Ставити px для тексту

css
/* неправильно: налаштування шрифту користувача ігноруються */ body { font-size: 16px; } /* правильно: масштабується з налаштуваннями */ body { font-size: 1rem; }

WCAG вимагає, щоб текст масштабувався з налаштуваннями шрифту користувача. px блокує це. Це питання доступності, а не просто стиль.

Використовувати em для border

css
/* добре */ button { border: 1px solid #333; font-size: 1rem; } /* виглядає дивно при великих шрифтах */ button { border: 0.1em solid #333; }

Рамки потребують фіксованого чіткого краю. em на border змінює її товщину разом зі шрифтом компонента, що рідко є наміром.

Де зустрічається в реальних проєктах

  • Tailwind CSS: p-4 = 1rem, класи border використовують px
  • Bootstrap 5: базовий $spacer: 1rem, фіксовані елементи в px
  • Material-UI (MUI): типографіка теми через h1: { fontSize: '2rem' }
  • Chakra UI: rem-based токени відступів по всій бібліотеці

Додаткові питання

Q: Який розмір шрифту root за замовчуванням у браузері?
A: 16px. Але користувач може змінити це в налаштуваннях браузера. Фактичне значення можна перевірити через getComputedStyle(document.documentElement).fontSize.

Q: Чи впливає viewport meta на rem?
A: Опосередковано. width=device-width встановлює базу логічних пікселів на мобільних. rem все одно береться від font-size html, але загальний макет масштабується під viewport.

Q: Чому box-shadow краще задавати в px?
A: Тіні з rem можуть виглядати розмитими при нестандартному зумі. px тримає їх чіткими, бо рендеринг тіней відбувається на рівні пікселів, а не шрифтів.

Q: У CSS-in-JS (Emotion, Styled Components) як працювати з rem при динамічному root?
A: Читай значення root під час виконання через getComputedStyle(document.documentElement).fontSize, потім конвертуй px в rem програмно. Більшість утиліт для тем у цих бібліотеках роблять це автоматично.

Q: Браузерний зум теж масштабує px. То в чому реальна різниця з доступності?
A: Зум масштабує все, включно з px. Але налаштування шрифту на рівні ОС (не зум) впливають тільки на відносні одиниці. Користувач із системним шрифтом 20px отримає користь від rem, але не від px.

Приклади

Накопичення em ламає вкладену навігацію

css
:root { font-size: 16px; } .nav { font-size: 1.125rem; } /* 18px */ .nav > li { font-size: 1.2em; } /* 21.6px */ .nav > li > a { font-size: 1.1em; /* 23.76px - виросло ненавмисно */ padding: 0.5em; /* 11.88px - теж непередбачувано */ } /* рішення: перейти на rem для внутрішніх елементів */ .nav > li > a { font-size: 1rem; /* 16px - передбачувано */ padding: 0.5rem; /* 8px - передбачувано */ }

Два рівні em-вкладення підняли шрифт вище 23px. Перехід на rem прив'язує все до root, незалежно від батьківського ланцюга.

Кнопка на rem, яка враховує налаштування користувача

css
:root { font-size: 16px; } .btn { font-size: 1rem; /* 16px, масштабується з root */ padding: 0.75rem 1.5rem; /* 12px вгору/вниз, 24px по боках */ border-radius: 4px; /* px - чіткі кути */ border: 1px solid #333; /* px - тонка чітка лінія */ }

Якщо користувач виставив базовий шрифт 20px, шрифт кнопки і padding масштабуються автоматично. Border залишається 1px - саме таку тонку чітку лінію ти і хочеш. Я бачив проєкти, де все було в px, а команда витрачала дні на дебагінг проблем з доступністю. Один перехід на rem вирішував більшість з них.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?