Типовые ошибки в вёрстке сайта — как их исправить
Даже опытные верстальщики иногда допускают одни и те же ошибки. Проблема в том, что «вроде работает» — не значит «сделано правильно». Ошибки в вёрстке накапливаются: сегодня это мелкий косяк, а через месяц — сайт не индексируется, разваливается на мобильных и сыплет ошибками в консоли. Вот подробный разбор 15+ типовых ошибок с неправильным и правильным кодом.
1. Фиксированная ширина контейнера в px
Ошибка:
.container {
width: 1200px;
margin: 0 auto;
}
На экране 768px контент вылезает за правый край, появляется горизонтальный скролл. На экране 2560px контент стоит узкой колонкой посреди пустоты.
Как надо:
.container {
max-width: 1200px;
width: min(90%, 1200px);
margin-inline: auto;
}
Используйте max-width + относительную ширину. На мобильных контейнер займёт 90% экрана, на десктопе — не больше 1200px. margin-inline: auto — современная альтернатива margin: 0 auto (центрирование по горизонтали).
2. Отсутствие адаптива для изображений
Ошибка:
<img src="photo-1920.jpg" width="1920" height="1080">
На мобильных экранах 375px картинка уходит за границу. Пользователь вынужден скроллить вправо-влево.
Как надо:
img {
max-width: 100%;
height: auto;
}
Это базовое правило — оно должно быть в каждом CSS-проекте. Плюс используйте атрибуты width и height в HTML, чтобы браузер зарезервировал место под изображение до его загрузки — это предотвращает Cumulative Layout Shift (CLS). Даже с max-width: 100% и height: auto, указанные width/height помогают браузеру рассчитать пропорции.
Для респонсивных изображений используйте srcset:
<img src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 800px"
alt="Описание изображения">
3. Flex-контейнеры без flex-wrap
Ошибка:
.row {
display: flex;
gap: 1rem;
}
.item {
width: 33%;
}
При сужении экрана элементы сжимаются, читать невозможно. Если контента много — элементы выпадают за пределы родителя.
Как надо:
.row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.item {
flex: 1 1 280px;
}
flex: 1 1 280px — элемент может расти (1), может сжиматься (1), но его базовая ширина 280px. Как только в строке не хватает места — элемент переносится на новую строку. Три элемента на десктопе, один на телефоне — автоматически.
4. Слишком мелкий текст на мобильных
Ошибка:
body {
font-size: 14px;
}
На мобильных экранах 14px — это катастрофа для читаемости. Пользователь вынужден увеличивать масштаб.
Как надо:
body {
font-size: clamp(16px, 2.5vw, 18px);
}
/* Минимум 16px на мобильных, максимум 18px на десктопе, плавно растёт */
Правило: минимальный размер текста на мобильных — 16px. Это предотвращает масштабирование браузером (на iOS Safari текст меньше 16px автоматически увеличивается).
5. Проблемы с типографикой
Ошибка: игнорирование line-height, letter-spacing, отсутствие контроля над висячими строками (orphans/widows).
p {
font-size: 16px;
/* line-height не указан — браузер ставит ~1.2, текст слипается */
}
Как надо:
body {
line-height: 1.6; /* для читаемости основного текста */
letter-spacing: 0.02em;
}
h1, h2, h3 {
line-height: 1.2; /* для заголовков */
letter-spacing: -0.02em;
}
p {
orphans: 2; /* минимум строк в конце абзаца */
widows: 2; /* минимум строк в начале страницы */
text-wrap: pretty; /* избегать «рваных» строк */
}
Правильная типографика улучшает читаемость на 30–40% без изменения контента. Особенно text-wrap: pretty — современное CSS-свойство, которое балансирует строки в абзаце, убирая «слова-одиночки» на последней строке.
6. Проблемы с доступностью (Accessibility)
Ошибка 1: Низкая контрастность.
.link {
color: #999; /* на белом фоне — контраст ~2.5:1, ниже нормы WCAG AA (4.5:1) */
}
Как надо: проверяйте контраст через WebAIM Contrast Checker. Минимум 4.5:1 для обычного текста, 3:1 для крупного (18px+ bold или 24px+ regular).
Ошибка 2: Отсутствие aria-атрибутов.
<button class="close">×</button>
<!-- скринридер скажет просто «раз», непонятно, что это кнопка закрытия -->
Как надо:
<button class="close" aria-label="Закрыть модальное окно">
<i class="fas fa-times" aria-hidden="true"></i>
</button>
Ошибка 3: Отсутствие focus-стилей.
/* Часто пишут: */
a:focus { outline: none; }
/* Это ломает навигацию с клавиатуры — пользователь не видит, на каком элементе фокус */
Как надо:
a:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
border-radius: 4px;
}
/* :focus-visible показывает фокус только при клавиатурной навигации, а не при клике мышью */
Ошибка 4: Нет skip link (ссылки пропуска).
Пользователи скринридеров вынуждены каждый раз прослушивать всю навигацию, чтобы дойти до контента. Решение:
<a href="#main-content" class="skip-link">Перейти к контенту</a>
Спрячьте визуально, пока не в фокусе:
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--primary);
color: #fff;
padding: 8px 16px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
7. «Div-каша» вместо семантических тегов
Ошибка:
<div class="header">
<div class="nav">
<div class="menu">...</div>
</div>
</div>
<div class="content">
<div class="section">...</div>
</div>
Скринридер не понимает структуру страницы. Поисковик не понимает, где контент, где навигация. Сайт теряет в SEO и доступности.
Как надо:
<header>
<nav>
<ul><li>...</li></ul>
</nav>
</header>
<main>
<section>
<article>...</article>
</section>
</main>
<footer>...</footer>
Используйте: <header> для шапки, <nav> для навигации, <main> для уникального контента страницы, <section> для смысловых блоков, <article> для самостоятельных материалов (пост, новость, карточка), <aside> для боковой панели, <footer> для подвала.
8. SEO-ошибки: заголовки
Ошибка 1: Несколько H1 на странице.
<h1>Название компании</h1>
<h1>Наши услуги</h1>
Поисковик не понимает, какая тема страницы главная. На странице должен быть один H1 — он задаёт основную тему.
Ошибка 2: Нарушенная вложенность заголовков.
<h1>Главная</h1>
<h3>Подраздел</h3>
<!-- пропущен H2 — нарушена иерархия -->
Как надо: H1 → H2 → H3 (нельзя перескакивать). Как в книге: глава → раздел → подраздел.
<h1>Услуги веб-студии</h1>
<h2>Разработка сайтов</h2>
<h3>Лендинги</h3>
<h3>Интернет-магазины</h3>
<h2>SEO-продвижение</h2>
9. Производительность: тяжёлые анимации и layout thrashing
Ошибка: анимация свойств, которые вызывают перекомпоновку (reflow).
/* Анимация width, height, top, left — вызывает Reflow */
.box {
transition: width 0.3s, height 0.3s, top 0.3s, left 0.3s;
}
Браузеру каждый раз нужно пересчитывать геометрию всех элементов — это дёргает сайт, на мобильных даёт 15–25 FPS вместо 60.
Как надо: анимируйте только transform и opacity.
.box {
transition: transform 0.3s, opacity 0.3s;
}
.box:hover {
transform: scale(1.05);
opacity: 0.9;
}
Transform и opacity обрабатываются на GPU — 60 FPS даже на слабых устройствах. Для перемещения используйте translate, для масштабирования — scale.
Layout thrashing (принудительный reflow в JS):
// ПЛОХО — каждый цикл вызывает reflow
for (let i = 0; i < 100; i++) {
el.style.width = el.offsetWidth + 10 + 'px';
}
// ХОРОШО — читаем один раз, пишем много
const currentWidth = el.offsetWidth;
for (let i = 0; i < 100; i++) {
el.style.width = currentWidth + i * 10 + 'px';
}
Чтение свойств offsetWidth, offsetHeight, getComputedStyle «сбрасывает» очередь изменений и заставляет браузер принудительно пересчитывать layout. Делайте все чтения до всех записей.
10. Ошибки с изображениями: alt, aspect-ratio, форматы
Ошибка 1: Пропущенный alt.
<img src="logo.png">
<!-- скринридер прочитает название файла — бесполезно -->
Как надо:
<img src="logo.png" alt="Web Innovator — разработка сайтов">
<!-- для декоративных картинок: alt="" (пустой, но обязательный атрибут) -->
<img src="bg-texture.png" alt="" role="presentation">
Ошибка 2: Неправильный aspect-ratio (кладбище пропорций).
Изображения с разными пропорциями в сетке карточек ломают вёрстку. Решение:
.card-image {
aspect-ratio: 16 / 9;
object-fit: cover;
width: 100%;
height: auto;
}
aspect-ratio заставляет все изображения быть одинаковых пропорций, object-fit: cover обрезает лишнее без искажений.
Ошибка 3: Старые форматы.
JPEG для скриншотов (артефакты), PNG для фото (5-10 МБ). Используйте WebP — в среднем на 25–35% легче JPEG при том же качестве, поддерживается всеми современными браузерами. Для логотипов с прозрачностью — SVG вместо PNG.
11. CSS-архитектура: specificity wars и !important
Ошибка: бесконтрольная специфичность и !important.
.header .nav .menu .item a { color: blue; }
.header .nav .menu .item .highlight a { color: red !important; }
/* Ещё один селектор — и придётся использовать !important снова */
Чем больше специфичность, тем сложнее переопределить стили. !important — костыль, который делает код неподдерживаемым.
Как надо: используйте методологии (БЭМ, SMACSS).
/* БЭМ: блок__элемент--модификатор */
.menu__link { color: blue; }
.menu__link--active { color: red; }
/* Все селекторы имеют одинаковую специфичность — никаких !important */
Ошибка: магические числа.
.card { margin-top: 17px; } /* Почему 17? Никто не знает */
Используйте CSS-переменные и единую систему отступов:
:root {
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 2rem;
--space-xl: 4rem;
}
.card { margin-top: var(--space-md); }
12. Ошибки в формах
Ошибка 1: Отсутствие label.
<input type="text" placeholder="Ваше имя">
<!-- нет label — скринридер не понимает, что это за поле -->
Как надо:
<label for="name">Ваше имя</label>
<input type="text" id="name" name="name" placeholder="Введите имя">
Каждый input должен иметь label с for, указывающим на id инпута. Это улучшает доступность и UX (клик по label фокусирует поле).
Ошибка 2: Неправильные типы input.
<input type="text" name="email">
<input type="text" name="phone">
Как надо:
<input type="email" name="email" autocomplete="email">
<input type="tel" name="phone" autocomplete="tel" inputmode="numeric">
Правильные типы: email (проверка формата + своя клавиатура), tel (цифровая клавиатура), number (цифры со стрелками), url (клавиатура с / и .com), date (календарь).
13. Игнорирование состояний :hover и :focus на сенсорных устройствах
Ошибка:
.card:hover {
transform: scale(1.02);
box-shadow: 0 8px 30px rgba(0,0,0,0.15);
}
На телефоне после нажатия эффект «залипает» — карточка остаётся увеличенной, пока пользователь не ткнёт в другое место. На телефонах нет курсора, hover не имеет смысла.
Как надо:
@media (hover: hover) {
.card:hover {
transform: scale(1.02);
box-shadow: 0 8px 30px rgba(0,0,0,0.15);
}
}
@media (hover: none) {
.card:active {
transform: scale(0.98);
}
}
@media (hover: hover) — применяет стили только к устройствам с курсором (десктопы, ноутбуки). @media (hover: none) — для сенсорных экранов.
14. Забытые теги title и meta description
Ошибка:
<head>
<meta charset="UTF-8">
<!-- нет title -->
</head>
В результатах поиска: «Untitled Document» или URL страницы. Пользователь не кликнет.
Как надо:
<title>Как сделать форму обратной связи | Web Innovator</title>
<meta name="description" content="Подробный гайд: 5 способов сделать форму обратной связи на сайте без бэкенда. Netlify Forms, EmailJS, Formspree, PHP-обработчик.">
Title — 50–60 символов, включает ключевое слово и бренд. Description — 150–160 символов, описывает содержание страницы и призывает кликнуть. Каждая страница сайта должна иметь уникальные title и description.
15. Как отлаживать ошибки: Chrome DevTools
Chrome DevTools — главный инструмент верстальщика. Вот несколько функций, которые экономят часы:
Вкладка Elements (F12):
— Инспектируйте элемент (правый клик → Inspect)
— Смотрите применённые стили и их источник (какой файл + строка)
— Включайте/выключайте стили в реальном времени
— Смотрите box-shadow: padding, border, margin — наглядная схема элемента
Вкладка Coverage:
Показывает, какой процент CSS и JS реально используется на странице. Откройте Coverage (Ctrl+Shift+P → Coverage → Record), обновите страницу — увидите красные полосы неиспользуемого кода. Обычно 40–70% CSS не применяется.
Вкладка Performance:
Записывает взаимодействие с страницей. Показывает: какие функции тормозят, сколько времени занимает layout/paint/composite, есть ли long tasks (>50 мс), вызывающие «подтормаживания».
Вкладка Lighthouse:
Автоматический аудит: Performance, Accessibility, Best Practices, SEO. Даёт конкретные рекомендации с кодом. Запустите Lighthouse на свою страницу — получите отчёт с оценкой и списком ошибок.
Совет: Mobile View (Ctrl+Shift+M)
Эмулирует любое устройство. Проверяйте сайт на iPhone SE (375px), iPad (768px), десктопе (1920px) — это покрывает 95% пользователей.
Большинство этих ошибок ловится за 5 минут в Chrome DevTools. Откройте → Mobile View → Coverage → Performance → и смотрите, что не так. Но лучше не допускать их на этапе вёрстки: семантическая разметка, методология CSS, правильные типы input и alt-тексты — это база, которую должен знать каждый верстальщик.