wui
组件

切换按钮 Toggle

用于在激活(On)与未激活(Off)两种双态之间切换并持久保持状态的轻量级动作按钮。

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

基础用法

最简单的切换按钮。点击切换文字粗体属性并保持激活高亮状态:

Loading…

安装与引入

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

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

import * as React from "react"
import { Toggle as TogglePrimitive } from "radix-ui"
import { cva, type VariantProps } from "class-variance-authority"

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

const toggleVariants = cva(
  "hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring/30 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium outline-none transition-[color,background-color,border-color,box-shadow,scale] duration-200 ease-out active:scale-[0.96] motion-reduce:active:scale-100 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
  {
    variants: {
      variant: {
        default: "border border-transparent",
        outline: "border-input bg-background shadow-xs border",
      },
      size: {
        sm: "h-8 min-w-8 px-2",
        default: "h-9 min-w-9 px-2.5",
        lg: "h-10 min-w-10 px-3",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ToggleProps
  extends
    React.ComponentProps<typeof TogglePrimitive.Root>,
    VariantProps<typeof toggleVariants> {}

/** 表示可保持开启或关闭状态的双态按钮。 */
function Toggle({
  className,
  variant = "default",
  size = "default",
  ...props
}: ToggleProps) {
  return (
    <TogglePrimitive.Root
      data-slot="toggle"
      className={cn(toggleVariants({ variant, size }), className)}
      {...props}
    />
  )
}

export { Toggle, toggleVariants }

属性 Props

Toggle 支持以下配置属性,并继承 Radix UI TogglePrimitive.Root 的全部 HTML 按钮属性:

属性类型默认值说明
pressedboolean—受控模式下的当前激活按下状态。
defaultPressedbooleanfalse非受控模式下的初始激活状态。
onPressedChange(pressed: boolean) => void—切换按钮状态改变时触发的回调函数,返回最新的布尔值。
variant"default" | "outline""default"按钮的视觉外观风格。'outline' 提供边界框与背景底色。
size"sm" | "default" | "lg""default"切换按钮的物理尺寸密度。
disabledbooleanfalse是否禁用按钮的交互与焦点。
aria-labelstring—纯图标模式下必须提供的无障碍文本标签。
classNamestring—应用于切换按钮的额外 CSS 类名。

事件 Events

属性类型默认值说明
onPressedChange(pressed: boolean) => void—当用户通过点击、触摸或按键(Space / Enter)改变切换按钮状态时触发。
onFocus(event: React.FocusEvent<HTMLButtonElement>) => void—按钮获得焦点时触发。
onBlur(event: React.FocusEvent<HTMLButtonElement>) => void—按钮失去焦点时触发。

使用场景与设计规范

Toggle 适用于状态持续保持的双态独立开关(如富文本加粗/斜体、收藏加星、窗口置顶、麦克风静音)。

  • Toggle vs Button vs Switch:
    • Toggle(切换按钮):以紧凑按钮形式呈现,激活后保持高亮按下状态,适合工具栏或快捷栏。
    • Button(普通按钮):即点即走,触发一次性动作(如“提交”、“下载”),不保留选中状态。
    • Switch(开关):具备滑动轨道的物理开关视觉,适合具有强二元开启/关闭含义的设置项。
  • 纯图标无障碍要求:若 Toggle 内部仅包含图标而无文字,必须显式提供 aria-label(如 aria-label="切换粗体"),确保屏幕阅读器用户能明确理解操作意图。

场景示例

收藏加星与业务状态

在内容流中用作收藏(Bookmark)和标星(Star)切换,支持自定义激活状态下的高亮色系,激活时图标轻微弹跳反馈:

Loading…

音视频会议控制

在会议控制栏中切换麦克风与摄像头,图标以旋转缩放的方式交叉切换。纯图标 Toggle 的 aria-label 应保持稳定,由 aria-pressed 表达当前状态:

Loading…

尺寸与外观风格

提供 sm、default、lg 三种尺寸,支持无边框默认风格与 outline 带边框风格:

Loading…

无障碍与交互 Accessibility

  • ARIA 规范:底层基于 Radix UI Toggle,自动挂载 role="button" 与 aria-pressed="true|false" 属性,清晰表达按压状态。
  • 键盘导航:
    • Tab:按 DOM 顺序将焦点移动至切换按钮。
    • Space / Enter:触发状态切换。
  • 焦点环高亮:提供统一的高对比度 focus-visible:ring,确保键盘导航清晰可见。
  • 按压反馈:按下时有轻微缩放(0.96),系统开启“减少动态效果”时自动关闭。