У лютому 2026 року Firefox 148 отримав експериментальне правило @custom-media, яке поки що працює лише за умови активації прапорця layout.css.custom-media.enabled. Ідея проста: так само як кастомні властивості дають змогу перевикористовувати значення CSS, custom media queries дозволяють один раз описати умову для медіа-запиту і далі просто посилатися на неї за назвою.
Ось як це виглядає на практиці:
@custom-media --no-hover (hover: none);
@custom-media --landscape (orientation: landscape);
@custom-media --no-hover-landscape (hover: none) and (orientation: landscape);
@custom-media --no-hover-landscape (--no-hover) and (--landscape);
@custom-media --no-hover-landscape (hover: none) and (--landscape);
@media (--no-hover) { }
@media (--landscape) { }
@media (--no-hover-landscape) { }
@media (--no-hover) and (--landscape) { }
@media (--no-hover) and (orientation: landscape) { }
@media (hover: none) and (--landscape) { }
Разом із такими запитами можна застосовувати звичайні типи медіа (all, screen, print), логічні оператори not, and, only та перелік через кому, що відповідає за логічне «або» — тобто вся звична синтаксична база медіа-запитів нікуди не зникає.
У цьому матеріалі зібрано кілька практичних пресетів @custom-media, які можна одразу підключати до будь-якого проєкту без зайвої специфіки. Але спершу варто розібратися, наскільки це взагалі підтримується браузерами.
Підтримка браузерами
- У Firefox функція доступна лише через прапорець
layout.css.custom-media.enabled - У Chrome існує лише Intent to Prototype, реального прогресу поки немає
- У Safari підтримки немає взагалі
Тобто говорити про готовність до продакшну зарано. Оскільки @custom-media підтримується не всіма основними браузерами, нижче наведено альтернативний варіант тих самих пресетів через синтаксис container style query, який уже працює повсюдно. Цей синтаксис також піддається автоматичній обробці — існує PostCSS-плагін, який дозволяє вже зараз готуватися до майбутньої підтримки.
Custom media queries для можливостей пристрою
Розмір екрана давно перестав бути надійним індикатором можливостей пристрою, якщо колись і був ним. Раніше вистачало трьох брейкпоінтів для мобільного, планшета й десктопа, але сьогодні ці категорії розмиті: деякі «мобільні» та «планшетні» пристрої підключають мишу й клавіатуру, а деякі «десктопи» мають сенсорний екран і навіть підтримку стилуса.
Брейкпоінти досі корисні, але самі по собі вони не розкажуть про реальні можливості пристрою. Важливіше визначити основний спосіб взаємодії користувача, щоб під нього підлаштувати інтерфейс. Для цього потрібна комбінація медіа-запитів, і саме тут @custom-media підходить ідеально:
@custom-media --mouse (hover: hover) and (pointer: fine); @custom-media --touch (hover: none) and (pointer: coarse); @custom-media --stylus (hover: none) and (pointer: fine); @custom-media --air (hover: hover) and (pointer: coarse); @custom-media --dpad (hover: none) and (pointer: none);
Водночас для якісного досвіду потрібно враховувати й додаткові способи введення. На жаль, однозначно визначити, яким саме пристроєм користувач користується зараз, якщо доступних варіантів кілька, неможливо. Наведу приклади:
- Миша навмисно підключена до планшета — ймовірно, користувач працює саме нею, але підтвердити це достовірно не вийде
- Десктоп із сенсорним екраном — користувач, швидше за все, працює мишею, але обидва варіанти вбудовані, тож напевно сказати важко
Виникає складна ситуація, чи не так? Навіть сімейство медіа-функцій any- (наприклад, any-hover) показує лише, чи має такою властивістю хоч якийсь із доступних входів, а не те, чи має її саме другорядний спосіб введення. Здавалося б, зручно, але насправді ні. Розгляньмо приклад:
@custom-media --mouse (any-hover: hover) and (any-pointer: fine);
Цей запит не перевіряє, чи є серед усіх входів миша; він перевіряє, чи хоч один зі входів — миша або повітряний вказівник (через any-hover: hover), і окремо, чи хоч один зі входів — миша або стилус (через any-pointer: fine), причому це можуть бути навіть різні пристрої.
Наступна комбінація теж не ідеальна. Вона означає, що основний пристрій введення — миша або повітряний вказівник, тоді як будь-який із доступних входів — миша або стилус.
@custom-media --mouse (hover: hover) and (any-pointer: fine);
Єдиний спосіб знати напевне — бути максимально конкретним, але чи варта це того, щоб підтримувати купу окремих custom media queries? Навряд. Розумніше орієнтуватися на основний спосіб введення, а водночас подбати, щоб сайт прийнятно працював і з іншими пристроями без додаткових налаштувань.
Це не означає, що крайніх випадків узагалі не буде, — просто варто сприймати any--запити як доповнення до основних. Так можна точково покривати другорядні способи введення, не плодячи зайвих custom media queries:
@media (--touch) and (any-hover: hover) { }
@media (--touch) and (any-pointer: fine) { }
@media (--mouse) and (any-pointer: coarse) { }
@media (--stylus) and (any-pointer: coarse) { }
Нагадаю: custom media queries ще не підтримуються всіма браузерами, тож ось той самий набір через container style query, який уже працює скрізь:
:root {
@media (hover: hover) and (pointer: fine) {
--mouse: any-valid-ident;
}
@media (hover: none) and (pointer: coarse) {
--touch: true;
}
@media (hover: none) and (pointer: fine) {
--stylus: true;
}
@media (hover: hover) and (pointer: coarse) {
--air: true;
}
@media (hover: none) and (pointer: none) {
--dpad: true;
}
}
@container style(--mouse) { }
@container style(--touch) { }
@container style(--stylus) { }
@container style(--air) { }
@container style(--dpad) { }
Невеличке застереження: container style queries не можна застосовувати до :root, бо це елемент найвищого рівня і в нього немає власних контейнерів.
Ще одне, менш очевидне застереження: контейнерні запити не можна поєднувати з медіа-запитами в одній умові, тож доводиться виносити все в окремі кастомні властивості, ось так:
:root {
@media (hover: none) and (pointer: coarse) {
--touch: true;
}
@media (any-hover: hover) {
--any-hover-hover: true;
}
}
@container style(--touch) and style(--any-hover-hover) { }
Особисто мені такий підхід не подобається зовсім — забагато кастомних властивостей доводиться відстежувати, особливо коли їх можна просто вкласти одну в одну:
@container style(--touch) {
@media (any-hover: hover) { }
}
Custom media queries для брейкпоінтів
Розміри екранів сьогодні дуже різняться, тож обмежитися трьома брейкпоінтами для мобільного, планшета й десктопа вже не вийде. Моя особиста стратегія — умова «ширина більша або дорівнює [значенню]» із кроком у 100 пікселів. Орієнтуючись на статистику роздільних здатностей екранів, я тримаю діапазон від 360px до 1560px. У вигляді custom media queries це виглядає так:
@custom-media --from-360 (width >= 360px);
@custom-media --from-460 (width >= 460px);
@custom-media --from-560 (width >= 560px);
@custom-media --from-660 (width >= 660px);
@custom-media --from-760 (width >= 760px);
@custom-media --from-860 (width >= 860px);
@custom-media --from-960 (width >= 960px);
@custom-media --from-1060 (width >= 1060px);
@custom-media --from-1160 (width >= 1160px);
@custom-media --from-1260 (width >= 1260px);
@custom-media --from-1360 (width >= 1360px);
@custom-media --from-1460 (width >= 1460px);
@custom-media --from-1560 (width >= 1560px);
@media (--from-360) { }
@media (--from-460) { }
@media (--from-560) { }
@media (--from-660) { }
@media (--from-760) { }
@media (--from-860) { }
@media (--from-960) { }
@media (--from-1060) { }
@media (--from-1160) { }
@media (--from-1260) { }
@media (--from-1360) { }
@media (--from-1460) { }
@media (--from-1560) { }
А ось той самий набір через container style query:
:root {
@media (width >= 360px) { --from-360: true; }
@media (width >= 460px) { --from-460: true; }
@media (width >= 560px) { --from-560: true; }
@media (width >= 660px) { --from-660: true; }
@media (width >= 760px) { --from-760: true; }
@media (width >= 860px) { --from-860: true; }
@media (width >= 960px) { --from-960: true; }
@media (width >= 1060px) { --from-1060: true; }
@media (width >= 1160px) { --from-1160: true; }
@media (width >= 1260px) { --from-1260: true; }
@media (width >= 1360px) { --from-1360: true; }
@media (width >= 1460px) { --from-1460: true; }
@media (width >= 1560px) { --from-1560: true; }
}
@container style(--from-360) { }
@container style(--from-460) { }
@container style(--from-560) { }
@container style(--from-660) { }
@container style(--from-760) { }
@container style(--from-860) { }
@container style(--from-960) { }
@container style(--from-1060) { }
@container style(--from-1160) { }
@container style(--from-1260) { }
@container style(--from-1360) { }
@container style(--from-1460) { }
@container style(--from-1560) { }
Брейкпоінтами «менше за» я користуюся рідко, але їх легко додати поруч із основними, якщо виникне потреба.
Для стислості проміжні брейкпоінти також можна описати одним медіа-запитом, щоправда, за рахунок гнучкості — уявіть, скільки варіантів таких комбінацій довелося б підтримувати.
Цей розділ трохи суб'єктивніший за інші, тож варто додавати власні варіанти з висотою, орієнтацією чи іншими параметрами, якщо вони дійсно виправдовують появу нових custom media queries. Також можна скористатися власною системою іменування (наприклад, --xsm/--sm/--md/--lg/--xlg тощо) — тут усе на розсуд розробника.
Custom media query для визначення справжнього HDR
Справжній HDR потребує дисплея з високою динамічною яскравістю і широкою колірною гамою. Без цього HDR-зображення та відео виглядають не так, як задумано: або висока яскравість виглядає неприродно ненасиченою, або насичені кольори — неприродно темними, або обидва ефекти одразу.
Щоб визначити, чи можна використовувати справжній HDR для зображень і відео, стане в пригоді така custom media query:
@custom-media --true-hdr (dynamic-range: high) and (color-gamut: p3);
@media (--true-hdr) { }
А ось вона ж у синтаксисі container style query:
:root {
@media (dynamic-range: high) and (color-gamut: p3) {
--true-hdr: true;
}
}
@container style(--true-hdr) { }
Підсумки
Автор матеріалу зізнається, що з нетерпінням чекає на повноцінне використання custom media queries, але поки задоволений і тим, що container style queries чудово працюють як тимчасова заміна.
Головна перевага від роботи з ними вже зараз — розуміння того, наскільки custom media queries схожі на звичайні кастомні властивості, коли йдеться про баланс між лаконічністю та гнучкістю. Правильно організувати їх із першого разу вдається не завжди, і на це йде певний час.
Once all web browsers support @custom-media, I’ll certainly add these presets to my CSS reset.
автор матеріалу, Master.dev Blog
Автор також цікавиться, чи є в інших розробників власні ідеї або спостереження щодо роботи @custom-media, і згадує, що свої готові пресети вже має проєкт Open Props.
Джерело: Master.dev Blog


