Пошук уроків, статей та іншого контенту
Розберемо поширені помилки у CSS: надмірну специфічність, дублювання, неочікуваний каскад і проблеми адаптивності.
CSS не застосовує стилі просто в порядку їхнього написання. Для кожної властивості браузер визначає, яке правило має пріоритет. На результат впливають:
джерело правила;
!important;
специфічність селектора;
порядок появи правил у коді.
Через це властивість може не змінюватися навіть тоді, коли нове правило написане нижче або здається «точнішим».
Наприклад:
.button {
color: blue;
}
.card .button {
color: red;
}Для елемента всередині .card буде застосовано червоний колір, оскільки .card .button має більшу специфічність, ніж .button.
Надмірна специфічність виникає, коли селектори стають надто довгими або містять багато вкладених елементів:
.page .content .card ul li a.button {
color: white;
}Такий селектор важко перевизначити. Щоб змінити його стиль, розробнику доведеться створювати ще специфічніше правило:
.page .content .card ul li a.button.primary {
color: yellow;
}З часом це призводить до «гонки специфічності», коли кожне наступне виправлення потребує ще складнішого селектора.
Використовуйте короткі селектори, які описують роль елемента, а не всю його структуру:
.button {
color: white;
}
.button--primary {
color: yellow;
}HTML:
<a class="button button--primary" href="#">Перейти</a>Клас .button--primary зрозумілий сам по собі й не залежить від того, де саме розташована кнопка.
Не прив’язуйте стилі до зайвої кількості батьківських елементів.
Не використовуйте селектори на кшталт header nav ul li a, якщо достатньо класу.
Не стилізуйте компоненти через випадкову структуру сторінки.
Намагайтеся підтримувати приблизно однаковий рівень специфічності для компонентів.
Для перевизначень додавайте модифікатор класу, а не ще один рівень вкладеності.
:where() :where() має нульову специфічність. Це корисно для базових стилів, які легко перевизначити:
:where(.card) {
padding: 1rem;
}
.card--compact {
padding: 0.5rem;
}Правило .card--compact переможе базове правило, тому що має більшу специфічність.
Дублювання з’являється, коли однакові властивості повторюються в багатьох правилах:
.button {
padding: 0.75rem 1rem;
border-radius: 0.5rem;
font-weight: 700;
}
.button--primary {
padding: 0.75rem 1rem;
border-radius: 0.5rem;
background-color: royalblue;
}
.button--secondary {
padding: 0.75rem 1rem;
border-radius: 0.5rem;
background-color: gray;
}Якщо зміниться внутрішній відступ або радіус, доведеться редагувати кілька місць. Це збільшує ризик помилок.
Спільні властивості потрібно залишити в базовому класі, а в модифікаторах описувати лише відмінності:
.button {
padding: 0.75rem 1rem;
border: 0;
border-radius: 0.5rem;
font: inherit;
font-weight: 700;
cursor: pointer;
}
.button--primary {
background-color: royalblue;
color: white;
}
.button--secondary {
background-color: gray;
color: white;
}HTML:
<button class="button button--primary">Зберегти</button>
<button class="button button--secondary">Скасувати</button>Елемент отримує спільний клас .button і додатковий клас-модифікатор.
Інша форма дублювання — однакові властивості в різних правилах:
.title {
margin-bottom: 1rem;
}
.card-title {
margin-bottom: 1rem;
}
.section-title {
margin-bottom: 1rem;
}Якщо елементи справді мають спільну поведінку, можна додати спільний клас:
.heading-with-spacing {
margin-bottom: 1rem;
}Однак не варто об’єднувати правила лише заради скорочення коду. Якщо елементи належать до різних компонентів і можуть змінюватися незалежно, окремі правила можуть бути зрозумілішими.
Цей приклад часто вводить в оману:
.card .title {
color: navy;
}
.title {
color: black;
}Хоча .title написаний нижче, текст залишиться navy, оскільки .card .title має більшу специфічність.
Щоб виправити код, краще зменшити специфічність першого правила:
.title {
color: navy;
}Або використати явний модифікатор:
.title {
color: navy;
}
.title--muted {
color: gray;
}Специфічність умовно можна порівнювати за трьома групами:
ідентифікатори: #header;
класи, атрибути та псевдокласи: .button, [disabled], :hover;
елементи та псевдоелементи: button, ::before.
Наприклад:
#profile .button { } /* один ідентифікатор, один клас */
.card .button { } /* два класи */
button { } /* один елемент */#profile .button має більшу специфічність, ніж .card .button, а .card .button — більшу, ніж button.
Порівнюється спочатку кількість ідентифікаторів, потім класів, потім елементів. Кількість елементів не може «перемогти» навіть один ідентифікатор.
!important!important примусово підвищує пріоритет правила:
.button {
color: red !important;
}Після цього звичайними правилами змінити колір буде складно. Часте використання !important приховує проблеми зі структурою CSS.
Не використовуйте його як стандартний спосіб виправлення каскаду. Спочатку перевірте:
чи правильний клас додано до HTML;
чи не має інше правило більшої специфічності;
чи не перекривається властивість медіазапитом;
чи не визначено стиль пізніше в іншому файлі;
чи справді властивість застосовується до потрібного елемента.
!important може бути виправданим у виняткових випадках, але такі випадки мають бути зрозумілими й обмеженими.
Скорочені властивості можуть непомітно скинути інші значення:
.card {
margin-top: 2rem;
}
.card {
margin: 1rem;
}Друге правило змінить не лише margin-top, а всі чотири відступи.
Так само:
.title {
font-size: 2rem;
font-weight: 700;
}
.title {
font: 1.5rem/1.4 sans-serif;
}Скорочена властивість font скидає значення інших властивостей шрифту, зокрема font-weight.
Використовуйте скорочені властивості свідомо. Якщо потрібно змінити лише одну сторону або одну характеристику, застосовуйте повну властивість:
.card {
margin-top: 1rem;
}Фіксована ширина може спричинити горизонтальне прокручування на вузьких екранах:
.container {
width: 1200px;
}Краще обмежити максимальну ширину, але дозволити елементу стискатися:
.container {
width: min(100% - 2rem, 1200px);
margin-inline: auto;
}Тут контейнер:
не виходить за межі батьківського елемента;
має бокові відступи 1rem на вузьких екранах;
не стає ширшим за 1200px;
автоматично центрується.
Зображення з власною шириною можуть зламати розмітку:
img {
width: 800px;
}Для адаптивного зображення зазвичай використовують:
img {
display: block;
max-width: 100%;
height: auto;
}max-width: 100% не дозволяє зображенню бути ширшим за контейнер, а height: auto зберігає пропорції.
Велика кількість точкових медіазапитів ускладнює підтримку:
.card {
width: 300px;
}
@media (max-width: 900px) {
.card {
width: 280px;
}
}
@media (max-width: 700px) {
.card {
width: 260px;
}
}
@media (max-width: 500px) {
.card {
width: 240px;
}
}Якщо компонент добре працює з гнучкими розмірами, частину медіазапитів можна замінити:
.card {
width: min(100%, 20rem);
}Медіазапити варто додавати тоді, коли змінюється структура або поведінка компонента, а не для кожного окремого значення.
Якщо медіазапити мають однакову специфічність, важливим є їхній порядок:
.card {
padding: 2rem;
}
@media (max-width: 700px) {
.card {
padding: 1rem;
}
}
@media (max-width: 500px) {
.card {
padding: 0.5rem;
}
}Для ширини 400px застосуються обидва медіазапити, але переможе правило з max-width: 500px, оскільки воно розташоване нижче.
Якщо використовувати підхід «від мобільного до широкого екрана», зручніше застосовувати min-width:
.card {
padding: 0.5rem;
}
@media (min-width: 501px) {
.card {
padding: 1rem;
}
}
@media (min-width: 701px) {
.card {
padding: 2rem;
}
}Важливо не змішувати різні підходи без потреби та підтримувати послідовний порядок правил.
Нижче наведено компонент картки. У ньому:
спільні стилі винесено в базові класи;
для варіантів використано модифікатори;
селектори мають невисоку специфічність;
контейнер і зображення адаптивні;
медіазапит змінює структуру сітки, а не виправляє десятки окремих значень.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Адаптивні картки</title>
<style>
/* Базові стилі сторінки */
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f3f4f6;
color: #1f2937;
}
/* Контейнер стискається на вузьких екранах */
.container {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
padding-block: 2rem;
}
/* Гнучка сітка без фіксованої ширини картки */
.card-list {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1rem;
margin: 0;
padding: 0;
list-style: none;
}
.card {
overflow: hidden;
border: 1px solid #d1d5db;
border-radius: 0.75rem;
background: #ffffff;
}
.card__image {
display: block;
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.card__body {
padding: 1rem;
}
.card__title {
margin: 0 0 0.5rem;
font-size: 1.25rem;
}
.card__text {
margin: 0 0 1rem;
line-height: 1.5;
}
.button {
display: inline-block;
padding: 0.625rem 0.875rem;
border: 0;
border-radius: 0.5rem;
font: inherit;
font-weight: 700;
text-decoration: none;
cursor: pointer;
}
.button--primary {
background: #2563eb;
color: #ffffff;
}
.button--primary:hover {
background: #1d4ed8;
}
/* На середніх екранах залишаємо дві колонки */
@media (max-width: 50rem) {
.card-list {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
/* На вузьких екранах картки розташовуються в одну колонку */
@media (max-width: 32rem) {
.card-list {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<main class="container">
<ul class="card-list">
<li class="card">
<img
class="card__image"
src="https://picsum.photos/640/360?random=1"
alt="Абстрактне зображення першої статті"
>
<div class="card__body">
<h2 class="card__title">Основи каскаду</h2>
<p class="card__text">
Як браузер обирає правило, яке потрібно застосувати.
</p>
<a class="button button--primary" href="#">
Читати
</a>
</div>
</li>
<li class="card">
<img
class="card__image"
src="https://picsum.photos/640/360?random=2"
alt="Абстрактне зображення другої статті"
>
<div class="card__body">
<h2 class="card__title">Адаптивна розмітка</h2>
<p class="card__text">
Як уникати фіксованих розмірів і горизонтального прокручування.
</p>
<a class="button button--primary" href="#">
Читати
</a>
</div>
</li>
<li class="card">
<img
class="card__image"
src="https://picsum.photos/640/360?random=3"
alt="Абстрактне зображення третьої статті"
>
<div class="card__body">
<h2 class="card__title">Підтримуваний CSS</h2>
<p class="card__text">
Як зменшити дублювання та зробити стилі передбачуваними.
</p>
<a class="button button--primary" href="#">
Читати
</a>
</div>
</li>
</ul>
</main>
</body>
</html>Коли стиль не працює, не варто одразу додавати !important. Перевірте елемент у DevTools браузера.
Порядок перевірки:
Переконайтеся, що потрібний клас справді є в HTML.
Перевірте, чи селектор відповідає потрібному елементу.
У панелі стилів знайдіть властивість, яка не застосувалася.
Подивіться на перекреслене правило.
Порівняйте специфічність активного та перекресленого селекторів.
Перевірте, чи не активний медіазапит із неочікуваною шириною viewport.
Перевірте вкладку обчислених стилів, щоб побачити фактичне значення властивості.
Особливо корисно змінити ширину viewport і перевірити компонент на кількох розмірах:
вузький мобільний екран;
широкий мобільний або планшет;
ноутбук;
широкий монітор.
!important для кожного виправлення.title {
color: black !important;
}Проблема не зникає, а переноситься в інше місце. Краще знайти правило з більшою специфічністю або змінити структуру класів.
main .content section article h2 {
margin-bottom: 1rem;
}Краще:
.card__title {
margin-bottom: 1rem;
}.home-page .card {
padding: 1rem;
}
.about-page .card {
padding: 1rem;
}Якщо картки однакові, спільний стиль має бути в .card. Сторінка повинна змінювати лише те, що справді відрізняється.
.content {
width: 1000px;
}Краще:
.content {
width: min(100% - 2rem, 1000px);
margin-inline: auto;
}h2 {
color: red;
}Глобальні правила для елементів можуть несподівано вплинути на всі компоненти сторінки. Якщо стиль потрібен конкретному компоненту, використовуйте його клас:
.card__title {
color: red;
}Якщо для кожного елемента додано окремий медіазапит, це часто означає, що базова структура недостатньо гнучка. Спочатку перевірте:
чи можна використати max-width: 100%;
чи підходить minmax() для сітки;
чи можна замінити фіксовану ширину на min();
чи справді потрібен новий breakpoint.
Надмірна специфічність ускладнює перевизначення стилів і створює залежність від структури HTML.
Спільні властивості слід виносити в базові класи, а відмінності — у модифікатори.
Правило, написане нижче, не завжди перемагає: важливішою може бути його специфічність.
!important не повинен бути стандартним способом виправлення каскаду.
Скорочені властивості можуть скинути значення інших властивостей.
Для адаптивності використовуйте гнучкі ширини, max-width, min(), minmax() і зміни структури через медіазапити.
DevTools допомагає побачити, яке саме правило перемогло та чому.