排版Typography
把一段文字按信息层级(标题 / 正文 / 辅助 / 次要)成套地表达,并可选单行省略与一键复制
数据展示 规格 37 · 排版 Typography 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-typography.css">
<!-- ③ 结构照抄下方任一演示块(类名与 6 端实现一致) -->
演示
每个演示都是真实渲染:预览帧加载 frameworks-mobile/Typography.html?demo=<id>(只显示该演示块),代码是该演示块在演示页里的原文,可复制。全部演示同屏可看 演示页 ↗。
01 组件类型
标题 / 正文 / 辅助 / 次要:字号与字色全部走令牌,层级差异一眼可辨而不靠颜色深浅硬撑。
查看代码(演示页原文 · 23 行)
<section class="demo-block" data-demo="levels">
<p class="demo-label">四个层级(level=title / body / assist / secondary,字号与字色全部走令牌)</p>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--title" data-assert="typography-title">
<span class="kole-m-typography__text">待付款订单</span>
</div>
</div>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--body" data-assert="typography-body">
<span class="kole-m-typography__text">正文用于承载主体内容,移动端 16px 比桌面端 14px 更大。</span>
</div>
</div>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--assist" data-assert="typography-assist">
<span class="kole-m-typography__text">辅助说明:补充正文但没有正文重要</span>
</div>
</div>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--secondary" data-assert="typography-secondary">
<span class="kole-m-typography__text">次要信息:2026-09-20 09:12</span>
</div>
</div>
</section>默认不省略,长文本按容器宽度换行;行高取 1.5,比桌面端的 1.4 更宽以便手指阅读定位。
查看代码(演示页原文 · 8 行)
<section class="demo-block" data-demo="paragraph">
<p class="demo-label">段落正文(默认不省略,长文本按容器宽度换行)</p>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--body" data-assert="typography-paragraph">
<span class="kole-m-typography__text">移动端的正文行高取 1.5 而非桌面的 1.4:手指阅读时行距更宽,长段落的回归定位更省力。层级只表达信息次序,不表达重要性判断。</span>
</div>
</div>
</section>02 组件状态
ellipsis=true:超宽截断、省略号在行尾;读屏仍能读到完整文字,被截断的内容不提供展开入口。
查看代码(演示页原文 · 8 行)
<section class="demo-block" data-demo="ellipsis">
<p class="demo-label">单行省略(ellipsis=true:超宽截断,省略号在行尾;多行省略不在本组件范围)</p>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--body kole-m-typography--ellipsis" data-assert="typography-ellipsis">
<span class="kole-m-typography__text">杭州市余杭区文一西路 969 号淘宝城 5 号楼 3 层西侧会议室</span>
</div>
</div>
</section>copyable=true:复制按钮热区 44px;点一下标记已复制,组件不读剪贴板,写入由宿主完成。
查看代码(演示页原文 · 18 行)
<section class="demo-block" data-demo="copyable">
<p class="demo-label">可复制(copyable=true:点右侧按钮复制,宿主写剪贴板后加 is-copied)</p>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--body kole-m-typography--copyable" id="typo-copy"
data-assert="typography-copyable">
<span class="kole-m-typography__text">订单号 20260920-8841</span>
<button class="kole-m-typography__copy" type="button" aria-label="复制订单号"
data-behavior="click-sets-attr:#typo-copy|data-copied|true">复制</button>
</div>
</div>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--assist kole-m-typography--copyable is-copied"
data-assert="typography-copied">
<span class="kole-m-typography__text">已复制态(is-copied:按钮文字与色变作为反馈)</span>
<button class="kole-m-typography__copy" type="button" aria-label="已复制">已复制</button>
</div>
</div>
</section>API
props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs
Props
| 名称 | 类型 | 默认值 | 说明 | 必传 |
|---|---|---|---|---|
level | 'title' | 'body' | 'assist' | 'secondary' | 'body' | 变体 level:文字层级(规格 §37.3) | N |
ellipsis | boolean | false | 变体 ellipsis:单行省略,省略号在行尾(规格 §37.3) | N |
copyable | boolean | false | 变体 copyable:右侧出现复制按钮(规格 §37.3) | N |
copyText | string | '复制' | 复制按钮的常态文字,同时作为 aria-label(规格 §37.6) | N |
copiedText | string | '已复制' | copied=true 时复制按钮的文字(规格 §37.4) | N |
text | string | '' | 文字内容;与默认插槽二选一,text 优先(规格 §37.2 text) | N |
copied | boolean | false | 状态 copied:宿主写剪贴板成功后置 true(规格 §37.4) | N |
「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。
事件
| 名称 | 参数 | 说明 |
|---|---|---|
copy | — | 点击复制按钮时触发;真正的剪贴板写入由宿主完成(规格 §37.5) |
插槽
| 名称 | 说明 |
|---|---|
default | 文字内容(与 text 二选一;text 优先) |
CSS 变量
组件级变量(在组件样式表里定义)。业务侧可在自己的作用域内覆盖,不必改组件源码。
| 名称 | 默认值 | 说明 |
|---|---|---|
--kole-m-typography-gap | var(--kole-space-8) | 组件内部默认值,可在业务侧覆盖 |
何时使用
- 把一段文字按信息层级(标题 / 正文 / 辅助 / 次要)成套地表达,并可选单行省略与一键复制
- 移动端正文基准 16px(桌面端 14px),层级只分四级:再细分会让差异小于字号可辨阈值
- 组件本体是静态的:根与文字节点不带指针语义,也不进键盘序列
- 复制动作由宿主完成 —— 组件不读系统剪贴板,只回传 copy 事件
- 省略形态下读屏仍能读到完整文字(text-overflow 只截视觉,不改可访问名)
交互与触控
- 组件本体是静态的:根与文字节点不带指针语义,也不进键盘序列
- 唯一的交互元素是复制按钮:热区 44×44px,一次轻点触发 copy 事件
- 复制动作由宿主完成:组件不读系统剪贴板,组件只回传事件并在宿主置 copied=true 后渲染反馈
- ellipsis=true 时只做单行省略:被截断的内容不提供展开入口
无障碍
- 根是普通容器,文字由读屏按文档流朗读;层级只改视觉,不改语义标签
- 省略形态下读屏仍能读到完整文字
- 复制按钮是原生 button 且带 aria-label,名称随状态在「复制」与「已复制」之间切换
相似组件
从「该用哪一个」的角度区分;PC 端的对应实现见 PC 文档站。
| 组件 | 何时用它而不是本组件 |
|---|---|
| 链接Link | 文字需要可点击跳转时用链接,排版只负责层级与省略、不承担点击语义 |
| 标签Tag | 行内状态标记用标签,不要用排版加粗加色来表达状态 |
| 单元格Cell | 带副标题的两行列表信息用单元格(标题 + 说明一次成组),单个文字层级才用排版 |
规格未定 / 禁止发明
| 类别 | 条目 |
|---|---|
| 禁止发明 | 富文本与 Markdown 渲染(换行、加粗、链接、代码块一律由宿主负责) |
| 禁止发明 | 多行省略(line-clamp)与「展开全文」交互 |
| 禁止发明 | 字号缩放 / 用户字号偏好档位 |
| 禁止发明 | 复制失败的兜底提示(宿主自行组合轻提示) |
| 规格未定 | secondary 层级是否应使用 11px(当前取 --kole-m-font-size-caption)而不是 12px |
| 规格未定 | 复制按钮在「已复制」态停留多久(当前不自动回退,由宿主控制) |
| 规格未定 | 省略态是否需要在长按气泡里显示全文 |
结构(anatomy)
| 字段 | 说明 |
|---|---|
typography | 根元素,横向承载「文字 + 可选复制按钮」 |
text | 文字本体,层级、省略、换行都在它身上生效;默认插槽与 text prop 二者取一(text 优先) |
copy | 复制按钮(原生 button),仅 copyable=true 时出现;热区 44px,负外边距吸收不撑高行 |
变体维度与类名映射
类名映射由构建脚本从契约 variantClasses 生成,并被 verify:mobile-docs 逐条对照组件 CSS 校验(类/变量必须真实存在)。
| 维度 | 取值 | 对应类名 / 变量 |
|---|---|---|
level | title / body / assist / secondary | title .kole-m-typography--title body .kole-m-typography--body assist .kole-m-typography--assist secondary .kole-m-typography--secondary |
ellipsis | false / true | false (由数据驱动,无专属类) true .kole-m-typography--ellipsis |
copyable | false / true | false (由数据驱动,无专属类) true .kole-m-typography--copyable |
代表变体
| 变体 | 标签 |
|---|---|
level=title | 标题(17px / 600) |
level=body | 正文(16px / 400,移动端基准) |
level=assist | 辅助说明(14px / 次级色) |
level=secondary | 次要信息(11px / 三级色) |
用到的令牌
构建时从本组件样式表扫描得出。蓝色为移动端自有令牌,绿色为继承的 PC 令牌(改一处两端生效)。
6 端源码
同一组件的六份实现(生产环境的类名与结构一致,差异只在技术栈写法与单位)。点开查看,右侧可复制。
frameworks-mobile/Typography.css · 纯样式(CSS) · 90 行
/* Kole UI Mobile · Typography 样式 — 对齐移动端规格 §37
文字层级:标题 / 正文 / 辅助 / 次要;字号与字色全部走令牌,本文件不写死字号。
可省略(ellipsis)只在单行生效;可复制(copyable)右侧给一个原生 button —— 复制动作本身
由宿主负责(本组件不读剪贴板),宿主完成后给根元素加 is-copied 作为反馈。
静态组件:根与文字节点不带 cursor:pointer,唯一的可点元素是复制按钮(原生 button)。 */
.kole-m-typography {
--kole-m-typography-gap: var(--kole-space-8);
box-sizing: border-box;
display: flex;
align-items: center;
gap: var(--kole-m-typography-gap);
color: var(--kole-color-text-body);
font-family: var(--kole-font-family);
font-size: var(--kole-m-font-size-body);
line-height: 1.5;
}
.kole-m-typography__text {
flex: 1 1 auto;
min-width: 0;
margin: 0;
}
/* 变体 level:四个层级(标题 / 正文 / 辅助 / 次要) */
.kole-m-typography--title {
font-size: var(--kole-m-font-size-title);
font-weight: 600;
line-height: 1.4;
color: var(--kole-color-text-title);
}
.kole-m-typography--body {
font-size: var(--kole-m-font-size-body);
color: var(--kole-color-text-body);
}
.kole-m-typography--assist {
font-size: var(--kole-m-font-size-label);
color: var(--kole-color-text-secondary);
}
.kole-m-typography--secondary {
font-size: var(--kole-m-font-size-caption);
color: var(--kole-color-text-tertiary);
}
/* 变体 ellipsis=true:单行省略(多行省略不在本组件范围内) */
.kole-m-typography--ellipsis .kole-m-typography__text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* 变体 copyable=true:右侧出现复制按钮(热区 44px,负外边距吸收,不撑高行) */
.kole-m-typography--copyable .kole-m-typography__copy {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-width: var(--kole-m-touch-target);
height: var(--kole-m-touch-target);
margin-inline-end: calc(-1 * var(--kole-space-8));
padding: 0;
border: 0;
border-radius: var(--kole-radius-base);
background: none;
color: var(--kole-color-brand);
font-family: inherit;
font-size: var(--kole-m-font-size-label);
line-height: 1;
cursor: pointer;
touch-action: manipulation;
}
.kole-m-typography--copyable .kole-m-typography__copy:active {
background: var(--kole-color-brand-bg);
}
.kole-m-typography--copyable .kole-m-typography__copy:focus-visible {
outline: 2px solid var(--kole-color-focus-ring);
outline-offset: -2px;
}
/* 状态 is-copied:宿主复制成功后加的反馈(色变,不改文字宽度以免布局跳动) */
.kole-m-typography.is-copied .kole-m-typography__copy {
color: var(--kole-color-success);
}
frameworks-mobile/Typography.html · H5 原生(无框架) · 123 行
<!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 · Typography(H5)</title>
<link rel="stylesheet" href="../.design_library/kole-ui-mobile/colors_and_type.css">
<link rel="stylesheet" href="Typography.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-box { padding: var(--kole-space-12) var(--kole-m-gutter); background: var(--kole-color-card-bg);
border-block: 1px solid var(--kole-color-border); }
.demo-box + .demo-box { border-top: 0; }
.demo-block[hidden] { display: none; }
</style>
</head>
<body>
<div class="demo">
<section class="demo-block" data-demo="levels">
<p class="demo-label">四个层级(level=title / body / assist / secondary,字号与字色全部走令牌)</p>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--title" data-assert="typography-title">
<span class="kole-m-typography__text">待付款订单</span>
</div>
</div>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--body" data-assert="typography-body">
<span class="kole-m-typography__text">正文用于承载主体内容,移动端 16px 比桌面端 14px 更大。</span>
</div>
</div>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--assist" data-assert="typography-assist">
<span class="kole-m-typography__text">辅助说明:补充正文但没有正文重要</span>
</div>
</div>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--secondary" data-assert="typography-secondary">
<span class="kole-m-typography__text">次要信息:2026-09-20 09:12</span>
</div>
</div>
</section>
<section class="demo-block" data-demo="paragraph">
<p class="demo-label">段落正文(默认不省略,长文本按容器宽度换行)</p>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--body" data-assert="typography-paragraph">
<span class="kole-m-typography__text">移动端的正文行高取 1.5 而非桌面的 1.4:手指阅读时行距更宽,长段落的回归定位更省力。层级只表达信息次序,不表达重要性判断。</span>
</div>
</div>
</section>
<section class="demo-block" data-demo="ellipsis">
<p class="demo-label">单行省略(ellipsis=true:超宽截断,省略号在行尾;多行省略不在本组件范围)</p>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--body kole-m-typography--ellipsis" data-assert="typography-ellipsis">
<span class="kole-m-typography__text">杭州市余杭区文一西路 969 号淘宝城 5 号楼 3 层西侧会议室</span>
</div>
</div>
</section>
<section class="demo-block" data-demo="copyable">
<p class="demo-label">可复制(copyable=true:点右侧按钮复制,宿主写剪贴板后加 is-copied)</p>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--body kole-m-typography--copyable" id="typo-copy"
data-assert="typography-copyable">
<span class="kole-m-typography__text">订单号 20260920-8841</span>
<button class="kole-m-typography__copy" type="button" aria-label="复制订单号"
data-behavior="click-sets-attr:#typo-copy|data-copied|true">复制</button>
</div>
</div>
<div class="demo-box">
<div class="kole-m-typography kole-m-typography--assist kole-m-typography--copyable is-copied"
data-assert="typography-copied">
<span class="kole-m-typography__text">已复制态(is-copied:按钮文字与色变作为反馈)</span>
<button class="kole-m-typography__copy" type="button" aria-label="已复制">已复制</button>
</div>
</div>
</section>
</div>
<script>
/* 演示页交互:点复制按钮 → 标记已复制并切换反馈态。
真实业务里宿主在这里写剪贴板;演示页只做状态反馈,不读系统剪贴板。 */
(function () {
var box = document.getElementById('typo-copy');
if (!box) return;
var btn = box.querySelector('.kole-m-typography__copy');
if (!btn) return;
btn.addEventListener('click', function () {
box.classList.add('is-copied');
box.setAttribute('data-copied', 'true');
btn.textContent = '已复制';
btn.setAttribute('aria-label', '已复制');
});
})();
</script>
<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');
var demoBox = document.querySelector('.demo');
if (demoBox) demoBox.style.minHeight = 'auto';
blocks.forEach(function (b) {
var label = b.querySelector('.demo-label');
if (label && !b.hidden) label.hidden = true;
});
})();
</script>
</body>
</html>
frameworks-mobile/Typography.jsx · React · 45 行
import React from 'react';
import './Typography.css';
/* 排版(移动端)— 规格 §37
静态文字组件:层级(title / body / assist / secondary)、单行省略、可复制。
复制动作由宿主完成(onCopy 回调里写剪贴板),组件只渲染按钮与反馈位 is-copied。
根与文字节点不带指针语义,唯一可点元素是原生 button。 */
export default function Typography({
level = 'body',
ellipsis = false,
copyable = false,
copyText = '复制',
copiedText = '已复制',
text = '',
copied = false,
onCopy,
children = null,
}) {
const content = text || children;
const cls =
'kole-m-typography' +
` kole-m-typography--${level}` +
(ellipsis ? ' kole-m-typography--ellipsis' : '') +
(copyable ? ' kole-m-typography--copyable' : '') +
(copied ? ' is-copied' : '');
return (
<div className={cls}>
<span className="kole-m-typography__text">{content}</span>
{copyable ? (
<button
className="kole-m-typography__copy"
type="button"
aria-label={copied ? copiedText : copyText}
onClick={() => {
if (onCopy) onCopy();
}}
>
{copied ? copiedText : copyText}
</button>
) : null}
</div>
);
}
frameworks-mobile/Typography.vue2.vue · Vue 2 · 40 行
<template>
<div class="kole-m-typography" :class="typoClass">
<span class="kole-m-typography__text"><slot>{{ text }}</slot></span>
<button
v-if="copyable"
class="kole-m-typography__copy"
type="button"
:aria-label="copied ? copiedText : copyText"
@click="$emit('copy')"
>{{ copied ? copiedText : copyText }}</button>
</div>
</template>
<script>
export default {
name: 'KoleMTypography',
props: {
level: { type: String, default: 'body' },
ellipsis: { type: Boolean, default: false },
copyable: { type: Boolean, default: false },
copyText: { type: String, default: '复制' },
copiedText: { type: String, default: '已复制' },
text: { type: String, default: '' },
copied: { type: Boolean, default: false }
},
computed: {
typoClass: function () {
return [
'kole-m-typography--' + this.level,
this.ellipsis ? 'kole-m-typography--ellipsis' : '',
this.copyable ? 'kole-m-typography--copyable' : '',
this.copied ? 'is-copied' : ''
].filter(Boolean);
}
}
};
</script>
<style src="./Typography.css"></style>
frameworks-mobile/Typography.vue3.vue · Vue 3 · 48 行
<template>
<div class="kole-m-typography" :class="typoClass">
<span class="kole-m-typography__text">{{ content }}</span>
<button
v-if="copyable"
class="kole-m-typography__copy"
type="button"
:aria-label="copied ? copiedText : copyText"
@click="onCopyClick"
>{{ copied ? copiedText : copyText }}</button>
</div>
</template>
<script setup>
import { computed, useSlots } from 'vue';
const props = defineProps({
level: { type: String, default: 'body' },
ellipsis: { type: Boolean, default: false },
copyable: { type: Boolean, default: false },
copyText: { type: String, default: '复制' },
copiedText: { type: String, default: '已复制' },
text: { type: String, default: '' },
copied: { type: Boolean, default: false }
});
const emit = defineEmits(['copy']);
const slots = useSlots();
const content = computed(() => {
if (props.text) return props.text;
const nodes = slots.default ? slots.default() : [];
return nodes.map((n) => (typeof n.children === 'string' ? n.children : '')).join('');
});
const typoClass = computed(() => [
`kole-m-typography--${props.level}`,
props.ellipsis ? 'kole-m-typography--ellipsis' : '',
props.copyable ? 'kole-m-typography--copyable' : '',
props.copied ? 'is-copied' : ''
].filter(Boolean));
function onCopyClick() {
emit('copy');
}
</script>
<style src="./Typography.css"></style>
frameworks-mobile/Typography.uniapp.vue · uni-app(跨端:小程序 / App / H5) · 119 行
<template>
<view class="kole-m-typography" :class="typoClass">
<text class="kole-m-typography__text">{{ content }}</text>
<view
v-if="copyable"
class="kole-m-typography__copy"
role="button"
:aria-label="copied ? copiedText : copyText"
@tap="onCopyTap"
>
<text>{{ copied ? copiedText : copyText }}</text>
</view>
</view>
</template>
<script setup>
/* uni-app 端 · 排版(移动端)— 规格 §37
跨端差异:用 view / text;复制按钮用 view + role="button" + @tap;
尺寸用 rpx(2rpx ≈ 1px),44px 触控最小边长写成 88rpx。
复制动作由宿主完成(本端不读剪贴板 API),组件只回传 copy 事件。 */
import { computed } from 'vue';
const props = defineProps({
level: { type: String, default: 'body' },
ellipsis: { type: Boolean, default: false },
copyable: { type: Boolean, default: false },
copyText: { type: String, default: '复制' },
copiedText: { type: String, default: '已复制' },
text: { type: String, default: '' },
copied: { type: Boolean, default: false }
});
const emit = defineEmits(['copy']);
const typoClass = computed(() => [
`kole-m-typography--${props.level}`,
props.ellipsis ? 'kole-m-typography--ellipsis' : '',
props.copyable ? 'kole-m-typography--copyable' : '',
props.copied ? 'is-copied' : ''
].filter(Boolean));
const content = computed(() => props.text);
function onCopyTap() {
emit('copy');
}
</script>
<style>
.kole-m-typography {
--kole-m-typography-gap: 16rpx;
--kole-m-touch-target: 88rpx;
--kole-m-font-size-body: 32rpx;
--kole-m-font-size-label: 28rpx;
--kole-m-font-size-title: 34rpx;
--kole-m-font-size-caption: 22rpx;
box-sizing: border-box;
display: flex;
align-items: center;
gap: var(--kole-m-typography-gap);
color: var(--kole-color-text-body);
font-size: var(--kole-m-font-size-body);
line-height: 1.5;
}
.kole-m-typography__text {
flex: 1;
min-width: 0;
}
.kole-m-typography--title {
font-size: var(--kole-m-font-size-title);
font-weight: 600;
line-height: 1.4;
color: var(--kole-color-text-title);
}
.kole-m-typography--body {
font-size: var(--kole-m-font-size-body);
color: var(--kole-color-text-body);
}
.kole-m-typography--assist {
font-size: var(--kole-m-font-size-label);
color: var(--kole-color-text-secondary);
}
.kole-m-typography--secondary {
font-size: var(--kole-m-font-size-caption);
color: var(--kole-color-text-tertiary);
}
.kole-m-typography--ellipsis .kole-m-typography__text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.kole-m-typography__copy {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-width: var(--kole-m-touch-target);
height: var(--kole-m-touch-target);
margin-right: -16rpx;
border-radius: 8rpx;
color: var(--kole-color-brand);
font-size: var(--kole-m-font-size-label);
}
.kole-m-typography--copyable .kole-m-typography__copy {
color: var(--kole-color-brand);
}
.kole-m-typography.is-copied .kole-m-typography__copy {
color: var(--kole-color-success);
}
</style>
测试与回归
断言在真实的 375×640 设备帧里跑(引擎与 PC 侧共用 tests/_runtime.js,触控行为动词来自移动端 tests/mobile/_behaviors.js)。
断言 19 条 · 全部通过 报告 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-typography.json(点击展开原始 JSON)
{
"schemaVersion": 1,
"sourceKind": "authored-spec",
"provenance": "authored-in-repo",
"specFile": "spec/移动端规格.md",
"specSection": "37 · 排版 Typography",
"confidence": "high",
"slug": "mobile-typography",
"name": "排版 Typography",
"semanticTypeCandidates": [
"typography",
"text",
"paragraph"
],
"variantDimensions": [
{
"name": "level",
"values": [
"title",
"body",
"assist",
"secondary"
]
},
{
"name": "ellipsis",
"values": [
"false",
"true"
]
},
{
"name": "copyable",
"values": [
"false",
"true"
]
}
],
"representativeVariants": [
{
"level": "title",
"label": "标题(17px / 600)"
},
{
"level": "body",
"label": "正文(16px / 400,移动端基准)"
},
{
"level": "assist",
"label": "辅助说明(14px / 次级色)"
},
{
"level": "secondary",
"label": "次要信息(11px / 三级色)"
}
],
"anatomy": {
"typography": "根元素,横向承载「文字 + 可选复制按钮」",
"text": "文字本体,层级、省略、换行都在它身上生效;默认插槽与 text prop 二者取一(text 优先)",
"copy": "复制按钮(原生 button),仅 copyable=true 时出现;热区 44px,负外边距吸收不撑高行"
},
"structurePatterns": {
"level": "title / body / assist / secondary(字号与字色全部走令牌)",
"ellipsis": "false 换行 / true 单行省略(省略号在行尾)",
"copyable": "false 纯展示 / true 右侧出现复制按钮"
},
"usageHints": [
"把一段文字按信息层级(标题 / 正文 / 辅助 / 次要)成套地表达,并可选单行省略与一键复制",
"移动端正文基准 16px(桌面端 14px),层级只分四级:再细分会让差异小于字号可辨阈值",
"组件本体是静态的:根与文字节点不带指针语义,也不进键盘序列",
"复制动作由宿主完成 —— 组件不读系统剪贴板,只回传 copy 事件",
"省略形态下读屏仍能读到完整文字(text-overflow 只截视觉,不改可访问名)"
],
"doNotInvent": [
"富文本与 Markdown 渲染(换行、加粗、链接、代码块一律由宿主负责)",
"多行省略(line-clamp)与「展开全文」交互",
"字号缩放 / 用户字号偏好档位",
"复制失败的兜底提示(宿主自行组合轻提示)"
],
"unknowns": [
"secondary 层级是否应使用 11px(当前取 --kole-m-font-size-caption)而不是 12px",
"复制按钮在「已复制」态停留多久(当前不自动回退,由宿主控制)",
"省略态是否需要在长按气泡里显示全文"
],
"interaction": [
"组件本体是静态的:根与文字节点不带指针语义,也不进键盘序列",
"唯一的交互元素是复制按钮:热区 44×44px,一次轻点触发 copy 事件",
"复制动作由宿主完成:组件不读系统剪贴板,组件只回传事件并在宿主置 copied=true 后渲染反馈",
"ellipsis=true 时只做单行省略:被截断的内容不提供展开入口"
],
"accessibility": [
"根是普通容器,文字由读屏按文档流朗读;层级只改视觉,不改语义标签",
"省略形态下读屏仍能读到完整文字",
"复制按钮是原生 button 且带 aria-label,名称随状态在「复制」与「已复制」之间切换"
],
"api": {
"source": "implementation",
"note": "props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs",
"requiredNote": "「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。",
"props": [
{
"name": "level",
"type": "'title' | 'body' | 'assist' | 'secondary'",
"default": "'body'",
"desc": "变体 level:文字层级(规格 §37.3)",
"required": false
},
{
"name": "ellipsis",
"type": "boolean",
"default": "false",
"desc": "变体 ellipsis:单行省略,省略号在行尾(规格 §37.3)",
"required": false
},
{
"name": "copyable",
"type": "boolean",
"default": "false",
"desc": "变体 copyable:右侧出现复制按钮(规格 §37.3)",
"required": false
},
{
"name": "copyText",
"type": "string",
"default": "'复制'",
"desc": "复制按钮的常态文字,同时作为 aria-label(规格 §37.6)",
"required": false
},
{
"name": "copiedText",
"type": "string",
"default": "'已复制'",
"desc": "copied=true 时复制按钮的文字(规格 §37.4)",
"required": false
},
{
"name": "text",
"type": "string",
"default": "''",
"desc": "文字内容;与默认插槽二选一,text 优先(规格 §37.2 text)",
"required": false
},
{
"name": "copied",
"type": "boolean",
"default": "false",
"desc": "状态 copied:宿主写剪贴板成功后置 true(规格 §37.4)",
"required": false
}
],
"events": [
{
"name": "copy",
"params": "—",
"desc": "点击复制按钮时触发;真正的剪贴板写入由宿主完成(规格 §37.5)"
}
],
"slots": [
{
"name": "default",
"desc": "文字内容(与 text 二选一;text 优先)"
}
]
},
"variantClasses": {
"level": {
"title": [
".kole-m-typography--title"
],
"body": [
".kole-m-typography--body"
],
"assist": [
".kole-m-typography--assist"
],
"secondary": [
".kole-m-typography--secondary"
]
},
"ellipsis": {
"false": [],
"true": [
".kole-m-typography--ellipsis"
]
},
"copyable": {
"false": [],
"true": [
".kole-m-typography--copyable"
]
}
},
"demos": [
{
"id": "levels",
"group": "01 组件类型",
"title": "四个层级",
"desc": "标题 / 正文 / 辅助 / 次要:字号与字色全部走令牌,层级差异一眼可辨而不靠颜色深浅硬撑。",
"variant": "level=title|body|assist|secondary"
},
{
"id": "paragraph",
"group": "01 组件类型",
"title": "段落正文",
"desc": "默认不省略,长文本按容器宽度换行;行高取 1.5,比桌面端的 1.4 更宽以便手指阅读定位。",
"variant": "level=body"
},
{
"id": "ellipsis",
"group": "02 组件状态",
"title": "单行省略",
"desc": "ellipsis=true:超宽截断、省略号在行尾;读屏仍能读到完整文字,被截断的内容不提供展开入口。",
"variant": "ellipsis=true"
},
{
"id": "copyable",
"group": "02 组件状态",
"title": "可复制",
"desc": "copyable=true:复制按钮热区 44px;点一下标记已复制,组件不读剪贴板,写入由宿主完成。",
"variant": "copyable=true"
}
],
"related": [
{
"slug": "mobile-link",
"why": "文字需要可点击跳转时用链接,排版只负责层级与省略、不承担点击语义"
},
{
"slug": "mobile-tag",
"why": "行内状态标记用标签,不要用排版加粗加色来表达状态"
},
{
"slug": "cell",
"why": "带副标题的两行列表信息用单元格(标题 + 说明一次成组),单个文字层级才用排版"
}
]
}