组件
渐变环绕发光 Glow
让多色彩虹渐变或单色呼吸光效沿内容边缘持续环绕流动,形成柔和漫射光晕,用于打造高视觉吸引力的重点卡片与推荐位。
基础用法
最简单的发光包装用法。组件提供多色流光(Rainbow)与单色呼吸(Solid)两种光效变体:
Loading…
安装与引入
通过 CLI 自动添加组件,或手动复制源码至项目中:
pnpm dlx @wui-design/cli@latest add @wui/glow安装动效依赖与工具库
pnpm add motion clsx tailwind-merge复制组件源码到
components/ui/glow.tsx"use client"
import * as React from "react"
import { motion, useReducedMotion, type HTMLMotionProps } from "motion/react"
import { cn } from "@/lib/utils"
export interface GlowProps extends Omit<HTMLMotionProps<"div">, "children"> {
/** Content placed above the glow. */
children: React.ReactNode
/** Visual treatment for the illuminated edge. @default "rainbow" */
variant?: "rainbow" | "solid"
/** CSS color used by the solid variant. @default "var(--chart-1)" */
color?: string
/** Colors distributed around the rainbow edge. Defaults to the theme chart palette. */
colors?: string[]
/** Distance the light spreads beyond the edge, in pixels. @default 18 */
spread?: number
/** Width of the illuminated edge, in pixels. @default 1 */
borderWidth?: number
/** Peak opacity of the glow. @default 0.7 */
glowOpacity?: number
/** Gently pulse the solid variant. @default true */
pulse?: boolean
/** Seconds for one animation cycle. @default 4 */
duration?: number
}
/** Adds an illuminated border and soft light around all four sides of content. */
function Glow({
children,
variant = "rainbow",
color = "var(--chart-1)",
colors = [
"var(--chart-1)",
"var(--chart-5)",
"var(--chart-2)",
"var(--chart-3)",
"var(--chart-1)",
],
spread = 18,
borderWidth = 1,
glowOpacity = 0.7,
pulse = true,
duration = 4,
className,
...props
}: GlowProps) {
const reduceMotion = useReducedMotion()
const shouldPulse = pulse && !reduceMotion
const lightSpread = Math.max(0, spread)
const edgeWidth = Math.max(0, borderWidth)
const gradient = (angle: number) =>
`conic-gradient(from ${angle}deg, ${colors.join(", ")})`
const mask =
"linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)"
const ringStyle: React.CSSProperties = {
inset: -edgeWidth,
padding: edgeWidth,
maskImage: mask,
maskComposite: "exclude",
WebkitMaskImage: mask,
WebkitMaskComposite: "xor",
}
const rainbowTransition = reduceMotion
? { duration: 0 }
: { duration, ease: "linear" as const, repeat: Infinity }
return (
<motion.div
data-slot="glow"
className={cn("relative isolate rounded-lg", className)}
{...props}
>
{variant === "rainbow" ? (
<>
<motion.div
aria-hidden="true"
data-slot="glow-light"
className="pointer-events-none absolute rounded-[inherit]"
style={{
...ringStyle,
backgroundImage: gradient(0),
filter: `blur(${lightSpread * 0.45}px)`,
}}
initial={false}
animate={
reduceMotion
? { opacity: glowOpacity }
: {
backgroundImage: [gradient(0), gradient(360)],
opacity: glowOpacity,
}
}
transition={rainbowTransition}
/>
<motion.div
aria-hidden="true"
data-slot="glow-edge"
className="pointer-events-none absolute rounded-[inherit]"
style={{ ...ringStyle, backgroundImage: gradient(0) }}
initial={false}
animate={
reduceMotion
? { opacity: 1 }
: {
backgroundImage: [gradient(0), gradient(360)],
opacity: 1,
}
}
transition={rainbowTransition}
/>
</>
) : (
<motion.div
aria-hidden="true"
data-slot="glow-light"
className="pointer-events-none absolute rounded-[inherit]"
style={{
inset: -edgeWidth,
border: `${edgeWidth}px solid ${color}`,
boxShadow: `0 0 ${lightSpread * 0.45}px ${color}, 0 0 ${lightSpread}px ${color}`,
}}
initial={false}
animate={
shouldPulse
? {
opacity: [
glowOpacity * 0.55,
glowOpacity,
glowOpacity * 0.55,
],
}
: { opacity: glowOpacity }
}
transition={
shouldPulse
? { duration, ease: "easeInOut", repeat: Infinity }
: { duration: 0 }
}
/>
)}
<div data-slot="glow-content" className="relative z-10 rounded-[inherit]">
{children}
</div>
</motion.div>
)
}
export { Glow }
属性 Props
Glow 支持以下流光与发光配置,并继承 motion.div 的全部 HTML 属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| children | React.ReactNode | — | 发光边框包裹的内容元素(卡片、徽标、横幅等)。 |
| variant | "rainbow" | "solid" | "rainbow" | 发光视觉变体:rainbow(多色环绕流光)或 solid(单色脉冲呼吸光)。 |
| color | string | "var(--chart-1)" | 在 solid 变体下生效的边框与发光主色(支持 OKLCH / RGB / HEX / CSS 变量)。 |
| colors | string[] | ["var(--chart-1)", "var(--chart-5)", "var(--chart-2)", "var(--chart-3)", "var(--chart-1)"] | 在 rainbow 变体下沿圆周渐变分布的色彩序列(conic-gradient)。 |
| spread | number | 18 | 光晕向外扩散的模糊半径(像素 px)。数值越大漫射光越柔和广阔。 |
| borderWidth | number | 1 | 发光细边框的物理粗细(像素 px)。 |
| glowOpacity | number | 0.7 | 发光漫射层的最大峰值不透明度(0~1)。 |
| pulse | boolean | true | 是否在 solid 变体下开启明暗呼吸动效。 |
| duration | number | 4 | 完成一次完整旋转或呼吸循环所需的周期时间(秒)。 |
| className | string | — | 应用于外层发光容器的额外 CSS 类名(需指定圆角如 rounded-xl 以便光晕贴合)。 |
事件 Events
Glow 作为视觉展示容器透传标准的 React / Motion 容器事件:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| onHoverStart | (event: MouseEvent, info: EventInfo) => void | — | 指针进入发光容器时触发。 |
| onHoverEnd | (event: MouseEvent, info: EventInfo) => void | — | 指针离开发光容器时触发。 |
| onClick | (event: React.MouseEvent<HTMLDivElement>) => void | — | 点击发光卡片容器时触发。 |
使用场景与设计规范
Glow 属于高吸引力的视觉强调组件(Fancy 系列)。
- 适用场景:
- 核心推荐方案(Pro / Featured Plan):定价页中将“最推荐”套餐包裹 Glow,迅速建立视觉第一焦点。
- 旗舰功能或 AI 核心卡片:突出具有代表性的技术亮点。
- 顶部醒目状态胶囊 / 公告条(Pills):新版本发布通知、系统运行健康状态等。
- 色彩与层级搭配:
Glow内部使用智能 CSS 遮罩(Mask exclude),光晕与边框精确贴合在内容最外缘,内部内容保持纯净背景,不会发生色彩溢出污染文字。- 务必确保传递给
Glow的className圆角与内部卡片保持一致(例如两者均为rounded-2xl),以获得最平滑的边缘贴合。
- 克制设计原则:
- 同一屏内通常只推荐配置 1~2 个发光焦点,避免光晕泛滥导致用户无法聚焦核心内容。
场景示例
旗舰推荐定价卡片
在价格对比表中为核心方案包裹流光边框,强化转化意图:
Loading…
状态指示与公告胶囊(Pills)
在小型胶囊标签或系统状态徽标上使用单色呼吸光或彩虹流光:
Loading…
无障碍与交互 Accessibility
- 自动动效降级:当操作系统开启了“减少动态效果(prefers-reduced-motion)”时,彩虹旋转与呼吸脉冲动画自动停止,保持静态柔和光晕,杜绝视觉干扰。
- 非侵入式光效:发光层标注为
aria-hidden="true"并配置pointer-events-none,不遮挡卡片内部的文本选区与按钮点击。 - 可读性保障:发光位于底层外周,内容区域通过实体背景色隔绝,文字始终保持高对比度与清晰可读。