Regression / regression (push) Canceled after 0s
## 品牌标识(本次会话) 起因:品牌此前没有任何图形标识 —— 唯一 favicon 是内联 data-URI 里的字母「A」, 那是 v2.0.0「Aurora Admin → Kole UI」改名漏掉的一处(PC 顶栏也是「A」, 移动端站已是「K」;移动端文档站则完全没有 favicon)。 - 几何:24 网格三个互不接触的笔画(竖 + 两斜),圆头描边; 描边 2.25 → 16px 标签页尺寸下正好 1.5px = 规范原文「描边1.5px」 - 取色分两套(刻意):favicon 硬编码品牌蓝/白(渲染在浏览器标签栏,不继承 kole-dark); 顶栏标记走 currentColor(实测暗色下自动转 rgb(20,22,28)) - 新增 theme-color 双条(light #FFFFFF / dark #1C1F26,取 --kole-color-card-bg) - 修 site/app.js hero 标语 KOLE ADMIN → KOLE UI(改名变形残留) - 移动端 7 个模板补 favicon(此前计数 0) 验收:门禁 9 条全 OK(site-routing/site-routes/mobile-docs/mobile-site/isolation/ theme/nav/i18n/icons);PC 回归 1464/1464 · 移动端 807/807,各连跑 8 次一致; 两端 favicon 405 字节逐字节一致;PC 站控制台错误 1→0。 ## 并行会话成果(本次一并入库) - 图标系统:2576 图标(TDesign/Element Plus,MIT)+ 11 端注入 + 5 个构建门禁工具 + IconPreview 预览页 + ICON-SPEC.md 冻结规格 - 移动端平台:47 组件 × 6 端 + 文档站 53 页 + 隔离门禁 - PC 组件:103 个大后台组件 / 组件11 批次 - uni-app:PC 端试点 + 移动端端实现 + 真实编译验证 ## 工程 - .gitignore 补 .scratch/ 与 .zcode-preexisting-*.txt(会话中间产物,实测 9.1MB,不入库) - CHANGELOG 补品牌标识条目 - ROADMAP 登 S8-P4(品牌标识任务包 + og:image/apple-touch-icon 未做部分)
2213 lines
107 KiB
Markdown
2213 lines
107 KiB
Markdown
# Kole UI · 移动端规格(Mobile Spec)
|
||
|
||
> **文档性质**:移动端组件的**规格原文**,与本仓库 `组件1~10.txt`(PC 端)同级 —— 契约 JSON 的
|
||
> usageHints / anatomy / doNotInvent 必须逐字来自本文件,不得发明。
|
||
> **来源声明**:PC 端规格来自外部交付的 `组件1~10.txt`;移动端**没有**外部交付的规格原文,
|
||
> 本文件由本仓库撰写并作为唯一授权来源(`sourceKind: "authored-spec"`)。
|
||
> 因此移动端契约的 `provenance` 字段必须写明 `authored-in-repo`,不得声称来自 `组件*.txt`。
|
||
> **分支**:`feat/s6-p21-component-families` 之后的 S7 阶段。
|
||
|
||
---
|
||
|
||
## 〇 · 隔离总则(先读)
|
||
|
||
移动端与 PC 端**原料隔离、令牌同源**:
|
||
|
||
| 维度 | PC 端 | 移动端 | 是否共享 |
|
||
|---|---|---|---|
|
||
| 实现目录 | `frameworks/` | `frameworks-mobile/` | 否 |
|
||
| 契约 | `.design_library/kole-ui/components/` | `.design_library/kole-ui-mobile/components/` | 否 |
|
||
| 类名前缀 | `kole-` | `kole-m-` | 否 |
|
||
| 令牌前缀 | `--kole-` | `--kole-m-` | 颜色/字体/圆角/阴影**共享**(@import PC 令牌文件) |
|
||
| 导出名 | `KoleButton` | `KoleMNavBar` | 否 |
|
||
| 测试页 | `tests/<slug>.html` | `tests/mobile/<slug>.html` | 否 |
|
||
| 回归报告 | `tests/report.json` | `tests/mobile-report.json` | 否 |
|
||
| 文档站 | `site/components/`、SPA 路由 | `site/m/`(静态站,不进 SPA 路由表) | 否 |
|
||
|
||
**硬规则**:
|
||
1. 移动端实现**不得**出现在 `frameworks/`;PC 实现**不得**出现在 `frameworks-mobile/`。
|
||
2. 移动端样式只能引用 `--kole-*` 或 `--kole-m-*` 令牌,**不得**出现硬编码十六进制颜色。
|
||
3. 移动端类名只能是 `kole-m-<name>` 或状态类 `is-<state>`。
|
||
4. PC 侧任何文件不得引用 `frameworks-mobile/` 或 `site/m/`;移动端不得引用 `frameworks/`。
|
||
|
||
---
|
||
|
||
## 1 · 顶部导航栏 NavBar
|
||
|
||
### 1.1 用途
|
||
页面顶部的标题栏,提供返回入口、页面标题与右侧操作区。
|
||
|
||
### 1.2 结构(anatomy)
|
||
- `bar`:导航栏容器,固定于页面顶部,高度 44px + 顶部安全区
|
||
- `back`:左侧返回入口,可为返回箭头或文字
|
||
- `title`:中间标题,单行省略
|
||
- `actions`:右侧操作区,可放 1~2 个图标按钮或一个文字按钮
|
||
- `divider`:可选底部分隔线
|
||
|
||
### 1.3 变体维度
|
||
- `titleAlign`:`center`(标题居中,左侧仅图标) / `left`(标题左对齐,紧跟返回)
|
||
- `elevated`:`false`(无阴影) / `true`(滚动后投影)
|
||
|
||
### 1.4 状态
|
||
- default:常态
|
||
- scrolled:页面滚动后出现分隔线或投影
|
||
- disabled:右侧操作不可用(按钮置灰)
|
||
|
||
### 1.5 交互与触控
|
||
- 返回入口与右侧操作的**点击热区不小于 44×44**,视觉图标可小于该尺寸
|
||
- 标题超长时单行省略,不换行、不撑开栏高
|
||
- 顶部内边距包含安全区,横屏与刘海屏不遮挡内容
|
||
|
||
### 1.6 无障碍
|
||
- 容器语义 `role="banner"`(页内使用时也可用 `role="navigation"` 并配 `aria-label`)
|
||
- 标题节点具备 `aria-label` 或可见文本
|
||
- 图标按钮具备 `aria-label`
|
||
|
||
### 1.7 doNotInvent
|
||
- 多行标题的折叠规则
|
||
- 返回行为的栈深度策略(是否回退到首页)
|
||
|
||
### 1.8 unknowns
|
||
- 阴影出现的确切滚动阈值
|
||
- 右侧操作超过 2 个时的收敛方式
|
||
|
||
---
|
||
|
||
## 2 · 底部标签栏 TabBar
|
||
|
||
### 2.1 用途
|
||
底部主导航,2~5 个标签页之间切换,是移动端一级导航。
|
||
|
||
### 2.2 结构(anatomy)
|
||
- `bar`:标签栏容器,固定于页面底部,高度 50px + 底部安全区
|
||
- `item`:单个标签,含图标与文字
|
||
- `icon`:图标,可为内联 SVG
|
||
- `label`:标签文字,11px
|
||
- `badge`:角标,可为数字或红点
|
||
|
||
### 2.3 变体维度
|
||
- `count`:`2` / `3` / `4` / `5`(标签数量,超过 5 项应改用其它导航形态)
|
||
- `badge`:`none` / `dot` / `number`
|
||
|
||
### 2.4 状态
|
||
- active:当前选中项,颜色为品牌色
|
||
- inactive:未选中项,颜色为次要文字色
|
||
- disabled:该项不可点击(置灰且不响应)
|
||
|
||
### 2.5 交互与触控
|
||
- 每项点击热区等分整栏宽度,高度不小于 44px
|
||
- 选中项切换后 `aria-selected` 同步变化
|
||
- 底部内边距包含安全区
|
||
|
||
### 2.6 无障碍
|
||
- 容器 `role="tablist"`,单项 `role="tab"`
|
||
- 选中项 `aria-selected="true"`,未选中 `false`
|
||
- 每项具备 `aria-label`(图标 + 文字时可用文字代替)
|
||
|
||
### 2.7 doNotInvent
|
||
- 标签项超过 5 个时的滚动或折叠规则
|
||
- 图标资源的成套规则(规格只约定尺寸与语义)
|
||
|
||
### 2.8 unknowns
|
||
- 角标超过两位数的收敛(如 99+)
|
||
- 选中态是否带图标填充切换
|
||
|
||
---
|
||
|
||
## 3 · 动作面板 ActionSheet
|
||
|
||
### 3.1 用途
|
||
从底部弹出的操作列表,用于在少量互斥操作中做一次选择。
|
||
|
||
### 3.2 结构(anatomy)
|
||
- `mask`:遮罩,点击关闭
|
||
- `panel`:面板容器,自底部滑出
|
||
- `title`:可选标题说明
|
||
- `action`:操作项,高度 56px,可标记危险操作
|
||
- `cancel`:底部取消按钮,与操作项之间有间隔
|
||
|
||
### 3.3 变体维度
|
||
- `tone`:`default` / `danger`(危险项用错误色)
|
||
- `cancel`:`inline`(取消作为普通项) / `separate`(取消独立成块)
|
||
|
||
### 3.4 状态
|
||
- closed:面板收起(默认)
|
||
- open:面板展开,遮罩可见
|
||
- disabled:单个操作项不可用(置灰、点击无效)
|
||
|
||
### 3.5 交互与触控
|
||
- 点击遮罩关闭;点击操作项或取消后关闭
|
||
- 面板滑出动画 240ms,缓动曲线 `cubic-bezier(.32,.72,0,1)`
|
||
- 操作项点击热区高度不小于 56px
|
||
|
||
### 3.6 无障碍
|
||
- 面板 `role="dialog"` + `aria-modal="true"`
|
||
- 遮罩为纯装饰,不参与焦点
|
||
- 操作项为原生 `button`,危险项带 `aria-label` 说明
|
||
|
||
### 3.7 doNotInvent
|
||
- 多级面板的堆叠规则
|
||
- 手势下滑关闭的触发阈值
|
||
|
||
### 3.8 unknowns
|
||
- 操作项超过多少条时需要内部滚动
|
||
- 危险项是否需要二次确认
|
||
|
||
---
|
||
|
||
## 4 · 下拉刷新 PullRefresh
|
||
|
||
### 4.1 用途
|
||
列表顶部下拉手势触发刷新,移动端最常见的列表刷新入口。
|
||
|
||
### 4.2 结构(anatomy)
|
||
- `viewport`:包裹滚动内容的容器,负责手势
|
||
- `indicator`:下拉指示区,含箭头或旋转图标与状态文字
|
||
- `content`:业务内容
|
||
|
||
### 4.3 变体维度
|
||
- `state`:`pull`(下拉中) / `ready`(已达阈值) / `refreshing`(刷新中) / `done`(完成提示)
|
||
- `threshold`:触发阈值,默认 60px
|
||
|
||
### 4.4 状态
|
||
- pull:下拉未达阈值,指示器随位移旋转
|
||
- ready:达到阈值,提示「松开立即刷新」
|
||
- refreshing:刷新中,指示器旋转,下拉不回弹
|
||
- disabled:手势失效(如刷新中再次下拉)
|
||
|
||
### 4.5 交互与触控
|
||
- 手势使用 Pointer Events,位移以纵向为主;横向位移更大时让位给页面横滑
|
||
- 达到阈值后松手进入 refreshing;未达阈值松手回弹
|
||
- 刷新期间再次下拉不重复触发
|
||
|
||
### 4.6 无障碍
|
||
- 指示区 `role="status"` + `aria-live="polite"`,状态文字变化被读屏播报
|
||
- 需保留一个非手势的等价入口(如列表底部的刷新按钮)
|
||
|
||
### 4.7 doNotInvent
|
||
- 惯性与阻尼曲线
|
||
- 与页面整体下拉(浏览器级)的竞争规则
|
||
|
||
### 4.8 unknowns
|
||
- 刷新超时的提示形式
|
||
- 完成提示的停留时长
|
||
|
||
---
|
||
|
||
## 5 · 滑动单元格 SwipeCell
|
||
|
||
### 5.1 用途
|
||
列表行左滑露出操作按钮,用于删除、标记等单行操作。
|
||
|
||
### 5.2 结构(anatomy)
|
||
- `cell`:可滑动的行容器
|
||
- `content`:行内容(标题、描述)
|
||
- `actions`:右侧操作区,随滑动露出
|
||
- `action`:单个操作按钮,可标记危险
|
||
|
||
### 5.3 变体维度
|
||
- `direction`:`left`(左滑露出右侧操作,默认) / `right`(右滑露出左侧操作)
|
||
- `actions`:`1` / `2`(操作数量,最多 2 个)
|
||
|
||
### 5.4 状态
|
||
- closed:未滑动(默认)
|
||
- open:已滑出,操作区可见
|
||
- dragging:拖拽中
|
||
|
||
### 5.5 交互与触控
|
||
- 横向位移超过 10px 判定为滑动,纵向位移更大时取消滑动
|
||
- 松手后按位移是否超过操作区宽度的一半决定展开或回弹
|
||
- 展开状态下点击内容区先收起,不触发内容点击
|
||
|
||
### 5.6 无障碍
|
||
- 操作按钮为原生 `button`,具备 `aria-label`
|
||
- 滑动不可作为唯一路径:操作按钮在展开后必须可键盘聚焦
|
||
|
||
### 5.7 doNotInvent
|
||
- 多行同时展开的互斥规则
|
||
- 滑动与纵向滚动的竞争阈值细节
|
||
|
||
### 5.8 unknowns
|
||
- 操作区宽度是否有标准档位
|
||
- 拖拽中的阴影表现
|
||
|
||
---
|
||
|
||
## 6 · 按钮 Button
|
||
|
||
### 6.1 用途
|
||
触发一个即时动作。移动端按钮要比桌面端更"敢按":点击热区不小于 44px,主次层级靠颜色与边框区分。
|
||
|
||
### 6.2 结构(anatomy)
|
||
- `button`:根元素,用原生 `button`,圆角取令牌
|
||
- `label`:按钮文字,单行不换行
|
||
- `icon`:可选图标或加载指示器,与文字间距 4px
|
||
- `block`:可选块级形态,撑满容器宽度
|
||
|
||
### 6.3 变体维度
|
||
- `type`:`primary` / `default` / `text` / `danger`
|
||
- `size`:`large`(44px 高,移动端默认)/ `default`(36px)/ `small`(28px)
|
||
- `block`:`false` / `true`
|
||
|
||
### 6.4 状态
|
||
- default:常态
|
||
- active:按下时背景加深(移动端没有 hover,反馈靠 `:active`)
|
||
- disabled:置灰且不响应点击
|
||
- loading:显示加载指示器并阻止重复触发
|
||
|
||
### 6.5 交互与触控
|
||
- 高度不小于 44px 的档位用于主操作区;小尺寸档只用于行列内联操作
|
||
- 按下反馈用 `:active` 背景色变化,不做位移缩放(避免长按抖动)
|
||
- loading 期间重复点击不触发第二次事件
|
||
|
||
### 6.6 无障碍
|
||
- 使用原生 `button`,天然可聚焦、可键盘触发
|
||
- 仅有图标时必须给 `aria-label`
|
||
- loading 时置 `aria-busy="true"` 且 `disabled`(避免重复提交)
|
||
|
||
### 6.7 doNotInvent
|
||
- 按钮内多行文字的排版规则
|
||
- 长按(long-press)的附加行为
|
||
|
||
### 6.8 unknowns
|
||
- 图标与文字同时存在时的最小宽度
|
||
- 危险按钮是否需要二次确认
|
||
|
||
---
|
||
|
||
## 7 · 单元格 Cell
|
||
|
||
### 7.1 用途
|
||
列表的基本单元:一行里承载"标题 + 说明 + 值 + 箭头",可整行点击进入下级。
|
||
|
||
### 7.2 结构(anatomy)
|
||
- `cell`:根元素,一行两类内容(左侧主区 / 右侧值区)
|
||
- `title`:主标题,单行省略
|
||
- `desc`:可选副标题,单行省略
|
||
- `value`:右侧值或状态文字
|
||
- `arrow`:可选右箭头,表示可进入
|
||
- `icon`:可选左侧图标
|
||
|
||
### 7.3 变体维度
|
||
- `arrow`:`false` / `true`
|
||
- `link`:`false`(纯展示)/ `true`(整行可点)
|
||
|
||
### 7.4 状态
|
||
- default:常态
|
||
- active:按下时整行背景变化
|
||
- disabled:置灰且不响应
|
||
|
||
### 7.5 交互与触控
|
||
- 可点单元格整行都是热区,高度不小于 56px
|
||
- 按下反馈是整行背景变化,不是只有文字变色
|
||
- 单元格之间默认有 1px 分隔线,可用 `borderless` 关掉
|
||
|
||
### 7.6 无障碍
|
||
- 可点单元格用原生 `button`;纯展示单元格用 `div`
|
||
- 值区只作展示时不要放进可聚焦元素
|
||
|
||
### 7.7 doNotInvent
|
||
- 多行标题的折叠规则
|
||
- 单元格内多列布局的栅格规则
|
||
|
||
### 7.8 unknowns
|
||
- 副标题最多显示几行
|
||
- 右侧值超长时的截断策略
|
||
|
||
---
|
||
|
||
## 8 · 分割线 Divider
|
||
|
||
### 8.1 用途
|
||
在内容之间画一条细分隔线;带文字时用于分区标题。
|
||
|
||
### 8.2 结构(anatomy)
|
||
- `divider`:根元素,水平时为 1px 高
|
||
- `text`:可选文字,居中或左/右对齐
|
||
- `line`:文字两侧的线
|
||
|
||
### 8.3 变体维度
|
||
- `orientation`:`horizontal` / `vertical`
|
||
- `align`:`left` / `center` / `right`(带文字时生效)
|
||
- `dashed`:`false` / `true`
|
||
|
||
### 8.4 状态
|
||
- 静态组件,无交互状态(这是设计如此,不是缺失)
|
||
|
||
### 8.5 交互与触控
|
||
- 纯静态,不参与点击与手势
|
||
- 垂直分割线高度跟随父容器(用 `align-self: stretch`)
|
||
|
||
### 8.6 无障碍
|
||
- 纯装饰:`role="separator"` + `aria-hidden="true"`(对读屏无信息量)
|
||
- 带文字的分割线不隐藏,文字本身即语义
|
||
|
||
### 8.7 doNotInvent
|
||
- 主题化分割线(渐变、图片)的表现
|
||
|
||
### 8.8 unknowns
|
||
- 垂直分割线的推荐间距
|
||
|
||
---
|
||
|
||
## 9 · 徽标 Badge
|
||
|
||
### 9.1 用途
|
||
在图标或头像右上角标出数量或状态(红点 / 数字 / 短文字)。
|
||
|
||
### 9.2 结构(anatomy)
|
||
- `badge`:根元素,可包裹子元素(角标形态)也可独立使用
|
||
- `count`:数字或短文字
|
||
- `dot`:红点形态(无内容)
|
||
- `wrap`:被包裹的内容(图标 / 头像 / 按钮)
|
||
|
||
### 9.3 变体维度
|
||
- `shape`:`dot` / `number` / `text`
|
||
- `standalone`:`false`(包裹在子元素上)/ `true`(独立使用)
|
||
|
||
### 9.4 状态
|
||
- default:常态
|
||
- overflow:数值超过 `max` 时显示 `max+`
|
||
- hidden:数值为 0 且未开启 `showZero` 时隐藏
|
||
|
||
### 9.5 交互与触控
|
||
- 角标本身不可点(可点的是被包裹的元素)
|
||
- 角标不改变被包裹元素的布局尺寸(绝对定位)
|
||
|
||
### 9.6 无障碍
|
||
- 数字角标配 `aria-label`(如「12 条未读」),否则读屏只读出数字
|
||
- 纯装饰红点 `aria-hidden="true"`
|
||
|
||
### 9.7 doNotInvent
|
||
- 角标内容的动画(出现 / 消失)
|
||
|
||
### 9.8 unknowns
|
||
- `max` 的默认取值(本实现取 99)
|
||
- 独立使用时是否需要背景色
|
||
|
||
---
|
||
|
||
## 10 · 标签 Tag
|
||
|
||
### 10.1 用途
|
||
用简短的文字标注状态、分类或属性;可关闭的标签用于已选项。
|
||
|
||
### 10.2 结构(anatomy)
|
||
- `tag`:根元素,圆角胶囊
|
||
- `label`:标签文字
|
||
- `close`:可选关闭按钮
|
||
|
||
### 10.3 变体维度
|
||
- `tone`:`default` / `primary` / `success` / `warning` / `danger`
|
||
- `size`:`default` / `small`
|
||
- `closable`:`false` / `true`
|
||
|
||
### 10.4 状态
|
||
- default:常态
|
||
- disabled:置灰且不可关闭
|
||
- closed:已关闭(由宿主从列表里移除)
|
||
|
||
### 10.5 交互与触控
|
||
- 关闭按钮热区不小于 24×24(小尺寸标签内用负外边距扩展热区)
|
||
- 关闭动作只触发一次事件,是否真的移除由宿主决定
|
||
|
||
### 10.6 无障碍
|
||
- 关闭按钮为原生 `button` 并带 `aria-label`(如「移除标签:已发货」)
|
||
- 纯展示标签不加交互角色
|
||
|
||
### 10.7 doNotInvent
|
||
- 标签的动态增删动画
|
||
- 超长标签的换行规则
|
||
|
||
### 10.8 unknowns
|
||
- 同一行最多放几个标签
|
||
- 关闭后是否保留占位
|
||
|
||
---
|
||
|
||
## 11 · 弹出层 Popup
|
||
|
||
### 11.1 用途
|
||
从指定方向弹出的浮层基座,承载面板、抽屉、动作列表等内容。
|
||
|
||
### 11.2 结构(anatomy)
|
||
- `mask`:遮罩,点击关闭(可关)
|
||
- `popup`:浮层容器,按方向定位
|
||
- `header`:可选标题区
|
||
- `body`:内容区,可滚动
|
||
- `close`:可选关闭按钮
|
||
|
||
### 11.3 变体维度
|
||
- `placement`:`center` / `bottom` / `top` / `left` / `right`
|
||
- `round`:`false` / `true`(贴边方向在靠内容一侧切圆角)
|
||
|
||
### 11.4 状态
|
||
- closed:收起(默认)
|
||
- open:展开,遮罩可见
|
||
- dragging:预留(本实现未做拖拽关闭)
|
||
|
||
### 11.5 交互与触控
|
||
- 遮罩点击关闭;`closeOnMask=false` 时不关闭
|
||
- 滑入动画 240ms,缓动 `cubic-bezier(.32,.72,0,1)`
|
||
- 内容超出时 `body` 内部滚动,遮罩不滚动
|
||
|
||
### 11.6 无障碍
|
||
- 浮层 `role="dialog"` + `aria-modal="true"`
|
||
- 遮罩 `aria-hidden="true"`(纯装饰)
|
||
- 关闭按钮为原生 `button` 并带 `aria-label`
|
||
|
||
### 11.7 doNotInvent
|
||
- 多浮层堆叠的层级规则
|
||
- 手势下滑关闭的阈值
|
||
|
||
### 11.8 unknowns
|
||
- 各方向的内容最大尺寸
|
||
- 是否需要焦点陷阱(focus trap)
|
||
|
||
---
|
||
|
||
## 12 · 轻提示 Toast
|
||
|
||
### 12.1 用途
|
||
在屏幕中央或底部短暂提示一条结果信息(成功 / 失败 / 警告 / 加载中),不打断当前操作。
|
||
|
||
### 12.2 结构(anatomy)
|
||
- `toast`:浮层本体,居中于视口
|
||
- `icon`:状态图标(可选)
|
||
- `text`:提示文字
|
||
- `mask`:可选透明遮罩(`mask=true` 时阻断下方点击)
|
||
|
||
### 12.3 变体维度
|
||
- `tone`:`info` / `success` / `warning` / `danger` / `loading`
|
||
- `position`:`center` / `bottom` / `top`
|
||
- `mask`:`false` / `true`(阻断交互)
|
||
|
||
### 12.4 状态
|
||
- hidden:收起(默认,靠 `is-open` 切换)
|
||
- open:展开可见
|
||
- loading:`tone=loading` 时图标持续旋转(`prefers-reduced-motion` 下降级为静止)
|
||
|
||
### 12.5 交互与触控
|
||
- 提示本身不可点(不抢焦点、不阻断),`mask=true` 时遮罩吸收手势
|
||
- 出现 / 消失动画 240ms;自动关闭时长由宿主控制(本组件只负责显示态)
|
||
|
||
### 12.6 无障碍
|
||
- 容器 `role="status"` + `aria-live="polite"`(结果朗读一次,不反复打断)
|
||
- `tone=loading` 时补 `aria-busy="true"`
|
||
- 图标为装饰(`aria-hidden="true"`),语义全部由文字承担
|
||
|
||
### 12.7 doNotInvent
|
||
- 自动关闭的默认时长
|
||
- 多条提示的排队 / 合并策略
|
||
|
||
### 12.8 unknowns
|
||
- 单行文字的最大宽度与换行规则
|
||
- 是否需要点击穿透设置
|
||
|
||
---
|
||
|
||
## 13 · 对话框 Dialog
|
||
|
||
### 13.1 用途
|
||
需要用户确认或输入的中断式浮层:标题 + 内容 + 操作按钮组。
|
||
|
||
### 13.2 结构(anatomy)
|
||
- `mask`:遮罩,点击可关(可配置)
|
||
- `dialog`:对话框本体,居中
|
||
- `header`:标题区
|
||
- `body`:内容区
|
||
- `footer`:操作按钮组(取消 / 确认)
|
||
|
||
### 13.3 变体维度
|
||
- `variant`:`confirm`(确认框)/ `alert`(提示框,只有一个按钮)
|
||
- `tone`:`default` / `danger`(确认按钮用错误色)
|
||
- `round`:`false` / `true`
|
||
|
||
### 13.4 状态
|
||
- closed:收起(默认)
|
||
- open:展开,遮罩可见
|
||
- loading:确认按钮进入加载态并禁用(由宿主传入)
|
||
|
||
### 13.5 交互与触控
|
||
- 遮罩点击关闭;`closeOnMask=false` 时不关闭
|
||
- `Esc` 关闭(键盘可达时)
|
||
- 按钮热区不小于 44px;操作按钮等宽排列
|
||
|
||
### 13.6 无障碍
|
||
- 对话框 `role="dialog"` + `aria-modal="true"` + `aria-labelledby` 指向标题
|
||
- 打开后焦点落在对话框内(本实现只标记 `tabindex="-1"` + `role`,焦点陷阱见 §13.7)
|
||
- 遮罩 `aria-hidden="true"`
|
||
|
||
### 13.7 doNotInvent
|
||
- 焦点陷阱(focus trap)的完整实现
|
||
- 多对话框嵌套时的层级规则
|
||
|
||
### 13.8 unknowns
|
||
- 对话框的最大宽度与最大高度
|
||
- 长内容是否在 body 内滚动
|
||
|
||
---
|
||
|
||
## 14 · 宫格 Grid
|
||
|
||
### 14.1 用途
|
||
把图标 / 文字入口按等分列排成网格,用于首页功能入口区。
|
||
|
||
### 14.2 结构(anatomy)
|
||
- `grid`:根容器
|
||
- `grid__item`:单个格子(图标 + 文字)
|
||
- `grid__icon`:图标区
|
||
- `grid__text`:文字标签
|
||
|
||
### 14.3 变体维度
|
||
- `columns`:`2` / `3` / `4`(每行格数)
|
||
- `border`:`false` / `true`(是否画格线)
|
||
- `square`:`false` / `true`(格子是否为正方形)
|
||
|
||
### 14.4 状态
|
||
- default:常态
|
||
- active:按下反馈(`:active` 底色变化)
|
||
- disabled:置灰且不可点
|
||
|
||
### 14.5 交互与触控
|
||
- 每个格子整块可点,热区不小于 44×44
|
||
- 按下反馈用 `:active`(不用 `:hover` —— 触屏没有悬停)
|
||
|
||
### 14.6 无障碍
|
||
- 可点格子用原生 `button`(整块热区 + 键盘可达)
|
||
- 纯展示格子用 `div` 且不加交互角色
|
||
- 图标装饰(`aria-hidden="true"`),文字即语义
|
||
|
||
### 14.7 doNotInvent
|
||
- 格子的拖拽排序
|
||
- 超出 4 列的响应式折行列数
|
||
|
||
### 14.8 unknowns
|
||
- 图标区的推荐尺寸
|
||
- 一格最多几个字
|
||
|
||
---
|
||
|
||
## 15 · 步骤条 Steps
|
||
|
||
### 15.1 用途
|
||
横向展示多步流程的当前进度(如「提交 → 审核 → 完成」)。
|
||
|
||
### 15.2 结构(anatomy)
|
||
- `steps`:根容器,横向排列
|
||
- `steps__item`:单个步骤
|
||
- `steps__dot`:序号点 / 勾选标记
|
||
- `steps__label`:步骤标题
|
||
- `steps__line`:连接线
|
||
|
||
### 15.3 变体维度
|
||
- `direction`:`horizontal` / `vertical`
|
||
- `status`:`wait`(未开始)/ `process`(进行中)/ `finish`(已完成)/ `error`
|
||
|
||
### 15.4 状态
|
||
- wait:灰色,未开始
|
||
- process:品牌色,进行中
|
||
- finish:品牌色 + 勾选
|
||
- error:错误色
|
||
|
||
### 15.5 交互与触控
|
||
- 纯展示组件,步骤本身不可点(这是设计如此,不是缺失)
|
||
- 步骤过多时横向可滚动(容器 `overflow-x: auto`)
|
||
|
||
### 15.6 无障碍
|
||
- 容器 `role="list"`,每步 `role="listitem"`
|
||
- `aria-current="step"` 标出当前步
|
||
- 状态不只靠颜色(进行中加粗 + 已完成用勾选字符)
|
||
|
||
### 15.7 doNotInvent
|
||
- 步骤之间的动画过渡
|
||
- 点击步骤跳转的规则
|
||
|
||
### 15.8 unknowns
|
||
- 纵向步骤条的推荐间距
|
||
- 标题最多几行
|
||
|
||
---
|
||
|
||
## 16 · 通知栏 NoticeBar
|
||
|
||
### 16.1 用途
|
||
在页面顶部或内容区之间横向滚动展示一条通告(如「系统维护通知」)。
|
||
|
||
### 16.2 结构(anatomy)
|
||
- `noticebar`:根容器
|
||
- `noticebar__icon`:左侧喇叭图标
|
||
- `noticebar__text`:通告文字(可滚动)
|
||
- `noticebar__close`:可选关闭按钮
|
||
|
||
### 16.3 变体维度
|
||
- `tone`:`default` / `success` / `warning` / `danger`
|
||
- `scrollable`:`false` / `true`(文字超宽时是否跑马灯)
|
||
- `closable`:`false` / `true`
|
||
|
||
### 16.4 状态
|
||
- default:常态
|
||
- scrollable:文字持续横向滚动
|
||
- closed:已关闭(由宿主移除)
|
||
|
||
### 16.5 交互与触控
|
||
- 关闭按钮热区不小于 24×24(用负外边距扩展)
|
||
- 跑马灯在 `prefers-reduced-motion: reduce` 下**停止滚动**并改为换行显示(无障碍硬要求)
|
||
- 关闭动作只触发一次事件,是否移除由宿主决定
|
||
|
||
### 16.6 无障碍
|
||
- 容器 `role="status"`(读屏会朗读一次通告)
|
||
- 关闭按钮为原生 `button` 并带 `aria-label`
|
||
- 跑马灯不得用 `aria-live`(滚动内容反复朗读会干扰)
|
||
|
||
### 16.7 doNotInvent
|
||
- 多条通告的排队 / 轮播
|
||
- 滚动速度的可配置项
|
||
|
||
### 16.8 unknowns
|
||
- 通告文字的最大长度
|
||
- 是否支持富文本
|
||
|
||
---
|
||
|
||
## 17 · 数字键盘 NumberKeyboard
|
||
|
||
### 17.1 用途
|
||
为金额、验证码等纯数字输入提供自绘键盘(比系统键盘更可控、更安全)。
|
||
|
||
### 17.2 结构(anatomy)
|
||
- `keyboard`:根容器,固定在底部
|
||
- `keyboard__key`:单个按键
|
||
- `keyboard__key--delete`:删除键
|
||
- `keyboard__confirm`:确认键(`type=confirm` 时)
|
||
|
||
### 17.3 变体维度
|
||
- `type`:`number`(0-9 + 小数点)/ `digit`(纯 0-9)
|
||
- `showDelete`:`false` / `true`
|
||
- `showConfirm`:`false` / `true`
|
||
|
||
### 17.4 状态
|
||
- default:常态
|
||
- active:按下反馈
|
||
- disabled:禁用(确认键不可用)
|
||
|
||
### 17.5 交互与触控
|
||
- 按键热区不小于 44×44,网格等分排列
|
||
- 按下用 `:active`,不做悬停态
|
||
- 键盘本身不持有输入值 —— 只 emit 按键事件,由宿主决定写入哪个输入框
|
||
|
||
### 17.6 无障碍
|
||
- 容器 `role="group"` + `aria-label`
|
||
- 每个按键为原生 `button`,文字即按键名(读屏读「1」「删除」)
|
||
- 删除键用 `aria-label="删除"`(不读成符号)
|
||
|
||
### 17.7 doNotInvent
|
||
- 键盘高度的手势拖拽调整
|
||
- 与系统键盘的互斥逻辑
|
||
|
||
### 17.8 unknowns
|
||
- 是否支持自定义按键顺序
|
||
- 长按连续删除的间隔
|
||
|
||
---
|
||
|
||
## 18 · 日期选择器 DatePicker
|
||
|
||
### 18.1 用途
|
||
从底部弹出的年 / 月 / 日选择器,用于生日、有效期这类需要日期输入的场景。
|
||
|
||
### 18.2 结构(anatomy)
|
||
- `mask`:遮罩,点击关闭
|
||
- `picker`:底部浮层
|
||
- `picker__header`:取消 / 标题 / 确定
|
||
- `picker__columns`:年 / 月 / 日三列
|
||
- `picker__column`:单列,可滚动
|
||
- `picker__option`:单个选项
|
||
|
||
### 18.3 变体维度
|
||
- `mode`:`date`(年月日)/ `month`(年月)
|
||
- `round`:`false` / `true`
|
||
|
||
### 18.4 状态
|
||
- closed:收起(默认)
|
||
- open:展开,遮罩可见
|
||
- selected:当前选中项(品牌色高亮)
|
||
|
||
### 18.5 交互与触控
|
||
- 遮罩点击关闭;`closeOnMask=false` 时不关闭
|
||
- 选项行高不小于 44px,滚动容器 `-webkit-overflow-scrolling: touch`
|
||
- 确定 / 取消按钮热区不小于 44px
|
||
|
||
### 18.6 无障碍
|
||
- 浮层 `role="dialog"` + `aria-modal="true"`
|
||
- 每列 `role="listbox"`,选项 `role="option"` + `aria-selected`
|
||
- 遮罩 `aria-hidden="true"`
|
||
- 选中值以 `YYYY-MM-DD` 文本呈现(不依赖视觉滚动位置)
|
||
|
||
### 18.7 doNotInvent
|
||
- 日期范围的禁用规则(由宿主传入)
|
||
- 滚轮惯性 / 吸附动画的物理参数
|
||
|
||
### 18.8 unknowns
|
||
- 可选年份的范围
|
||
- 是否支持「至今」这类特殊选项
|
||
|
||
---
|
||
|
||
## 19 · 图标 Icon
|
||
|
||
### 19.1 用途
|
||
用一个字形表达状态或动作(选中、警告、返回、更多)。移动端与桌面端的差别在**尺寸基线**:手指操作要求图标更大、且图标自身从不承担点击 —— 点击由包裹它的 44px 按钮提供。
|
||
|
||
### 19.2 结构(anatomy)
|
||
- `icon`:根元素,一个固定边长的内联盒子,负责尺寸与颜色
|
||
- `glyph`:字形节点,纯符号或内联 SVG,不带语义
|
||
- `label`:可选的无障碍名称,有值时图标成为「有语义的图」,无值时对读屏隐藏
|
||
- `size`:四档边长(small 16 / default 20 / large 24 / xlarge 32)
|
||
- `tone`:颜色来源(继承父级或取语义色)
|
||
|
||
### 19.3 变体维度
|
||
- `size`:`small` / `default` / `large` / `xlarge`
|
||
- `tone`:`default` / `brand` / `secondary` / `danger`
|
||
- `spin`:`false` / `true`
|
||
|
||
### 19.4 状态
|
||
- default:常态(静态图)
|
||
- spinning:持续旋转,仅表示「进行中」,不表示成功
|
||
- disabled:无独立禁用态 —— 由父级按钮 / 单元格置灰,图标继承其颜色
|
||
|
||
### 19.5 交互与触控
|
||
- 图标自身不是热区;可点时由父级按钮提供不小于 44×44 的点击区
|
||
- 图标与相邻文字的间距取 4px(与按钮内图标一致)
|
||
- `spin=true` 在 `prefers-reduced-motion: reduce` 下停止旋转(无障碍硬要求)
|
||
- 同一行内图标与文字基线对齐(`vertical-align: -0.125em`),避免文字被顶高
|
||
|
||
### 19.6 无障碍
|
||
- `label` 有值时 `role="img"` + `aria-label`,读屏读出该名称
|
||
- `label` 为空时 `aria-hidden="true"`,纯装饰不进读屏序列
|
||
- 图标不得作为唯一信息载体:颜色变化必须伴随文字或 label
|
||
- 语义色图标与背景的对比度不低于 3:1(WCAG 1.4.11 非文本对比)
|
||
|
||
### 19.7 doNotInvent
|
||
- 图标资源清单(本仓库不引图标字体,字形由各端内置表或内联 SVG 提供)
|
||
- 图标更换 / 过渡动画
|
||
- 点击图标自身触发动作
|
||
|
||
### 19.8 unknowns
|
||
- 业务侧自定义字形的注册方式
|
||
- 图标与文字组合时的推荐最小间距(本实现取 4px)
|
||
|
||
|
||
---
|
||
|
||
## 20 · 布局 Layout
|
||
|
||
### 20.1 用途
|
||
把一行内容按比例切成若干列(等分或按 12 栅格取值)。移动端屏幕窄,绝大多数场景是 2~4 等分;需要主次分栏时才用不等宽 —— 桌面端的「12 栅格 + 响应式断点」在移动端退化为「一档列数 × 可覆盖的列宽」。
|
||
|
||
### 20.2 结构(anatomy)
|
||
- `layout`:行容器,`display: flex` + 换行,承载**列间距**变量
|
||
- `col`:列,默认占满一行(`flex-basis: 100%`),由行级等分或列级 span 类决定实际宽度
|
||
- `panel`:列内容的可选包壳,提供内边距与最小高度,业务也可直接放自己的卡片
|
||
- `gutter`:列间距,四档(0 / 8 / 16 / 24),由行级类写到 CSS 变量上
|
||
- `span`:列级权重类(12 栅格取值),可覆盖行级等分
|
||
|
||
### 20.3 变体维度
|
||
- `gutter`:`0` / `8` / `16` / `24`
|
||
- `columns`:`0`(不启用等分) / `2` / `3` / `4`
|
||
- `align`:`start` / `center` / `end` / `stretch`
|
||
- `wrap`:`true`(换行) / `false`(不换行、横向滚动)
|
||
|
||
### 20.4 状态
|
||
- default:常态
|
||
- 本组件无交互状态(纯排布,这是设计如此,不是缺失)
|
||
|
||
### 20.5 交互与触控
|
||
- 布局容器不绑点击,不设 `cursor: pointer`,热区始终由列内的业务元素提供
|
||
- 列内若是可点区域,其热区不小于 44×44(见 §19.5 与 §6.5)
|
||
- `gutter` 由 CSS 变量承载,同一行内所有列的宽度计算共用该变量,避免手工算宽
|
||
- `wrap=false` 时容器横向滚动,纵向页面滚动不受影响
|
||
- 列宽使用 `flex-basis: calc(...)`,窄屏下不产生横向溢出(列内文本默认 `min-width: 0`)
|
||
|
||
### 20.6 无障碍
|
||
- 布局是**纯视觉分组**:不添加 `role`、不添加 `aria-*`
|
||
- 不要为了布局把语义节点(列表 / 按钮)拆到不相邻的列里,否则读屏顺序会错乱
|
||
- 视觉顺序必须与 DOM 顺序一致(不用 `order` 重排)
|
||
|
||
### 20.7 doNotInvent
|
||
- 响应式断点(移动端只有一档列数,不为大屏定义断点)
|
||
- 列的排序 / 拖拽
|
||
- 栅格嵌套的层级规则
|
||
|
||
### 20.8 unknowns
|
||
- 列内推荐的最大列数(本实现提供 2 / 3 / 4 三档)
|
||
- 列高等分(`stretch` 之外的等高策略)
|
||
|
||
|
||
---
|
||
|
||
## 21 · 链接 Link
|
||
|
||
### 21.1 用途
|
||
一段内联文字承载「跳转 / 打开下一级」或「触发一次轻量动作」。移动端与桌面端的差别在**下划线策略**:桌面端靠 hover 变色提示可点,触屏没有 hover,因此链接必须靠**颜色常驻**区分;默认不加下划线(正文里满屏下划线噪声大),正文段落内与条款页再加下划线。
|
||
|
||
### 21.2 结构(anatomy)
|
||
- `link`:根元素,有 `href` 时是原生 `a`,无 `href` 时是原生 `button`(只回传事件)
|
||
- `label`:链接文字,单行不换行(超长由宿主截断)
|
||
- `icon`:可选尾部图标,继承链接颜色,与文字间距 4px
|
||
- `href`:跳转地址;禁用时不渲染该属性(否则仍可被打开)
|
||
- `text`:纯文字快捷入口,与默认插槽二选一
|
||
|
||
### 21.3 变体维度
|
||
- `tone`:`brand`(默认) / `default`(继承父级文字色) / `danger` / `success`
|
||
- `underline`:`false` / `true`
|
||
- `block`:`false` / `true`(撑满容器、整行可点)
|
||
|
||
### 21.4 状态
|
||
- default:常态
|
||
- active:按下时颜色加深 + 极浅底色(移动端没有 hover)
|
||
- disabled:置灰且不响应点击,同时移出 tab 序列
|
||
- visited:**不区分**(业务型链接不标注已访问,避免用户误判状态)
|
||
|
||
### 21.5 交互与触控
|
||
- 点击热区高度不小于 44px,文字可短但热区不缩水(内边距撑开)
|
||
- 按下反馈为颜色变化,不做位移缩放(避免长按抖动)
|
||
- 相邻链接之间至少留 8px 间距,防止误触(同时给出 16px 的更稳选择)
|
||
- 禁用链接点击不触发事件,也不跳转
|
||
|
||
### 21.6 无障碍
|
||
- 有 `href` 用原生 `a`(可聚焦、可长按复制、读屏报「链接」);无 `href` 用原生 `button`
|
||
- 禁用链接不能用 `href`,且要 `aria-disabled="true"` + `tabindex="-1"`(移出 tab 序列)
|
||
- 链接文字必须自解释,不要出现孤立的「点击这里」(读屏会脱离上下文朗读)
|
||
- 颜色不是唯一线索:正文段落内的链接必须带下划线或图标,避免色觉障碍用户无法识别
|
||
|
||
### 21.7 doNotInvent
|
||
- 外跳协议处理(`tel:` / `mailto:` / 唤起 App)
|
||
- 已访问状态的样式
|
||
- 链接的埋点 / 统计
|
||
|
||
### 21.8 unknowns
|
||
- 一屏内链接的最大推荐数量
|
||
- 链接与相邻文字的推荐最小间距(同时给出 8px 与 16px 两档)
|
||
|
||
|
||
---
|
||
|
||
## 22 · 加载 Loading
|
||
|
||
### 22.1 用途
|
||
告诉用户「系统正在处理,请等」并占住当前位置。移动端与桌面端的差别在**是否独占屏幕**:桌面端加载指示多是区块内的一个转圈,移动端常需要 `fullscreen` 铺一层遮罩 —— 提交订单、支付这类不可中断的动作期间,必须挡住下方的重复点击。
|
||
|
||
### 22.2 结构(anatomy)
|
||
- `loading`:根元素,内联形态(转圈 + 文案同行或上下排);`fullscreen=true` 时它本身即遮罩层
|
||
- `spinner`:转圈,由 CSS 动画驱动(三端一致,不依赖图片或字体)
|
||
- `text`:可选文案,说明「在等什么」;为空时只有转圈
|
||
- `panel`:全屏形态下的卡片面板,承载指示器与文案,保证遮罩上的对比度可控
|
||
- `mask`:全屏形态的遮罩底色(取令牌,语义等同弹窗遮罩)
|
||
|
||
### 22.3 变体维度
|
||
- `size`:`small`(16px) / `default`(20px) / `large`(28px)
|
||
- `vertical`:`false`(横行) / `true`(上下排布)
|
||
- `fullscreen`:`false`(区块内) / `true`(遮罩全屏)
|
||
|
||
### 22.4 状态
|
||
- loading:转圈持续旋转,`aria-busy="true"`(默认语义)
|
||
- open:`fullscreen=true` 且展开时可见并可截获手势
|
||
- done:加载结束后的过渡态(转圈停止),实际结果提示由宿主替换(本组件不自动消失)
|
||
- disabled:无独立禁用态 —— 加载中「不可操作」由遮罩承担,不是把控件置灰
|
||
|
||
### 22.5 交互与触控
|
||
- 加载组件本身**不可点**,也不抢焦点(不打断读屏正在读的内容)
|
||
- `fullscreen=true` 且 `open` 时遮罩截获手势,下方内容不可点;未展开时不截获
|
||
- 转圈动画 800ms/圈(`--kole-m-loading-duration` 可覆盖),时长恒定不随尺寸变化
|
||
- `prefers-reduced-motion: reduce` 下停止旋转(改为静态环),避免前庭不适
|
||
- 加载超过一次会话的合理时长时应由宿主提供取消入口,本组件不自己造取消按钮
|
||
|
||
### 22.6 无障碍
|
||
- 容器 `role="status"` + `aria-live="polite"`(状态变化被播报,且不打断当前朗读)
|
||
- 加载中置 `aria-busy="true"`;全屏遮罩未展开时置 `"false"`
|
||
- 转圈是装饰(`aria-hidden="true"`),语义全部由文案与 `role="status"` 承担
|
||
- 文案要具体(「正在提交订单…」而不是「加载中」),读屏用户与视力用户获得同样的信息量
|
||
|
||
### 22.7 doNotInvent
|
||
- 加载耗时的进度百分比(本组件不假装知道进度)
|
||
- 超时后的自动提示 / 自动重试
|
||
- 多条加载的排队与合并
|
||
|
||
### 22.8 unknowns
|
||
- 全屏加载持续多久后应提示「可能需要更长时间」
|
||
- 文案的最大长度与换行策略
|
||
|
||
|
||
---
|
||
|
||
## 23 · 头像 Avatar
|
||
|
||
### 23.1 用途
|
||
用一张图或一两个字符代表一个主体(用户、企业、群组);移动端列表与详情页里大量出现,必须能单手扫读,因此尺寸只有三档、形状只有两种,且图片不可用时必须立刻退回文字,不能出现裂图。
|
||
|
||
### 23.2 结构(anatomy)
|
||
- `avatar`:根元素,正方形圆角容器,尺寸由 size 决定
|
||
- `image`:图片,铺满容器并按形状裁切
|
||
- `text`:文字内容(姓名首字或简称),图片缺失时它就是主体
|
||
- `fallback`:兜底节点,图片加载失败后退回的形状(文字或图标)
|
||
- `badge`:可选右下角角标位,挂在线状态或未读数
|
||
|
||
### 23.3 变体维度
|
||
- `size`:`small`(32px)/ `default`(40px)/ `large`(56px)
|
||
- `shape`:`circle`(圆形,用于人)/ `square`(圆角方形,用于企业或群组)
|
||
- `type`:`text`(文字头像)/ `image`(图片头像,失败退文字)
|
||
|
||
### 23.4 状态
|
||
- default:常态
|
||
- fallback:图片加载失败,退回文字或图标
|
||
- disabled:置灰(如成员已离职)
|
||
|
||
### 23.5 交互与触控
|
||
- 头像本身不是按钮;可点时必须由宿主包一层原生 `button` 或 `a`,热区不小于 44×44
|
||
- 图片 `alt` 或根节点 `aria-label` 必填其一,读屏读「姓名 + 头像」
|
||
- 图片加载失败只触发一次 `error` 事件,是否替换资源由宿主决定
|
||
|
||
### 23.6 无障碍
|
||
- 文字头像根节点 `role="img"` 并带 `aria-label`(内容为姓名)
|
||
- 图片头像直接用 `<img alt>`,`alt` 为空时视为装饰并对读屏隐藏
|
||
- 角标不改变头像的可访问名
|
||
|
||
### 23.7 doNotInvent
|
||
- 图片裁剪的 focal point(人脸居中)算法
|
||
- 角标位置随形状(圆 / 方)的微调规则
|
||
|
||
### 23.8 unknowns
|
||
- 文字头像的底色是否按名字散列取多色
|
||
- 姓名超过两个汉字时的截断规则
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 24 · 列表 List
|
||
|
||
### 24.1 用途
|
||
把一组同类信息按行排列,用于「设置项 / 订单 / 成员」这类需要扫读的场合;移动端一屏只有 6~8 行,因此每行信息层级必须收敛到「标题 + 可选的说明或值」,分组之间用标题与留白分隔,而不是靠边框。
|
||
|
||
### 24.2 结构(anatomy)
|
||
- `list`:根元素,一个列表区块
|
||
- `header`:可选分组标题,位于列表之上
|
||
- `item`:列表项,一行承载「前缀 + 主区 + 后缀」
|
||
- `prefix`:可选前缀位(头像、图标、序号)
|
||
- `body`:主区,标题 + 可选副标题,两行都单行省略
|
||
- `suffix`:可选后缀位(值文字、标签、箭头、开关)
|
||
- `footer`:可选分组脚注,用于补充说明
|
||
|
||
### 24.3 变体维度
|
||
- `border`:`true`(行间 1px 分隔线)/ `false`(无分隔线,靠间距分组)
|
||
- `size`:`default`(行高 56px)/ `compact`(行高 44px)
|
||
- `divider`:`inset`(分隔线缩进到行内容起点)/ `full`(通栏分隔线)
|
||
|
||
### 24.4 状态
|
||
- default:常态
|
||
- active:行按下时整行背景变化
|
||
- disabled:置灰且不响应点击
|
||
- empty:列表为空时显示占位文案
|
||
|
||
### 24.5 交互与触控
|
||
- 可点行整行都是热区,`default` 行高不小于 56px、`compact` 不小于 44px
|
||
- 按下反馈是整行背景变化,不是只有文字变色
|
||
- 前缀位不参与点击判定(点图标等于点整行),后缀位里的独立控件(开关、按钮)要阻止事件冒泡,避免一次点击触发两个动作
|
||
- 列表滚动由宿主容器负责;本组件不接管滚动、不做虚拟列表
|
||
|
||
### 24.6 无障碍
|
||
- 容器 `role="list"`,纯展示行 `role="listitem"`;可点行用原生 `button`(原生语义优先于 listitem)
|
||
- 分组标题用 `aria-label` 或可见文本,读屏在进入分组时能读到
|
||
- 空列表用 `aria-live="polite"` 播报占位文案
|
||
|
||
### 24.7 doNotInvent
|
||
- 虚拟滚动与无限加载的触发规则
|
||
- 行的拖拽排序与左滑操作(那是 SwipeCell 的职责)
|
||
|
||
### 24.8 unknowns
|
||
- 单行最多几列(前缀 + 主区 + 后缀之外的排布)
|
||
- 分组标题是否吸顶
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 25 · 折叠面板 Collapse
|
||
|
||
### 25.1 用途
|
||
把长内容按主题收起来,让用户先看到标题、按需展开某一段;移动端屏幕窄,展开后内容会顶走上下文,因此一次只展开一个(手风琴)是默认推荐形态,展开态必须明确到不靠颜色也能看出。
|
||
|
||
### 25.2 结构(anatomy)
|
||
- `collapse`:根元素,一组面板的容器
|
||
- `item`:单个面板,含标题行与内容区
|
||
- `header`:标题行,整行可点,高度不小于 44px
|
||
- `arrow`:标题行右侧箭头,展开时旋转 90°
|
||
- `panel`:内容区,展开时可见(收起时高度为 0 或 `hidden`)
|
||
- `content`:内容区内的正文节点
|
||
|
||
### 25.3 变体维度
|
||
- `accordion`:`false`(多面板可同时展开)/ `true`(手风琴,同时只展开一个)
|
||
- `bordered`:`true`(面板之间有分隔线与外框)/ `false`(无边框,靠留白分隔)
|
||
|
||
### 25.4 状态
|
||
- collapsed:收起(默认)
|
||
- expanded:展开
|
||
- disabled:标题行置灰且不响应
|
||
|
||
### 25.5 交互与触控
|
||
- 标题行整行都是热区,高度不小于 44px
|
||
- 视觉箭头转 90°(180ms 过渡),展开时 `aria-expanded` 同步为 `true`
|
||
- 手风琴模式下展开新面板会收起当前展开项;`accordion=false` 时互不影响
|
||
- 内容区不做高度动画,直接切换 `hidden`(省电,读屏也不会读到中间态);减少动态偏好下箭头同样瞬时切换
|
||
|
||
### 25.6 无障碍
|
||
- 标题行用原生 `button` 并带 `aria-expanded` / `aria-controls`
|
||
- 内容区与标题用 `id` / `aria-controls` 建立关联,收起时用 `hidden` 属性隐藏(而不是只靠 CSS 高度)
|
||
- 禁用项置 `aria-disabled="true"` 且不可聚焦
|
||
|
||
### 25.7 doNotInvent
|
||
- 展开动画的高度换算公式(内容高度由浏览器决定)
|
||
- 嵌套折叠面板的层级样式
|
||
|
||
### 25.8 unknowns
|
||
- 默认是否展开第一项
|
||
- 标题行右侧是否允许放额外操作
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 26 · 进度条 Progress
|
||
|
||
### 26.1 用途
|
||
把一个过程的完成度可视化(上传、审核、额度耗尽);移动端的进度多数伴随文字出现,因此百分比文案与状态色是标配而非可选,且进度变化要能被读屏播报而不只是画出来。
|
||
|
||
### 26.2 结构(anatomy)
|
||
- `progress`:根元素,承载轨道与文案
|
||
- `track`:轨道,未完成部分的底色
|
||
- `bar`:已完成部分,宽度由 percentage 决定
|
||
- `ring`:环形进度的圆环轨道(type=circle 时替代 track/bar)
|
||
- `label`:百分比文案,可置于条内、条右侧或环心
|
||
- `status`:状态图标位(成功 / 失败),非进行中时显示
|
||
|
||
### 26.3 变体维度
|
||
- `type`:`line`(线形)/ `circle`(环形)
|
||
- `status`:`normal`(进行中,品牌色)/ `success`(成功)/ `error`(失败)
|
||
- `labelPlacement`:`inside`(文案在条内)/ `right`(条右侧)/ `center`(环心,仅 circle)
|
||
|
||
### 26.4 状态
|
||
- default:进行中(0 < percentage < 100)
|
||
- complete:percentage = 100,文案显示 100%
|
||
- error:失败,进度停在断点并转为错误色
|
||
- paused:暂停,条体降透明度
|
||
|
||
### 26.5 交互与触控
|
||
- 进度条本身不可交互、不接收点击;需要取消时由宿主在旁边放按钮
|
||
- 数值变化用 CSS 宽度过渡(240ms,`--kole-m-progress-duration`),不做无限循环动画(省电且不干扰读屏)
|
||
- 无动画偏好(`prefers-reduced-motion`)下直接跳到目标宽度
|
||
|
||
### 26.6 无障碍
|
||
- 根节点 `role="progressbar"` + `aria-valuemin="0"` / `aria-valuemax="100"` / `aria-valuenow`
|
||
- 文案节点 `aria-hidden="true"`,避免与 `aria-valuenow` 重复播报
|
||
- 不确定进度(无法给出百分比)用 `aria-valuetext="进行中"` 表达
|
||
|
||
### 26.7 doNotInvent
|
||
- 环形进度的线宽与半径的自适应规则(由 size 决定,不做响应式推导)
|
||
- 进度到达 100% 后的自动隐藏时机
|
||
|
||
### 26.8 unknowns
|
||
- 百分比是否四舍五入到整数
|
||
- 环形进度是否支持渐变描边
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 27 · 输入框 Input
|
||
|
||
### 27.1 用途
|
||
在一行内收集单行文本(姓名、手机号、金额、验证码等);移动端与桌面端的关键差别是热区与字号:输入框整行占满、高度不小于 44px,字号不小于 16px,否则 iOS 聚焦时会自动放大页面;清除动作也必须在框内完成,因为触屏没有悬停的鼠标可以移开。
|
||
|
||
### 27.2 结构(anatomy)
|
||
- `field`:字段容器,包裹输入框与下方错误提示(错误提示在框外,不挤占输入区)
|
||
- `prefix`:可选前缀,放单位或符号(如「¥」)
|
||
- `control`:原生 `input`,占满剩余宽度,字号 16px
|
||
- `clear`:可选清除按钮,有值且 `clearable=true` 时出现在右侧
|
||
- `suffix`:可选后缀,由默认插槽给出的自定义内容(单位、显示密码等)
|
||
- `errorText`:字段下方的错误提示文字
|
||
|
||
### 27.3 变体维度
|
||
- `size`:`default`(高度 44px)/ `large`(高度 52px)
|
||
- `clearable`:`false`(无清除按钮)/ `true`(有值时右侧出现清除按钮)
|
||
|
||
### 27.4 状态
|
||
- default:常态
|
||
- focus:聚焦,边框变品牌色并带 2px 聚焦外发光
|
||
- error:错误,边框变错误色且 `aria-invalid="true"`
|
||
- disabled:置灰且不可聚焦
|
||
|
||
### 27.5 交互与触控
|
||
- 输入框高度不小于 44px;整行可点(把 `kole-m-input` 放在 `label` 里,点行即聚焦)
|
||
- 清除按钮视觉是 16px 图标,热区外扩到 44px 最小触控边长
|
||
- 聚焦反馈是边框色 + 2px 外发光,150ms 过渡;不改变布局(不撑开高度)
|
||
- 有值时清除按钮才出现;清除后焦点留在输入框
|
||
- 键盘「完成」键触发 confirm 事件,值随 input 事件实时回传
|
||
|
||
### 27.6 无障碍
|
||
- 输入框用原生 `input`,名称由 `aria-label` 给出(占位文字不算标签)
|
||
- 错误态用 `aria-invalid="true"`,错误文案用 `aria-describedby` 关联
|
||
- 清除按钮是原生 `button` 且带 `aria-label="清除"`
|
||
- 禁用态用原生 `disabled`,读屏会跳过
|
||
|
||
### 27.7 doNotInvent
|
||
- 输入内容的正则与业务校验(合法性判断在宿主)
|
||
- `inputmode` 之外的自定义软键盘行为(键盘类型由宿主按场景指定)
|
||
- 自动填充与验证码自动读取的策略
|
||
|
||
### 27.8 unknowns
|
||
- 密码是否需要内置「显示/隐藏」开关
|
||
- 数字输入的千分位格式化时机
|
||
- 前缀里是否允许放图片/图标
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 28 · 搜索框 Search
|
||
|
||
### 28.1 用途
|
||
用关键词从长列表里取回一小段结果;移动端的搜索框几乎总是页面顶部的独立一行,输入即过滤(不等回车),并且要给出一个明确的退出动作 —— 取消,因为触屏没有 Esc 键,用户清空关键词后仍需一键回到列表。
|
||
|
||
### 28.2 结构(anatomy)
|
||
- `search`:根元素,一行里放进「搜索框 + 取消动作」
|
||
- `icon`:框内左侧放大镜,纯装饰(不承载语义,读屏由 label 承担)
|
||
- `control`:原生 `input type="search"`,占满剩余宽度
|
||
- `clear`:可选清除按钮,有值且清除可用时出现在图标与取消之间
|
||
- `cancel`:可选取消动作,默认文案「取消」,由 showCancel 控制显隐
|
||
|
||
### 28.3 变体维度
|
||
- `round`:`true`(胶囊形,页面顶部常用)/ `false`(方角,嵌在卡片或工具栏里)
|
||
- `showCancel`:`false`(只有输入框)/ `true`(右侧出现取消动作)
|
||
|
||
### 28.4 状态
|
||
- default:常态
|
||
- filled:有值(清除按钮出现)
|
||
- disabled:置灰且不可聚焦
|
||
|
||
### 28.5 交互与触控
|
||
- 搜索框高度不小于 44px,取消动作热区不小于 44px
|
||
- 输入即触发 input 事件(不等回车);键盘「搜索」键触发 search 事件
|
||
- 清除按钮只在有值时出现,点击清空并把焦点留在输入框
|
||
- 清除与取消都是 44px 热区;两者同时出现时先清除、再取消(自右向左层级递进)
|
||
|
||
### 28.6 无障碍
|
||
- 输入框用原生 `input type="search"`(iOS 键盘右下角键位变成「搜索」)
|
||
- 搜索图标是装饰性的,置 `aria-hidden="true"`,名称由 `aria-label` 给出
|
||
- 清除按钮是原生 `button` 且带 `aria-label="清除"`;取消动作是原生 `button`
|
||
- 取消按钮不用图标代替文字(触屏上文字比图标更好点)
|
||
|
||
### 28.7 doNotInvent
|
||
- 搜索的防抖时值与接口节流策略(由宿主决定)
|
||
- 搜索历史的存储与展示
|
||
- 搜索结果的高亮规则
|
||
|
||
### 28.8 unknowns
|
||
- 取消文案是否允许替换(如「返回」)
|
||
- 是否需要自动聚焦并拉起键盘
|
||
- 语音/扫码等扩展入口是否放进框内
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 29 · 开关 Switch
|
||
|
||
### 29.1 用途
|
||
即时启停一项配置或业务状态(启用通知、公开数据、自动同步);移动端与桌面端的关键差别是**热区**:开关本体视觉只有 48×28,但整行(开关 + 文字)都是可点热区,行高不小于 44px,否则手指点不中;关态与开态也不能只靠颜色区分,必须同时看到滑块位移。
|
||
|
||
### 29.2 结构(anatomy)
|
||
- `switch`:根元素,一行里放进「轨道 + 文字标签」,整行可点
|
||
- `track`:轨道,承载背景色与滑块位移的边界
|
||
- `knob`:滑块,关态靠左、开态靠右(位移是开/关的主要视觉信号)
|
||
- `text`:可选文字标签,说明这项开关控制什么
|
||
- `control`:可点的整行控件(原生 `button` + `role="switch"`),承接键盘与读屏
|
||
|
||
### 29.3 变体维度
|
||
- `size`:`default`(轨道 48×28)/ `small`(轨道 40×22,用于紧凑表单)
|
||
- `labelPlacement`:`right`(文字在开关右侧,默认)/ `left`(文字在左侧,值区右对齐时用)
|
||
|
||
### 29.4 状态
|
||
- off:关态(默认)
|
||
- on:开态,滑块位移 + 背景变品牌色,`aria-checked="true"`
|
||
- disabled:置灰且不可聚焦
|
||
|
||
### 29.5 交互与触控
|
||
- 整行(开关 + 文字)都是热区,行高不小于 44px;开关本体不可单独缩到 44px 以下
|
||
- 点击切换只需要一次触摸,不要求拖动滑块(拖动是桌面习惯,触屏误触率高)
|
||
- 切换动效是滑块位移 150ms 过渡;减少动态偏好下瞬时切换
|
||
- 关态与开态不能只靠颜色区分:滑块位置 + `aria-checked` 双通道
|
||
- 切换后立即触发 change 事件,不做二次确认(需要确认的场景由宿主先弹对话框)
|
||
|
||
### 29.6 无障碍
|
||
- 用 `role="switch"` + `aria-checked="true|false"`,而不是 `role="checkbox"`(读屏会播报「开关」)
|
||
- 承载控件是原生 `button`,键盘可聚焦、空格/回车可切换,并有可见焦点环
|
||
- 文字标签在控件内部,读屏播报的名称就是标签本身;无标签时用 `label` 属性补 `aria-label`
|
||
- 禁用态用原生 `disabled`,读屏会播报不可用
|
||
|
||
### 29.7 doNotInvent
|
||
- 二次确认弹窗与「切换失败回滚」的业务流程
|
||
- 三态开关(关 / 开 / 待定)的视觉表达
|
||
- 与表单一起提交时的隐藏字段(由宿主添加)
|
||
|
||
### 29.8 unknowns
|
||
- 开关本体是否允许小于 48×28(紧凑表单的下限)
|
||
- 文案与开关的间距是否跟随字号
|
||
- 加载态(切换请求进行中)如何表达
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 30 · 步进器 Stepper
|
||
|
||
### 30.1 用途
|
||
在一段有界区间里连续加减小整数(购买数量、份数、编号);移动端与桌面端的关键差别是**加号与减号必须各自独立占一个 44px 见方的热区**,且到边界时不是把按钮藏起来,而是置灰 —— 触屏上「按钮消失」会让用户以为界面坏了,而置灰能表达「到头了」。
|
||
|
||
### 30.2 结构(anatomy)
|
||
- `stepper`:根元素,一行里放进「减号 + 值 + 加号」
|
||
- `minus`:减号按钮,到达 `min` 时置灰
|
||
- `value`:值区;`editable=true` 时是可聚焦的数字输入框,否则是纯文本
|
||
- `plus`:加号按钮,到达 `max` 时置灰
|
||
- `unit`:可选单位文案,跟在值后面(如「件」),由默认插槽给出
|
||
- `label`:无障碍分组名称(`role="group"` + `aria-label`)
|
||
|
||
### 30.3 变体维度
|
||
- `size`:`default`(控件高 44px)/ `small`(控件高 36px,视觉更紧凑)
|
||
- `round`:`false`(方角)/ `true`(全圆角,用于购物车等轻量场合)
|
||
|
||
### 30.4 状态
|
||
- default:常态
|
||
- disabled:整组置灰且不可聚焦
|
||
- 边界:值等于 `min` 时减号置灰,等于 `max` 时加号置灰(按钮仍在原位,不隐藏)
|
||
|
||
### 30.5 交互与触控
|
||
- 加号与减号各自是 44px 见方的热区;`size=small` 视觉 36px 时用 `--kole-m-hit-slack` 把热区外扩回 44px
|
||
- 单击步进一个 `step`,不响应长按连击(长按是桌面习惯,触屏上容易多跳)
|
||
- 到达 `min` / `max` 时对应按钮置 `disabled` 且置灰,点击不产生值与事件
|
||
- 值变化后立即触发 change 事件,不做防抖
|
||
- `editable=true` 时值区接受键盘直接输入;非法输入(非数字、越界)在失焦时回落到边界值
|
||
|
||
### 30.6 无障碍
|
||
- 根元素 `role="group"` + `aria-label` 说明这组控件在调什么
|
||
- 减号 / 加号是原生 `button`,各自的 `aria-label` 是「减少」/「增加」(不用符号代替名称)
|
||
- 值区在只读态置 `role="spinbutton"` 并写 `aria-valuenow` / `aria-valuemin` / `aria-valuemax`
|
||
- 置灰的按钮用原生 `disabled`,读屏会播报不可用且键盘会跳过
|
||
|
||
### 30.7 doNotInvent
|
||
- 长按连击、惯性加速的时值曲线
|
||
- 小数与浮点精度(本组件只处理整数;金额请用输入框 + 数字键盘)
|
||
- 超出边界的提示文案(由宿主决定是否提示)
|
||
|
||
### 30.8 unknowns
|
||
- 值为 0 时是否自动隐藏整个步进器(购物车场景)
|
||
- 是否需要键盘上的上下方向键加减
|
||
- 单位文案是否随语言变化
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 31 · 多行文本框 Textarea
|
||
|
||
### 31.1 用途
|
||
收集可能超过一行的自由文本(备注、收货说明、退换原因);移动端与桌面端的关键差别是**高度**:文本框本身要够高(至少 3 行),因为触屏不能像桌面那样在输入过程中看到上下文;并且要给出实时字数反馈,超限时是「止写 + 报错」而不是静默截断。
|
||
|
||
### 31.2 结构(anatomy)
|
||
- `field`:字段容器,包裹文本框、计数行与错误提示
|
||
- `control`:原生 `textarea`,多行输入,行高不小于 1.5 倍字号
|
||
- `counter`:右下角字数计数(`已输入/上限`),maxlength>0 时出现
|
||
- `errorText`:字段下方的错误提示文字(与计数同行时计数不消失)
|
||
- `label`:无障碍名称(`aria-label`),textarea 的初始高度由 rows 决定
|
||
|
||
### 31.3 变体维度
|
||
- `size`:`default`(最小高度 3 行)/ `large`(最小高度 5 行,长文本场景)
|
||
- `showCounter`:`false`(不显示计数)/ `true`(右下角显示「已输入/上限」)
|
||
|
||
### 31.4 状态
|
||
- default:常态
|
||
- focus:聚焦,边框变品牌色并带 2px 聚焦外发光
|
||
- error:错误,边框变错误色且 `aria-invalid="true"`
|
||
- disabled:置灰且不可聚焦
|
||
- full:已达到 `maxlength` 上限(计数变错误色,继续输入不再增加)
|
||
### 31.5 交互与触控
|
||
- 文本框最小高度 3 行(约 96px:3 × 24px 行高 + 上下 12px 内边距);只允许纵向伸缩(`resize: vertical`),不允许横向拉宽破坏 375 宽的布局
|
||
- 计数随输入实时更新;达到上限时计数置错误色并停止接收新字符(原生 `maxlength` 兜底,超限靠宿主提示)
|
||
- 文本框整体可点即聚焦(外层不出可点装饰);错误提示与计数都在框外,不挤占输入区
|
||
- 聚焦反馈是边框色 + 2px 外发光,150ms 过渡;不改变高度
|
||
- 不使用自动增高(高度随内容跳动会让上下文错位),需要更长文本时用 `size=large`
|
||
|
||
### 31.6 无障碍
|
||
- 控件是原生 `textarea`,名称由 `aria-label` 给出(占位文字不算标签)
|
||
- 错误态用 `aria-invalid="true"` + `aria-describedby` 关联错误文案
|
||
- 计数是**参考信息**,用 `aria-live="polite"` 播报(不要每敲一个字都播报,只在接近上限时提示)
|
||
- 禁用态用原生 `disabled`,读屏会跳过
|
||
|
||
### 31.7 doNotInvent
|
||
- 自动增高(随内容撑高)的实现细节
|
||
- 富文本 / Markdown 的编辑与渲染
|
||
- 内容敏感词过滤与提交前的业务校验
|
||
|
||
### 31.8 unknowns
|
||
- `maxlength` 缺省时上限取多少(本实现默认 200)
|
||
- 是否需要在接近上限时提前变色(本实现只在到达上限时变色)
|
||
- 计数是否包含空格与换行
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 32 · 表单 Form
|
||
|
||
### 32.1 用途
|
||
把一组字段(标签 + 控件 + 错误提示)组织成一次可提交的操作(下单、开票、认证);移动端与桌面端的关键差别是**标签位置**:窄屏放不下左右两列时标签默认置顶,只有需要一组字段纵向对齐时才退回到定宽左标签,且每个字段的错误提示必须贴在它自己的控件下方 —— 触屏上用户看不到「页头汇总错误」。
|
||
|
||
### 32.2 结构(anatomy)
|
||
- `form`:根元素,包住全部字段与提交行
|
||
- `item`:单个字段,一列一个,字段之间用 1px 分隔线划分
|
||
- `label`:字段标签;`required=true` 时前面带一个错误色星号
|
||
- `star`:必填星号,装饰性(`aria-hidden`),语义由控件的 `aria-required` 承担
|
||
- `control`:字段控件(原生 `input` / `textarea`,或包一层的可点区域)
|
||
- `errorText`:字段级错误提示,落在该字段控件下方
|
||
|
||
### 32.3 变体维度
|
||
- `labelPosition`:`top`(标签在控件上方,默认)/ `left`(标签定宽 72px 与控件同行)
|
||
- `borderless`:`false`(字段之间有分隔线)/ `true`(不画线,靠留白分组)
|
||
|
||
### 32.4 状态
|
||
- default:常态
|
||
- error:字段级错误,整项加 `is-error`,控件加 `aria-invalid="true"`,下方出现错误文案
|
||
- disabled:整表置灰且不可聚焦(子控件用原生 `disabled`)
|
||
|
||
### 32.5 交互与触控
|
||
- 控件高度不小于 44px;非输入型控件(选择器一类)整行都是热区,按下反馈是整行背景变化
|
||
- 错误提示出现在控件正下方,不挤占输入区,也不跨到标签列
|
||
- 失焦即校验(触屏上软键盘收起会触发 `blur`),提交时再全量校验一次
|
||
- 校验失败时不跳转、不滚动到页首,错误就落在出错的字段上
|
||
- 提交成功后写 `data-submitted="true"` 作为可断言的提交凭证;星号与错误色不单独承载语义
|
||
|
||
### 32.6 无障碍
|
||
- 标签用 `aria-labelledby` 关联到控件(标签不是占位符的替代品)
|
||
- 必填用控件的 `aria-required="true"` 与原生 `required` 双写;星号本身 `aria-hidden`
|
||
- 错误文案用 `aria-describedby` 关联到控件,并带 `role="alert"` 播报
|
||
- 禁用态用原生 `disabled`,读屏会播报不可用且键盘会跳过
|
||
|
||
### 32.7 doNotInvent
|
||
- 字段内容的业务校验规则(正则、长度、合法性判断都在宿主,本组件只表达结构)
|
||
- 提交请求、失败重试与「提交中」的按钮态
|
||
- 字段的联动显隐与动态增删(由宿主决定渲染什么)
|
||
- 页首错误汇总条(移动端用字段级提示,不做汇总)
|
||
|
||
### 32.8 unknowns
|
||
- 左标签模式下标签列宽是否随字号变化(当前固定 72px)
|
||
- 提交行是否允许放两个并列动作(如「保存草稿 + 提交」)
|
||
- 只读(readonly)字段是否需要独立的视觉层,还是复用 disabled
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 33 · 单选框 Radio
|
||
|
||
### 33.1 用途
|
||
在一组互斥选项里选且只选一项(支付方式、配送时效、发票类型);移动端与桌面端的关键差别是**选中态的表达**:触屏没有鼠标悬停可以预告状态,选中必须同时有形状差异(圆点内圈实心)与 `aria-checked`,不能只靠颜色;且整行(圆点 + 文字)都是热区,行高不小于 44px。
|
||
|
||
### 33.2 结构(anatomy)
|
||
- `group`:根元素,`role="radiogroup"` + `aria-label` 说明这组在选什么
|
||
- `item`:单个选项,整行都是热区(原生 `button` + `role="radio"`)
|
||
- `icon`:圆点,未选中是空心环、选中是实心圆 + 内圈反色点
|
||
- `label`:选项文字,占满剩余宽度
|
||
- `desc`:可选说明行,跟在文字下方(如「需先完成企业认证」)
|
||
- `state`:选中态 `is-checked` 与 `aria-checked` 双写
|
||
|
||
### 33.3 变体维度
|
||
- `orientation`:`vertical`(纵向排列,组内画分隔线)/ `horizontal`(横向排列,靠间距分组)
|
||
- `button`:`false`(圆点 + 文字)/ `true`(胶囊标签式,无圆点)
|
||
|
||
### 33.4 状态
|
||
- default:未选中(空心环)
|
||
- checked:选中(实心圆 + 内圈反色点,`aria-checked="true"`)
|
||
- disabled:置灰且不可聚焦,读屏播报不可用
|
||
|
||
### 33.5 交互与触控
|
||
- 整行(圆点 + 文字)都是热区,行高不小于 44px;横向组里每项自身也保持这个边长
|
||
- 一次触摸即选中,不需要二次确认;选中后**不能**再点回空值(需要清空由宿主提供额外的「清除」动作)
|
||
- 互斥由组件负责:选中一项会立即清掉同组其它项的选中态,不留下两个选中
|
||
- 切换动效是圆点内圈 150ms 缩放;减少动态偏好下瞬时切换
|
||
- 选中后立即触发 change 事件,回传该项的值
|
||
|
||
### 33.6 无障碍
|
||
- 组用 `role="radiogroup"` + `aria-label` 说明分组名称
|
||
- 每项是原生 `button` + `role="radio"` + `aria-checked`(读屏会播报「单选按钮,已选中/未选中」)
|
||
- 圆点与内圈是纯装饰,对读屏隐藏(`aria-hidden`),语义全靠 `role="radio"`
|
||
- 禁用项用原生 `disabled` 并补 `aria-disabled="true"`
|
||
|
||
### 33.7 doNotInvent
|
||
- 「取消选中」的回到空值交互(单选的语义就是必有一项)
|
||
- 横向组自动换行的列数策略(由宿主按文案长度决定,组件只负责 flex-wrap)
|
||
- 与表单一起提交时的隐藏字段(由宿主添加)
|
||
- 选项内容的异步加载与搜索过滤
|
||
|
||
### 33.8 unknowns
|
||
- 横向组超过一行时是否改成纵向(当前包裹后当行处理)
|
||
- 圆点尺寸是否随字号一起放大(当前固定 20px)
|
||
- `desc` 说明行是否允许两行以上(当前单行省略)
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 34 · 多选框 Checkbox
|
||
|
||
### 34.1 用途
|
||
在一组选项里同时选中任意多项(兴趣标签、订阅范围、筛选维度),并支持「全选」;移动端与桌面端的关键差别是**半选态的表达**:触屏上没有鼠标悬停预告状态,全选行在「部分选中」时必须显示出与「全选 / 全不选」都不同的第三态(横杠),且整行(方框 + 文字)都是热区,行高不小于 44px。
|
||
|
||
### 34.2 结构(anatomy)
|
||
- `group`:根元素,`role="group"` + `aria-label` 说明这组在选什么
|
||
- `item`:单个选项,整行都是热区(原生 `button` + `role="checkbox"`)
|
||
- `icon`:方框,未选中是空心框、选中是品牌底 + 反色勾号、半选是品牌底 + 横杠
|
||
- `label`:选项文字,占满剩余宽度
|
||
- `desc`:可选说明行,跟在文字下方(如「需先绑定手机号」)
|
||
- `all`:全选行,不存储自己的状态,由组内各项推导
|
||
|
||
### 34.3 变体维度
|
||
- `orientation`:`vertical`(纵向排列,组内画分隔线)/ `horizontal`(横向排列,靠间距分组)
|
||
- `button`:`false`(方框 + 文字)/ `true`(胶囊标签式,无方框)
|
||
- `selectAll`:`false`(不显示全选行)/ `true`(组首显示全选行)
|
||
|
||
### 34.4 状态
|
||
- default:未选中(空心框)
|
||
- checked:选中(品牌底 + 反色勾号,`aria-checked="true"`)
|
||
- indeterminate:半选,只在全选行出现(`aria-checked="mixed"`)
|
||
- disabled:置灰且不可聚焦,读屏播报不可用
|
||
|
||
### 34.5 交互与触控
|
||
- 整行(方框 + 文字)都是热区,行高不小于 44px;横向组里每项自身也保持这个边长
|
||
- 一次触摸即切换,各项互相独立;不限制同时选中的数量上限
|
||
- 点全选行:只要还有未选中项就全部选中,否则全部清空(不在「半选」上停留)
|
||
- 全选的选中态是**推导值**,不单独存储 —— 单项变化后立即重算,避免出现「全选已勾上但还有一项没选」
|
||
- 切换动效是勾号 150ms 缩放;减少动态偏好下瞬时切换
|
||
- 每次切换立即触发 change 事件,回传切换后的完整值数组
|
||
|
||
### 34.6 无障碍
|
||
- 组用 `role="group"` + `aria-label` 说明分组名称
|
||
- 每项是原生 `button` + `role="checkbox"` + `aria-checked`(读屏会播报「复选框,已选中/未选中」)
|
||
- 全选行的半选态用 `aria-checked="mixed"`(checkbox 角色允许的第三个值)
|
||
- 方框与勾号是纯装饰,对读屏隐藏(`aria-hidden`),语义全靠 `role="checkbox"`
|
||
- 禁用项用原生 `disabled` 并补 `aria-disabled="true"`
|
||
|
||
### 34.7 doNotInvent
|
||
- 「最多选 N 项」的数量上限与超出提示(业务规则在宿主)
|
||
- 分组嵌套(一组里再分组)的层级表达
|
||
- 与表单一起提交时的隐藏字段(由宿主添加)
|
||
- 选项内容的异步加载与「已选 N 项」的汇总条
|
||
|
||
### 34.8 unknowns
|
||
- 全选行是否显示「已选 2/5」这类计数
|
||
- 半选态在非全选行上的用例(如父级节点)
|
||
- 胶囊按钮式是否也需要禁用态的视觉层(当前复用同一条置灰规则)
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 35 · 滑动选择器 Slider
|
||
|
||
### 35.1 用途
|
||
在一个连续区间里用一次滑动选出数值(预算上限、价格区间、音量、亮度);移动端与桌面端的关键差别是**热区**:轨道可视高度只有 4px,但整条轨道所在的行必须是 44px 高的热区,且按下任意位置就要跳到该处 —— 触屏上要求用户精确抓住 20px 的滑块是点不中的。
|
||
|
||
### 35.2 结构(anatomy)
|
||
- `slider`:根元素,一行里放进「轨道热区 + 数值」
|
||
- `rail`:轨道热区,44px 高的可聚焦区域,承载指针事件与锚点
|
||
- `track`:轨道本体(可视 4px),承载选中段与刻度的边界
|
||
- `filled`:选中段,从起点铺到滑块位置
|
||
- `thumb`:滑块,位置由当前值的百分比决定;拖动中放大作为「抓住了」的反馈
|
||
- `mark`:刻度点,仅 `marks=with-marks` 时出现(只标位置,不写文案)
|
||
|
||
### 35.3 变体维度
|
||
- `mode`:`single`(单滑块)/ `range`(双滑块,左值不越过右值)
|
||
- `marks`:`with-marks`(轨道上标出等分点)/ `none`(不标刻度)
|
||
- `showValue`:`true`(右侧显示当前值)/ `false`(不显示数值)
|
||
|
||
### 35.4 状态
|
||
- default:常态
|
||
- dragging:拖动中(滑块 20px → 24px,轨道加一层浅底)
|
||
- disabled:整条置灰且不响应,指针与键盘都不改值
|
||
|
||
### 35.5 交互与触控
|
||
- 轨道热区高 44px,**按下任意位置即跳到该处**,随后横向拖动连续改值;不要求抓住滑块
|
||
- 手势是「按下 + 拖动」一次连续动作,不响应长按或双击;横向分量优先,纵向滚动不被吞掉(`touch-action: pan-y`)
|
||
- 取值按 `step` 吸附并夹在 `[min, max]`;`mode=range` 时左值不越过右值、右值不越过左值
|
||
- 拖动期间滑块放大到 24px,松手后 150ms 回弹;减少动态偏好下瞬时切换
|
||
- 值变化立即触发 change 事件(连续回传),不做防抖;松手时才做业务提交
|
||
|
||
### 35.6 无障碍
|
||
- 单滑块:轨道热区是 `role="slider"` 且可聚焦,写 `aria-valuemin` / `aria-valuemax` / `aria-valuenow` / `aria-label`
|
||
- 双滑块:外层是 `role="group"` + `aria-label`,每个滑块各自持有 `aria-valuenow`(两条独立数值)
|
||
- 禁用时写 `aria-disabled="true"`,读屏会播报不可用
|
||
- 键盘:聚焦后左右方向键按 `step` 步进(触屏之外的替代路径)
|
||
|
||
### 35.7 doNotInvent
|
||
- 数值输入框与滑块的双向绑定(宿主自行组合 Input,本组件只回传值)
|
||
- 刻度上的文案标注与区间高亮(只标位置,不写文案)
|
||
- 拖动结束后的「撤销 / 重置」流程
|
||
- 纵向滑块与圆形表盘式选择器
|
||
|
||
### 35.8 unknowns
|
||
- 双滑块的最小间距是否应当是 1 个 step(当前是),还是用户可配
|
||
- 刻度数量是否应由 `step` 推导(当前按 5 等分固定标点)
|
||
- 拖动中是否要把当前值放大显示在滑块上方(气泡提示)
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 36 · 评分 Rate
|
||
|
||
### 36.1 用途
|
||
让用户对一次体验给出星级评价(商品、物流、服务),或只读展示已有的平均分;移动端与桌面端的关键差别是**没有悬停预览**:桌面上鼠标划过就能预告「点下去会是几分」,触屏上这个预告不存在,所以填充比例(含半星)必须直接可读,且星形本体只有 24px、热区要补到 44px 高,否则手指点不准相邻的两颗星。
|
||
|
||
### 36.2 结构(anatomy)
|
||
- `rate`:根元素,一行里放进「星组 + 数值文案」
|
||
- `stars`:星组,横向排列,只负责布局
|
||
- `star`:单颗星,可点区域(原生 `button`),自身撑满 44px 高
|
||
- `glyph`:星形本体(视觉 24px),由灰底星 + 品牌色覆盖层叠成
|
||
- `fill`:覆盖层,宽度即该颗星的填充比例(100% 整星 / 50% 半星 / 0% 未选)
|
||
- `text`:数值文案,默认「N 分」,可由默认插槽替换
|
||
|
||
### 36.3 变体维度
|
||
- `size`:`default`(星形 24px)/ `small`(星形 18px,行内展示用)
|
||
- `allowHalf`:`false`(只能取整星)/ `true`(允许半星)
|
||
- `readonly`:`false`(可评分)/ `true`(只读展示已有评分)
|
||
|
||
### 36.4 状态
|
||
- default:未评分(全部灰底星)
|
||
- selected:已评分(填充比例 = 所选分值与满分的比例)
|
||
- disabled:置灰且不可聚焦,点击不改值
|
||
|
||
### 36.5 交互与触控
|
||
- **取值规则**:点第 N 颗星取 N 分;`allowHalf=true` 时点第 N 颗星的**左半**取 N-0.5 分、**右半**取 N 分(半星只在开启该项时存在,默认整星)
|
||
- 星形本体视觉 24px(`size=small` 时 18px),但每颗星的可点区域高度是 44px;横向不做死区,相邻星的边界就是两半分界
|
||
- 一次触摸即取值,不需要二次确认;不响应长按与拖动(拖动选分在触屏上容易滑错,规格未纳入)
|
||
- 已选中的部分用**填充比例**表达(半星就是左半填充),不能只靠颜色深浅区分
|
||
- 取值后立即触发 change 事件(回传 3.5 这类半星值也是合法输入)
|
||
- `readonly=true` 时不渲染任何可点区域(只读展示不抢键盘序列)
|
||
|
||
### 36.6 无障碍
|
||
- 可评分形态:根是 `role="radiogroup"` + `aria-label`,每颗星是原生 `button` + `role="radio"` + `aria-checked`
|
||
- 星形的名称是「N 星」(`aria-label`),读屏播报的是分值而不是符号
|
||
- 只读形态:整组是一个 `role="img"` + `aria-label`(「评分 4.5 分(满分 5 分)」),组内星形 `aria-hidden`
|
||
- 禁用态用原生 `disabled` 并补 `aria-disabled="true"`
|
||
- 键盘:聚焦后左右方向键步进(整星模式步长 1,半星模式步长 0.5)
|
||
|
||
### 36.7 doNotInvent
|
||
- 评分的业务含义映射(如「4 分以上算好评」)与统计口径
|
||
- 拖动选分与悬停预览(触屏没有悬停,拖动容易滑错)
|
||
- 评分理由 / 标签的联动采集(由宿主另行组合)
|
||
- 异步提交与失败回滚(宿主负责)
|
||
|
||
### 36.8 unknowns
|
||
- 分值是否允许与文案一一对应(如 1 分「很差」、5 分「很好」)
|
||
- 星形换用图标字体或 SVG 后填充比例的表达是否仍然一致
|
||
- 只读形态是否要显示评价条数(当前由宿主用插槽补)
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 37 · 排版 Typography
|
||
|
||
### 37.1 用途
|
||
把一段文字按信息层级(标题 / 正文 / 辅助 / 次要)成套地表达,并可选单行省略与一键复制。移动端与桌面端的关键差别是**正文基准字号**:桌面端正文 14px,移动端 16px(`--kole-m-font-size-body`),且层级只有四级 —— 手机屏幕上再细分「大标题 / 中标题 / 小标题」会让层级差异小于字号可辨阈值,用户只能看到「一堆差不多大的字」。
|
||
|
||
### 37.2 结构(anatomy)
|
||
- `typography`:根元素,横向承载「文字 + 可选复制按钮」
|
||
- `text`:文字本体,层级、省略、换行都在它身上生效;默认插槽与 `text` prop 二者取一(`text` 优先)
|
||
- `copy`:复制按钮(原生 `button`),仅 `copyable=true` 时出现;热区 44px,负外边距吸收不撑高行
|
||
|
||
### 37.3 变体维度
|
||
- `level`:`title`(标题 17px / 600)/ `body`(正文 16px / 400)/ `assist`(辅助 14px / 次级色)/ `secondary`(次要 11px / 三级色)
|
||
- `ellipsis`:`false`(按容器换行)/ `true`(单行省略,省略号在行尾)
|
||
- `copyable`:`false`(纯展示)/ `true`(右侧出现复制按钮)
|
||
|
||
### 37.4 状态
|
||
- default:常态
|
||
- copied:`is-copied` —— 复制成功后的反馈(按钮文字换成「已复制」且转成功色),由宿主在写剪贴板后加上
|
||
|
||
### 37.5 交互与触控
|
||
- 组件本体是**静态**的:根与文字节点不带指针语义,也不进键盘序列(读屏只读文字)
|
||
- 唯一的交互元素是复制按钮:热区 44×44px(`--kole-m-touch-target`),一次轻点触发 `copy` 事件
|
||
- **复制动作由宿主完成**:组件不读系统剪贴板(Web 端 `navigator.clipboard` 需要用户手势与安全上下文,小程序端完全没有这个 API,跨端无法统一),组件只回传事件并在宿主置 `copied=true` 后渲染反馈
|
||
- `ellipsis=true` 时只做单行省略:被截断的内容不提供展开入口,长内容应改用多行展示或详情页
|
||
- 层级只表达信息次序,不做任何按数值/长度自动降级的推断
|
||
|
||
### 37.6 无障碍
|
||
- 根是普通容器,文字由读屏按文档流朗读;层级只改视觉,**不改语义标签**(是否用 `h1`/`p` 由宿主决定,组件不替业务决定文档大纲)
|
||
- 省略形态下读屏仍能读到完整文字(`text-overflow: ellipsis` 只截视觉,不改可访问名)
|
||
- 复制按钮是原生 `button` 且带 `aria-label`,名称随状态在「复制」与「已复制」之间切换
|
||
- 复制结果是异步确认的视觉反馈,因此不额外加 `aria-live`(避免与页面其它播报抢读)
|
||
|
||
### 37.7 doNotInvent
|
||
- 富文本与 Markdown 渲染(换行、加粗、链接、代码块一律由宿主负责)
|
||
- 多行省略(`line-clamp`)与「展开全文」交互
|
||
- 字号缩放 / 用户字号偏好档位
|
||
- 复制失败的兜底提示(宿主自行组合轻提示)
|
||
|
||
### 37.8 unknowns
|
||
- `secondary` 层级是否应使用 11px(当前取 `--kole-m-font-size-caption`)而不是 12px
|
||
- 复制按钮在「已复制」态停留多久(当前不自动回退,由宿主控制)
|
||
- 省略态是否需要在长按气泡里显示全文
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 38 · 分段器 Segmented
|
||
|
||
### 38.1 用途
|
||
在 2~5 个**互斥**选项里选一个,并让「当前选的是哪个」一眼可见(订单状态、时间范围、列表/网格视图切换)。移动端与桌面端的关键差别是**没有悬停**:桌面端用户把鼠标移到未选项上就能预览「这里可以点」,触屏上这个预告不存在 —— 所以选中项必须靠**形态**(白底滑块 + 阴影)而非色相区分,且每一项都要占满 44px 高,否则 2~5 个选项挤在一行时手指点不准。
|
||
|
||
### 38.2 结构(anatomy)
|
||
- `segmented`:容器,浅底 + 内边距,横向排列选项;`role="radiogroup"` 并带 `aria-label` 说明这组在选什么
|
||
- `item`:单个选项,原生 `button` + `role="radio"`(整项可点、可键盘聚焦),`data-value` 携带取值
|
||
- `label`:选项文字(可含装饰图标,图标 `aria-hidden` 由文字承担语义)
|
||
|
||
### 38.3 变体维度
|
||
- `size`:`default`(44px 高)/ `small`(32px 高,卡片内次级筛选用)
|
||
- `block`:`false`(宽度随内容)/ `true`(占满容器且各项等分)
|
||
|
||
### 38.4 状态
|
||
- default:未选中(透明底 + 次级字色)
|
||
- selected:选中(`is-active` —— 卡片底滑块 + 品牌字色 + `aria-checked="true"`)
|
||
- disabled:整段置灰、不响应点击;单个选项也可单独禁用
|
||
|
||
### 38.5 交互与触控
|
||
- 选项切换是**一次轻点**:点击后该项 `is-active`、同组其它项复位;不响应长按、双击与拖动(横滑切换属于标签栏手势,不在本组件)
|
||
- 选中态是视觉与属性的**双向同步**:除类名外必须同时更新 `aria-checked`,只改颜色不改属性会被无障碍判为缺口
|
||
- 每项热区高 ≥ 44px(`size=small` 时视觉 32px,但**触摸命中区仍按 44px 计**,纵向不留死区)
|
||
- 选中项再次点击**不重复触发** `change`(值未变不发事件,避免宿主收到同值事件后做无意义的重渲染)
|
||
- 受控:组件不存值,只回传目标值 `change`;宿主不采纳时视觉不变化
|
||
- 切换动效 120ms(`--kole-duration-fast`),`prefers-reduced-motion` 下瞬时切换
|
||
|
||
### 38.6 无障碍
|
||
- 容器 `role="radiogroup"` + `aria-label`;每个选项原生 `button` + `role="radio"` + `aria-checked`
|
||
- 选项名称由文字承担;装饰性图标必须 `aria-hidden="true"`
|
||
- 禁用时容器补 `aria-disabled="true"`,选项用原生 `disabled`(读屏播报不可用,且不进 Tab 序列)
|
||
- 键盘:Tab 进入分组,左右方向键在选项间移动并选中(原生 radio 组的键盘约定)
|
||
|
||
### 38.7 doNotInvent
|
||
- 多选(同时选中多个)—— 需要多选时改用标签组或多选框
|
||
- 选项的横向滚动、换行与「更多」折叠(超过 5 个应换组件)
|
||
- 选中项的下划线滑块动画(那是标签栏的视觉语言,不是分段器)
|
||
- 选项禁用条件与业务权限的判断
|
||
|
||
### 38.8 unknowns
|
||
- 选项数量上限是否应硬约束在 5 个(当前只写建议,不做运行时拦截)
|
||
- `size=small` 的命中区是否需要在纵向自动补到 44px(当前靠 `--kole-m-hit-slack` 思路,未在样式中强制)
|
||
- 是否需要「滑动经过即选中」(当前只认轻点,滑动不选中)
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 39 · 吸顶容器 Sticky
|
||
|
||
### 39.1 用途
|
||
让一段内容在滚动时**贴住滚动容器的边缘**保持可见(列表标题、分组、购物车合计条)。移动端与桌面端的关键差别是**滚动惯性**:桌面端滚动是离散的滚轮步进,用「监听 scroll + `position: fixed`」的 JS 方案看不出问题;触屏上滚动是带惯性的连续位移,`fixed` 方案会在惯性阶段出现一帧的抖动与跳位,且元素脱离文档流后原位置塌陷、必须手动补一个占位元素 —— 所以这里用 CSS 原生的 `position: sticky`,贴合由浏览器在合成层完成,不监听滚动。
|
||
|
||
### 39.2 结构(anatomy)
|
||
- `sticky`:根元素,**就是滚动内容流里的那一行**(sticky 不脱离文档流,因此不需要占位元素)
|
||
- `title`:标题文字,单行省略,占满剩余宽度
|
||
- `extra`:右侧附加说明(数量、合计等),可选
|
||
- `action`:右侧动作按钮(原生 `button`,热区 44px),可选
|
||
|
||
### 39.3 变体维度
|
||
- `position`:`top`(贴顶,最常用)/ `bottom`(贴底,合计条一类)
|
||
- `safeArea`:`false`(只用偏移量)/ `true`(偏移量再叠加刘海或底部横条安全区)
|
||
- `shadow`:`false`(恒定无投影)/ `true`(**仅吸顶后**才有投影,用于区分「贴住了」与「还在流里」)
|
||
|
||
### 39.4 状态
|
||
- default:未吸顶,正常参与文档流
|
||
- stuck:`is-stuck` —— 已贴合边缘(加下边框 / 投影)。**判定由宿主负责**:贴合时机只有浏览器知道,组件不监听滚动
|
||
|
||
### 39.5 交互与触控
|
||
- 贴合位置由组件级变量 `--kole-m-sticky-offset` 决定,默认等于导航栏高度;业务侧覆盖它即可适配自有导航(贴底用法通常覆盖为 `0px`)
|
||
- `safeArea=true` 时偏移叠加 `--kole-m-safe-top` / `--kole-m-safe-bottom`:支持 `env()` 的浏览器拿到真实刘海高度,不支持的拿到 `0px`(不会因非法值整条声明被丢弃)
|
||
- **组件本身不监听滚动**:`is-stuck` 由宿主按滚动位置切换。原因有两条 —— ① 贴合判定与滚动容器强绑定,组件无法知道自己在哪个容器里;② 逐帧读写滚动位置在移动端会挤占合成线程,原生 sticky 已经把这件事交给浏览器
|
||
- 触屏热区:整条高度 ≥ 44px;右侧动作按钮自身撑满 44px,负外边距吸收不撑高栏体
|
||
- `position=bottom` 时吸顶后的描边换到**上边**(视觉上仍是「靠内容那一侧」的分隔)
|
||
- 不响应长按、双击与横滑:吸顶条不是可拖拽元素(拖拽排序不在本组件范围内)
|
||
|
||
### 39.6 无障碍
|
||
- 根是普通容器,标题文字正常参与读屏朗读;`is-stuck` 是纯视觉增强,不添加任何 `aria-*`(吸顶不是状态变化,播报它只会打断用户)
|
||
- 右侧动作是原生 `button`,名称由可见文字承担;装饰性图形必须 `aria-hidden="true"`
|
||
- 吸顶不改变文档顺序:读屏与键盘的遍历顺序与未吸顶时完全一致(这也是不用 `fixed` 的另一个理由 —— `fixed` 会它把挪出正常流,键盘聚焦时的滚动定位会跳)
|
||
- `prefers-reduced-motion` 下不引入任何过渡(组件本身不加动效,此处显式声明避免宿主样式穿透)
|
||
|
||
### 39.7 doNotInvent
|
||
- 吸顶触发的位移/缩放动画(如标题从大字缩成小字)—— 规格只定义了贴合,没有定义形态演变
|
||
- 多个吸顶条的层叠顺序与相互推挤(层叠上下文规则由宿主决定)
|
||
- 进入/离开视口时的埋点事件与曝光统计
|
||
- 拖拽排序与吸附
|
||
|
||
### 39.8 unknowns
|
||
- 吸顶判定是否应由组件内部提供一个可选的滚动监听辅助(当前完全交给宿主)
|
||
- 是否需要在吸顶时自动隐藏相邻内容(当前不做,靠宿主布局)
|
||
- 贴底形态在内容不足一屏时是否应始终贴底(当前 sticky 语义下会贴着容器底,未做额外分支)
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 40 · 遮罩层 Overlay
|
||
|
||
### 40.1 用途
|
||
浮层的**基座**:在内容之上盖一层半透明遮罩,让下层内容退到背后(对话框、抽屉、图片预览、卡片加载态)。它与弹出层的分工是:弹出层自带方向位移与开合动画、是「能独立使用的完整浮层」;遮罩层只提供「变暗 + 拦手势 + 承载任意内容」三件事,属于被别的浮层复用的底座。移动端与桌面端的关键差别是**手势拦截面**:触屏上遮罩必须真的吃掉触摸事件(否则惯性滚动会从遮罩底下穿过去,把下层页面滚走),而桌面端只需要处理鼠标点击。
|
||
|
||
### 40.2 结构(anatomy)
|
||
- `overlay`:根元素,定位容器与开合开关;`role="presentation"`,自身不承担语义
|
||
- `scrim`:遮罩面(原生 `button`),`aria-hidden="true"` 且 `tabindex="-1"` —— 可点但不进键盘序列
|
||
- `content`:内容容器(默认插槽落点),语义由宿主决定(对话框给 `role="dialog"`、面板给 `role="region"`)
|
||
|
||
### 40.3 变体维度
|
||
- `tone`:`default`(普通遮罩)/ `strong`(浓遮罩,用于需要专注的确认)/ `blur`(叠加背景模糊,用于图片预览)
|
||
- `contained`:`false`(固定全屏)/ `true`(绝对定位填充最近的定位祖先,做卡内局部遮罩)
|
||
|
||
### 40.4 状态
|
||
- closed:收起(`opacity: 0` + `pointer-events: none`,视觉与手势同时让开)
|
||
- open:展开(`is-open`;`pointer-events: auto` 拦下所有手势)
|
||
|
||
### 40.5 交互与触控
|
||
- 一次轻点遮罩即关闭;`closeOnMask=false` 时不关闭(填到一半的表单不该因为一次误触丢数据)
|
||
- 遮罩关闭的**键盘路径不落在遮罩上**:遮罩显式 `tabindex="-1"`,键盘用户靠内容里的关闭按钮(与宿主实现的 Esc)关闭 —— 让 Tab 停在遮罩上会让读屏读到一个没有名称的元素
|
||
- `lockScroll=true` 时宿主应锁住下层滚动(组件在根上写 `data-lock-scroll` 作为标记,实际的 `overflow: hidden` 由宿主执行:组件不该直接改 `document.body.style`,那会污染宿主状态并在多层浮层叠加时互相踩踏)
|
||
- 开合动效 240ms(`--kole-m-duration-slide`),`prefers-reduced-motion: reduce` 下瞬时切换
|
||
- `contained=true` 时要求宿主祖先链上存在定位元素(`position: relative` 一类),否则会向上找到视口
|
||
|
||
### 40.6 无障碍
|
||
- 遮罩面 `aria-hidden="true"`(纯装饰)且 `tabindex="-1"`(不进键盘序列)
|
||
- 根 `role="presentation"`:容器本身不产生语义,避免读屏把它当成一个空的分组
|
||
- 内容语义完全由宿主提供:焦点陷阱、`aria-modal`、初始焦点与关闭后焦点归还都属于宿主职责(本组件是基座,不替业务决定这些策略)
|
||
- 遮罩关闭不依赖颜色:开合只改透明度且有 240ms 过渡,减少动态偏好下瞬时切换
|
||
|
||
### 40.7 doNotInvent
|
||
- 焦点陷阱(focus trap)与初始焦点策略
|
||
- 多层遮罩的层叠顺序管理
|
||
- 手势下滑关闭与拖拽阻尼
|
||
- 滚动锁定的实现细节(组件只写标记,改 DOM 由宿主做)
|
||
|
||
### 40.8 unknowns
|
||
- `blur` 档在低端机上的性能开销是否可接受(未做降级探测)
|
||
- `contained=true` 时是否应自动为宿主补 `position: relative`(当前要求宿主自己保证)
|
||
- 是否要支持「点遮罩不关但双击关」这类折中策略
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 41 · 弹出气泡 Popover
|
||
|
||
### 41.1 用途
|
||
在**某个元素的旁边**弹出一小块说明或轻量操作(运费规则、字段解释、更多操作),说完就收。移动端与桌面端的关键差别是**触发方式与关闭路径**:桌面端靠 hover 弹出、移开即收;触屏没有悬停,只能点击展开,而展开后「怎么收起来」变成了真问题 —— 桌面上移开鼠标就收了,触屏必须显式给一条关闭路径(点击气泡之外)。这也是它与轻提示 / 遮罩层的根本区别:气泡是**相对某个触发元素**定位的,不是铺满视口的浮层。
|
||
|
||
### 41.2 结构(anatomy)
|
||
- `popover`:根元素,`position: relative` 的包一层,气泡在其中绝对定位(因此永远贴着自己的触发器)
|
||
- `trigger`:触发器(原生 `button`),热区 ≥44px,写 `aria-expanded` 与 `aria-haspopup`
|
||
- `panel`:气泡面板,按 `placement` 贴着触发器的某一侧;`role="dialog"` 并带 `aria-label`
|
||
- `title` / `text`:标题与正文,可选
|
||
- 箭头:`arrow=true` 时由纯 CSS 三角(border 拼出,无图片、无 hex)指向触发器
|
||
|
||
### 41.3 变体维度
|
||
- `placement`:`top` / `bottom` / `left` / `right`(气泡相对触发器出现在哪一侧)
|
||
- `arrow`:`false`(不带三角,小屏空间紧张时用)/ `true`(三角指向触发器)
|
||
|
||
(`closeOnOutside` 是行为开关而不是形态维度,见 §41.5:它只改变关闭路径,不改变任何视觉。)
|
||
|
||
### 41.4 状态
|
||
- closed:收起(`opacity: 0` + `visibility: hidden` + `pointer-events: none`,不可见也不可点)
|
||
- open:展开(`is-open`;触发器同步 `aria-expanded="true"` 并转为品牌色描边)
|
||
|
||
### 41.5 交互与触控
|
||
- 一次轻点触发器展开 / 收起,**同一个按钮负责开与关**(触屏没有「移开鼠标」这条隐式关闭路径)
|
||
- 点击外部关闭:落点不在「气泡或触发器」之内时收起。气泡**没有遮罩可依赖**(有遮罩就成了弹出层),判定靠监听文档级 `pointerdown`;uni-app 端没有 `document`,改用铺满视口的透明捕获层(层级低于面板)
|
||
- 不响应长按、双击与拖动;气泡自身不消费纵向滚动 —— 内容超长应改用弹出层,不在气泡里做内部滚动
|
||
- 气泡与触发器之间留 `--kole-m-popover-gap`(默认 8px)的间隙,避免气泡盖住触发器的按下反馈
|
||
- 开合动效 120ms(`--kole-duration-fast`),减少动态偏好下瞬时切换
|
||
- 边缘空间不足时的翻转(flip)由宿主决定:把 `placement` 换成对侧即可,组件不做自动测量
|
||
|
||
### 41.6 无障碍
|
||
- 触发器是原生 `button`,带 `aria-expanded`(读屏能播报「已展开 / 已折叠」)与 `aria-haspopup`
|
||
- 面板 `role="dialog"` + `aria-label`:名称取 `title`,为空时退回触发器文字,保证读屏不会读到一个匿名对话框
|
||
- 弹出时**不移动焦点**(气泡是补充说明而非中断式浮层,抢焦点会让用户丢失当前输入位置);键盘用户用 Tab 进入气泡内容,用 Esc 或再点一次触发器收起
|
||
- 气泡不因展开而隐藏任何内容:关闭后触发器仍在原地可再次打开
|
||
|
||
### 41.7 doNotInvent
|
||
- 基于可用空间的自动翻转与自动方位选择(`placement` 由宿主决定)
|
||
- 悬停触发(触屏没有悬停;桌面端若需要,由宿主包装)
|
||
- 气泡内的表单校验与提交流程
|
||
- 气泡之间的互斥开关(哪个开着由宿主管理)
|
||
|
||
### 41.8 unknowns
|
||
- 是否需要在气泡贴近视口边缘时自动夹在边界内(当前只做静态定位)
|
||
- `role="dialog"` 对纯文字说明类气泡是否过重(当前统一用 dialog + aria-label)
|
||
- 点击外部关闭是否需要区分「点了另一个气泡」的情况(当前点另一个气泡会同时收起前一个)
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 42 · 消息通知 Message
|
||
|
||
### 42.1 用途
|
||
在页面**顶部**给一条不打断操作的结果提示(提交成功、网络异常、库存告警、操作回执)。移动端与桌面端的关键差别是**不阻断**:桌面端顶部提示常做成整条横幅、推进页面布局;触屏上页面本身就是有限的可视区域,横幅会把内容挤下去并引发重排,所以这条消息**浮在内容之上**(`position: fixed`)且**不吃手势**(层自身 `pointer-events: none`),用户读完继续操作,页面不跳。它与轻提示 Toast 的分工是:Toast 占据视口中央、用于「结果就是你此刻唯一关心的事」;Message 贴在顶部一条、用于「结果要告诉你,但不该拦着你」。
|
||
|
||
### 42.2 结构(anatomy)
|
||
- `message`:消息层根元素,顶部固定的纵向列表容器;`pointer-events: none` 让下方页面照常可点
|
||
- `item`:单条消息,一行「图标 + 文字(+ 可选关闭)」,带 `role="status"` 与 `aria-live="polite"`
|
||
- `icon`:语气图标(装饰),`aria-hidden="true"`,语义由文字承担
|
||
- `text`:消息文字,超长换行,不截断
|
||
- `close`:可选的关闭按钮(原生 `button`,热区 44px)
|
||
|
||
### 42.3 变体维度
|
||
- `tone`:`info`(信息)/ `success`(成功)/ `warning`(警告)/ `error`(错误)—— 图标字形与侧边描边同族,文字保持正文色
|
||
- `closable`:`false`(读完自己消失)/ `true`(右侧出现关闭按钮)
|
||
|
||
### 42.4 状态
|
||
- closed:收起(`opacity: 0` + 上移 100%,不可见)
|
||
- open:展开(`is-open`,滑下淡入 240ms)
|
||
|
||
### 42.5 交互与触控
|
||
- **两种用法,API 只暴露组件形态**:
|
||
- **组件形态**(本组件提供的接口):受控 `open` + 内容 props;`duration` 到点回传 `close`,由宿主决定是否收起
|
||
- **命令式调用**(宿主侧组装):宿主维护一个消息数组与定时器,把 `open` / `tone` / `text` 逐个喂给组件实例 —— 命令式 API 需要单例容器、跨端定时器与「销毁后仍在计时的定时器」治理,属宿主或框架层职责,组件不提供全局方法(规格 §42.7)
|
||
- 消息层不吃手势(根 `pointer-events: none`),**只有关闭按钮自己**接收手势(`pointer-events: auto`)——因此顶部有消息时,下方页面仍可正常点击与滚动
|
||
- 自动消失时长由**宿主**决定:组件没有默认时长之外的隐式行为,`duration=0` 表示不自动关闭(需要用户读完的长文案)
|
||
- 入场从上滑下并淡入 240ms(`--kole-m-duration-slide`),减少动态偏好下瞬时切换
|
||
- 多条并列时纵向排列,新的追加在下方;**同屏条数上限与超出后的合并策略由宿主决定**(规格 §42.7)
|
||
|
||
### 42.6 无障碍
|
||
- 每条消息 `role="status"` + `aria-live="polite"`:读屏朗读一次,不打断用户当前操作(不用 `assertive`,那会打断朗读)
|
||
- 语气图标 `aria-hidden="true"`,**语义全部由文字承担**(颜色不是唯一的信息通道:图标字形与描边同时变化)
|
||
- 关闭按钮是原生 `button` 并带 `aria-label`(「关闭消息」),可用 Tab 聚焦、Enter 触发
|
||
- 消息不抢焦点:出现时不移动焦点,用户正在输入的内容不受影响
|
||
|
||
### 42.7 doNotInvent
|
||
- 自动关闭的默认时长与「超时后是否保留」的策略
|
||
- 多条消息的排队、合并、去重与同屏上限
|
||
- 命令式全局方法(`Message.success()` 这类)与其单例容器
|
||
- 消息内的操作按钮与跳转链接(需要动作时请用通知栏或对话框)
|
||
|
||
### 42.8 unknowns
|
||
- `tone=warning` 与 `error` 是否需要不同的停留时长(当前统一由宿主传 `duration`)
|
||
- 顶部多条同时出现时是否该限制为最多两条(当前不限,由宿主控制)
|
||
- 是否需要在消息层上提供「点整条跳详情」的交互(当前只有可选的关闭按钮可点)
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 43 · 选择器 Picker
|
||
|
||
### 43.1 用途
|
||
从一组**有限且已知**的选项里选出一项或几项(城市、分值、时间),是「表单里那个下拉框」在触屏上的形态;移动端与桌面端的关键差别是**没有悬停、也没有空格去展开**:下拉框在手机上只能变成从底部升起的滚轮浮层,手指点选即高亮,并且必须有明确的「确定 / 取消」来收口 —— 桌上点一下就走、触屏上误触代价大,用户需要一次反悔的机会。可绑定到 Popup 的底部形态(`position: bottom`),但本组件自带遮罩与面板,不要在弹出层里再套一层。
|
||
|
||
### 43.2 结构(anatomy)
|
||
- `mask`:遮罩,点击关闭(`closeOnMask=false` 时不关)
|
||
- `picker`:底部浮层面板,`role="dialog"` + `aria-modal="true"`
|
||
- `picker__header`:取消 / 标题 / 确定三格,标题元素由 `aria-labelledby` 指向
|
||
- `picker__columns`:列容器,`mode=single` 一列、`mode=multiple` 多列等分
|
||
- `picker__column`:单列,`role="listbox"` + `aria-label` 说明这列在选什么,可滚动
|
||
- `picker__option`:单个选项,`role="option"` + `aria-selected`,行高不小于 44px
|
||
|
||
### 43.3 变体维度
|
||
- `mode`:`single`(单列滚轮)/ `multiple`(多列滚轮,各列独立选中;是否联动由宿主的选项决定)
|
||
- `round`:`false`(直角)/ `true`(靠内容一侧切圆角)
|
||
|
||
### 43.4 状态
|
||
- default:常态
|
||
- open:浮层展开(遮罩可点、面板滑入)
|
||
- closed:收起态(遮罩 `pointer-events: none`,页面可正常滚动与点击)
|
||
- selected:当前选中项,`is-selected` 与 `aria-selected="true"` 同步表达
|
||
- disabled:整块或单个选项置灰且不响应,写 `aria-disabled="true"`(整块禁用用状态类 `is-disabled`)
|
||
|
||
### 43.5 交互与触控
|
||
- 遮罩点击关闭;`closeOnMask=false` 时不关闭
|
||
- 选项行高不小于 44px,滚动容器 `-webkit-overflow-scrolling: touch`
|
||
- 点击选项只改本列高亮(同列其余项取消高亮),不改宿主的值;确认时才提交
|
||
- 确定 / 取消按钮热区不小于 44px
|
||
- 取消防返回:点取消或遮罩丢弃本次点选,宿主侧的值回到打开前的状态
|
||
|
||
### 43.6 无障碍
|
||
- 浮层 `role="dialog"` + `aria-modal="true"`,标题元素 id 由 `aria-labelledby` 指向
|
||
- 每列 `role="listbox"` + `aria-label`,选项 `role="option"` + `aria-selected`
|
||
- 遮罩 `aria-hidden="true"`(纯装饰,读屏不播报)
|
||
- 选中值以文本呈现在标题里(不依赖视觉滚动位置),收起态写 `aria-hidden="true"`
|
||
- 禁用项写 `aria-disabled="true"`,读屏播报不可用
|
||
|
||
### 43.7 doNotInvent
|
||
- 选项数据源与联动规则(由宿主传入 `columns`,本组件不发明城市库或级联关系)
|
||
- 滚轮惯性 / 吸附动画的物理参数
|
||
- 搜索过滤与键盘输入定位(那是 Input / Search 的职责)
|
||
- 多选(一次选多个值)——本组件是「多列各选一项」,不是「一列选多项」
|
||
|
||
### 43.8 unknowns
|
||
- 面板最大高度是否应随列数增长(当前固定 `max-height` 一列 200px)
|
||
- 是否要支持「不选」的空值项
|
||
- 列数上限(当前实现不限制,但三列以上在 375px 宽度下每列会很窄)
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 44 · 级联选择器 Cascader
|
||
|
||
### 44.1 用途
|
||
在**有层级关系**的选项里逐级选到末级(省 → 市 → 区、品类 → 子品类 → SKU),移动端与桌面端的最大差别是**没有横向空间**:桌面上三列并排一眼就能看到全路径,375px 宽的手机上并排三列每列只剩 100px 出头,文字全部折行不可读。因此移动端一次只展示**当前一层**,已选路径收进上方路径条,靠路径条回退而不是靠「上一级」按钮 —— 那会多一次点击。
|
||
|
||
### 44.2 结构(anatomy)
|
||
- `cascader`:根元素,`mode=panel` 时内嵌在页面里,`mode=popup` 时是底部浮层
|
||
- `mask`:遮罩(仅 `mode=popup`),点击关闭
|
||
- `cascader__header`:取消 / 标题 / 确定三格(仅 `mode=popup`)
|
||
- `cascader__path`:路径条,按已选深度渲染;除末位外都可点,点了回退到该级
|
||
- `cascader__panel`:选项区,`role="listbox"` + `aria-label` 说明当前在选第几级、选什么
|
||
- `cascader__option`:单个选项,`role="option"` + `aria-selected`;有下级显示 `›`,叶子显示 `✓`
|
||
|
||
### 44.3 变体维度
|
||
- `mode`:`panel`(内嵌在页面里)/ `popup`(底部浮层,自带遮罩与确定取消)
|
||
- `showPath`:`true`(显示路径条,可回退)/ `false`(不显示,层级少的场景省一行高度)
|
||
- `round`:`false`(直角)/ `true`(浮层靠内容一侧切圆角)
|
||
|
||
### 44.4 状态
|
||
- default:常态(停在根级)
|
||
- selected:当前路径上的项,`is-selected` 与 `aria-selected="true"` 同步
|
||
- open / closed:仅 `mode=popup`,浮层展开 / 收起(收起时遮罩 `pointer-events: none`)
|
||
- disabled:整块或单个结点置灰且不响应,`aria-disabled="true"`
|
||
- empty:当前级没有可选项时显示占位文案
|
||
|
||
### 44.5 交互与触控
|
||
- 点某一级后**下一级选项随之变化**:面板始终只渲染当前一层,选择即下钻
|
||
- 点路径条里的上级可**回退**:回退到该级并重新展示它的下一级;末位是「你在这里」的锚点,不可点
|
||
- 级数不写死:路径条按已选深度渲染,两级与四级用同一份实现
|
||
- 点到叶子结点后再点同级另一项,会截断更深的层级(改选不会留下旧的深层残留)
|
||
- 选项行高与路径条各项热区均不小于 44px;选项区滚动容器 `-webkit-overflow-scrolling: touch`
|
||
|
||
### 44.6 无障碍
|
||
- `mode=panel` 根元素 `role="group"` + `aria-label`;`mode=popup` 根元素 `role="dialog"` + `aria-modal="true"`
|
||
- 选项区 `role="listbox"`,`aria-label` 随层级变化(如「级联选项 · 城市」),读屏能播报当前在第几级
|
||
- 选项 `role="option"` + `aria-selected`;禁用项写 `aria-disabled="true"`
|
||
- 路径条末位写 `aria-current="true"`,读屏播报「当前项」
|
||
- 路径条每一项是原生 `button`(键盘可达),末位用 `disabled` 让键盘跳过
|
||
|
||
### 44.7 doNotInvent
|
||
- 层级数据源(由宿主传入 `options` 树,本组件不发明省市区库)
|
||
- 搜索定位某级选项(那需要把整棵树拍平,是独立的检索组件)
|
||
- 异步逐级加载的占位与重试流程
|
||
- 多选(一次选多条路径)
|
||
|
||
### 44.8 unknowns
|
||
- 路径很长时路径条是折行还是横向滚动(当前折行,四级以上会占两行)
|
||
- 叶子被选中后是否要自动收起浮层(当前不自动收,等宿主决定)
|
||
- 是否要保留「上一级」按钮作为路径条之外的第二种回退入口
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 45 · 颜色选择器 ColorPicker
|
||
|
||
### 45.1 用途
|
||
从一组**预设色**里挑一个颜色(主题色、标签色、看板分类色),或在预设之外补一个自定义色值;移动端与桌面端的关键差别是**没有取色器画布的位置,也没有精确拖拽的精度**:375px 宽下色相环只有一百多像素、手指覆盖几十像素,拖出来的色值几乎不可复现。因此移动端默认只给**预设色板**——色值是离散的、可预期的,选中与否一眼可辨;需要自由取色时退化为「输入色值」而不是「拖色相」。
|
||
|
||
### 45.2 结构(anatomy)
|
||
- `colorpicker`:根元素,承载当前色与色板
|
||
- `colorpicker__current`:当前色显示,由「圆点 + 色值文字」组成
|
||
- `colorpicker__dot`:当前色圆点,背景取 `--kole-m-colorpicker-current`(色值是数据,由宿主以行内自定义属性传入)
|
||
- `colorpicker__value`:色值文字(等宽字),读屏靠 `aria-label` 而不是色块本身
|
||
- `colorpicker__swatches`:色板,`role="radiogroup"` + `aria-label`,定列数网格
|
||
- `colorpicker__swatch`:单个色块,`role="radio"` + `aria-checked`,背景取 `--kole-m-colorpicker-swatch`
|
||
- `colorpicker__custom`:自定义色值输入区(仅 `mode=custom`),由宿主以插槽填入
|
||
|
||
### 45.3 变体维度
|
||
- `mode`:`swatch`(只有预设色板)/ `custom`(色板之后追加自定义色值输入)
|
||
- `round`:`false`(方形色块)/ `true`(圆形,主题选择器常用)
|
||
- `showValue`:`true`(显示色值文字)/ `false`(只留色块,空间紧张时用)
|
||
- `columns`:色板每行列数(默认 6,通过 `--kole-m-colorpicker-columns` 覆盖)
|
||
|
||
### 45.4 状态
|
||
- default:常态
|
||
- selected:当前选中色块,`is-selected` 与 `aria-checked="true"` 同步;视觉是**外环 + 轻微放大**,不是换色 —— 浅色块(白、浅黄)上换色根本看不出来
|
||
- disabled:整块置灰且不响应;色块写 `aria-disabled="true"` 并用原生 `disabled` 让键盘跳过
|
||
|
||
### 45.5 交互与触控
|
||
- 点色块即选中并回传该色;同组互斥(选新的自动取消旧的)
|
||
- 色块视觉 ≥32px、热区按网格列宽铺满,点空白处不响应
|
||
- 选中反馈是外环 + `scale(1.08)`,变换用 `--kole-duration-fast`;减少动态偏好下不放大
|
||
- `mode=custom` 时色值输入只在确认(点「应用」或回车)后生效,输入过程中不回传
|
||
- 色值非法时输入框置红并给出错误文案,**不自动纠正**(不发明「就近取色」这类行为)
|
||
|
||
### 45.6 无障碍
|
||
- 色板 `role="radiogroup"` + `aria-label` 说明这组在选什么(如「主题色」)
|
||
- 每个色块 `role="radio"` + `aria-checked`,`aria-label` 必须是**色值本身**(如 `#2F54EB`)—— 色块没有文字,读屏只能靠 aria-label
|
||
- 当前色以等宽文字重复一遍:颜色不作为唯一信息通道(色觉障碍用户读得到色值)
|
||
- 禁用块写 `aria-disabled="true"`,读屏播报不可用
|
||
|
||
### 45.7 doNotInvent
|
||
- 色相环 / 明度滑条的取色画布(移动端精度不足,本组件不提供)
|
||
- 颜色空间转换(RGB / HSL / HSV 互转)与色值自动纠正(只接受 `#RRGGBB`)
|
||
- 主题派生(由品牌色自动生成 hover / active / 浅底等衍生色阶)
|
||
- 取色器(吸管)与屏幕取色
|
||
|
||
### 45.8 unknowns
|
||
- 色板上限(当前不限制数量,但 40 个以上在小屏上会变成一片色噪)
|
||
- 自定义色值的格式是否要支持 `rgb()` / `hsl()` 书写
|
||
- 是否需要「最近使用」一行动态色块
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 46 · 上传 Upload
|
||
|
||
### 46.1 用途
|
||
把手机里的文件交给服务端(实名认证的身份证照、报销的发票、工单的附件)。移动端与桌面端的关键差别是**没有拖拽**:桌面上可以「把文件拖进虚线框」,手机上既没有 hover 也没有拖放,所以**触发按钮是唯一入口**,虚线框只能表达「这里可以放东西」而不能作为交互方式;另一个差别是**相机**——很多上传场景其实期望的是「拍一张」,宿主可以用同一个触发器同时给出「拍照」与「从相册选择」,但那是宿主的编排,不是本组件的职责。
|
||
|
||
### 46.2 结构(anatomy)
|
||
- `upload`:根元素,纵向排列「标题行 + 触发器 + 文件列表」
|
||
- `upload__header`:标题与计数(`已选 N / 上限 M`)
|
||
- `upload__trigger`:选择触发器,点击调起系统文件选择器;虚线框是 `variant=dashed`,实心按钮是 `variant=button`
|
||
- `upload__list` / `upload__item`:文件列表与单行,行高不小于 44px
|
||
- `upload__thumb`:缩略图位(图片用背景图,其他类型放扩展名文字块)
|
||
- `upload__body` / `upload__name` / `upload__meta`:文件名与「进度条 + 状态文案」
|
||
- `upload__track` / `upload__bar`:进度条轨道与进度段,宽度取 `--kole-m-upload-percent`
|
||
- `upload__actions`:行内动作(重试 / 删除),各自 44px 热区
|
||
|
||
### 46.3 变体维度
|
||
- `variant`:`dashed`(虚线框,页面级上传区)/ `button`(实心按钮,列表内嵌的「+ 添加」)
|
||
- `maxCount`:文件数上限(到达上限后触发器变成提示行,不再可点)
|
||
|
||
### 46.4 状态
|
||
- pending:待上传(进度条不占宽,文案「待上传」)
|
||
- uploading:上传中(品牌色进度条,文案给百分比;`role="progressbar"` + `aria-valuenow`)
|
||
- success:成功(满格 + 成功色 +「已上传」)
|
||
- error:失败(满格 + 错误色 + 失败原因,行内出现「重试」)
|
||
- empty:还没有任何文件时的说明文案
|
||
- disabled:整块置灰(状态类 `is-disabled`,触发器与删除按钮都不可用);到 `maxCount` 后触发器变提示行
|
||
|
||
### 46.5 交互与触控
|
||
- 点触发器调起系统文件选择器(本组件不发请求,选完由宿主拿文件并自行上传)
|
||
- 删除按钮自己 44px 热区,点一次移除该项;删除进行中(`uploading`)时文案是「取消」
|
||
- 失败行提供「重试」,重试由宿主重新发起,组件只回传下标
|
||
- 进度不自己走:**没有内置定时器或假进度**,百分比全部由宿主回传,避免出现「看起来在上传其实没动」
|
||
- 列表为空时显示空态文案;到 `maxCount` 后触发器改成不可点的提示行
|
||
|
||
### 46.6 无障碍
|
||
- 触发器是原生 `button`,带 `aria-disabled`;到上限时用 `disabled` 让键盘跳过
|
||
- 进度条写 `role="progressbar"` + `aria-valuemin` / `aria-valuemax` / `aria-valuenow`,`aria-label` 说明是哪个文件的进度
|
||
- 删除 / 重试按钮各自带 `aria-label`(含文件名),读屏播报「删除 合同扫描件.pdf」而不是孤零零的「删除」
|
||
- 状态文案是文字而不只是颜色(「已上传」「上传失败」),颜色不作为唯一信息通道
|
||
|
||
### 46.7 doNotInvent
|
||
- 真实的传输:请求、分片、断点续传、并发数(本组件只回传事件,不发任何请求)
|
||
- 图片压缩、裁剪、水印与方向纠正
|
||
- 服务端的校验规则(大小上限、类型白名单以文案与 `accept` 表达,不代为判断)
|
||
- 拍照与相册的原生调起(由宿主在 `select` 事件里自行调用平台 API)
|
||
|
||
### 46.8 unknowns
|
||
- 上传中能否同时继续添加文件(当前允许,列表各自独立)
|
||
- 失败自动重试的次数与退避策略
|
||
- 是否需要在成功行上展示服务端返回的文件 id / URL
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
## 47 · 表格 Table
|
||
|
||
### 47.1 用途
|
||
把多条同构记录按列并排,用于「订单列表 / 库存明细 / 对账单」这类需要**逐行横向对比**的场合;移动端与桌面端的关键差别是**宽度根本不够**:桌面上 8 列一眼看完,375px 的手机上同样 8 列每列只剩 47px,文字全部折断。因此移动端表格必须做「**在组件自己的容器里横向滚动**」——**绝不能让页面整体横向溢出**,否则整页会跟着左右晃、顶栏与底部栏一起位移;列更多或更该读字段名时改用 `mode=card`,把每行摊成键值对,彻底不需要横滚。
|
||
|
||
### 47.2 结构(anatomy)
|
||
- `table`:根元素,纵向排列「视口 + 空态」
|
||
- `table__viewport`:**横向滚动的唯一发生点**(`overflow-x: auto`),页面整体不横向溢出
|
||
- `table__inner`:原生 `table`,`min-width` 由 `--kole-m-table-min-width` 给出;超过容器即滚
|
||
- `table__caption`:可选表格标题(说明这张表在讲什么)
|
||
- `table__head` / `table__head-cell`:表头与表头单元格,`position: sticky` 固定在滚动视口顶部
|
||
- `table__row` / `table__cell`:数据行与单元格,行高不小于 44px;单元格可带 `data-label`(卡片模式的字段名来源)
|
||
- `table__empty`:空态占位,`role="status"` + `aria-live="polite"`
|
||
|
||
### 47.3 变体维度
|
||
- `mode`:`scroll`(横向滚动,列多时用)/ `card`(每行摊成键值对,小屏彻底不横滚)
|
||
- `size`:`default`(常规行高)/ `compact`(收紧内边距与字号;行高仍不小于 44px)
|
||
- `stripe`:`false`(纯白底)/ `true`(偶数行浅底,便于横向扫读时对齐行)
|
||
- `bordered`:`false`(只有横线)/ `true`(单元格之间也有竖线)
|
||
|
||
### 47.4 状态
|
||
- default:常态
|
||
- selected:当前选中行,`is-selected` 与 `aria-selected` 同步(行选中不是复选框,单行高亮即可)
|
||
- disabled:整行置灰且不响应,写 `aria-disabled="true"`
|
||
- empty:没有数据时的占位文案(列宽语义不变,不会把容器撑宽)
|
||
- clickable:行可点(`clickable=true` 时整行都是热区,按下反馈是整行背景变化)
|
||
|
||
### 47.5 交互与触控
|
||
- **横向滚动发生在 `table__viewport` 上**:根与视口都写 `min-width: 0`,否则内部 `min-width` 会把父级顶宽、页面整体横向溢出
|
||
- 选项行高不小于 44px;触屏上横向滑动不被父级纵向滚动吞掉(`touch-action: pan-x pan-y`)
|
||
- 表头用 `position: sticky` 而不是 `fixed`——`fixed` 会脱出滚动容器,横向滚动时表头不跟随
|
||
- 长文本列默认单行省略,完整值放 `title`;数字列用等宽字右对齐,便于按位对比
|
||
- 点整行即选中(同表互斥);行内的独立控件(按钮 / 链接)要阻止冒泡,避免一次点击触发两个动作
|
||
- 行可点时同时提供键盘路径(聚焦后回车 / 空格),不把鼠标点击当成唯一入口
|
||
|
||
### 47.6 无障碍
|
||
- 用原生 `table` / `thead` / `tbody` / `tr` / `th` / `td`,表头写 `scope="col"`(读屏能报出列名)
|
||
- 空态用 `role="status"` + `aria-live="polite"` 播报占位文案
|
||
- 禁用行写 `aria-disabled="true"`,读屏播报不可用
|
||
- **可点行必须同时可聚焦**:`clickable=true` 时行写 `tabindex="0"` + `role="row"` + `aria-selected`,聚焦后回车 / 空格即选中 —— 只有鼠标点击而没有键盘路径是缺陷,不是简化
|
||
- 焦点环画在行内第一个单元格上(`tr` 上的 `outline` 在部分浏览器不渲染)
|
||
- 行内的独立控件(链接 / 按钮)有可见焦点环,并阻止冒泡
|
||
- `mode=card` 时字段名由 `data-label` 生成并参与读屏(键值对在视觉与语义上都成立)
|
||
- 可点行的 `cursor: pointer` 只给表体行:表头行不承载点击,跟着变手型会把「这里能点」这个信号弄错
|
||
|
||
### 47.7 doNotInvent
|
||
- 虚拟滚动与无限加载的触发规则(由宿主容器负责;本组件不接管纵向滚动)
|
||
- 服务端排序 / 筛选 / 分页(排序图标与请求都由宿主发起)
|
||
- 列宽拖拽调整与列显隐设置(移动端没有这个操作面)
|
||
- 冻结列(`position: sticky` 的横向版本;当前只固定表头的纵向位置)
|
||
|
||
### 47.8 unknowns
|
||
- 表头是否需要在纵向滚动时也吸顶(当前只在宿主给固定高度时生效)
|
||
- 行选中是单选还是多选(当前单选;多选应交给 Checkbox 列)
|
||
- 卡片模式下字段名的排列是「左标签右值」还是「上标签下值」
|
||
|
||
---
|
||
|