Components
Toast
일시적 알림. 상태 변경 결과를 방해 없이 전달.
Mobile
모바일에서 Toast는 화면 가장자리 안전 영역(safe area)을 따라 떠 있는 카드 형태로 표시된다. Web과 시맨틱(자동 dismiss·계층적 priority)은 동일.
- 위치: 기본은 화면 상단 (notch/status bar 아래) — iOS 관례. 키보드가 떠 있을 땐 키보드 위로 자리. 사용 맥락에 따라 하단도 허용 (예: 화면 상단 Header가 복잡할 때)
- safe area:
useSafeAreaInsets기반 offset 적용 (top + 12또는bottom + 12) - 너비:
calc(100vw - 32px), max 480px. 좌우 16px 인셋 - dismiss:
- 자동 dismiss (info/success 4s, warning/error 6s) — web과 동일
- Swipe-to-dismiss: 위/아래 또는 좌/우 스와이프로 즉시 닫기 (
react-native-toast-message또는react-native-reanimated제스처) - 탭 시 dismiss 또는 액션 트리거 (CTA가 있는 toast)
- hover-to-pause 없음: 모바일은 hover 개념이 없으므로 사용자 인터랙션으로 일시정지 패턴은 long-press로 대체하거나 생략
- 권장 라이브러리:
react-native-toast-message— safe area·queue·animation 내장 - CTA toast: 우측 액션 버튼은 hit area ≥44pt 확보. CTA가 길어지면 별도 줄로 분리
Preview
preview-mobile
preview-mobile
Top toast — safe area + 12pt offset
구조
ToastProvider
└─ ToastViewport fixed · bottom-right · w-[360px]
└─ Toast group relative · rounded-xl · border · p-4 · shadow-lg
├─ ToastClose absolute -left-2 -top-2 · h-5 w-5 · rounded-full
│ bg-white · ring-1 · shadow-md
│ opacity-0 → group-hover:opacity-100
├─ div (text, 좌) flex flex-col gap-0.5
│ ├─ ToastTitle label1Medium
│ └─ ToastDescription caption1Regular (optional)
└─ ToastAction shrink-0 · h-8 · rounded-lg · button2Medium (optional)Variants
모든 variant는 흰 배경을 공유합니다. 시맨틱 구분은 아이콘 컬러 + 테두리 색상으로만 표현합니다. 제목·설명 텍스트와 액션 버튼은 모든 variant에서 동일합니다.
| Variant | Border | Icon | Title | Description | Action button |
|---|---|---|---|---|---|
default | stone-200 | — | brown-800 | stone-400 | bg-stone-100 text-brown-800 |
warning | amber-200 | AlertTriangle · amber-500 | brown-800 | stone-400 | bg-amber-100 text-amber-700 |
destructive | rose-200 | AlertCircle · rose-500 | brown-800 | stone-400 | bg-rose-100 text-rose-700 |
success | lime-200 | CheckCircle2 · lime-600 | brown-800 | stone-400 | bg-lime-100 text-lime-700 |
Token Mapping
| Token | Value (web) | Value (mobile) |
|---|---|---|
| position | 화면 하단 또는 상단 (Radix viewport에 따름) | 화면 상단 (status bar 아래, safe area + 12pt) — 키보드 시 키보드 위로 |
| width | w-[360px] | calc(100vw - 32px), max 480px |
| background | white (모든 variant 동일) | 동일 |
| radius | rounded-xl (16px) | 동일 |
| shadow | shadow-lg | native shadow (iOS) / elevation (Android) |
| padding | p-4 | 동일 |
| gap | gap-3 (icon · text · action 사이) | 동일 |
| title | label1Medium · brown-800 | 동일 |
| description | caption1Regular · stone-400 | 동일 |
| icon | h-4 w-4 · variant 색상 | 동일 |
| action button height | h-8 (32px) · button2Medium | ≥44pt hit area (hitSlop 또는 row 전체 hit area) |
| close position | absolute -left-2 -top-2 | 사용 안 함 — swipe-to-dismiss로 대체 |
| close size | h-5 w-5 · rounded-full · bg-white · ring-1 · shadow-md | — |
| close visibility | opacity-0 → group-hover:opacity-100 | — (hover 없음) |
| dismiss | 자동 4s/6s · close 버튼 클릭 · hover 시 pause | 자동 4s/6s · swipe-to-dismiss (위/아래/좌/우) · 탭 dismiss · hover-pause 없음 |
동작 규칙
| 항목 | 값 |
|---|---|
| 동시 표시 최대 | 1개 (TOAST_LIMIT = 1) |
| 기본 duration | 3초 |
| 지속 toast | duration: Infinity |
| 위치 | desktop: bottom-right / mobile: top |
| 닫기 방식 | swipe (우) · Close 버튼 · duration 만료 |
| 진입 | slide-in-from-bottom-2 · fade-in |
| 퇴장 | slide-out-to-right-full · fade-out-80 |
Usage
// 기본 (title만)
toast({ title: "링크가 복사되었습니다" })
// description 포함
toast({
title: "마이크 연결됨",
description: deviceName,
})
// destructive
toast({
title: "마이크 접근이 차단되었습니다",
description: "브라우저 설정에서 마이크 권한을 허용해 주세요.",
variant: "destructive",
})
// action 포함
toast({
title: "노트가 삭제되었습니다",
description: "팀 주간 회의",
action: <ToastAction altText="실행 취소">실행 취소</ToastAction>,
})
// 지속 toast
const { dismiss } = toast({
title: "연결이 끊겼습니다",
variant: "destructive",
duration: Infinity,
})
dismiss()A11y
aria-live="polite"(default/success) /aria-live="assertive"(destructive)- Focus를 빼앗지 않음 — 현재 작업 방해 금지
- Close 버튼:
aria-label="닫기"필수
Do / Don't
- Do: 상태 변경 결과, 짧은 피드백에 사용
- Do:
duration: Infinity는 사용자가 직접 해소해야 하는 상태에만 - Do: action은 1개, 레이블은 동사 단어 ("실행 취소", "설정 열기")
- Don't: Critical error (사용자 액션 필수)에 Toast 단독 사용 금지 →
AlertDialog - Don't: title 없이 description만 사용 금지
- Don't: 여러 개 쌓지 않음 — TOAST_LIMIT 1 준수
