Как сделать адаптивный сайт: viewport, медиазапросы, Flexbox, Grid и srcset

Как сделать адаптивный сайт: viewport, медиазапросы, Flexbox, Grid и srcset Полезное

Адаптивный сайт — это сайт, у которого одна и та же HTML-страница перестраивает раскладку под ширину экрана: на телефоне блоки идут в одну колонку, на планшете в две, на компьютере в три-четыре, и нигде не появляется горизонтальная прокрутка. Делается это средствами HTML и CSS, без отдельной мобильной версии. Ниже — полный рабочий пример, разбор каждого приема, типовые ошибки и способ проверки.

Примеры проверены в Google Chrome 153 (сентябрь 2026) с эмуляцией телефона 375 px, планшета 768 px и десктопа 1280 px. Результаты, приведенные ниже, взяты из этого прогона.

Мини-словарь: что означают термины

Термин Что это Где встречается
Viewport Область окна, в которой браузер раскладывает страницу, в CSS-пикселях <meta name="viewport">
CSS-пиксель Единица верстки; на экране с плотностью 3x один CSS-пиксель = 3×3 физических точки px, devicePixelRatio
Медиазапрос Условие в CSS: «применить эти правила, если ширина >= N» @media
Брейкпоинт Ширина, на которой раскладка меняется значение в медиазапросе
Резиновая сетка Размеры в долях (%, fr) и с ограничением max-width вместо жестких пикселей Flexbox, Grid

Термины «адаптивный» и «отзывчивый» (responsive) в русскоязычной практике обычно используют как синонимы. В строгом смысле adaptive design — это несколько заранее сверстанных фиксированных макетов, между которыми страница переключается, а responsive — плавная раскладка, которая тянется между брейкпоинтами. Пример ниже — второй вариант, его сейчас и называют адаптивной версткой.

Минимальный полный пример

Сохраните код в файл index.html и откройте в браузере. Затем включите режим устройства в DevTools (F12, значок телефона) и меняйте ширину.

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Адаптивная страница</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { margin: 0; font-family: system-ui, sans-serif; line-height: 1.5; }
    img { max-width: 100%; height: auto; display: block; }

    .container { width: 100%; max-width: 1100px; margin: 0 auto; padding: 0 16px; }

    .header { display: flex; flex-wrap: wrap; align-items: center;
              justify-content: space-between; gap: 12px; padding: 12px 0; }
    .nav { display: flex; flex-direction: column; gap: 8px; width: 100%; }

    .cards { display: grid; gap: 16px;
             grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
    .card { border: 1px solid #ddd; border-radius: 8px; padding: 16px; }

    h1 { font-size: clamp(1.5rem, 1rem + 2.5vw, 2.5rem); }

    @media (min-width: 768px) {
      .nav { flex-direction: row; width: auto; }
    }
  </style>
</head>
<body>
  <div class="container">
    <header class="header">
      <a href="/" class="logo">Логотип</a>
      <nav class="nav">
        <a href="#">Курсы</a><a href="#">Блог</a><a href="#">Контакты</a>
      </nav>
    </header>
    <h1>Адаптивная страница</h1>
    <section class="cards">
      <article class="card">Карточка 1</article>
      <article class="card">Карточка 2</article>
      <article class="card">Карточка 3</article>
      <article class="card">Карточка 4</article>
    </section>
  </div>
</body>
</html>

Что получится на трех ширинах (замер в Chrome 153):

ширина  колонок карточек  меню        размер h1  ширина документа
375     1                 столбиком   25.375px   375 (без прокрутки)
768     2                 в строку    35.2px     768
1280    4                 в строку    40px       1280

Медиазапрос в примере всего один — он переключает меню. Число колонок карточек меняется без медиазапросов: это делает Grid.

Разбор по приемам

1. Meta viewport

Строка <meta name="viewport" content="width=device-width, initial-scale=1"> говорит мобильному браузеру: «раскладывай страницу по реальной ширине экрана в CSS-пикселях». Без нее мобильный браузер считает, что перед ним сайт для компьютера, раскладывает его на виртуальной ширине около 980 px и уменьшает целиком.

Проверка: та же страница без meta viewport на эмуляции телефона 375 px дала ширину раскладки 981 px, масштаб 0.38, три колонки карточек и меню в строку. Медиазапросы при этом честно сработали — но для ширины 980, а не 375. Текст стал мелким, как на скриншоте десктопа.

Не добавляйте user-scalable=no и maximum-scale=1: они мешают пользователю увеличить текст, это проблема доступности.

2. Резиновый контейнер вместо фиксированной ширины

.container занимает 100% ширины, но не больше 1100 px, и центрируется margin: 0 auto. Отступ padding: 0 16px не дает тексту прилипнуть к краю телефона. box-sizing: border-box включает padding в ширину, иначе width: 100% плюс отступы вылезут за экран.

3. Flexbox для строк, Grid для сеток

Шапка сделана на Flexbox: justify-content: space-between разносит логотип и меню по краям, flex-wrap: wrap переносит меню на новую строку, если места мало. Карточки — на Grid: repeat(auto-fit, minmax(240px, 1fr)) означает «столько колонок, сколько влезет при минимальной ширине 240 px, остаток поделить поровну».

Задача Что взять Почему
Элементы в одну линию с переносом (шапка, кнопки, теги) Flexbox Раскладка по одной оси, размеры от содержимого
Сетка карточек, галерея Grid с auto-fit + minmax Колонки подстраиваются без медиазапросов
Макет страницы: шапка, сайдбар, контент, подвал Grid с областями (grid-template-areas) Две оси сразу, перестановка блоков в медиазапросе
Компонент, который зависит от ширины своего блока, а не экрана Container queries (@container) Один компонент ведет себя по-разному в узкой и широкой колонке

4. Медиазапросы и брейкпоинты

/* по умолчанию - стили для узкого экрана */
.nav { flex-direction: column; }

/* от 768px и шире */
@media (min-width: 768px) {
  .nav { flex-direction: row; }
}

Брейкпоинты выбирают по содержимому, а не по списку моделей телефонов: сужайте окно и ставьте брейкпоинт там, где раскладка начинает ломаться. Значения ниже — распространенный ориентир, а не стандарт.

Диапазон ширины Типичный класс устройства
до 767 px телефоны
768-1023 px планшеты, узкие окна
от 1024 px ноутбуки и мониторы

Кроме min-width в современных браузерах работает запись диапазоном: @media (width >= 768px). Если нужно поддерживать старые браузеры, надежнее классическая форма.

5. Гибкий размер текста

font-size: clamp(1.5rem, 1rem + 2.5vw, 2.5rem) задает размер заголовка, который растет с шириной экрана, но не меньше 1.5rem и не больше 2.5rem. В прогоне это 25.375px на телефоне и 40px на десктопе. Основной текст оставляйте в rem, чтобы работала настройка размера шрифта в браузере.

6. Адаптивные изображения: srcset и sizes

max-width: 100%; height: auto не дает картинке вылезти за контейнер. Но телефону незачем скачивать файл шириной 1600 px — для этого есть srcset:

<img
  src="photo-800.png"
  srcset="photo-480.png 480w, photo-800.png 800w, photo-1200.png 1200w, photo-1600.png 1600w"
  sizes="(min-width: 1100px) 1068px, calc(100vw - 32px)"
  width="1600" height="900"
  alt="Схема адаптивной сетки"
  loading="lazy">

srcset перечисляет файлы и их реальную ширину (w), sizes сообщает браузеру, какой ширины картинка будет в верстке. Браузер сам выбирает файл с учетом плотности экрана. В прогоне Chrome 153: телефон 375 px с плотностью 3x взял photo-1200.png (картинка шириной 343 CSS px = около 1029 физических точек), десктоп 1280 px с плотностью 1x — тоже photo-1200.png (1068 px). Выбор — решение браузера, спецификация разрешает ему учитывать кеш и сеть, так что на других браузерах файл может отличаться.

Атрибуты width и height задают пропорцию, и браузер резервирует место до загрузки — текст не прыгает. loading="lazy" не ставьте на главную картинку первого экрана: она должна грузиться сразу. Если на телефоне нужна другая картинка по содержанию (кадрированная), используют <picture> с <source media="...">.

Типовая ошибка: фиксированная ширина

Неверно — контейнер с жесткой шириной, как в макете под десктоп:

.container { width: 960px; margin: 0 auto; padding: 0 16px; }

Результат на телефоне 375 px (замер в Chrome 153): ширина документа 960 px при видимой области 375 px — появилась горизонтальная прокрутка, карточки остались в три колонки. На планшете 768 px — та же прокрутка.

Исправление — ограничивать ширину сверху, а не фиксировать:

.container { width: 100%; max-width: 1100px; margin: 0 auto; padding: 0 16px; }

После замены ширина документа совпадает с экраном на всех трех ширинах.

Как проверить адаптивность

  1. DevTools в Chrome, Firefox или Safari: режим устройства, ширины 320, 375, 768, 1024, 1280 и плавное сужение окна.
  2. Реальный телефон: эмуляция не повторяет все особенности мобильных браузеров (панели, которые прячутся при прокрутке, клавиатура, касания).
  3. Lighthouse в DevTools: он отметит отсутствие viewport, мелкие области нажатия и сдвиги макета.
  4. Контрольный вопрос для каждой ширины: нет ли горизонтальной прокрутки, читается ли текст без увеличения, нажимаются ли ссылки пальцем.

Если не получилось

Симптом Вероятная причина Что сделать
На телефоне сайт мелкий, как уменьшенный скриншот Нет meta viewport Добавить строку из примера в <head>
Горизонтальная прокрутка Элемент с фиксированной шириной в px, длинное слово или URL, таблица Найти в DevTools самый широкий элемент; max-width: 100%, overflow-wrap: anywhere, таблицу обернуть в блок с overflow-x: auto
Медиазапрос не срабатывает Опечатка, правило ниже по коду перебивает, выше специфичность Проверить во вкладке Styles, какое правило зачеркнуто
Картинка растянута или сплющена Задана ширина и фиксированная высота height: auto или aspect-ratio + object-fit: cover
Контент прыгает при загрузке У изображений нет width и height Добавить атрибуты с реальными пропорциями

Конструкторы вместо кода

Адаптивную страницу можно собрать без ручной верстки в конструкторах сайтов, например в Tilda: там готовые блоки уже адаптированы, а мобильную раскладку правят в редакторе. Adobe Muse из прежних подборок больше не развивается — Adobe прекратила его поддержку в 2020 году. Конструктор экономит время на типовом лендинге, но нестандартная раскладка, производительность и доработка кода там ограничены. Понимание viewport, Flexbox, Grid и медиазапросов нужно и в этом случае — чтобы понять, почему блок ломается на телефоне.

Выводы

  • Адаптивная страница начинается с <meta name="viewport" content="width=device-width, initial-scale=1">: без нее мобильный браузер раскладывает страницу примерно на 980 px и уменьшает.
  • Вместо фиксированной ширины — width: 100% + max-width, box-sizing: border-box, отступы по краям.
  • Flexbox — для строк элементов, Grid с auto-fit и minmax — для сеток, которые меняют число колонок без медиазапросов.
  • Брейкпоинты ставят там, где ломается содержимое; типичные 768 и 1024 px — ориентир, а не стандарт.
  • Для изображений — max-width: 100%, srcset + sizes, атрибуты width и height; проверка — DevTools плюс реальный телефон.

Где применяется / связь с практикой

Адаптивная верстка — базовый навык верстальщика и фронтенд-разработчика: практически любой коммерческий макет сдается в вариантах для телефона и десктопа. Следующий шаг после этого примера — сверстать по реальному макету из Figma лендинг из 4-5 секций (шапка, первый экран, сетка карточек, форма, подвал) и проверить его на трех ширинах.

Освойте тему на практике

Системно HTML, CSS, Flexbox, Grid и адаптив с проверкой домашних заданий разбирают на курсе «HTML/CSS». Попробовать формат и задать вопросы практикующим разработчикам можно на бесплатных открытых уроках Otus.

FAQ

Нужен ли JavaScript для адаптивного сайта?
Для раскладки нет: viewport, медиазапросы, Flexbox и Grid — это HTML и CSS. JavaScript нужен для поведения, например чтобы открывать и закрывать мобильное меню по кнопке.

Чем адаптивный сайт отличается от отдельной мобильной версии?
Отдельная мобильная версия — это другой набор страниц, часто на поддомене вида m.example.com, который приходится поддерживать параллельно. Адаптивный сайт — одни и те же адреса и HTML, меняется только CSS-раскладка.

Что лучше для адаптива: Bootstrap или чистый CSS?
Bootstrap дает готовую сетку и компоненты, это ускоряет типовые интерфейсы. Чистый Flexbox и Grid дают полный контроль и меньший объем CSS; понимать их полезно в любом случае, потому что сетка Bootstrap построена на тех же механизмах.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)