Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «CSS препроцесори: SASS, SCSS та LESS». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**CSS препроцесори** (SASS, SCSS, LESS) розширюють CSS змінними, вкладеністю та міксинами, а потім компілюють у звичайний CSS для браузера. SCSS використовують найчастіше, бо він є повною надмножиною CSS. ```scss $color: #007bff; .nav { background: $color; } // Компілюється у: .nav { background: #007bff; } ``` **Ключове:** SCSS на 100% сумісний з CSS - будь-який валідний CSS файл є одночасно валідним SCSS.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**CSS препроцесори** компілюють розширений синтаксис CSS зі змінними, вкладеністю, міксинами та функціями у звичайний CSS, який розуміє браузер. ## Теорія ### TL;DR - Препроцесор схожий на текстовий редактор з шаблонами: пишеш структуровані файли з багаторазовими блоками, компілюєш у plain CSS для браузера. - SCSS є строгою надмножиною CSS (будь-який валідний CSS є валідним SCSS); SASS прибирає фігурні дужки на користь відступів; LESS використовує `@` для змінних і компілюється через JS. - Головний поділ: SCSS і LESS використовують фігурні дужки та крапки з комою, SASS - тільки відступи. - Правило вибору: SCSS для команд (міграція CSS без змін); LESS для динамічного JS-теміфікування; обидва можна пропустити, якщо вистачає CSS custom properties. - Bootstrap 5.3, Angular 18 та Bulma поставляються з SCSS. ### Швидкий приклад ```scss // input.scss $blue: #007bff; .nav { background: $blue; .item { padding: 1rem; &:hover { background: lighten($blue, 10%); // обчислюється до #4dabf7 } } } ``` ```css /* Скомпільований результат */ .nav { background: #007bff; } .nav .item { padding: 1rem; } .nav .item:hover { background: #4dabf7; } ``` Компілятор читає вкладену структуру, вирівнює її у стандартні селектори і підставляє всі змінні за один прохід. ### Головна різниця SCSS на 100% сумісний з CSS. Вставляєш будь-який існуючий stylesheet у `.scss` файл - він компілюється без жодних правок. Саме це робить міграцію простою. Оригінальний SASS замінює фігурні дужки та крапки з комою на відступи, що деяким розробникам подобається за мінімалістичний вигляд. LESS зовні схожий на SCSS, але використовує синтаксис `@variable`, і його JS-компілятор може давати несподівані результати у складних циклах на великих кодових базах. Більшість React-проектів, з якими я працював, зупинялись на SCSS щойно кількість компонентів переходила за 20 - просто тому, що наявний CSS вставляється без правок. ### Коли що використовувати - **Командний проект з наявним CSS**: SCSS - мігруєш файли без змін. - **Динамічне теміфікування через JS** (як у Ant Design 5): LESS - його `@var` добре інтегрується з JS-конфігурацією. - **Малий проект або прототип**: пропускай препроцесори, використовуй CSS custom properties (`--color: red;`) - підтримуються всіма сучасними браузерами з 2016 року. - **Angular або React/Vite**: будь-який варіант - `sass-loader` або вбудована підтримка Vite впораються з компіляцією. - **Ruby-стек**: SASS - найглибша екосистема там. ### Таблиця порівняння | Особливість | SASS (.sass) | SCSS (.scss) | LESS (.less) | |---|---|---|---| | Синтаксис | Тільки відступи | Фігурні дужки + крапки з комою | Фігурні дужки + крапки з комою | | Надмножина CSS | Ні | Так (100%) | Так (здебільшого) | | Змінні | `$var` | `$var` | `@var` | | Міксини | `@mixin` | `@mixin` | `.mixin()` | | Компіляція | Ruby/Node | Ruby/Node/Dart | JS/Node/браузер | | Цикли | `@each`, `@for` | `@each`, `@for` | Guards, рекурсивні міксини | | Коли використовувати | Ruby-розробники | Команди, міграція CSS | JS-проекти, динамічні змінні | ### Як працює компіляція Ти пишеш `.scss`, `.sass` або `.less` файли. Компілятор (Dart Sass для SCSS/SASS починаючи з v1.77+, `lessc` для LESS v4.1+) розбирає вихідний код у AST (abstract syntax tree), підставляє всі змінні та виклики міксинів за один прохід і видає стандартний CSS. `sass-loader` для Webpack v14+ або вбудована підтримка Vite слідкують за файлами і перекомпіловують при збереженні, записуючи результат у `/dist`. Браузер ніколи не бачить вихідних файлів препроцесора. ### Типові помилки **Вкладеність глибше 3-4 рівнів** ```scss // Неправильно - компілюється у .a .b .c .d .e { color: red; } a { b { c { d { e { color: red; } } } } } ``` Глибока вкладеність роздуває скомпільований вивід і уповільнює пошук селекторів. Chrome DevTools позначає селектори глибше 6 рівнів. Тримай вкладеність до 3 рівнів або використовуй BEM: `.nav__item--active { color: red; }`. **Глобальне перевизначення змінних між файлами** ```scss // _theme.scss $color: blue; // _component.scss імпортує _theme.scss, потім: $color: red; // перевизначає глобально ``` Часткова перекомпіляція у Webpack може пропустити залежності і дати непослідовний результат. Використовуй `@use 'theme' as *;` з Sass modules (v1.23+). Це виокремлює простір імен змінних і обчислює кожен файл лише один раз. **`@import` замість `@use`** `@import` скидає все у глобальний простір імен і повторно виконує файл при кожному імпорті. `@use` завантажує файл один раз і дає доступ через простір імен (`theme.$primary`). Dart Sass позначив `@import` як застарілий починаючи з v1.80. **Компіляція LESS у браузері** Стара практика підключення `less.js` безпосередньо у браузері не генерує source maps і ламається на сучасних міксинах. Завжди компілюй наперед за допомогою `lessc` або бандлера. **Ліниве обчислення у циклах LESS** ```less // LESS - guard-based цикл може пропускати значення .loop(@n) when (@n > 0) { .w-@{n} { width: ~"@{n}%"; } .loop((@n - 5)); } .loop(25); ``` Для надійних циклів `@for` у SCSS поводиться передбачуваніше: ```scss @for $i from 1 through 5 { .w-#{$i * 5} { width: $i * 5%; } } ``` ### Де зустрічається у реальних проектах - **Bootstrap 5.3**: SCSS змінні та міксини для теміфікування (`$primary`, `@mixin button-variant`). - **Ant Design 5**: LESS для динамічного теміфікування з JS-конфігурацією змінних. - **Angular 18**: вбудована підтримка SCSS з вкладеністю для `:host`. - **Bulma 1.0**: чистий SCSS з CSS custom properties як fallback. - **React/Vite**: `sass-loader` плюс SCSS modules на рівні компонентів. ### Питання на співбесіді **Q:** Яка різниця між `@mixin` і `@extend` у SCSS? **A:** `@mixin` копіює оголошені стилі у кожен селектор, який його підключає - це може роздути скомпільований вивід. `@extend` змушує кілька селекторів спільно використовувати один блок правил, що компактніше, але чутливо до порядку. Використовуй `@extend` для утилітарних класів на кшталт `.btn`; `@mixin` - коли потрібно передавати аргументи. **Q:** Як обробляти vendor prefixes при роботі з препроцесорами? **A:** Не безпосередньо через препроцесор. Стандартна практика - запускати Autoprefixer як PostCSS плагін після компіляції. Він читає налаштування browserslist і додає `-webkit-`, `-moz-` тощо автоматично. **Q:** Яка різниця між `@use` і `@import` у Sass? **A:** `@import` скидає все у глобальний простір імен і повторно виконує файл при кожному імпорті. `@use` завантажує файл один раз і виокремлює змінні через простір імен (`theme.$primary`). З'явився у Dart Sass v1.23; `@import` визнано застарілим з v1.80. **Q:** Чим Dart Sass відрізняється від LibSass? **A:** Dart Sass - офіційна реалізація (v1.77+ станом на 2024), точно слідує специфікації. LibSass - C++ порт, визнаний застарілим у 2021 через відставання від `@use` і `@forward`. Якщо ще використовуєш `node-sass` - мігруй на пакет `sass`. **Q (senior):** У монорепо зі 100+ SCSS партіалів як уникнути каскадних перезбірок? **A:** Використовуй `@use` замість `@import`, щоб кожен партіал обчислювався один раз. Поєднуй з Vite `?inline` імпортами для HMR на рівні компонентів. Повільні збірки профілюй через `sass --trace`. Уникай glob-імпортів - вони ламають відстеження залежностей. ## Приклади ### Навбар з брейкпоінтами та темою React-застосунок: навбар адаптується до розміру екрана і бере кольори зі спільного файлу теми. ```scss // styles/navbar.scss $primary: #0d6efd; $breakpoint-md: 768px; @mixin flex-center { display: flex; align-items: center; } .navbar { @include flex-center; padding: 1rem; background: $primary; @media (min-width: $breakpoint-md) { padding: 1.5rem; } .logo { font-size: 1.5rem; } &__link { color: white; text-decoration: none; &:hover { color: lighten($primary, 20%); } } } ``` ```css /* Скомпільований результат (спрощено) */ .navbar { display: flex; align-items: center; padding: 1rem; background: #0d6efd; } @media (min-width: 768px) { .navbar { padding: 1.5rem; } } .navbar .logo { font-size: 1.5rem; } .navbar__link { color: white; text-decoration: none; } .navbar__link:hover { color: #6ea8fe; } ``` Міксин бере на себе flex-налаштування, `$primary` оголошено один раз, а медіа-запит знаходиться поруч з компонентом, якого він стосується, а не в окремому файлі брейкпоінтів. ### Sass modules з `@use` Цей підхід замінює `@import` у будь-якому Dart Sass проекті (v1.23+) і є поточною рекомендованою практикою. ```scss // _theme.scss $primary: #0d6efd; $danger: #dc3545; $font-size-base: 1rem; ``` ```scss // button.scss @use 'theme'; .btn { font-size: theme.$font-size-base; background: theme.$primary; &--danger { background: theme.$danger; } } ``` Префікс `theme.` одразу показує, звідки береться кожна змінна. Не доведеться шукати по 50 партіалах, який файл востаннє встановив `$primary`. ### Цикли: SCSS проти LESS Генерація utility-класів для відступів - типове завдання. Ось як кожен препроцесор з ним справляється. ```scss // SCSS - передбачуваний @for цикл @for $i from 1 through 5 { .mt-#{$i} { margin-top: $i * 0.25rem; // 0.25, 0.5, 0.75, 1, 1.25rem } } ``` ```less // LESS - рекурсивний міксин з guard (аналогічний результат) .spacing-loop(@n) when (@n > 0) { .mt-@{n} { margin-top: (@n * 0.25rem); } .spacing-loop((@n - 1)); } .spacing-loop(5); ``` Обидва варіанти генерують однакові п'ять класів. SCSS версія читається як звичайний цикл; LESS версія вимагає розуміння рекурсії через guards, перш ніж безпечно її змінювати.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.