Community图像github.com

RevanthThota55/FreeFrame-Studio

Sonner React 吐司通知库的完整指南:安装、配置 Toaster、选择合适的 toast() 调用、Promise 和加载提示、更新、关闭、持久化、样式、主题和图标、定位以及多个 toaster。用于使用或排查 Sonner 问题,例如吐司不显示、重复显示、样式丢失、忽略 Tailwind 类、位于模态框后面或不跟随深色模式。

FreeFrame-Studio 是什么?

此技能提供了关于 Sonner(一个由 Emil Kowalski 开发的轻量级 React 吐司通知库)的全面指南。它详细说明了核心设置,包括正确安装一个 `<Toaster />` 组件以及如何在客户端代码中使用 `toast()` 函数。该指南涵盖了从基本文本消息到成功/错误/加载状态、Promise 集成、自定义 JSX 以及通过 ID 更新现有吐司的各种 toast 调用方法。它还系统地介绍了一种分级的样式管理方法,从默认主题到内联样式、带 `!important` 的 Tailwind 类,最终推荐使用无头 `toast.custom()` 以实现设计系统的完全控制。此外,该技能还针对常见的故障场景提供了详尽的故障排查表,例如吐司不显示、重复、样式失效、位于重叠元素后方或深色模式不生效等问题。此技能适用于 Cursor、Codex 及类似的 AI 编码助手。

兼容平台~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 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

使用 Sonner

初始响应

当此技能首次被调用且没有具体问题时,仅回复:

我已准备好帮助您设置、样式化和排查 Sonner 问题,我的知识来源于其作者 Emil Kowalski。

在用户提出问题之前,不要提供任何其他信息。

这是一个面向 Sonner 吐司通知库的指南技能。当任务涉及 Sonner(包括配置、渲染、样式化或修复问题)时,请首先从此文件获取答案。<Toaster /> 和 toast() 的完整属性表位于 API.md 中;当您需要确切的属性名、类型或默认值时,请阅读该文件。

安装设置

只有两个步骤:

  1. 一个 <Toaster />,挂载一次,尽可能靠近根组件(在 Next.js 中:layout.tsx — 它可以运行在服务器组件内部)。切勿在每个页面或有条件地渲染它;第二个挂载的 Toaster 会重复显示每一个吐司。
  2. 从客户端代码调用 toast() — 事件处理函数、副作用、回调函数。它是一个普通函数,无需 Hook 或 Provider,但在服务器端无效果。在服务器操作中,应返回结果,并在接收结果的客户端代码中调用 toast()。
import { Toaster } from 'sonner'; // 一次,在 layout 中
import { toast } from 'sonner';   // 在任何客户端组件中

选择合适的调用方式

目标调用方式
纯文本消息toast('Title') — 添加 { description } 以显示第二行
成功/错误/信息/警告图标toast.success('…'), toast.error('…') 等
自己管理状态时显示旋转器toast.loading('…'),然后通过 ID 更新
加载 → 与 Promise 绑定的成功/错误toast.promise(promise, { loading, success, error }) — success/error 可接收返回已决值/错误的函数
带操作的按钮{ action: { label, onClick } } — 除非 onClick 调用了 event.preventDefault(),否则会关闭吐司;cancel 是次要变体
自定义 JSX,使用默认吐司外壳toast(<jsx />)
自定义 JSX,完全无样式toast.custom((t) => <jsx />) — 无头模式,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()。

在文本中嵌入链接或组件 — 为 title 或 description 传递一个函数:toast(() => <a href="…">查看</a>)。

多个 Toaster — 为每个 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)。如果您标记了多个重要项,请停止 — 转向无头模式。
  4. 无头模式 — 使用带自定义 JSX 的 toast.custom(),保留 Sonner 的定位、堆叠和滑动功能。这是设计系统吐司的推荐方法:将其封装在您自己的 toast() 抽象中。(unstyled: true 作为一种中间方案存在,但无头模式在付出相同努力的情况下提供了更多控制权。)

图标 — 使用 Toaster 的 icons 属性按类型替换默认图标,使用 icon 按单个吐司替换,使用 null 移除图标。

主题 — theme 默认为 'light' 且不跟踪操作系统。请传入 theme="system",或连接您的主题提供程序:<Toaster theme={resolvedTheme} />(来自 next-themes)。

故障排查

症状原因 → 修复
吐司从未显示没有挂载 <Toaster />,或它被卸载了(条件渲染、每页放置)。在根组件挂载一个。如果从服务器操作调用:toast() 仅支持客户端 — 在客户端使用操作的结果调用它。
同一个吐司出现两次挂载了两个 Toaster(layout 和 page)— 保留一个。或者在 React 严格模式的开发双重调用下,在副作用中触发了 toast() — 改为从事件处理函数触发,或传递一个稳定的 id,这样第二次调用会更新而不是重复。
Tailwind/CSS 类无效默认样式会覆盖它们。请标记为 !important,或使用 unstyled / 无头模式(参见上面的升级方案)。
吐司完全无样式渲染(常见于 Astro、视图过渡)Sonner 注入的样式表丢失了 — 在布局中显式导入:import 'sonner/dist/styles.css'。
在 Shadow DOM 内无样式样式被添加到 document.head,而不是 shadow root。请将包含 [data-sonner-toaster] 文本的 style 标签复制到 shadow root 中。
吐司位于模态框/覆盖层后面或被裁剪某个祖先元素创建了层叠上下文(transform, filter, overflow)或覆盖层的 z-index 高于 toaster。将 <Toaster /> 移动到文档根组件,位于任何对话框/传送门容器之外。
深色模式被忽略theme 默认为 'light' — 请设置 theme="system" 或传递已解析的主题(参见上面的主题部分)。
成功/错误显示为灰色,而非绿色/红色这是默认行为。请在 Toaster 上添加 richColors。
吐司从不关闭duration: Infinity、dismissible: false,或 toast.promise 的 Promise 从未完成 — 加载提示会一直等待。
toast.promise 卡在加载状态它需要一个 Promise(或返回 Promise 的函数)作为第一个参数,并且该 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.

相关技能