---
title: 开关 Switch
url: https://ui.zhaozimin.com/components/switch/
markdown: https://ui.zhaozimin.com/components/switch.md
group: 组件
origin: 提炼层组件（src/extracted），本仓库是唯一源头
source:
  - src/extracted/Switch.jsx  # https://ui.zhaozimin.com/src/extracted/Switch.jsx
  - src/extracted/extracted.css  # https://ui.zhaozimin.com/src/extracted/extracted.css
version: v0.2.0  # 设计系统版本，发布于 2026-09-23
scope: .zzm-v4   # 根节点必须带这个类，否则样式不生效
css: https://ui.zhaozimin.com/v/0.2.0/zzm.css   # 锁版本地址，本仓库再改也不影响你
---

# 开关 Switch

> 墨色开关：关闭是浅灰轨道，开启是墨色轨道，白色滑块沿标准缓动滑过去。

### 样张 · 点一下试试

React：

```jsx
import { useState } from 'react'
import { Switch } from './extracted/Switch.jsx'

export default function SwitchDemo() {
  const [on, setOn] = useState(true)
  return (
    <div style={{ display: 'flex', gap: 32, flexWrap: 'wrap', alignItems: 'center' }}>
      <Switch id="sw-notify" checked={on} onChange={setOn} label={on ? '已开启' : '已关闭'} />
      <Switch id="sw-locked" disabled label="禁用" />
    </div>
  )
}
```

HTML（构建时由上面的 React 渲染得到，可直接粘贴）：

```html
<div style="display: flex; gap: 32px; flex-wrap: wrap; align-items: center">
  <span style="display: inline-flex; align-items: center; gap: 12px">
    <button
      id="sw-notify"
      type="button"
      role="switch"
      aria-checked="true"
      class="zzm-switch"
      style="
        width: 42px;
        height: 25px;
        border-radius: 999px;
        border: 0;
        padding: 0;
        flex: none;
        position: relative;
        cursor: pointer;
        opacity: 1;
        background: #1d1d1b;
        transition: background 0.2s ease;
      "
    >
      <span
        class="zzm-switch__knob"
        style="
          position: absolute;
          top: 2.5px;
          left: 19.5px;
          width: 20px;
          height: 20px;
          border-radius: 50%;
          background: #ffffff;
          transition: left 0.2s cubic-bezier(0.2, 0.7, 0.2, 1);
        "
      ></span>
    </button>
    <label for="sw-notify" style="font-size: 12.5px; color: #3f3f3b">已开启</label>
  </span>
  <span style="display: inline-flex; align-items: center; gap: 12px">
    <button
      id="sw-locked"
      type="button"
      role="switch"
      aria-checked="false"
      disabled=""
      class="zzm-switch"
      style="
        width: 42px;
        height: 25px;
        border-radius: 999px;
        border: 0;
        padding: 0;
        flex: none;
        position: relative;
        cursor: not-allowed;
        opacity: 0.6;
        background: #ededea;
        transition: background 0.2s ease;
      "
    >
      <span
        class="zzm-switch__knob"
        style="
          position: absolute;
          top: 2.5px;
          left: 2.5px;
          width: 20px;
          height: 20px;
          border-radius: 50%;
          background: #ffffff;
          transition: left 0.2s cubic-bezier(0.2, 0.7, 0.2, 1);
        "
      ></span>
    </button>
    <label for="sw-locked" style="font-size: 12.5px; color: #b0b0aa">禁用</label>
  </span>
</div>
```

### 样张 · 三种状态

React：

```jsx
import { Switch } from './extracted/Switch.jsx'

export function States() {
  return (
    <>
      <Switch id="s-on" checked label="已开启" />
      <Switch id="s-off" label="已关闭" />
      <Switch id="s-dis" disabled label="禁用" />
    </>
  )
}
```

HTML（构建时由上面的 React 渲染得到，可直接粘贴）：

```html
<span style="display: inline-flex; align-items: center; gap: 12px">
  <button
    id="s-on"
    type="button"
    role="switch"
    aria-checked="true"
    class="zzm-switch"
    style="
      width: 42px;
      height: 25px;
      border-radius: 999px;
      border: 0;
      padding: 0;
      flex: none;
      position: relative;
      cursor: pointer;
      opacity: 1;
      background: #1d1d1b;
      transition: background 0.2s ease;
    "
  >
    <span
      class="zzm-switch__knob"
      style="
        position: absolute;
        top: 2.5px;
        left: 19.5px;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: #ffffff;
        transition: left 0.2s cubic-bezier(0.2, 0.7, 0.2, 1);
      "
    ></span>
  </button>
  <label for="s-on" style="font-size: 12.5px; color: #3f3f3b">已开启</label>
</span>
<span style="display: inline-flex; align-items: center; gap: 12px">
  <button
    id="s-off"
    type="button"
    role="switch"
    aria-checked="false"
    class="zzm-switch"
    style="
      width: 42px;
      height: 25px;
      border-radius: 999px;
      border: 0;
      padding: 0;
      flex: none;
      position: relative;
      cursor: pointer;
      opacity: 1;
      background: #d8d8d3;
      transition: background 0.2s ease;
    "
  >
    <span
      class="zzm-switch__knob"
      style="
        position: absolute;
        top: 2.5px;
        left: 2.5px;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: #ffffff;
        transition: left 0.2s cubic-bezier(0.2, 0.7, 0.2, 1);
      "
    ></span>
  </button>
  <label for="s-off" style="font-size: 12.5px; color: #3f3f3b">已关闭</label>
</span>
<span style="display: inline-flex; align-items: center; gap: 12px">
  <button
    id="s-dis"
    type="button"
    role="switch"
    aria-checked="false"
    disabled=""
    class="zzm-switch"
    style="
      width: 42px;
      height: 25px;
      border-radius: 999px;
      border: 0;
      padding: 0;
      flex: none;
      position: relative;
      cursor: not-allowed;
      opacity: 0.6;
      background: #ededea;
      transition: background 0.2s ease;
    "
  >
    <span
      class="zzm-switch__knob"
      style="
        position: absolute;
        top: 2.5px;
        left: 2.5px;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: #ffffff;
        transition: left 0.2s cubic-bezier(0.2, 0.7, 0.2, 1);
      "
    ></span>
  </button>
  <label for="s-dis" style="font-size: 12.5px; color: #b0b0aa">禁用</label>
</span>
```

## 属性

| 属性 | 取值 | 默认 | 说明 |
|---|---|---|---|
| `checked` | boolean | false | 是否开启（受控） |
| `onChange` | (next: boolean) => void | — | 切换回调 |
| `disabled` | boolean | false | 禁用：更浅的轨道 + 60% 透明 |
| `label` | string | — | 右侧文字；缺省时读屏名为「开关」 |
| `id` | string | — | 关联 label 的 for |

## 说明

尺寸 42×25、滑块偏移 2.5 是 edu 样张的原值，为了 1:1 保留，不在 4pt 梯上。它在 edu 里只以后台样张存在，这里提炼成组件，本站是它的源头。

## 本页用到的 CSS

按样张里出现的类名，从源文件原样裁出（完整版见 zzm.css）：

```css
/* extracted.css */

.zzm-v4 .zzm-switch__knob { box-shadow: 0 1px 3px rgba(0, 0, 0, .25); }

.zzm-v4 .zzm-switch:disabled .zzm-switch__knob { box-shadow: 0 1px 3px rgba(0, 0, 0, .14); }

.zzm-v4 .zzm-switch:focus-visible,
.zzm-v4 .zzm-check:focus-visible,
.zzm-v4 .zzm-tab:focus-visible,
.zzm-v4 .zzm-page:focus-visible,
.zzm-v4 .zzm-sort:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(166, 64, 47, .2);
}
```

---

上一页：[输入槽](https://ui.zhaozimin.com/components/input.md) · 下一页：[复选框](https://ui.zhaozimin.com/components/checkbox.md) · 全站目录：https://ui.zhaozimin.com/llms.txt
