CSS — селекторы и каскад
CSS = «придай элементам внешний вид». Но прежде чем стилизовать, нужно НАЙТИ элементы — это селекторы. И когда два правила противоречат — нужно понимать, что побеждает (каскад). Без этого CSS — сплошное «почему оно не применяется».
Виды селекторов
p — все параграфы (по тегу). .btn — все с классом btn. #header — элемент с id="header" (один на страницу). [type="email"] — атрибут. Комбинации: .btn.primary (оба класса), .card .title (.title ВНУТРИ .card), .card > .title (только прямой ребёнок). Псевдо-классы: :hover (наведение), :focus, :first-child, :not(.disabled). ИИ часто использует [data-mode="active"] — селектор по data-атрибуту.
Специфичность — кто побеждает
Если два правила задают color для одного элемента — побеждает то, у кого выше специфичность. Считается так: id = 100, class/атрибут/псевдокласс = 10, тег = 1. #x (100) > .btn.primary (20) > .btn (10) > p (1). При равной специфичности побеждает то, что НИЖЕ в файле. Поэтому новички часто пишут !important — а потом тонут в конфликтах.
Каскад и наследование
Каскад — последовательность откуда применяются стили: 1) браузерные дефолты 2) внешние CSS 3) <style> в head 4) inline style="...". Inline побеждает всё, кроме !important. Наследование: некоторые свойства передаются от родителя детям (color, font-family) — задаёшь на body и дальше всё подхватывается. Не наследуются: padding, margin, border — это размеры, у каждого элемента свои.
!important — крайняя мера
color: red !important — выигрывает у всего с обычной специфичностью. Кажется удобно, но: 1) когда в проекте 10 !important, новый !important уже не помогает. 2) Чем больше !important, тем сложнее переопределить — особенно сторонним библиотекам или твоей же тёмной теме. Правило: НЕ используй !important. Если приходится — вероятно, специфичность твоего селектора слишком низкая, повысь её.
/* === Базовые селекторы === */
p { color: #333 } /* все параграфы */
.btn { padding: 10px 20px } /* класс */
#header { background: black } /* конкретный id */
[type="email"] { border: 2px solid blue } /* атрибут */
/* === Комбинации === */
.btn.primary { background: red } /* оба класса на ОДНОМ элементе */
.card .title { font-size: 24px } /* .title ВНУТРИ .card на любой глубине */
.card > .title { font-size: 24px } /* только прямой ребёнок */
.row + .row { margin-top: 16px } /* следующий брат */
.list .item:not(.done) { opacity: 1 } /* item который НЕ done */
/* === Псевдо-классы === */
.btn:hover { background: #f00 }
.input:focus { border-color: red }
.list-item:first-child { padding-top: 0 }
.list-item:nth-child(odd) { background: #f5f5f5 }
/* === Каскад в действии === */
/* Допустим есть HTML: <p class="warning">Текст</p> */
p { color: black } /* специфичность 1 */
.warning { color: orange } /* специфичность 10 → ПОБЕЖДАЕТ */
/* Если оба .warning, побеждает тот что ниже: */
.warning { color: orange }
.warning { color: red } /* ← победит он */
/* === Антипаттерн !important === */
.warning { color: red !important } /* перебивает всё */
/* Теперь чтобы перекрыть — нужен ещё !important или inline style */
/* Это путь в ад. Лучше не делай. */
/* === Лучше — повысить специфичность === */
.card .warning { color: red } /* специфичность 20 — победит над .warning */Селекторы и каскад в реальной CSS