// WAI-ARIA tabs: click or arrow keys select a tab, Home/End jump to the ends, only the selected tab is in the Tab order.
for (const list of document.querySelectorAll('[role=tablist]')) {
const tabs = [...list.querySelectorAll('[role=tab]')];
const select = tab => tabs.forEach(t => {
const on = t === tab;
t.setAttribute('aria-selected', on);
t.tabIndex = on ? 0 : -1;
document.getElementById(t.getAttribute('aria-controls')).hidden = !on;
});
list.addEventListener('click', e => { const t = e.target.closest('[role=tab]'); if (t) select(t); });
list.addEventListener('keydown', e => {
const i = tabs.indexOf(e.target);
const n = { ArrowLeft: i - 1, ArrowUp: i - 1, ArrowRight: i + 1, ArrowDown: i + 1, Home: 0, End: tabs.length - 1 }[e.key];
if (n === undefined || i < 0) return;
e.preventDefault();
const t = tabs[(n + tabs.length) % tabs.length];
t.focus();
select(t);
});
}
About this set
A media viewer built as tabs: a large 16:10 frame with a caption and a row of four thumbnails underneath, where each thumbnail selects its frame. Gradients stand in for photos so the set has no image files; swap them for img or background images. Use it for portfolio pieces, product shots, case studies or album artwork where the thumbnails are the navigation. The selected thumbnail gets a two-ring indigo outline, and a dark scrim at the bottom of the frame keeps the white caption readable on any picture. The plain CSS version is a radio group: each thumbnail is a label with visually hidden text, and :has() shows the frame for the checked radio. The Bootstrap version is a .nav of tab buttons driven by the Tab plugin, with the frames in a .tab-content inside a .ratio set to 16:10. The Tailwind version uses the same markup with utilities and a small WAI-ARIA tabs script. Thumbnails lift slightly on hover, and that movement stops under reduced motion.
What’s included
Large 16:10 frame with caption and a four-thumbnail strip
Thumbnails carry text names for screen readers
Dark scrim keeps white caption text readable on any image
Selected ring and keyboard focus ring are drawn differently
Bootstrap version uses .ratio and the Tab plugin
Accessibility
In the plain CSS version the tabs are a radio group, not ARIA tabs: the radios sit inside an element with role="radiogroup" and an aria-label, each takes its name from its visible label, Tab enters the group and the arrow keys move the selection (radios have no Home or End). In the Bootstrap and Tailwind versions they follow the WAI-ARIA tabs pattern: role tablist, tab and tabpanel, aria-selected, aria-controls, a roving tabindex, arrow keys plus Home and End. Each thumbnail is named by visually hidden text or an aria-label. The selected thumbnail has an indigo ring and keyboard focus adds a separate dark outline around it, and the hover lift is removed under prefers-reduced-motion.
Customise it
Replace the .g1 to .g4 gradients with your images. The selected ring is the box-shadow using #4f46e5 on the checked label (plain CSS), on .thumbs .active (Bootstrap) or in the aria-selected:shadow utility (Tailwind); the frame ratio is aspect-ratio, --bs-aspect-ratio or aspect-[16/10].