Seg
먼저 보고, 그다음 계약을 읽습니다
항상 하나가 선택되는 단일 필수 선택용 segmented control.
Component intent의도와 경계 읽기
문서 종류·뷰 모드처럼 **항상 하나**가 선택돼 있는 단일 필수 선택(라디오 의미)을 위한 segmented control. VariantSwitcher·DocSeg 가 inline style 로 재발명하던 패턴을 흡수한 SSOT 다. 외부 dep 0 — 네이티브 <button aria-pressed> 묶음이며 스타일은 100% CSS 클래스(.axe-seg)로 처리한다. "0개 또는 N개"가 가능한 다중/해제형은 ToggleGroup 을, 낱개 on/off 는 Toggle 을 쓴다. controlled 전용이고 value 는 제네릭 T(string 서브타입)로 좁혀진다.
예제
SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.
import { Seg } from "@axe/ui";import { useState } from "react";function DocTypePicker() { const [docType, setDocType] = useState("ic"); return ( <Seg ariaLabel="문서 종류" value={docType} onChange={setDocType} options={[ { value: "ic", label: "IC Memo" }, { value: "lp", label: "LP Letter" }, ]} /> );}<div class="axe-seg" role="group" aria-label="문서 종류"> <button type="button" class="axe-seg__btn" aria-pressed="true">IC Memo</button> <button type="button" class="axe-seg__btn" aria-pressed="false">LP Letter</button></div>Props
TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.
| 이름 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
value | T | 필수 | — | 선택된 값 (controlled) — 항상 하나. |
options | SegOption<T>[] | 필수 | — | 선택지 목록. 각 항목 = { value: T; label: React.ReactNode }. |
onChange | (value: T) => void | 필수 | — | 선택 변경 콜백 — 고른 값을 넘김. |
ariaLabel | string | 필수 | — | role="group" 컨테이너의 접근성 라벨. |
size | "sm" | "md" | — | "md" | 버튼 크기. |
...rest | Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> | — | — | className 등 나머지는 루트 <div role="group"> 으로 전파. |
.axe-* 클래스 계약
React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.
| 클래스 | 안정성 | 용도 |
|---|---|---|
.axe-seg | stable | segmented control 루트 컨테이너 (role="group", pill 트랙). |
.axe-seg__btn | stable | 각 세그먼트 버튼. |
.axe-seg__btn--active | stable | 활성 세그먼트의 대체 표식 — aria-pressed="true" 와 동일 스타일(둘 다 CSS 셀렉터로 매칭). |
.axe-seg--sm | stable | sm 크기 변형 (버튼 높이 24px). |
접근성
키보드, ARIA, 구현 노트를 함께 검토합니다.
- Tab 으로 세그먼트 순회 (각 버튼이 tab stop)
- Enter/Space 로 해당 세그먼트 선택
컨테이너 role="group" + aria-label(ariaLabel). 선택된 버튼만 aria-pressed="true" — 항상 정확히 하나가 눌린 상태.
의미상 라디오(단일 필수 선택)지만 role="radio"/radiogroup 이 아니라 group + aria-pressed toggle 버튼으로 구현했다(시각·조작 단순성 우선). 스크린리더는 라디오 "n중 m" 이 아니라 toggle 버튼의 pressed 로 읽는다 — 진짜 폼 라디오 시맨틱이 필요하면 Radio/RadioGroup 을 쓸 것.