组件 · COMPONENT
开关 Switch
墨色开关:关闭是浅灰轨道,开启是墨色轨道,白色滑块沿标准缓动滑过去。
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>
)
}<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>/* 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);
}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="禁用" />
</>
)
}<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>/* 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);
}属性
| 属性 | 取值 | 默认 | 说明 |
|---|---|---|---|
| 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 里只以后台样张存在,这里提炼成组件,本站是它的源头。