ToggleGroup
먼저 보고, 그다음 계약을 읽습니다
눌림 상태 버튼 묶음 — 다중 토글 또는 해제 가능한 단일 선택.
Component intent의도와 경계 읽기
정렬/서식 툴바·필터 칩처럼 "0개 또는 N개"가 자연스러운 곳을 위한 버튼 묶음. type="multiple" 은 다중 토글(체크박스 의미, value: T[]), type="single" 은 **해제 가능한** 단일 선택(같은 항목 재클릭 시 null)이다. 항상 하나가 선택되는 단일 필수 선택이면 Seg 를, 낱개 토글이면 Toggle 을 쓴다. 외부 dep 0 — 네이티브 <button aria-pressed> 묶음이며 스타일은 100% CSS 클래스(.axe-toggle-group)로 처리한다. controlled 전용이고 value 는 제네릭 T(string 서브타입)로 좁혀진다.
예제
SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.
import { ToggleGroup } from "@axe/ui";import { useState } from "react";function FormatToolbar() { const [fmt, setFmt] = useState<string[]>([]); return ( <ToggleGroup type="multiple" ariaLabel="텍스트 서식" value={fmt} onValueChange={setFmt} options={[ { value: "bold", label: "B" }, { value: "italic", label: "I" }, { value: "underline", label: "U" }, ]} /> );}import { ToggleGroup } from "@axe/ui";import { useState } from "react";function AlignPicker() { const [align, setAlign] = useState<string | null>(null); return ( <ToggleGroup type="single" ariaLabel="정렬" value={align} onValueChange={setAlign} options={[ { value: "left", label: "왼쪽" }, { value: "center", label: "가운데" }, { value: "right", label: "오른쪽" }, ]} /> );}<div class="axe-toggle-group" role="group" aria-label="텍스트 서식"> <button type="button" class="axe-toggle-group__btn" aria-pressed="true">B</button> <button type="button" class="axe-toggle-group__btn" aria-pressed="false">I</button></div>Props
TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.
| 이름 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
type | "multiple" | "single" | 필수 | — | 다중 토글(체크박스 의미) 또는 해제 가능한 단일 선택(같은 항목 재클릭 시 null). value/onValueChange 타입을 판별. |
value | T[] (multiple) | T | null (single) | 필수 | — | 눌린 값(들) (controlled). multiple=값 배열, single=값 또는 해제 시 null. |
onValueChange | (value: T[]) => void (multiple) | (value: T | null) => void (single) | 필수 | — | 값 변경 콜백. type 에 대응하는 다음 값을 넘김. |
options | ToggleGroupOption<T>[] | 필수 | — | 버튼 항목 목록. 각 항목 = { value: T; label: React.ReactNode; disabled?: boolean }. |
ariaLabel | string | 필수 | — | role="group" 컨테이너의 접근성 라벨. |
size | "sm" | "md" | — | "md" | 버튼 크기. |
disabled | boolean | — | false | 묶음 전체 비활성화 (개별은 option.disabled). |
...rest | Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'> | — | — | className 등 나머지는 루트 <div role="group"> 으로 전파. |
.axe-* 클래스 계약
React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.
| 클래스 | 안정성 | 용도 |
|---|---|---|
.axe-toggle-group | stable | 묶음 루트 컨테이너 (role="group", pill 트랙). |
.axe-toggle-group__btn | stable | 묶음 내부 각 토글 버튼. |
.axe-toggle-group--sm | stable | sm 크기 변형 (버튼 높이 24px). |
접근성
키보드, ARIA, 구현 노트를 함께 검토합니다.
- Tab 으로 버튼 순회 (roving tabindex 없이 각 버튼이 tab stop)
- Enter/Space 로 해당 항목 토글
컨테이너 role="group" + aria-label(ariaLabel). 각 버튼은 aria-pressed 로 눌림 표시 — multiple 은 여러 개가 동시에 true, single 은 최대 1개 true(재클릭 시 전부 false).
개별 비활성 = option.disabled → 해당 <button> 의 disabled 속성. 전체 비활성 = 모든 버튼 disabled(시각은 버튼 :disabled 규칙이 담당). 라디오 의미가 아니므로 role 은 radiogroup 이 아닌 group 이다.