Статьи

Как сделать адаптивную вёрстку — CSS Grid, Flexbox, clamp()

23 июля 2026, 15:15  |  14 мин чтения

Адаптивная вёрстка прошла долгий путь: от фиксированных табличных макетов в 2000-х до резиновых сеток на CSS Grid в 2026. Если раньше верстальщик держал в голове три разрешения (320px, 768px, 1024px) и писал медиа-запросы под каждое, то сегодня CSS позволяет создавать макеты, которые подстраиваются автоматически. Разберём три главных инструмента: Flexbox, CSS Grid и математические функции, а также новейшие технологии вроде Container Queries.

Краткая история: от таблиц до Container Queries

В начале 2000-х вёрстка делалась на HTML-таблицах. Ширина задавалась в пикселях. Если экран был уже — появлялся горизонтальный скролл. В середине 2000-х появились float-макеты с фиксированной шириной. Затем — резиновая вёрстка с процентами. В 2012-2015 стали популярны медиа-запросы и mobile-first. В 2017-2020 CSS получил Flexbox и Grid — настоящие инструменты для раскладки. В 2023-2026 добавились Container Queries и каскадные слои (@layer), которые окончательно изменили подход к адаптиву. Сегодня грамотный верстальщик пишет минимум медиа-запросов, полагаясь на встроенные возможности CSS.

1. Flexbox — для одномерных макетов

Flexbox (Flexible Box Layout) предназначен для раскладки элементов в одном направлении — по строке или колонке. Это идеальный инструмент для шапок, навигации, карточек, центрирования.

Свойства контейнера

display: flex — включает flex-контекст. Все дочерние элементы становятся flex-элементами.

flex-direction — задаёт направление главной оси:

.container { flex-direction: row; }           /* слева направо (по умолчанию) */
.container { flex-direction: column; }        /* сверху вниз */
.container { flex-direction: row-reverse; }   /* справа налево */

justify-content — выравнивание по главной оси:

.container { justify-content: flex-start; }    /* прижаты к началу */
.container { justify-content: center; }         /* по центру */
.container { justify-content: space-between; }  /* равномерно, крайние к краям */
.container { justify-content: space-evenly; }   /* строго равные отступы */

align-items — выравнивание по поперечной оси:

.container { align-items: stretch; }   /* растянуть на всю высоту (по умолчанию) */
.container { align-items: center; }    /* по центру - самое частое */
.container { align-items: flex-start; } /* прижать к верху */

flex-wrap — перенос элементов на новую строку:

.container { flex-wrap: nowrap; }  /* не переносить (по умолчанию) */
.container { flex-wrap: wrap; }    /* переносить, если не влезают */

Свойства flex-элементов

flex-grow — коэффициент растягивания (0 — не растягивать, 1 — растянуть пропорционально).

flex-shrink — коэффициент сжатия (1 — можно сжимать, 0 — нельзя сжимать).

flex-basis — начальный размер элемента до растяжения/сжатия.

Короткая запись — flex: grow shrink basis:

.flex-1 { flex: 1; }              /* flex: 1 1 0 — растянуть, занимает всё пространство */
.flex-auto { flex: 1 1 auto; }     /* растянуть, но учитывать размер контента */
.flex-none { flex: 0 0 auto; }     /* не растягивать, исходный размер */
.flex-300 { flex: 1 1 300px; }     /* минимум 300px, может расти */

Реальный пример: шапка сайта на Flexbox

.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 1rem 2rem;
}
.logo { flex: 0 0 auto; }
.nav { display: flex; gap: 2rem; }
.cta { flex: 0 0 auto; }

На десктопе логотип слева, навигация по центру, кнопка справа. На мобильном навигация переносится на новую строку. Ни одного медиа-запроса.

Реальный пример: карточки услуг

.services {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
}
.service-card {
  flex: 1 1 280px;
  padding: 1.5rem;
  border: 1px solid var(--border);
  border-radius: 12px;
}

Карточки занимают минимум 280px. На экране 1200px — 4 карточки. На 600px — 2. На 300px — 1. Идеально адаптивно.

2. CSS Grid — для двумерных макетов

CSS Grid управляет строками и колонками одновременно. Это полноценная двумерная система в отличие от одномерного Flexbox.

Основные свойства Grid

grid-template-columns и grid-template-rows — определяют количество и размер колонок/строк:

.grid {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;  /* три колонки: средняя в 2 раза шире */
  grid-template-rows: auto 1fr auto;   /* шапка, контент, подвал */
  gap: 1.5rem;
}

repeat() — повторяет колонки:

grid-template-columns: repeat(3, 1fr);   /* три равные колонки */
grid-template-columns: repeat(12, 1fr);  /* 12-колоночная сетка */

auto-fill vs auto-fit:

/* auto-fill — создаёт пустые колонки */
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
/* auto-fit — схлопывает пустые колонки */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

auto-fill оставляет пустые треки, если элементов меньше, чем может поместиться. auto-fit схлопывает их — элементы растягиваются. Для карточек товаров обычно нужен auto-fit.

minmax() — задаёт минимальный и максимальный размер колонки:

grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
/* каждая колонка минимум 280px, максимум 1fr */

grid-area и именованные области

.layout {
  display: grid;
  grid-template-areas:
    "header header header"
    "sidebar content content"
    "footer footer footer";
  grid-template-columns: 250px 1fr 1fr;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer { grid-area: footer; }

/* На мобильных */
@media (max-width: 768px) {
  .layout {
    grid-template-areas:
      "header"
      "content"
      "sidebar"
      "footer";
    grid-template-columns: 1fr;
  }
}

Этот приём невероятно удобен: вы описываете макет словами, а медиа-запрос просто переопределяет расположение областей.

Реальный пример: галерея на Grid

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 0.5rem;
}
.gallery img {
  width: 100%;
  height: 200px;
  object-fit: cover;
  border-radius: 8px;
}
.gallery img:first-child {
  grid-column: span 2;
  grid-row: span 2;
  height: 412px;
}

Первое изображение занимает 2 колонки и 2 строки — оно крупнее. Остальные — равномерная сетка. Выглядит как hand-crafted сетка в Pinterest, но делается в 10 строк CSS.

Flexbox vs CSS Grid: когда что использовать

СценарийFlexboxGrid
Шапка сайта (лого + навигация)
Центрирование блока по вертикали и горизонтали
Карточки с переносом строк
Сложная сетка с overlapping
Раскладка «шапка-контент-подвал» на весь экран
Галерея с элементами разного размера

Правило: если макет одномерный (строка или колонка) — Flexbox. Если двумерный (строки и колонки одновременно) — Grid. Умение выбирать правильный инструмент — признак опытного верстальщика.

3. clamp(), min(), max() — математика адаптива

Современный CSS включает три функции для размеров как диапазонов, без медиа-запросов:

clamp(MIN, PREFERRED, MAX) — размер в диапазоне. Если preferred меньше MIN — берётся MIN. Больше MAX — берётся MAX.

/* Размер заголовка: от 1.5rem до 3rem, плавно меняется */
h1 { font-size: clamp(1.5rem, 4vw, 3rem); }

/* Отступы контейнера */
.container { padding: clamp(1rem, 5vw, 3rem); }

/* Ширина колонки с текстом (читаемость) */
article { width: clamp(300px, 65ch, 800px); }

/* Высота hero-секции */
.hero { min-height: clamp(300px, 50vh, 700px); }

min() — выбирает наименьшее значение:

.container {
  width: min(90%, 1200px);  /* 90% от родителя, но не больше 1200px */
}

max() — выбирает наибольшее значение:

.sidebar {
  width: max(250px, 20%);  /* минимум 250px, но и 20% от родителя */
}

4. CSS Container Queries — новая эра адаптива

Container Queries (поддержка в Chrome 105+, Safari 16+, Firefox 110+) позволяют компоненту подстраиваться под размер своего контейнера, а не под размер окна. Это решает проблему, когда один и тот же компонент используется в разных контекстах.

/* Шаг 1: определить контейнер */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Шаг 2: стили внутри контейнера */
@container card (max-width: 400px) {
  .card { flex-direction: column; }
  .card-image { width: 100%; }
}
@container card (min-width: 401px) {
  .card { flex-direction: row; }
  .card-image { width: 200px; }
}

В 2026 Container Queries стабильно поддерживаются всеми современными браузерами. Используйте их как прогрессивное улучшение.

5. Когда медиа-запросы всё-таки нужны

Медиа-запросы полностью не исчезли. Они нужны для:

— Изменения порядка блоков (sidebar под контент на мобильных)
— Скрытия/показа блоков (сложная декорация убирается на телефоне)
— Навигации (меню-гамбургер на мобильных)
— Коррекции отступов между секциями
— Тёмной темы (prefers-color-scheme)
— Поведения пользователя (prefers-reduced-motion)

/* mobile-first: базовая верстка для мобильных */
.nav { display: none; }
.hamburger { display: block; }

/* desktop */
@media (min-width: 1024px) {
  .nav { display: flex; gap: 2rem; }
  .hamburger { display: none; }
}

6. mobile-first vs desktop-first

Mobile-first — базовые стили для мобильных, затем min-width для больших экранов. Рекомендуемый подход: фокус на главном, прогрессивное улучшение.

Desktop-first — стили для десктопа, max-width для меньших экранов. Работает, но мобильная версия выглядит как «урезанная десктопная».

В 2026 году мобильный трафик — 60-70% всего интернет-трафика. Начинайте с мобильной версии.

7. Инструменты тестирования адаптива

Chrome DevTools. F12 → иконка телефона → переключайтесь между устройствами, эмулируйте touch.

Responsively App. Бесплатно. Показывает сайт на 10+ разрешениях одновременно.

BrowserStack. Платный сервис для реальных устройств. Бесплатный период — 30 минут.

LambdaTest. Аналог BrowserStack с бесплатным тарифом.

Современная вёрстка — это flex/grid + clamp() + auto-fill, дополненные Container Queries для вложенных компонентов. Медиа-запросы — только для исключений. Чем меньше медиа-запросов — тем чище и поддерживаемее код.