Современный CSS: переменные, вложенность, :has() и слои
Зачем:Сегодняшний CSS умеет то, ради чего раньше брали препроцессоры и скрипты. После урока вы собираете значения в переменные, пишете вложенные правила, подсвечиваете блок по его содержимому через :has() и упорядочиваете стили слоями.
30 минут
Озвучено синтезом речи (Qwen3-TTS), голос не принадлежит человеку.
Современный CSS — переменные, вложенные правила, селектор :has() и каскадные слои @layer: то, ради чего раньше подключали препроцессоры и скрипты.
Новая эпоха: CSS 2025
До сих пор вы писали CSS так, как его писали в 2005 и 2014 годах. Но язык не стоит на месте, и за последние несколько лет браузеры научили его тому, ради чего раньше подключали препроцессоры (Sass, Less) и скрипты. Этот урок про то, чем пишут сегодня. Он относится к нашей эпохе 2025 года: страница та же, но стили переписаны.
Нас интересуют четыре новинки.
Переменные
Пользовательские свойства, они же «переменные CSS», объявляются с двумя дефисами и читаются через var():
:root {
--color-text: #1d2430;
--color-accent: #1a4d8f;
--space: 1rem;
}
.button {
color: white;
background-color: var(--color-accent);
padding: var(--space);
} :root это сам элемент html: переменные, объявленные там, видны во всей странице. Преимущества:
- значение в одном месте. Хотите другой акцентный цвет? Поменяйте одну строку, а не тридцать;
- они живые. В отличие от переменных препроцессора, переменные CSS остаются в браузере: их можно переопределить у любого блока (карточка в тёмном блоке получит другие цвета) и менять из JavaScript;
- они наследуются вниз по дереву, как цвет шрифта.
Хорошее правило: назвать переменные по роли, а не по значению: --color-accent, а не --blue. Тогда через год акцент может стать зелёным, а имя останется верным. Такие именованные значения называют дизайн-токенами, и их разбор впереди, в части про дизайн-системы.
Тёмная тема с переменными делается почти бесплатно: все цвета через переменные, а в @media (prefers-color-scheme: dark) меняются только их значения. Правила карточек и кнопок вообще не нужно трогать. Дополнительно в head пишут <meta name="color-scheme" content="light dark">: он подсказывает браузеру, что у сайта две темы, чтобы он не мигал белым при загрузке.
Подвигайте ползунки на схеме: одна карточка, одни и те же правила, а вид меняется из-за значений. Внизу ещё про слои.
:root {
--color-bg: hsl(210 20% 99%);
--color-surface: hsl(210 25% 94%);
--color-text: hsl(210 30% 14%);
--color-accent: hsl(210 65% 32%);
--color-on-accent: hsl(210 20% 99%);
--radius: 8px;
--space: 1rem;
} Чтобы сделать тему тёмной, мы не переписали ни одно правило карточки, а только поменяли значения переменных в одном месте.
| Селектор | Специфичность | color | Слой |
|---|---|---|---|
| #cta | (1, 0, 0) | красный | |
| .button | (0, 1, 0) | синий | |
| a | (0, 0, 1) | зелёный |
Ссылка будет цвета зелёный: победило правило a, потому что оно вне слоёв, а такое правило сильнее любого слоя. Порядок слоёв объявлен один раз: @layer base, components, utilities;
Проверьте себя: почему переменную лучше назвать --color-accent, а не --blue?
Потому что роль устойчивее значения: акцент завтра может стать зелёным, а имя останется верным.
Вложенность
Раньше, чтобы написать правила для карточки и её внутренностей, приходилось повторять селектор: .card { … }, .card h3 { … }, .card:hover { … }. Теперь правила вкладывают друг в друга:
.card {
padding: var(--space);
& h3 {
margin-top: 0;
}
&:hover {
background-color: var(--color-surface);
}
} Знак & означает «родительский селектор». Вложенность делает файл короче и группирует всё, что относится к карточке, в одном месте. Не вкладывайте глубже двух-трёх уровней: сложные цепочки бьют по читаемости и по специфичности.
:has(): родитель по ребёнку
Селектор :has() выбирает элемент по тому, что в нём лежит. Родительского селектора в CSS не было совсем, и этот приём казался невозможным:
section:has(.cards) { padding-block: 1.5rem; } /* раздел, где есть карточки */
form:has(input:invalid) button { opacity: 0.7; } /* кнопка в форме, где есть неверное поле */
label:has(input:checked) { font-weight: 600; } /* подпись отмеченной галочки */ Во всех браузерах :has() работает с конца 2023 года. Это одна из самых полезных новинок: многое, что раньше делали скриптом, теперь делает CSS.
Проверьте себя: что выберет form:has(input:invalid)?
Форму, внутри которой есть хотя бы одно неверное поле. Через неё можно оформить, например, кнопку.
Слои
Чем больше CSS, тем чаще правила дерутся: вспомните каскад из первого урока. Борьба кончалась !important и всё более длинными селекторами. Слои (@layer) дают порядок наперёд:
@layer reset, base, components, utilities; Одна строка в начале файла объявляет слои от слабого к сильному. Дальше каждое правило кладётся в свой слой:
@layer base {
a { color: var(--color-accent); }
}
@layer components {
.button { color: var(--color-on-accent); }
} Правило простое. Слой выше побеждает слой ниже, независимо от специфичности. Специфичность решает только внутри одного слоя. А правила вне слоёв сильнее любого слоя: поэтому неслоёные стили держат для исключений, а основное кладут в слои. В схеме выше внизу это можно проверить руками.
Эта схема спасает от «войны специфичности»: селектор #cta в слое base не сможет перебить простой .button из слоя utilities. Слои пришли в браузеры в 2022 году.
Проверьте себя: что сильнее: правило вне слоёв или правило из последнего слоя?
Правило вне слоёв. Оно сильнее любого слоя, даже самого позднего.
Практика
Вы начинаете эпоху 2025 года. Страницу и стили мы вам дали: это тот результат, что вы закончили в прошлом уроке (если вы свои стили сохранили, можете вставить их). Вам нужно превратить стили в современные: переменные на :root, тёмная тема, слои, вложенность и :has().
Проверки здесь видят переменные, тег color-scheme и правило с :has(). Слои, вложенность и тёмную тему они увидеть не могут: браузер выдаёт им только вычисленные значения. Эти три вещи проверяйте глазами: откройте инструменты разработчика, найдите в «Стилях» свои слои, а тёмную тему включите в панели «Рендеринг» (Rendering, «Эмуляция prefers-color-scheme»).
Что было не так с 2014 годом
Тогда большой CSS без Sass или Less писать было почти невозможно: нужны были переменные, вложенность, миксины. Препроцессор превращал ваш файл в обычный CSS на сервере, а браузер видел уже готовое. Сегодня половина этого умеет сам браузер. А вторая половина проблемы: специфичность и !important, пока их не заменили слоями.
Практика
Готовлю вашу страницу…
Тест
Вопрос 1 из 6
Карточки
Эти карточки попадут в повторение, когда вы завершите урок.
Как объявить и использовать переменную CSS?
Ответ
Объявить: --color-accent: #1a4d8f; (обычно на :root). Использовать: color: var(--color-accent). Живут в браузере и наследуются.
Как сделать тёмную тему?
Ответ
Все цвета через переменные, а в @media (prefers-color-scheme: dark) :root меняет только их значения. Плюс <meta name="color-scheme" content="light dark">.
Что такое вложенность в CSS?
Ответ
Правило внутри правила: .card { & h3 { … } &:hover { … } }. Знак & означает родительский селектор. Раньше для этого нужен был препроцессор.
Что делает :has()?
Ответ
Выбирает родителя по содержимому: section:has(.cards), form:has(:invalid). Родительского селектора раньше в CSS не было.
Что такое @layer и как слои спорят?
Ответ
@layer reset, base, components, utilities; порядок слоёв объявляют один раз. Поздний слой сильнее раннего независимо от специфичности, а правила вне слоёв сильнее всех.
Источники
- Using CSS custom properties (variables) (MDN) CC BY-SA, по-английски
- :has() CSS pseudo-class (MDN) CC BY-SA, по-английски
- @layer (MDN) CC BY-SA, по-английски
- CSS nesting (MDN) CC BY-SA, по-английски
- Пользовательские свойства — Дока только ссылка, по-русски
Дальше
Теперь вы знаете: пользовательские свойства (переменные), вложенность в CSS, селектор :has(), слои каскада @layer.