wui
组件

文字微光 Text Shimmer

运用纯 CSS 渐变遮罩与 GPU 渲染管线,在文字表面平滑扫过一道灵动高光的微动效组件。

第三方依赖 · motion

基础用法

最简单的文字微光用法。高光将以设定的周期与渐变宽度在文字表面无限循环扫过:

Loading…

安装与引入

通过 CLI 自动添加组件,或手动复制源码至项目中:

pnpm dlx @wui-design/cli@latest add @wui/text-shimmer
安装依赖与 Motion 动效库
pnpm add motion clsx tailwind-merge
复制组件源码到 components/ui/text-shimmer.tsx
components/ui/text-shimmer.tsx
"use client"

import * as React from "react"
import { motion, useReducedMotion } from "motion/react"

import { cn } from "@/lib/utils"

export interface TextShimmerProps extends React.ComponentProps<"span"> {
  /** Text carrying the shimmer. */
  children: string
  /** HTML element rendered by the component. @default "span" */
  as?: React.ElementType
  /** Seconds for one shimmer pass. @default 2 */
  duration?: number
  /** Gradient width relative to the text. @default 2 */
  spread?: number
}

/** Sweeps a theme-aware highlight across text. */
function TextShimmer({
  children,
  as = "span",
  duration = 2,
  spread = 2,
  className,
  style,
  ...props
}: TextShimmerProps) {
  const reduceMotion = useReducedMotion()
  const Component = React.useMemo(() => motion.create(as), [as])
  const shimmerSpread = `${Math.max(Array.from(children).length * spread, 0)}px`

  return (
    <Component
      data-slot="text-shimmer"
      className={cn(
        "inline-block bg-clip-text text-transparent [--shimmer-base:var(--muted-foreground)] [--shimmer-highlight:var(--foreground)]",
        className
      )}
      style={{
        backgroundImage: reduceMotion
          ? "linear-gradient(var(--shimmer-base),var(--shimmer-base))"
          : "linear-gradient(90deg,transparent calc(50% - var(--shimmer-spread)),var(--shimmer-highlight),transparent calc(50% + var(--shimmer-spread))),linear-gradient(var(--shimmer-base),var(--shimmer-base))",
        backgroundRepeat: "no-repeat",
        backgroundSize: "250% 100%, auto",
        "--shimmer-spread": shimmerSpread,
        ...style,
      } as React.CSSProperties}
      animate={
        reduceMotion
          ? { backgroundPosition: "100% center" }
          : { backgroundPosition: ["100% center", "0% center"] }
      }
      transition={
        reduceMotion
          ? { duration: 0 }
          : { duration, ease: "linear", repeat: Infinity }
      }
      {...props}
    >
      {children}
    </Component>
  )
}

export { TextShimmer }

属性 Props

TextShimmer 支持以下配置属性,并继承底层渲染 HTML 元素(默认 <span>)的全部属性:

属性类型默认值说明
childrenstring—应用微光扫光动效的纯文本内容。
durationnumber2高光完成一次从左到右完整扫描所花费的时间(单位:秒)。
spreadnumber2高光渐变的宽度扩散系数。数值越大高光边缘越柔和开阔,数值越小高光光柱越集中锋利。
asReact.ElementType"span"外层容器所渲染的语义 HTML 标签(如 span, p, h2, h3 等)。
classNamestring—应用于外层容器的额外 CSS 类名,可在此定义 CSS 变量 [--shimmer-base] 与 [--shimmer-highlight]。
styleReact.CSSProperties—应用于外层容器的内联样式对象。

事件 Events

TextShimmer 支持所有原生 HTML 文本元素的鼠标与焦点事件:

属性类型默认值说明
onClick(event: React.MouseEvent) => void—点击微光文字区域时触发。

使用场景与设计规范

TextShimmer 适用于表达进行时、加载等待或尊贵高光特权,常见于 AI 生成过程提示、高级功能订阅 CTA 按钮、后台任务同步卡片与状态指示器。

  • 明暗与对比度保障:默认情况下,底色变量 --shimmer-base 采用次级文字色(var(--muted-foreground)),高光变量 --shimmer-highlight 采用主前景色(var(--foreground))。如需自定义颜色,请确保底色在当前背景下仍满足 WCAG AA 文本对比度标准(≥ 4.5:1)。
  • 持续时间建议:扫光周期推荐设置在 1.5s ~ 2.5s。过快的扫光容易引起视觉疲劳,过慢则缺乏流动感。
  • 与骨架屏结合:在异步数据加载或 AI 流式准备阶段,相比传统的灰色色块骨架屏,带有扫光的动态文本能给用户更明确的进度预期。

场景示例

高优先级行动按钮

将 TextShimmer 与操作按钮结合,提升 AI 操作或升级入口的吸引力。放在主按钮上时,用 color-mix 基于 --primary-foreground 派生底色与高光:

Loading…

后台处理状态卡片

在多步骤的 AI 任务中,只让“正在进行”的那一步带有微光,已完成与待办步骤保持静态,层级一目了然:

Loading…

自定义主题配色

通过覆盖 --shimmer-base 和 --shimmer-highlight CSS 变量适配语义状态色。示例直接使用主题的 success / info / warning / destructive 令牌,并用 color-mix 与背景色混合出高光,亮暗模式自动适配:

Loading…
<TextShimmer
  duration={1.8}
  spread={3}
  className="[--shimmer-base:theme(colors.emerald.600)] [--shimmer-highlight:theme(colors.emerald.200)]"
>
  All nodes synchronized (0 errors)
</TextShimmer>

无障碍与交互 Accessibility

  • 文本可被正常选中与复制:底层通过 bg-clip-text 与 text-transparent 实现渐变填充,DOM 结构内保留完整纯文本,用户可以正常鼠标双击选词并复制文本。
  • 系统减少动态偏好(Reduced Motion):当系统开启 prefers-reduced-motion 时,组件将禁用无限循环的 backgroundPosition 动画,直接展示静态的单色基底文本,保障前庭觉敏感用户的舒适度。