ДокументацияCSSАдаптивная верстка: media queries, mobile-first, container queries
Начальный 11 мин чтения

Адаптивная верстка: media queries, mobile-first, container queries

Адаптивная верстка CSS — media queries, подход mobile-first, breakpoints, container queries, @container и советы по отзывчивому дизайну.

адаптивная версткаmedia queriesresponsivemobile-firstbreakpointscontainer queriesCSS

Что такое адаптивная верстка

Адаптивная (responsive) верстка — страница выглядит хорошо на любом экране: телефон, планшет, ноутбук, широкий монитор. Ключевой инструмент — media queries.

Media Queries

Media query применяет стили только при определённых условиях:

.card {
  width: 100%;
  padding: 16px;
}

@media (min-width: 768px) {
  .card {
    width: 48%;
    padding: 24px;
  }
}

@media (min-width: 1200px) {
  .card {
    width: 31%;
    padding: 32px;
  }
}

Условия

@media (min-width: 768px) { }  /* ширина viewport от 768px */
@media (max-width: 767px) { }  /* ширина viewport до 767px */
@media (min-width: 768px) and (max-width: 1023px) { }  /* диапазон */

@media (orientation: landscape) { }  /* горизонтальная ориентация */
@media (prefers-color-scheme: dark) { }  /* тёмная тема ОС */
@media (prefers-reduced-motion: reduce) { }  /* пользователь просит меньше анимаций */
@media (hover: hover) { }  /* устройство с мышью (не тач-экран) */

Диапазонный синтаксис (современный)

@media (width >= 768px) { }
@media (768px <= width < 1200px) { }

Поддержка уже хорошая, но для продакшна пока надёжнее min-width / max-width.

Mobile-first

Пишите стили для мобильных, а затем расширяйте для больших экранов через min-width:

.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

@media (min-width: 768px) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
  }
}

Почему mobile-first лучше desktop-first:

/* Mobile-first: стили по умолчанию для мобильных, дополняем */
@media (min-width: 768px) { /* добавляем */ }

/* Desktop-first: стили по умолчанию для десктопа, переписываем */
@media (max-width: 767px) { /* отменяем */ }

При mobile-first вы добавляете стили, при desktop-first — перезаписываете. Меньше кода — меньше багов.

Популярные breakpoints

Нет единого стандарта, но часто используют:

BreakpointУстройство
320–479pxмаленький телефон
480–767pxтелефон
768–1023pxпланшет
1024–1279pxноутбук
1280–1535pxдесктоп
1536px+широкий монитор

Не пишите стили под конкретные устройства — устройств тысячи. Проверяйте на breakpoints и между ними.

Относительные единицы вместо фиксированных

.bad {
  width: 960px;  /* не поместится на телефоне */
  font-size: 16px;
}

.good {
  max-width: 1200px;
  width: 100%;
  font-size: clamp(0.875rem, 2vw, 1.125rem);
}

clamp() — отзывчивые значения

h1 {
  font-size: clamp(1.5rem, 4vw, 3rem);
  /* минимум 1.5rem, предпочтительно 4vw, максимум 3rem */
}

.container {
  padding: clamp(16px, 4vw, 48px);
}

Container Queries

Media queries реагируют на размер viewport. Container queries — на размер родительского элемента. Компонент адаптируется к месту, где он размещён.

Определение контейнера

.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

.main-content {
  container-type: inline-size;
  container-name: main;
}

Правила @container

.card {
  display: flex;
  flex-direction: column;
}

@container sidebar (min-width: 300px) {
  .card {
    flex-direction: row;
  }
}

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

Одна и та же .card будет выглядеть по-разному в sidebar и в main — без дополнительных классов.

Практические паттерны

Адаптивная навигация

.nav {
  display: flex;
  gap: 8px;
}

.nav-links {
  display: none;
}

.nav-burger {
  display: block;
}

@media (min-width: 768px) {
  .nav-links {
    display: flex;
  }

  .nav-burger {
    display: none;
  }
}

Адаптивный герой-блок

.hero {
  padding: clamp(2rem, 8vw, 6rem) clamp(1rem, 4vw, 3rem);
  text-align: center;
}

.hero h1 {
  font-size: clamp(1.75rem, 5vw, 4rem);
  line-height: 1.1;
}

@media (min-width: 768px) {
  .hero {
    text-align: left;
  }
}

Скрытие на мобильных / десктопе

.hide-mobile {
  display: none;
}

@media (min-width: 768px) {
  .hide-mobile {
    display: block;
  }
}

@media (max-width: 767px) {
  .hide-desktop {
    display: none;
  }
}

Адаптивные изображения

.responsive-img {
  max-width: 100%;
  height: auto;
  display: block;
}

/* Фон */
.hero-bg {
  background-image: url('hero-mobile.jpg');
  background-size: cover;
  background-position: center;
}

@media (min-width: 768px) {
  .hero-bg {
    background-image: url('hero-tablet.jpg');
  }
}

@media (min-width: 1200px) {
  .hero-bg {
    background-image: url('hero-desktop.jpg');
  }
}

Через HTML — элемент picture:

<picture>
  <source media="(min-width: 768px)" srcset="hero-desktop.webp">
  <source media="(min-width: 480px)" srcset="hero-tablet.webp">
  <img src="hero-mobile.webp" alt="Герой" width="800" height="600">
</picture>

Метатег viewport

Без этого метатега мобильные браузеры покажут десктопную версию, уменьшив её:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Обязательно на каждой странице.

Итог

  • Mobile-first: стили для мобильных по умолчанию, min-width для расширения
  • @media (min-width: Npx) — основной инструмент
  • clamp() — отзывчивые значения без media queries
  • Container queries (@container) — компонент реагирует на размер родителя, не viewport
  • max-width: 100%; height: auto — адаптивные изображения
  • Метатег viewport обязателен