CSS одиниці: px, rem, em
CSS одиниці px, rem та em визначають точку відліку для розмірів: фіксований піксель, font-size батька або font-size кореневого елемента.
Теорія
TL;DR
px- цегла: фіксований розмір, не залежить ні від батька, ні від rootemвідносний до font-size батьківського елемента і накопичується при вкладенніremвідносний до font-size кореневогоhtmlі однаковий скрізь- Проблема em: три рівні по 1.2em = 1.728em (1.2³), а не 1.2em
- Правило вибору:
remмайже скрізь,pxдля рамок і тіней,emколи відступ має масштабуватись разом зі шрифтом компонента
Швидкий приклад
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
Таблиця порівняння
| Аспект | px | em | rem |
|---|---|---|---|
| Точка відліку | Фіксована (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 для загальних відступів
/* ламається при вкладенні */
.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 для тексту
/* неправильно: налаштування шрифту користувача ігноруються */
body { font-size: 16px; }
/* правильно: масштабується з налаштуваннями */
body { font-size: 1rem; }WCAG вимагає, щоб текст масштабувався з налаштуваннями шрифту користувача. px блокує це. Це питання доступності, а не просто стиль.
Використовувати em для border
/* добре */
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 ламає вкладену навігацію
: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, яка враховує налаштування користувача
: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 вирішував більшість з них.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.