Skip to content
Lenso UI

Switch 开关

用于布尔状态的开关组件。

用法

import { Switch, SwitchGroup, Label } from '@lenso/ui';

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";
export function Basic() {  return (    <Switch>      <Switch.Content>        <Switch.Control>          <Switch.Thumb />        </Switch.Control>        Enable notifications      </Switch.Content>    </Switch>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

组件结构

import { Switch, Label, Description } from '@lenso/ui';
export default () => (  <Switch>    <Switch.Control>      <Switch.Thumb>        <Switch.Icon/> {/* 可选 */}      </Switch.Thumb>    </Switch.Control>    <Switch.Content>      <Label />      <Description /> {/* 可选 */}    </Switch.Content>  </Switch>);

要对多个 Switch 进行分组,请使用 SwitchGroup 组件:

import { Switch, SwitchGroup, Label } from '@lenso/ui';
export default () => (  <SwitchGroup>    <Switch>      <Switch.Control>        <Switch.Thumb />      </Switch.Control>      <Label>Option 1</Label>    </Switch>    <Switch>      <Switch.Control>        <Switch.Thumb />      </Switch.Control>      <Label>Option 2</Label>    </Switch>  </SwitchGroup>);

示例

尺寸

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { display: "flex", gap: "1.5rem" } });export function Sizes() {  return (    <div {...stylex.props(styles.root)}>      <Switch size="sm">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Small        </Switch.Content>      </Switch>      <Switch size="md">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Medium        </Switch.Content>      </Switch>      <Switch size="lg">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Large        </Switch.Content>      </Switch>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

带图标

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import {  BellFill,  BellSlash,  Check,  Microphone,  MicrophoneSlash,  Moon,  Power,  Sun,  VolumeFill,  VolumeSlashFill,} from "@gravity-ui/icons";import { Switch } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  root: { display: "flex", gap: ".75rem" },  icon: { width: ".75rem", height: ".75rem", color: "inherit", opacity: 1 },  offIcon: { opacity: 0.7 },  green: {    backgroundColor: {      default: "rgb(34 197 94 / .8)",      ":is([data-slot='switch'][data-checked] *)": "rgb(34 197 94 / .8)",      ":is([data-slot='switch'][data-checked]:hover *)": "rgb(34 197 94 / .8)",      ":is([data-slot='switch'][data-checked]:active *)": "rgb(34 197 94 / .8)",    },  },  red: {    backgroundColor: {      default: "rgb(239 68 68 / .8)",      ":is([data-slot='switch'][data-checked] *)": "rgb(239 68 68 / .8)",      ":is([data-slot='switch'][data-checked]:hover *)": "rgb(239 68 68 / .8)",      ":is([data-slot='switch'][data-checked]:active *)": "rgb(239 68 68 / .8)",    },  },  purple: {    backgroundColor: {      default: "rgb(168 85 247 / .8)",      ":is([data-slot='switch'][data-checked] *)": "rgb(168 85 247 / .8)",      ":is([data-slot='switch'][data-checked]:hover *)": "rgb(168 85 247 / .8)",      ":is([data-slot='switch'][data-checked]:active *)": "rgb(168 85 247 / .8)",    },  },  blue: {    backgroundColor: {      default: "rgb(59 130 246 / .8)",      ":is([data-slot='switch'][data-checked] *)": "rgb(59 130 246 / .8)",      ":is([data-slot='switch'][data-checked]:hover *)": "rgb(59 130 246 / .8)",      ":is([data-slot='switch'][data-checked]:active *)": "rgb(59 130 246 / .8)",    },  },});const icons = {  check: { off: Power, on: Check, selectedControl: styles.green },  darkMode: { off: Moon, on: Sun, selectedControl: undefined },  microphone: { off: Microphone, on: MicrophoneSlash, selectedControl: styles.red },  notification: { off: BellSlash, on: BellFill, selectedControl: styles.purple },  volume: { off: VolumeFill, on: VolumeSlashFill, selectedControl: styles.blue },};export function WithIcons() {  return (    <div {...stylex.props(styles.root)}>      {Object.entries(icons).map(([key, value]) => (        <Switch          key={key}          defaultChecked          aria-label={key}          size="lg"          render={(props, { checked }) => (            <span {...props}>              <Switch.Content>                <Switch.Control xstyle={checked && value.selectedControl}>                  <Switch.Thumb>                    <Switch.Icon>                      {checked ? (                        <value.on {...stylex.props(styles.icon)} aria-hidden="true" />                      ) : (                        <value.off                          {...stylex.props(styles.icon, styles.offIcon)}                          aria-hidden="true"                        />                      )}                    </Switch.Icon>                  </Switch.Thumb>                </Switch.Control>              </Switch.Content>            </span>          )}        />      ))}    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

禁用

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";export function Disabled() {  return (    <Switch disabled>      <Switch.Content>        <Switch.Control>          <Switch.Thumb />        </Switch.Control>        Enable notifications      </Switch.Content>    </Switch>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

无标签

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";export function WithoutLabel() {  return (    <Switch aria-label="Enable notifications">      <Switch.Content>        <Switch.Control>          <Switch.Thumb />        </Switch.Control>      </Switch.Content>    </Switch>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

带描述

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Description, Switch, TextField } from "@lenso/ui";import { switchSupportingStyles } from "@lenso/tokens/switch";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { maxWidth: "24rem" } });export function WithDescription() {  return (    <div {...stylex.props(styles.root)}>      <TextField>        <Switch aria-describedby="public-profile-help">          <Switch.Content>            <Switch.Control>              <Switch.Thumb />            </Switch.Control>            Public profile          </Switch.Content>          <Description id="public-profile-help" xstyle={switchSupportingStyles.direct}>            Allow others to see your profile information          </Description>        </Switch>      </TextField>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

默认选中

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";export function DefaultSelected() {  return (    <Switch defaultChecked>      <Switch.Content>        <Switch.Control>          <Switch.Thumb />        </Switch.Control>        Enable notifications      </Switch.Content>    </Switch>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

受控组件

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";import { useState } from "react";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  root: { display: "flex", flexDirection: "column", gap: "1rem" },  status: { fontSize: ".875rem", color: "var(--muted)" },});export function Controlled() {  const [isSelected, setIsSelected] = useState(false);  return (    <div {...stylex.props(styles.root)}>      <Switch checked={isSelected} onCheckedChange={setIsSelected}>        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Enable notifications        </Switch.Content>      </Switch>      <p {...stylex.props(styles.status)}>Switch is {isSelected ? "on" : "off"}</p>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

标签位置

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { display: "flex", flexDirection: "column", gap: "1rem" } });export function LabelPosition() {  return (    <div {...stylex.props(styles.root)}>      <Switch>        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Label after        </Switch.Content>      </Switch>      <Switch>        <Switch.Content>          Label before          <Switch.Control>            <Switch.Thumb />          </Switch.Control>        </Switch.Content>      </Switch>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

分组

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch, SwitchGroup } from "@lenso/ui";export function Group() {  return (    <SwitchGroup>      <Switch name="notifications">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Allow Notifications        </Switch.Content>      </Switch>      <Switch name="marketing">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Marketing emails        </Switch.Content>      </Switch>      <Switch name="social">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Social media updates        </Switch.Content>      </Switch>    </SwitchGroup>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

横向分组

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch, SwitchGroup } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { overflowX: "auto" } });export function GroupHorizontal() {  return (    <SwitchGroup xstyle={styles.root} orientation="horizontal">      <Switch name="notifications">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Notifications        </Switch.Content>      </Switch>      <Switch name="marketing">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Marketing        </Switch.Content>      </Switch>      <Switch name="social">        <Switch.Content>          <Switch.Control>            <Switch.Thumb />          </Switch.Control>          Social        </Switch.Content>      </Switch>    </SwitchGroup>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

表单集成

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Button, Switch, SwitchGroup } from "@lenso/ui";import type { FormEvent } from "react";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  form: { display: "flex", flexDirection: "column", gap: "1rem" },  submit: { marginTop: "1rem" },});export function Form() {  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {    e.preventDefault();    const formData = new FormData(e.currentTarget);    alert(      `Form submitted with:\n${Array.from(formData.entries())        .map(([key, value]) => `${key}: ${value}`)        .join("\n")}`,    );  };  return (    <form {...stylex.props(styles.form)} onSubmit={handleSubmit}>      <SwitchGroup>        <Switch name="notifications" value="on">          <Switch.Content>            <Switch.Control>              <Switch.Thumb />            </Switch.Control>            Enable notifications          </Switch.Content>        </Switch>        <Switch defaultChecked name="newsletter" value="on">          <Switch.Content>            <Switch.Control>              <Switch.Thumb />            </Switch.Control>            Subscribe to newsletter          </Switch.Content>        </Switch>        <Switch name="marketing" value="on">          <Switch.Content>            <Switch.Control>              <Switch.Thumb />            </Switch.Control>            Receive marketing updates          </Switch.Content>        </Switch>      </SwitchGroup>      <Button xstyle={styles.submit} size="sm" type="submit" variant="primary">        Submit      </Button>    </form>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

渲染属性

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";export function RenderProps() {  return (    <Switch      render={(props, { checked }) => (        <span {...props}>          <Switch.Content>            <Switch.Control>              <Switch.Thumb />            </Switch.Control>            {checked ? "Enabled" : "Disabled"}          </Switch.Content>        </span>      )}    />  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

渲染函数

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Switch } from "@lenso/ui";export function RenderFunction() {  return (    <Switch render={(props) => <div {...props} data-custom="foo" />}>      <Switch.Content>        <Switch.Control>          <Switch.Thumb />        </Switch.Control>        Enable notifications      </Switch.Content>    </Switch>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义样式

Tailwind CSS

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Description, Label, Switch, TextField } from "@lenso/ui";import { switchSupportingStyles } from "@lenso/tokens/switch";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  control: {    "--switch-control-bg-checked": "var(--success)",    "--switch-control-bg-checked-hover": "var(--success)",  },  copy: { display: "flex", flexDirection: "column", gap: ".125rem" },});export function CustomStyles() {  return (    <TextField>      <Switch id="autosave" aria-describedby="autosave-help">        <Switch.Content>          <Switch.Control xstyle={styles.control}>            <Switch.Thumb />          </Switch.Control>          <div {...stylex.props(styles.copy)}>            <Label>Auto-save drafts</Label>            <Description id="autosave-help" xstyle={switchSupportingStyles.direct}>              Changes are saved as you type.            </Description>          </div>        </Switch.Content>      </Switch>    </TextField>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

全局 CSS

若要自定义组件类,可使用 @layer components 指令。了解更多。

@layer components {  .switch {    @apply inline-flex gap-3 items-center;  }
  .switch__control {    @apply h-5 w-8 bg-gray-400 data-[selected=true]:bg-blue-500;  }
  .switch__thumb {    @apply bg-white shadow-sm;  }
  .switch__content {    @apply flex flex-col gap-1;  }
  .switch__icon {    @apply h-3 w-3 text-current;  }}

样式参考

HeroUI 遵循 BEM 方法论,确保组件变体与状态可复用且易于自定义。

CSS 类

Switch 类 [!toc]

Switch 组件使用以下 CSS 类(查看源码样式):

  • .switch - Switch 根容器(字段)
  • .switch__content - 包裹控件与标签文本的可点击 label
  • .switch__control - Switch 轨道
  • .switch__thumb - 可移动的滑块
  • .switch__icon - 滑块内可选图标
  • .switch--sm - 小尺寸变体
  • .switch--md - 中尺寸变体(默认)
  • .switch--lg - 大尺寸变体

SwitchGroup 类 [!toc]

SwitchGroup 组件使用以下 CSS 类(查看源码样式):

  • .switch-group - Switch 组容器
  • .switch-group__items - Switch 项容器
  • .switch-group--horizontal - 横向布局
  • .switch-group--vertical - 纵向布局(默认)

交互状态

该 Switch 同时支持 CSS 伪类与 data 属性,以提供更灵活的状态控制:

  • 已选中:[data-selected="true"](滑块位置与背景色变化)
  • 悬停::hover 或 [data-hovered="true"](作用于 Switch.Control / 按钮)
  • 聚焦::focus-visible 或 [data-focus-visible="true"](在按钮上显示轨道焦点环)
  • 禁用:[data-disabled="true"](降低透明度,包括帮助文本)
  • 按压::active 或 [data-pressed="true"]

API 参考

Switch

继承自 React Aria SwitchField。

Prop类型默认值描述
size'sm' | 'md' | 'lg''md'Switch 尺寸。
isSelectedbooleanfalseSwitch 是否打开。
defaultSelectedbooleanfalse默认是否打开(非受控)。
isDisabledbooleanfalseSwitch 是否禁用。
isInvalidbooleanfalseSwitch 是否无效。
isReadOnlybooleanfalseSwitch 是否只读。
isRequiredbooleanfalseSwitch 是否必须打开。
validate(value: boolean) => ValidationError | true | null | undefined-自定义校验函数。
validationBehavior'native' | 'aria''native'使用原生 HTML 校验或 ARIA 校验。
namestring-输入元素名称,用于提交 HTML 表单。
valuestring-输入元素值,用于提交 HTML 表单。
onChange(isSelected: boolean) => void-Switch 值变化时的事件处理函数。
onPress(e: PressEvent) => void-Switch 被按下时的事件处理函数。
childrenReact.ReactNode | (values: SwitchFieldRenderProps) => React.ReactNode-Switch 内容或字段级渲染 prop。
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, SwitchFieldRenderProps>-使用自定义渲染函数覆盖默认 DOM 元素。

Switch.Content

包裹控件与标签文本的可点击 <label>。请把 Switch.Control 与 Label 放在它内部;Description/FieldError 作为 Switch.Content 的兄弟节点。对于没有标签的 switch,省略 Label 并在 Switch 上传入 aria-label。

Prop类型默认值描述
childrenReact.ReactNode | (values: SwitchButtonRenderProps) => React.ReactNode-按钮内容(控件 + 标签),或按钮级渲染 prop
classNamestring | (values: SwitchButtonRenderProps) => string-应用到可点击 label 的类名

SwitchFieldRenderProps

在根 Switch 上使用渲染 prop 时,提供以下字段级值:

Prop类型描述
isSelectedbooleanSwitch 当前是否打开。
isDisabledbooleanSwitch 是否禁用。
isReadOnlybooleanSwitch 是否只读。
isInvalidbooleanSwitch 是否无效。
isRequiredbooleanSwitch 是否必填。
stateToggleStateSwitch 的状态。

SwitchButtonRenderProps

Switch.Control 使用按钮级渲染 prop(isHovered、isPressed、isFocusVisible 等)。将函数作为 Switch.Control 的子元素即可访问。

SwitchGroup

Prop类型默认值描述
orientation'horizontal' | 'vertical''vertical'Switch 组方向。
childrenReact.ReactNode-要渲染的 Switch 项。
classNamestring-额外的 CSS 类。

相关组件