RadioGroup
APG radio group 패턴 — roving tabindex로 그룹이 탭 순서에 한 번만 잡히고, 화살표 키가 순환 이동하며 즉시 선택합니다. 비제어(defaultValue) 우선, value로 제어 모드도 지원.
마지막 업데이트 2026-06-11
한눈에#
상호배타 옵션을 모두 펼쳐 한눈에 비교시키는 APG radio group입니다 — Tab으로 그룹에 한 번 진입하고 화살표로 순환 이동하며 즉시 선택합니다.
- APG radio group
- roving tabindex
- 화살표 순환 선택
- 2~5 옵션 권장
그룹이 탭 순서에 한 번만 잡히고(roving), 비활성 옵션은 화살표 이동이 건너뜁니다
사용 시점#
상호배타 옵션 2~5개를 펼쳐 비교시키면 RadioGroup — 길거나 세그먼트·토글이면 다른 컴포넌트입니다.
요금제·공개 범위처럼 상호배타 옵션 2~5개를 한눈에 펼쳐 비교
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
import { RadioGroup } from '@wds/ui-web';
<RadioGroup
aria-label="요금제"
options={[
{ value: 'basic', label: '베이직' },
{ value: 'pro', label: '프로' },
{ value: 'team', label: '팀' },
]}
/>해부#
한 항목의 컨트롤·라벨·히트 영역 위에 토큰 실측을 핀으로 얹습니다.
- control
- 20px
icon.size-md - dot
- ≈50% (inset 25%)
- border
- 1px
border-strong - gap
- 8px
space-2 - label
- 16px
body-1 - row min-height
- 32px
space-8 - hit area
- 44px
control.height-md - focus radius
- 6px
radius.sm
변형#
크기#
size로 2단(ADR-012 B-P2) — md(기본) icon.size-md 컨트롤 + body-1 라벨,
sm icon.size-sm 컨트롤 + body-2 라벨. 항목 최소 높이 space.8(32px)는 공통이라
터치 타깃을 유지합니다.
상태#
- Selected —
aria-checked+color.primary링과 도트 - Hover — 비선택 컨트롤 보더가
color.border-strong으로 진해짐 - Focus — 항목
:focus-visible에 2px primary 아웃라인 - Disabled — 옵션 단위
disabled— 커서 차단 +color.text-placeholder잉크
제어/비제어 — 기본은 비제어(defaultValue)이며 내부에서 선택 상태를 소유합니다.
value를 주면 제어 모드가 되어 부모가 상태를 소유하고, 클릭·화살표 키 등 모든 선택 전환은
onChange(value)로 통지됩니다(렌더는 부모 value를 따르며, 같은 값 재선택은 발화하지 않음).
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
options | ReadonlyArray<RadioOption> | — | { value, label, disabled? } 배열 — 데이터 prop 방식 |
value | string | — | 제어 선택 value — 제공하면 제어 모드(내부 상태 무시, 모든 전환이 onChange로 통지) |
defaultValue | string | — | 초기 선택 value — 이후 상태는 내부 소유(비제어, value 미제공 시) |
onChange | (value: string) => void | — | 선택 변경 콜백 — 같은 값 재선택은 발화하지 않음 |
size | 'sm' | 'md' | 'md' | 컨트롤 크기 — md(icon.size-md+body-1) / sm(icon.size-sm+body-2) |
…rest | Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'> | — | aria-label 등은 radiogroup 루트로 전달 |
접근성#
루트가 role="radiogroup", 각 항목이 button[role="radio"] +
aria-checked입니다. 그룹 이름은 aria-label로 부여하세요.
| 키 | 동작 |
|---|---|
Tab | 그룹 진입/이탈 — 선택(없으면 첫 활성) 항목만 탭 순서에 포함(roving tabindex) |
→ / ↓ | 다음 활성 항목으로 순환 이동 + 즉시 선택 + 포커스 |
← / ↑ | 이전 활성 항목으로 순환 이동 + 즉시 선택 + 포커스 |
Space / Enter | 네이티브 button 활성화 — 현재 항목 선택 |
화살표 이동은 비활성 옵션을 건너뛰고, 끝에서 반대쪽 끝으로 순환합니다.
토큰#
component 토큰 없이 semantic을 직접 소비합니다(신설 기준 §4 미충족).
| 속성 | 토큰 |
|---|---|
| 컨트롤 | icon.size-md 원형 + color.surface 배경 |
| 보더 | color.border → hover color.border-strong |
| 선택 | color.primary 링/도트 |
| 라벨 | font.size-body-1 · color.text |
| 비활성 | color.surface-muted + color.text-placeholder |
| 포커스 링 | color.primary 2px 아웃라인 + radius.sm |
| 간격 | space.2 · space.3 · 최소 높이 space.8 |
| 모션 | duration.fast + ease.standard |