Как адаптировать сайт под мобильные устройства

Начните адаптацию сайта с viewport: он задает ширину области отображения. Затем уберите жесткие ширины блоков, настройте изображения, текст и формы. Обязательный минимум – страница без горизонтальной прокрутки, читаемый текст и удобные кнопки. Примеры HTML и CSS помогут исправить эти проблемы последовательно.

Что значит «сайт адаптирован»

Адаптивная верстка подстраивает расположение блоков под доступную ширину окна. Одна страница работает на смартфоне, планшете и большом мониторе. Проверьте три признака готовности:

  • Контент помещается по ширине экрана без общей горизонтальной прокрутки.
  • Основной текст читается без увеличения страницы пальцами.
  • Посетитель нажимает кнопки и ссылки, не задевая соседние элементы.

Отдельная прокрутка широкой таблицы или листинга допустима внутри блока. Но она не должна растягивать всю страницу.

Мобильная версия сайта важна и для поисковой индексации. Google использует мобильную версию страницы для индексирования и ранжирования. Сохраняйте на ней основной текст, изображения и доступ к действиям.

Не удаляйте и не скрывайте важное содержимое ради узкого экрана. Скрытые характеристики товара, условия доставки или кнопки лишают посетителя нужной информации. Перестраивайте блоки в одну колонку, переносите строки и сохраняйте доступ к действиям.

Не создавайте поддомен m. только ради исправления верстки. Отдельная версия добавляет задачи синхронизации контента, адресов и перенаправлений. Для обычного сайта удобнее поддерживать одну адаптивную страницу.

Перед изменениями сохраните копию шаблона и таблицы стилей. Работайте на тестовой странице или копии сайта. Примеры CSS добавляйте после правил, которые хотите переопределить; классы заменяйте классами своих блоков. Если изменение ломает страницу, верните сохраненные файлы.

Шаг 1. Правильный viewport

Сначала откройте HTML-шаблон страницы и найдите элемент <head>. Добавьте в него настройку viewport:

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

Если такой тег уже есть, замените его содержимое. Не оставляйте несколько противоречащих друг другу настроек.

Значение width=device-width связывает ширину области верстки с шириной устройства. Значение initial-scale=1 задает начальный масштаб страницы. Без подходящего viewport мобильный браузер может показывать уменьшенную настольную страницу.

Поэтому viewport проверяют раньше медиа-запросов: неверная область верстки мешает их ожидаемому срабатыванию.

  • width=700 фиксирует виртуальную ширину вместо подстройки под экран. На узком телефоне браузер может уменьшать страницу, делая текст мелким.
  • user-scalable=no запрещает масштабирование в браузерах, которые соблюдают это ограничение. Пользователь теряет способ увеличить мелкий текст.
  • maximum-scale=1 тоже ограничивает увеличение там, где браузер учитывает настройку. Не добавляйте его ради сохранения внешнего вида.

После сохранения откройте страницу на смартфоне и обновите ее. Текст должен перестать выглядеть уменьшенной копией настольной версии. Если блоки по-прежнему выходят за экран, исправляйте их ширины.

Сам viewport не делает жесткую сетку адаптивной.

Шаг 2. Медиа-запросы и брейкпоинты

Медиа-запрос применяет CSS при выполнении условия, например при узком окне. Брейкпоинт – ширина, на которой вы меняете расположение элементов.

Для исправления существующей страницы подойдет условие max-width. Оно включает правила при ширине не больше указанной:

@media (max-width: 48rem) {
  .layout {
    display: block;
  }

  .sidebar {
    margin-top: 1.5rem;
  }
}

Для новой верстки используйте mobile-first: сначала задайте узкий одноколоночный макет. Затем расширяйте его через min-width:

.layout {
  display: grid;
  gap: 1.5rem;
}

.layout > * {
  min-width: 0;
}

@media (min-width: 48rem) {
  .layout {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  }
}

Здесь 48rem – пример точки переключения, не универсальный стандарт.

Сужайте окно, пока меню, карточки или заголовки не начнут мешать друг другу. Поставьте брейкпоинт перед этим моментом.

Список моделей телефонов для этого не нужен. Одна и та же модель меняет доступную ширину при повороте. После правки медленно растяните окно: макет должен оставаться удобным между точками переключения.

Шаг 3. Сетка без фиксированных ширин

Жесткая ширина основного контейнера часто создает горизонтальную прокрутку. Замените width: 1200px сочетанием гибкой ширины и верхнего ограничения:

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
}

.container {
  width: 100%;
  max-width: 75rem;
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 2rem);
}

Теперь контейнер занимает доступное место, но не растягивается бесконечно. Правило border-box включает отступы и рамки в заданную ширину.

Grid может сам выбирать число колонок карточек:

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: 1rem;
}

.cards > * {
  min-width: 0;
}

Карточка стремится занять минимум 16rem, но не шире контейнера. Значение 1fr распределяет оставшееся пространство между колонками. В узком окне карточки выстроятся друг под другом.

Для строки элементов с переносом подойдет flexbox:

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.actions > * {
  max-width: 100%;
}

Используйте проценты для долей контейнера, rem – для размеров относительно корневого шрифта. Функция clamp() задает минимум, гибкое значение и максимум.

Пиксели подходят для рамок и отдельных размеров, но фиксированная ширина всей страницы мешает адаптации.

Шаг 4. Изображения

Добавьте в таблицу стилей ограничение для обычных изображений:

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

Картинка уменьшится вместе с контейнером и сохранит пропорции.

Это правило не повышает четкость маленького исходника при увеличении.

Подготовьте несколько размеров фотографии и перечислите их в srcset. Атрибут sizes описывает ширину картинки в макете. Браузер учитывает ее вместе с плотностью экрана при выборе файла.

<img
  src="/images/product-640.jpg"
  srcset="/images/product-640.jpg 640w, /images/product-1280.jpg 1280w"
  sizes="(min-width: 48rem) 50vw, 100vw"
  width="1280"
  height="960"
  loading="lazy"
  alt="Настольная лампа на деревянной полке">

Замените пути собственными файлами с указанной реальной шириной. Пример предполагает половину окна на широком экране и полную ширину на узком. Если у вас другой макет, измените sizes.

Атрибуты width и height помогают заранее зарезервировать пропорции изображения. Отложенную загрузку loading="lazy" оставляйте для картинок вне первого экрана.

Главную фотографию в начале страницы не откладывайте.

Элемент <picture> позволяет предложить другой формат или кадрирование. Например, подготовьте WebP и запасной JPEG:

<picture>
  <source srcset="/images/product.webp" type="image/webp">
  <img src="/images/product.jpg" width="800" height="600"
       alt="Настольная лампа крупным планом">
</picture>

Для разных плотностей при одинаковом размере отображения можно использовать дескрипторы 1x и 2x. Не смешивайте их с дескрипторами ширины w в одном srcset. Для AVIF подготовьте отдельный source с типом image/avif.

Фоновые изображения требуют другого правила:

.banner {
  min-height: 12rem;
  background: url("/images/banner.jpg") center / cover no-repeat;
}

.logo-panel {
  min-height: 8rem;
  background: url("/images/logo.svg") center / contain no-repeat;
}

Значение cover заполняет область, обрезая часть картинки при несовпадении пропорций. Значение contain сохраняет изображение целиком, но может оставлять свободное место. Проверьте, что обрезка не скрыла лицо, подпись или товар.

Шаг 5. Текст и длинные строки

Начните с основного текста размером 16px и достаточного межстрочного интервала. Для полей ввода тоже задавайте размер шрифта от 16px.

html {
  font-size: 100%;
}

body {
  font-size: 1rem;
  line-height: 1.5;
}

.article {
  max-width: 65ch;
  margin-inline: auto;
  overflow-wrap: break-word;
  hyphens: auto;
}

.article h2 {
  font-size: clamp(1.5rem, 4vw, 2rem);
}

Ограничение 65ch не дает строке растянуться на весь монитор. Оцените удобство чтения с вашим шрифтом и при необходимости измените ограничение.

Свойство overflow-wrap: break-word переносит длинную непрерывную строку, если она не помещается. В старом CSS встречается word-wrap – историческое имя, сохраненное как совместимый псевдоним. Для нового кода используйте overflow-wrap.

Длинная строка выходит за границу узкого экрана без переноса

Для автоматических переносов слов укажите язык документа:

<html lang="ru">

Работа hyphens: auto зависит от доступного словаря браузера. Для кода удобнее отдельная горизонтальная прокрутка, сохраняющая форматирование:

pre {
  max-width: 100%;
  overflow-x: auto;
}

.identifier {
  overflow-wrap: anywhere;
  letter-spacing: 0.08em;
}

Класс identifier подойдет для длинного идентификатора. Межбуквенный интервал визуально раздвинет символы без добавления пробелов в текст. Скопируйте идентификатор и вставьте его в текстовый редактор для сравнения. Вставленный идентификатор должен совпадать с исходным.

Шаг 6. Формы на мобильных

Подберите тип поля под ожидаемое значение. Браузер сможет предложить подходящую клавиатуру и базовую проверку ввода.

ЗначениеАтрибутыНазначение
Почтаtype="email" autocomplete="email"Ввод адреса электронной почты
Телефонtype="tel" autocomplete="tel"Телефонная клавиатура
Адрес сайтаtype="url" autocomplete="url"Ввод URL
Количествоtype="number" min="1" step="1"Числовое значение с ограничениями
Почтовый индексtype="text" inputmode="numeric" autocomplete="postal-code"Строка с цифровой клавиатурой

Не используйте type="number" для телефонов и идентификаторов. Это строки, в которых важны ведущие нули и другие символы.

Атрибут inputmode подсказывает клавиатуру, но не проверяет содержимое.

В форме заявки подпишите каждое поле через label. Подсказка внутри поля не заменяет постоянную подпись:

<form class="request-form" action="/request" method="post">
  <label for="customer-name">Ваше имя</label>
  <input id="customer-name" name="name" type="text"
         autocomplete="name" autocapitalize="words" required="required">

  <label for="customer-email">Электронная почта</label>
  <input id="customer-email" name="email" type="email"
         autocomplete="email" autocapitalize="none" required="required">

  <button type="submit">Отправить заявку</button>
</form>

Замените /request адресом существующего обработчика заявок вашего сайта. HTML сам не сохраняет заявку: обработчик должен принять поля и показать результат отправки.

.request-form {
  display: grid;
  gap: 0.75rem;
  max-width: 30rem;
}

.request-form input,
.request-form textarea,
.request-form select {
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  padding: 0.75rem;
  font-size: 16px;
}

Атрибут autocapitalize="words" подсказывает начинать слова имени с заглавной буквы. Не преобразовывайте введенное имя принудительно: пользователь может предпочитать другое написание.

Не блокируйте кнопку отправки навсегда после первого нажатия. Потеря связи или прерывание работы браузера звонком могут оставить форму без ответа. При асинхронной отправке возвращайте кнопку в активное состояние после ошибки или тайм-аута. Одного ожидания ответа сервера без ограничения времени недостаточно.

Обычной форме из примера блокировка вообще не нужна. Повторную обработку одной заявки предотвращайте на сервере. Пройдите форму на телефоне. Заполните поля, исправьте ошибочный email и отправьте тестовую заявку.

Успешное сообщение должно соответствовать реально принятой заявке.

Шаг 7. Тап-зоны и навигация

Делайте область нажатия кнопок достаточно крупной для пальца. Практический ориентир – 44–48 CSS-пикселей по каждой стороне. В примере используется 48px; это размер области нажатия, сама иконка может быть меньше.

.tap-target {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 48px;
  min-height: 48px;
  padding: 0.5rem 0.75rem;
}

.navigation {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

Оставляйте промежутки между самостоятельными ссылками меню. Проверьте навигацию одной рукой: соседняя ссылка не должна срабатывать случайно.

Для компактного раскрывающегося меню можно обойтись без JavaScript. Нативные элементы details и summary сохраняют управление с клавиатуры:

<details class="mobile-menu">
  <summary>☰ Меню</summary>
  <nav aria-label="Основная навигация">
    <a href="/">Главная</a>
    <a href="/catalog">Каталог</a>
    <a href="/contacts">Контакты</a>
  </nav>
</details>
.mobile-menu summary {
  min-height: 48px;
  padding: 0.75rem;
  cursor: pointer;
}

.mobile-menu nav {
  display: grid;
  gap: 0.5rem;
}

.mobile-menu a {
  display: block;
  padding: 0.75rem;
}

Замените адреса ссылок реальными разделами сайта. Если делаете собственный бургер через кнопку, обновляйте ее aria-expanded при раскрытии. Скрытые пункты не должны получать клавиатурный фокус.

Фиксированная шапка постоянно занимает часть небольшого экрана. При увеличении текста или открытой клавиатуре она может перекрывать содержимое. Если шапка уже использует position: fixed, отключите фиксацию для узкого окна:

@media (max-width: 48rem) {
  .site-header {
    position: static;
  }

  .page-content {
    padding-top: 0;
  }
}

Обнуляйте верхний отступ только тогда, когда он компенсировал высоту фиксированной шапки. Иначе вы уберете нужное расстояние между блоками.

Для полноэкранной панели учитывайте подвижную адресную строку браузера:

.menu-panel {
  box-sizing: border-box;
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
}

Единица dvh следует за динамической высотой видимой области. Первое объявление оставляет запасной вариант для браузера без ее поддержки. Откройте панель на телефоне и спрячьте адресную строку прокруткой. Последний пункт меню должен оставаться доступным.

Шаг 8. Скорость

Адаптивная сетка не уменьшает вес загружаемых файлов сама по себе.

Подготовьте изображения подходящих размеров, сожмите их и удалите ненужные скрипты. Особенно внимательно проверьте виджеты, которые работают на каждой странице.

Для показа текста во время загрузки шрифта используйте CSS:

@font-face {
  font-family: "SiteText";
  src: url("/fonts/site-text.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: "SiteText", system-ui, -apple-system,
               "Segoe UI", sans-serif;
}

Укажите путь к своему файлу шрифта и подходящее начертание. Значение swap разрешает сначала показать текст запасным шрифтом. После загрузки браузер заменит его основным.

Для такой замены WebFont Loader и подобные JavaScript-загрузчики не нужны. Системный стек дает запасной шрифт без дополнительной загрузки. Если фирменная гарнитура не нужна, оставьте только системный стек.

Подбирайте запасной шрифт с похожими пропорциями: сильное отличие меняет переносы строк и сдвигает блоки после загрузки.

В Core Web Vitals оценивают загрузку, отзывчивость и стабильность страницы:

  • LCP отражает время появления крупнейшего видимого блока содержимого. Не откладывайте загрузку главного изображения первого экрана.
  • INP отражает задержку реакции на взаимодействия. Уменьшайте длительные задачи JavaScript, особенно при открытии меню и работе формы.
  • CLS отражает неожиданные сдвиги макета. Резервируйте место под изображения, рекламу и встраиваемые блоки.

Поиск учитывает показатели удобства страницы, но скорость не заменяет полезный контент. После оптимизации сравните загрузку одной страницы при одинаковых условиях. Проверьте также, что удаление скриптов не сломало форму и меню.

Как проверить результат

Откройте инструменты разработчика браузера через контекстное меню страницы, выбрав просмотр элемента. В панели найдите переключатель с изображением телефона и планшета. Он включает режим устройства, где можно менять ширину области просмотра.

Если переключатель найти не получается, сужайте обычное окно браузера. Так вы проверите сетку и брейкпоинты, хотя клавиатуру и касания этот способ не воспроизведет.

  1. Меняйте ширину плавно, включая промежуточные значения. Следите за меню, карточками, заголовками и нижней частью страницы.
  2. Откройте Lighthouse в инструментах разработчика, если он доступен в вашем браузере. Запустите мобильный аудит производительности и доступности для открытой страницы.
  3. Откройте тот же адрес на настоящем смартфоне. Пройдите путь от первого экрана до целевого действия.
  4. Проверьте страницу при повороте телефона. Меню, форма и всплывающие панели должны сохранять доступность.
  5. Увеличьте страницу пальцами. Убедитесь, что шапка не перекрыла текст и элементы управления.

Lighthouse помогает найти причины задержек, но не заменяет проверку касаний. Его лабораторный отчет также не равен опыту всех посетителей сайта.

Для поиска общей горизонтальной прокрутки выполните в консоли DevTools:

document.documentElement.scrollWidth >
  document.documentElement.clientWidth

Значение true указывает, что содержимое шире видимой области. Найти подозрительные элементы поможет дополнительная команда:

const viewportWidth = document.documentElement.clientWidth;

console.log(
  [...document.querySelectorAll("body *")].filter((element) => {
    const rect = element.getBoundingClientRect();
    return rect.right > viewportWidth || rect.left < 0;
  })
);

Список показывает кандидатов, включая намеренно вынесенные за экран элементы. Выберите подозрительный элемент в консоли и посмотрите его CSS. Проверьте фиксированную ширину, min-width, отступы и длинные строки.

Не скрывайте проблему общим overflow-x: hidden у страницы. Это может обрезать контент, оставляя причину переполнения на месте.

  • Viewport содержит width=device-width, initial-scale=1, увеличение страницы разрешено.
  • На странице нет общей горизонтальной прокрутки.
  • Картинки сохраняют пропорции и не растягивают контейнеры.
  • Основной текст доступен полностью, длинные строки переносятся.
  • Поля вызывают подходящую клавиатуру, подписи остаются видимыми.
  • После ошибки отправки пользователь может повторить попытку.
  • Меню работает пальцем и клавиатурой, шапка не перекрывает контент.
  • При загрузке шрифтов текст появляется, блоки не скачут заметно.

Совет от нашей редакции: завершайте проверку реальным действием посетителя. Найдите товар, заполните заявку или откройте нужный раздел на телефоне. Такой проход обнаруживает проблемы, которые не видны на уменьшенном скриншоте.

Комментарии

Оставьте свой комментарий
IR
Irina R.

Статья интересная, но если вы не специалист, то самостоятельно разобраться во всех тонкостях адаптации сайта под мобильные устройства довольно тяжело.

Г
Григорий

Хорошая статья, спасибо

ДР
Денис Р.

Одна из лучших статей по адаптации!

XX
Xxraduxx X.

Zdrastvuite, pomoghite pojalusta ubrati problemu adaptinosti saita.
Vseo rabotaet normalino, krome tovo chto shirina bloka saita bolishe cem kontent, toesti poevleaetsa prokrutka po gorizontali, mojete posmotreti esli umenishiti adress saita v brauzere ili zaiti cerez mobilnik, Spasibo za ranee.

A
Aleks

Скажите возниклв такая ситуация с рекламой гугла. Установил её на сайт( блоки адаптивные), но вот из-за кривой верстки сайта на компе она помещается и хорошо смотрится на странице, а вот при просмотре с мобильного не хочет адаптироваться и выходит за правую сторону экрана практически в половину. Чем это можно поправить?

TN
The N.

Я перепробовал целую кучу способов меты вьюпорт и ничего не работало, а тут о чудо - работает! Спасибо)

ИС
Иван С.

Хорошая шпаргалка!)

А
Андрей

Не понял как выбрать ширину. Допусти есть фиксированный дизайн с шириной 960px
нужно написать
_meta name=viewport content="width=1000"_ ?

АК
Андрей К.

Хорошая статья. Благодарю.