탭

Tabs

가로로 나열된 제목 중 하나를 골라, 그에 맞는 콘텐츠 패널 하나만 보여주는 내비게이션.

다른 이름: 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);

탭 목록(role="tablist")은 탭 버튼들(role="tab")을 담고, 각 탭은 aria-controls로 자신의 패널(role="tabpanel")을 가리킵니다. 대부분의 구현은 "자동 활성화"를 씁니다 — 방향키로 탭에 포커스만 옮겨도 그 즉시 콘텐츠가 바뀌고, 별도로 Enter를 누르지 않습니다.

세그먼티드 컨트롤과 헷갈리기 쉽습니다. 탭은 콘텐츠 영역 전체를 바꾸는 내비게이션에 가깝고 보통 컨테이너 상단에 붙어 밑줄이나 언더라인으로 표시됩니다. 세그먼티드 컨트롤은 버튼 그룹처럼 보이는 알약 모양이고, 콘텐츠를 통째로 바꾸기보다 같은 화면 안에서 정렬 기준이나 보기 방식 같은 "상태"를 전환할 때 씁니다.

키보드: 좌우 방향키로 탭 사이를 이동(roving tabindex — 활성 탭만 tabindex="0"), Home/End로 처음/끝 탭으로.