Современный 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;
}
Карточка услуги

Цвет, отступ и скругление берутся из переменных через var().

Записаться

Чтобы сделать тему тёмной, мы не переписали ни одно правило карточки, а только поменяли значения переменных в одном месте.

Слои: что сильнее
СелекторСпецифичность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

Что делает запись color: var(--color-accent)?

Один ответ

Карточки

Эти карточки попадут в повторение, когда вы завершите урок.

  • Как объявить и использовать переменную 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; порядок слоёв объявляют один раз. Поздний слой сильнее раннего независимо от специфичности, а правила вне слоёв сильнее всех.

Источники

Дальше

Теперь вы знаете: пользовательские свойства (переменные), вложенность в CSS, селектор :has(), слои каскада @layer.