Як писати чистіший CSS: дюжина порад від банальних до неочевидних
CSS як технологія справді трохи роздутий, але не такий хаотичний і складний, як його іноді описують. Просто потрібно прагнути лаконічності – у тому числі, за допомогою описаних підходів.
Ненависть до CSS цілком можна зрозуміти – врешті-решт технологія розвивається вже понад 25 років. Нові браузери, що з’являються на ринку, реалізують підтримку CSS по-різному, що призводить до того, що частина коду працює в одному браузері, але не працює в інших. А розробнику доводиться інтегрувати до коду купу вендорних префіксів. Адже це потім ще треба розширювати і підтримувати!
Щоб не потонути в коді, користуйтеся більш сучасними та наочними підходами. Про деяких із них ми й поговоримо.
Підійдіть до вивчення CSS з правильного боку
Найкращий спосіб не вивчати CSS – це використовувати фреймворки, на зразок Bootstrap або Tailwind CSS. Ці інструменти допомагають швидко отримати гарний інтерфейс користувача, але ніяк не сприяють вивченню основ CSS (використовуючи їх, ви навчаєте тільки сам інструмент). І ви точно відчуєте біль і страждання при спробі від них відмовитися.
Вивчення основ CSS дає більше контролю над кодом та творчістю. І найкраща порада – почати з блокової моделі CSS, тому що зрозумівши її, ви знайдете більше сенсу у всій іншій мові.
Просто думайте про кожен HTML-елемент, як про блок, всередині якого міститься контент. Блок може мати висоту ( height
) та ширину ( width
), межу ( border
), відступи всередині ( padding
), а також пробіли навколо цієї межі зовні ( margin
). Все, що CSS пов’язане з макетом і розташуванням, спирається на блокову модель.
Якщо ви відкриєте інструменти Chrome, побачите, як обчислюється блокова модель для будь-якого елемента на сторінці.
І це автоматично приводить нас до наступної поради.
Використовуйте Flexbox
Історично розташування елементів один щодо одного було одним із найскладніших аспектів CSS.
Ох вже це споконвічне питання про те, як центрувати елемент div
і по горизонталі, і по вертикалі! Один із варіантів це реалізувати – задати дочірньому елементу абсолютне позиціонування, а потім перемістити його в нижній правий кут, використовуючи властивості top і left. Щоб помістити в центр не верхній лівий кут елемента, а саме його центр, потрібно також вказати властивість transform: translate(-50%, -50%)
. Ця властивість переміщає елемент вгору на 50% його висоти вздовж осі Y і вліво на 50% його ширини вздовж вісь X. У результаті потрібної точки виявиться саме центр.
.no-flex {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
Такий підхід працює, але дуже не інтуїтивний.
Більш сучасний інструмент CSS – це flex
, який створює гнучкий стовпець або рядок в будь-якому місці інтерфейсу користувача. У flex-елемента також є осі X і Y, якими можна вирівняти його дочірні елементи (їх можна вирівняти вздовж головної осі за допомогою властивості justify-content: center
). Перемістити елемент в центр і по горизонталі, і по вертикалі можна за допомогою властивості align-items
, вирівнявши відносно поперечної осі.
.with-flex {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
Grid – чудовий інструмент
Flex – це перший інструмент, до якого слід звертатися, коли справа стосується макету. Але він має один істотний недолік. Якщо користувальницький інтерфейс великий і складний, з безліччю рядків і стовпців, що перетинаються, в HTML-коді буде занадто багато елементів-контейнерів. Ці елементи не мають семантичного значення і існують просто для того, щоб CSS-код був, до чого прикріпитися.
На заміну їм є сучасна функція CSS – Grid. На відміну від flex
, який працює тільки з окремими стовпцями та рядками, grid
дозволяє думати про макет в цілому. Якщо ви займаєтеся веб-розробкою досить довго, ця ідея може здатися вам знайомою, тому що дуже нагадує таблиці. Просто grid
набагато більш дружелюбний до розробників.
Встановлюючи grid
, можна визначити його дочірні елементи як набір стовпців і рядків. Стовпці мають ширину, яку можна визначити за допомогою властивості. grid-template-columns.
Можна використовувати значення fr
або fractional unit, яка ділитиме доступний простір з іншими стовпцями в сітці. Ми також можемо визначити декілька рядків, після чого кожен елемент усередині сітки буде розміщуватися автоматично.
У наступному прикладі дочірні елементи розміщуються в сітці 2 рядки та 3 стовпці. Середній стовпець має фіксовану ширину 500px
, а інші два стовпці займають простір, що залишився. Рядки мають фіксовану висоту 100px
і 200px
відповідно.
.grid {
display: grid;
grid-template-columns: 1fr 500px 1fr;
grid-template-rows: 100px 200px;
place-items: center;
}
Використовуйте функцію Clamp
У контексті адаптивних макетів найчастіше йдеться просто про зміну ширини чогось залежно від доступного місця на екрані пристрою або в області видимості. Є кілька способів її скоригувати.
Допустимо, ви хочете, щоб стаття займала 50% ширини. Але на маленьких екранах її необхідно зробити фіксованою – шириною 200 пікселів, а на великих – 800 пікселів. Ви можете зробити це, вказавши @media
, які застосовуватимуться, залежно від розмірів області перегляду. Проблема в тому, що @media
будуть розмножуватися разом із зростанням проекту і підтримувати це стане складно.
article {
width: 50%;
}
@media only screen and (max-width: 600px) {
article {
width: 200px;
}
}
@media only screen and (min-width: 1200px) {
article {
width: 800px;
}
}
Ви можете змінити ситуацію, використовуючи функції min
, max
та clamp
. З їх допомогою код легко реорганізується в:
article {
width: clamp(200px, 50%, 800px);
}
Тут 200px
– мінімальне значення, 800px
– максимальне, а 50%
– переважне.
Використовуйте aspect-ratio
Наступна порада для тих, хто вставляє на сторінки адаптивні зображення або відео, які підтримують певне співвідношення сторін, наприклад 16:9.
Щоб усе нормально відображалося, завжди доводилося додавати зверху відступ padding-top: 56.25%
, а потім задавати дочірньому елементу абсолютне позиціонування.
.container-16x9 {
position: relative;
padding-top: 56.25%;
}
video {
width: 100%;
position: absolute;
top: 0;
}
Але це можна спростити, використовуючи таку властивість video
, як aspect-ratio
. У нашому випадку – aspect-ratio: 16/9
.
video {
width: 100%;
aspect-ratio: 16 / 9;
}
Задавайте змінні користувача
Ми часто використовуємо те саме значення кольору в декількох місцях на сторінці. Якщо потрібно змінити колір, доводиться змінювати кожен рядок коду, який посилається на нього.
p {
color: rgb(255, 0, 0);
}
h1 {
color: rgb(255, 0, 0);
}
h2 {
color: rgb(255, 0, 0);
}
Але набагато простіше визначити докорінно глобальну змінну, на яку можна буде посилатися скрізь, де це необхідно. Якщо доведеться змінити цей колір, потрібно буде поміняти лише один рядок.
:root {
--main-color: hsl(0, 100%, 50%);
}
p {
--main-color: green; /* переопределение, как в обычном css */
color: var(--main-color);
}
h1 {
color: var(--main-color);
}
h2 {
color: var(--main-color);
}
Змінні каскадуються, як і решта CSS. Це означає, що будь-яку глобальну змінну можна поставити докорінно, але перевизначити глибше в дереві. Також змінні можна комбінувати для складання складніших значень. Наприклад, колір RGB можна визначити на основі трьох інших змінних.
Раніше використання змінних було прерогативою препроцесорів CSS, дуже приємно, що такий корисний інструмент перекочував і в сам CSS. До речі, нещодавно в CSS з’явився і nestling (раніше він також існував лише в препроцесорах).
Використовуйте просту математику
CSS насправді не є мовою програмування у традиційному сенсі. Але в ньому є можливість виконувати базові обчислення за допомогою функції calc
, де доступні деякі базові математичні обчислення. Але найцікавіше, що у цій функції можна комбінувати різні одиниці виміру. Наприклад, можна відняти 50 пікселів із поточної ширини області перегляду.
Допустимо, в коді є анімація, в якій елементи падають зверху. Але ми хочемо їх упорядкувати, щоб вони з’являлися один за одним. Один із способів досягти цього — застосувати різну затримку анімації для кожного окремого елемента.
.first {
animation-delay: 100ms;
}
.second {
animation-delay: 200ms;
}
.third {
animation-delay: 300ms;
}
/*
<i class="drop first"></i>
<i class="drop second"></i>
<i class="drop third"></i>
*/
Але за великої кількості елементів таке важко організувати в коді. Складніший підхід — визначити вбудовану змінну CSS для порядкового номера кожного елемента. З її допомогою можна визначити затримку анімації як порядковий номер, помножений на 100 мілісекунд. Так можна обробляти безліч елементів, не збільшуючи обсяг CSS.
.drop {
animation-delay: calc(100ms * var(--order));
}
/*
<i class="drop" style="--order: 1"></i>
<i class="drop" style="--order: 2"></i>
<i class="drop" style="--order: 3"></i>
*/
Керуйте станом
Вище ми згадували, що CSS не є повноцінною мовою програмування, але насправді в неї вбудований механізм керування станом. Ви можете відстежувати поточну позицію у своєму CSS-коді, не написавши жодного рядка JavaScript.
Відмінний приклад – нумерація заголовків. Простий спосіб зробити це — вручну додати ці цифри до самого HTML. Але якщо ви будь-коли захочете додати новий заголовок, доведеться перенумерувати все вручну.
Більш розумний підхід – лічильник CSS. Він створюється через властивість скидання лічильника ( counter-reset
). Йому можна дати будь-яке ім’я, а потім збільшувати щоразу, коли застосовується потрібний селектор за допомогою counter-increment
. Такий лічильник почнеться з 0 і щоразу збільшуватиметься на одиницю.
:root {
counter-reset: headings;
}
h1 {
counter-increment: headings;
}
h1::before {
content: counter(headings);
}
Не нехтуйте focus-within
Можна подумати, що при створенні складного меню, що розкривається, для керування відкритим і закритим станом повинен використовуватися деякий JavaScript. Але тільки з CSS також можна просунутися досить далеко.
Швидше за все, ви знайомі з псевдокласом focus
, який застосовується до елемента, коли ви вводите форму або натискаєте кнопку. Його можна використовувати, щоб відкрити меню. Але при натисканні на щось всередині меню втрачає фокус і закривається. Саме в цей момент зазвичай використовується JavaScript для керування станом.
Однак усередині є ще один псевдоклас focus-within
. Він залишається активним, якщо в результаті взаємодії з елементами на сторінці фокус зберігся у будь-яких дочірніх елементів меню. Так що в цьому випадку можна взагалі не використовувати JavaScript для включення та вимкнення станів.
Використовуйте відносні одиниці
Коли можливо, замість статичних значень пікселів використовуйте відносні одиниці.
При розробці адаптивних макетів у вас можуть бути заголовки або контейнери з фіксованими полями та відступами. Ви змінюєте їх на екранах меншого розміру за допомогою @media.
Якщо у точному дотриманні розмірів цих полів немає потреби, це overhead. Підтримувати таке складно, плюс це не дуже добре працює на пристроях, що автоматично масштабують шрифти під розмір екрану.
Замість фіксованих одиниць, на зразок пікселів, можна використовувати відносні – em
або rem
:
em
– автоматично розрахує розмір, спираючись на шрифт елемента (а оскільки розмір шрифту успадковується та рідко перевизначається, за фактом em спиратиметься на шрифт найближчого батьківського елемента). Якщо розмір шрифту 16px, то 2em дорівнюватиме 32px;rem
– Працює аналогічно, але спирається на розмір кореневого шрифту. Цей спосіб кращий
Єдине зауваження – em та rem реагують на зміну розміру шрифту браузера. Отже, якщо хтось у своєму браузері збільшить розмір тексту, зовнішній вигляд сторінки може змінитися.
У CSS є інші відносні одиниці, зокрема ch – ширина символу. За допомогою неї можна продати один трюк.
Якщо ви справді серйозно ставитеся до дизайну, можливо, ви читали «Основи стилю в друкарні» Роберта Брінгхерста. Він каже, що оптимальний розмір абзацу становить від 45 до 75 символів. У CSS ми можемо забезпечити зразкове дотримання цього правила за допомогою функції clamp, яку згадували вище ( width: clamp(45ch, 50%, 75ch);
). Так кожен абзац матиме ідеальну ширину.
Відверто кажучи 45ch
– це не рівно 45 символів, а відстань, на якій можна розмістити 45 нулів. Якщо використовується моноширинний шрифт, все працює ідеально. Якщо ні, краще перевірити результат практично.
Орієнтуйтеся на кольори HSL (замість RGB)
RGB – це червоний, зелений, синій, де кожен елемент може мати значення від 0 до 255. Шістнадцятковий формат містить ту ж інформацію, але в компактнішому вигляді, який ще складніше інтерпретувати.
HSL – це відтінок, насиченість та яскравість. І такий формат спрощує обчислення гарної палітри кольорів. Просто виберіть насиченість і яскравість, а потім збільште відтінок на певну величину, наприклад, на 25. Так можна отримати кілька кольорів, поєднання яких виглядає добре. Ще простіше за допомогою HSL затемнити або висвітлити елемент інтерфейсу. На відміну від RGB, для цього не доведеться ставити окремий колір.
Не забувайте виправити прокручування
На веб-сайтах з фіксованою навігаційною панеллю часто зустрічається одна проблема. Коли ви переходите до якірного посилання, сторінка автоматично прокручується, але потрібний заголовок закривається фіксованою навігаційною панеллю.
Щоб цього уникнути, виправте властивість scroll-padding
, яка додасть додатковий відступ.
PS Рука не піднялася оголосити це окремою порадою, але в оригінальній добірці автори рекомендували використовувати емодзі як імена класів. З їхньої точки зору так код стане більш читаним.
.💪 {
display: flex;
}
.💄 {
color: red;
}
.🌈 {
background: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet);
}
/*
<div class="💪">
<p class="💄">Hello</p>
<p class="🌈">World</p>
</div>
*/