Skip to Content
@anker-in/headless-ui 2.0 is released 🎉

Alert 警告提示

警告提示组件用于向用户展示重要的反馈信息,帮助用户理解当前操作的结果或系统状态。支持成功、错误、警告、信息四种状态类型,并可配置关闭按钮。

何时使用

  • 当操作需要用户关注或确认时
  • 需要向用户展示操作结果反馈(成功、失败、警告)
  • 展示系统级别的通知信息
  • 页面中需要显著位置的静态提示信息
  • 需要持久化显示的提示内容(区别于短暂的 Toast)

快速开始

安装

pnpm add @anker-in/headless-ui

基础用法

import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' export default function App() { return ( <Alert status="success"> <AlertTitle>成功</AlertTitle> <AlertDescription>您的操作已成功完成</AlertDescription> </Alert> ) }

演示

Alert 演示

警告提示组件的各种使用示例

加载中...
当前视口: 1920px × 600px场景: 默认状态
打开链接

组件结构

Alert 是一个灵活的复合组件,由以下部分构成:

import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' <Alert status="info"> <AlertTitle>提示标题</AlertTitle> <AlertDescription>详细的提示内容描述</AlertDescription> </Alert>

组件说明

组件用途HTML 元素
Alert警告提示容器<div role="alert">
AlertTitle提示标题<h5>
AlertDescription提示内容<div>

组件组合灵活性:Alert 可以只包含 AlertDescription(无标题),也可以同时包含 AlertTitle 和 AlertDescription。


状态类型(Status)

Alert 提供四种状态类型,每种状态都有对应的图标和语义:

Info - 信息提示

用于一般性信息提示,配有蓝色图标。

<Alert status="info"> <AlertTitle>温馨提示</AlertTitle> <AlertDescription> 您有 3 个待处理的任务,请及时完成。 </AlertDescription> </Alert>

视觉特征

  • 图标:蓝色圆形背景 (#00BEFA) 的信息图标(i)
  • 适用场景:中性的提示信息、帮助文本、补充说明

Success - 成功提示

用于操作成功的反馈,配有绿色图标。

<Alert status="success"> <AlertTitle>操作成功</AlertTitle> <AlertDescription> 您的数据已成功保存,可以继续下一步操作。 </AlertDescription> </Alert>

视觉特征

  • 图标:绿色圆形背景 (#30D158) 的对勾图标
  • 适用场景:表单提交成功、数据保存成功、操作完成确认

Warning - 警告提示

用于需要用户注意但不阻塞操作的警告信息,配有黄色图标。

<Alert status="warning"> <AlertTitle>注意事项</AlertTitle> <AlertDescription> 您的账户余额不足,可能影响部分功能的使用。 </AlertDescription> </Alert>

视觉特征

  • 图标:黄色圆形背景 (#FFC24D) 的警告图标(!)
  • 适用场景:需要注意的非阻塞性问题、潜在风险提示、操作建议

Error - 错误提示

用于操作失败或错误的反馈,配有红色图标。

<Alert status="error"> <AlertTitle>操作失败</AlertTitle> <AlertDescription> 网络连接失败,请检查网络设置后重试。 </AlertDescription> </Alert>

视觉特征

  • 图标:红色圆形背景 (#FF4D4D) 的错误图标(!)
  • 适用场景:操作失败、表单验证错误、系统错误提示

变体(Variants)

Default - 默认样式

使用系统的背景色和前景色,适合大多数场景。

<Alert status="info" variant="default"> <AlertTitle>默认样式</AlertTitle> <AlertDescription>这是默认的样式变体</AlertDescription> </Alert>

Destructive - 破坏性操作

使用红色边框和文字,强调警告或危险操作。

<Alert status="error" variant="destructive"> <AlertTitle>危险操作</AlertTitle> <AlertDescription> 此操作将永久删除数据,且无法恢复! </AlertDescription> </Alert>

使用建议destructive 变体通常与 status="error" 配合使用,用于强调需要用户谨慎对待的操作或错误。


可关闭功能

通过 closable 属性启用关闭按钮,允许用户手动关闭提示:

import { useState } from 'react' import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' export default function ClosableAlert() { const [visible, setVisible] = useState(true) if (!visible) return null return ( <Alert status="info" closable> <div onClick={() => setVisible(false)}> <AlertTitle>可关闭提示</AlertTitle> <AlertDescription> 点击右侧关闭按钮可以隐藏此提示 </AlertDescription> </div> </Alert> ) }

实现说明:目前 Alert 组件的 closable 属性仅显示关闭按钮 UI,实际的关闭逻辑需要在父组件中通过状态管理实现。可以通过包裹一个可点击区域或监听关闭按钮的点击事件来控制 Alert 的显示/隐藏。


Props API

Alert Props

属性类型默认值必需说明
status'success' | 'error' | 'warning' | 'info'-警告提示的状态类型,决定图标和语义
variant'default' | 'destructive''default'警告提示的样式变体
closablebooleanfalse是否显示关闭按钮
childrenReactNode-警告提示的内容(通常包含 AlertTitle 和/或 AlertDescription)
classNamestring-自定义 CSS 类名
rolestring'alert'ARIA 角色(自动设置为 alert)

AlertTitle Props

属性类型默认值必需说明
childrenReactNode-标题内容
classNamestring-自定义 CSS 类名

AlertDescription Props

属性类型默认值必需说明
childrenReactNode-描述内容
classNamestring-自定义 CSS 类名

继承的 Props

Alert 组件继承 HTMLDivElement 的所有属性。


类型定义

import { VariantProps } from 'class-variance-authority' import * as React from 'react' // Alert 组件 Props interface AlertProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof alertVariants> { /** * 警告提示的状态类型 * - success: 成功(绿色对勾图标) * - error: 错误(红色警告图标) * - warning: 警告(黄色警告图标) * - info: 信息(蓝色信息图标) */ status?: 'success' | 'error' | 'warning' | 'info' /** * 是否显示关闭按钮 * 注意:需要配合父组件状态管理实现实际的关闭功能 */ closable?: boolean } // AlertTitle 组件 Props interface AlertTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {} // AlertDescription 组件 Props interface AlertDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {} // 组件导出 const Alert = React.forwardRef<HTMLDivElement, AlertProps>(...) const AlertTitle = React.forwardRef<HTMLParagraphElement, AlertTitleProps>(...) const AlertDescription = React.forwardRef<HTMLParagraphElement, AlertDescriptionProps>(...) Alert.displayName = 'Alert' AlertTitle.displayName = 'AlertTitle' AlertDescription.displayName = 'AlertDescription' export { Alert, AlertTitle, AlertDescription } export type { AlertProps, AlertTitleProps, AlertDescriptionProps }

使用示例

基础示例

import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' export default function BasicExample() { return ( <div className="space-y-4"> <Alert status="info"> <AlertTitle>提示</AlertTitle> <AlertDescription>这是一条信息提示</AlertDescription> </Alert> <Alert status="success"> <AlertTitle>成功</AlertTitle> <AlertDescription>操作已成功完成</AlertDescription> </Alert> <Alert status="warning"> <AlertTitle>警告</AlertTitle> <AlertDescription>请注意这个警告信息</AlertDescription> </Alert> <Alert status="error"> <AlertTitle>错误</AlertTitle> <AlertDescription>发生了一个错误</AlertDescription> </Alert> </div> ) }

仅包含描述

对于简短的提示信息,可以只使用 AlertDescription:

import { Alert, AlertDescription } from '@anker-in/headless-ui' export default function DescriptionOnly() { return ( <Alert status="info"> <AlertDescription> 您有新的系统消息,请及时查看。 </AlertDescription> </Alert> ) }

可关闭的警告提示

import { useState } from 'react' import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' export default function ClosableExample() { const [alerts, setAlerts] = useState({ welcome: true, update: true, warning: true, }) const closeAlert = (key: keyof typeof alerts) => { setAlerts((prev) => ({ ...prev, [key]: false })) } return ( <div className="space-y-4"> {alerts.welcome && ( <Alert status="success" closable> <div onClick={() => closeAlert('welcome')}> <AlertTitle>欢迎</AlertTitle> <AlertDescription> 感谢您使用我们的服务! </AlertDescription> </div> </Alert> )} {alerts.update && ( <Alert status="info" closable> <div onClick={() => closeAlert('update')}> <AlertTitle>系统更新</AlertTitle> <AlertDescription> 新版本已发布,包含多项功能改进。 </AlertDescription> </div> </Alert> )} {alerts.warning && ( <Alert status="warning" closable> <div onClick={() => closeAlert('warning')}> <AlertTitle>注意</AlertTitle> <AlertDescription> 您的会话即将过期,请保存工作内容。 </AlertDescription> </div> </Alert> )} </div> ) }

表单验证反馈

import { useState } from 'react' import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' export default function FormValidation() { const [errors, setErrors] = useState<string[]>([]) const [success, setSuccess] = useState(false) const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault() const formData = new FormData(e.currentTarget) const email = formData.get('email') as string const password = formData.get('password') as string // 验证逻辑 const newErrors: string[] = [] if (!email) newErrors.push('邮箱不能为空') if (!email.includes('@')) newErrors.push('邮箱格式不正确') if (!password) newErrors.push('密码不能为空') if (password.length < 6) newErrors.push('密码至少需要6个字符') if (newErrors.length > 0) { setErrors(newErrors) setSuccess(false) } else { setErrors([]) setSuccess(true) } } return ( <form onSubmit={handleSubmit} className="max-w-md space-y-4"> {errors.length > 0 && ( <Alert status="error" variant="destructive"> <AlertTitle>表单验证失败</AlertTitle> <AlertDescription> <ul className="mt-2 list-inside list-disc"> {errors.map((error, index) => ( <li key={index}>{error}</li> ))} </ul> </AlertDescription> </Alert> )} {success && ( <Alert status="success"> <AlertTitle>注册成功</AlertTitle> <AlertDescription> 您的账户已创建,欢迎加入我们! </AlertDescription> </Alert> )} <div> <label htmlFor="email" className="block text-sm font-medium"> 邮箱 </label> <input id="email" name="email" type="email" className="mt-1 block w-full rounded-md border px-3 py-2" /> </div> <div> <label htmlFor="password" className="block text-sm font-medium"> 密码 </label> <input id="password" name="password" type="password" className="mt-1 block w-full rounded-md border px-3 py-2" /> </div> <button type="submit" className="rounded-md bg-primary px-4 py-2 text-white" > 注册 </button> </form> ) }

API 请求状态

import { useState } from 'react' import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' export default function ApiStatus() { const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle') const [message, setMessage] = useState('') const fetchData = async () => { setStatus('loading') try { // 模拟 API 请求 await new Promise((resolve, reject) => { setTimeout(() => { Math.random() > 0.5 ? resolve('success') : reject('error') }, 1000) }) setStatus('success') setMessage('数据加载成功') } catch (error) { setStatus('error') setMessage('数据加载失败,请重试') } } return ( <div className="space-y-4"> <button onClick={fetchData} disabled={status === 'loading'} className="rounded-md bg-primary px-4 py-2 text-white disabled:opacity-50" > {status === 'loading' ? '加载中...' : '获取数据'} </button> {status === 'loading' && ( <Alert status="info"> <AlertTitle>加载中</AlertTitle> <AlertDescription> 正在从服务器获取数据,请稍候... </AlertDescription> </Alert> )} {status === 'success' && ( <Alert status="success"> <AlertTitle>成功</AlertTitle> <AlertDescription>{message}</AlertDescription> </Alert> )} {status === 'error' && ( <Alert status="error" variant="destructive"> <AlertTitle>错误</AlertTitle> <AlertDescription>{message}</AlertDescription> </Alert> )} </div> ) }

系统公告

import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' export default function SystemAnnouncement() { return ( <div className="space-y-4"> <Alert status="warning"> <AlertTitle>系统维护通知</AlertTitle> <AlertDescription> 系统将于 2025 年 1 月 20 日 02:00 - 04:00 进行维护, 期间部分功能可能无法使用,给您带来不便敬请谅解。 </AlertDescription> </Alert> <Alert status="info"> <AlertTitle>新功能上线</AlertTitle> <AlertDescription> 我们很高兴地宣布,全新的数据分析功能已经上线! <a href="#" className="ml-1 font-medium underline"> 了解详情 → </a> </AlertDescription> </Alert> </div> ) }

自定义样式

import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' export default function CustomStyle() { return ( <div className="space-y-4"> {/* 自定义背景色 */} <Alert status="success" className="border-green-500 bg-green-50 dark:bg-green-950" > <AlertTitle className="text-green-800 dark:text-green-200"> 订单已完成 </AlertTitle> <AlertDescription className="text-green-700 dark:text-green-300"> 您的订单 #12345 已成功处理并发货 </AlertDescription> </Alert> {/* 自定义边框和阴影 */} <Alert status="warning" className="border-2 border-yellow-500 shadow-lg" > <AlertTitle>重要提醒</AlertTitle> <AlertDescription> 请在截止日期前完成认证,否则将影响账户使用 </AlertDescription> </Alert> {/* 自定义圆角和内边距 */} <Alert status="info" className="rounded-2xl px-6 py-4" > <AlertTitle>温馨提示</AlertTitle> <AlertDescription> 定期更新密码可以提高账户安全性 </AlertDescription> </Alert> </div> ) }

带操作按钮

import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' import { Button } from '@anker-in/headless-ui' export default function AlertWithActions() { const handleConfirm = () => { console.log('用户确认了操作') } const handleCancel = () => { console.log('用户取消了操作') } return ( <Alert status="warning"> <AlertTitle>确认删除</AlertTitle> <AlertDescription> <p className="mb-3"> 此操作将永久删除该项目及其所有相关数据,无法恢复。 您确定要继续吗? </p> <div className="flex gap-2"> <Button size="sm" variant="destructive" onClick={handleConfirm} > 确认删除 </Button> <Button size="sm" variant="outline" onClick={handleCancel} > 取消 </Button> </div> </AlertDescription> </Alert> ) }

可访问性

ARIA 属性

  • Alert 组件自动设置 role="alert",向辅助技术声明这是一个警告提示区域
  • 当 Alert 出现时,屏幕阅读器会立即通知用户
  • AlertTitle 使用语义化的 <h5> 标签,提供清晰的信息层级

最佳实践

提供清晰的标题和描述

使用 AlertTitle 提供简洁的标题,AlertDescription 提供详细的说明文本,帮助所有用户(包括使用屏幕阅读器的用户)快速理解提示内容。

选择合适的状态类型

根据消息的重要性和性质选择正确的 status:

  • 成功操作使用 success
  • 错误信息使用 error
  • 需要注意的事项使用 warning
  • 一般信息使用 info

确保足够的颜色对比度

虽然 Alert 使用图标来区分状态,但仍要确保文本与背景的对比度符合 WCAG AA 标准(至少 4.5:1)。

避免仅依赖颜色传达信息

Alert 组件已经为每种状态配备了不同的图标,确保即使是色盲用户也能区分不同类型的提示。

关闭功能的可访问性

如果使用 closable 属性,确保关闭按钮可以通过键盘访问(Tab 键聚焦 + Enter/Space 触发)。当前实现需要在父组件中添加键盘事件处理:

<Alert status="info" closable> <div onClick={() => setVisible(false)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() setVisible(false) } }} role="button" tabIndex={0} > <AlertTitle>可关闭提示</AlertTitle> <AlertDescription>按 Enter 或 Space 关闭</AlertDescription> </div> </Alert>

重要信息持久化

Alert 适合显示需要用户确认或阅读的重要信息。对于临时性的通知,考虑使用 Toast 组件。


最佳实践

✅ 推荐做法

  • 为每个 Alert 提供清晰的标题和描述
  • 根据消息的严重程度选择合适的 status
  • 使用 destructive 变体强调破坏性操作或严重错误
  • 在表单顶部显示验证错误的 Alert
  • 为可关闭的 Alert 实现状态管理
// ✅ 正确示例:完整的信息结构 <Alert status="error" variant="destructive"> <AlertTitle>操作失败</AlertTitle> <AlertDescription> 由于网络错误,您的数据未能保存。 请检查网络连接后重试。 </AlertDescription> </Alert> // ✅ 正确示例:使用状态管理实现关闭功能 function Example() { const [visible, setVisible] = useState(true) if (!visible) return null return ( <Alert status="info" closable> <div onClick={() => setVisible(false)}> <AlertTitle>提示</AlertTitle> <AlertDescription>这是一条可关闭的提示</AlertDescription> </div> </Alert> ) }

❌ 避免做法

  • 不要在一个页面同时显示过多 Alert(建议不超过 3 个)
  • 不要使用 Alert 显示不重要的信息
  • 不要省略 AlertTitle(除非内容非常简短)
  • 不要将 Alert 用于临时通知(应使用 Toast)
  • 不要在 Alert 中放置过长的文本(超过 3-4 行)
// ❌ 错误示例:信息过于简单,不需要 Alert <Alert status="info"> <AlertDescription>已加载</AlertDescription> </Alert> // ✅ 正确示例:使用 Toast 或简单的文本提示 <p className="text-sm text-muted-foreground">已加载</p> // ❌ 错误示例:文本过长,影响阅读 <Alert status="info"> <AlertDescription> 这是一段非常非常长的文本,包含了大量的详细信息... (超过 200 字的内容) </AlertDescription> </Alert> // ✅ 正确示例:长文本使用折叠或链接 <Alert status="info"> <AlertTitle>系统更新</AlertTitle> <AlertDescription> 系统已更新至最新版本。 <a href="#" className="ml-1 underline">查看详情</a> </AlertDescription> </Alert>

常见问题

如何实现自动关闭功能?

配合 setTimeout 和状态管理实现:

function AutoCloseAlert() { const [visible, setVisible] = useState(true) useEffect(() => { const timer = setTimeout(() => { setVisible(false) }, 5000) // 5 秒后自动关闭 return () => clearTimeout(timer) }, []) if (!visible) return null return ( <Alert status="success" closable> <div onClick={() => setVisible(false)}> <AlertTitle>操作成功</AlertTitle> <AlertDescription> 此提示将在 5 秒后自动关闭 </AlertDescription> </div> </Alert> ) }

Alert 和 Toast 有什么区别?

特性AlertToast
显示位置文档流内(静态位置)屏幕角落(固定位置)
持久性持久显示,需手动关闭临时显示,自动消失
适用场景重要信息、需确认的内容操作反馈、临时通知
布局影响占用页面布局空间不影响页面布局

如何自定义 Alert 的图标?

目前 Alert 的图标是内置的 SVG,如果需要自定义图标,可以不使用 status 属性,手动传入图标:

import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' import { CustomIcon } from './icons' <Alert className="pl-7"> <CustomIcon className="absolute left-4 top-2 size-6" /> <AlertTitle>自定义图标</AlertTitle> <AlertDescription>使用自定义的 SVG 图标</AlertDescription> </Alert>

Alert 是否支持多行文本?

支持。AlertDescription 可以包含任意长度的文本,但建议控制在 3-4 行以内以保持良好的可读性:

<Alert status="info"> <AlertTitle>注意事项</AlertTitle> <AlertDescription> <p className="mb-2">第一段提示信息。</p> <p>第二段补充说明。</p> <ul className="mt-2 list-inside list-disc"> <li>要点一</li> <li>要点二</li> </ul> </AlertDescription> </Alert>

如何在 Alert 中添加链接或按钮?

可以在 AlertDescription 中直接添加交互元素:

<Alert status="warning"> <AlertTitle>版本更新</AlertTitle> <AlertDescription> <p className="mb-3"> 检测到新版本,建议立即更新以获得最佳体验。 </p> <div className="flex gap-2"> <button className="rounded bg-primary px-3 py-1 text-sm text-white"> 立即更新 </button> <button className="rounded border px-3 py-1 text-sm"> 稍后提醒 </button> </div> </AlertDescription> </Alert>

如何让 Alert 只显示一次(使用 localStorage)?

function OneTimeAlert() { const [visible, setVisible] = useState(() => { return localStorage.getItem('alert-shown') !== 'true' }) const handleClose = () => { localStorage.setItem('alert-shown', 'true') setVisible(false) } if (!visible) return null return ( <Alert status="info" closable> <div onClick={handleClose}> <AlertTitle>欢迎</AlertTitle> <AlertDescription> 这是您第一次访问,此提示只会显示一次。 </AlertDescription> </div> </Alert> ) }

如何实现 Alert 的进入/退出动画?

配合 CSS 过渡或动画库(如 Framer Motion)实现:

import { useState } from 'react' import { Alert, AlertTitle, AlertDescription } from '@anker-in/headless-ui' function AnimatedAlert() { const [visible, setVisible] = useState(true) return ( <div className={`transition-all duration-300 ${ visible ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-4' }`} > {visible && ( <Alert status="success" closable> <div onClick={() => setVisible(false)}> <AlertTitle>成功</AlertTitle> <AlertDescription>操作已完成</AlertDescription> </div> </Alert> )} </div> ) }

相关资源


更新日志

v2.0.0 (2025-01-17)

  • 🎉 完善组件文档
  • ✨ 新增四种状态类型(success, error, warning, info)
  • ✨ 新增可关闭功能(closable 属性)
  • ✨ 新增 destructive 变体
  • ♿ 改进可访问性(ARIA 属性、语义化标签)
  • 📝 完善使用示例和最佳实践

本文档有帮助吗?

在 GitHub 上反馈

Last updated on