ToggleGroup
상호 배타적인 옵션을 한 트랙에 묶고 선택한 칸을 위로 띄우는 묶음 토글
목차
옵션 여럿을 한 트랙에 묶어 하나만 고르게 하는 묶음 토글입니다. 트랙은 surface-container-high, 선택한 칸은
surface-raised 면으로 떠올라 가장 작은 그림자 단계(CoreShadow.xs)를 갖습니다. Flutter/Web 양쪽에서 같은 API(ToggleGroup)를 제공합니다.
필터의 세그먼트, 시간 선택기의 AM / PM, 색 선택기의 모드 탭이 같은 모양을 쓰도록 한 곳에 둔 부품입니다.
ToggleGroup 은 controlled 컴포넌트입니다 — 선택값은 부모가 value 로 소유하고 onChanged
로 갱신합니다. onChanged 가 없거나 enabled: false 이면 입력을 받지 않고 비활성 모양으로 그립니다.
기본 사용법 (Basic Usage)#
ToggleGroup<String>(
value: _selected,
onChanged: (value) => setState(() => _selected = value),
items: const [
CoreToggleGroupItem(value: 'am', label: 'AM'),
CoreToggleGroupItem(value: 'pm', label: 'PM'),
],
)
ToggleGroup<String>(
value: _selected,
onChanged: (value) => setState(() => _selected = value),
items: const [
CoreToggleGroupItem(value: 'am', label: 'AM'),
CoreToggleGroupItem(value: 'pm', label: 'PM'),
],
)
Live Preview#
코드 보기
class ToggleGroupDefaultExample extends StatefulComponent {
const ToggleGroupDefaultExample({super.key});
@override
State<ToggleGroupDefaultExample> createState() => _ToggleGroupDefaultExampleState();
}
class _ToggleGroupDefaultExampleState extends State<ToggleGroupDefaultExample> {
String? _selected = 'am';
void handleChanged(String value) {
setState(() => _selected = value);
}
@override
Component build(BuildContext context) {
return ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'am', label: 'AM'),
CoreToggleGroupItem(value: 'pm', label: 'PM'),
],
);
}
}
class ToggleGroupDefaultExample extends StatefulWidget {
const ToggleGroupDefaultExample({super.key});
@override
State<ToggleGroupDefaultExample> createState() => _ToggleGroupDefaultExampleState();
}
class _ToggleGroupDefaultExampleState extends State<ToggleGroupDefaultExample> {
String? _selected = 'am';
void handleChanged(String value) {
setState(() => _selected = value);
}
@override
Widget build(BuildContext context) {
return ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'am', label: 'AM'),
CoreToggleGroupItem(value: 'pm', label: 'PM'),
],
);
}
}
구조 (Anatomy)#
ToggleGroup
└─ track (surface-container-high · 모서리 10(md) / 8(sm) · 안쪽 여백 4(md) / 3(sm))
└─ segment × N (높이 32(md) / 26(sm) · 최소 폭 = 높이 · 모서리 6 · 간격 2)
├─ icon? (CoreToggleGroupItem.icon)
└─ label (CoreToggleGroupItem.label — showLabel: false 면 접근성 이름으로만 남음)
선택한 칸만 surface-raised 면과 그림자를 가지고, 나머지 칸은 채움이 없어 트랙이 그대로 보입니다.
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 두세 개의 옵션 중 하나를 바로 바꾸는 자리 (AM / PM, 보기 방식, 모드 탭)
- 옵션이 항상 화면에 모두 보여야 할 때
대신 다른 컴포넌트를 사용하세요:
- Filter: 초기화 버튼이 붙은 목록 필터
- Tabs: 선택에 따라 콘텐츠 패널이 바뀔 때
- RadioGroup: 폼 안의 라디오 입력 계약이 필요할 때
- ChipGroup: 여러 개를 고를 때
Props / Parameters#
T 는 선택 값, W 는 Flutter Widget / Web Jaspr Component
입니다.
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
items |
List<CoreToggleGroupItem<T, W>> |
필수 | 옵션 목록 (1개 이상) |
value |
T? |
필수 | 현재 선택 값. null 이면 아무 칸도 선택하지 않습니다. |
onChanged |
void Function(T)? |
필수 | 사용자가 칸을 고른 값을 알립니다. null 이면 입력을 받지 않습니다. |
enabled |
bool? |
null |
null 이면 onChanged != null. 명시하면 그 값이 이깁니다. |
size |
CoreComponentSize |
md |
크기 단계.
md
는 칸 32 · 트랙 40,
sm
은 칸 26 · 트랙 32 입니다.
sm
이 아닌 다른 값은
md
로 읽습니다. 판(시간 선택기의 AM / PM, 색 선택기의 모드 탭) 안에서는
sm
을 씁니다.
|
direction |
CoreAxis |
horizontal |
칸을 놓는 축.
horizontal
은 가로 한 줄,
vertical
은 세로 한 줄(AM 위, PM 아래)입니다. 칸 사이 간격 · 트랙 안쪽 여백 · 칸 값은 두 방향이 같고, 세로에서는 칸이 가장 넓은 칸의 폭(
fillWidth
이면 부모 폭)으로 늘어나고 내용은 가운데입니다.
|
fillWidth |
bool |
false |
켜면 트랙이 부모가 주는 폭을 채우고 칸이 그 폭을 똑같이 나눠 가집니다(칸 안 내용은 가운데). 판 안 모드 탭처럼 아래 폭 가득한 영역과 가장자리를 맞출 때 씁니다. 끄면 트랙이 칸 내용만큼만 차지합니다. |
trailing |
W? |
null |
트랙 안, 마지막 칸 뒤에 놓는 호출자 콘텐츠(예:
Filter
의 구분선과 reset 버튼). 칸이 아니므로 선택 상태와 무관하고, 칸 사이 간격(
itemSpacing
)이 마지막 칸과 이 슬롯 사이에도 적용됩니다.
|
toggleGroupStyle |
CoreToggleGroupStyle? |
null |
스타일 단일 슬롯 |
CoreToggleGroupItem#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
value |
T |
— | 이 칸이 나타내는 값. value 와 == 로 비교해 선택 여부를 정합니다. |
label |
String |
— |
칸의 라벨.
showLabel: false
여도 접근성 이름(
aria-label
/
Semantics(label:)
)으로 남습니다.
|
icon |
W? |
null |
라벨 앞에 그리는 호출자 제공 아이콘. 상태별 색·크기는
iconStyle
/
selectedIconStyle
/
disabledIconStyle
이 정합니다.
|
showLabel |
bool |
true |
라벨을 그릴지 여부. icon != null && showLabel == false 이면 아이콘 전용 정사각 칸이 됩니다. |
스타일 시스템 (Style System)#
모든 시각 chrome 은 단일 toggleGroupStyle: CoreToggleGroupStyle? 슬롯으로 들어갑니다.
ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
items: items,
toggleGroupStyle: const CoreToggleGroupStyle(
borderRadius: CoreBorderRadius.all(CoreRadius.radius16),
itemBorderRadius: CoreBorderRadius.all(CoreRadius.radius12),
),
)
CoreToggleGroupStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
backgroundColor | CoreColor? | Fill of the track. |
selectedColor |
CoreColor? |
Fill of the chosen segment. |
disabledBackgroundColor |
CoreColor? |
Fill of the track of a disabled group. |
disabledSelectedColor |
CoreColor? |
Fill of the chosen segment of a disabled group. |
selectedShadow |
List<CoreShadowLayer>? |
Shadow under the chosen segment. An empty list paints none. |
borderRadius |
CoreBorderRadius? |
Track corner radius. |
itemBorderRadius |
CoreBorderRadius? |
Per-segment corner radius. |
padding |
CoreEdgeInsets? |
Inset between the track edge and the segments. |
itemPadding |
CoreEdgeInsets? |
Padding inside each segment — its uniform inset around [icon][label box]. |
labelPadding |
CoreEdgeInsets? |
Inset of the label box inside a segment (painted only when the item shows its label). |
itemHeight |
double? |
Fixed segment height (logical px); content centres vertically. |
itemSpacing |
double? |
Spacing between segments (logical px). |
selectedFontWeight |
int? |
Font weight of the chosen segment's label (CSS font-weight integer). |
transitionDuration |
Duration? |
Chosen-state colour transition duration. |
stateLayerColor |
CoreColor? |
Colour of the state layer laid over a hovered segment. |
stateLayer |
CoreStateLayer? |
Weights of the segment state layer (only hover is read). |
labelTextStyle |
CoreTextStyle? |
Label text style of unchosen segments. |
selectedLabelTextStyle |
CoreTextStyle? |
Label text style of the chosen segment. |
disabledLabelTextStyle |
CoreTextStyle? |
Label text style of a disabled group, chosen and unchosen alike. |
iconStyle |
CoreIconStyle? |
Icon style of an unchosen segment's caller-supplied icon. |
selectedIconStyle |
CoreIconStyle? |
Icon style of the chosen segment's caller-supplied icon. |
disabledIconStyle |
CoreIconStyle? |
Icon style of a disabled group's icons, chosen and unchosen alike. |
clickableStyle |
CoreClickableStyle? |
Nested [CoreClickableStyle] for the composed per-segment
Clickable
(press scale / durations / focus ring), merged on top of [defaultClickableStyle] and raw-forwarded.
|
Resolve chain#
디자인 시스템 기본값 → CoreToggleGroupTheme.style → 위젯 toggleGroupStyle. 변형(variant) 축이 없어
variantStyles 도 없습니다.
테마로 덮어쓰기#
CoreComponentTheme(
toggleGroup: CoreToggleGroupTheme(
style: CoreToggleGroupStyle(
backgroundColor: CoreColor.token(CoreColors.surfaceContainerHigh),
),
),
)
빠른 오버라이드 (Chain)#
이미 만든 ToggleGroup 인스턴스에 스타일을 이어 붙이려면 withStyle 체인을 씁니다. 생성자의 toggleGroupStyle
인자와 같게 동작합니다.
class ToggleGroupChainExample extends StatefulWidget {
const ToggleGroupChainExample({super.key});
@override
State<ToggleGroupChainExample> createState() => _ToggleGroupChainExampleState();
}
class _ToggleGroupChainExampleState extends State<ToggleGroupChainExample> {
String? _selected = 'am';
void handleChanged(String value) {
setState(() => _selected = value);
}
@override
Widget build(BuildContext context) {
// withStyle full-control — 트랙 모서리와 선택 면 그림자를 한 번에.
return ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'am', label: 'AM'),
CoreToggleGroupItem(value: 'pm', label: 'PM'),
],
).withStyle(
const CoreToggleGroupStyle(
borderRadius: CoreBorderRadius.all(CoreRadius.radius16),
itemBorderRadius: CoreBorderRadius.all(CoreRadius.radius12),
selectedShadow: CoreShadow.none,
),
);
}
}
class ToggleGroupChainExample extends StatefulComponent {
const ToggleGroupChainExample({super.key});
@override
State<ToggleGroupChainExample> createState() => _ToggleGroupChainExampleState();
}
class _ToggleGroupChainExampleState extends State<ToggleGroupChainExample> {
String? _selected = 'am';
void handleChanged(String value) {
setState(() => _selected = value);
}
@override
Component build(BuildContext context) {
// withStyle full-control — 트랙 모서리와 선택 면 그림자를 한 번에.
return ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'am', label: 'AM'),
CoreToggleGroupItem(value: 'pm', label: 'PM'),
],
).withStyle(
const CoreToggleGroupStyle(
borderRadius: CoreBorderRadius.all(CoreRadius.radius16),
itemBorderRadius: CoreBorderRadius.all(CoreRadius.radius12),
selectedShadow: CoreShadow.none,
),
);
}
}
변형 (Variants)#
세로#
direction: CoreAxis.vertical 이면 칸을 위에서 아래로 놓습니다(AM 위, PM 아래). sm 두 칸은 3 + 26 + 2 + 26 + 3 = 60
으로 시간 선택기의 시 · 분 칸 높이와 같아, 시간 선택기의 AM / PM 이 이 모양을 칸 옆에 위쪽 맞춤으로 둡니다.
코드 보기
class ToggleGroupVerticalExample extends StatefulComponent {
const ToggleGroupVerticalExample({super.key});
@override
State<ToggleGroupVerticalExample> createState() => _ToggleGroupVerticalExampleState();
}
class _ToggleGroupVerticalExampleState extends State<ToggleGroupVerticalExample> {
String? _selected = 'am';
void handleChanged(String value) {
setState(() => _selected = value);
}
@override
Component build(BuildContext context) {
// 세로 sm — AM 위, PM 아래. 두 칸이 3 + 24 + 2 + 24 + 3 = 56 으로
// 시간 선택기의 시 · 분 칸 높이와 같다.
return ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
size: CoreComponentSize.sm,
direction: CoreAxis.vertical,
items: const [
CoreToggleGroupItem(value: 'am', label: 'AM'),
CoreToggleGroupItem(value: 'pm', label: 'PM'),
],
);
}
}
class ToggleGroupVerticalExample extends StatefulWidget {
const ToggleGroupVerticalExample({super.key});
@override
State<ToggleGroupVerticalExample> createState() => _ToggleGroupVerticalExampleState();
}
class _ToggleGroupVerticalExampleState extends State<ToggleGroupVerticalExample> {
String? _selected = 'am';
void handleChanged(String value) {
setState(() => _selected = value);
}
@override
Widget build(BuildContext context) {
// 세로 sm — AM 위, PM 아래. 두 칸이 3 + 24 + 2 + 24 + 3 = 56 으로
// 시간 선택기의 시 · 분 칸 높이와 같다.
return ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
size: CoreComponentSize.sm,
direction: CoreAxis.vertical,
items: const [
CoreToggleGroupItem(value: 'am', label: 'AM'),
CoreToggleGroupItem(value: 'pm', label: 'PM'),
],
);
}
}
비활성#
onChanged 를 주지 않거나 enabled: false 로 끄면 평평한 비활성 모양이 됩니다.
코드 보기
class ToggleGroupDisabledExample extends StatefulComponent {
const ToggleGroupDisabledExample({super.key});
@override
State<ToggleGroupDisabledExample> createState() => _ToggleGroupDisabledExampleState();
}
class _ToggleGroupDisabledExampleState extends State<ToggleGroupDisabledExample> {
String? _selected = 'slider';
void handleChanged(String value) {
setState(() => _selected = value);
}
@override
Component build(BuildContext context) {
// 비활성 — `onChanged` 를 주지 않거나 `enabled: false` 로 끈다.
return div([
ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'wheel', label: 'Wheel'),
CoreToggleGroupItem(value: 'slider', label: 'Slider'),
CoreToggleGroupItem(value: 'input', label: 'Input'),
],
),
const Gap.space12(),
ToggleGroup<String>(
value: _selected,
enabled: false,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'wheel', label: 'Wheel'),
CoreToggleGroupItem(value: 'slider', label: 'Slider'),
CoreToggleGroupItem(value: 'input', label: 'Input'),
],
),
]);
}
}
class ToggleGroupDisabledExample extends StatefulWidget {
const ToggleGroupDisabledExample({super.key});
@override
State<ToggleGroupDisabledExample> createState() => _ToggleGroupDisabledExampleState();
}
class _ToggleGroupDisabledExampleState extends State<ToggleGroupDisabledExample> {
String? _selected = 'slider';
void handleChanged(String value) {
setState(() => _selected = value);
}
@override
Widget build(BuildContext context) {
// 비활성 — `onChanged` 를 주지 않거나 `enabled: false` 로 끈다.
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'wheel', label: 'Wheel'),
CoreToggleGroupItem(value: 'slider', label: 'Slider'),
CoreToggleGroupItem(value: 'input', label: 'Input'),
],
),
const Gap.space12(),
ToggleGroup<String>(
value: _selected,
enabled: false,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'wheel', label: 'Wheel'),
CoreToggleGroupItem(value: 'slider', label: 'Slider'),
CoreToggleGroupItem(value: 'input', label: 'Input'),
],
),
],
);
}
}
- 트랙은
disabled-container - 선택한 칸은
surface-container-high로 가라앉고 그림자가 없습니다 - 글자와 아이콘은
disabled-content(선택 여부와 무관). 선택한 칸의 굵기는 유지됩니다.
아이콘 칸#
ToggleGroup<String>(
value: _selected,
onChanged: handleChanged,
items: const [
CoreToggleGroupItem(value: 'grid', label: 'Grid', icon: Icon(LucideIcons.layoutGrid)),
CoreToggleGroupItem(value: 'list', label: 'List', icon: Icon(LucideIcons.list), showLabel: false),
],
)
동작 스펙 (Behavior)#
-
value == item.value로 선택을 표시합니다. 칸을 고르면onChanged(item.value)만 호출하고, 부모가 값을 갱신해야 선택이 바뀝니다. - 칸 하나를 개별로 비활성화하는 플래그는 없습니다. 고를 수 없는 값은
items에서 뺍니다.
시각#
-
트랙
surface-container-high, 모서리 10, 안쪽 여백 3 (4px 격자 밖의 값이라 상수 하나로만 적혀 있고, Web 에서는 밀도 축이 움직이지 않는 길이로 나갑니다) - 선택한 칸은
surface-raised+CoreShadow.xs, 모서리 8, 세미볼드 라벨 - 선택이 바뀔 때 150ms 색·그림자 전환
-
포인터가 올라간 칸 위에 공통 상태 레이어가 깔립니다 —
on-surface(stateLayerColor)를 hover 가중치(stateLayer.hover)로 칸 모양 그대로 덮습니다. 선택한 칸도 면 위에 같은 레이어를 받고(면 색과 그림자는 그대로), 비활성 그룹에는 없습니다 -
판(
surface-bright) 위에서surface-container는 판과 같은 값이므로 트랙은 한 단계 위 토큰(surface-container-high)을 씁니다.
레이아웃#
-
칸은 가로로
itemSpacing(2) 간격, 높이는 크기 단계가 정한itemHeight로 고정이고, 칸 안 내용은 가로 · 세로 모두 가운데입니다. 트랙 높이는안쪽 여백 + 칸 + 안쪽 여백이라md(4 + 32 + 4)는 40 으로 같은 줄의mdTextField · Button 과,sm(3 + 26 + 3)은 32 로sm컨트롤과 높이가 맞습니다. -
크기 단계가 바꾸는 값: 트랙 안쪽 여백(4 / 3) · 트랙 모서리(10 / 8) · 칸 높이(32 / 26) · 칸 좌우 여백(2 / 0) · 라벨 글자 역할(
labelLarge/labelMedium). 칸 모서리 6 · 라벨 박스 좌우 여백 8 · 아이콘 크기 16 은 두 단계가 같습니다. 칸 모서리 6 은md트랙 10 에서 안쪽 여백 4 를 뺀 값이라 트랙과 칸이 같은 중심을 씁니다. -
칸은 높이보다 좁아지지 않습니다(최소 폭 = 칸 높이) — 아이콘만 있는 칸은
md32×32 ·sm26×26 정사각형이고, 높이보다 넓은 라벨 칸은 제 폭 그대로입니다. -
sm두 칸은3 + 26 + 2 + 26 + 3 = 60으로, 시간 선택기의 시 · 분 칸 높이와 맞습니다. 그래서 시간 선택기의 AM / PM 과 색 선택기의 모드 탭이sm을 씁니다. 세로 배치(direction: .vertical)에서도 같은 60 입니다 — 시간 선택기의 AM / PM 은 판 폭을 좁게 두려고 시 · 분 칸 옆에 세로로, 칸 위쪽에 맞춰 놓습니다. -
direction: .vertical이면 칸이 위에서 아래로 놓이고 칸 사이 간격itemSpacing(2) · 트랙 안쪽 여백은 가로와 같습니다. 칸은 가장 넓은 칸의 폭으로 맞춰지고(Flutter 는Column의 stretch, Web 은flex-col+items-stretch) 내용은 가운데입니다. 선택한 칸의 면 · 그림자는 칸마다 칠해지므로 방향과 무관하게 같은 칸을 따라갑니다. 세로sm두 칸은3 + 26 + 2 + 26 + 3 = 60, 세로md두 칸은4 + 32 + 2 + 32 + 4 = 74입니다. 방향키는 위 · 아래 · 좌 · 우 모두 칸을 옮깁니다. -
Figma 의
ToggleGroup은Size=md | sm축이Width=Hug | Fill·Orientation=Horizontal | Vertical축과 직교로 있고, 판 안 인스턴스는Size=sm입니다. - 칸의 폭은 가장 굵은(선택) 글자 폭으로 잡혀, 선택이 옮겨 가도 그룹의 폭이 변하지 않습니다.
-
fillWidth: true이면 트랙이 부모 폭을 채우고 칸이 폭을 똑같이 나눠 가집니다(예: 3칸이면 3등분, 칸 안 라벨은 가운데). 칸 사이 간격itemSpacing과 트랙 안쪽 여백은 그대로입니다. 부모의 폭이 정해져 있어야 합니다(가로로 무한한 자리에서는 쓰지 않습니다). Figma 의ToggleGroup은Width=Hug | Fill축으로 같은 표현을 갖고,Fill변형의 인스턴스는 가로 크기를 FILL 로 둡니다.
사용 가이드라인 (Usage Guidelines)#
Do#
- 옵션이 2~4개이고 이름이 짧을 때 쓰세요.
- 항상 하나가 선택된 상태에서 시작하세요 (
value가null이면 아무것도 강조되지 않습니다).
Don't#
접근성 (Accessibility)#
역할#
-
Web: 트랙이
role="radiogroup", 각 칸이role="radio"+aria-checked+aria-label=item.label입니다. 비활성이면 그룹에aria-disabled="true"가 붙고 칸은 탭 정지점이 없습니다. -
Flutter: 각 칸이
Semantics(checked:, inMutuallyExclusiveGroup: true, enabled:, label:)입니다. 합성한Clickable은 버튼 역할을 내려놓습니다(semanticsButton: false).
키보드#
| 키 | Flutter | Web |
|---|---|---|
Tab | 칸마다 정지 | 그룹당 하나(선택한 칸, 없으면 첫 칸) |
Enter / Space | 포커스된 칸 선택 | 포커스된 칸 선택 |
← → ↑ ↓ |
프레임워크 방향성 포커스 이동 (선택은 바꾸지 않음) | 이전/다음 칸으로 이동하며 선택, 끝에서 순환 |
Web 은 WAI-ARIA radio group 패턴(roving tabindex)을 따릅니다.
스크린 리더#
각 칸의 이름은 item.label 입니다. showLabel: false 인 아이콘 칸도 이름이 남습니다. 그룹 자체의 이름은 없습니다 — 호출부가 곁에 보이는 제목을 두세요.
포커스 관리#
오버레이가 아니므로 포커스 이동·트랩이 없습니다. 포커스 링은 합성한 Clickable 의 기본 링이 칸마다 그립니다.
알려진 제약#
- Flutter 는 칸마다 탭 정지점이 있고 화살표가 선택을 바꾸지 않습니다(Web 만 radio group 키 패턴).
- 개별 칸 비활성화는 없습니다.
이 컴포넌트가 직접 다루지 않는 대비·모션 등 전역 축은 전역 접근성 축 에서 한 곳으로 관리됩니다.
크로스 플랫폼 차이점 (Platform Differences)#
items / value / onChanged / enabled / size
/ direction / fillWidth / toggleGroupStyle 은 양 플랫폼 동일 API 입니다.
| 항목 | Flutter | Web |
|---|---|---|
| 트랙 | 합성한 OutlinedContainer |
<div role="radiogroup"> + 콘텐츠 표면 표시 |
| 선택 의미 | Semantics(checked, inMutuallyExclusiveGroup) |
role="radio" · aria-checked |
| 키보드 | 칸별 Tab · Enter / Space | roving tabindex · 양 축 화살표로 선택 이동 |
| hover 상태 레이어 | Clickable.onHover 가 잡고 칸의 foregroundDecoration 으로 깝니다 |
칸의 마커 클래스 + 인라인
--coui-state-layer-hover
, 전역 규칙이
:hover
에서
background-image
로 깝니다
|
| 선택 면 그림자 | BoxShadow (CoreShadow.xs) |
box-shadow: var(--shadow-xs) |
| 전환 | AnimatedContainer | CSS transition |
| 폭 유지 | 숨긴 세미볼드 텍스트가 폭만 차지 | aria-hidden 라벨 고스트 |
관련 컴포넌트 (Related Components)#
- Filter: 같은 모양에 초기화 버튼이 붙은 필터
- Tabs: 콘텐츠 패널 전환
- RadioGroup: 폼 안의 라디오 선택