wui
组件

文字提示 Tooltip

在鼠标悬停或键盘聚焦时,为图标或紧凑控件浮层展示简短的上下文说明与快捷键。

第三方依赖 · radix-ui第三方依赖 · class-variance-authority

基础用法

最基础的文字提示用法。包裹图标按钮,为纯图标交互提供明确的语义标签与快捷键说明:

Loading…

安装与引入

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

pnpm dlx @wui-design/cli@latest add @wui/tooltip
安装基础依赖与 Radix 原语
pnpm add radix-ui class-variance-authority clsx tailwind-merge
复制组件源码到 components/ui/tooltip.tsx
components/ui/tooltip.tsx
"use client"

import * as React from "react"
import { cva } from "class-variance-authority"
import { Tooltip as TooltipPrimitive } from "radix-ui"

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

const tooltipContentVariants = cva(
  "z-50 max-w-72 origin-(--radix-tooltip-content-transform-origin) rounded-md bg-foreground font-medium text-background shadow-md outline-none will-change-[transform,opacity] animate-in fade-in-0 zoom-in-95 duration-150 ease-[cubic-bezier(0.22,1,0.36,1)] data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 data-[state=instant-open]:zoom-in-100 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:duration-100 data-[state=closed]:ease-in motion-reduce:animate-none",
  {
    variants: {
      size: {
        sm: "px-2 py-1 text-[11px] leading-4",
        default: "px-2.5 py-1.5 text-xs leading-4",
      },
    },
    defaultVariants: {
      size: "default",
    },
  }
)

export interface TooltipProviderProps
  extends React.ComponentProps<typeof TooltipPrimitive.Provider> {}

/** Coordinates open delays between multiple tooltips. */
function TooltipProvider({
  delayDuration = 350,
  skipDelayDuration = 200,
  ...props
}: TooltipProviderProps) {
  return (
    <TooltipPrimitive.Provider
      delayDuration={delayDuration}
      skipDelayDuration={skipDelayDuration}
      {...props}
    />
  )
}

export interface TooltipProps
  extends Omit<
    React.ComponentProps<typeof TooltipPrimitive.Root>,
    | "open"
    | "defaultOpen"
    | "onOpenChange"
    | "delayDuration"
    | "disableHoverableContent"
  > {
  /** Controlled open state. */
  open?: boolean
  /** Initial open state when uncontrolled. @default false */
  defaultOpen?: boolean
  /** Called whenever the open state changes. */
  onOpenChange?: (open: boolean) => void
  /** Hover delay in milliseconds for this tooltip. */
  delayDuration?: number
  /** Close when the pointer leaves the trigger instead of allowing content hover. @default false */
  disableHoverableContent?: boolean
}

/** Controls the open state of one tooltip. */
function Tooltip(props: TooltipProps) {
  return <TooltipPrimitive.Root {...props} />
}

function TooltipTrigger(
  props: React.ComponentProps<typeof TooltipPrimitive.Trigger>
) {
  return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}

export interface TooltipContentProps
  extends Omit<
    React.ComponentProps<typeof TooltipPrimitive.Content>,
    "sideOffset"
  > {
  /** Physical size of the tooltip panel. @default "default" */
  size?: "sm" | "default"
  /** Gap in pixels between the trigger and panel. @default 6 */
  sideOffset?: number
  /** Whether to render the directional arrow. @default true */
  showArrow?: boolean
}

/** A portal-rendered label or short description for the trigger. */
function TooltipContent({
  className,
  sideOffset = 6,
  size = "default",
  showArrow = true,
  children,
  ...props
}: TooltipContentProps) {
  return (
    <TooltipPrimitive.Portal>
      <TooltipPrimitive.Content
        data-slot="tooltip-content"
        data-size={size}
        sideOffset={sideOffset}
        className={cn(tooltipContentVariants({ size }), className)}
        {...props}
      >
        {children}
        {showArrow ? (
          <TooltipPrimitive.Arrow
            data-slot="tooltip-arrow"
            className="fill-foreground"
            width={8}
            height={4}
          />
        ) : null}
      </TooltipPrimitive.Content>
    </TooltipPrimitive.Portal>
  )
}

export {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
  tooltipContentVariants,
}

属性 Props

TooltipProvider

用于全局或局部协调多个相邻 Tooltip 的打开与跳过延迟:

属性类型默认值说明
delayDurationnumber350指针悬停到触发器后,浮层延迟显示的毫秒数。
skipDelayDurationnumber200在移动到下一个相邻 Tooltip 时,跳过延迟直接显示的毫秒窗口期。
disableHoverableContentbooleanfalse当指针离开触发器时是否立即关闭提示(禁止指针移入 Tooltip 内容本身)。

Tooltip

单个提示项的根状态控制器:

属性类型默认值说明
openboolean—受控模式下的展开状态。
defaultOpenbooleanfalse非受控模式下的初始展开状态。
onOpenChange(open: boolean) => void—展开状态发生变化时的回调函数。
delayDurationnumber—覆盖当前 Tooltip 单实例的悬停延迟毫秒数。
disableHoverableContentbooleanfalse单独对当前实例禁止悬浮内容检测。

TooltipTrigger

触发器组件,包裹触发 Tooltip 打开的目标元素:

属性类型默认值说明
asChildbooleanfalse是否将触发器属性与事件直接合并到传入的单个子元素上。

TooltipContent

浮层面板内容容器,挂载至 Portal 门禁并在视口边缘自动计算避让:

属性类型默认值说明
size"sm" | "default""default"提示面板的视觉尺寸。sm 为超紧凑微提示,default 为常规提示。
side"top" | "right" | "bottom" | "left""top"提示面板首选出现的方位。若视口空间不足会自动反转对齐。
align"start" | "center" | "end""center"提示面板在对应侧边缘的对齐方式。
sideOffsetnumber6提示面板与触发器之间的间距偏移量(像素)。
showArrowbooleantrue是否在提示面板边缘渲染指向触发器的三角形指示箭头。
classNamestring—应用于提示面板容器的额外 CSS 类名。

事件 Events

属性类型默认值说明
onOpenChange(open: boolean) => void—提示因鼠标悬停、焦点获入/丢失或按键操作改变显隐状态时触发。
onEscapeKeyDown(event: KeyboardEvent) => void—在 Tooltip 打开状态下按下 Escape 键时触发,可通过 event.preventDefault() 阻止关闭。
onPointerDownOutside(event: CustomEvent) => void—在 Tooltip 范围外部发生指针按下事件时触发。

使用场景与设计规范

Tooltip 用于为界面中的紧凑元素(如纯图标按钮、截断缩略文本、专业术语指标)提供非必要、轻量级的辅助说明。

  • 组件选型对比:
    • Tooltip:只读纯文本或带快捷键的微提示,不可包含可交互链接或表单按钮。
    • Popover:需要用户点击内部链接、复制大段文本或在浮层内填写简易表单时使用。
    • Dialog / Modal:重大业务操作、复杂多步骤表单。
  • 共享 TooltipProvider:建议在工具栏或页面根级使用单个 TooltipProvider,这样当用户快速从“复制”图标划到“剪切”图标时,第二个提示会瞬时响应而不会重复经历 350ms 的等待延迟。
  • 文案克制原则:Tooltip 文案应极度精炼(通常 1~2 句话或若干词汇),避免大段冗长段落。

场景示例

弹出方位与指示箭头

支持 top、right、bottom、left 四个方向定位,并内置智能防溢出碰撞检测。提示从触发器方向轻微滑入,并以箭头所在位置为缩放原点展开:

Loading…

尺寸规格与快捷键徽标

提供 default 与 sm 尺寸,并在内容中以弱化文字标注键盘快捷键。在同一个 TooltipProvider 内快速划过多个触发器时,后续提示瞬时切换,仅保留淡入而不再缩放:

Loading…

丰富上下文说明(多行/状态说明)

在需要辅助解释复杂系统安全机制或指标含义时使用富文本样式:

Loading…

受控模式

通过 open 与 onOpenChange 将提示显隐状态委托给外部 React 状态。示例中点击复制后主动展开“已复制”反馈,并在提示期间忽略指针移出导致的关闭:

Loading…

无障碍与交互 Accessibility

  • ARIA 关联规范:底层自动为触发器赋予 aria-describedby 关联 TooltipContent 的唯一 ID,屏幕阅读器在聚焦时能即刻播报提示内容。
  • 键盘焦点感知:当用户使用 Tab 键将焦点导航至触发器时,Tooltip 会自动展开;按下 Esc 键可立即关闭提示。
  • 视口边缘碰撞避让:内置智能碰撞检测(Collision Detection),当触发器靠近屏幕边缘导致 Tooltip 放不下时,浮层会自动翻转至对立侧或调整轴向偏移。
  • 触控设备优化:在移动端长按触发器时能呼出提示,松手后轻触外部任意区域即可平滑收起。
  • 动效降级:进入与退出动画在系统开启“减少动态效果”时自动关闭。