跳到正文
册页

表单

Switch - 开关

一个立刻生效的设置。

用法

什么时候用它

放在带保存按钮的表单里,开关就是在骗人——那种情况用 Checkbox。
TSX
import { Switch } from '@misoto22/folio'

说明

一个立即生效的设置。

和 Checkbox 不同,而且这个区别不是外观上的:开关在拨动那一刻生效,勾选框在提交时生效。一个放在带“保存”按钮的表单里的开关,是在对“改动什么时候发生”撒谎。

打开时轨道填满墨色,滑块是带细线的纸白——而不是一个浮在投影上的白色滑块,这套系统没有投影。关闭态是一条填了线色的轨道,所以这个控件在白色页面上仍然读得出是个控件。

结构

Switch anatomy
部件说明
轨道必填一个 36×20、胶囊圆角的 <button role="switch">,关时填 --stone,开时填 --accent。是填充而不是描边,所以在一张白底页面上,它照样读得出是个控件。
滑块必填一个 14px 的 --paper 圆片,带一条细线,而不是一块投影上的白饼——这套系统没有阴影。按住时它拉长到 20px,落位时再圆回来;motion-reduce 下这一整段动作都不做。
标签这里同样不渲染。不过 Radix 的根节点确实就是一个 <button>,而 <label for> 是绑得住它的——所以和 Select 或 RadioGroup 不一样,Switch 上方那个 Field 标签,真的能点进来。

实践建议

推荐

  • 失败要在控件这里处理。这一拨已经宣称改动发生了,所以 onCheckedChange 里请求失败时,必须把滑块拨回去并说明原因,否则页面上摆着的是一个服务端根本没有的设置。
  • 按状态命名,不要按动作命名:无障碍名称是和「开」或「关」一起被念出来的,所以「邮件通知,开」是一句话,而「打开邮件通知,开」是两句互相矛盾的话。
  • 哪怕写入并不即时,眼睛看到的这一拨也要是即时的——一个乐观更新的滑块配一条安静的撤销,胜过在一个全部主张就是「它已经生效了」的控件上转圈。

避免

  • 开关只有两个状态,没有第三个。「继承工作区设置」当不了开关,因为它唯一画得出来的样子就是未勾选,而那播报出来是「关」——那种东西是 RadioGroup 或 Select。
  • 这里没有 readOnly:disabled 是唯一的锁,而它会把控件挪出 Tab 顺序,所以一路 Tab 过表单的键盘用户,会从这个设置旁边路过,始终没听到它的值。
  • 不要从调用处加 transition-all。它会把 transition-[transform,width] 整个换掉,于是轨道的颜色被套上滑块那条更长的时长,一次拨动就变成了一次淡入淡出。

示例

default

两个一拨就生效、而不是等保存才生效的设置——这就是它和 Checkbox 之间那整条界线,而且不是装饰性的:一个开关放在带「保存」按钮的表单里,是在谎报这个改动是什么时候发生的。两个都是按状态命名的,不是按动作,因为名字会和「开」或「关」一起读出来:「邮件通知,开」是一句话,而「打开邮件通知,开」是两句互相矛盾的话。Radix 的根节点是一个 button,label 确实绑得上,所以不像 Select 或 RadioGroup,Switch 上方 Field 的那些字是真能点穿的。

a failed write

一拨过去就已经宣称改动发生了,所以一次失败的写入必须在控件这里收场:把滑块推回去,并说明为什么。把这个打开——它背后的请求过一会儿会失败,开关退回关,理由出现在它下面。一个停在「开」上、而写入从未落地的开关,是一个页面在展示一个服务端没有的设置。哪怕写入不是即时的,也要让那一拨保持即时:一个乐观的滑块配一次安静的撤销,好过在一个整个主张就是「它已经生效了」的控件上转圈。

a third state

一个开关只有两个状态,没有第三个。「继承工作区的设置」在一条轨道上画不出来:它唯一可能的样子是未选中,而未选中会把自己播报成「关」——那是对另一个问题的另一个答案。左边是一个确实只有开和关的设置;右边是同一个设置在多出第三个取值之后的样子,那是一个 RadioGroup,不是一个被谁玩出花来的开关。

Two states

Three

键盘操作

Switch keyboard interactions
按键作用
SpaceEnter切换开关,改动立即生效。