ComponentsP3 본문

Tabs

APG Tabs 패턴 컴파운드 컴포넌트 — 비제어(defaultValue) 또는 제어(value) 선택 상태, roving tabindex, 화살표 자동 활성화.

마지막 업데이트 2026-06-11

한눈에#

한 영역에서 한 번에 한 뷰만 보이며 콘텐츠 패널을 전환하는 APG Tabs입니다 — 화살표로 이동하면 즉시 활성화됩니다(자동 활성화).

탭 선택 상태는 Tabs 루트가 컨텍스트로 소유합니다.
  • APG Tabs 패턴
  • roving tabindex
  • 화살표 자동 활성화
  • 제어·비제어

선택 상태는 Tabs 루트가 컨텍스트로 소유 — 패널 전환에 별도 배선이 필요 없습니다

사용 시점#

한 번에 한 뷰만 보이며 패널을 전환하면 Tabs — 동시 비교나 옵션 토글은 다른 컴포넌트입니다.

쓴다
한 번에 한 패널만 보입니다.

개요·사용법·변경 이력처럼 상호 배타적인 콘텐츠 패널을 한 번에 하나씩 전환

대신 Accordion

여러 섹션을 동시에 펼쳐 비교할 때(type=multiple)

대신 SegmentedButton

콘텐츠 패널 없이 옵션만 토글(필터·뷰 모드)할 때

플레이그라운드#

컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.

개요 내용
import { Tabs } from '@wds/ui-web';

<Tabs
  defaultValue="overview"
>
  <Tabs.List aria-label="프로젝트 섹션">
      <Tabs.Trigger value="overview">개요</Tabs.Trigger>
      <Tabs.Trigger value="members">멤버</Tabs.Trigger>
      <Tabs.Trigger value="settings">설정</Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content value="overview">개요 내용</Tabs.Content>
    <Tabs.Content value="members">멤버 내용</Tabs.Content>
    <Tabs.Content value="settings">설정 내용</Tabs.Content>
</Tabs>

해부#

트리거(탭 버튼)가 측정 단위입니다 — 패딩과 활성 인디케이터를 핀으로 표기합니다.

트리거 — space.3 × space.4, 활성 시 하단 primary 보더
padding-block
12pxspace-3
padding-inline
16pxspace-4
gap
8pxspace-2
font
16 / 500body-1 · medium
active indicator
2pxcolor.primary
list divider
1pxcolor.border

변형#

단일 변형입니다 — 활성 인디케이터는 하단 보더(primary) 한 가지 어휘만 사용합니다. 비활성 탭은 네이티브 disabled로 표현하며 키보드 순회에서 제외됩니다.

disabled 탭은 화살표 이동 대상에서 빠집니다.
disabled 탭 — 클릭·키보드 순회 모두 제외

크기#

단일 크기입니다 — 트리거 패딩은 space.3/4, 레이블은 font-size.body-1 고정. 3단 size는 component 토큰 신설 기준(§4) 미충족으로 도입하지 않았습니다.

상태#

비제어/제어 컴파운드입니다 — 기본은 비제어로 <Tabs defaultValue>가 선택 상태를 소유하고 하위 파트(List/Trigger/Content)는 컨텍스트로 소비합니다. value를 주면 제어 모드가 되어 부모가 상태를 소유하고, 탭 클릭·화살표 자동 활성화 등 모든 전환은 내부 상태를 쓰지 않고 onValueChange(value)로만 통지됩니다(렌더는 부모 value를 따름). Hover/Focus/Disabled는 CSS 의사클래스, 선택 상태는 aria-selected 속성 셀렉터로 표현됩니다.

Props#

점 표기(Tabs.List)와 평탄 이름(TabsList)은 동일 함수입니다 — 서버 컴포넌트(RSC)에서는 클라이언트 참조에 점 접근이 해석되지 않으므로 평탄 이름을 사용하세요(이 문서의 데모도 평탄 이름).

<Tabs> (루트)

Prop타입기본값설명
valuestring제어 선택 탭 value — 제공하면 제어 모드(내부 상태 무시, 전환이 onValueChange로 통지)
defaultValuestring초기 선택 탭 value — 이후 상태는 내부 소유(비제어, value 미제공 시)
onValueChange(value: string) => void선택 변경 콜백 — 제어/비제어 모두에서 호출
classNamestring루트 클래스 합성

<Tabs.List>

Prop타입기본값설명
aria-labelstring탭 목록의 접근 가능한 이름 — APG 필수
variant'fixed' | 'scrollable'표시 방식 — 기본 'fixed'(균등 배치), 'scrollable'은 탭이 많을 때 가로 스크롤 (ADR-012 B-P1)
level'primary' | 'secondary'위계 — 기본 'primary', 'secondary'는 작은 폰트·여백 (ADR-012 B-P1)
classNamestring클래스 합성

<Tabs.Trigger> — 네이티브 button 속성 확장(disabled 등 전달)

Prop타입기본값설명
valuestring같은 value의 Content와 짝 — 필수

<Tabs.Content>

Prop타입기본값설명
valuestring대응하는 Trigger의 value — 필수
classNamestring클래스 합성

접근성#

계약구현
역할tablist / tab / tabpanel
이름List의 aria-label 필수, 패널은 aria-labelledby로 탭과 연결
선택aria-selected + aria-controls (useId 기반 id 자동 연결)
roving tabindex선택 탭만 tabindex=0 — Tab 한 번에 목록 진입/탈출
키보드←/→ 순환 이동, Home/End 처음/끝 — 이동 즉시 자동 활성화(APG)
disabled네이티브 disabled — 클릭·화살표 순회 모두 제외
패널 도달패널 tabIndex=0 — 내부 포커서블 없어도 키보드 도달
모션패널 전환은 opacity/transform만 + prefers-reduced-motion 존중

토큰#

component 토큰 없이 semantic을 직접 소비합니다(신설 기준 §4 미충족).

속성토큰
목록 구분선color.border
트리거 텍스트color.text-muted → hover color.text → disabled color.text-placeholder
활성 탭color.primary-text(레이블) · color.primary(하단 인디케이터)
포커스 링color.focus-ring · radius.sm
타이포font.sans · font-size.body-1 · font-weight.medium · line-height.tight
간격space.1/2/3/4
모션duration.fast · ease.standard / ease.out

관련#