组件
闪光按钮 Shiny Button
带有流动扫光效果的填充按钮。
基础示例
Loading…
安装组件:
pnpm dlx wui@latest add @wui/shiny-button"use client"
import * as React from "react"
import { motion, useReducedMotion, type HTMLMotionProps } from "motion/react"
import { cn } from "@/lib/utils"
export interface ShinyButtonProps extends HTMLMotionProps<"button"> {
/** Seconds for one full light sweep across the button. @default 3 */
speed?: number
/** Pause between sweeps, in seconds. @default 1.2 */
gap?: number
// Narrows motion's children type (which allows MotionValue) back to ReactNode.
children?: React.ReactNode
}
/**
* A "fancy" template component: a filled button with a light sweep that
* continuously glides across it. Effect components like this are opt-in and are
* allowed to be more opinionated than the core UI — but they still gate motion
* on `prefers-reduced-motion`. Copy it as a starting point for your own effects.
*/
function ShinyButton({
className,
speed = 3,
gap = 1.2,
children,
...props
}: ShinyButtonProps) {
const reduceMotion = useReducedMotion()
return (
<motion.button
data-slot="shiny-button"
whileTap={reduceMotion ? undefined : { scale: 0.97 }}
className={cn(
"relative inline-flex h-10 shrink-0 items-center justify-center gap-2 overflow-hidden whitespace-nowrap rounded-lg border border-primary/20 bg-primary px-6 text-sm font-medium text-primary-foreground shadow-sm outline-none transition-colors focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
className
)}
{...props}
>
<span className="relative z-10 inline-flex items-center gap-2">
{children}
</span>
{reduceMotion ? null : (
<motion.span
aria-hidden
data-slot="shiny-button-sheen"
className="pointer-events-none absolute inset-y-0 w-1/2"
style={{
background:
"linear-gradient(120deg, transparent 20%, var(--shine) 50%, transparent 80%)",
}}
initial={{ x: "-150%" }}
animate={{ x: "300%" }}
transition={{
duration: speed,
ease: "linear",
repeat: Infinity,
repeatDelay: gap,
}}
/>
)}
</motion.button>
)
}
export { ShinyButton }
组件作用
ShinyButton 是 Fancy 系列的首个组件。该系列包含偏展示性的效果组件,类似 CodePen
中的创意效果;它们与核心 UI 共存,并通过相同的 wui add 流程按需拉取。
效果组件比核心基础组件更有视觉倾向,也更醒目,因此应克制、有目的地使用:
- 适合用于首屏行动按钮、定价页的“升级”按钮或空状态等需要单一视觉焦点的位置。
- 不要在整个表单或工具栏中铺满闪光按钮;持续动画会与内容争夺注意力并造成视觉疲劳。
- 与所有 wui 动画一样,当用户偏好减少动态效果时,扫光会自动禁用,按钮仍保持为普通的实色按钮。
组件属性
ShinyButton 继承 motion.button 的全部属性,并额外提供:
| Prop | Type | Default | Description |
|---|---|---|---|
| speed | number | 3 | Seconds for one full light sweep across the button. |
| gap | number | 1.2 | Pause between sweeps, in seconds. |
| disabled | boolean | — |
事件
ShinyButton 会像 Button 一样,将 onClick、onFocus
等原生按钮事件透传给底层元素。
扩展使用
组件源码位于你的项目中,可以自由调整外观:修改 speed 和 gap 控制扫光的速度与密度,
将 bg-primary 表面替换为渐变,或修改 linear-gradient 实现彩色高光。
添加自己的效果组件
新效果组件遵循标准的组件规范,额外要求仅为在 registry.json 中添加
"categories": ["fancy"],并将页面放到组件侧边栏的 Fancy 分组下。
完整清单见 docs/COMPONENT-SPEC.md 第 9 节。