Большие таблицы часто становятся нечитаемыми на маленьких экранах, поскольку столбцы переполняются или уменьшаются до нечитаемости. В этом руководстве показано, как использовать липкое позиционирование, чтобы заголовки оставались видимыми при прокрутке, и как преобразовать таблицу в оптимизированный для мобильных устройств макет с накоплением, когда пространство ограничено.
Рекомендуемый подход
Суть адаптивной таблицы заключается в сохранении связи между ячейками данных и их заголовками.
- Семантическая основа. Используйте стандартные элементы
<table>с элементами<thead>,<tbody>и<th>. - Закрепленный контекст. Примените
position: stickyк заголовкам столбцов и строк. Благодаря этому пользователь всегда будет понимать, к чему относятся данные, независимо от того, как далеко он прокрутит таблицу. - Адаптивные преобразования. Используйте запросы
@containerвместо@media. Это позволяет таблице адаптироваться к собственной ширине (например, при размещении на боковой панели или в узком виджете панели управления), а не ко всей области просмотра. - Вставка ярлыков. В макете со стеком элемент
<thead>скрыт, а доступные заголовки вставляются в каждую ячейку с помощью псевдоэлементов::beforeи переменных CSS.
Руководство по настройке
1. Разметка и макет
Используйте стандартные семантические заголовки. Используйте .table-wrapper, чтобы обрабатывать переполнение и предоставлять контейнер для запросов. Добавьте атрибут tabindex="0" в контейнер, чтобы пользователи, которые работают только с клавиатурой, могли прокручивать таблицу.
Добавьте атрибут role="region", чтобы указать назначение контейнера. Укажите доступное название с помощью атрибута aria-labelledby (ссылающегося на атрибут id существующего элемента, который подписывает/называет таблицу) или атрибута aria-label.
<div tabindex="0" role="region" aria-labelledby="employee-table-caption" class="table-wrapper">
<table>
<caption id="employee-table-caption">Current employee directory</caption>
<thead>
<tr>
<th>Employee</th>
<th>Role</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<!-- Row header remains sticky horizontally on desktop,
and becomes a sticky card header on mobile -->
<th>Alex Rivera</th>
<td>Engineer</td>
<td>Active</td>
</tr>
</tbody>
</table>
</div>
2. Как настроить закрепленные заголовки
Включите прокрутку и закрепите заголовки. Используйте логические свойства и явные значения z-index для управления порядком наложения.
.table-wrapper {
overflow: auto;
max-inline-size: 100%;
max-block-size: min(500px, 80vh);
container-type: inline-size;
}
table {
border-collapse: separate;
border-spacing: 0;
}
/* Sticky column headers (desktop) */
thead {
position: sticky;
inset-block-start: 0;
z-index: 1; /* Above sticky row headers */
}
/* Sticky row headers (desktop) */
tbody th {
position: sticky;
inset-inline-start: 0;
background: white; /* Required to cover background during scroll */
}
3. Адаптивный макет со стеком
Если места мало, скройте исходную строку заголовка и преобразуйте таблицу в карточки. Добавьте ярлыки с помощью псевдоэлементов. Добавляйте альтернативный текст только для нетекстового контента, иначе программы чтения с экрана могут озвучивать и текстовый контент, и доступное название.
@container (width < 600px) {
table, thead, tbody, tr, th, td {
display: block;
}
thead {
/* MANDATORY: Hide column header from layout and screen readers. */
display: none
}
tr {
margin-block-end: 1.5rem;
border: 1px solid #ccc;
}
/* Sticky row headers at the top of each block/card */
tbody th {
position: sticky;
inset-block-start: 0;
z-index: 2;
background: #eee;
}
/* Define column labels as CSS variables (often set via JS) */
table {
--label-1: "Employee";
--label-2: "Role";
--label-3: "Status";
}
/* Inject labels */
td:nth-child(2)::before {
/* MANDATORY: If using non-text content, provide the accessible name (after the /) which ensures screen readers hear the text alternative. */
content: var(--label-2) ": ";
}
/* MANDATORY: Map the label for each column */
td:nth-child(3)::before {
content: var(--label-3) ": ";
}
}