Логічні властивості CSS
Логічні властивості CSS (CSS logical properties) прив'язують відступи, розміри та позиціонування до напрямку тексту, а не до фіксованих країв екрана.
Теорія
TL;DR
- Уяви GPS, що автоматично перевертає "поверни ліворуч" на "поверни праворуч" для RTL-карти. Логічні властивості працюють так само.
margin-leftзавжди залишається зліва по екрану в RTL;margin-inline-startпереходить вправо.- Одне правило CSS покриває LTR, RTL і вертикальний текст без
[dir="rtl"]-перевизначень. - Використовуй логічні властивості якщо підтримуєш RTL або вертикальне письмо; фізичні підходять для простих LTR-інтерфейсів.
- Підтримка браузерів: Chrome, Firefox, Safari (2020+), Edge 79+. IE не підтримується.
Швидкий приклад
/* Фізичне - потрібне дублювання для RTL */
.sidebar {
border-left: 2px solid #ccc;
}
[dir="rtl"] .sidebar {
border-left: none;
border-right: 2px solid #ccc; /* окреме перевизначення */
}
/* Логічне - одне правило для обох напрямків */
.sidebar {
border-inline-start: 2px solid #ccc; /* в RTL автоматично стає правим бордером */
}Логічна версія не знає про "ліво" і "право". Вона знає "початок інлайн-напрямку", що є лівом у LTR і правом у RTL.
Ключова різниця
Фізичні властивості (margin-left, top, border-right) прив'язані до фіксованих країв viewport. Вони не рухаються незалежно від мови. Логічні властивості (margin-inline-start, inset-block-start) обчислюються відносно поточного режиму письма: інлайн-вісь слідує напрямку тексту, блочна вісь слідує напрямку стекування рядків. Тому margin-inline-start стає margin-left у LTR і margin-right у RTL, та сама властивість, різна інструкція для браузера.
Коли застосовувати
- Підтримка RTL (арабська, іврит, перська): логічні властивості скрізь.
- Вертикальний текст (CJK, сайти в стилі манги): логічні +
writing-mode: vertical-rl. - Глобальний продукт або дизайн-система: логічні за замовчуванням, фізичні тільки для SVG і декоративних елементів.
- LTR-прототип що живе тиждень: фізичні, менше понять для розуміння.
- Компоненти теми (MUI, Chakra, Bootstrap RTL): логічні для відступів що не залежать від напрямку.
Таблиця відповідностей
| Фізична | Логічна | LTR | RTL | vertical-rl |
|---|---|---|---|---|
margin-left | margin-inline-start | left | right | top |
margin-right | margin-inline-end | right | left | bottom |
margin-top | margin-block-start | top | top | right |
padding-bottom | padding-block-end | bottom | bottom | left |
width | inline-size | width | width | height |
height | block-size | height | height | width |
float: left | float: inline-start | left | right | top |
| Коли | тільки LTR, legacy | RTL/глобальні застосунки | CJK вертикальні UI |
Як браузери обчислюють логічні властивості
Браузер першочергово читає direction і writing-mode під час обчислення стилів, а потім розкладає логічні властивості відповідно до специфікації CSS Writing Modes Level 3. margin-inline-start стає margin-left у LTR і margin-right у RTL. Blink і WebKit кешують розкладені фізичні значення в дереві компонування, тому повторного розбору на зміну dir немає, якщо вже використовуються логічні властивості. Але сама зміна dir запускає повний перерахунок layout. Групуй DOM-зміни і використовуй transform для анімацій якщо перемикаєш напрямок в runtime.
Ще один момент, що часто здивовує: у border-radius немає класичних логічних еквівалентів. Нові властивості називаються border-start-start-radius, border-start-end-radius, border-end-start-radius, border-end-end-radius. Підтримка з'явилась в Chrome, Firefox та Safari приблизно в 2021 році.
Типові помилки
Змішування фізичних і логічних властивостей на одному елементі:
/* Неправильно - обидва застосовуються, відступ додається */
.box {
margin-left: 10px;
margin-inline-start: 20px; /* 30px в LTR, а не 20px */
}
/* Правильно - обери одну систему */
.box {
margin-inline-start: 20px;
}Логічне скорочення не перевизначає фізичне. Обидва обчислюються незалежно і додаються. Це часто здивовує розробників що мігрують кодову базу на логічні властивості.
Очікування що text-align: start центрує контент:
/* В RTL, start = правий бік. Не центр. */
p { text-align: start; } /* Вирівнювання вправо в RTL */
/* Для справжнього центру використовуй center - у нього немає логічного еквівалента */
p { text-align: center; }Використання фізичного resize у вертикальному режимі письма:
/* Не дає корисного результату в writing-mode: vertical-rl */
textarea { resize: horizontal; }
/* Правильно */
textarea { resize: inline; } /* Зміна розміру вздовж інлайн-осі */Припущення що position: sticky однаково поводиться у вертикальному режимі:
В writing-mode: vertical-rl, position: sticky; inset-block-start: 0 прилипає до правого краю, бо block-start дорівнює right. Фізичне top: 0 ігнорує режим письма і прилипає до верху viewport. Я натрапив на це на CJK-дашборді де кожен sticky-заголовок прив'язувався до неправильного краю у вертикальному макеті. Перевіряй у RTL-симуляторі Chrome DevTools перед деплоєм.
Де застосовується в реальних проектах
- Material-UI v6 використовує
marginInlineв системі тем для RTL-сумісних відступів вsx-пропсах. - Tailwind CSS v3.4+ відображає
ms-4наmargin-inline-startі підтримує RTL черезdir-плагін. - Bootstrap 5.3 використовує
pe-3(padding-inline-end) в RTL-білдах черезdata-bs-dir="rtl". - Ionic Framework використовує логічні float для підтримки іврит у гібридних iOS/Android застосунках.
- Правило вибору: логічні якщо
dirзмінюється в runtime; фізичні для статичних LTR SVG або canvas.
Додай @supports (margin-inline-start: 0) для фізичного фолбеку якщо потрібна підтримка старих браузерів.
Питання з інтерв'ю
Q: Що відбувається з inline-size проти width в writing-mode: vertical-rl?
A: inline-size стає висотою, бо інлайн-вісь іде зверху вниз. width залишається шириною по екрану. Використовуй inline-size для манги або японських текстових макетів де осі поміняні місцями.
Q: Як логічні властивості взаємодіють з align-items у Flexbox?
A: align-items: start вирівнює по block-start краю, що є верхом у горизонтальному режимі і правом в vertical-rl. Осі Flexbox теж слідують режиму письма.
Q: Назви властивість без логічного еквівалента.
A: outline не має логічної форми. У border-radius тепер є логічні еквіваленти (border-start-start-radius тощо), але вони з'явились значно пізніше за основні логічні властивості і досі менш відомі.
Q: Зміна dir запускає reflow?
A: Так, повний перерахунок layout. Якщо перемикаєш LTR/RTL динамічно, групуй DOM-зміни і використовуй transform для анімацій щоб триматись біля 60fps.
Q: Складне питання: у shadow DOM з writing-mode: vertical-rl, як поводиться position: sticky; inset-block-start: 0 порівняно з фізичним top: 0?
A: Sticky-контейнер прилипає до правого краю, бо block-start дорівнює right в vertical-rl. Фізичне top: 0 ігнорує режим і прилипає до верху. Це не взаємозамінні варіанти, вони дають абсолютно різний результат.
Приклади
Порівняння: фізичне проти логічного в RTL
<!DOCTYPE html>
<html dir="rtl">
<head>
<style>
.card-physical {
margin-left: 20px; /* Залишається зліва по екрану в RTL */
border-left: 4px solid blue; /* Неправильний бік для RTL */
}
.card-logical {
margin-inline-start: 20px; /* В RTL переходить вправо */
border-inline-start: 4px solid blue; /* Бордер вправо в RTL */
}
</style>
</head>
<body>
<div class="card-physical">Фізична картка</div>
<div class="card-logical">Логічна картка</div>
</body>
</html>В RTL: фізична картка отримує лівий відступ і лівий бордер, неправильний бік для RTL-макета. Логічна картка автоматично переносить обидва вправо. Зміни dir="rtl" на dir="ltr" і обидві виглядають однаково. В цьому вся суть.
RTL-сумісний React-компонент
function Card({ title, children, dir = 'ltr' }) {
return (
<div dir={dir} style={{
marginInlineStart: '2rem', // RTL: стає margin-right
paddingBlock: '1rem', // Однаково в усіх режимах
inlineSize: '300px', // RTL: все одно 300px ширини
borderInlineStart: '5px solid blue' // RTL: бордер справа
}}>
<h3>{title}</h3>
{children}
</div>
);
}
// Використання
<Card dir="rtl" title="مرحبا">Контент тут</Card>
// Результат: синій бордер справа, 2rem відступ справа, 300px шириниMaterial-UI v6 використовує саме такий патерн у своїй системі тем. marginInlineStart в sx-пропсі адаптується до dir на найближчому батьківському елементі без жодної додаткової RTL-конфігурації.
Вертикальний режим письма з логічними розмірами
.manga-panel {
writing-mode: vertical-rl; /* інлайн-вісь = зверху вниз, блочна = справа наліво */
inline-size: 200px; /* стає висотою (інлайн іде вертикально) */
block-size: 100%; /* стає шириною */
margin-block-end: 20px; /* стає margin-left в vertical-rl */
}<div class="manga-panel">縦書きテキスト</div>Часта помилка: float: inline-start в writing-mode: vertical-rl відображається на float: top, якого legacy float не підтримує. Використовуй flexbox або grid для потоку елементів у вертикальному письмі.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.