Community이미지github.com

RevnathThota55/FreeFrame-Studio

Sonner React 토스트 라이브러리 가이드 — Toaster 설치 및 연결, 올바른 toast() 호출 선택, 프로미스 및 로딩 토스트, 업데이트, 닫기 및 유지, 스타일링, 테마 및 아이콘, 위치 지정 및 여러 Toaster 사용법. Sonner 사용 또는 문제 해결 시 활용하세요 — 토스트가 나타나지 않거나, 두 번 나타나거나, 스타일이 사라지거나, Tailwind 클래스가 무시되거나, 모달 뒤에 숨거나, 다크 모드를 따르지 않는 경우.

FreeFrame-Studio란 무엇인가요?

이 스킬은 React 애플리케이션에서 Sonner 토스트 라이브러리를 설정, 스타일링 및 문제 해결하는 방법을 제공합니다. Sonner의 창시자 Emil Kowalski의 문서를 기반으로 하며, `<Toaster />` 컴포넌트 설치부터 `toast()` 함수의 다양한 호출 방식, 스타일링 계층 구조, 그리고 일반적인 문제 해결 방법까지 포괄적으로 다룹니다. Next.js, Astro 등 다양한 프레임워크에서의 설정 방법과 함께 프로미스 기반 토스트, 로딩 상태 관리, 여러 Toaster 동시 사용 등 고급 기능도 포함되어 있습니다. 스타일링은 기본값부터 headless 커스텀까지 단계별로 설명하며, 각 단계에서 발생할 수 있는 문제와 해결책을 표로 정리하여 개발자가 빠르게 문제를 진단하고 수정할 수 있도록 도와줍니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/RevanthThota55/FreeFrame-Studio/tree/HEAD/.claude/skills/ask-sonner

Installed? Explore more 이미지 skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Sonner 작업하기

초기 응답

이 스킬이 특정 질문 없이 처음 호출되면, 다음 내용만 응답하세요:

Sonner를 설정하고, 스타일링하고, 문제를 해결하는 데 도움을 드릴 준비가 되었습니다. 제 지식은 작성자 Emil Kowalski로부터 왔습니다.

사용자가 질문하기 전까지는 다른 정보를 제공하지 마세요.

Sonner 토스트 라이브러리를 위한 가이드 스킬입니다. 작업이 Sonner와 관련된 경우 — 연결, 토스트 렌더링, 스타일링, 또는 수정 — 이 파일을 먼저 참고하여 답변하세요. <Toaster /> 및 toast()의 전체 속성 표는 API.md에 있습니다. 정확한 속성 이름, 유형 또는 기본값이 필요할 때 읽어보세요.

설정

두 가지 요소만 있으면 됩니다:

  1. <Toaster /> 하나, 한 번만 마운트 — 가능한 한 루트에 가깝게 배치합니다 (Next.js의 경우: layout.tsx — 서버 컴포넌트 내에서도 작동합니다). 페이지별 또는 조건부로 렌더링하지 마세요; 두 번째 마운트된 Toaster는 모든 토스트를 중복으로 만듭니다.
  2. 클라이언트 코드에서 toast() 호출 — 이벤트 핸들러, 효과, 콜백. 일반 함수이며, 훅이나 프로바이더가 필요하지 않지만 서버에서는 아무 작업도 수행하지 않습니다: 서버 액션에서는 결과를 반환하고, 이를 수신하는 클라이언트 코드에서 toast()를 호출하세요.
import { Toaster } from 'sonner'; // 한 번, layout에서
import { toast } from 'sonner';   // 클라이언트 측 어디서든

올바른 호출 선택하기

원하는 기능호출 방법
일반 메시지toast('제목') — 두 번째 줄을 위해 { description } 추가
성공 / 에러 / 정보 / 경고 아이콘toast.success('…'), toast.error('…') 등
상태를 직접 관리하면서 스피너 표시toast.loading('…'), 그 다음 id로 업데이트
프로미스에 연결된 로딩 → 성공/에러toast.promise(promise, { loading, success, error }) — success/error는 각각 resolve된 값 또는 에러를 받는 함수 허용
동작을 수행하는 버튼{ action: { label, onClick } } — onClick이 event.preventDefault()를 호출하지 않으면 토스트를 닫음; cancel은 보조 변형
사용자 정의 JSX, 기본 토스트 셸toast(<jsx />)
사용자 정의 JSX, 스타일 없음toast.custom((t) => <jsx />) — headless, t는 닫기에 사용할 id 제공

레시피

토스트 업데이트 — 같은 id로 toast()를 다시 호출하세요; 전달한 속성만 변경됩니다. toast.success(…, { id })로 전환하면 유형도 변경됩니다. 이것이 toast.promise 없이 로딩 → 성공 흐름이 작동하는 방식입니다:

const id = toast.loading('업로드 중…');
toast.success('업로드됨', { id });

유지 — { duration: Infinity }. 닫기 — toast.dismiss(id), 또는 모든 토스트를 닫으려면 toast.dismiss(). 활성 토스트 읽기 — React에서는 useSonner(), 그 외에서는 toast.getActiveToasts().

텍스트 내 링크 또는 컴포넌트 — 제목 또는 설명에 함수 전달: toast(() => <a href="…">보기</a>).

여러 Toaster — 각각에 id를 지정하고 toast('…', { toasterId: 'canvas' })로 타겟팅합니다. toasterId가 없으면 모든 Toaster가 토스트를 렌더링합니다.

닫기 콜백 — onDismiss는 닫기 버튼 또는 스와이프 시 실행; onAutoClose는 타임아웃 시 실행됩니다. 이들은 별개이며, 단일 "닫힘" 콜백은 없습니다.

스타일링 — 단계별 가이드

변경 사항에 필요한 만큼만 단계를 올라가세요. 너무 일찍 최상위 단계로 가는 것은 괜찮지만 (권장되는 최종 상태), 중간 단계에 머무르는 것은 좋지 않습니다.

  1. 기본값 — Toaster에 richColors를 추가하여 다채로운 성공/에러 표시, invert로 테마 반전.
  2. 인라인 조정 — 모든 토스트에 적용하려면 Toaster에 toastOptions={{ style: {…} }}, 또는 각 toast() 호출에 style.
  3. 파트별 클래스 — toastOptions={{ classNames: { toast, title, description, actionButton, cancelButton, closeButton } }}. Sonner의 주입된 스타일이 캐스케이드에서 우선하므로 모든 클래스는 !important가 필요합니다 (Tailwind: !text-red-900). 여러 항목에 important를 표시해야 한다면 중단하고 headless로 가세요.
  4. Headless — toast.custom()과 사용자 정의 JSX를 사용하여 Sonner의 위치 지정, 쌓기, 스와이프 기능을 유지합니다. 디자인 시스템 토스트에 권장되는 방법: 자체 toast() 추상화로 래핑하세요. (unstyled: true도 중간 해결책으로 존재하지만, headless가 동일한 노력으로 더 많은 제어를 제공합니다.)

아이콘 — Toaster의 icons 속성으로 유형별 기본값을 바꾸거나, 토스트별로 icon을 사용하고, null로 제거합니다.

테마 — theme은 기본적으로 'light'이며 OS 설정을 추적하지 않습니다. theme="system"을 전달하거나, 테마 프로바이더와 연결하세요: next-themes의 <Toaster theme={resolvedTheme} />.

문제 해결

증상원인 → 해결책
토스트가 나타나지 않음<Toaster />가 마운트되지 않았거나, 마운트 해제됨 (조건부 렌더링, 페이지별 배치). 루트에 하나를 마운트하세요. 서버 액션에서 호출하는 경우: toast()는 클라이언트 전용입니다 — 액션의 결과로 클라이언트에서 호출하세요.
같은 토스트가 두 번 나타남두 개의 Toaster가 마운트됨 (layout 및 page) — 하나만 유지하세요. 또는 React StrictMode의 개발 모드 이중 호출로 인해 useEffect에서 toast()가 실행된 경우 — 이벤트 핸들러에서 호출하거나, 안정적인 id를 전달하여 두 번째 호출이 중복 대신 업데이트되도록 하세요.
Tailwind/CSS 클래스가 적용되지 않음기본 스타일이 덮어씁니다. !important를 표시하거나, unstyled / headless를 사용하세요 (위 단계별 가이드 참조).
토스트가 완전히 스타일 없이 렌더링됨 (Astro, view transitions에서 흔함)Sonner의 주입된 스타일시트가 손실됨 — layout에서 명시적으로 가져오기: import 'sonner/dist/styles.css'.
Shadow DOM 내에서 스타일 없음스타일이 document.head에 들어가므로 Shadow Root에는 들어가지 않습니다. [data-sonner-toaster]가 포함된 스타일 태그의 텍스트를 Shadow Root에 복사하세요.
모달/오버레이 뒤에 토스트가 있거나 잘림조상 요소가 쌓임 컨텍스트를 생성함 (transform, filter, overflow) 또는 오버레이가 Toaster보다 높은 z-index를 가짐. <Toaster />를 다이얼로그/포털 컨테이너 외부, 문서 루트로 이동하세요.
다크 모드 무시됨theme이 기본적으로 'light'임 — theme="system"을 설정하거나 resolve된 테마를 전달하세요 (위 테마 참조).
성공/에러가 회색으로 보임 (녹색/빨간색 아님)기본값입니다. Toaster에 richColors를 추가하세요.
토스트가 닫히지 않음duration: Infinity, dismissible: false, 또는 toast.promise의 프로미스가 해결되지 않음 — 로딩 토스트가 영원히 기다림.
toast.promise가 로딩에서 멈춤첫 번째 인자로 프로미스 (또는 프로미스를 반환하는 함수)가 필요하며, 프로미스가 실제로 resolve/reject되어야 함.
스와이프로 닫기가 잘못된 방향으로 / 작동하지 않음방향은 position에서 파생됩니다. Toaster의 swipeDirections로 재정의하세요.
토스트가 모든 Toaster에 나타남여러 Toaster는 타겟팅이 필요함: 각 Toaster에 id를 지정하고 toast() 호출에 toasterId를 전달하세요.
모바일에서 토스트가 화면 가장자리에 너무 가까움offset (데스크탑, 기본 32px) 및 mobileOffset (<600px, 기본 16px) — 숫자, CSS 문자열, 또는 측면별 객체.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

RevanthThota55/FreeFrame-Studio

Build an animation from scratch, making the decisions in the order that determines whether it feels right — should it animate at all, what purpose, which tool, which properties, which curve and duration, how it interrupts, how it exits. Writes the implementation. Use when asked to animate something, add motion, make a component feel alive, or build a transition. For critiquing existing motion use review-animations; for auditing a whole codebase use improve-animations.

RevanthThota55/FreeFrame-Studio

Keep web animations inside the frame budget — animate composite-only properties, keep motion off the main thread when the page is busy, and find the cause when an animation drops frames. Covers the layout/paint/composite pipeline, cheaper property swaps, CSS vs WAAPI vs requestAnimationFrame, Framer Motion's hardware-accelerated path, React re-rendering per frame, inherited CSS variable recalculation, blur cost, and when `will-change` actually helps. Use when an animation is janky, choppy, stutters, or drops frames; when choosing which property to animate; when deciding between CSS and JS animation on performance grounds; or when reaching for `will-change` or GPU layers. For choosing easing, duration, or whether to animate at all use animate; for reviewing motion as a whole use review-animations.

RevanthThota55/FreeFrame-Studio

Reverse-lookup glossary that turns a vague description of a web animation or motion effect into its exact term ("the bouncy thing when a popover opens" → Pop in; "the iOS rubber-band scroll" → Rubber-banding). Use when the user asks "what's it called when…", or describes a motion effect without knowing its name and wants the right word to prompt an AI or designer with. For naming an effect, not designing or building one.

RevanthThota55/FreeFrame-Studio

Apple's approach to interface design and fluid, physical motion, translated for the web. Use when building or reviewing gesture-driven UI, spring animations, drag/swipe/sheet interactions, momentum and interruptible transitions, translucent materials and depth, typography (optical sizing, tracking, leading), reduced-motion, or the design foundations (feedback, spatial consistency, restraint) behind Apple-style interfaces.

관련 스킬