Button
먼저 보고, 그다음 계약을 읽습니다
행동을 트리거하는 기본 버튼.
Component intent의도와 경계 읽기
primary/secondary/ghost/danger 4 변형과 sm/md/lg 크기를 CSS 변수로 처리한다. loading 은 자식 앞에 sm Spinner 를 렌더하고 버튼을 disabled + aria-busy 로 만든다. leftIcon/rightIcon 은 `axe-btn__icon` 슬롯으로 래핑된다. asChild(Radix Slot)로 <a> 등 임의 element 에 버튼 스타일을 입힐 수 있으나 이때 Slot 은 단일 자식만 허용하므로 Spinner/icon 슬롯은 렌더되지 않는다.
예제
SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.
import { Button } from "@axe/ui";export function Example() { return ( <> <Button variant="primary" onClick={() => {}}>저장</Button> <Button variant="ghost" size="sm">취소</Button> <Button variant="danger" loading={false}>삭제</Button> </> );}import { Button } from "@axe/ui";<Button asChild variant="secondary"> <a href="/docs">문서 열기</a></Button><button class="axe-btn axe-btn--primary" type="button">저장</button><button class="axe-btn axe-btn--secondary" type="button"> <span class="axe-btn__icon">→</span>다음</button>Props
TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.
| 이름 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
variant | "primary" | "secondary" | "ghost" | "danger" | — | "secondary" | 시각 강조 단계 / 의미(danger = 파괴적 행동). |
size | "sm" | "md" | "lg" | — | "md" | 높이·패딩·폰트 크기. md 는 modifier 없이 기본. |
asChild | boolean | — | false | true 면 <button> 대신 Radix Slot 으로 단일 자식 element 에 스타일/props 를 병합(예 <a>). 이때 Spinner/leftIcon/rightIcon 미렌더. |
loading | boolean | — | false | disabled + aria-busy + 자식 앞 sm Spinner. `axe-btn--loading` 도 추가. |
leftIcon | React.ReactNode | — | — | 자식 앞 icon 슬롯(`axe-btn__icon` 래핑). |
rightIcon | React.ReactNode | — | — | 자식 뒤 icon 슬롯(`axe-btn__icon` 래핑). |
type | "button" | "submit" | "reset" | — | "button" | 네이티브 button type. 폼 제출은 명시적으로 submit 지정. |
...rest | React.ButtonHTMLAttributes<HTMLButtonElement> | — | — | onClick·aria-label·name 등 나머지는 루트 element 로 전파(asChild 면 자식 element 로). 단 disabled·type 은 rest 가 아니라 명시 처리되어(disabled=loading||disabled, asChild 는 aria-disabled 로만) 전달된다. |
.axe-* 클래스 계약
React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.
| 클래스 | 안정성 | 용도 |
|---|---|---|
.axe-btn | stable | 버튼 루트. |
.axe-btn--primary | stable | primary 변형(CTA). |
.axe-btn--secondary | stable | secondary 변형(기본). |
.axe-btn--ghost | stable | ghost 변형(배경 없음). |
.axe-btn--danger | stable | danger 변형(파괴적 행동). |
.axe-btn--sm | stable | small 크기. |
.axe-btn--lg | stable | large 크기. |
.axe-btn--loading | stable | loading 상태 modifier. |
.axe-btn__icon | stable | left/right icon 슬롯 래퍼. |
.axe-interactive | stable | Button 이 아닌 임의 클릭 요소(내비 행·표 링크·카드)에 다는 공유 affordance 유틸리티 — hover/focus-visible/press 어휘만 준다. Button 자신은 .axe-btn 어휘를 쓰므로 겹쳐 달지 않는다. |
.axe-interactive--active | stable | .axe-interactive 의 활성 상태(accent 텍스트 + accent-soft 배경). aria-current="page" · aria-selected="true" · data-active="true" 와 동등. |
접근성
키보드, ARIA, 구현 노트를 함께 검토합니다.
- Enter/Space 로 실행(네이티브 <button>)
기본은 네이티브 <button> — role 불요. loading 시 disabled + aria-busy="true". asChild 는 native disabled 를 걸 수 없어 loading/disabled 를 aria-disabled="true" 로만 전달한다(키보드 활성화까지 막지는 않음 — 확실한 비활성이 필요하면 asChild 없이 쓸 것).
아이콘 전용 버튼은 aria-label 을 직접 넘길 것(...rest 로 전파). :active scale(0.98) 누름 피드백은 native :disabled 에서만 빠진다 — asChild 로 렌더된 aria-disabled 버튼(비-loading)은 :disabled 가 매치되지 않아 여전히 눌림 피드백이 적용된다(확실한 비활성은 asChild 없이).