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

Компонент прошёл ревью, дизайнер поставил лайк, на макете всё пиксель-в-пиксель. Через две недели тот же компонент оказывается внутри модального окна с ограниченной шириной, рядом с длинным пользовательским текстом на немецком, и вёрстка разъезжается. Разбор частой картины: компонент выглядит готовым, потому что его проверяли только в идеальных условиях макета. Ниже — чек-лист того, что стоит проверить до выкатки, с ценой каждого пропущенного пункта.
Материал не про конкретный фреймворк. Приёмы одинаково применимы к React, Vue, Svelte или чистому CSS с БЭМ — речь про свойства самого компонента, а не про синтаксис его объявления. Тестирование логики и покрытие юнит-тестами — отдельная тема, здесь только вёрстка и поведение в вёрстке.
Изоляция: компонент не должен знать, где он стоит
Главная ошибка — компонент, который верстался в одном контексте и молча закладывается на него. Проверьте три вещи.
Внешние отступы принадлежат родителю, а не компоненту
Если у карточки товара стоит margin-bottom: 24px, она диктует расстояние до соседа. Поставьте две такие карточки в горизонтальный ряд — отступ снизу окажется лишним, а между ними отступа не будет вовсе. Внутренние отступы (padding) — часть компонента, внешние (margin) — забота контейнера. Практический признак проблемы: компонент выглядит правильно только в одном конкретном списке.
Компонент отвечает за то, как он выглядит внутри. За то, где он стоит и на сколько отстоит от соседей, отвечает тот, кто его поставил.
Ширина задаётся снаружи
Жёсткий width: 320px внутри компонента ломается в первом же узком контейнере. Компонент должен занимать ширину, которую ему дают: width: 100% плюс max-width, если есть верхняя граница по дизайну. Проверка: поместите компонент в блок шириной 200px и в блок на всю страницу. Если в одном из них перелом — ширина зашита неправильно.
Нет утечки стилей наружу и внутрь
Селектор вида .card h2 { margin: 0 } перекрасит любой заголовок, который окажется внутри карточки, включая вложенный чужой компонент. В React/Vue проблему снимают CSS-модули или scoped-стили; в чистом CSS — методология именования вроде БЭМ, где класс однозначно принадлежит своему блоку. Признак утечки: правка стилей одного компонента ломает вид другого.
Контент, которого нет на макете
Дизайнер рисует идеальные данные. Продакшен присылает пустые строки, тексты на 400 символов и картинки в неправильной пропорции. Проверьте компонент на четырёх крайних состояниях.
- Пусто. Что показывает список без элементов, аватар без картинки, поле без значения? Если ничего — пользователь видит пустую дыру и решает, что страница сломалась.
- Очень длинный текст. Заголовок в одну строку на макете и заголовок в пять строк в жизни. Решите заранее: перенос, обрезка через
text-overflow: ellipsisили троеточие после N строк через-webkit-line-clamp. - Очень много элементов. Список из трёх тегов и список из тридцати. Переносятся ли они, скроллятся, или ломают ширину контейнера.
- Ошибка и загрузка. У компонента, который тянет данные, минимум три состояния помимо успеха: загрузка, пусто, ошибка. Скелетон или спиннер на время загрузки — не украшение, а способ не показать пользователю прыгающий layout.
Дешёвый способ прогнать эти состояния — Storybook1 или любой аналог с изолированным рендером: заводите отдельную историю на каждое крайнее состояние и смотрите их рядом, а не выискиваете в реальном приложении.
Адаптив и переполнение
Компонент, который верстался на десктопе 1440px, проверяйте минимум на трёх ширинах: узкий телефон (около 360px), планшет, десктоп. Отдельно — на что реагирует компонент.
| Подход | На что реагирует | Когда выбирать |
|---|---|---|
| Media queries | Ширина окна (viewport) | Раскладка страницы целиком, глобальные брейкпоинты |
| Container queries | Ширина родительского контейнера | Переиспользуемый компонент, который живёт в разных по ширине местах |
| Внутренний flex/grid с wrap | Доступное место автоматически | Ряды карточек, теги, тулбары — когда достаточно «перенеси, если не влезает» |
Container queries (container-type: inline-size и правило @container) поддерживаются во всех актуальных версиях Chrome, Firefox и Safari — но если у вас в требованиях старые браузеры, сверьте поддержку на caniuse перед тем, как закладываться. Именно они решают классическую боль: один компонент в широкой колонке показывает картинку слева от текста, а в узком сайдбаре — сверху, без единого media query на уровне страницы.
Проверка на переполнение отдельно: временно добавьте всем блокам outline: 1px solid red или откройте DevTools и посмотрите, не появляется ли горизонтальный скролл. Частый виновник — фиксированная минимальная ширина у вложенного элемента и длинное слово без пробелов, которое лечится через overflow-wrap: break-word.
Доступность: минимум, который стоит копейки
Полный аудит доступности — большая работа, но базовый набор занимает минуты и отсекает грубые провалы.
- Клавиатура. Пройдите по компоненту клавишей Tab. Все интерактивные элементы должны получать фокус, а видимый фокус-ринг — не быть убран через
outline: noneбез замены. Кнопка, сделанная изdivс обработчиком клика, не фокусируется и не реагирует на Enter — используйтеbutton. - Семантика. Ссылка, которая ведёт на URL —
a. Действие на странице —button. Наоборот делать нельзя: скринридер и браузер обрабатывают их по-разному. - Контраст. Серый текст на белом фоне на макете выглядит стильно, но при контрасте ниже 4.5:1 для обычного текста его физически трудно читать. Проверяется встроенной проверкой контраста в DevTools.
- Альтернативы картинкам. Информативная картинка — с
alt. Декоративная — с пустымalt="", чтобы скринридер её пропустил.
Стабильность интерфейса под данными
Две вещи, которые чинятся до релиза дёшево, а после — только через жалобы пользователей.
Сдвиг раскладки (layout shift). Картинка без заданных размеров подгружается и сдвигает весь контент вниз в момент, когда пользователь целится в кнопку. Резервируйте место: aspect-ratio для медиа, фиксированная высота у контейнера скелетона. Метрика, которую стоит смотреть в проде, — Cumulative Layout Shift в Lighthouse или в поле через web-vitals.
z-index и слои. Выпадающее меню, которое верстали изолированно, в реальной странице оказывается под соседним блоком. Проблема почти всегда в том, что родитель создаёт новый контекст наложения (например, из-за transform или opacity), и локальный z-index компонента ничего не значит снаружи. Для оверлеев, тултипов и модалок разумнее рендерить их через портал в конец body, а не внутри компонента.
Чек-лист одной страницей
- Внешние отступы вынесены из компонента наружу.
- Ширина не зашита жёстко, компонент выживает в узком и широком контейнере.
- Стили не текут наружу и внутрь (CSS-модули, scoped или БЭМ).
- Проверены состояния: пусто, длинный текст, много элементов, загрузка, ошибка.
- Проверены три ширины экрана, нет горизонтального скролла.
- Tab проходит по всем интерактивным элементам, фокус виден.
- Семантика тегов верна:
aдля ссылок,buttonдля действий. - Контраст текста не ниже 4.5:1.
- Медиа зарезервировали место, нет прыжков раскладки.
- Оверлеи и модалки не тонут под соседними блоками.
Что делать дальше
Соберите этот список в шаблон pull request и требуйте отметку по каждому пункту при ревью компонентов — так проверка перестаёт зависеть от памяти конкретного разработчика. Следующий шаг после ручной проверки — автоматизация: визуальные регресс-тесты (Chromatic, Playwright со снимками) ловят разъехавшуюся вёрстку раньше, чем её увидит пользователь, а линтер вроде eslint-plugin-jsx-a11y отсекает часть проблем доступности прямо в редакторе. В проде смотрите на CLS и долю пользователей с узкими экранами в аналитике — они подскажут, какие компоненты чинить в первую очередь.
1 Storybook — инструмент для разработки и просмотра UI-компонентов в изоляции от приложения: каждый компонент и его состояние оформляются отдельной «историей» и открываются в браузере без запуска всего проекта.
Тендерный специалист 44-ФЗ / 223-ФЗ: Разбор PDF-документации закупки, поиск «заточек» под поставщика, чек-лист заявки.
Спросить за 20 ₽Источники: MDN: CSS container queries, web.dev: Cumulative Layout Shift (CLS)
Частые вопросы
Компонентный подход и React — это одно и то же?
У меня старые браузеры в требованиях, container queries не подойдут?
Обязательно ли поднимать Storybook ради проверки состояний?
Проверил по чек-листу, а на проде вёрстка всё равно разъехалась — почему?
Кто должен задавать внешние отступы, если компонент используется в десятках мест?
Достаточно ли для доступности пройтись по чек-листу из статьи?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.