Sass @custom-media не работает: решение проблемы
Узнайте, почему Sass не поддерживает @custom-media и как создать кастомные медиа-запросы с помощью миксинов, PostCSS и других методов.
Sass не может обработать новый at‑rule @custom-media: что делать?
Я пытаюсь добавить кастомные медиа‑запросы в свой проект с использованием синтаксиса:
@custom-media --phone-small (width >= #{$breakpoint-tablet-small})
Однако Sass выдает ошибку ({ expected scss(css-lcurlyexpected) как в редакторе, так и при сборке проекта. Sass не распознает этот at‑rule. Как правильно использовать @custom-media в Sass, или есть ли альтернативные способы создания кастомных медиа‑запросов?
Sass не поддерживает @custom-media at‑rule, так как эта функция пока не реализована ни в одной версии Sass. Основная проблема заключается в том, что @custom-media является частью спецификации CSS Level 4, которая ещё не полностью поддерживается в Sass, особенно при использовании сложных выражений с переменными. Вместо этого следует использовать альтернативные подходы с миксинами или PostCSS для создания кастомных медиа‑запросов.
Содержание
- Почему Sass не поддерживает @custom-media
- Основные ошибки при использовании @custom-media
- Альтернативные методы создания кастомных медиа‑запросов
- Решение с использованием PostCSS
- Правильные миксины для медиа‑запросов в Sass
- Рекомендации по организации медиа‑запросов
Почему Sass не поддерживает @custom-media
Основная причина, по которой Sass не может обработать @custom-media, заключается в том, что эта функция пока не реализована в нативной поддержке Sass. Как указано в исследованиях, на момент декабря 2021 года:
“As of now (Dec 2021), it appears that, while in the spec, @custom-media has not been implemented anywhere yet. Neither caniuse nor MDN know about @custom-media.” [Stack Overflow]
Sass имеет ограничения в поддержке CSS Media Queries Level 4, особенно при использовании:
- Range context (например,
width >= 320px) - Сложных выражений с переменными
- Пользовательских свойств в медиа‑запросах
Даже в последних версиях Dart Sass сохраняются проблемы с синтаксисом диапазонов:
“LibSass and older versions of Dart Sass and Ruby Sass don’t support media queries with features written in a range context.” [Sass Documentation]
Основные ошибки при использовании @custom-media
Ошибка ({ expected scss(css-lcurlyexpected) возникает из‑за нескольких причин:
1. Неподдерживаемый синтаксис диапазона
// НЕ РАБОТАЕТ
@custom-media --phone-small (width >= #{$breakpoint-tablet-small})
Sass не распознает операторы >=, <=, <, > в контексте медиа‑запросов без правильной обработки.
2. Проблемы с переменными
// НЕ РАБОТАЕТ
$breakpoint: 768px;
@custom-media --mobile (max-width: $breakpoint);
Sass не может подставить переменные напрямую в @custom-media правила.
3. Синтаксические ошибки в миксинах
При попытке создать миксины для @custom-media возникают ошибки компиляции:
// ВЫЗЫВАЕТ ОШИБКУ
@mixin custom-media($breakpoints) {
@each $breakpoint in $breakpoint-names {
@custom-media --#{$breakpoint}-media #{$media-query};
}
}
“the error is (max-width: 40em) isn’t a valid CSS value, despite that being exactly what i want the value to be. does sass not support the @custom-media syntax?” [Stack Overflow]
Альтернативные методы создания кастомных медиа‑запросов
1. Стандартные миксины для медиа‑запросов
// Правильный подход с миксинами
@mixin mobile {
@media (max-width: 768px) {
@content;
}
}
@mixin tablet {
@media (min-width: 769px) and (max-width: 1024px) {
@content;
}
}
@mixin desktop {
@media (min-width: 1025px) {
@content;
}
}
// Использование
.container {
@include mobile {
padding: 10px;
}
}
2. Миксины с гибкими параметрами
// Более гибкий подход
@mixin respond-to($breakpoint) {
@if $breakpoint == mobile {
@media (max-width: 768px) { @content; }
}
@if $breakpoint == tablet {
@media (min-width: 769px) and (max-width: 1024px) { @content; }
}
@if $breakpoint == desktop {
@media (min-width: 1025px) { @content; }
}
}
// Использование
.header {
@include respond-to(mobile) {
font-size: 14px;
}
}
3. Использование map для breakpoints
// Организация через map
$breakpoints: (
mobile: 768px,
tablet: 1024px,
desktop: 1200px
) !default;
@mixin media-up($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
$breakpoint-value: map.get($breakpoints, $breakpoint);
@media (min-width: $breakpoint-value) {
@content;
}
}
}
@mixin media-down($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
$breakpoint-value: map.get($breakpoints, $breakpoint);
@media (max-width: ($breakpoint-value - 1px)) {
@content;
}
}
}
// Использование
.hero {
@include media-up(tablet) {
grid-template-columns: 1fr 1fr;
}
}
Решение с использованием PostCSS
Если вам действительно нужна функциональность @custom-media, лучшим решением является использование PostCSS с плагином postcss-custom-media.
Настройка PostCSS
Создайте файл postcss.config.js:
const customMediaUnprefixed = require('./src/hooks/useMedia/custom-media.js');
const customMediaCache = {
customMedia: Object.assign(
{},
...Object.keys(customMediaUnprefixed).map((key) => ({
[`--${key}`]: customMediaUnprefixed[key]
}))
),
};
const config = {
plugins: [
require('postcss-preset-env')({
stage: 0,
customProperties: false,
browsers: ['> 0.5%, last 2 versions, Firefox ESR, not dead'],
importFrom: [customMediaCache],
features: {
'color-mod-function': { unresolved: 'warn' },
},
}),
],
};
module.exports = config;
Файл кастомных медиа‑запросов
Создайте custom-media.js:
module.exports = {
mobile: '(max-width: 719px)',
tablet: '(min-width: 720px)',
desktop: '(min-width: 1280px)',
large: '(min-width: 1920px)',
};
Использование в SCSS
// Теперь вы можете использовать кастомные медиа
.element {
@media (--mobile) {
color: red;
}
@media (--tablet) {
color: blue;
}
}
“config const customMediaUnprefixed = require(‘./src/hooks/useMedia/custom-media.js’); const customMediaCache = { customMedia: Object.assign({}, …Object.keys(customMediaUnprefixed).map((key) => ({[
--${key}]: customMediaUnprefixed[key]}))), };” [Stack Overflow]
Правильные миксины для медиа‑запросов в Sass
Вот надёжные миксины, которые работают во всех версиях Sass:
1. Базовые миксины для диапазонов
// Минимальная ширина
@mixin min-width($breakpoint) {
@media (min-width: $breakpoint) {
@content;
}
}
// Максимальная ширина
@mixin max-width($breakpoint) {
@media (max-width: $breakpoint) {
@content;
}
}
// Диапазон между
@mixin between($min, $max) {
@media (min-width: $min) and (max-width: $max) {
@content;
}
}
// Вне диапазона
@mixin outside($min, $max) {
@media (max-width: $min), (min-width: $max) {
@content;
}
}
2. Продвинутая система с map
// Глобальные переменные
$breakpoints: (
'xs': 0,
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px,
'xxl': 1400px
) !default;
// Миксин для min-width
@mixin media-breakpoint-up($name) {
$min: map-get($breakpoints, $name);
@if $min {
@media (min-width: $min) {
@content;
}
}
}
// Миксин для max-width
@mixin media-breakpoint-down($name) {
$max: map-get($breakpoints, $name);
@if $max {
$max: $max - 0.02px;
@media (max-width: $max) {
@content;
}
}
}
// Миксин для диапазона
@mixin media-breakpoint-between($lower, $upper) {
$min: map-get($breakpoints, $lower);
$max: map-get($breakpoints, $upper);
@if $min and $max {
$max: $max - 0.02px;
@media (min-width: $min) and (max-width: $max) {
@content;
}
}
}
3. Пример использования
.header {
padding: 1rem;
@include media-breakpoint-up(md) {
padding: 2rem;
}
@include media-breakpoint-down(sm) {
font-size: 0.9rem;
}
@include media-breakpoint-between(sm, lg) {
background-color: #f0f0f0;
}
}
“Simply put, you’ve made your mixin too specific and not very reusable for other sites. I’m currently using a collection of 4 mixins to handle the most common media queries: min-width, max-width, between, and outside” [Stack Overflow]
Рекомендации по организации медиа‑запросов
1. Используйте семантические имена
// Хорошо
@mixin mobile { @media (max-width: 768px) { @content; } }
@mixin tablet { @media (min-width: 769px) and (max-width: 1024px) { @content; } }
@mixin desktop { @media (min-width: 1025px) { @content; } }
// Плохо
@mixin small { @media (max-width: 768px) { @content; } }
@mixin medium { @media (min-width: 769px) and (max-width: 1024px) { @content; } }
2. Группируйте медиа‑запросы
.button {
background: blue;
color: white;
padding: 10px 20px;
// Mobile-first подход
@include mobile {
padding: 8px 16px;
font-size: 14px;
}
@include tablet {
padding: 12px 24px;
font-size: 16px;
}
@include desktop {
padding: 15px 30px;
font-size: 18px;
}
}
3. Избегайте вложенности медиа‑запросов
// Плохая практика
.container {
.header {
.logo {
width: 100px;
@include mobile {
width: 50px;
}
}
}
}
// Хорошая практика
.logo {
width: 100px;
@include mobile {
width: 50px;
}
}
4. Используйте переменные для повторяющихся значений
$spacing-unit: 8px;
$font-size-base: 16px;
.element {
padding: $spacing-unit * 2;
font-size: $font-size-base;
@include mobile {
padding: $spacing-unit;
font-size: $font-size-base * 0.875;
}
}
“Since our @media query is global, it cannot know about this --mobile-breakpoint variable. This extends to the :root selector as well. SASS / SCSS can make this even more confusing by allowing @media blocks to be nested inside of other rulesets. Don’t be fooled!” [Alternative to CSS variable media queries]
Источники
- Sass: Breaking Change: Media Queries Level 4
- Sass: CSS At‑Rules
- Why @custom-media does not apply the respective styles in sass - Stack Overflow
- How can I use @custom-media in Sass mixins? - Stack Overflow
- Want CSS variables in media query declarations? Try this!
- CSSNext postcss-custom-media - unable to import medias from file with ‘importFrom’ option - Stack Overflow
- Handling custom media queries in Sass with Twitter Bootstrap - Stack Overflow
- Media queries in Sass - Stack Overflow
Заключение
- Sass не поддерживает
@custom-media– эта функция находится в спецификации CSS Level 4, но ещё не реализована в Sass нативно. - Используйте стандартные миксины вместо
@custom-media– они обеспечивают лучшую совместимость и предсказуемое поведение. - PostCSS с плагином
postcss-custom-media– лучший вариант, если вам нужна именно функциональность@custom-media. - Организуйте медиа‑запросы через map и переменные – это обеспечивает гибкость и поддерживаемость кода.
- Следуйте Mobile‑First подходу – начинайте с мобильных стилей и постепенно добавляйте более широкие экраны для лучшей производительности.
Для большинства проектов стандартные миксины на основе @media правил являются оптимальным решением, обеспечивающим максимальную совместимость и предсказуемое поведение компиляции Sass.