Button
사용자의 액션을 트리거하는 컴포넌트. Type과 Layout을 조합해 다양한 형태를 구성할 수 있다.
Preview
Anatomy
Button
├─ Type primary / secondary / outlined / ghost / text / destructive
└─ Layout text-only / icon+text / text+icon / icon-onlyType은 컨테이너의 시각적 강조 수준을 결정하고, Layout은 내부 요소(아이콘·텍스트)의 구성을 결정한다. 두 축은 독립적으로 조합된다.
Type
| Type | 언제 |
|---|---|
| primary | 화면 내 주요 CTA. 페이지당 하나 |
| secondary | Primary와 나란히 쓰는 대안 액션. "저장/취소", "확인/닫기"처럼 두 선택지를 동등하게 제시할 때. 배경 fill로 버튼의 무게감을 맞춤 |
| outlined | Primary 없이 단독으로 쓰는 보조 액션. 테이블 행·리스트 아이템처럼 밀도 높은 UI에서 반복 등장하거나, 강조도가 secondary보다 낮아야 할 때 |
| ghost | 툴바·헤더·네비 등 컨테이너 없이 아이콘/텍스트만으로 충분한 맥락에서 사용 |
| text | 문장 안에 자연스럽게 녹아드는 인라인 링크성 액션. 독립 버튼 영역 없이 흐름 내에서만 |
| destructive | 삭제·초기화 등 되돌릴 수 없는 액션. 반드시 확인 단계와 함께 사용 |
secondary와 outlined는 역할이 다른 별도 type이다. secondary outlined 같은 hybrid는 만들지 않는다.
outlined는 기본 상태에서 transparent를 유지한다. hover/pressed에서만 현재 surface에 맞는 약한 fill을 받는다.
Layout
Layout은 버튼 내부의 요소 구성을 결정한다. Type과 독립적으로 조합된다.
텍스트만
아이콘 + 텍스트
아이콘을 **좌측(Prefix)**에 배치한다. 아이콘이 액션의 의미를 보강할 때 사용한다.
- 아이콘 크기: Foundations/Iconography 기준 — icon+text는
md16px 기본 - 아이콘과 텍스트 간격: size별로 다름 —
xs/sm은gap-1.5(6px),md/lg/xl은gap-2(8px). 자세한 값은 아래 Sizes 표 참조 strokeWidth={1.75}필수 (Tiro 기본값)
텍스트 + 아이콘
아이콘을 **우측(Suffix)**에 배치한다. Chevron(방향 이동)과 외부 링크 아이콘에 한정한다.
texttype과 함께 주로 사용- Suffix 아이콘을 일반 의미 전달에 사용하지 않는다 — 그 경우는 Prefix
아이콘만
라벨 없이 아이콘만으로 의미가 명확한 액션에 사용한다. 툴바, 헤더, 리스트 행 등.
- 정사각형:
sm32×32 /md40×40 /lg44×44 aria-label필수- 실면적이 32px 미만이면 invisible padding으로 최소 44pt 확보 (iOS HIG)
States
| State | 처리 |
|---|---|
| hover | type별 bg/color 변경 |
| pressed | brown-900 (primary) / stone-300 (secondary) |
| focus-visible | ring-2 ring-offset-2 ring-brown-800 |
| disabled | opacity-50 + pointer-events-none |
| loading | inline Spinner · aria-busy="true" |
| selected (icon-only) | bg-brown-50 배경 |
Disabled
모든 type에 동일하게 opacity-50 pointer-events-none을 적용한다. 색상을 type별로 따로 오버라이드하지 않는다.
disabledHTML 속성과 함께 사용 (aria-disabled="true"대안 가능)- 비활성 이유가 사용자에게 불명확할 때는 Tooltip으로 이유 제공
Sizes
| Size | Height | Square (icon-only) | 아이콘 — icon+text | 아이콘 — icon-only | px / py | gap (icon+text) | Text | Primary use | Mobile (≥44pt hit area) |
|---|---|---|---|---|---|---|---|---|---|
xs | 28px | 28×28 | xs 12px | sm 14px | 12 / 6 | gap-1.5 (6px) | caption1Regular 12/16 (medium override) | 태그·칩·인라인 액션 · rounded-full 전용 | hitSlop으로 hit area 44pt 확장 필수 |
sm | 32px | 32×32 | sm 14px | md 16px | 10 / 6 | gap-1.5 (6px) | button2Medium 13/20 | Dense UI (form primary 금지) | hitSlop으로 hit area 44pt 확장 필수 |
md | 40px | 40×40 | md 16px | lg 20px | 16 / 10 | gap-2 (8px) | button2Medium 13/20 | Default (web) | hitSlop 4pt 권장 |
lg | 44px | 44×44 | lg 20px | xl 24px | 20 / 10 | gap-2 (8px) | button1Medium 15/24 | Mobile primary CTA | ✓ 기본 충족 |
xl | 52px | — | lg 20px | — | 24 / 14 | gap-2 (8px) | button1Medium 15/24 | Desktop Hero primary | 사용 안 함 (모바일은 lg까지) |
Platform defaults
- Default size:
md— 일반 UI 액션의 표준 xl은 Hero CTA(랜딩, 마케팅 페이지의 단일 메인 액션) 전용sm32×32 icon-only는 마우스/트랙패드 입력에서 충분한 hit area
xs는rounded-full(pill) 전용 — 태그·필터·인라인 선택형 액션에만 사용xl은 icon-only 레이아웃 없음- 아이콘 크기 기준: Foundations/Iconography (
xs12 /sm14 /md16 /lg20 /xl24) sm–xlRadius:r2→rounded-lg(8px)
Spec
| Type | bg | bg (hover) | text | border |
|---|---|---|---|---|
| primary | brown-800 | brown-700 | white | — |
| secondary | stone-50 | stone-100 | brown-800 | — |
| outlined | — | stone-50 | brown-800 | stone-200 |
| ghost | — | stone-50 | brown-800 | — |
| ghost (muted) | — | stone-50 (icon-only) / — (text) | stone-400 → stone-600 | — |
| text | — | — | brown-600 → brown-900 | — |
| destructive | rose-600 | rose-700 | white | — |
Muted Ghost / Icon-only
툴바 · 헤더 · 보조 컨트롤 등에서 ghost 버튼이 주변 요소에 비해 시각적으로 더 물러서야 할 때는 텍스트 · 아이콘 색상을 stone-400으로 낮춘다. hover 시에는 stone-600으로 진하게 올려 인터랙티브함을 전달한다.
배경 fill이 필요하면 icon-only에 한해 hover:bg-stone-50을 추가한다. 텍스트만인 muted ghost는 hover 시 배경 없이 color 변화만으로 충분하다.
Destructive Ghost / Icon-only
텍스트나 아이콘이 rose-* 계열이면 hover 배경도 rose-50을 사용한다. stone-50을 사용하지 않는다.
Usage
// 텍스트만 — primary
<Button>기록 시작</Button>
// 아이콘 + 텍스트
<Button layout="icon-text" prefixIcon={<Mic size={16} />}>녹음 시작</Button>
// 텍스트 + 아이콘 (text type + chevron)
<Button type="text" layout="text-icon" suffixIcon={<ChevronRight size={16} />}>
최근 2주간의 미팅을 요약해줘
</Button>
// 아이콘만 — ghost
<Button layout="icon-only" aria-label="닫기"><X size={20} /></Button>
// Loading
<Button loading disabled>저장 중...</Button>A11y
- icon-only 레이아웃:
aria-label(web) /accessibilityLabel(mobile) 필수 — 옵션 아님 - 툴바 그룹 내 icon-only 버튼은 순차 focus 가능해야 함
texttype은 링크처럼 보이지만<button>유지 — 페이지 이동이면<a>사용- loading 상태에서
aria-busy="true"추가 권장
Do / Don't
- Do: 리스트 행 또는
Item컴포넌트 내부에 버튼을 배치할 때는 한 단계 작은 사이즈를 사용한다 — 기본 맥락이md이면sm,sm이면xs로 낮춘다. 행의 밀도에 맞게 크기를 줄여 시각적 균형을 유지한다. - Do: Primary는 화면당 하나의 주요 액션에만
- Do: Suffix 아이콘은 Chevron·외부 링크에만 — 의미 전달용 아이콘은 Prefix
- Do: icon-only 레이아웃에는
aria-label항상 제공 - Do:
texttype은 흐름 내 보조 액션에만 — 주요 CTA 금지 - Do: Ghost 버튼이 주변 요소보다 시각적으로 물러서야 할 때는
stone-400(muted) 사용 — hover 시stone-600으로 올림 - Don't: Ghost를 주요 CTA로 사용 금지
- Don't: Primary 여러 개를 한 화면에 배치 금지
- Don't: brown 외 액센트 컬러 도입 금지
- Don't: 텍스트 없이 의미 전달이 불확실한 경우 icon-only 사용 금지
- Don't: 선택·활성 상태를 아이콘 색상만으로 표현하지 않음 — 배경색·테두리 병행
- Don't: rose 텍스트·아이콘 버튼에
hover:bg-stone-50사용 금지 — 반드시hover:bg-rose-50
