Табы (tabs)
Переключаемые панели контента с ARIA
tablist/tab/tabpanel— клавиатурная навигация и доступность из коробки.
Задача
Нужен компонент вкладок, где клик по ярлыку переключает контент. Должны работать Tab/стрелки для навигации и screen reader.
Решение
<div class="tabs">
<div class="tabs__list" role="tablist" aria-label="Разделы">
<button class="tabs__tab active" role="tab" aria-selected="true"
aria-controls="panel-1" id="tab-1">
Описание
</button>
<button class="tabs__tab" role="tab" aria-selected="false"
aria-controls="panel-2" id="tab-2" tabindex="-1">
Характеристики
</button>
<button class="tabs__tab" role="tab" aria-selected="false"
aria-controls="panel-3" id="tab-3" tabindex="-1">
Отзывы
</button>
</div>
<div class="tabs__panel" role="tabpanel" id="panel-1" aria-labelledby="tab-1">
<p>Текст описания продукта.</p>
</div>
<div class="tabs__panel" role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
<p>Технические характеристики.</p>
</div>
<div class="tabs__panel" role="tabpanel" id="panel-3" aria-labelledby="tab-3" hidden>
<p>Отзывы покупателей.</p>
</div>
</div>
.tabs__list {
display: flex;
gap: 0;
border-bottom: 2px solid #e2e8f0;
}
.tabs__tab {
padding: 10px 20px;
border: none;
background: none;
cursor: pointer;
color: #64748b;
font-size: 0.95rem;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
transition: color 0.15s, border-color 0.15s;
}
.tabs__tab:hover { color: #1e293b; }
.tabs__tab.active,
.tabs__tab[aria-selected="true"] {
color: #3b82f6;
border-bottom-color: #3b82f6;
font-weight: 600;
}
.tabs__panel { padding: 20px 0; }
const tabList = document.querySelector('[role="tablist"]');
const tabs = [...tabList.querySelectorAll('[role="tab"]')];
function activate(tab) {
// Сбросить все
tabs.forEach((t) => {
t.classList.remove('active');
t.setAttribute('aria-selected', 'false');
t.setAttribute('tabindex', '-1');
document.getElementById(t.getAttribute('aria-controls')).hidden = true;
});
// Активировать выбранный
tab.classList.add('active');
tab.setAttribute('aria-selected', 'true');
tab.removeAttribute('tabindex');
document.getElementById(tab.getAttribute('aria-controls')).hidden = false;
tab.focus();
}
tabList.addEventListener('click', (e) => {
const tab = e.target.closest('[role="tab"]');
if (tab) activate(tab);
});
// Клавиатурная навигация: стрелки ←/→
tabList.addEventListener('keydown', (e) => {
const curr = tabs.indexOf(document.activeElement);
if (curr === -1) return;
let next;
if (e.key === 'ArrowRight') next = (curr + 1) % tabs.length;
if (e.key === 'ArrowLeft') next = (curr - 1 + tabs.length) % tabs.length;
if (e.key === 'Home') next = 0;
if (e.key === 'End') next = tabs.length - 1;
if (next !== undefined) { e.preventDefault(); activate(tabs[next]); }
});
Ключевые моменты
role="tablist/tab/tabpanel"+aria-selected+aria-controls— стандартный ARIA-паттерн; screen reader объявит «вкладки».tabindex="-1"на неактивных вкладках — Tab переходит только на активную; стрелки переключают между вкладками.hiddenатрибут на панелях — скрывает от screen reader, а не только визуально (display: none).- Навигация
Home/End— быстрый переход к первой/последней вкладке (ARIA best practices).