Segmented Control
같은 내용을 2 ~ 4가지로 바로 거르거나 · 정렬하거나 · 다르게 보는 컨트롤. 그 내용 바로 위에, 한 화면에 하나 둔다. 다른 구역으로 옮기면 Tabs, 2 ~ 4개 짧은 폼 값은 Chip, 목록 조건을 여럿 걸고 풀면 Chip 의 필터 바다.
구조는 당근 SEED Segmented Control(Apache-2.0)을 따른다 — 트랙 · 칸 · 글 · 고른 알약. 칸 폭만 groo 가 다시 정했고(칸이 트랙을 똑같이 나눈다), 나머지 값은 groo 토큰이다. SEED 와 다른 자리는 맨 아래 "SEED 와 다른 점" 에 적었다(2026-10-02 사용자 결정).
수치 원본은 segmented-control.yaml이다. 수치 표 자리([표: …])와 그림 자리([그림: …])는 사이트가 그 파일로 그린다 — GitHub 에서는 그 파일 · 그림 코드로 가는 링크로 보인다.
직접 골라 보기
칸 수 · 고른 칸 · 알림 점 · 막힌 칸 · 긴 글을 고르면 스펙대로 그린 컨트롤과 그 코드가 바뀐다. 실제로 누르고 화살표로 옮길 수 있다.
- 점심 식사식비 · 현대카드 M−12,000원
- 급여수입 · 국민은행+3,200,000원
- 스타벅스카페 · 현대카드 M−5,600원
- 지하철교통 · 국민 체크카드−1,450원
- 중고 거래수입 · 토스뱅크 통장+25,000원
import { SegmentedControl, SegmentedControlItem } from "@/components/ui/segmented-control"
const [view, setView] = useState("all")
<SegmentedControl aria-label="가계부 보기" value={view} onValueChange={setView}>
<SegmentedControlItem value="all">전체</SegmentedControlItem>
<SegmentedControlItem value="expense">지출</SegmentedControlItem>
<SegmentedControlItem value="income">수입</SegmentedControlItem>
</SegmentedControl>Anatomy
| 부위 | 설명 |
|---|---|
| ⓐ Track | 트랙 — 알약. 놓인 자리 폭을 채우고, 칸이 그 폭을 똑같이 나눈다. |
| ⓑ Segment | 칸 — 누르는 자리. 모든 칸이 같은 폭 · 같은 높이다. |
| ⓒ Label | 글 — 짧게. 길면 단어 단위로 줄을 바꾸고 모든 칸이 가장 높은 칸에 맞춘다. |
| ⓓ Indicator | 고른 알약 — 고른 칸 뒤에 깔리고, 다른 칸을 고르면 미끄러져 옮긴다. |
| ⓔ Notification | 알림 점 — 새 소식이 있는 칸에만, 글 오른쪽 위. |
부위
| 부위 | 설명 |
|---|---|
root | 트랙 — 알약. 놓인 자리 폭을 채우고 칸이 그 폭을 똑같이 나눈다 |
item | 칸 — 누르는 자리. 모든 칸이 같은 폭 · 같은 높이 |
label | 글 — 짧게. 길면 단어 단위로 줄을 바꾼다 |
indicator | 고른 알약 — 고른 칸 뒤에 깔리고, 다른 칸을 고르면 미끄러져 옮긴다 |
notification | 알림 점 — 새 소식이 있는 칸에만, 글 오른쪽 위 |
focusRing | 키보드 포커스 링(웹) — 칸 바깥 |
Properties
크기 · 변형이 하나다. 트랙 안쪽 4 + 칸 34 = 42, 글 16 · 700(고르든 안 고르든 — 글자 크기 설정을 따르지 않는 t5-static), 칸 좌우 12. 칸은 트랙 폭을 칸 수로 똑같이 나누고 최소 폭이 없어, 폰(콘텐츠 폭 312)에서도 4개가 들어간다. 넓은 화면에서는 트랙이 놓인 자리를 채우므로 자리를 좁혀 둔다.
State
| 상태 | 모습 |
|---|---|
enabled | 트랙 bg-neutral-weak-alpha(투명 — 어느 바탕에서나 보인다, v120) 위 안 고른 글 fg-neutral-subtle(다크는 fg-neutral-muted — 투명 트랙 위 4.5:1 을 지키려고, 2026-10-17 사용자 결정 — 01-040 B 후속), 고른 칸은 흰 알약(bg-layer-default + 안쪽 짙은 1px stroke-neutral-contrast) 위 fg-neutral |
hovered | 웹 — 누름과 같은 바탕(축소 없음) |
pressed | 안 고른 칸 bg-neutral-weak-pressed + 1px stroke-neutral-weak(글은 fg-neutral-muted), 고른 칸 bg-layer-default-pressed + 짙은 1px 그대로. 칸 바탕은 그대로 두고 안의 글만 2px 거리로 축소한다 |
focused | 키보드 포커스에만 링 2px · 띄움 2px — 칸 바깥 |
disabled | 글 fg-disabled · 커서 not-allowed — 흐리게 하지 않는다(v106). 고른 채 막히면 칸에 bg-disabled + 짙은 1px stroke-neutral-solid 를 남긴다 |
상태 — 안 고름
| 상태 | item 배경 | item 테두리 | item 테두리 두께 | item 커서 | label 글자·아이콘 | label 배율 | label 축소 시간 | label 축소 곡선 | 링 두께 | 링 간격 | 링 색 |
|---|---|---|---|---|---|---|---|---|---|---|---|
enabled 기본 | — | — | — | pointer | color-fg-neutral-subtle #62697A · 다크 color-fg-neutral-muted #535866 — 다크는 한 단계 짙게 — 투명 트랙이 다크 표면에 섞여 밝아져 fg-neutral-subtle 이 카드 위 4.09 · 시트 위 3.56 이다. muted 는 카드 5.18 · 시트 4.50 · 바닥 5.93, 라이트는 subtle 그대로 4.59 ~ 4.95(2026-10-17 사용자 결정 — 01-040 B 후속) | — | — | — | — | — | — |
hovered 웹 — 누름과 같은 바탕(v106), 축소는 없다 | color-bg-neutral-weak-pressed #EDEFF3 · 다크 #404757 | color-stroke-neutral-weak #E5E8EF · 다크 #404757 — 안쪽 1px — 고른 알약과 같은 테두리 | 1px | pointer | color-fg-neutral-muted #535866 · 다크 #B7BDCC — 누름 바탕 위에서 한 단계 짙게 — fg-neutral-subtle 이면 다크 3.91:1, muted 4.95 · 라이트 6.17(List 의 강조 줄과 같은 방법) | — | — | — | — | — | — |
pressed 누름 바탕 + 칸 안의 글만 2px 거리 축소(바탕은 그대로) | color-bg-neutral-weak-pressed #EDEFF3 · 다크 #404757 | color-stroke-neutral-weak #E5E8EF · 다크 #404757 | 1px | pointer | color-fg-neutral-muted #535866 · 다크 #B7BDCC | 글 2px 거리 축소 — 칸 바탕은 그대로, 안의 글만 준다(SEED scaleScope: content · v104) — 모션 줄이기면 하지 않는다 | motion-duration-pressed-scale 150ms | motion-ease-pressed-scale cubic-bezier(0, 0, 0.15, 1) | — | — | — |
focused 웹 — 키보드 포커스에만 링 | — | — | — | pointer | color-fg-neutral-subtle #62697A · 다크 color-fg-neutral-muted #535866 — 다크는 한 단계 짙게 — 투명 트랙이 다크 표면에 섞여 밝아져 fg-neutral-subtle 이 카드 위 4.09 · 시트 위 3.56 이다. muted 는 카드 5.18 · 시트 4.50 · 바닥 5.93, 라이트는 subtle 그대로 4.59 ~ 4.95(2026-10-17 사용자 결정 — 01-040 B 후속) | — | — | — | 2px | 2px — 칸 바깥 — 알약을 따라 둥글다 | color-stroke-focus-ring #1A1F2E · 다크 #F5F6FA |
disabled 막힌 칸 · 막힌 트랙 — 전용 색(v106), 흐리게 하지 않는다 | — | — | — | not-allowed — 누름 바탕 · 축소 없음 | color-fg-disabled #8A91A0 · 다크 #838997 | — | — | — | — | — | — |
상태 — 고름
| 상태 | item 배경 | item 테두리 | item 테두리 두께 | item 커서 | label 글자·아이콘 | label 배율 | label 축소 시간 | label 축소 곡선 | 링 두께 | 링 간격 | 링 색 |
|---|---|---|---|---|---|---|---|---|---|---|---|
enabled 기본 | — | — | — | pointer | color-fg-neutral #1A1F2E · 다크 #F5F6FA — 글자색만 바뀐다 — 굵기 · 크기 그대로 | — | — | — | — | — | — |
hovered 웹 — 누름과 같은 바탕(v106), 축소는 없다 | color-bg-layer-default-pressed #F7F8FD · 다크 #353B4D — 칸에 칠해 알약을 덮는다 — 짙은 테두리는 그대로 | color-stroke-neutral-contrast #1A1F2E · 다크 #F5F6FA | 1px | pointer | color-fg-neutral #1A1F2E · 다크 #F5F6FA — 글자색만 바뀐다 — 굵기 · 크기 그대로 | — | — | — | — | — | — |
pressed 누름 바탕 + 칸 안의 글만 2px 거리 축소(바탕은 그대로) | color-bg-layer-default-pressed #F7F8FD · 다크 #353B4D — SEED palette.gray-100 의 역할 짝 — 짙은 테두리는 그대로 | color-stroke-neutral-contrast #1A1F2E · 다크 #F5F6FA | 1px | pointer | color-fg-neutral #1A1F2E · 다크 #F5F6FA — 글자색만 바뀐다 — 굵기 · 크기 그대로 | 글 2px 거리 축소 — 칸 바탕은 그대로, 안의 글만 준다(SEED scaleScope: content · v104) — 모션 줄이기면 하지 않는다 | motion-duration-pressed-scale 150ms | motion-ease-pressed-scale cubic-bezier(0, 0, 0.15, 1) | — | — | — |
focused 웹 — 키보드 포커스에만 링 | — | — | — | pointer | color-fg-neutral #1A1F2E · 다크 #F5F6FA — 글자색만 바뀐다 — 굵기 · 크기 그대로 | — | — | — | 2px | 2px — 칸 바깥 — 알약을 따라 둥글다 | color-stroke-focus-ring #1A1F2E · 다크 #F5F6FA |
disabled 막힌 칸 · 막힌 트랙 — 전용 색(v106), 흐리게 하지 않는다 | color-bg-disabled #F5F6FA · 다크 #353B4D — 고른 채 막힘 — 칸에 칠해 알약을 덮는다 | color-stroke-neutral-solid #767C8B · 다크 #838997 — 짙은 1px 를 회색으로 남겨 무엇을 골랐는지 보인다(Chip 의 고른 채 막힘과 같다) | 1px | not-allowed — 누름 바탕 · 축소 없음 | color-fg-disabled #8A91A0 · 다크 #838997 | — | — | — | — | — | — |
공통
| 부위 | 속성 | 값 | 비고 |
|---|---|---|---|
root | 여백 | spacing-x1 4px | |
root | 모서리 | radius-full 9999px | |
root | 배경 | color-bg-neutral-weak-alpha #0000000C · 다크 #FFFFFF20 | SEED 그대로 투명(검정 4.7% · 다크 흰 12.5%) — 놓인 바탕에 섞여 회색 바닥 · 흰 면 위 1.11 · 다크 1.47:1(v120) |
root | 너비 | 놓인 자리 폭 | 트랙이 자리를 채우고 칸 수로 똑같이 나눈다(최소 폭 없음) — 넓은 화면에서는 놓는 자리를 좁힌다. 머리 오른쪽 자리(카드 · 구역 머리)에서는 내용 폭 = (가장 긴 칸 글 + 칸 좌우 12 · 12) × 칸 수 + 트랙 안쪽 4 · 4 — 칸 글이 글자 크기를 따르지 않아(t5-static) 폭이 흔들리지 않는다. 제목이 머리 폭의 50% 이상을 지키지 못하면 머리 아래 줄 오른쪽 끝으로 내려간다(2026-10-25 08-2 — 웹 · 앱이 이 규칙으로 맞췄다) |
root | 칸 수 | 칸 수(2 ~ 4) | 모든 칸이 같은 폭 · 같은 높이(가장 높은 칸에 맞춘다) |
item | 최소 높이 | 34px | 트랙 안쪽 4 를 더해 42 |
item | 좌우 여백 | spacing-x3 12px | SEED 24 → 12 — 칸이 트랙을 나누므로 폰에서 4개도 들어간다 |
item | 상하 여백 | spacing-x1_5 6px | |
item | 모서리 | radius-full 9999px | |
item | 커서 | pointer | |
item | 터치 영역 | 칸 전체 | 34 × 칸 폭 — AA ✓(24 이상) · AAA 는 44 에 모자란다(SEED 와 같다) |
item | 전환 대상 | background-color, color, box-shadow | |
item | 전환 시간 | motion-duration-color-transition 150ms | |
item | 전환 곡선 | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | |
label | 글자 | text-t5-static 16px / 22px | 16 / 22 — 고르든 안 고르든 같다. -static — 글자 크기 설정을 따르지 않는다(웹 text-t5-static, 앱 TextScaler.noScaling). 큰 글자에서 4칸이 "오 / 늘" 처럼 한 글자씩 쪼개지지 않게 — WCAG 1.4.4 의 예외(하단 탭 라벨과 같은 대가, 2026-10-17 사용자 결정 — 03-B 결정 4 A. 2 × 2 격자는 고르지 않았다) |
label | 굵기 | 700 | 고르든 안 고르든 700 |
label | 글자·아이콘 | color-fg-neutral-subtle #62697A · 다크 color-fg-neutral-muted #535866 | 다크는 한 단계 짙게 — 투명 트랙이 다크 표면에 섞여 밝아져 fg-neutral-subtle 이 카드 위 4.09 · 시트 위 3.56 이다. muted 는 카드 5.18 · 시트 4.50 · 바닥 5.93, 라이트는 subtle 그대로 4.59 ~ 4.95(2026-10-17 사용자 결정 — 01-040 B 후속) |
label | 글 맞춤 | center | 길면 단어 단위로 줄을 바꾼다(v114) |
indicator | 배경 | color-bg-layer-default #FFFFFF · 다크 #242938 | 흰 알약 — 다크에서는 트랙보다 어둡다(SEED 도 다크 알약이 검정) |
indicator | 테두리 | color-stroke-neutral-contrast #1A1F2E · 다크 #F5F6FA | 안쪽 1px(inset) 짙은 테두리 — 고른 칸을 트랙과 라이트 13.71 ~ 14.79 · 다크 7.84 ~ 10.33:1 로 가른다(SEED 옅은 1px 은 1.14 · 1.20 — 사용자 결정 2026-10-02) |
indicator | 테두리 두께 | 1px | |
indicator | 모서리 | radius-full 9999px | |
indicator | 알약 들임 | 4px | 트랙 안쪽 4 — 폭 = (트랙 − 8) ÷ 칸 수, 고른 칸 번호만큼 옆으로 옮긴다 |
indicator | 전환 대상 | transform | 고른 칸으로 미끄러진다 |
indicator | 전환 시간 | motion-duration-d4 200ms | |
indicator | 전환 곡선 | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | |
notification | 크기 | 6px | |
notification | 모서리 | radius-full 9999px | |
notification | 배경 | color-fg-brand HR #075B3C · Desk #0147AD | 브랜드 글자색 — 트랙 위 Desk 라이트 7.00 ~ 7.55 · 다크 3.56 ~ 4.69 · HR 6.82 ~ 7.36 · 3.55 ~ 4.67(v120 투명 트랙 — 놓인 바탕에 따라, bg-brand-solid 는 다크에서 Desk · HR 1.33 이라 안 보인다). 고른 칸에는 없다 — 고르면 사라진다 |
notification | 간격 | 2px | 글 끝에서 2, 글 위쪽에 맞춘다 |
모션
| 전환 | 시간 | 곡선 | 대상 | 비고 |
|---|---|---|---|---|
고른 알약 — 미끄러짐 | motion-duration-d4 200ms | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | transform | |
누름 · 호버 · 고름 — 색 | motion-duration-color-transition 150ms | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | background-color, color, box-shadow | |
누름 — 글 | motion-duration-pressed-scale 150ms | motion-ease-pressed-scale cubic-bezier(0, 0, 0.15, 1) | scale | 칸 안의 글만 2px 거리 축소 — 모션 줄이기면 하지 않는다 |
Guidelines
같은 내용을 바로 바꿀 때
Segmented Control 은 조작이다 — 고르면 화면의 그 내용만 바로 거르거나 · 정렬하거나 · 다르게 보인다(프리셋을 많이 쓴 순 · 최근 사용 · 이름순으로, 더치페이를 진행 중 · 완료 · 친구로). 다른 구역 · 페이지로 옮기는 탐색에는 쓰지 않는다 — 그건 Tabs 다. 저장할 폼 값도 아니다 — 폼 값은 Chip · Select.
비슷한 셋은 이렇게 견준다. 폼 값 · 설정 값을 고르는 표는 Select 의 "고르는 컴포넌트 고르기" 한 곳이다(2026-10-22 사용자 결정 1B — https://claude.ai/artifact/SmHD31RQxTjWbfK83nbcym).
| Segmented Control | Chip 필터 바 | Tabs | |
|---|---|---|---|
| 핵심 용도 | 같은 내용을 거르기 · 정렬 · 다르게 보기 | 조건을 여럿 걸고 푼다 | 다른 구역 · 화면으로 옮긴다 |
| 위치 | 바꾸는 내용 바로 위 | 목록 위 한 줄(가로 스크롤 · 끝 흐림) | 화면 · 구역 맨 위 |
| 영향 범위 | 그 내용만 | 그 목록만 | 아래 전체를 바꾼다 |
| 개수 | 2 ~ 4(사용자 결정) | 정해지지 않음 · 개수가 바뀐다 | 1차 Fill 5개까지 |
| 한 화면에 | 하나(사용자 결정) — 둘째 축은 Chip 하나 고르기 · Select | 하나 | 1차 하나 + 2차 Chip Tabs |
내용 바로 위에, 한 화면에 하나
자기가 바꾸는 내용 바로 위에 둔다 — 떨어져 있으면 무엇을 바꾸는지 알 수 없다. 한 화면에 하나만 둔다. 같은 모양이 둘이면 어느 것이 어느 내용을 바꾸는지 헷갈린다 — 둘째 축은 Chip(하나 고르기) · Select 로 둔다.
머리 오른쪽에 둘 때
카드 · 구역 머리의 오른쪽(제목과 같은 줄)에 두면 트랙이 자리를 채우지 않고 내용 폭이다 — 모든 칸이 가장 긴 칸 글에 칸 좌우 여백을 더한 같은 폭이고, 트랙은 그 칸들에 안쪽 여백을 더한 폭이다(셈은 segmented-control.yaml 의 root.width 비고). 칸 글은 글자 크기를 따르지 않으므로 큰 글자에서도 폭이 그대로다. 제목은 머리 폭의 절반 이상을 지킨다 — 그 자리가 남지 않으면 Segmented 가 머리 아래 줄 오른쪽 끝으로 내려간다. List 목록 제목 · Card 머리 동작의 반 폭 판정과 같은 결이다(2026-10-25 08-2 — 통계 고정 탭 월별 "지출 · 수입", 웹 · 앱이 이 규칙으로 맞췄다). 머리 오른쪽에 둬도 "자기가 바꾸는 내용 바로 위" 다 — 머리 아래가 곧 그 내용일 때만 둔다.
2 ~ 4개, 글은 짧게
칸은 2 ~ 4개다. 5개 이상이면 Chip 의 하나 고르기 줄 · Select 로 둔다. 글은 짧게("이름순" · "최근 사용") — 칸에 비해 글이 길면 줄이 바뀌어 모든 칸이 높아진다. 그러면 다른 컴포넌트를 쓴다.
코드
레시피 recipes/shadcn/components/ui/segmented-control.tsx 를 쓴다(라디오 묶음 — 화살표로 옮기면 바로 고른다). 이름은 aria-label 로 단다. 늘 하나가 골라져 있어야 하므로 value(또는 defaultValue)를 준다. 아래 미리보기는 스펙 값으로 그린 모습이다.
- 카드 대금 확인오늘
- 관리비 이체오늘
import { SegmentedControl, SegmentedControlItem } from "@/components/ui/segmented-control"
<SegmentedControl aria-label="할 일 보기" value={view} onValueChange={setView}>
<SegmentedControlItem value="today">오늘</SegmentedControlItem>
<SegmentedControlItem value="week">이번 주</SegmentedControlItem>
<SegmentedControlItem value="all">전체</SegmentedControlItem>
<SegmentedControlItem value="done">완료</SegmentedControlItem>
</SegmentedControl>Behavior
| 인터랙션 | 동작 |
|---|---|
| Click · Tap | 그 칸을 고르고 내용을 바로 바꾼다. 고른 알약이 200ms 로 미끄러진다. 고른 칸을 다시 눌러도 그대로다. |
← → · ↑ ↓ | 이웃 칸으로 옮기며 바로 고른다(라디오). 막힌 칸은 건너뛴다. |
Home · End | 받지 않는다 — 초점도 고른 칸도 그대로, 화면도 스크롤되지 않는다(PageUp · PageDown 도). 첫 · 끝 칸으로는 화살표로 간다 — SEED · 진짜 라디오 · WAI-ARIA APG Radio Group 과 같다(2026-10-17 사용자 결정 — 03-E 결정 3 A). Tabs 는 Home · End 로 옮기며 고른다. |
Tab | 고른 칸 하나에만 선다. |
| 마우스 호버 | 누름과 같은 바탕(축소 없음). |
| Disabled | 칸 하나 또는 트랙 전체를 막는다 — 누를 수 없다 · 커서 not-allowed · 화살표 이동에서 건너뛴다. |
Accessibility
| 기준 | 검증 |
|---|---|
| WCAG 1.4.3 Color contrast(글자 ≥ 4.5:1) | 안 고른 글 fg-neutral-subtle 트랙 위 — 트랙이 투명해 놓인 바탕에 따라 다르다(v120): 라이트 흰 면 4.95 · 회색 바닥 4.59, 다크 바닥 4.68 ✓ · 다크는 fg-neutral-muted 로 한 단계 짙게 — 다크 카드(bg-layer-default) 5.18 · 다크 시트(bg-layer-floating) 4.50 · 다크 바닥 5.93 ✓(subtle 이면 4.09 · 3.56 — 2026-10-17 사용자 결정 — 01-040 B 후속). 올리거나 누르는 동안 fg-neutral-muted 6.17 · 4.95, 고른 글 fg-neutral 알약 위 16.41 · 13.42 · 누름 15.48 · 10.32 ✓. 비활성 fg-disabled 는 기준 밖(비활성 UI) |
| WCAG 1.4.11 Non-text contrast(≥ 3:1) | 고른 칸은 알약의 짙은 1px stroke-neutral-contrast(트랙과 라이트 13.71 ~ 14.79 · 다크 7.84 ~ 10.33) ✓ — SEED 의 옅은 1px(1.14 · 1.20)을 바꿨다. 고른 채 막힘 stroke-neutral-solid 은 bg-disabled 위 3.87 · 3.18 ✓(비활성이라 기준 밖). 키보드 포커스 링 Desk 8.38 · 6.10 · HR 8.17 · 6.07 ✓. 알림 점 fg-brand 트랙 위 Desk 라이트 7.00 ~ 7.55 · 다크 3.56 ~ 4.69 · HR 라이트 6.82 ~ 7.36 · 다크 3.55 ~ 4.67 ✓ — 고른 칸에는 그리지 않는다 |
| WCAG 1.4.4 Resize text | 칸 글은 예외다 — t5-static 16 이라 글자 크기 설정을 따르지 않는다(웹 text-t5-static · 앱 TextScaler.noScaling). 칸이 트랙을 나눠 폰 4칸이 76(글 자리 52)이라, 200% 에서 "오 / 늘" · "이 / 번 / 주" 처럼 한 글자씩 세로로 읽혔다. 하단 탭 라벨과 같은 대가(DESIGN v104 -static 목록, 2026-10-17 사용자 결정 — 03-B 결정 4 A). 큰 글자에서 2 × 2 격자로 바꾸는 안은 고르지 않았다 |
| WCAG 2.4.7 Focus visible | 키보드 포커스에 링 2px · 띄움 2px — 칸 바깥 |
| WCAG 2.5.8 Target Size — Minimum(AA ≥ 24×24) | 칸 34 × 칸 폭 ✓(폰 4개면 칸 폭 76) |
| WCAG 2.5.5 Target Size — Enhanced(AAA ≥ 44×44) | 칸 34 ✗(SEED 와 같다) |
| ARIA | 트랙 role="radiogroup" + aria-label, 칸은 라디오(aria-checked) — 고른 칸만 tabindex="0". 막힌 칸 disabled. 알림 점은 보조 기술에 "새 소식" 을 덧붙인다(점만으로 알리지 않는다 — Tabs · Chip Tabs · 하단 탭 바와 같은 말, 2026-10-15 사용자 결정 — 구현 세션 질문 Q-010-4, 안 A) |
Do / Don't
✅ Do
- 같은 내용을 2 ~ 4가지로 바로 바꾸는 자리에 쓰고, 그 내용 바로 위에 둔다.
- 한 화면에 하나.
- 글은 짧게.
- 늘 하나를 골라 둔다.
❌ Don't
- 다른 구역 · 페이지로 옮기는 데 쓰기(Tabs).
- 저장할 폼 값으로 쓰기(Chip · Select).
- 한 화면에 둘 이상.
- 5개 이상 · 긴 글.
Specification
segmented-control.yaml 의 규칙을 하나도 빼지 않고 조건마다 그린다 — 웹 · 앱이 Segmented Control 을 만들 때 이 값을 그대로 쓴다. 조건이 없는 Base 가 모든 조합에 걸리고, 뒤의 규칙이 앞의 같은 값을 덮는다. 상태는 enabled 에서 바뀌는 값만 적었다.
Base
| 부위 | 상태 | 속성 | 값 |
|---|---|---|---|
root | enabled | 너비width | 놓인 자리 폭트랙이 자리를 채우고 칸 수로 똑같이 나눈다(최소 폭 없음) — 넓은 화면에서는 놓는 자리를 좁힌다. 머리 오른쪽 자리(카드 · 구역 머리)에서는 내용 폭 = (가장 긴 칸 글 + 칸 좌우 12 · 12) × 칸 수 + 트랙 안쪽 4 · 4 — 칸 글이 글자 크기를 따르지 않아(t5-static) 폭이 흔들리지 않는다. 제목이 머리 폭의 50% 이상을 지키지 못하면 머리 아래 줄 오른쪽 끝으로 내려간다(2026-10-25 08-2 — 웹 · 앱이 이 규칙으로 맞췄다) |
| 여백padding | spacing-x14px | ||
| 칸 수columns | 칸 수(2 ~ 4)모든 칸이 같은 폭 · 같은 높이(가장 높은 칸에 맞춘다) | ||
| 모서리radius | radius-full9999px(알약) | ||
| 배경background | bg-neutral-weak-alpha#0000000C다크 #FFFFFF20SEED 그대로 투명(검정 4.7% · 다크 흰 12.5%) — 놓인 바탕에 섞여 회색 바닥 · 흰 면 위 1.11 · 다크 1.47:1(v120) | ||
item | enabled | 최소 높이minHeight | 34px트랙 안쪽 4 를 더해 42 |
| 상하 여백paddingY | spacing-x1_56px | ||
| 좌우 여백paddingX | spacing-x312pxSEED 24 → 12 — 칸이 트랙을 나누므로 폰에서 4개도 들어간다 | ||
| 모서리radius | radius-full9999px(알약) | ||
| 커서cursor | pointer | ||
| 터치 영역touchTarget | 칸 전체34 × 칸 폭 — AA ✓(24 이상) · AAA 는 44 에 모자란다(SEED 와 같다) | ||
| 전환 대상transitionProperty | background-color, color, box-shadow | ||
| 전환 시간transitionDuration | motion-duration-color-transition150ms | ||
| 전환 곡선transitionEasing | motion-ease-easingcubic-bezier(0.35, 0, 0.35, 1) | ||
label | enabled | 글자·아이콘foreground | fg-neutral-subtle#62697A다크 #A2A8B7다크 fg-neutral-muted#535866다크 #B7BDCC다크는 한 단계 짙게 — 투명 트랙이 다크 표면에 섞여 밝아져 fg-neutral-subtle 이 카드 위 4.09 · 시트 위 3.56 이다. muted 는 카드 5.18 · 시트 4.50 · 바닥 5.93, 라이트는 subtle 그대로 4.59 ~ 4.95(2026-10-17 사용자 결정 — 01-040 B 후속) |
| 글자typography | text-t5-static16 / 22 — 고르든 안 고르든 같다. -static — 글자 크기 설정을 따르지 않는다(웹 text-t5-static, 앱 TextScaler.noScaling). 큰 글자에서 4칸이 "오 / 늘" 처럼 한 글자씩 쪼개지지 않게 — WCAG 1.4.4 의 예외(하단 탭 라벨과 같은 대가, 2026-10-17 사용자 결정 — 03-B 결정 4 A. 2 × 2 격자는 고르지 않았다) | ||
| 굵기fontWeight | 700고르든 안 고르든 700 | ||
| 글 맞춤textAlign | center길면 단어 단위로 줄을 바꾼다(v114) | ||
indicator | enabled | 모서리radius | radius-full9999px(알약) |
| 배경background | bg-layer-default#FFFFFF다크 #242938흰 알약 — 다크에서는 트랙보다 어둡다(SEED 도 다크 알약이 검정) | ||
| 테두리borderColor | stroke-neutral-contrast#1A1F2E다크 #F5F6FA안쪽 1px(inset) 짙은 테두리 — 고른 칸을 트랙과 라이트 13.71 ~ 14.79 · 다크 7.84 ~ 10.33:1 로 가른다(SEED 옅은 1px 은 1.14 · 1.20 — 사용자 결정 2026-10-02) | ||
| 테두리 두께borderWidth | 1px | ||
| 전환 대상transitionProperty | transform고른 칸으로 미끄러진다 | ||
| 전환 시간transitionDuration | motion-duration-d4200ms | ||
| 전환 곡선transitionEasing | motion-ease-easingcubic-bezier(0.35, 0, 0.35, 1) | ||
| 알약 들임inset | 4px트랙 안쪽 4 — 폭 = (트랙 − 8) ÷ 칸 수, 고른 칸 번호만큼 옆으로 옮긴다 | ||
notification | enabled | 크기size | 6px |
| 간격gap | 2px글 끝에서 2, 글 위쪽에 맞춘다 | ||
| 모서리radius | radius-full9999px(알약) | ||
| 배경background | fg-brandDesk#0147AD다크 #7AA9F6HR#075B3C다크 #2ABE84브랜드 글자색 — 트랙 위 Desk 라이트 7.00 ~ 7.55 · 다크 3.56 ~ 4.69 · HR 6.82 ~ 7.36 · 3.55 ~ 4.67(v120 투명 트랙 — 놓인 바탕에 따라, bg-brand-solid 는 다크에서 Desk · HR 1.33 이라 안 보인다). 고른 칸에는 없다 — 고르면 사라진다 | ||
label | pressed | 배율scale | 글 2px 거리 축소칸 바탕은 그대로, 안의 글만 준다(SEED scaleScope: content · v104) — 모션 줄이기면 하지 않는다 |
| 축소 시간scaleDuration | motion-duration-pressed-scale150ms | ||
| 축소 곡선scaleEasing | motion-ease-pressed-scalecubic-bezier(0, 0, 0.15, 1) | ||
focusRing | focused | 링 두께width | 2px |
| 링 색color | stroke-focus-ringDesk#0147AD다크 #7AA9F6HR#075B3C다크 #2ABE84 | ||
| 링 간격offset | 2px칸 바깥 — 알약을 따라 둥글다 | ||
item | disabled | 커서cursor | not-allowed누름 바탕 · 축소 없음 |
label | disabled | 글자·아이콘foreground | fg-disabled#8A91A0다크 #838997 |
selected=unselected
| 부위 | 상태 | 속성 | 값 |
|---|---|---|---|
item | hovered | 배경background | bg-neutral-weak-pressed#EDEFF3다크 #404757 |
| 테두리borderColor | stroke-neutral-weak#E5E8EF다크 #404757안쪽 1px — 고른 알약과 같은 테두리 | ||
| 테두리 두께borderWidth | 1px | ||
label | hovered | 글자·아이콘foreground | fg-neutral-muted#535866다크 #B7BDCC누름 바탕 위에서 한 단계 짙게 — fg-neutral-subtle 이면 다크 3.91:1, muted 4.95 · 라이트 6.17(List 의 강조 줄과 같은 방법) |
item | pressed | 배경background | bg-neutral-weak-pressed#EDEFF3다크 #404757 |
| 테두리borderColor | stroke-neutral-weak#E5E8EF다크 #404757 | ||
| 테두리 두께borderWidth | 1px | ||
label | pressed | 글자·아이콘foreground | fg-neutral-muted#535866다크 #B7BDCC |
selected=selected
| 부위 | 상태 | 속성 | 값 |
|---|---|---|---|
label | enabled | 글자·아이콘foreground | fg-neutral#1A1F2E다크 #F5F6FA글자색만 바뀐다 — 굵기 · 크기 그대로 |
item | hovered | 배경background | bg-layer-default-pressed#F7F8FD다크 #353B4D칸에 칠해 알약을 덮는다 — 짙은 테두리는 그대로 |
| 테두리borderColor | stroke-neutral-contrast#1A1F2E다크 #F5F6FA | ||
| 테두리 두께borderWidth | 1px | ||
item | pressed | 배경background | bg-layer-default-pressed#F7F8FD다크 #353B4DSEED palette.gray-100 의 역할 짝 — 짙은 테두리는 그대로 |
| 테두리borderColor | stroke-neutral-contrast#1A1F2E다크 #F5F6FA | ||
| 테두리 두께borderWidth | 1px | ||
item | disabled | 배경background | bg-disabled#F5F6FA다크 #353B4D고른 채 막힘 — 칸에 칠해 알약을 덮는다 |
| 테두리borderColor | stroke-neutral-solid#767C8B다크 #838997짙은 1px 를 회색으로 남겨 무엇을 골랐는지 보인다(Chip 의 고른 채 막힘과 같다) | ||
| 테두리 두께borderWidth | 1px |
SEED 와 다른 점
- 칸이 트랙 폭을 똑같이 나눈다 — SEED 는 칸 최소 86 · 좌우 24 라 4개(352)가 360 · 375 폰에 들어가지 않는다(SEED 안에서 2 ~ 4개를 허용하는 규칙과 어긋난다). groo 는 최소 폭 없이 칸 수로 나누고 좌우 12 다 — 높이 · 글 · 알약은 SEED 그대로(사용자 결정 2026-10-02).
- 반투명 색을 불투명 짝으로(v102) — 안 고른 칸 누름 테두리
stroke-neutral-weak. 트랙은 SEED 그대로 투명한bg-neutral-weak-alpha다 — v102 에는 검사기가 8자리 hex 를 받지 않아 불투명bg-neutral-weak로 두었는데, 회색 바닥 위에서 트랙이 바닥과 같은 색(1.00:1)이라 흰 알약만 떠 보여 v120 에 되돌렸다(2026-10-16 사용자 결정 — 기초 웹 01-040 B). 고른 알약은 SEEDpalette.gray-00(라이트 흰 · 다크 검정) 대신bg-layer-default— 다크에서도 트랙보다 어둡다. 고른 칸 누름은bg-layer-default-pressed. - 고른 알약의 테두리를 짙게 — SEED 의 옅은 1px(
stroke.neutral-muted)는 트랙과 1.14:1 이라 고른 칸이 WCAG 1.4.11 에 못 미친다. 짙은 1pxstroke-neutral-contrast(트랙과 라이트 13.71 ~ 14.79 · 다크 7.84 ~ 10.33:1)로 바꿨다 — Chip(Outline Weak 고름) · Select Box 의 고른 표시와 같은 말이다. 고른 채 막히면stroke-neutral-solid(사용자 결정 2026-10-02). - 안 고른 칸을 올리거나 누르는 동안 글을
fg-neutral-muted로 — 누름 바탕 위fg-neutral-subtle이 다크에서 3.91:1 이라서다(List 의 강조 줄과 같은 방법). - 다크의 안 고른 글은 늘
fg-neutral-muted— SEED 는fg.neutral-subtle이다. 투명 트랙(v120)이 다크 표면에 섞여 밝아지면서 subtle 이 카드 위 4.09 · 시트 위 3.56 이 돼, 다크만 한 단계 짙게 둔다(카드 5.18 · 시트 4.50). 라이트는 subtle 그대로(4.59 ~ 4.95)다(2026-10-17 사용자 결정 — 01-040 B 후속). - 줄바꿈은 단어 단위(v114) — SEED 는 글자 단위(
overflow-wrap: break-word)다. - 알림 점은 브랜드 글자색(
fg-brand)이고, 보조 기술에 "새 소식" 을 덧붙인다. SEED 의bg.brand-solid짝은 groo 다크에서 트랙과 Desk · HR 1.33:1 이라 쓰지 않는다. 고른 칸에는 점을 그리지 않는다.
Migration notes
2026-10-30 — 머리 오른쪽에 둘 때는 내용 폭
통계 고정 탭(08-2)에서 웹 · 앱이 맞춘 규칙을 적었다 — 머리 오른쪽 Segmented 는 내용 폭 = (가장 긴 칸 글 + 24) × 칸 수 + 8, 제목 50% 를 지키고 넘치면 머리 아래 오른쪽 끝. 셈은 segmented-control.yaml root.width 비고에 두고 md 에는 규칙만 적었다. 값 · 토큰 변경 없음 — 레시피에 따로 크기 축을 두지 않고 놓는 자리가 내용 폭 상자(웹 w-max max-w-full)로 감싼다. 옛 스펙은 segmented-control.history/v-pre-cleanup-1030.*.
2026-10-22 — Chip 필터 바 · Tabs 와 견주는 표
Segmented Control · Chip 필터 바 · Tabs 를 견주는 표를 두고, 폼 · 설정 값 고르기는 Select 의 "고르는 컴포넌트 고르기" 로 링크한다(사용자 결정 1B — https://claude.ai/artifact/SmHD31RQxTjWbfK83nbcym). 2 ~ 4 · 한 화면 하나는 10-02 결정 그대로다. 값 변경 없음.
2026-10-17 — 칸 글은 -static(03-B 결정 4 A)
웹 · 앱 칸 글이 글자 크기 설정을 따라 200% 에서 32 가 되어 4칸이 한 글자씩 쪼개졌다(조사 03B-007 — 앱 p_segmented_control.dart, 웹 segmented-control-variants.ts:21 text-t5). 사용자가 비교 페이지에서 "칸 글은 -static 16"(A)을 골랐다 — segmented-control.yaml label $text-t5-static, DESIGN v104 의 -static 목록에 "Segmented 칸 글". 레시피 · 예제 text-t5-static, 미리보기 --text-t5-static. 웹 ds 는 text-t5-static, 앱은 칸 글에 TextScaler.noScaling. 옛 스펙은 segmented-control.history/v-pre-03e-03b.md.
2026-10-17 — 하나 고르기 묶음은 Home · End 를 받지 않는다(03-E 결정 3 A)
웹 공용 라디오 묶음(Chip 하나 고르기 · Radio Group · Segmented Control · Select Box 하나 고르기 · List 라디오)은 Radix RovingFocusGroup 이 Home · End(· PageUp · PageDown)로 초점만 첫 · 끝으로 옮겨 초점과 고른 것이 갈렸다. 사용자가 비교 페이지에서 "받지 않는다"(A — 화살표만, SEED · 진짜 라디오 · APG)를 골랐다. 레시피는 묶음이 캡처 단계에서 라디오의 그 키를 막는다(ignoreRadioJumpKeys — radio-group.tsx, Segmented Control 도 쓴다). 웹 ds 의 같은 묶음도 같게, 앱은 바뀌는 것 없음. 옮기며 고르는 안(B — Tabs 처럼)은 고르지 않았다. 옛 스펙은 segmented-control.history/v-pre-03e-03b.md.
2026-10-15 — 알림 점은 "새 소식"
구현 세션 질문 Q-010-4 — 알림 점에 덧붙이는 글이 Tabs 는 "새 소식", Segmented Control 은 "새 내용" 으로 갈려 있었다. 사용자가 "새 소식" 하나로 골랐다(안 A — 컴포넌트마다 따로(B) · "새 내용" 으로 맞추기(C)는 고르지 않았다). 웹은 segmented-control.tsx 의 숨은 글을 바꾸고, 앱은 호출부가 notificationLabel 로 넘기던 글을 컴포넌트가 갖는다("새 소식").
2026-10-02 — 새로 둔다(SEED Segmented Control)
사용자가 비교 페이지에서 Tabs 와 나눠(구역 이동은 Tabs · 같은 내용 조작은 Segmented Control) SEED 모양을 고르고, 칸 폭만 "트랙에 맞춰 똑같이 나눈다"(B)로 정했다. 옛 Tabs 의 container · pills 모양이 하던 거르기 · 정렬 · 보기 자리를 맡는다.
제품은 앱 적용 단계에서 옮긴다(2026-10-02 조사). 같은 내용 조작 34곳(Desk 웹 17 · 앱 16 · HR 1) 중 2 ~ 4개는 Segmented Control, 그보다 많으면 Chip(하나 고르기) · Select 로 간다.
- Desk 웹 · 앱 — 회색 트랙 탭(웹 26 · 앱 23)이 높이 28 ~ 34 · 모서리 2 ~ 8 · 글 12 ~ 13 · 500 이고, 고른 칸과 트랙이 1.12 · 1.15:1 이다. 다크에서는 고른 칸이 트랙보다 어둡다. 할 일 "오늘 · 이번 주 · 전체 · 완료" 처럼 4개를 폰에 쓰는 자리가 있다.
- Desk 증권(토스 발견) — 보유 · 관심 · 발견 + 급상승 · 급하락 · 거래량 + 국내 · 미국이 같은 회색 트랙으로 셋 쌓인다 — 한 화면 하나 규칙에 걸려 앱 적용 때 나눈다.
- HR 웹 — 보기 전환 버튼 묶음 1곳이 고른 상태를 알리지 않고 칸마다 Tab 이 선다.
Searchable List
위 검색칸에 치는 대로 아래 목록이 걸러지고, 그 목록에서 하나를 고르는 묶음 — 은행 · 증권사 · 카드 상품 · 종목 · 결재자처럼 스크롤만으로 찾기 어려운 긴 목록에 쓴다. 대개 Input Button 이 여는 검색 시트 · 팝오버의 내용이고, 고르는 것이 그 단계의 일이면 화면 · 단계 안에 둔다. 짧은 선택지는 Select, 줄 몇 개에서 하나는 List 의 라디오 줄이다.
Select Box
테두리 상자로 선택지를 보여 주고 하나 · 여럿을 고르게 하는 컴포넌트. 설명 · 그림 · 딸린 입력이 붙는 선택지를 비교해 고르고, 저장 · 다음 같은 버튼으로 반영하는 자리에 쓴다.