본문으로 건너뛰기
오버레이 / toast

Toast

화면 모서리에 쌓이는 비동기 알림 — sonner 래퍼 + AXE 토큰/클래스.
오버레이toastStatic specimentoast토스트sonnernotification
01 · Specimen

먼저 보고, 그다음 계약을 읽습니다

화면 모서리에 쌓이는 비동기 알림 — sonner 래퍼 + AXE 토큰/클래스.

Workbench 불러오는 중…
Component intent의도와 경계 읽기

Toaster provider 를 루트에 한 번 마운트하고, 어디서든 imperative `toast.*` 로 호출한다. sonner 를 감싸 --bg-elevated 표면·좌측 시맨틱 accent bar·light/dark 자동(theme="system")을 입혔고, 내부 마크업에 .axe-toast/.axe-toast__* 클래스를 classNames prop 으로 주입한다. AI-first 4-state 에 맞춰 loading→success/danger 갱신(같은 id 재사용)과 toast.promise 자동 전이를 지원하며, sonner.error 의 별칭으로 우리 시맨틱인 toast.danger 를 추가로 노출한다.

02 · Use

예제

SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.

01마운트 + 기본 호출tsxfragment
마운트 + 기본 호출
// app/layout.tsx — 루트에 한 번import { Toaster } from "@axe/ui";<Toaster position="bottom-right" />// 어디서든import { toast } from "@axe/ui";toast.success("저장됨");toast.danger("저장 실패 — 네트워크 오류");
02AI-first 4-state (같은 id 갱신 / promise)tsxfragment
AI-first 4-state (같은 id 갱신 / promise)
import { toast } from "@axe/ui";// 수동 전이const id = toast.loading("Agent 실행 중…");try {  const r = await runAgent();  toast.success(`완료 — ${r.summary}`, { id });} catch (e) {  toast.danger(humanizeApiError(e), { id });}// 자동 전이toast.promise(runAgent(), {  loading: "Agent 실행 중…",  success: (r) => `완료 — ${r.summary}`,  error: (e) => humanizeApiError(e),});
03 · React

Props

TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.

이름타입필수기본값설명
position"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right""bottom-right"토스트 스택이 뜨는 화면 모서리.
theme"light" | "dark" | "system""system"표면 톤. 기본 system — 표면 색이 CSS 변수 기반이라 실제 시각은 토큰이 결정하지만, sonner 내부 상태 클래스에 이 값이 전달된다.
expandbooleanfalsefalse 면 겹쳐 쌓고 hover 시에만 펼침, true 면 항상 펼쳐 나열.
closeButtonbooleantrue각 토스트에 닫기(✕) 버튼(.axe-toast__close)을 표시할지.
visibleToastsnumber4동시에 보이는 최대 개수. 초과분은 뒤에 큐잉.
durationnumber4000자동 dismiss 까지의 ms. loading 토스트는 갱신/dismiss 전까지 유지된다.
classNamestringviewport 루트에 .axe-toast-viewport 와 함께 병합. (개별 토스트가 아니라 sonner 컨테이너에 붙는다.)
...restOmit<ToasterProps, "className">그 외 sonner Toaster 옵션(offset·gap·richColors·hotkey·containerAriaLabel·toastOptions 등)을 그대로 전파.
04 · Any stack

.axe-* 클래스 계약

React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.

클래스안정성용도
.axe-toaststable개별 토스트 루트 — elevated 배경·좌측 3px accent bar·shadow-md.
.axe-toast--successstablesuccess 토스트 — 좌측 bar = --success.
.axe-toast--infostableinfo 토스트 — 좌측 bar = --info.
.axe-toast--warningstablewarning 토스트 — 좌측 bar = --warning.
.axe-toast--dangerstabledanger/error 토스트 — 좌측 bar = --danger (sonner error 에 매핑).
.axe-toast--loadingstableloading 토스트 — 좌측 bar = --accent.
.axe-toast__titlestable제목 줄 — semibold·text-primary.
.axe-toast__descriptionstable보조 설명 — text-secondary·text-xs.
.axe-toast__actionstable액션 버튼 — cta 배경.
.axe-toast__cancelstable취소 버튼 — 투명 배경·subtle 테두리.
.axe-toast__closestable닫기(✕) 버튼 — 원형·subtle 배경.
.axe-toast-viewportinternalviewport 컨테이너 마커(className 기본). 실제 톤 오버라이드는 [data-sonner-toaster] 속성 셀렉터가 담당 — 전용 규칙 없음.
비-React 소비 노트
CSS-only 로는 재현 불가 — sonner 런타임이 마크업·스택·자동 dismiss·live region 을 그리고, .axe-toast* 클래스는 classNames prop 으로 그 내부에 주입된다. 스타일(overlays.css)은 components.css 가 @import 하므로 별도 로드는 불요.
05 · Inclusive

접근성

키보드, ARIA, 구현 노트를 함께 검토합니다.

Keyboard
  • Alt+T(sonner 기본 hotkey)로 토스트 알림 영역에 포커스
  • 각 토스트는 tabIndex 0 — Tab 으로 순회, 액션/닫기 버튼으로 진입
ARIA

sonner 가 알림 영역을 <section aria-live="polite" aria-relevant="additions text" aria-atomic="false" aria-label="Notifications Alt+T"> 로 렌더 — 새 토스트가 polite 로 발화된다(danger/error 도 기본 polite, assertive 아님). 닫기 버튼 aria-label="Close toast".

Notes

모든 애니메이션은 prefers-reduced-motion 을 sonner 가 존중. provider 는 앱당 한 번만 마운트(여러 개면 발화·스택이 갈린다).

06 · Judgment

권장 · 지양

권장
  • provider(Toaster)는 앱 루트에 정확히 하나.
  • 진행 중 작업은 loading 으로 시작해 같은 id 로 success/danger 갱신(토스트 난립 방지).
  • 되돌릴 수 없는 실패에만 danger — 정상 완료는 success.
지양
  • inline 상태 통지(폼 바로 아래 '저장됨')를 toast 로 처리하지 말 것 — 그건 StatusBanner.
  • 긴 문장을 title 에 몰아넣지 말 것(description 으로 분리).
검색창을 열면 컴포넌트 인덱스를 불러옵니다.