{ "schemaVersion": 1, "sourceKind": "authored-spec", "provenance": "authored-in-repo", "specFile": "spec/移动端规格.md", "specSection": "29 · 开关 Switch", "confidence": "high", "slug": "mobile-switch", "name": "开关 Switch", "semanticTypeCandidates": [ "switch", "toggle", "boolean-input" ], "variantDimensions": [ { "name": "size", "values": [ "default", "small" ] }, { "name": "labelPlacement", "values": [ "right", "left" ] } ], "representativeVariants": [ { "size": "default", "labelPlacement": "right", "label": "默认(轨道 48×28,文字在右)" }, { "size": "small", "labelPlacement": "right", "label": "紧凑(轨道 40×22,紧凑表单用)" }, { "size": "default", "labelPlacement": "left", "label": "文字在左(整行右对齐的值区风格)" } ], "anatomy": { "switch": "根元素,一行里放进「轨道 + 文字标签」,整行可点", "track": "轨道,承载背景色与滑块位移的边界", "knob": "滑块,关态靠左、开态靠右(位移是开/关的主要视觉信号)", "text": "可选文字标签,说明这项开关控制什么", "control": "可点的整行控件(原生 button + role=\"switch\"),承接键盘与读屏" }, "structurePatterns": { "size": "default(轨道 48×28)/ small(轨道 40×22,用于紧凑表单)", "labelPlacement": "right(文字在开关右侧,默认)/ left(文字在左侧,值区右对齐时用)" }, "usageHints": [ "即时启停一项配置或业务状态(启用通知、公开数据、自动同步)", "移动端开关本体视觉只有 48×28,但整行(开关 + 文字)都是可点热区,行高不小于 44px", "关态与开态不能只靠颜色区分,必须同时看到滑块位移", "点击切换只需要一次触摸,不要求拖动滑块(拖动是桌面习惯,触屏误触率高)", "切换后立即触发 change 事件,不做二次确认(需要确认的场景由宿主先弹对话框)" ], "doNotInvent": [ "二次确认弹窗与「切换失败回滚」的业务流程", "三态开关(关 / 开 / 待定)的视觉表达", "与表单一起提交时的隐藏字段(由宿主添加)" ], "unknowns": [ "开关本体是否允许小于 48×28(紧凑表单的下限)", "文案与开关的间距是否跟随字号", "加载态(切换请求进行中)如何表达" ], "interaction": [ "整行(开关 + 文字)都是热区,行高不小于 44px;开关本体不可单独缩到 44px 以下", "点击切换只需要一次触摸,不要求拖动滑块(拖动是桌面习惯,触屏误触率高)", "切换动效是滑块位移 150ms 过渡;减少动态偏好下瞬时切换", "关态与开态不能只靠颜色区分:滑块位置 + aria-checked 双通道", "切换后立即触发 change 事件,不做二次确认(需要确认的场景由宿主先弹对话框)" ], "accessibility": [ "用 role=\"switch\" + aria-checked=\"true|false\",而不是 role=\"checkbox\"(读屏会播报「开关」)", "承载控件是原生 button,键盘可聚焦、空格/回车可切换,并有可见焦点环", "文字标签在控件内部,读屏播报的名称就是标签本身;无标签时用 label 属性补 aria-label", "禁用态用原生 disabled,读屏会播报不可用" ], "api": { "source": "implementation", "note": "props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs", "requiredNote": "「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。", "props": [ { "name": "checked", "type": "boolean", "default": "false", "desc": "受控开关值;与 aria-checked 同步写(规格 §29.5)", "required": false }, { "name": "size", "type": "'default' | 'small'", "default": "'default'", "desc": "变体 size:default 轨道 48×28,small 轨道 40×22(规格 §29.3)", "required": false }, { "name": "labelPlacement", "type": "'right' | 'left'", "default": "'right'", "desc": "变体 labelPlacement:文字在开关右侧还是左侧(规格 §29.3)", "required": false }, { "name": "disabled", "type": "boolean", "default": "false", "desc": "状态 disabled:置灰且不可聚焦(规格 §29.4)", "required": false }, { "name": "label", "type": "string", "default": "''", "desc": "文字标签;同时作为无障碍名称落到 aria-label(规格 §29.6)", "required": false } ], "events": [ { "name": "change", "params": "(checked)", "desc": "切换时触发,回传切换后的目标值(规格 §29.5)" } ], "slots": [ { "name": "default", "desc": "文字标签内容,优先于 label 属性(规格 §29.2 text)" } ] }, "variantClasses": { "size": { "default": [], "small": [ ".kole-m-switch--small" ] }, "labelPlacement": { "right": [], "left": [ ".kole-m-switch--left" ] } }, "demos": [ { "id": "basic", "group": "01 组件类型", "title": "基础用法", "desc": "整行可点:开关本体只有 48×28,单点本体手指点不中。", "variant": "size=default" }, { "id": "states", "group": "02 组件状态", "title": "关态与开态", "desc": "两者不能只靠颜色区分:滑块位置 + aria-checked 双通道。", "variant": "状态 off|on" }, { "id": "size", "group": "01 组件类型", "title": "尺寸两档", "desc": "default 轨道 48×28;small 轨道 40×22 用于紧凑表单。", "variant": "size=default|small" }, { "id": "left", "group": "01 组件类型", "title": "文字在左", "desc": "labelPlacement=left:整行右对齐的值区风格,文字在开关左侧。", "variant": "labelPlacement=left" }, { "id": "disabled", "group": "02 组件状态", "title": "禁用", "desc": "置灰且不响应;读屏会播报不可用。", "variant": "disabled=true" } ], "related": [ { "slug": "cell", "why": "开关常作为单元格的右侧内容(Cell 负责行结构与分隔线,开关只管切换)" }, { "slug": "mobile-button", "why": "需要用户确认后一次性提交的用按钮;状态需要即时生效的用开关" }, { "slug": "mobile-dialog", "why": "切换前需要用户确认(如扣费项)时先弹对话框;开关本身不做二次确认" } ] }