Какие современные возможности CSS стоит знать?

MiddleCSS #css #modern

Вопрос

Какие современные возможности CSS появились в последние годы и решают задачи, для которых раньше требовался препроцессор или JS?

Короткий ответ

К ключевым современным возможностям относятся CSS custom properties (переменные), container queries (адаптация по размеру родителя, а не viewport), псевдокласс :has() (родительский/условный селектор) и функции clamp()/min()/max() для адаптивных вычислений без медиазапросов.

Подробный ответ

CSS custom properties (--var) — в отличие от переменных препроцессоров (Sass), это реальные значения в runtime: их можно менять через JS (element.style.setProperty), они наследуются по DOM-дереву как обычные CSS-свойства и могут быть переопределены на любом уровне каскада, включая медиазапросы и :root vs локальный скоуп. Читаются через var(--name, fallback).

Container queries (@container) — позволяют применять стили в зависимости от размера родительского контейнера, а не всего viewport, как это делает @media. Это критично для по-настоящему переиспользуемых компонентов: одна и та же карточка должна выглядеть по-разному в узком сайдбаре и в широкой основной колонке, независимо от ширины экрана. Требует объявить container-type: inline-size (или size) на родителе.

:has() — псевдокласс, позволяющий выбрать элемент на основе его потомков или соседей, то есть фактически родительский селектор, которого CSS не хватало десятилетиями. Пример: подсветить form, у которой есть невалидное поле, или стилизовать .card, содержащую изображение, иначе, чем без него.

clamp()/min()/max() — математические функции для адаптивных значений без медиазапросов:

  • clamp(min, preferred, max) — значение ограничено сверху и снизу, а между границами плавно масштабируется по preferred (часто с vw).
  • min() — выбирает наименьшее из значений, удобно для "не шире чем X, но не больше 100%".
  • max() — выбирает наибольшее, удобно для нижней границы.

Пример

:root {
  --gap: 16px;
  --accent: #3b82f6;
}

.card {
  padding: var(--gap);
  border: 1px solid var(--accent);
}

/* container query: карточка адаптируется под ширину родителя, а не экрана */
.card-container {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 120px 1fr;
  }
}

/* :has() — форма с невалидным полем получает красную рамку */
form:has(input:invalid) {
  border-color: crimson;
}

/* карточка без изображения получает другой паддинг */
.card:not(:has(img)) {
  padding: 24px;
}

/* адаптивная типографика без медиазапросов */
.title {
  font-size: clamp(1.25rem, 2vw + 1rem, 3rem);
  width: min(90%, 640px);
}

Дополнительные вопросы

  • Чем container queries принципиально отличаются от медиазапросов с точки зрения переиспользуемости компонентов?
  • Как CSS custom properties взаимодействуют с наследованием и каскадом по сравнению с Sass-переменными?
  • Какие ограничения производительности есть у :has() при глубоко вложенных селекторах?