Вёрстка адаптивной мобильной версии сайта (Responsive Design)
Адаптивная вёрстка — не добавление max-width: 100% к изображениям. Это проектирование интерфейса, который работает корректно при ширине от 320px до 2560px+, с разными плотностями пикселей, разными методами ввода (касание vs мышь) и разными условиями сети.
Mobile-first: почему важен порядок
Mobile-first — базовые стили пишутся для мобильных, и расширяются через min-width медиазапросы. Альтернатива — Desktop-first с max-width — труднее поддерживать из-за нарастающего переопределения стилей.
/* Mobile-first: базовые стили для мобильных */
.card-grid {
display: grid;
grid-template-columns: 1fr; /* 1 колонка на мобильных */
gap: 16px;
}
/* Расширяем для планшета */
@media (min-width: 640px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
}
/* Расширяем для десктопа */
@media (min-width: 1024px) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
}
Точки останова (breakpoints)
Точки останова должны определяться контентом, а не устройствами. Нет смысла ориентироваться на конкретные модели iPhone — у каждого нового поколения другие размеры.
Типичная система:
| Имя | Значение | Контекст |
|---|---|---|
xs |
320px | Минимальный поддерживаемый размер |
sm |
640px | Большие телефоны, маленькие планшеты |
md |
768px | Планшеты в портретной ориентации |
lg |
1024px | Планшеты в альбомной, маленькие ноутбуки |
xl |
1280px | Ноутбуки, десктопы |
2xl |
1536px | Широкие экраны |
В CSS-переменных или Sass:
:root {
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
}
Viewport и единицы измерения
<!-- Обязательный мета-тег -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Без этого тега браузер на мобильном рендерит страницу как 980px широкую и масштабирует её — медиазапросы не работают как ожидается.
dvh вместо vh для мобильных:
/* vh не учитывает адресную строку браузера на мобильных */
.hero { min-height: 100vh; } /* Может быть обрезано */
/* dvh — динамический viewport height */
.hero { min-height: 100dvh; } /* Правильно на мобильных */
svh / lvh — small viewport height (со скрытой адресной строкой) и large viewport height (с видимой). dvh меняется при скролле, svh/lvh — фиксированные крайние значения.
Адаптивная типографика
/* Fluid typography без медиазапросов */
:root {
--text-base: clamp(1rem, 0.85rem + 0.75vw, 1.125rem);
--text-lg: clamp(1.125rem, 1rem + 0.625vw, 1.375rem);
--text-xl: clamp(1.375rem, 1rem + 1.875vw, 2rem);
--text-2xl: clamp(1.75rem, 1.25rem + 2.5vw, 2.75rem);
--text-3xl: clamp(2rem, 1.25rem + 3.75vw, 3.5rem);
}
body { font-size: var(--text-base); }
h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
clamp(min, preferred, max) — шрифт масштабируется плавно между min и max в зависимости от ширины экрана.
Касательная область (Touch targets)
На мобильных кликабельная область должна быть не менее 44×44px (Apple HIG) или 48×48px (Google Material). Визуальный размер может быть меньше:
.icon-btn {
width: 24px;
height: 24px;
position: relative;
cursor: pointer;
}
/* Увеличиваем зону касания не меняя визуал */
.icon-btn::before {
content: '';
position: absolute;
inset: -12px; /* Расширяем на 12px в каждую сторону = 48×48px зона */
}
Или через padding:
.nav__link {
display: inline-flex;
align-items: center;
padding: 12px 16px; /* Достаточная зона касания */
min-height: 44px;
}
Адаптивные изображения
<!-- art direction: разные кадры для разных размеров -->
<picture>
<source
media="(min-width: 1024px)"
srcset="hero-desktop.webp 1440w, hero-desktop-2x.webp 2880w"
sizes="100vw"
>
<source
media="(min-width: 640px)"
srcset="hero-tablet.webp 768w, hero-tablet-2x.webp 1536w"
>
<img
src="hero-mobile.webp"
srcset="hero-mobile.webp 390w, hero-mobile-2x.webp 780w"
alt="Hero image"
width="390"
height="520"
loading="eager"
fetchpriority="high"
>
</picture>
Для всех остальных изображений — srcset + sizes:
<img
src="product-400.webp"
srcset="product-400.webp 400w, product-800.webp 800w, product-1200.webp 1200w"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
alt="Название товара"
width="400"
height="400"
loading="lazy"
>
sizes говорит браузеру, какую долю экрана займёт изображение при данной ширине окна — это позволяет выбрать правильный файл до загрузки CSS.
Адаптивная навигация
Hamburger-меню для мобильных:
const Header: React.FC = () => {
const [menuOpen, setMenuOpen] = useState(false);
return (
<header className="header">
<a href="/" className="header__logo">Logo</a>
{/* Десктопная навигация */}
<nav className="header__nav header__nav--desktop" aria-label="Основная навигация">
<NavLinks />
</nav>
{/* Мобильный тоггл */}
<button
className="header__burger"
aria-label={menuOpen ? 'Закрыть меню' : 'Открыть меню'}
aria-expanded={menuOpen}
aria-controls="mobile-menu"
onClick={() => setMenuOpen(o => !o)}
>
<BurgerIcon open={menuOpen} />
</button>
{/* Мобильное меню */}
<nav
id="mobile-menu"
className={`header__nav header__nav--mobile ${menuOpen ? 'is-open' : ''}`}
aria-hidden={!menuOpen}
>
<NavLinks onClose={() => setMenuOpen(false)} />
</nav>
</header>
);
};
.header__nav--desktop {
display: none;
}
.header__burger {
display: flex;
}
@media (min-width: 1024px) {
.header__nav--desktop { display: flex; }
.header__burger { display: none; }
.header__nav--mobile { display: none !important; }
}
.header__nav--mobile {
position: fixed;
inset: 0;
background: var(--color-bg-primary);
transform: translateX(100%);
transition: transform 300ms ease;
}
.header__nav--mobile.is-open {
transform: translateX(0);
}
Производительность на мобильных
Мобильные сети медленнее десктопных. Критично:
-
Lazy loading для всего что ниже сгиба:
loading="lazy"на изображениях -
Font subsetting: для кириллицы хватает
latin + cyrillicподмножества, не нужен весь шрифт -
CSS containment:
contain: layout paintна секциях для ускорения рендеринга - Preload только для критических ресурсов первого экрана
<link rel="preload" href="hero-mobile.webp" as="image"
imagesrcset="hero-mobile.webp 390w, hero-mobile-2x.webp 780w"
imagesizes="100vw"
media="(max-width: 639px)">
Сроки
| Тип работы | Время |
|---|---|
| Адаптация готовой десктопной вёрстки под мобильные | 1–2 дня |
| Mobile-first вёрстка landing page | 2–3 дня |
| Mobile-first вёрстка корпоративного сайта | 4–6 дней |







