Таблицы в вёрстке: когда они уместны, а когда ломают макет
Разбираем, для каких данных <table> — правильный выбор, а где он делает разметку хрупкой, недоступной и неотзывчивой. С критериями, по которым решать в спорных случаях.

Раскладка страницы на <table> умерла в середине 2000-х вместе с версткой сайтов «резиновыми» контейнерами на float. Но сам тег никуда не делся, и путаница осталась: одни верстальщики боятся таблиц как признака устаревшего кода, другие суют <table> в карточки товаров и формы, где ему не место. Разберём один вопрос: для каких данных таблица — правильный семантический выбор, а где она делает разметку хрупкой и недоступной. Раскладку страницы через таблицы не рассматриваем — это закрытая тема, здесь речь только про содержимое.
Критерий один: это табличные данные или нет
Таблица уместна, когда у вас есть данные с двумя измерениями — строки и столбцы, где пересечение ячейки имеет смысл. Классика: прайс-лист, расписание, сравнение тарифов, финансовый отчёт, спецификация товара «параметр — значение». Если вы можете сказать «в строке X, столбце Y находится значение Z» и это осмысленно — перед вами таблица.
Проверка от обратного. Если данные — это список одинаковых сущностей (карточки товаров, посты, отзывы), которые на широком экране идут в несколько колонок, а на узком перестраиваются в одну — это не таблица. Это список (<ul>) или набор статей (<article>), разложенный сеткой через CSS Grid или Flexbox. Признак: если ячейки должны переноситься и менять число колонок в зависимости от ширины — таблица тут будет драться с вами на каждом брейкпоинте.
Таблица описывает отношение между строкой и столбцом. Если такого отношения нет, а есть просто «несколько блоков рядом» — вы используете <table> как инструмент раскладки, и это ошибка, даже если визуально всё легло ровно.
Где таблица — единственно правильный выбор
- Сравнение тарифов и планов. Столбцы — планы, строки — характеристики. Скринридер объявит «Тариф Про, объём диска, 100 ГБ» — именно то, что нужно.
- Финансовые и числовые данные. Отчёты, выписки, статистика. Пользователь сканирует по столбцам, сравнивает значения по вертикали.
- Расписания. День недели × время — двумерная структура в чистом виде.
- Спецификации «свойство — значение». Технические характеристики товара. Здесь, правда, есть альтернатива —
<dl>, о ней ниже.
Пограничные случаи и как их решать
Три ситуации, где спор возникает чаще всего.
Характеристики товара: table или dl
«Процессор — Intel i5», «Оперативная память — 16 ГБ» — это пары ключ-значение в одну колонку. Здесь <table> избыточен: у вас нет второго измерения, только список пар. Семантически точнее <dl> (список определений):
<dl>
<dt>Процессор</dt>
<dd>Intel Core i5-12400</dd>
<dt>Оперативная память</dt>
<dd>16 ГБ DDR4</dd>
</dl>
Таблица оправдана, если вы сравниваете характеристики нескольких товаров бок о бок — тогда появляется второе измерение и столбцы начинают что-то значить.
Форма с полями, выровненными в две колонки
Соблазн большой: «подпись слева, поле справа» ложится в таблицу идеально. Не делайте этого. Форма — это <form> с <label> и полями, а выравнивание в колонки — задача CSS Grid. Таблица здесь ломает навигацию с клавиатуры и мешает скринридеру связать подпись с полем через for/id. На узком экране таблица с формой тоже развалится первой.
Календарь
Месячный календарь — законная таблица: строки-недели, столбцы-дни недели. Пересечение «вторая неделя, среда» осмысленно. А вот лента событий или список дел на день — это <ul>, не таблица.
| Данные | Правильная разметка | Почему не таблица |
|---|---|---|
| Сравнение тарифов | <table> | — |
| Сетка карточек товаров | <ul> + CSS Grid | Число колонок меняется от ширины |
| Характеристики одного товара | <dl> | Нет второго измерения |
| Форма ввода | <form> + Grid | Ломает связь label с полем |
| Месячный календарь | <table> | — |
| Финансовый отчёт | <table> | — |
Правильная таблица: разметка, которую часто забывают
Если случай ваш — таблица нужна, — соберите её полностью, а не из голых <tr> и <td>. Минимально грамотная таблица включает заголовки и подпись:
<table>
<caption>Тарифы на облачное хранилище, 2024</caption>
<thead>
<tr>
<th scope="col">Тариф</th>
<th scope="col">Диск</th>
<th scope="col">Цена в месяц</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Базовый</th>
<td>100 ГБ</td>
<td>199 ₽</td>
</tr>
<tr>
<th scope="row">Про</th>
<td>1 ТБ</td>
<td>499 ₽</td>
</tr>
</tbody>
</table>
Что здесь важно и что чаще всего пропускают:
<caption>— подпись таблицы. Скринридер объявит её при входе в таблицу, пользователь поймёт, куда попал.<th scope="col">— заголовки столбцов. Атрибутscopeявно говорит, что этот заголовок относится к колонке.<th scope="row">— первая ячейка строки как заголовок строки. Тогда скринридер прочитает «Про, диск, 1 ТБ», а не просто «1 ТБ» без контекста.<thead>и<tbody>— разделение шапки и тела. Помогает и стилям, и печати с повтором шапки на каждой странице.
Отзывчивость: главная боль таблиц
Таблица не переносит ячейки и не сжимается бесконечно — при нехватке ширины она либо ломает вёрстку, либо выпихивает контент за экран. Три рабочих подхода на узких экранах, у каждого своя цена.
- Горизонтальный скролл. Оборачиваете таблицу в контейнер с
overflow-x: auto. Просто, ничего не ломает, данные остаются таблицей. Цена — пользователь скроллит вбок, что на телефоне неудобно для широких таблиц. - Перестройка в карточки. На узком экране через CSS каждая строка превращается в блок «подпись: значение». Читается лучше скролла, но требует дублировать подписи столбцов (через
data-*атрибуты и::before) и ломается на сложных таблицах с объединёнными ячейками. - Скрытие второстепенных столбцов. Прячете менее важные колонки на узких экранах. Быстро, но пользователь теряет часть данных — годится, только если эти столбцы правда второстепенны.
Простейший вариант со скроллом, который стоит применять по умолчанию:
.table-wrap {
overflow-x: auto; /* горизонтальный скролл при нехватке ширины */
-webkit-overflow-scrolling: touch;
}
.table-wrap table {
min-width: 480px; /* ниже этой ширины включается скролл */
border-collapse: collapse;
}
Типичные ошибки, которые всплывают на ревью
<table>для раскладки блоков. Признак — нет<th>, ячейки содержат разнородный контент, структура нужна ради выравнивания. Переносите на Grid или Flexbox.- Таблица без единого
<th>. Если у данных нет заголовков — скорее всего, это не таблица, а список. - Пустые ячейки для отступов. Наследие старой вёрстки. Отступы — это
padding, а не колонка-распорка. - Вложенные таблицы. Почти всегда знак, что таблицей чинят раскладку. Разнесите на отдельные структуры.
- Отсутствие
scopeна сложных таблицах. Для таблиц с объединёнными ячейками одногоscopeмало — там нужныid/headers, иначе скринридер запутается в связях.
Что делать дальше
Перед тем как поставить <table>, задайте себе один вопрос: имеет ли смысл фраза «в строке X, столбце Y — значение Z»? Если да — таблица, собранная с caption, thead и scope. Если нет — список или сетка на CSS Grid.
Дальше прогоните готовую таблицу через скринридер (VoiceOver на macOS, NVDA на Windows) и через клавиатурную навигацию — заголовки должны читаться вместе со значениями. Проверьте поведение на ширине 320 px: если таблица выпихивает контент за экран, добавьте обёртку со скроллом. Для сложных таблиц с объединёнными ячейками почитайте раздел про связывание ячеек через headers/id в спецификации HTML на MDN.
Карточки для маркетплейсов: Таблица товаров и папка фотографий на входе — комплект слайдов и описаний на выходе, по требованиям Wildberries и Ozon.
Спросить за 5 ₽Источники: HTML table element — MDN Web Docs, Tables — Web Accessibility Initiative (W3C)
Частые вопросы
Раскладка страницы на таблицах — это всегда ошибка?
А если дизайнер нарисовал сетку карточек, которая на десктопе выглядит как таблица?
Google хуже индексирует контент в таблицах?
Что делать, если таблица очень широкая и не влезает даже на десктопе?
Перестройка таблицы в карточки на мобильном через CSS не сработала — ячейки едут. Почему?
Можно ли использовать role="table" на div-ах вместо тега table?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.