Статьи

Типовые ошибки в вёрстке сайта — как их исправить

25 июля 2026  |  14 мин чтения

Даже опытные верстальщики иногда допускают одни и те же ошибки. Проблема в том, что «вроде работает» — не значит «сделано правильно». Ошибки в вёрстке накапливаются: сегодня это мелкий косяк, а через месяц — сайт не индексируется, разваливается на мобильных и сыплет ошибками в консоли. Вот подробный разбор 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">&times;</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-тексты — это база, которую должен знать каждый верстальщик.