Открой любой старый фронтенд-репозиторий и загляни в таблицы стилей. С вероятностью 99% ты увидишь там классическую «россыпь магических чисел»: десятки разбросанных условий @media (max-width: 768px), где-то затесалось @media (min-width: 1024px), а в паре компонентов верстальщик ошибся на один пиксель и написал max-width: 767px, сломав сетку карточек на стыке разрешений.
Эта проблема преследует веб-индустрию более пятнадцати лет. Мы научились выносить цвета, отступы и шрифты в CSS Custom Properties (переменные вида --primary-color), но сами медиавыражения до сих пор копипастим вручную.
Спецификация CSS Media Queries Level 5 закрывает эту историческую несправедливость директивой @custom-media.
Давай разберем, как устроена эта директива и как практические пресеты из блога Master.dev помогают навести идеальный порядок в адаптивной верстке.
Проклятие магических пикселей: почему старые брейкпоинты устарели
Традиционный подход к адаптивности страдает от трех фундаментальных пороков:
- Дублирование и ад поддержки: если дизайн-команда решает сдвинуть границу мобильного лейаута с 768px на 800px, инженеры вынуждены запускать глобальный автопоиск и замену по сотням CSS-модулей. Обязательно что-то пропустишь, и в продакшене появится нестыковка стилей.
- Ложные эвристики устройств: исторически мы привыкли считать: «если экран уже 768px — это мобильный тач-экран, если шире — это десктоп с мышью». Сегодня эта логика полностью сломалась. Современный планшет iPad Pro с экраном 13 дюймов и внешней клавиатурой с трекпадом может быть шире офисного ноутбука, но пользователь в любой момент может коснуться экрана пальцем. А компактный нетбук с экраном 11 дюймов управляется высокоточной мышью. Ориентироваться только на ширину окна в пикселях — тупиковый путь.
- Отсутствие семантики: абстрактная конструкция
@media (hover: none) and (pointer: coarse)заставляет разработчика вспоминать спецификации, вместо того чтобы сразу понять суть намерения автора: «здесь мы увеличиваем хитбокс кнопки под палец на сенсорном экране».
Синтаксис @custom-media: переменные для логических медиавыражений
Директива @custom-media работает как объявление глобальной переменной для правил @media. Она связывает идентификатор с двумя дефисами (--name) с любым допустимым медиавыражением или логической комбинацией:
/* Базовые определения именованных выражений */
@custom-media --narrow-window (max-width: 600px);
@custom-media --mouse (hover: hover) and (pointer: fine);
@custom-media --touch (hover: none) and (pointer: coarse);
/* Комбинирование ранее созданных пресетов */
@custom-media --mobile-touch (--narrow-window) and (--touch);
/* Использование в компонентах интерфейса */
@media (--mouse) {
.interactive-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}
}
@media (--touch) {
.interactive-card {
min-height: 48px; /* Увеличенная зона клика под палец */
}
}
Код становится самодокументируемым. Ты один раз объявляешь семантические правила в корне проекта и используешь их по всей кодовой базе.
Пресеты Даниэля Шварца: модульная сетка, типы ввода и True HDR
Фронтенд-архитектор Даниэль Шварц сформулировал готовую систему именованных медиавыражений, разделенную на три прикладных блока:
- Типы ввода (Input Modalities):
Вместо угадывания типа гаджета по ширине стекла пресеты проверяют реальный физический способ манипуляции:
--mouse:(hover: hover) and (pointer: fine)— мышь или трекпад с курсором;--touch:(hover: none) and (pointer: coarse)— палец на смартфоне или планшете;--stylus:(hover: none) and (pointer: fine)— высокоточный стилус на графическом планшете;--air:(hover: hover) and (pointer: coarse)— жестовое управление в пространстве (Apple Vision Pro, Meta Quest);--dpad:(hover: none) and (pointer: none)— кнопочный пульт телевизора Smart TV или геймпад.
- Модульная линейка брейкпоинтов с шагом 100px:
Шварц предлагает отказаться от субъективных названий
--mobile,--tabletи--desktop, которые у каждого дизайнера означают разные цифры, в пользу строгой числовой сетки:@custom-media --from-360 (min-width: 360px); @custom-media --from-480 (min-width: 480px); @custom-media --from-720 (min-width: 720px); @custom-media --from-1080 (min-width: 1080px); @custom-media --from-1440 (min-width: 1440px); - Расширенный динамический диапазон (True HDR):
@custom-media --true-hdr (dynamic-range: high) and (color-gamut: p3);
Этот пресет позволяет активировать сверхъяркие цвета палитры Display P3 только на экранах, аппаратно поддерживающих HDR (OLED, mini-LED), предотвращая искажения на дешевых офисных мониторах.
Статус в браузерах и фоллбеки: PostCSS против container style()
На текущий момент нативная поддержка директивы @custom-media уже реализована в движке Gecko (Firefox 148+), однако пока скрыта за экспериментальным системным флагом layout.css.custom-media.enabled.
Как использовать этот синтаксис в продакшене прямо сейчас?
Основной проверенный путь — плагин postcss-custom-media. На этапе сборки утилита компилирует именованные пресеты в стандартный CSS:
// postcss.config.js
module.exports = {
plugins: [
require('postcss-custom-media')({
importFrom: 'src/styles/custom-media.css'
})
]
};
Второй современный путь без препроцессоров — использование стилистических контейнерных запросов @container style():
:root {
@media (dynamic-range: high) and (color-gamut: p3) {
--is-hdr: true;
}
}
@container style(--is-hdr: true) {
.hero-glow-badge {
background: color(display-p3 1 0.1 0.2);
}
}
Как мы отмечали в разборе 12 возможностей современного CSS, нативная платформа шаг за шагом забирает себе функционал, ради которого раньше приходилось подключать тяжелые препроцессоры Sass и Less.
Внедрение пресетов @custom-media на уровне дизайн-системы избавляет команду от путаницы в пикселях и закладывает надежный фундамент под любые устройства будущего.
