Tabs

탭

A row of labeled buttons that switches which single content panel is visible.

Also known as: Tab barTabbed navigation
···
html
<div class="tw">
  <div role="tablist" aria-label="플랜" class="tablist">
    <button role="tab" id="t1" aria-controls="p1" aria-selected="true" tabindex="0">개요</button>
    <button role="tab" id="t2" aria-controls="p2" aria-selected="false" tabindex="-1">가격</button>
    <button role="tab" id="t3" aria-controls="p3" aria-selected="false" tabindex="-1">리뷰</button>
    <i class="ind"></i>
  </div>
  <div role="tabpanel" id="p1" aria-labelledby="t1" class="panel">모든 기능을 한 곳에서 관리해요.</div>
  <div role="tabpanel" id="p2" aria-labelledby="t2" class="panel" hidden>월 9,900원, 연 결제 시 2개월 무료.</div>
  <div role="tabpanel" id="p3" aria-labelledby="t3" class="panel" hidden>"팀 협업이 훨씬 쉬워졌어요" — 4.8★</div>
</div>
css
.tw{width:min(360px,92%)}
.tablist{position:relative;display:flex;border-bottom:1px solid var(--line)}
.tablist [role=tab]{flex:1;padding:9px 4px;border:0;background:none;color:var(--muted);font-size:13px;font-weight:600;cursor:pointer}
.tablist [role=tab][aria-selected=true]{color:var(--fg)}
.ind{position:absolute;bottom:-1px;left:0;width:calc(100% / 3);height:2px;background:var(--accent);transition:transform .28s ease}
.panel{padding:16px 4px;color:var(--muted);font-size:13px;min-height:44px}
js
const tabs = [...document.querySelectorAll('[role=tab]')];
const panels = [...document.querySelectorAll('[role=tabpanel]')];
const ind = document.querySelector('.ind');
function select(i) {
  tabs.forEach((t, idx) => { t.setAttribute('aria-selected', String(idx === i)); t.tabIndex = idx === i ? 0 : -1; });
  panels.forEach((p, idx) => p.hidden = idx !== i);
  ind.style.transform = 'translateX(' + (i * 100) + '%)';
}
let auto = true, cur = 0;
tabs.forEach((t, i) => t.addEventListener('click', () => { auto = false; cur = i; select(i); }));
document.querySelector('.tablist').addEventListener('keydown', (e) => {
  if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
  auto = false; e.preventDefault();
  cur = (cur + (e.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length;
  select(cur); tabs[cur].focus();
});
setInterval(() => { if (!auto) return; cur = (cur + 1) % tabs.length; select(cur); }, 2000);

A tablist (role="tablist") holds tab buttons (role="tab"), each pointing at its panel (role="tabpanel") via aria-controls. Most implementations use "automatic activation" — moving focus with arrow keys switches content immediately, with no separate Enter press.

It's easy to confuse with a segmented control. Tabs read as navigation that swaps an entire content area, typically anchored to the top of a container with an underline indicator. A segmented control looks like a pill-shaped button group and usually toggles a state — sort order, view mode — within the same screen rather than replacing content wholesale.

Keyboard: left/right arrows move between tabs (roving tabindex — only the active tab has tabindex="0"), Home/End jump to the first/last tab.