{ "schemaVersion": 1, "sourceKind": "authored-spec", "provenance": "authored-in-repo", "specFile": "spec/移动端规格.md", "specSection": "40 · 遮罩层 Overlay", "confidence": "high", "slug": "mobile-overlay", "name": "遮罩层 Overlay", "semanticTypeCandidates": [ "overlay", "scrim", "mask" ], "variantDimensions": [ { "name": "tone", "values": [ "default", "strong", "blur" ] }, { "name": "contained", "values": [ "false", "true" ] } ], "representativeVariants": [ { "tone": "default", "contained": "false", "label": "基础全屏遮罩" }, { "tone": "strong", "contained": "false", "label": "浓遮罩(需要专注的确认)" }, { "tone": "blur", "contained": "true", "label": "模糊 + 局部(卡内加载态)" } ], "anatomy": { "overlay": "根元素,定位容器与开合开关;role=\"presentation\",自身不承担语义", "scrim": "遮罩面(原生 button),aria-hidden=\"true\" 且 tabindex=\"-1\" —— 可点但不进键盘序列", "content": "内容容器(默认插槽落点),语义由宿主决定(对话框给 role=\"dialog\"、面板给 role=\"region\")" }, "structurePatterns": { "tone": "default 普通 / strong 浓遮罩 / blur 叠加背景模糊", "contained": "false 固定全屏 / true 绝对定位填充最近的定位祖先", "状态类": "is-open 展开(视觉与手势同时让开)" }, "usageHints": [ "浮层的基座:在内容之上盖一层半透明遮罩,让下层内容退到背后(对话框、抽屉、图片预览、卡片加载态)", "与弹出层的分工:弹出层是能独立使用的完整浮层,遮罩层只提供「变暗 + 拦手势 + 承载任意内容」", "触屏上遮罩必须真的吃掉触摸事件,否则惯性滚动会从遮罩底下穿过去把下层页面滚走", "遮罩关闭的键盘路径不落在遮罩上:遮罩显式 tabindex=\"-1\",键盘用户靠内容里的关闭按钮", "lockScroll=true 时组件只在根上写 data-lock-scroll 标记,实际的 overflow: hidden 由宿主执行" ], "doNotInvent": [ "焦点陷阱(focus trap)与初始焦点策略", "多层遮罩的层叠顺序管理", "手势下滑关闭与拖拽阻尼", "滚动锁定的实现细节(组件只写标记,改 DOM 由宿主做)" ], "unknowns": [ "blur 档在低端机上的性能开销是否可接受(未做降级探测)", "contained=true 时是否应自动为宿主补 position: relative", "是否要支持「点遮罩不关但双击关」这类折中策略" ], "interaction": [ "一次轻点遮罩即关闭;closeOnMask=false 时不关闭", "遮罩显式 tabindex=\"-1\",不进键盘序列;键盘用户靠内容里的关闭按钮与 Esc 关闭", "开合动效 240ms(--kole-m-duration-slide),prefers-reduced-motion 下瞬时切换", "contained=true 时要求宿主祖先链上存在定位元素,否则会向上找到视口" ], "accessibility": [ "遮罩面 aria-hidden=\"true\"(纯装饰)且 tabindex=\"-1\"(不进键盘序列)", "根 role=\"presentation\":容器本身不产生语义,避免读屏把它当成一个空的分组", "内容语义完全由宿主提供:焦点陷阱、aria-modal、初始焦点与关闭后焦点归还都属于宿主职责", "遮罩关闭不依赖颜色:开合只改透明度且有 240ms 过渡" ], "api": { "source": "implementation", "note": "props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs", "requiredNote": "「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。", "props": [ { "name": "open", "type": "boolean", "default": "false", "desc": "状态 open:展开且拦下所有手势(规格 §40.4)", "required": false }, { "name": "contained", "type": "boolean", "default": "false", "desc": "变体 contained:绝对定位填充最近的定位祖先,做卡内局部遮罩(规格 §40.3)", "required": false }, { "name": "tone", "type": "'default' | 'strong' | 'blur'", "default": "'default'", "desc": "变体 tone:遮罩浓度与质感(规格 §40.3)", "required": false }, { "name": "lockScroll", "type": "boolean", "default": "true", "desc": "是否锁住下层滚动;组件只写 data-lock-scroll 标记(规格 §40.5)", "required": false }, { "name": "closeOnMask", "type": "boolean", "default": "true", "desc": "遮罩点击是否关闭;表单类内容通常设为 false(规格 §40.5)", "required": false } ], "events": [ { "name": "close", "params": "—", "desc": "点击遮罩(closeOnMask=true 时)触发;是否收起由宿主决定(规格 §40.5)" } ], "slots": [ { "name": "default", "desc": "遮罩之上的内容;语义(role / aria-modal)由宿主提供(规格 §40.2 content)" } ] }, "variantClasses": { "tone": { "default": [ ".kole-m-overlay--default" ], "strong": [ ".kole-m-overlay--strong" ], "blur": [ ".kole-m-overlay--blur" ] }, "contained": { "false": [], "true": [ ".kole-m-overlay--contained" ] } }, "demos": [ { "id": "basic", "group": "01 组件类型", "title": "基础遮罩", "desc": "tone=default:遮罩本身不带语义,role 与标题由内容自己给;一次轻点遮罩即关闭。", "variant": "tone=default" }, { "id": "strong", "group": "01 组件类型", "title": "浓遮罩", "desc": "tone=strong:遮挡更强,用于需要专注的确认;默认展开以便直接对照两层浓度。", "variant": "tone=strong" }, { "id": "blur", "group": "01 组件类型", "title": "模糊遮罩", "desc": "tone=blur:背景内容做 backdrop 模糊,用于图片预览;模糊只影响遮罩下方,内容卡片保持清晰。", "variant": "tone=blur" }, { "id": "contained", "group": "02 组件状态", "title": "局部遮罩", "desc": "contained=true:绝对定位填充最近的定位祖先,只遮住一张卡,页面其它区域仍可操作。", "variant": "contained=true" }, { "id": "keep-open", "group": "02 组件状态", "title": "遮罩不关闭", "desc": "closeOnMask=false:误触遮罩不收起;填到一半的表单不该因为一次误触就丢弃已填数据。", "variant": "closeOnMask=false" } ], "related": [ { "slug": "mobile-popup", "why": "需要带方向位移与标题栏的完整浮层时用弹出层,遮罩层只做底座不负责内容结构" }, { "slug": "mobile-dialog", "why": "需要确认或输入的中断式浮层用对话框,它自己已含遮罩与按钮组" }, { "slug": "mobile-loading", "why": "局部加载态需要转圈图标时用加载组件,遮罩层只提供变暗与拦手势" } ] }