Пошук уроків, статей та іншого контенту
Застосуємо правила читабельності, специфічності, повторного використання та безпечного каскаду в CSS.
CSS-проєкт швидко ускладнюється: стилі накопичуються, компоненти повторюються, а одна зміна може несподівано вплинути на інші елементи.
Якісний CSS має бути:
читабельним;
передбачуваним;
достатньо специфічним, але не надмірно;
придатним до повторного використання;
безпечним щодо порядку підключення та каскаду.
Мета не в тому, щоб написати якомога менше CSS. Мета — зробити правила зрозумілими та простими для зміни.
Усередині правила зручно дотримуватися приблизного порядку:
позиціонування;
розміри;
відступи;
типографіка;
кольори та фон;
рамки;
інші ефекти.
.card {
/* Позиціонування */
position: relative;
/* Розміри */
width: 100%;
max-width: 24rem;
/* Відступи */
padding: 1.5rem;
/* Типографіка */
font-size: 1rem;
line-height: 1.5;
/* Кольори та фон */
color: #1f2937;
background-color: #ffffff;
/* Рамка та ефекти */
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
box-shadow: 0 0.25rem 1rem rgb(15 23 42 / 10%);
}Такий порядок не є вимогою CSS, але він полегшує пошук потрібної властивості.
Краще записати кожну властивість з нового рядка:
.button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}Замість важкого для читання запису:
.button { display: inline-flex; align-items: center; justify-content: center; gap: .5rem; }Не залишайте закоментовані старі правила в робочому коді. Для історії змін використовуйте систему контролю версій, а не великі блоки старого CSS.
Назва класу має описувати роль елемента, а не його зовнішній вигляд.
Добрі приклади:
<article class="product-card">
<h2 class="product-card__title">Навушники</h2>
<p class="product-card__description">Бездротова модель.</p>
</article>Проблемні назви:
<div class="red-box">
<p class="big-text">Текст</p>
</div>Клас red-box пов’язаний із конкретним кольором. Якщо дизайн зміниться, назва стане неправдивою. Клас product-card описує призначення компонента й залишиться актуальним навіть після зміни кольорів.
Для компонентів можна використовувати підхід, подібний до BEM:
product-card — компонент;
product-card__title — частина компонента;
product-card--featured — варіант компонента.
<article class="product-card product-card--featured">
<h2 class="product-card__title">Навушники</h2>
</article>.product-card {
padding: 1.5rem;
background-color: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
}
.product-card__title {
margin: 0;
color: #1f2937;
font-size: 1.25rem;
}
.product-card--featured {
border-color: #2563eb;
}Модифікатор не повинен дублювати весь стиль компонента. Він містить лише відмінності.
Якщо одне значення використовується в кількох місцях, винесіть його в CSS-змінну:
:root {
--color-primary: #2563eb;
--color-primary-dark: #1d4ed8;
--color-text: #1f2937;
--color-surface: #ffffff;
--radius-md: 0.5rem;
--space-md: 1rem;
}Тепер зміна основного кольору виконується в одному місці:
.button {
padding: var(--space-md);
color: #ffffff;
background-color: var(--color-primary);
border: 0;
border-radius: var(--radius-md);
}
.button:hover {
background-color: var(--color-primary-dark);
}CSS-змінні особливо корисні для:
кольорів;
розмірів шрифтів;
відступів;
радіусів;
тіней;
значень, які мають бути узгодженими в усьому інтерфейсі.
Утилітарний клас відповідає за одну невелику властивість або групу тісно пов’язаних властивостей:
.text-center {
text-align: center;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}Утиліти зручні, коли вони мають чітке призначення. Не варто створювати класи на кшталт .blue-large-rounded-padded, які описують набір випадкових властивостей і прив’язують HTML до деталей дизайну.
Специфічність визначає, яке правило переможе, якщо кілька правил стосуються одного елемента.
У спрощеному вигляді пріоритет селекторів такий:
стилі елемента через атрибут style;
селектори з ідентифікаторами;
класи, атрибути та псевдокласи;
селектори елементів і псевдоелементи.
Наприклад:
p {
color: black;
}
.article p {
color: navy;
}
.article .article__text {
color: darkgreen;
}Для такого HTML:
<article class="article">
<p class="article__text">Текст статті</p>
</article>переможе останнє правило, оскільки селектор .article .article__text має більшу специфічність, ніж .article p.
Прості класи легше перевизначати:
.button {
color: #ffffff;
}
.button--secondary {
color: #1f2937;
}Натомість такий селектор створює зайву залежність від структури HTML:
.page main section article .button {
color: #ffffff;
}Якщо змінити структуру документа, стиль може перестати працювати. Компонентний клас .button не залежить від того, де саме він розташований.
Проблемний варіант:
.header .navigation ul li a {
color: #1f2937;
}Кращий варіант:
.navigation__link {
color: #1f2937;
}Глибока вкладеність:
збільшує специфічність;
прив’язує стилі до HTML-структури;
ускладнює перевизначення;
робить код менш повторно використовуваним.
Каскад CSS враховує:
походження та важливість правила;
специфічність;
порядок появи правила.
Якщо правила мають однакову специфічність, переможе те, яке розташоване нижче.
.button {
background-color: gray;
}
.button {
background-color: royalblue;
}Фон буде royalblue, оскільки друге правило з’являється пізніше.
Зазвичай зручно організувати стилі в такому порядку:
базові налаштування;
змінні;
загальні стилі;
компоненти;
модифікатори;
утиліти.
Наприклад:
:root {
--color-text: #1f2937;
}
body {
color: var(--color-text);
}
.card {
padding: 1rem;
}
.card--compact {
padding: 0.5rem;
}
.text-center {
text-align: center;
}Модифікатор має підключатися після базового компонента, щоб його відмінності могли застосуватися.
@layer дає змогу явно визначити порядок груп CSS-правил. Стилі в пізнішому шарі мають перевагу над стилями в попередньому шарі, якщо вони конкурують у каскаді.
@layer reset, base, components, utilities;
@layer base {
body {
margin: 0;
color: #1f2937;
background-color: #f8fafc;
}
}
@layer components {
.button {
padding: 0.75rem 1rem;
color: #ffffff;
background-color: #2563eb;
border: 0;
border-radius: 0.5rem;
}
}
@layer utilities {
.button--muted {
background-color: #64748b;
}
}Шари допомагають уникати ситуації, коли для простого перевизначення доводиться штучно збільшувати специфічність селектора.
!important!important змінює звичайний порядок каскаду та ускладнює подальше перевизначення стилю.
Проблемний приклад:
.button {
color: white !important;
}
.button--secondary {
color: #1f2937 !important;
}Якщо таких правил стане багато, з’явиться ланцюг нових !important, які намагаються перебити попередні.
Спочатку перевірте:
чи правильно підключено файл CSS;
чи не надто висока специфічність іншого селектора;
чи правильний порядок правил;
чи не потрібно використати модифікатор компонента.
!important може бути виправданим у рідкісних ситуаціях, але не має бути звичайним способом вирішення конфліктів.
Нижче наведено завершений приклад із:
CSS-змінними;
компонентними класами;
модифікатором;
низькою специфічністю;
утилітарним класом;
явними каскадними шарами.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Картки продуктів</title>
<style>
@layer base, components, utilities;
@layer base {
:root {
--color-text: #1f2937;
--color-muted: #64748b;
--color-surface: #ffffff;
--color-border: #e2e8f0;
--color-primary: #2563eb;
--color-primary-dark: #1d4ed8;
--color-featured: #fef3c7;
--radius-md: 0.75rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
}
* {
box-sizing: border-box;
}
body {
min-width: 20rem;
margin: 0;
color: var(--color-text);
background-color: #f8fafc;
font-family: Arial, sans-serif;
line-height: 1.5;
}
button {
font: inherit;
}
}
@layer components {
.product-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
gap: var(--space-lg);
max-width: 60rem;
margin: 0 auto;
padding: var(--space-lg);
}
.product-card {
display: flex;
flex-direction: column;
gap: var(--space-md);
padding: var(--space-lg);
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
}
.product-card__title {
margin: 0;
font-size: 1.25rem;
}
.product-card__description {
margin: 0;
color: var(--color-muted);
}
.product-card__price {
margin: 0;
font-size: 1.125rem;
font-weight: 700;
}
.product-card--featured {
background-color: var(--color-featured);
border-color: #f59e0b;
}
.button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--space-sm) var(--space-md);
color: #ffffff;
background-color: var(--color-primary);
border: 0;
border-radius: 0.5rem;
cursor: pointer;
}
.button:hover {
background-color: var(--color-primary-dark);
}
.button:focus-visible {
outline: 0.1875rem solid rgb(37 99 235 / 35%);
outline-offset: 0.125rem;
}
.button--secondary {
color: var(--color-text);
background-color: #e2e8f0;
}
}
@layer utilities {
.text-center {
text-align: center;
}
}
</style>
</head>
<body>
<main class="product-list">
<article class="product-card product-card--featured">
<h1 class="product-card__title">Бездротові навушники</h1>
<p class="product-card__description">
Легкі навушники для щоденного використання.
</p>
<p class="product-card__price">2 499 грн</p>
<button class="button" type="button">Додати до кошика</button>
</article>
<article class="product-card">
<h2 class="product-card__title">Механічна клавіатура</h2>
<p class="product-card__description">
Компактна клавіатура для роботи та навчання.
</p>
<p class="product-card__price">3 199 грн</p>
<button class="button button--secondary" type="button">
Докладніше
</button>
</article>
</main>
</body>
</html>У цьому прикладі стилі кнопки не залежать від її розташування. Один і той самий компонент можна використати в картці, заголовку чи формі, а його варіант змінюється через модифікатор.
Перед додаванням нового правила перевірте:
Чи справді потрібен новий клас?
Чи не можна використати наявний компонент або модифікатор?
Чи описує назва класу призначення, а не поточний вигляд?
Чи не дублюється значення, яке варто винести в CSS-змінну?
Чи достатньо селектора з одним класом?
У якому шарі або розділі має бути це правило?
Чи не створює правило залежність від конкретної HTML-структури?
Чи можна обійтися без !important?
Такий список допомагає підтримувати узгодженість коду під час його розвитку.
.page .content .product-card h2.product-card__title {
color: red;
}Краще:
.product-card__title {
color: red;
}#main-button {
background-color: blue;
}Ідентифікатор має високу специфічність і погано підходить для повторно використовуваних компонентів. Для стилів використовуйте класи:
.button {
background-color: blue;
}article h2 {
margin-bottom: 1rem;
}Таке правило вплине на всі відповідні елементи в усіх article. Якщо стиль належить конкретному компоненту, краще назвати його явно:
.product-card__title {
margin-bottom: 1rem;
}Проблемний варіант:
.primary-button {
padding: 0.75rem 1rem;
border-radius: 0.5rem;
color: white;
background-color: blue;
}
.success-button {
padding: 0.75rem 1rem;
border-radius: 0.5rem;
color: white;
background-color: green;
}Спільні властивості краще винести в базовий компонент:
.button {
padding: 0.75rem 1rem;
border: 0;
border-radius: 0.5rem;
color: white;
}
.button--primary {
background-color: blue;
}
.button--success {
background-color: green;
}!important як першої реакціїЯкщо правило не працює, не додавайте !important автоматично. Спочатку знайдіть причину конфлікту в специфічності, порядку правил або структурі класів.
Пишіть CSS у послідовному та читабельному форматі.
Називайте класи за призначенням компонента, а не за кольором чи розміром.
Використовуйте компоненти та модифікатори замість дублювання стилів.
Виносьте повторювані значення в CSS-змінні.
Прагніть до селекторів із низькою специфічністю.
Не прив’язуйте стилі до зайво глибокої HTML-структури.
Плануйте порядок правил і, за потреби, використовуйте @layer.
Уникайте !important, якщо проблему можна вирішити звичайним каскадом.
Перед додаванням CSS перевіряйте, чи не можна повторно використати вже наявний стиль.