На главную
CSS и вёрстка
Медиазапросы и контейнерные
Точки перелома в готовые @media: mobile-first или наоборот, px или em, новый синтаксис диапазонов. Плюс запросы к возможностям устройства, миксины SCSS, matchMedia и контейнерные запросы.
Точки перелома
По одной в строке: «md: 768». Только возрастание
Запросы к возможностям устройства
CSS
/* Сначала телефон, потом добавляем. Базовые правила живут вне запросов. */
@media (min-width: 640px) {
/* sm — от 640px */
.container { }
}
@media (min-width: 768px) {
/* md — от 768px */
.container { }
}
@media (min-width: 1024px) {
/* lg — от 1024px */
.container { }
}
@media (min-width: 1280px) {
/* xl — от 1280px */
.container { }
}
@media (min-width: 1536px) {
/* 2xl — от 1536px */
.container { }
}
/* Системная тема пользователя. Ручной переключатель на сайте должен уметь перебить её. */
@media (prefers-color-scheme: dark) {
.container { }
}
/* Человеку от анимации становится плохо. Не «выключить красоту», а убрать длинные перемещения. */
@media (prefers-reduced-motion: reduce) {
.container { }
}
Миксины SCSS
@use 'sass:math';
$breakpoints: (
'sm': 640px,
'md': 768px,
'lg': 1024px,
'xl': 1280px,
'2xl': 1536px,
);
@mixin at($name) {
$px: map-get($breakpoints, $name);
@if not $px {
@error 'Нет точки перелома «#{$name}»';
}
$value: $px;
@media (min-width: #{$value}) {
@content;
}
}
/* .card { padding: 8px; @include at('md') { padding: 24px; } } */
matchMedia в JS
const QUERIES = {
sm: '(min-width: 640px)',
md: '(min-width: 768px)',
lg: '(min-width: 1024px)',
xl: '(min-width: 1280px)',
'2xl': '(min-width: 1536px)',
};
// Браузер сам зовёт обработчик, когда условие перестаёт или начинает выполняться.
// Слушать изменение размера окна ради этого не нужно: лишние срабатывания на каждый пиксель.
export function watch(name, onChange) {
const mq = window.matchMedia(QUERIES[name]);
onChange(mq.matches);
const handler = (event) => onChange(event.matches);
mq.addEventListener('change', handler);
return () => mq.removeEventListener('change', handler);
}
Контейнерные запросы
Ширина окна ничего не говорит о месте под карточку: в сайдбаре она узкая и на широком мониторе. Контейнерный запрос спрашивает про блок, а не про экран, — одна карточка работает и в ленте, и в колонке.
container.css
/* Контейнером объявляется родитель, а не сам блок: элемент не может
отвечать на собственную ширину — он же её и задаёт. */
.card-wrap {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
/* wide: место под карточку шире 400px — неважно, какой ширины окно */
.card { }
}
@container card (min-width: 640px) {
/* full: место под карточку шире 640px — неважно, какой ширины окно */
.card { }
}
/* Внутри контейнера работают свои единицы: 1cqi — сотая доля его ширины. */
.card__title {
font-size: clamp(1rem, 4cqi, 1.75rem);
}