Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Логічні властивості CSS». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Логічні властивості CSS** (CSS logical properties) замінюють фізичні властивості типу `margin-left` або `border-right` на відносні до потоку (flow-relative) еквіваленти, що автоматично адаптуються до напрямку тексту. ```css /* Фізичне: залишається зліва по екрану навіть в RTL */ .icon { margin-left: 8px; } /* Логічне: в RTL переходить вправо */ .icon { margin-inline-start: 8px; } ``` **Ключове:** одне правило покриває LTR, RTL і вертикальний текст без `[dir="rtl"]`-перевизначень.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Логічні властивості 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 не підтримується. ### Швидкий приклад ```css /* Фізичне - потрібне дублювання для 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 році. ### Типові помилки **Змішування фізичних і логічних властивостей на одному елементі:** ```css /* Неправильно - обидва застосовуються, відступ додається */ .box { margin-left: 10px; margin-inline-start: 20px; /* 30px в LTR, а не 20px */ } /* Правильно - обери одну систему */ .box { margin-inline-start: 20px; } ``` Логічне скорочення не перевизначає фізичне. Обидва обчислюються незалежно і додаються. Це часто здивовує розробників що мігрують кодову базу на логічні властивості. **Очікування що `text-align: start` центрує контент:** ```css /* В RTL, start = правий бік. Не центр. */ p { text-align: start; } /* Вирівнювання вправо в RTL */ /* Для справжнього центру використовуй center - у нього немає логічного еквівалента */ p { text-align: center; } ``` **Використання фізичного `resize` у вертикальному режимі письма:** ```css /* Не дає корисного результату в 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 ```html <!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-компонент ```jsx 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-конфігурації. ### Вертикальний режим письма з логічними розмірами ```css .manga-panel { writing-mode: vertical-rl; /* інлайн-вісь = зверху вниз, блочна = справа наліво */ inline-size: 200px; /* стає висотою (інлайн іде вертикально) */ block-size: 100%; /* стає шириною */ margin-block-end: 20px; /* стає margin-left в vertical-rl */ } ``` ```html <div class="manga-panel">縦書きテキスト</div> ``` Часта помилка: `float: inline-start` в `writing-mode: vertical-rl` відображається на `float: top`, якого legacy float не підтримує. Використовуй flexbox або grid для потоку елементів у вертикальному письмі.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.