步骤条Steps
横向展示多步流程的当前进度(如「提交 → 审核 → 完成」)
导航 规格 15 · 步骤条 Steps 6 端实现 触摸优先
<!-- ① 令牌:PC 令牌 + 移动端 --kole-m-* 合成单文件,引一次 -->
<link rel="stylesheet" href="kole-ui/mobile/tokens.css">
<!-- ② 本组件样式(全量则用 kole-ui/mobile/components/index.css) -->
<link rel="stylesheet" href="kole-ui/mobile/components/mobile-steps.css">
<!-- ③ 结构照抄下方任一演示块(类名与 6 端实现一致) -->
演示
每个演示都是真实渲染:预览帧加载 frameworks-mobile/Steps.html?demo=<id>(只显示该演示块),代码是该演示块在演示页里的原文,可复制。全部演示同屏可看 演示页 ↗。
01 组件类型
三种状态并列展示:已完成带勾选、进行中加粗并用品牌色实心点。
查看代码(演示页原文 · 17 行)
<section class="demo-block" data-demo="basic">
<p class="demo-label">横向步骤(status:wait / process / finish —— 进行中加粗,已完成带勾选)</p>
<ol class="kole-m-steps" data-assert="steps-basic">
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__label">提交订单</span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--process" role="listitem" aria-current="step">
<span class="kole-m-steps__dot" aria-hidden="true">2</span>
<span class="kole-m-steps__label">等待审核</span>
</li>
<li class="kole-m-steps__item" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">3</span>
<span class="kole-m-steps__label">完成</span>
</li>
</ol>
</section>失败步骤用错误色 + 感叹号,状态同时写在文字里 —— 不只靠颜色传达。
查看代码(演示页原文 · 17 行)
<section class="demo-block" data-demo="error">
<p class="demo-label">含失败步骤(status=error:错误色 + 状态写在文字里,不只靠颜色)</p>
<ol class="kole-m-steps" data-assert="steps-error">
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__label">实名认证</span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--error" role="listitem" aria-current="step">
<span class="kole-m-steps__dot" aria-hidden="true">!</span>
<span class="kole-m-steps__label">银行卡校验失败</span>
</li>
<li class="kole-m-steps__item" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">3</span>
<span class="kole-m-steps__label">开通服务</span>
</li>
</ol>
</section>步骤较多时横向平铺,标题短、点不被挤压。
查看代码(演示页原文 · 21 行)
<section class="demo-block" data-demo="four-steps">
<p class="demo-label">四步流程(步骤较多时横向排布,标题换行不挤点)</p>
<ol class="kole-m-steps" data-assert="steps-four">
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__label">下单</span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__label">支付</span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--process" role="listitem" aria-current="step">
<span class="kole-m-steps__dot" aria-hidden="true">3</span>
<span class="kole-m-steps__label">发货</span>
</li>
<li class="kole-m-steps__item" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">4</span>
<span class="kole-m-steps__label">收货</span>
</li>
</ol>
</section>02 组件状态
标题较长时改纵向:连接线变竖线,标题可以换行。
查看代码(演示页原文 · 17 行)
<section class="demo-block" data-demo="vertical">
<p class="demo-label">纵向步骤(direction=vertical:步骤标题较长时用,连接线改为竖线)</p>
<ol class="kole-m-steps kole-m-steps--vertical" data-assert="steps-vertical">
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__content"><span class="kole-m-steps__label">已提交资料</span></span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--process" role="listitem" aria-current="step">
<span class="kole-m-steps__dot" aria-hidden="true">2</span>
<span class="kole-m-steps__content"><span class="kole-m-steps__label">资质审核中(预计 1 个工作日)</span></span>
</li>
<li class="kole-m-steps__item" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">3</span>
<span class="kole-m-steps__content"><span class="kole-m-steps__label">开通账号</span></span>
</li>
</ol>
</section>API
props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs
Props
| 名称 | 类型 | 默认值 | 说明 | 必传 |
|---|---|---|---|---|
direction | 'horizontal' | 'vertical' | 'horizontal' | 变体 direction:横向或纵向排列(规格 §15.3) | N |
steps | Array<{ label, status }> | [] | 步骤数据;status ∈ wait / process / finish / error(规格 §15.3) | N |
「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。
事件
| 名称 | 参数 | 说明 |
|---|
插槽
| 名称 | 说明 |
|---|---|
default | 额外的自定义步骤(追加在 steps 之后) |
CSS 变量
组件级变量(在组件样式表里定义)。业务侧可在自己的作用域内覆盖,不必改组件源码。
| 名称 | 默认值 | 说明 |
|---|---|---|
--kole-m-steps-dot-size | 24px | 组件内部默认值,可在业务侧覆盖 |
何时使用
- 横向展示多步流程的当前进度(如「提交 → 审核 → 完成」)
- 纯展示组件,步骤本身不可点(这是设计如此,不是缺失)
- 步骤过多时横向可滚动(容器 overflow-x: auto)
- 容器 role="list",每步 role="listitem"
- aria-current="step" 标出当前步
- 状态不只靠颜色(进行中加粗 + 已完成用勾选字符)
交互与触控
- 纯展示组件,步骤本身不可点(这是设计如此,不是缺失)
- 步骤过多时横向可滚动(容器 overflow-x: auto)
无障碍
- 容器 role="list",每步 role="listitem"
- aria-current="step" 标出当前步
- 状态不只靠颜色(进行中加粗 + 已完成用勾选字符)
相似组件
从「该用哪一个」的角度区分;PC 端的对应实现见 PC 文档站。
| 组件 | 何时用它而不是本组件 |
|---|---|
| 单元格Cell | 步骤条展示流程进度;具体每一步的详情入口用单元格列出来 |
| 标签Tag | 单条记录的状态用标签;只有多步流程才用步骤条 |
| 分割线Divider | 步骤之间靠连接线表达先后关系,不要用分割线替代 |
规格未定 / 禁止发明
| 类别 | 条目 |
|---|---|
| 禁止发明 | 步骤之间的动画过渡 |
| 禁止发明 | 点击步骤跳转的规则 |
| 规格未定 | 纵向步骤条的推荐间距 |
| 规格未定 | 标题最多几行 |
结构(anatomy)
| 字段 | 说明 |
|---|---|
steps | 根容器,横向排列 |
steps__item | 单个步骤 |
steps__dot | 序号点 / 勾选标记 |
steps__label | 步骤标题 |
steps__line | 连接线 |
变体维度与类名映射
类名映射由构建脚本从契约 variantClasses 生成,并被 verify:mobile-docs 逐条对照组件 CSS 校验(类/变量必须真实存在)。
| 维度 | 取值 | 对应类名 / 变量 |
|---|---|---|
direction | horizontal / vertical | horizontal (由数据驱动,无专属类) vertical .kole-m-steps--vertical |
status | wait / process / finish / error | wait (由数据驱动,无专属类) process .kole-m-steps__item--process finish .kole-m-steps__item--finish error .kole-m-steps__item--error |
代表变体
| 变体 | 标签 |
|---|---|
direction=horizontal · status=process | 横向(进行中) |
direction=horizontal · status=finish | 横向(已完成) |
direction=horizontal · status=error | 横向(含失败) |
direction=vertical · status=process | 纵向(标题较长) |
用到的令牌
构建时从本组件样式表扫描得出。蓝色为移动端自有令牌,绿色为继承的 PC 令牌(改一处两端生效)。
6 端源码
同一组件的六份实现(生产环境的类名与结构一致,差异只在技术栈写法与单位)。点开查看,右侧可复制。
frameworks-mobile/Steps.css · 纯样式(CSS) · 115 行
/* Kole UI Mobile · Steps 样式 — 对齐移动端规格 §15
步骤条:纯展示;状态不只靠颜色(进行中加粗 + 已完成用勾选字符);
步骤过多时横向可滚动。 */
.kole-m-steps {
--kole-m-steps-dot-size: 24px;
display: flex;
align-items: flex-start;
gap: 0;
margin: 0;
padding: var(--kole-space-16) var(--kole-m-gutter);
background: var(--kole-color-card-bg);
font-family: var(--kole-font-family);
list-style: none;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.kole-m-steps__item {
position: relative;
flex: 1 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: var(--kole-space-8);
min-width: 72px;
color: var(--kole-color-text-secondary);
font-size: var(--kole-m-font-size-label);
line-height: 1.3;
text-align: center;
}
.kole-m-steps__dot {
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--kole-m-steps-dot-size);
height: var(--kole-m-steps-dot-size);
border: 1px solid var(--kole-color-border);
border-radius: 50%;
background: var(--kole-color-card-bg);
color: var(--kole-color-text-secondary);
font-size: var(--kole-m-font-size-caption);
line-height: 1;
}
.kole-m-steps__label {
max-width: 100%;
overflow-wrap: anywhere;
}
/* 连接线:由每项的 ::after 画到下一项(最后一项不画) */
.kole-m-steps__item:not(:last-child)::after {
content: '';
position: absolute;
top: calc(var(--kole-m-steps-dot-size) / 2);
inset-inline-start: calc(50% + var(--kole-m-steps-dot-size) / 2 + var(--kole-space-4));
width: calc(100% - var(--kole-m-steps-dot-size) - var(--kole-space-8));
height: 1px;
background: var(--kole-color-border);
}
/* 变体 status=process:进行中(品牌色 + 加粗) */
.kole-m-steps__item--process { color: var(--kole-color-brand); font-weight: 500; }
.kole-m-steps__item--process .kole-m-steps__dot {
border-color: var(--kole-color-brand);
background: var(--kole-color-brand);
color: var(--kole-color-text-inverse);
}
/* 变体 status=finish:已完成(品牌色 + 勾选) */
.kole-m-steps__item--finish { color: var(--kole-color-text-body); }
.kole-m-steps__item--finish .kole-m-steps__dot {
border-color: var(--kole-color-brand);
color: var(--kole-color-brand);
}
.kole-m-steps__item--finish:not(:last-child)::after { background: var(--kole-color-brand); }
/* 变体 status=error:错误(错误色 + 感叹号) */
.kole-m-steps__item--error { color: var(--kole-color-error); }
.kole-m-steps__item--error .kole-m-steps__dot {
border-color: var(--kole-color-error);
color: var(--kole-color-error);
}
/* 变体 direction=vertical:纵向排列,连接线改为竖线 */
.kole-m-steps--vertical {
flex-direction: column;
gap: var(--kole-space-16);
overflow-x: visible;
}
.kole-m-steps--vertical .kole-m-steps__item {
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
min-width: 0;
text-align: start;
}
.kole-m-steps--vertical .kole-m-steps__item:not(:last-child)::after {
top: calc(var(--kole-m-steps-dot-size) + var(--kole-space-4));
inset-inline-start: calc(var(--kole-m-steps-dot-size) / 2);
width: 1px;
height: calc(100% - var(--kole-m-steps-dot-size));
}
.kole-m-steps__content { min-width: 0; }
frameworks-mobile/Steps.html · H5 原生(无框架) · 117 行
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Kole UI Mobile · Steps(H5)</title>
<link rel="stylesheet" href="../.design_library/kole-ui-mobile/colors_and_type.css">
<link rel="stylesheet" href="Steps.css">
<style>
body { margin: 0; background: var(--kole-color-page-bg); font-family: var(--kole-font-family); color: var(--kole-color-text-body); }
.demo { max-width: 375px; margin: 0 auto; padding: var(--kole-m-gutter) 0; }
.demo-label { margin: 0; padding: var(--kole-space-12) var(--kole-m-gutter) var(--kole-space-8);
font-size: var(--kole-m-font-size-label); color: var(--kole-color-text-secondary); }
.demo-block[hidden] { display: none; }
</style>
</head>
<body>
<div class="demo">
<section class="demo-block" data-demo="basic">
<p class="demo-label">横向步骤(status:wait / process / finish —— 进行中加粗,已完成带勾选)</p>
<ol class="kole-m-steps" data-assert="steps-basic">
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__label">提交订单</span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--process" role="listitem" aria-current="step">
<span class="kole-m-steps__dot" aria-hidden="true">2</span>
<span class="kole-m-steps__label">等待审核</span>
</li>
<li class="kole-m-steps__item" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">3</span>
<span class="kole-m-steps__label">完成</span>
</li>
</ol>
</section>
<section class="demo-block" data-demo="error">
<p class="demo-label">含失败步骤(status=error:错误色 + 状态写在文字里,不只靠颜色)</p>
<ol class="kole-m-steps" data-assert="steps-error">
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__label">实名认证</span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--error" role="listitem" aria-current="step">
<span class="kole-m-steps__dot" aria-hidden="true">!</span>
<span class="kole-m-steps__label">银行卡校验失败</span>
</li>
<li class="kole-m-steps__item" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">3</span>
<span class="kole-m-steps__label">开通服务</span>
</li>
</ol>
</section>
<section class="demo-block" data-demo="four-steps">
<p class="demo-label">四步流程(步骤较多时横向排布,标题换行不挤点)</p>
<ol class="kole-m-steps" data-assert="steps-four">
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__label">下单</span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__label">支付</span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--process" role="listitem" aria-current="step">
<span class="kole-m-steps__dot" aria-hidden="true">3</span>
<span class="kole-m-steps__label">发货</span>
</li>
<li class="kole-m-steps__item" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">4</span>
<span class="kole-m-steps__label">收货</span>
</li>
</ol>
</section>
<section class="demo-block" data-demo="vertical">
<p class="demo-label">纵向步骤(direction=vertical:步骤标题较长时用,连接线改为竖线)</p>
<ol class="kole-m-steps kole-m-steps--vertical" data-assert="steps-vertical">
<li class="kole-m-steps__item kole-m-steps__item--finish" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">✓</span>
<span class="kole-m-steps__content"><span class="kole-m-steps__label">已提交资料</span></span>
</li>
<li class="kole-m-steps__item kole-m-steps__item--process" role="listitem" aria-current="step">
<span class="kole-m-steps__dot" aria-hidden="true">2</span>
<span class="kole-m-steps__content"><span class="kole-m-steps__label">资质审核中(预计 1 个工作日)</span></span>
</li>
<li class="kole-m-steps__item" role="listitem">
<span class="kole-m-steps__dot" aria-hidden="true">3</span>
<span class="kole-m-steps__content"><span class="kole-m-steps__label">开通账号</span></span>
</li>
</ol>
</section>
</div>
<script>
/* ?demo=<id> → 只显示该演示块(文档站按块预览用;无参数时全部显示,测试与回归走无参数路径) */
(function () {
var id = new URLSearchParams(location.search).get('demo');
if (!id) return;
var blocks = Array.prototype.slice.call(document.querySelectorAll('.demo-block'));
var hit = false;
blocks.forEach(function (b) {
var on = b.getAttribute('data-demo') === id;
if (on) hit = true;
b.hidden = !on;
});
if (!hit) { blocks.forEach(function (b) { b.hidden = false; }); return; }
document.body.classList.add('demo-single');
blocks.forEach(function (b) {
var label = b.querySelector('.demo-label');
if (label && !b.hidden) label.hidden = true;
});
})();
</script>
</body>
</html>
frameworks-mobile/Steps.jsx · React · 53 行
import React from 'react';
import './Steps.css';
/* 步骤条(移动端)— 规格 §15
纯展示:步骤本身不可点(规格 §15.5 明确如此,不是缺失);
状态不只靠颜色 —— 进行中加粗、已完成用勾选字符、失败用感叹号。 */
export default function Steps({
direction = 'horizontal',
steps = [],
children = null,
}) {
const cls =
'kole-m-steps' + (direction === 'vertical' ? ' kole-m-steps--vertical' : '');
const statusOf = (s) => (s && s.status) || 'wait';
/* aria-current="step" 标出当前步(进行中与失败都算「停在这一步」) */
const isCurrent = (s) => {
const st = statusOf(s);
return st === 'process' || st === 'error';
};
const dotOf = (s, i) => {
const st = statusOf(s);
if (st === 'finish') return '✓';
if (st === 'error') return '!';
return i + 1;
};
return (
<ol className={cls}>
{steps.map((s, i) => (
<li
key={i}
className={`kole-m-steps__item kole-m-steps__item--${statusOf(s)}`}
role="listitem"
aria-current={isCurrent(s) ? 'step' : undefined}
>
<span className="kole-m-steps__dot" aria-hidden="true">
{dotOf(s, i)}
</span>
{direction === 'vertical' ? (
<span className="kole-m-steps__content">
<span className="kole-m-steps__label">{s && s.label}</span>
</span>
) : (
<span className="kole-m-steps__label">{s && s.label}</span>
)}
</li>
))}
{children}
</ol>
);
}
frameworks-mobile/Steps.vue2.vue · Vue 2 · 51 行
<template>
<ol class="kole-m-steps" :class="stepsClass">
<li
v-for="(s, i) in steps"
:key="i"
class="kole-m-steps__item"
:class="itemClass(s)"
role="listitem"
:aria-current="s && s.status === 'process' ? 'step' : null"
>
<span class="kole-m-steps__dot" aria-hidden="true">{{ dotOf(s) }}</span>
<span class="kole-m-steps__content"><span class="kole-m-steps__label">{{ s && s.label }}</span></span>
</li>
<slot></slot>
</ol>
</template>
<script>
export default {
name: 'KoleMSteps',
props: {
direction: { type: String, default: 'horizontal' },
steps: { type: Array, default: function () { return []; } }
},
computed: {
stepsClass: function () {
return [this.direction === 'vertical' ? 'kole-m-steps--vertical' : ''].filter(Boolean);
}
},
methods: {
itemClass: function (s) {
if (!s) return [];
var out = [];
if (s.status === 'process') out.push('kole-m-steps__item--process');
if (s.status === 'finish') out.push('kole-m-steps__item--finish');
if (s.status === 'error') out.push('kole-m-steps__item--error');
return out;
},
/* 状态不只靠颜色:已完成用勾选字符,失败用感叹号(规格 §15.6) */
dotOf: function (s) {
if (!s) return '';
if (s.status === 'finish') return '✓';
if (s.status === 'error') return '!';
return String(s.index || 0);
}
}
};
</script>
<style src="./Steps.css"></style>
frameworks-mobile/Steps.vue3.vue · Vue 3 · 47 行
<template>
<ol class="kole-m-steps" :class="stepsClass">
<li
v-for="(s, i) in steps"
:key="i"
class="kole-m-steps__item"
:class="itemClass(s)"
role="listitem"
:aria-current="s && s.status === 'process' ? 'step' : null"
>
<span class="kole-m-steps__dot" aria-hidden="true">{{ dotOf(s) }}</span>
<span class="kole-m-steps__content"><span class="kole-m-steps__label">{{ s && s.label }}</span></span>
</li>
<slot></slot>
</ol>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
direction: { type: String, default: 'horizontal' },
steps: { type: Array, default: () => [] }
});
const stepsClass = computed(() => [props.direction === 'vertical' ? 'kole-m-steps--vertical' : ''].filter(Boolean));
function itemClass(s) {
if (!s) return [];
const out = [];
if (s.status === 'process') out.push('kole-m-steps__item--process');
if (s.status === 'finish') out.push('kole-m-steps__item--finish');
if (s.status === 'error') out.push('kole-m-steps__item--error');
return out;
}
/* 状态不只靠颜色:已完成用勾选字符,失败用感叹号(规格 §15.6) */
function dotOf(s) {
if (!s) return '';
if (s.status === 'finish') return '✓';
if (s.status === 'error') return '!';
return String(s.index || 0);
}
</script>
<style src="./Steps.css"></style>
frameworks-mobile/Steps.uniapp.vue · uni-app(跨端:小程序 / App / H5) · 150 行
<template>
<view class="kole-m-steps" :class="stepsClass" role="list">
<view
v-for="(s, i) in steps"
:key="i"
class="kole-m-steps__item"
:class="itemClass(s)"
role="listitem"
:aria-current="s && s.status === 'process' ? 'step' : ''"
>
<text class="kole-m-steps__dot">{{ dotOf(s) }}</text>
<view class="kole-m-steps__content"><text class="kole-m-steps__label">{{ s && s.label }}</text></view>
</view>
<slot></slot>
</view>
</template>
<script setup>
/* uni-app 端 · 步骤条(移动端)— 规格 §15
跨端差异:用 view / text(ol/li 不是 uni 基础组件),列表语义靠 role="list"/"listitem";
尺寸用 rpx(2rpx ≈ 1px);纯展示组件,无手势与点击。 */
import { computed } from 'vue';
const props = defineProps({
direction: { type: String, default: 'horizontal' },
steps: { type: Array, default: () => [] }
});
const stepsClass = computed(() => [props.direction === 'vertical' ? 'kole-m-steps--vertical' : ''].filter(Boolean));
function itemClass(s) {
if (!s) return [];
const out = [];
if (s.status === 'process') out.push('kole-m-steps__item--process');
if (s.status === 'finish') out.push('kole-m-steps__item--finish');
if (s.status === 'error') out.push('kole-m-steps__item--error');
return out;
}
/* 状态不只靠颜色:已完成用勾选字符,失败用感叹号(规格 §15.6) */
function dotOf(s) {
if (!s) return '';
if (s.status === 'finish') return '✓';
if (s.status === 'error') return '!';
return String(s.index || 0);
}
</script>
<style>
.kole-m-steps {
--kole-m-steps-dot-size: 48rpx;
--kole-m-font-size-label: 28rpx;
--kole-m-font-size-caption: 22rpx;
--kole-m-gutter: 32rpx;
display: flex;
align-items: flex-start;
padding: 32rpx var(--kole-m-gutter);
background-color: var(--kole-color-card-bg);
font-size: var(--kole-m-font-size-label);
}
.kole-m-steps__item {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
min-width: 144rpx;
color: var(--kole-color-text-secondary);
text-align: center;
}
.kole-m-steps__dot {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
width: var(--kole-m-steps-dot-size);
height: var(--kole-m-steps-dot-size);
border: 2rpx solid var(--kole-color-border);
border-radius: 50%;
background-color: var(--kole-color-card-bg);
color: var(--kole-color-text-secondary);
font-size: var(--kole-m-font-size-caption);
line-height: 1;
}
.kole-m-steps__label {
margin-top: 16rpx;
font-size: var(--kole-m-font-size-label);
}
/* 连接线:由每项的 ::after 画到下一项(最后一项不画) */
.kole-m-steps__item:not(:last-child)::after {
content: '';
position: absolute;
top: 24rpx;
left: calc(50% + 28rpx);
width: calc(100% - 48rpx);
height: 2rpx;
background-color: var(--kole-color-border);
}
.kole-m-steps__item--process { color: var(--kole-color-brand); }
.kole-m-steps__item--process .kole-m-steps__dot {
border-color: var(--kole-color-brand);
background-color: var(--kole-color-brand);
color: var(--kole-color-text-inverse);
}
.kole-m-steps__item--finish { color: var(--kole-color-text-body); }
.kole-m-steps__item--finish .kole-m-steps__dot {
border-color: var(--kole-color-brand);
color: var(--kole-color-brand);
}
.kole-m-steps__item--finish:not(:last-child)::after { background-color: var(--kole-color-brand); }
.kole-m-steps__item--error { color: var(--kole-color-error); }
.kole-m-steps__item--error .kole-m-steps__dot {
border-color: var(--kole-color-error);
color: var(--kole-color-error);
}
.kole-m-steps--vertical {
flex-direction: column;
}
.kole-m-steps--vertical .kole-m-steps__item {
flex-direction: row;
align-items: flex-start;
width: 100%;
min-width: 0;
margin-bottom: 32rpx;
text-align: left;
}
.kole-m-steps--vertical .kole-m-steps__item:not(:last-child)::after {
top: 56rpx;
left: 24rpx;
width: 2rpx;
height: calc(100% - 48rpx);
}
.kole-m-steps__content { flex: 1; }
</style>
测试与回归
断言在真实的 375×640 设备帧里跑(引擎与 PC 侧共用 tests/_runtime.js,触控行为动词来自移动端 tests/mobile/_behaviors.js)。
断言 14 条 · 全部通过 报告 2026-09-20 16:50:33
node site/dev-server.js &
REG_BASE=http://127.0.0.1:3311 node tools/run-mobile-regression.mjs # 全量 5 个组件
npm run verify:mobile-docs # 本页内容完整性 + API 与源码一致性
设计契约
components/mobile-steps.json(点击展开原始 JSON)
{
"schemaVersion": 1,
"sourceKind": "authored-spec",
"provenance": "authored-in-repo",
"specFile": "spec/移动端规格.md",
"confidence": "high",
"specSection": "15 · 步骤条 Steps",
"slug": "mobile-steps",
"name": "步骤条 Steps",
"semanticTypeCandidates": [
"steps",
"wizard",
"progress"
],
"variantDimensions": [
{
"name": "direction",
"values": [
"horizontal",
"vertical"
]
},
{
"name": "status",
"values": [
"wait",
"process",
"finish",
"error"
]
}
],
"representativeVariants": [
{
"direction": "horizontal",
"status": "process",
"label": "横向(进行中)"
},
{
"direction": "horizontal",
"status": "finish",
"label": "横向(已完成)"
},
{
"direction": "horizontal",
"status": "error",
"label": "横向(含失败)"
},
{
"direction": "vertical",
"status": "process",
"label": "纵向(标题较长)"
}
],
"anatomy": {
"steps": "根容器,横向排列",
"steps__item": "单个步骤",
"steps__dot": "序号点 / 勾选标记",
"steps__label": "步骤标题",
"steps__line": "连接线"
},
"structurePatterns": {
"direction": "horizontal / vertical",
"status": "wait(未开始)/ process(进行中)/ finish(已完成)/ error"
},
"usageHints": [
"横向展示多步流程的当前进度(如「提交 → 审核 → 完成」)",
"纯展示组件,步骤本身不可点(这是设计如此,不是缺失)",
"步骤过多时横向可滚动(容器 overflow-x: auto)",
"容器 role=\"list\",每步 role=\"listitem\"",
"aria-current=\"step\" 标出当前步",
"状态不只靠颜色(进行中加粗 + 已完成用勾选字符)"
],
"doNotInvent": [
"步骤之间的动画过渡",
"点击步骤跳转的规则"
],
"unknowns": [
"纵向步骤条的推荐间距",
"标题最多几行"
],
"interaction": [
"纯展示组件,步骤本身不可点(这是设计如此,不是缺失)",
"步骤过多时横向可滚动(容器 overflow-x: auto)"
],
"accessibility": [
"容器 role=\"list\",每步 role=\"listitem\"",
"aria-current=\"step\" 标出当前步",
"状态不只靠颜色(进行中加粗 + 已完成用勾选字符)"
],
"api": {
"source": "implementation",
"note": "props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs",
"requiredNote": "「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。",
"props": [
{
"name": "direction",
"type": "'horizontal' | 'vertical'",
"default": "'horizontal'",
"desc": "变体 direction:横向或纵向排列(规格 §15.3)",
"required": false
},
{
"name": "steps",
"type": "Array<{ label, status }>",
"default": "[]",
"desc": "步骤数据;status ∈ wait / process / finish / error(规格 §15.3)",
"required": false
}
],
"events": [],
"slots": [
{
"name": "default",
"desc": "额外的自定义步骤(追加在 steps 之后)"
}
]
},
"variantClasses": {
"direction": {
"horizontal": [],
"vertical": [
".kole-m-steps--vertical"
]
},
"status": {
"wait": [],
"process": [
".kole-m-steps__item--process"
],
"finish": [
".kole-m-steps__item--finish"
],
"error": [
".kole-m-steps__item--error"
]
}
},
"demos": [
{
"id": "basic",
"group": "01 组件类型",
"title": "横向步骤",
"desc": "三种状态并列展示:已完成带勾选、进行中加粗并用品牌色实心点。",
"variant": "direction=horizontal"
},
{
"id": "error",
"group": "01 组件类型",
"title": "含失败步骤",
"desc": "失败步骤用错误色 + 感叹号,状态同时写在文字里 —— 不只靠颜色传达。",
"variant": "status=error"
},
{
"id": "four-steps",
"group": "01 组件类型",
"title": "四步流程",
"desc": "步骤较多时横向平铺,标题短、点不被挤压。",
"variant": "direction=horizontal · 4 步"
},
{
"id": "vertical",
"group": "02 组件状态",
"title": "纵向步骤",
"desc": "标题较长时改纵向:连接线变竖线,标题可以换行。",
"variant": "direction=vertical"
}
],
"related": [
{
"slug": "cell",
"why": "步骤条展示流程进度;具体每一步的详情入口用单元格列出来"
},
{
"slug": "mobile-tag",
"why": "单条记录的状态用标签;只有多步流程才用步骤条"
},
{
"slug": "mobile-divider",
"why": "步骤之间靠连接线表达先后关系,不要用分割线替代"
}
]
}