ZhaoUI 视觉演示

DESIGN LANGUAGE · v0.1.0

ZhaoUI

设计语言与组件库

一套面向中后台与消费端的跨平台设计系统。色彩由 OKLCH 生成,用 Token 统一品牌、亮暗、密度与风格,让每一个界面既克制又精致。

  • B 端 / C 端
  • Vue 3 + TypeScript
  • OKLCH
  • Lucide 图标
品牌色阶跟随右上角品牌色切换
语义色与控件
成功警告危险信息
主要按钮次要按钮幽灵按钮
字体
Aa 赵
PingFang SC / Segoe UI
圆角
B 端 4–10 · C 端 10–24

OVERVIEW

体系概览

ZhaoUI 由一份唯一的 Token 数据源驱动。改动色彩或风格只需在 Token 包里调整,所有组件与演示页自动跟随。

01

主题维度

六个维度可自由组合,互不干扰。

  • 品牌
  • 亮暗
  • 密度
  • 受众
  • 涨跌色
  • 风格
02

双受众

B 端追求信息密度与效率,C 端强调品牌感与触达。

  • B 端 · 14px
  • C 端 · 16px
03

Token 分层

基础与语义两层已落地,组件层持续补充。

  • 基础
  • 语义
  • 组件(规划)
04

技术栈

Vue 3 + TypeScript + Vite,无头组件基于 Reka UI。

  • Vue 3
  • Reka UI
  • Nuxt SSR
05

跨端策略

Electron 复用 Web;移动端仅提供 H5 适配。

  • Web
  • Electron
  • H5
06

浏览器基线

色彩在构建期由 OKLCH 生成并输出为 HEX;玻璃风格依赖 backdrop-filter,不支持时退回实色。

  • Chrome ≥ 111
  • Safari ≥ 16.4
  • Firefox ≥ 113
  • 建议值

章节地图

快速开始

原生版只需要两个文件:zhaoui.css 与 zhaoui.js,不依赖框架与构建工具。组件用 data-zh-* 属性声明,也可以调用全局对象 ZhaoUI。版本 0.1.0。

引入

<link rel="stylesheet" href="zhaoui.css">

<button class="zh-btn primary" data-zh-open="#dlg">打开</button>
<dialog class="zh-modal" id="dlg" aria-labelledby="t">
  <header id="t">你好</header>
  <div class="body">这是 ZhaoUI 的模态框。</div>
  <footer><button class="zh-btn primary" data-zh-close>知道了</button></footer>
</dialog>

<script src="zhaoui.js"></script>   <!-- 放在 </body> 前 -->

文件

文件说明大小
zhaoui.cssToken 变量 + 全部组件样式189.3 KB → 165.0 KB,gzip 28.3 KB
modular/core.css核心:Token、基础、通用组件样式156.8 KB → 136.4 KB,gzip 24.0 KB
modular/select.css下拉选择与级联选择的样式8.8 KB → 7.8 KB,gzip 2.0 KB
modular/upload.css文件上传的样式4.2 KB → 3.7 KB,gzip 1.1 KB
modular/tree.css树的样式4.4 KB → 3.9 KB,gzip 1.2 KB
modular/chart.css基础图表的样式2.5 KB → 2.2 KB,gzip 0.9 KB
modular/command.css命令面板的样式3.2 KB → 2.9 KB,gzip 1.0 KB
modular/date.css日期、日期范围、时间、日期时间选择的样式10.0 KB → 8.9 KB,gzip 2.1 KB
zhaoui-utilities.css可选:公共类(间距、布局、文字、响应式显示隐藏、动画)65.1 KB → 56.0 KB,gzip 9.1 KB
zhaoui-marketing.css可选:营销页区块(页宽、区块、hero、特性卡、价格卡)10.9 KB → 8.1 KB,gzip 2.2 KB
zhaoui-style-glass.css可选:玻璃拟态风格包(实验性)16.5 KB → 14.0 KB,gzip 1.9 KB
zhaoui-icons.js可选:完整 Lucide 图标集(372 个)67.0 KB → 62.6 KB,gzip 16.0 KB
zhaoui.js主题运行时 + 全部组件行为 + 内置图标 + 自动初始化264.2 KB → 179.8 KB,gzip 55.7 KB
modular/brand.js品牌色运行时:ZhaoUI.setBrand()、Logo 取色(可选)22.6 KB → 13.7 KB,gzip 6.1 KB
modular/core.js核心:组件注册与自动初始化 + 通用组件行为(必须最先加载)123.8 KB → 83.9 KB,gzip 25.3 KB
modular/select.js下拉选择与级联选择的行为(在 core.js 之后加载)35.9 KB → 23.2 KB,gzip 7.5 KB
modular/upload.js文件上传的行为(在 core.js 之后加载)9.3 KB → 7.3 KB,gzip 3.0 KB
modular/tree.js树的行为(在 core.js 之后加载)7.7 KB → 5.5 KB,gzip 2.2 KB
modular/chart.js基础图表的行为(在 core.js 之后加载)12.4 KB → 8.7 KB,gzip 3.7 KB
modular/command.js命令面板的行为(在 core.js 之后加载)7.6 KB → 5.5 KB,gzip 2.5 KB
modular/date.js日期、日期范围、时间、日期时间选择的行为(在 core.js 之后加载)48.6 KB → 34.0 KB,gzip 10.1 KB

主题属性 <html data-*>

属性取值说明
data-audienceb | c默认 b。B 端 / C 端:字号、圆角、控件高度、动效、阴影
data-modelight | dark默认 light。不会自动跟随系统
data-densitycompact | standard | comfortable默认 standard
data-trendcn | intl默认 cn。红涨绿跌 / 绿涨红跌
data-brandindigo | teal | coral | violet | rose默认 indigo;任意颜色用 ZhaoUI.setBrand(hex)
data-styleflat | glass默认 flat。glass 需另引风格包,实验性

用代码切换:ZhaoUI.setTheme({ mode: 'dark', audience: 'c' })、ZhaoUI.setBrand('#00857f')。完整文档在 /Users/zhaowei/project/sheji/zhaoui/packages/ui/docs/,Claude Code 技能在 zhaoui/skills/zhaoui/。

组件调用

下面是已经抽成独立版的组件。每个组件的标记、属性、方法和事件都取自 meta.json,与 Markdown 文档、技能资料同源;示例代码由自动测试在 Chromium、Firefox、WebKit 里原样运行过。演示页里其余的组件还没有独立版。

锚点目录

长页面的“本页目录”:一列指向页内各节的链接,滚动时自动标出当前所在的一节,点击平滑滚动过去。适合文档、设置页的右侧。

<div style="display:flex;gap:24px;align-items:flex-start">
  <nav data-zh-anchor data-target="#an-box" data-offset="8" style="width:12rem;flex:none">
    <ul>
      <li><a href="#an-a">简介</a></li>
      <li><a href="#an-b">用法</a>
        <ul><li><a href="#an-c">属性</a></li></ul>
      </li>
    </ul>
  </nav>
  <div id="an-box" tabindex="0" role="region" aria-label="文档内容" style="height:12rem;overflow:auto;flex:1;min-width:0;padding:0 16px;border:1px solid var(--zh-border);border-radius:var(--zh-radius-md)">
    <p id="an-a" style="margin-top:0"><strong>简介</strong></p><p style="height:7rem">锚点目录适合长页面。</p>
    <p id="an-b"><strong>用法</strong></p><p style="height:7rem">把目录放在内容旁边。</p>
    <p id="an-c"><strong>属性</strong></p><p style="height:9rem">data-offset、data-target。</p>
  </div>
</div>
属性用在取值说明
data-zh-anchor<nav>里面放 <ul><li><a href="#节的id">声明为锚点目录;嵌套的 <ul> 表示二级标题
data-offset<nav>像素,默认 16滚动后目标离顶部保留的距离;页面有吸顶导航条时设成它的高度
data-target<nav>选择器内容在某个可滚动容器里时,监听它而不是整个页面
事件触发于detail说明
zh:anchor-change<nav>undefined当前所在的一节变化

头像

图片头像;图片缺失或加载失败时,用姓名首字(中文取第一个字,英文取首字母)和由姓名决定的颜色代替。

<span class="zh-avatar" data-name="张三" data-size="md"></span>
<span class="zh-avatar" data-name="Ada Lovelace" data-size="md"></span>
<span class="zh-avatar" data-name="设计" data-size="md" data-shape="square"></span>
<span class="zh-avatar" data-name="在线" data-size="lg" data-status="success"></span>
属性用在取值说明
data-name.zh-avatar字符串姓名;必填,库据此初始化,也是无障碍名称
data-src.zh-avatar图片地址头像图片;加载失败自动退回首字
data-size.zh-avatarxs | sm | md | lg | xl尺寸
data-shape.zh-avatarsquare | circle(默认)方形头像,默认圆形
data-icon.zh-avatar图标名用图标代替首字
data-status.zh-avatarsuccess | warning | danger | primary右下角的在线状态点
data-tone头像brand用品牌色代替按名字挑选的 8 种分类色,适合一个品牌页里所有头像颜色一致

返回顶部

滚动超过一段距离后出现的圆形按钮,点击平滑回到顶部;系统开启“减少动态效果”时直接跳回。

<button class="zh-backtop" data-zh-backtop data-threshold="200"></button>
属性用在取值说明
data-zh-backtop<button>(可为空)声明为返回顶部按钮;空按钮会自动放入向上的箭头图标和 aria-label
data-threshold按钮像素,默认 400滚动超过多少像素后出现
data-target按钮选择器监听某个可滚动容器而不是整页;这时按钮不会固定在视口,需要你自己定位
事件触发于detail说明
zh:backtop按钮无点击回到顶部时触发

徽标

挂在头像、图标、按钮角上的数字、红点或文字徽标。数量为 0 时自动隐藏,超过上限显示 99+。

<span class="zh-bdg"><button class="zh-btn" aria-label="消息">消息</button><span class="zh-bdg-mark" id="unread" data-count="5" data-tone="danger" data-label="条未读"></span></span>
<span class="zh-bdg"><button class="zh-btn">待办</button><span class="zh-bdg-mark" data-count="128" data-tone="primary" data-label="项待办"></span></span>
<span class="zh-bdg"><button class="zh-btn">动态</button><span class="zh-bdg-mark" data-dot="true" data-tone="danger" data-label="有更新"></span></span>
<span class="zh-bdg"><button class="zh-btn">功能</button><span class="zh-bdg-mark" data-text="NEW" data-tone="success"></span></span>

<script>
var m = document.getElementById('unread');
m.dataset.count = '12';
ZhaoUI.badge.paint(m); ZhaoUI.badge.pop(m);
</script>
属性用在取值说明
data-count.zh-bdg-mark整数数字徽标;0 时隐藏(除非 data-show-zero)
data-max.zh-bdg-mark整数,默认 99超过则显示 99+
data-dot.zh-bdg-marktrue红点模式,只表示有更新
data-text.zh-bdg-mark字符串,如 NEW文字徽标
data-tone.zh-bdg-markdanger | primary | success | warning | info | neutral颜色
data-label.zh-bdg-mark字符串,如「条未读消息」读屏时跟在数字后面,务必填写
data-pos.zh-bdg(外层)top-left | bottom-left | bottom-right(默认右上)徽标相对被包裹元素的位置,默认右上
data-show-zero.zh-bdg-marktrue数量为 0 也显示
  • ZhaoUI.badge.paint(el):修改 data-count 等属性后调用,重新渲染
  • ZhaoUI.badge.pop(el):播放一次弹跳动画(开启「减少动态效果」时不播放)

面包屑

纯样式组件:不需要 JS,写好标记即可。页面层级路径。当前页用 aria-current="page";手机宽度只保留最后两级。

<nav aria-label="面包屑">
  <ol class="zh-crumb">
    <li><a href="#">首页</a></li>
    <li><a href="#">内容管理</a></li>
    <li aria-current="page">文章管理</li>
  </ol>
</nav>
属性用在取值说明
zh-crumb<ol>(class)每一级一个 <li>,前面的用 <a>,最后一级用 aria-current="page"分隔符由样式画出,不用自己写 /
aria-current="page"最后一个 <li>page当前页,样式加深,读屏读作「当前页」

按钮组

纯样式组件:不需要 JS,写好标记即可。把几个相关按钮连成一组:共用边框,只有两端有圆角。常用来做视图切换或分段筛选;选中项用 is-active 或 aria-pressed="true"。

<div class="zh-btn-group" role="group" aria-label="视图">
  <button class="zh-btn" aria-pressed="true">列表</button>
  <button class="zh-btn" aria-pressed="false">卡片</button>
  <button class="zh-btn" aria-pressed="false">看板</button>
</div>
属性用在取值说明
zh-btn-group容器(class)里面放多个 zh-btn横向连接的一组按钮
zh-btn-group--vertical.zh-btn-group竖向排列
is-active / aria-pressed="true"组内的按钮选中状态;切换时由你的代码更新

内嵌日历

一直显示在页面里的月历(不是弹出的选择器):点选一天、键盘切换、月年快速跳转,可禁用日期,还能给某些天加小圆点或热力底色(如日程、签到)。

<div id="cal-demo" style="max-width:22rem"></div>
<script>
  var cal = ZhaoUI.calendar('#cal-demo', {
    value: new Date(),
    disabledDate: function (d) { return d.getDay() === 0 ? '周日休息' : ''; },
    marks: function (d) { return d.getDate() % 5 === 0 ? { dots: [1, 3], label: '2 项日程' } : null; }
  });
</script>
属性用在取值说明
data-zh-calendar<div>声明为内嵌日历
data-value<div>YYYY-MM-DD默认选中的日期,也决定初始显示的月份
data-min / data-max<div>YYYY-MM-DD可选范围
data-disable-weekends<div>周末不可选
data-disabled-dates<div>逗号分隔的日期指定不可选的日期
  • ZhaoUI.calendar(host, config):命令式创建。config:value、min、max、disabledDate(d)(返回原因文字即禁用,返回空则可选)、marks(d)
  • marks(d):返回 null,或 { dots: [1 到 8], label }(最多 3 个小圆点,数字是图表色序号)或 { heat: 1 到 3, label }(热力底色深浅);label 会写进该日的无障碍名称
  • host.zhCalendar.get() / set(v) / refresh():读取选中日期、设置选中日期(字符串或 Date)、数据变化后重画标记
事件触发于detail说明
zh:calendar-change<div>undefined选中了某一天

卡片

纯样式组件:不需要 JS,写好标记即可。带边框、圆角、阴影的内容容器。可只放一个标题和内容,也可用 zh-card--flush 配头部、正文、底部、封面图;整张卡片可点时用 zh-card--hover。

<div class="zh-stack">
  <div class="zh-card">
    <h4>基础卡片</h4>
    <p>容器负责边界与留白,内容由业务决定。</p>
    <div class="zh-cardx-meta"><span class="zh-tag primary">品牌</span><span class="zh-tag">默认</span></div>
  </div>

  <div class="zh-card zh-card--flush" style="max-width:22rem">
    <div class="zh-cardx-cover"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 9'%3E%3Crect width='16' height='9' fill='%23c7d2fe'/%3E%3C/svg%3E" alt="设计语言 2.0 的封面"></div>
    <div class="zh-cardx-head"><h4 class="zh-cardx-title">发布确认</h4><div class="zh-cardx-extra"><button class="zh-btn sm text">全部</button></div></div>
    <div class="zh-cardx-body">确认后文章将立即对外可见。</div>
    <div class="zh-cardx-foot"><button class="zh-btn sm">取消</button><button class="zh-btn sm primary">发布</button></div>
  </div>

  <div class="zh-card">
    <div class="zh-cardx-stat-label">活跃用户</div>
    <strong>12.4 万</strong>
    <div class="zh-cardx-stat-foot"><span style="color:var(--zh-trend-up)">+8.2%</span><span class="zh-hint">较上周</span></div>
  </div>

  <a class="zh-card zh-card--hover" href="#"><h4>进入项目</h4><p>查看成员、进度与最近动态。</p></a>
</div>
属性用在取值说明
zh-card<div>(class)内部可放 <h4> 作标题基础卡片:内边距、边框、圆角、阴影随受众与密度变化
zh-card--flush与 zh-card 一起配合下面的 zh-cardx-* 分区去掉内边距,由 head / body / foot 各自控制;溢出裁切,封面图贴边
zh-cardx-head / -title / -extraflush 卡片内标题在左,-extra 里放右侧操作按钮卡片头部
zh-cardx-body / -footflush 卡片内-foot 里放右对齐的按钮正文与底部操作区
zh-cardx-coverflush 卡片内里面放一张 <img alt="…"> 或一个 <svg>封面区域:16:9 比例、浅色底,里面的图片或 SVG 撑满并裁切;放在卡片最上面
zh-cardx-meta卡片内里面放 zh-tag一行标签或元信息
zh-cardx-stat-label / -stat-foot基础卡片内label 是指标名,foot 放趋势与说明指标卡的两端;中间的数字自己写(用 --zh-text 等变量),库不提供数字样式
zh-card--hover写在 <a> 上,与 zh-card 一起href 指向目标整张卡片是链接:悬停上浮并描边,键盘聚焦有聚焦环

轮播

一组幻灯片:左右箭头、圆点、键盘方向键、触屏滑动(基于 CSS 滚动吸附);可自动播放。

<div data-zh-carousel data-autoplay="6000" aria-label="精选内容" style="max-width:28rem">
  <div class="zh-carousel-track">
    <div class="zh-carousel-slide" style="aspect-ratio:16/9;display:grid;place-items:center;background:var(--zh-color-primary-subtle)">第一张</div>
    <div class="zh-carousel-slide" style="aspect-ratio:16/9;display:grid;place-items:center;background:var(--zh-bg-subtle)">第二张</div>
    <div class="zh-carousel-slide" style="aspect-ratio:16/9;display:grid;place-items:center;background:var(--zh-info-bg)">第三张</div>
  </div>
</div>
属性用在取值说明
data-zh-carousel容器里面放 .zh-carousel-track,轨道里放多个 .zh-carousel-slide声明为轮播;箭头和圆点由库生成
data-autoplay容器毫秒,如 5000自动播放间隔;悬停、聚焦、页面不可见或系统开启“减少动态效果”时暂停
data-loop容器在首尾循环,否则到头时箭头禁用
  • ZhaoUI.carousel.go(el, index):切换到第 index 张(从 0 开始)
事件触发于detail说明
zh:carousel-change容器{ index, slide }切换到另一张后触发

级联选择

多级联动选择(省市区、分类树)。数据写成页面里的嵌套列表,或用 ZhaoUI.cascader 传一棵树。支持搜索、悬停展开,窄屏自动变成逐级下钻。

<div data-zh-cascader data-name="region" data-searchable data-value="浙江/杭州/西湖区" data-placeholder="请选择地区" aria-label="地区">
  <ul hidden>
    <li>浙江
      <ul>
        <li>杭州<ul><li>西湖区</li><li>余杭区</li></ul></li>
        <li>宁波<ul><li>海曙区</li><li>鄞州区</li></ul></li>
      </ul>
    </li>
    <li>江苏<ul><li>南京<ul><li>玄武区</li></ul></li></ul></li>
    <li data-disabled>海南<ul><li>海口<ul><li>龙华区</li></ul></li></ul></li>
  </ul>
</div>

<div id="cas2"></div>

<script>
document.querySelector('[data-zh-cascader]').addEventListener('zh:cascader-change', function (e) {
  console.log(e.detail.text);   // 浙江 / 杭州 / 西湖区
});

ZhaoUI.cascader('#cas2', {
  expandTrigger: 'hover',
  data: [{ label: '前端', value: 'fe', children: [{ label: 'Vue', value: 'vue' }, { label: 'React', value: 'react' }] }],
  onChange: function (labels, text, nodes) { console.log(nodes.map(function (n) { return n.value; })); }
});
</script>
属性用在取值说明
data-zh-cascader容器 <div>无声明这是一个级联选择;容器里的第一个 <ul> 是数据,读取后会被替换成控件
data-name容器字段名生成一个隐藏 <input name=…> 参与表单提交,值是用 / 连接的路径(如 浙江/杭州/西湖区);如果路径上每个节点都写了 data-value,则用 data-value 连接
data-value容器用 / 连接的标签路径,如 浙江/杭州/西湖区初始选中的路径;标签里含有 / 时请改用命令式
data-placeholder容器字符串占位文字,默认「请选择」
data-searchable容器布尔属性可输入搜索,匹配完整路径文字,只搜到叶子节点
data-clearable容器false 表示关闭默认有清除按钮
data-expand-trigger容器click(默认)| hover悬停展开下一级;触屏和窄屏始终是点击
data-size容器sm | md(默认)| lg尺寸
data-sep容器字符串,默认「 / 」输入框里各级之间的分隔文字
data-labelledby容器标签元素的 id无障碍名称;也可以直接写 aria-label
data-disabled / data-value<li>布尔属性 / 字符串禁用该节点及其子树 / 该节点的值(用于表单)
<label for="容器 id">容器所在页面的 <label>for 指向容器的 idlabel 会成为输入框的无障碍名称,点击 label 会聚焦输入框;容器是空 <div>,label 的 for 指向它的 id 即可
  • ZhaoUI.cascader(host, { data, value, searchable, clearable, expandTrigger, size, placeholder, sep, labelledby, onChange }):命令式创建。data 是节点树 [{ label, children?, disabled?, …你的字段 }];value 是标签数组;onChange(labels, text, nodes)
  • el.zhCascader.get() / .getNodes():读取当前路径的标签数组 / 节点对象数组
  • el.zhCascader.set(labels) / .setData(tree) / .open() / .close():设置路径(不触发 change,并同步隐藏字段)、更换数据、打开、关闭
事件触发于detail说明
zh:cascader-change容器{ path, text, nodes }选中或清除后触发(冒泡)。path 是标签数组,text 是拼好的文字,nodes 是节点对象数组(能读到你自己加的字段,如 value);清除时都是空的

图表

基础图表:折线、面积、柱状、环形、迷你趋势。颜色用主题固定的 8 色分类色板(随亮暗主题变化),只有一条纵轴(不做双纵轴),图例可点击隐藏某个系列,鼠标悬停或键盘左右键查看每个点的数值,并附带给读屏用的数据表。需要堆叠、散点、地图、动画等复杂图表时请用专门的图表库。

<div class="zh-stack">
  <div data-zh-chart data-type="line" data-title="近 7 天访问量">
    <script type="application/json">{ "labels": ["周一","周二","周三","周四","周五","周六","周日"], "series": [ { "name": "访问", "data": [1200, 1800, 1500, 2300, 2100, 2900, 3200] }, { "name": "下单", "data": [120, 190, 150, 260, 210, 330, 380] } ] }</script>
  </div>
  <div class="zh-row">
    <div style="width:12rem" data-zh-chart data-type="donut" data-height="180" data-title="渠道占比"><script type="application/json">{ "series": [ { "name": "自然", "data": [520] }, { "name": "广告", "data": [310] }, { "name": "推荐", "data": [170] } ] }</script></div>
    <div style="width:10rem" data-zh-chart data-type="spark" data-title="趋势"><script type="application/json">{ "labels": ["1","2","3","4","5","6"], "series": [ { "name": "值", "data": [3, 5, 4, 7, 6, 9] } ] }</script></div>
  </div>
  <div data-zh-chart data-type="bar" data-title="季度销售额"><script type="application/json">{ "labels": ["Q1","Q2","Q3","Q4"], "series": [ { "name": "华东", "data": [320, 410, 380, 520] }, { "name": "华南", "data": [280, 350, 400, 460] } ] }</script></div>
</div>
属性用在取值说明
data-zh-chart<div>里面放 <script type="application/json">{…}</script>声明为图表;JSON 就是下面的配置
data-type<div>line | area | bar | donut | spark图表类型,默认 line
data-height<div>像素,默认 240图表高度;spark 默认 40;宽度跟随容器
data-title<div>文字图表标题,用于无障碍名称和数据表标题
data-no-legend<div>不显示图例
  • ZhaoUI.chart(host, config):命令式创建,返回 { update(partialConfig), toggle(seriesIndex, visible) }。config:type、labels(横轴标签数组)、series([{ name, data: [数字或 null] }],null 表示断开)、height、title、legend、points(折线是否画点,默认 true)、format(value)(数值格式化,默认大数显示“万”“亿”)、inner(环形内圈比例,默认 0.62)、centerLabel(环形中心下方文字,默认“合计”)
  • 环形图的数据:每个系列只取第一个数,一个系列就是一块扇区;中心显示合计
  • 柱状与折线:柱状图从 0 开始;折线与面积图的纵轴自动取合适的刻度
事件触发于detail说明
zh:chart-hover图表容器undefined指针或键盘移到某个数据点
zh:chart-toggle图表容器undefined点击图例显示或隐藏某个系列

代码块

纯样式组件:不需要 JS,写好标记即可。展示代码:zh-code 用在 <pre> 上(等宽字体,过宽时横向滚动),zh-code-inline 用在行内的 <code> 上。不含语法高亮。

<p>调用 <code class="zh-code-inline">ZhaoUI.message('已保存')</code> 显示提示。</p>
<pre class="zh-code"><code>ZhaoUI.message('已保存');
ZhaoUI.dialog({ title: '确认' });</code></pre>
属性用在取值说明
zh-code<pre>(class)里面放 <code>代码块,带底色和边框,长行横向滚动
zh-code-inline<code>(class)行内代码

折叠面板

手风琴或多开的折叠列表。展开状态用 .is-open 表示,库负责切换、无障碍属性和键盘。

<div class="zh-collapse" data-mode="single">
  <div class="zh-collapse-item is-open">
    <h4 class="zh-collapse-h"><button class="zh-collapse-trigger" type="button"><span class="lead">第一项</span><i data-i="chevron-right" class="zh-collapse-chev"></i></button></h4>
    <div class="zh-collapse-panel"><div class="zh-collapse-inner"><div class="zh-collapse-content">内容一</div></div></div>
  </div>
  <div class="zh-collapse-item">
    <h4 class="zh-collapse-h"><button class="zh-collapse-trigger" type="button"><span class="lead">第二项</span><i data-i="chevron-right" class="zh-collapse-chev"></i></button></h4>
    <div class="zh-collapse-panel"><div class="zh-collapse-inner"><div class="zh-collapse-content">内容二</div></div></div>
  </div>
</div>
属性用在取值说明
data-mode.zh-collapsesingle | multiple(默认)single:同时只展开一项;multiple:各项独立
is-open.zh-collapse-itemclass初始展开的项加上这个类
disabled.zh-collapse-trigger布尔禁用某一项,不可点击也不参与方向键
事件触发于detail说明
zh:collapse-toggle.zh-collapse{ item, open }某一项展开或收起时触发(冒泡)

命令面板

按快捷键或点按钮弹出的搜索框:输入几个字就能跳到某个页面或执行某个操作。支持模糊匹配(含关键词)、按分组显示、最近使用、键盘上下选择、回车执行。命令用一个数组描述。

<button class="zh-btn" id="cmd-open"><i data-i="search"></i>搜索 <span class="zh-kbd">Ctrl</span><span class="zh-kbd">K</span></button>
<script>
  var palette = ZhaoUI.commandPalette({
    hotkey: true,
    commands: [
      { id: 'home', group: '页面', label: '概览', icon: 'layout-dashboard', keywords: 'home dashboard 首页' },
      { id: 'set', group: '页面', label: '系统设置', icon: 'settings', keywords: 'settings 偏好' },
      { id: 'new', group: '操作', label: '新建文章', icon: 'file-text', hint: 'N', run: function () { ZhaoUI.message('新建文章'); } }
    ]
  });
  document.getElementById('cmd-open').addEventListener('click', function (e) { palette.open(e.currentTarget); });
</script>
  • ZhaoUI.commandPalette(options):创建面板,返回 { open(from), close(), setCommands(list), el }。open 的参数是触发元素,关闭后焦点回到它
  • options:hotkey 为 true 时 Ctrl/⌘+K 打开或关闭(默认不开,避免和你的页面快捷键冲突);recent 是“最近”分组的条数,0 则不显示;onRun(command) 在命令执行后调用
  • 命令 command:label 参与匹配并高亮命中的字;keywords 是额外的搜索词(可写拼音、英文、同义词);group 是分组标题;hint 显示快捷键提示;run(command) 是要执行的函数
事件触发于detail说明
zh:command-run面板元素(冒泡)undefined执行了某个命令

评论

纯样式组件:不需要 JS,写好标记即可。评论列表项:头像、昵称、时间、正文、操作,回复缩进显示。头像用 zh-avatar。

<article class="zh-comment">
  <span class="zh-avatar" data-name="李明" data-size="sm"></span>
  <div class="zh-comment-body">
    <div class="zh-comment-head"><span class="zh-comment-name">李明</span><time class="zh-comment-time" datetime="2026-10-04T15:20">2 小时前</time></div>
    <p class="zh-comment-text">这次的发布流程比上次清楚多了。</p>
    <div class="zh-comment-actions"><button class="zh-btn sm text">回复</button><button class="zh-btn sm text">赞</button></div>
  </div>
</article>
属性用在取值说明
zh-comment<article>(class)里面放头像和 zh-comment-body一条评论
zh-comment-headbody 里的容器昵称 zh-comment-name 与时间 zh-comment-time标题行
zh-comment-actionsbody 里的容器里面放按钮或链接回复、点赞等操作
zh-comment-repliesbody 里的容器里面放嵌套的 zh-comment回复列表,左侧有竖线并缩进

日期选择

点击输入框弹出日历,也可以直接键入日期。支持最早 / 最晚日期、禁用周末、禁用指定日期,键盘完整可用。声明式写一个空 <div data-zh-date>,命令式用 ZhaoUI.datePicker。周从周一开始。

<div data-zh-date data-name="birthday" data-value="2026-10-15" data-min="2026-10-05" data-max="2026-10-25" data-msg="#birthdayMsg" aria-label="日期"></div>
<div id="birthdayMsg" aria-live="polite"></div>

<div data-zh-date data-disable-weekends data-disabled-dates="2026-10-14" aria-label="工作日"></div>

<div id="custom"></div>

<script>
document.querySelector('[data-zh-date]').addEventListener('zh:date-change', function (e) {
  console.log(e.detail.value);   // 2026-10-20
});

ZhaoUI.datePicker('#custom', {
  size: 'lg',
  disabledDate: function (d) { return d.getDate() === 1 ? '每月 1 号不开放' : ''; },
  onChange: function (d) { console.log(d); }
});
</script>
属性用在取值说明
data-zh-date<div>无声明这是日期选择;控件会构建在这个 div 里
data-value容器YYYY-MM-DD初始日期
data-min / data-max容器YYYY-MM-DD最早 / 最晚可选日期,范围外的日子禁用
data-disable-weekends容器布尔属性周六周日不可选
data-disabled-dates容器逗号分隔的日期,如 2026-10-01,2026-10-02指定日期不可选
data-placeholder容器字符串,默认 YYYY-MM-DD占位文字
data-name容器字段名生成隐藏 <input name=…> 参与表单提交
data-size容器sm | md(默认)| lg尺寸
aria-label / data-labelledby / data-label容器字符串 / 标签元素 id / 弹层标题输入框的无障碍名称;data-label 是弹出面板的标题
data-msg容器一个元素的选择器,如 #status可选的状态提示元素:选择、报错时会把文字写进去(读屏可以朗读),元素上可预先写好默认文字
<label for="容器 id">容器所在页面的 <label>for 指向容器的 idlabel 会成为输入框的无障碍名称,点击 label 会聚焦输入框;容器是空 <div>,label 的 for 指向它的 id 即可
  • ZhaoUI.datePicker(host, { value, min, max, disabledDate, size, placeholder, label, labelledby, msg, onChange }):命令式创建。value / min / max 可以是 'YYYY-MM-DD' 字符串或 Date;disabledDate(date) 返回空串表示可选,返回文字表示不可选并作为原因;onChange(date)
  • el.zhDate.get() / .set(value) / .open() / .close():get 返回 Date 或 null;set 接受字符串或 Date,不触发事件
事件触发于detail说明
zh:date-change容器{ value: 'YYYY-MM-DD' | '', date: Date | null }用户选择、键入确认或清除后触发(冒泡);用 set() 设置不触发

日期范围选择

两个输入框加一个双月日历,选开始和结束两次点击;带「近 7 天 / 近 30 天 / 本月 / 上月」快捷项。窄屏变为单月。

<div data-zh-daterange data-name-start="from" data-name-end="to" data-start="2026-10-01" data-end="2026-10-07" data-max="2026-12-31" aria-label="统计周期"></div>

<script>
document.querySelector('[data-zh-daterange]').addEventListener('zh:daterange-change', function (e) {
  console.log(e.detail.start, '→', e.detail.end);
});
</script>
属性用在取值说明
data-zh-daterange<div>无声明这是日期范围选择
data-start / data-end容器YYYY-MM-DD初始的开始与结束日期
data-min / data-max容器YYYY-MM-DD可选范围
data-presets容器false 表示关闭默认显示快捷项
data-name-start / data-name-end容器字段名分别生成开始、结束两个隐藏 input
data-size容器sm | md(默认)| lg尺寸
aria-label / data-labelledby / data-label容器字符串 / 标签元素 id / 弹层标题输入框的无障碍名称;data-label 是弹出面板的标题
data-msg容器一个元素的选择器,如 #status可选的状态提示元素:选择、报错时会把文字写进去(读屏可以朗读),元素上可预先写好默认文字
<label for="容器 id">容器所在页面的 <label>for 指向容器的 idlabel 会成为输入框的无障碍名称,点击 label 会聚焦输入框;容器是空 <div>,label 的 for 指向它的 id 即可
  • ZhaoUI.dateRangePicker(host, { start, end, min, max, presets, size, label, msg, onChange }):命令式创建;onChange(start, end)
  • el.zhDateRange.get() / .set(start, end) / .open() / .close():get 返回 [Date|null, Date|null];set 接受字符串或 Date
事件触发于detail说明
zh:daterange-change容器{ start, end, dates: [Date|null, Date|null] }范围确定、键入确认或清除后触发(冒泡);start / end 是 YYYY-MM-DD 字符串或空串

日期时间选择

左边日历、右边时间列的组合面板,一次选定日期和时间,点「确定」提交。窄屏上下排列。

<div data-zh-datetime data-name="when" data-value="2026-10-15 09:30" data-min="2026-10-05" data-msg="#whenMsg" aria-label="预约时间"></div>
<div id="whenMsg" aria-live="polite"></div>

<script>
document.querySelector('[data-zh-datetime]').addEventListener('zh:datetime-change', function (e) {
  console.log(e.detail.value);
});
</script>
属性用在取值说明
data-zh-datetime<div>无声明这是日期时间选择
data-value容器YYYY-MM-DD HH:mm初始值
data-min / data-max容器YYYY-MM-DD可选日期范围
data-disable-weekends / data-disabled-dates容器布尔属性 / 逗号分隔日期禁用周末 / 指定日期
data-name容器字段名生成隐藏 <input name=…> 参与表单提交
data-size容器sm | md(默认)| lg尺寸
aria-label / data-labelledby / data-label容器字符串 / 标签元素 id / 弹层标题输入框的无障碍名称;data-label 是弹出面板的标题
data-msg容器一个元素的选择器,如 #status可选的状态提示元素:选择、报错时会把文字写进去(读屏可以朗读),元素上可预先写好默认文字
<label for="容器 id">容器所在页面的 <label>for 指向容器的 idlabel 会成为输入框的无障碍名称,点击 label 会聚焦输入框;容器是空 <div>,label 的 for 指向它的 id 即可
  • ZhaoUI.dateTimePicker(host, { value: { date, time }, min, max, disabledDate, size, msg, onChange }):命令式创建;也接受 'YYYY-MM-DD HH:mm' 字符串作为 value;onChange({ date, time } | null)
  • el.zhDateTime.get() / .set(value) / .open() / .close():get 返回 { date, time } 或 null;set 接受对象或字符串,不触发事件
事件触发于detail说明
zh:datetime-change容器{ value: 'YYYY-MM-DD HH:mm' | '', date, time }日期或时间变化、键入确认、清除后触发(冒泡);面板里选日期或调时间时输入框会实时更新,也会触发

描述列表

纯样式组件:不需要 JS。用 <dl> 展示一组「名称:值」,如订单详情、项目信息。有两种外观:plain(无框)、bordered(带框表格式);列数 1 到 3 可选,窄屏自动降列。

<div class="zh-stack">
  <div class="zh-descr-wrap">
    <dl class="zh-descr" data-cols="3" data-variant="bordered" aria-label="订单信息">
      <div class="zh-descr-item"><dt>订单号</dt><dd>20260929-0086</dd></div>
      <div class="zh-descr-item"><dt>状态</dt><dd><span class="zh-tag success">已发货</span></dd></div>
      <div class="zh-descr-item"><dt>金额</dt><dd>¥ 1,286.00</dd></div>
      <div class="zh-descr-item"><dt>收货人</dt><dd>张三</dd></div>
      <div class="zh-descr-item"><dt>电话</dt><dd>138 **** 0086</dd></div>
      <div class="zh-descr-item"><dt>物流</dt><dd><a class="zh-descr-link" href="#">SF1234567890</a></dd></div>
      <div class="zh-descr-item" data-span="3"><dt>地址</dt><dd>上海市浦东新区世纪大道 100 号</dd></div>
    </dl>
  </div>
  <dl class="zh-descr" data-cols="2" data-variant="plain" aria-label="项目信息">
    <div class="zh-descr-item"><dt>负责人</dt><dd>李四</dd></div>
    <div class="zh-descr-item"><dt>创建时间</dt><dd>2026-09-01 09:30</dd></div>
  </dl>
</div>
属性用在取值说明
zh-descr<dl>(class)里面每组放一个 <div class="zh-descr-item"><dt>名称</dt><dd>值</dd></div>网格容器,默认 3 列
data-cols.zh-descr1 | 2 | 3(默认 3)列数;平板宽度 3 列降为 2 列,手机宽度全部降为 1 列
data-variant.zh-descrplain | borderedplain 无框,名称在上、值在下;bordered 是带线框的表格式,名称在左侧的灰底格里
data-span.zh-descr-item2 | 3该项横跨的列数,适合地址、备注等长内容;手机宽度忽略;各项加起来填不满一行时,末尾会留空格
zh-descr-wrap外层 <div>(class)仅 bordered 时使用给 bordered 的列表加圆角外框并裁掉最外层多余的线
zh-descr-link<dd> 里的 <a>值里的链接样式

模态框与抽屉

模态框基于原生 <dialog>,抽屉是右侧滑出的面板。用 data-zh-open / data-zh-close 声明即可,也可用 ZhaoUI.open / close / dialog 调用。

<button class="zh-btn" data-zh-open="#dlg">打开</button>
<dialog class="zh-modal" id="dlg" aria-labelledby="dlgT" data-zh-backdrop-close>
  <header id="dlgT">确认删除</header>
  <div class="body">删除后无法恢复。</div>
  <footer>
    <button class="zh-btn" data-zh-close>取消</button>
    <button class="zh-btn primary" data-zh-close>确定</button>
  </footer>
</dialog>

<button class="zh-btn" data-zh-open="#drw">打开抽屉</button>
<aside class="zh-drawer" id="drw" aria-label="详情">
  <button class="zh-btn text" data-zh-close>关闭</button>
  <p>抽屉内容</p>
</aside>

<script>
ZhaoUI.dialog({ title: '删除文章?', content: '删除后无法恢复', danger: true })
  .then(function (ok) { if (ok) { /* 执行删除 */ } });
</script>
属性用在取值说明
data-zh-open按钮或链接目标的 CSS 选择器,如 #dlg点击后打开目标对话框或抽屉,并记住触发元素,关闭后焦点回到它
data-zh-close对话框或抽屉内的按钮留空,或目标选择器点击后关闭所在的对话框或抽屉;写了选择器则关闭那个目标
data-zh-backdrop-closedialog.zh-modal无点击遮罩(对话框以外的区域)关闭
  • ZhaoUI.open(target, opener?):打开对话框或抽屉。target 是元素或选择器;opener 是关闭后要聚焦回去的元素(可选)
  • ZhaoUI.close(target):关闭对话框或抽屉
  • ZhaoUI.dialog({ title, content, ok, cancel, danger, backdropClose }):弹出确认框,返回 Promise:点确定为 true,取消、Esc 为 false。选项:title 标题;content 正文(纯文本,要放标签请传 DOM 节点);ok 确定按钮文字(默认「确定」);cancel 取消按钮文字(默认「取消」,传 false 则不显示取消);danger 为 true 时确定按钮用危险样式(红色),否则是主色;backdropClose 为 true 时点遮罩也算取消。例:ZhaoUI.dialog({ title: '删除文章?', content: '删除后无法恢复', ok: '删除', cancel: '再想想', danger: true }).then(function (yes) { if (yes) remove(); })
事件触发于detail说明
zh:open抽屉无抽屉打开后触发(冒泡)
zh:close抽屉无抽屉关闭后触发(冒泡)。模态框用原生 close 事件

下拉菜单

点击按钮弹出菜单。菜单内容写在页面里的一个隐藏列表中,选中后触发 zh:select。一个菜单可以被多个按钮共用(比如表格每一行一个「操作」按钮,都指向同一个 #menu)。

<button class="zh-btn" data-zh-dropdown="#menu">操作 <i data-i="chevron-down"></i></button>
<ul class="zh-menu" id="menu" hidden>
  <li><a href="#" data-value="edit">编辑</a></li>
  <li><a href="#" data-value="copy">复制</a></li>
  <li><a href="#" data-value="del">删除</a></li>
</ul>

<script>
document.querySelector('[data-zh-dropdown]').addEventListener('zh:select', function (e) {
  console.log(e.detail.value);
});
</script>

<!-- 表格行菜单:每行一个按钮,共用同一个菜单 -->
<table>
  <tr data-id="1"><td>文章一</td><td><button class="zh-btn text" data-zh-dropdown="#rowMenu">操作</button></td></tr>
  <tr data-id="2"><td>文章二</td><td><button class="zh-btn text" data-zh-dropdown="#rowMenu">操作</button></td></tr>
</table>
<ul class="zh-menu" id="rowMenu" hidden>
  <li><a href="#" data-value="edit">编辑</a></li><li><a href="#" data-value="del">删除</a></li>
</ul>
<script>
document.addEventListener('zh:select', function (e) {
  var btn = e.target.closest('[data-zh-dropdown="#rowMenu"]'), id = btn.closest('tr').dataset.id;
  console.log(e.detail.value, '第', id, '行');
});
</script>
属性用在取值说明
data-zh-dropdown按钮菜单元素的选择器,如 #menu声明这个按钮控制哪个菜单
data-zh-placement按钮bottom-start(默认)、bottom-end、top-start、top-end、left、right 等菜单出现的位置,空间不足时自动翻转
hidden菜单 <ul class="zh-menu">布尔菜单源元素保持隐藏;库会克隆它来显示
data-value菜单项 <a> 或 <button>字符串选中时作为 detail.value;不写则取文字
aria-disabled菜单项true禁用某一项
事件触发于detail说明
zh:select触发按钮{ value, item }选中某项后在被点击的那个按钮上触发(冒泡),随后菜单关闭。多个按钮共用一个菜单时,用 e.target.closest('[data-zh-dropdown]') 就能知道是哪一行的按钮

警告条、空状态、骨架屏

纯样式组件:不需要 JS,写好标记即可。页面内的提示条(信息、成功、警告、危险)、没有数据时的空状态、加载中的骨架屏。会自动消失的轻提示用 ZhaoUI.message。

<div class="zh-stack">
  <div class="zh-alert info" role="status"><i data-i="info"></i><span>系统将在今晚 23:00 进行维护。</span></div>
  <div class="zh-alert success" role="status"><i data-i="circle-check"></i><span>保存成功。</span></div>
  <div class="zh-alert warning" role="alert"><i data-i="triangle-alert"></i><span>存在 3 台设备离线,请及时处理。</span></div>
  <div class="zh-alert danger" role="alert"><i data-i="circle-x"></i><span>发布失败,请重试。</span></div>

  <div class="zh-empty">
    <i data-i="search-x" data-s="48"></i>
    <div>没有匹配的数据</div>
    <button class="zh-btn sm">清除筛选</button>
  </div>

  <div class="zh-card" role="status" aria-busy="true" aria-label="加载中">
    <div class="zh-skeleton" style="width:60%"></div>
    <div class="zh-skeleton" style="margin-top:12px"></div>
    <div class="zh-skeleton" style="margin-top:12px;width:80%"></div>
  </div>
</div>
属性用在取值说明
zh-alert + info | success | warning | danger<div>(class)里面放一个图标 <i data-i="info"> 和一个 <span> 文字页面内提示条。图标颜色跟随类型。信息用 role="status",警告和危险用 role="alert"
zh-empty<div>(class)图标、说明文字、可选的操作按钮,居中空状态:没有数据、搜索无结果时用
zh-skeleton<div>(class)用 style="width:60%" 控制长度,高度默认 12px加载中的灰色条,带流光动画;开启「减少动态效果」时流光停止

页脚

纯样式组件:不需要 JS,写好标记即可。页面底部:版权文字在一侧,链接在另一侧,窄屏自动换行。

<footer class="zh-footer">
  <span>© 2026 ZhaoUI. 保留所有权利。</span>
  <ul class="zh-footer-links"><li><a href="#">隐私政策</a></li><li><a href="#">服务条款</a></li><li><a href="#">联系我们</a></li></ul>
</footer>
属性用在取值说明
zh-footer<footer>(class)顶部分隔线,两端对齐,可换行
zh-footer-links<ul>(class)里面放 <li><a>链接列表,横向排列

表单控件与字段

纯样式组件:不需要 JS,写好标记即可。字段(标签、控件、提示)、文本输入、下拉、多行文本、复选框、单选、开关。带搜索、清除、校验等行为的输入框见「输入框」「下拉选择」等组件。

<div class="zh-stack">
  <div class="zh-field">
    <label for="f-name" class="zh-req">名称</label>
    <input id="f-name" class="zh-input" placeholder="请输入名称" required aria-describedby="f-name-h">
    <span class="zh-hint" id="f-name-h">2 到 20 个字</span>
  </div>

  <div class="zh-field">
    <label for="f-mail">邮箱</label>
    <input id="f-mail" class="zh-input" value="abc" aria-invalid="true" aria-describedby="f-mail-h">
    <span class="zh-hint err" id="f-mail-h">邮箱格式不正确</span>
  </div>

  <div class="zh-field">
    <label for="f-city">城市</label>
    <div class="zh-select"><select id="f-city"><option>北京</option><option>上海</option></select></div>
  </div>

  <div class="zh-field">
    <label for="f-note">备注</label>
    <textarea id="f-note" class="zh-textarea"></textarea>
  </div>

  <label class="zh-check"><input type="checkbox" checked> 记住我</label>
  <label class="zh-check"><input type="radio" name="r" checked> 选项一</label>
  <label class="zh-check"><input type="radio" name="r"> 选项二</label>
  <label class="zh-check"><input type="checkbox" class="zh-switch" role="switch" checked> 启用通知</label>
</div>
属性用在取值说明
zh-field<div>(class)里面放 <label for> 与控件,再放 zh-hint纵向排列的字段:标签在上、控件、提示。多个字段用布局里的 zh-stack 或 zh-grid 排
zh-req<label>(class)必填字段的标签在标签后加红色星号;仍然要给控件加 required 或 aria-required
zh-hint / zh-hint err字段内(class)提示文字;错误用 class="zh-hint err"次要说明与错误提示。错误时同时给控件加 aria-invalid="true"
zh-input<input>(class)可加 sm | lg 改高度文本输入。aria-invalid="true" 时边框变危险色;聚焦时有主色描边
zh-textarea<textarea>(class)多行文本,最小高度两行。自动增高与计数用「多行文本」组件
zh-select包着 <select> 的 <span> 或 <div>(class)<div class="zh-select"><select>…</select></div>原生下拉的样式;需要搜索、多选时用「下拉选择」组件
zh-check<label>(class)里面放 <input type="checkbox"> 或 <input type="radio"> 再放文字复选框与单选:主色,大小随字号,整行可点
zh-switch<input type="checkbox">(class)同时写 role="switch"开关;放在 zh-check 里,文字是它的标签

图片

纯样式组件:不需要 JS,写好标记即可。图片的响应式与外观:zh-img 让图片不超出容器、保持比例;--rounded 圆角,--circle 正圆(头像),--thumb 带边框的缩略图。配图说明用 <figure> 加 zh-figure。

<figure class="zh-figure" style="max-width:16rem">
  <img class="zh-img zh-img--rounded" alt="示例配图" width="320" height="180" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 9'%3E%3Crect width='16' height='9' fill='%23c7d2fe'/%3E%3C/svg%3E">
  <figcaption>图 1 示例配图</figcaption>
</figure>
属性用在取值说明
zh-img<img>(class)宽度不超过容器,高度按比例
zh-img--rounded / --circle / --thumb.zh-img圆角 / 正圆裁切 / 带边框内边距的缩略图
zh-figure<figure>(class)里面放图片和 <figcaption>说明文字居中显示在图片下方

行内编辑

点击文字原地变成输入框,Enter 或失焦保存,Esc 取消。空内容不会被保存。

<div class="zh-edit" id="ed" data-label="名称">
  <button type="button" class="zh-edit-view" aria-label="编辑名称:中控系统"><span class="zh-edit-text">中控系统</span><i data-i="pencil" data-s="16"></i></button>
</div>

<script>
document.getElementById('ed').addEventListener('zh:edit-save', function (e) {
  console.log(e.detail.previous, '→', e.detail.value);
});
</script>
属性用在取值说明
data-label.zh-edit字符串,默认「名称」给屏幕阅读器用的字段名,用于输入框与按钮的 aria-label
事件触发于detail说明
zh:edit-save.zh-edit{ value, previous }内容有变化并保存时触发(冒泡);内容没变化不触发
zh:edit-cancel.zh-edit无按 Esc 取消,或在空内容上失焦还原旧值时触发

输入框

带前置图标、清除按钮、密码显示开关、搜索快捷键的文本输入。结构是 .zh-inp 包着 .zh-inp-box 和原生 input,按钮按需添加。

<div class="zh-inp"><span class="zh-inp-box"><i data-i="search" data-s="18" class="zh-inp-pre"></i><input class="zh-inp-el" type="text" placeholder="邮箱" aria-label="邮箱"><button type="button" class="zh-inp-btn zh-inp-clear" aria-label="清除" hidden><i data-i="x" data-s="16"></i></button></span></div>

<div class="zh-inp"><span class="zh-inp-box"><input class="zh-inp-el" type="password" value="hunter2" aria-label="密码" autocomplete="new-password"><button type="button" class="zh-inp-btn zh-inp-eye" aria-label="显示密码" aria-pressed="false"><i data-i="eye" data-s="18"></i></button></span></div>

<div class="zh-inp zh-inp-search" data-search><span class="zh-inp-box"><i data-i="search" data-s="18" class="zh-inp-pre"></i><input class="zh-inp-el" type="search" placeholder="搜索文章" aria-label="搜索" enterkeyhint="search"><button type="button" class="zh-inp-btn zh-inp-clear" aria-label="清除搜索" hidden><i data-i="x" data-s="16"></i></button></span></div>

<script>
document.querySelector('[data-search]').addEventListener('zh:search', function (e) {
  console.log('搜索:', e.detail.query);
});
</script>
属性用在取值说明
zh-inp-clear输入框内的按钮(class)加 hidden 作为初始状态有内容时自动显示,点击清空并让输入框获得焦点
zh-inp-eyepassword 输入框旁的按钮(class)aria-pressed="false"点击在密码与明文之间切换,并同步 aria-label
data-search.zh-inp无搜索模式:Enter 触发搜索,Esc 清空内容
is-error / is-success.zh-inpclass错误与成功状态的外观;校验逻辑由你来写
事件触发于detail说明
zh:search.zh-inp[data-search]{ query }在搜索框按 Enter 时触发(冒泡),query 已去掉首尾空格
zh:search-clear.zh-inp[data-search]无在有内容的搜索框按 Esc 清空后触发

键位

纯样式组件:不需要 JS。用 <kbd class="zh-kbd"> 显示键盘按键,如快捷键提示。

<p>按 <kbd class="zh-kbd">Ctrl</kbd> + <kbd class="zh-kbd">K</kbd> 打开搜索,<kbd class="zh-kbd">Esc</kbd> 关闭。</p>
属性用在取值说明
zh-kbd<kbd>(class)按键名一个按键一个元素;组合键用多个并排

布局

纯样式组件:不需要 JS,写好标记即可。响应式栅格(桌面 12 列、平板 8 列、手机 4 列)、自适应卡片网格、主内容加侧栏、垂直堆叠、水平排列、两端对齐。间距来自 Token,不要自己写死。

<div class="zh-grid">
  <div class="zh-col" style="--d:3;--t:4;--p:4"><div class="zh-card">访客</div></div>
  <div class="zh-col" style="--d:3;--t:4;--p:4"><div class="zh-card">订单</div></div>
  <div class="zh-col" style="--d:6;--t:8;--p:4"><div class="zh-card">趋势(桌面占一半,手机占满)</div></div>
</div>

<div class="zh-auto-grid">
  <div class="zh-card">一</div><div class="zh-card">二</div><div class="zh-card">三</div><div class="zh-card">四</div>
</div>

<div class="zh-with-aside">
  <main class="zh-card">主内容</main>
  <aside class="zh-card">侧栏</aside>
</div>

<div class="zh-split"><h4>文章管理</h4><button class="zh-btn primary">新建</button></div>
<div class="zh-row" style="--g:8px"><button class="zh-btn">甲</button><button class="zh-btn">乙</button></div>
<div class="zh-stack" style="--g:8px"><div>第一行</div><div>第二行</div></div>
属性用在取值说明
zh-grid + zh-col容器与子元素(class)在 zh-col 上用 style="--d:6;--t:8;--p:4"12 / 8 / 4 列栅格。--d 是桌面(≥ 1200px)占几列,--t 是平板(768 到 1199px),--p 是手机(≤ 767px);不写则桌面占 3、平板占 4、手机占 4。列间距随断点变化
zh-auto-grid容器(class)直接放卡片等子元素按宽度自动排:每列至少 220px,放得下几列就几列,不需要写列数
zh-with-aside容器(class)两个子元素:主内容、侧栏主内容加 320px 侧栏;宽度 ≤ 1199px 时侧栏掉到下面
zh-stack容器(class)可用 style="--g:8px" 改间距,默认 12px垂直堆叠
zh-row容器(class)可用 style="--g:8px"水平排列,放不下自动换行,垂直居中
zh-split容器(class)两个子元素两端对齐:标题在左,操作在右,窄屏自动换行

列表

纯样式组件:不需要 JS,写好标记即可。一行一项的列表:主文字、说明、右侧附加内容(标签、时间、按钮),行之间有分隔线。zh-list--bordered 加外框,zh-list--hover 悬停高亮,zh-list--plain 是带圆点的普通列表。

<ul class="zh-list zh-list--bordered">
  <li><div class="zh-list-main"><p class="zh-list-title">产品发布会回顾</p><p class="zh-list-desc">李明 · 2026-10-04</p></div><div class="zh-list-extra"><span class="zh-tag success">已发布</span></div></li>
  <li><div class="zh-list-main"><p class="zh-list-title">季度运营复盘</p><p class="zh-list-desc">王芳 · 2026-10-04</p></div><div class="zh-list-extra"><span class="zh-tag warning">待审核</span></div></li>
</ul>
属性用在取值说明
zh-list<ul>(class)每个 <li> 里放 zh-list-main,可再放 zh-list-extra列表容器
zh-list-title / zh-list-desc<li> 里的 <p>主文字(加粗,单行省略)与说明
zh-list-extra<li> 里的容器右侧附加内容,不会被挤压
zh-list--bordered.zh-list圆角外框和底色
zh-list--hover.zh-list悬停整行高亮;整行可点时请把内容放进 <a>
zh-list--plain.zh-list普通圆点列表,用于文章里的要点

营销页区块

可选的 zhaoui-marketing.css:搭一个落地页用的整块样式,页宽与区块间距、hero(左文右卡)、区块标题、特性卡、价格卡、品牌渐变横幅、带品牌色发光的主按钮,以及可选的渐变文字、数据条、极光光斑和玻璃标签。极光、渐变文字、玻璃标签只是装饰,默认不用:普通页面直接用 zh-hero 的浅色渐变;只有深色、发布会式的首屏才考虑加 zh-aurora(并在该区块写 data-mode="dark")。下面的示例为了演示全部类才把它们放在一起,不是推荐的搭配。全部读主题变量,跟着品牌色、亮暗和 B/C 端走。需要额外引入 zhaoui-marketing.css,放在 zhaoui.css 之后。

<section class="zh-hero zh-aurora zh-aurora--grid" data-mode="dark">
  <div class="zh-wrap zh-hero-grid">
    <div>
      <span class="zh-tag zh-tag--lg primary">2026 全新服务</span>
      <h2 class="zh-display" style="margin-top:16px">一句话讲清<span class="zh-gradient-text">你的价值</span></h2>
      <p class="zh-section-sub" style="margin-bottom:24px">一个副标题,补充说明。主操作用 xl 按钮,次操作用默认按钮。</p>
      <div class="zh-hero-actions"><a class="zh-btn gradient xl" href="#mk-features">主要操作</a><a class="zh-btn xl" href="#mk-price">次要操作</a></div>
      <ul class="zh-stats"><li><b>500+</b><span>合作伙伴</span></li><li><b>98%</b><span>满意度</span></li></ul>
    </div>
    <div class="zh-card zh-float-card"><span class="zh-tag zh-tag--filled primary zh-float-flag">进行中</span><h3>产品卡片</h3><p>放一张展示产品实际样子的卡片。</p></div>
  </div>
</section>
<section class="zh-section zh-section--subtle" id="mk-features">
  <div class="zh-wrap">
    <ol class="zh-stats-bar" style="list-style:none;margin:0 0 32px"><li class="zh-stat"><b class="zh-gradient-text">60+</b><span>个组件</span></li><li class="zh-stat"><b class="zh-gradient-text">0</b><span>运行时依赖</span></li></ol>
    <p class="zh-eyebrow">特性</p>
    <h2 class="zh-section-title">区块标题</h2>
    <p class="zh-section-sub">一段说明文字,宽度自动限制在舒适的行长内。</p>
    <div class="zh-feature-grid">
      <div class="zh-card zh-feature zh-feature--brand"><span class="zh-tile"><i data-i="sparkles"></i></span><h3>主推特性</h3><p>品牌渐变底的特性卡,白字。</p><div class="zh-feature-tags"><span class="zh-tag">标签一</span><span class="zh-tag">标签二</span></div></div>
      <div class="zh-card zh-feature"><span class="zh-index">01</span><h3>普通特性</h3><p>悬停时上浮并带品牌色的柔和阴影。</p><div class="zh-feature-tags"><span class="zh-tag primary">标签一</span><span class="zh-tag primary">标签二</span></div></div>
    </div>
  </div>
</section>
<section class="zh-section zh-section--center" id="mk-price">
  <div class="zh-wrap">
    <h2 class="zh-section-title">价格卡</h2>
    <div class="zh-feature-grid" style="max-width:40rem;margin-inline:auto">
      <div class="zh-card zh-price"><p class="zh-price-name">基础版</p><div class="zh-price-amount">¥99<small>/月</small></div><p class="zh-price-desc">适合个人</p><ul class="zh-price-list"><li><i data-i="check"></i>功能一</li><li><i data-i="check"></i>功能二</li></ul><a class="zh-btn lg" href="#">选择</a></div>
      <div class="zh-card zh-price zh-price--popular"><span class="zh-tag zh-tag--filled primary zh-price-flag">最受欢迎</span><p class="zh-price-name">专业版</p><div class="zh-price-amount">¥299<small>/月</small></div><p class="zh-price-desc">适合团队</p><ul class="zh-price-list"><li><i data-i="check"></i>功能一</li><li><i data-i="check"></i>功能二</li><li><i data-i="check"></i>功能三</li></ul><a class="zh-btn primary lg" href="#">选择</a></div>
    </div>
  </div>
</section>
<section class="zh-section zh-section--brand">
  <div class="zh-wrap"><h2 class="zh-section-title">行动号召</h2><p class="zh-section-sub" style="margin:0 auto 24px">品牌渐变底的整行区块,字用 on-primary。</p><a class="zh-btn xl" href="#">立即开始</a></div>
</section>
属性用在取值说明
zh-wrap容器(class)页面内容宽度:最宽 --zh-page-max(80rem),两侧留白随屏幕在 16 到 64px 之间变化
zh-section<section>(class)加 --subtle(浅灰底)、--brand(品牌渐变底、白字)、--center(标题居中)一个区块;上下留白用 --zh-section-gap(64 到 96px,随屏幕)。给区块加 data-mode="dark" 就是一整块深色区
zh-eyebrow / zh-display / zh-section-title / zh-section-sub文字(class)小标题、展示级大标题(--zh-font-display,em 部分用品牌色)、区块标题(--zh-font-title)、区块说明(限制行宽)
zh-hero + zh-hero-grid区块与容器首屏:柔和渐变底,左边文字、右边产品卡片,900px 以下上下排
zh-float-card + zh-float-flag卡片与标签带大阴影的产品展示卡,标签浮在右上角并带品牌色发光
zh-feature-grid + zh-feature网格与卡片加 zh-feature--brand 做主推卡自动按宽度分列的特性卡;zh-feature-grid--4 每列更窄;悬停上浮
zh-feature-tags / zh-tags标签组卡片里的多个标签:比单个状态标签更高、更松,间距 8px
zh-tile图标块里面放 <i data-i>52px 的圆角图标底块,品牌浅色底
zh-price 系列价格卡zh-price-name、zh-price-amount、zh-price-desc、zh-price-list、zh-price-flag价格卡;zh-price--popular 加品牌边框和发光
zh-nav--contained导航条(class)让导航内容和页面内容对齐在同一条线上
zh-glow按钮等(class)品牌色发光阴影;hero 里的主按钮已自带
zh-btn xl按钮(class)超大按钮,落地页的主操作用;比 lg 再大 8px 高度
zh-gradient-text / zh-index文字(class)渐变文字 / 渐变编号(01、02…)。渐变取自品牌派生的三个强调色 --zh-accent-a/b/c,只用在大字号上(强调色在白底和近黑底上都有 3:1,是大字的下限,不适合小字)
--zh-accent-a / b / c品牌令牌颜色三个强调色,换品牌色会一起变;想固定成指定的三色(如珊瑚、橙、紫),在 CSS 里直接覆盖这三个变量。--zh-gradient-accent 是用它们画的渐变
zh-stats-bar + zh-stat容器与项项里放 <b>数字</b><span>说明</span>一排关键数据放在一张柔和的大圆角卡里,竖线分隔;数字上加 zh-gradient-text 得到渐变数字;给它负的上外边距可以压在上面的 hero 上
zh-aurora(+ zh-aurora--grid)区块(class)需同时加 data-mode="dark"可选装饰:深色底上用强调色画的静态光斑,加 --grid 再叠一层淡网格;没有模糊滤镜也没有动画,几乎没有绘制成本
zh-aurora-stage + zh-glass-tag展示卡与标签glass-tag 用 style 的 left/top 定位深色展示卡,上面浮动的玻璃标签(半透明加模糊;不支持模糊的浏览器只是半透明);只在深色或图片背景上使用
zh-btn gradient按钮(class)主操作的渐变按钮:从主色渐变到深一档的主色,白字对比度达标;原站那种“白字压鲜艳渐变”约 3.2:1,不达标,库里不提供

滚动公告

一行向左滚动的公告文字,首尾无缝衔接;悬停或聚焦时暂停;系统开启“减少动态效果”时不滚动,改为可横向拖动。

<div data-zh-marquee data-speed="50" aria-label="公告">系统将在今晚 23:00 进行维护,预计 30 分钟。 <a href="#">查看详情</a></div>
属性用在取值说明
data-zh-marquee容器里面放文字或链接声明为滚动公告;库会复制一份内容(对读屏隐藏)做无缝循环
data-speed容器像素每秒,默认 60滚动速度

遮罩条

纯样式组件:不需要 JS,写好标记即可。盖在图片或卡片底部的说明条:渐变深色底配白字,在任何图片上都能读。zh-maskbar--solid 是纯色条,zh-maskbar--hover 平时隐藏、悬停或聚焦时出现。

<div class="zh-mask-wrap" style="max-width:16rem;aspect-ratio:16/9;background:var(--zh-color-primary-subtle)">
  <div class="zh-maskbar">产品发布会现场</div>
</div>
属性用在取值说明
zh-mask-wrap容器(class)相对定位并裁切,里面放图片和遮罩条
zh-maskbar容器里的元素文字底部渐变遮罩条;颜色固定为深底白字,不随主题变
zh-maskbar--solid / --hover.zh-maskbar纯色底 / 悬停才出现

配置式菜单与右键菜单

用一个数组描述菜单,再挂到按钮或区域上:分组标题、分隔线、图标、快捷键提示、复选项、单选项、危险项、禁用项、多级子菜单。同一份数组也能做右键菜单。固定写在 HTML 里的简单菜单用「下拉菜单」(data-zh-dropdown)更省事。

<div class="zh-row">
  <button class="zh-btn" id="mn-btn">文件 <i data-i="chevron-down"></i></button>
  <div id="mn-area" class="zh-card" style="padding:16px">在这里点右键</div>
</div>
<script>
  var items = [
    { type: 'group', label: '文件' },
    { label: '重命名', icon: 'pencil', shortcut: 'F2' },
    { label: '移动到', icon: 'folder-open', sub: [{ label: '我的文档' }, { label: '项目归档' }] },
    { type: 'sep' },
    { type: 'check', label: '显示隐藏文件', checked: true },
    { type: 'sep' },
    { label: '删除', icon: 'trash-2', danger: true }
  ];
  ZhaoUI.menu(document.getElementById('mn-btn'), items, { label: '文件操作', onSelect: function (it) { ZhaoUI.message('选择了:' + it.label); } });
  ZhaoUI.contextMenu(document.getElementById('mn-area'), items);
</script>
  • ZhaoUI.menu(button, items, options):点击按钮打开菜单,返回弹出层句柄(open、close、toggle、isOpen)。options:placement(默认 bottom-start)、label(菜单的 aria-label)、onSelect(item)、onClose()、trigger(click 默认,manual 则自己调用 open)
  • ZhaoUI.contextMenu(area, items, options):在区域上右键、按 Shift+F10 或菜单键时,在鼠标处(或区域中心)打开菜单。区域会自动可聚焦。options:label、onSelect(item)
  • 菜单项 item:type 取 'sep'(分隔线)、'group'(分组标题,label 为标题)、'check'(复选,点击切换 checked,菜单保持打开)、'radio'(同一 name 的单选,菜单保持打开);普通项点击后触发 onSelect 并关闭;sub 是子菜单数组

消息提示

顶部居中的轻量提示,自动消失。只提供方法调用,不需要写标记。

<script>
ZhaoUI.message('保存成功');
ZhaoUI.message('网络异常,请重试', { type: 'error', duration: 4000 });

var close = ZhaoUI.message('正在上传…', { type: 'info', duration: 0 });
// 完成后
close();
</script>
  • ZhaoUI.message(text, { type, duration }):显示一条消息。type:success(默认)、info、warning、error;duration 为毫秒,默认 2400,传 0 则不自动消失。返回一个函数,调用即提前关闭。text 按纯文本处理

顶部导航条

网站或应用顶部的导航:左侧标志,中间链接(当前页下方有滑动的下划线),右侧放搜索、通知、头像等你自己的内容。有嵌套 <ul> 的链接自动成为下拉菜单;宽度小于 768px 时链接收进汉堡按钮打开的侧滑抽屉。

<header class="zh-nav" data-zh-nav aria-label="主导航">
  <a class="zh-nav-logo" href="#"><i aria-hidden="true"></i><span>ZhaoUI</span></a>
  <ul class="zh-nav-links">
    <li><a class="zh-nav-link" href="#" aria-current="page" data-icon="layout-dashboard">概览</a></li>
    <li><button class="zh-nav-link" type="button" data-icon="file-text">产品<i data-i="chevron-down"></i></button>
      <ul><li><a href="#">中控台</a></li><li><a href="#">数据分析</a></li></ul></li>
    <li><a class="zh-nav-link" href="#">价格</a></li>
  </ul>
  <div class="zh-nav-right"><button class="zh-nav-avatar" type="button" aria-label="账户">ZW</button></div>
</header>
属性用在取值说明
data-zh-nav<header class="zh-nav"> 或 <nav>声明为导航条;直接子元素按顺序放 .zh-nav-logo、ul.zh-nav-links、.zh-nav-right
zh-nav-link<a> 或 <button>(class)一个导航项;当前页加 aria-current="page"
嵌套 <ul>含子链接的 <li> 里里面放 <a href>把外层的 zh-nav-link 变成下拉菜单触发器;手机抽屉里变成可展开的二级
data-icon<a> / 顶层 zh-nav-link图标名抽屉里该项前的图标(桌面端不显示)
zh-nav-iconbtn<button>(class)里面放 <i data-i>右侧的图标按钮,如通知;必须有 aria-label
zh-nav-avatar<button>(class)缩写文字圆形头像按钮,可配合 data-zh-dropdown 做账户菜单
zh-nav-search<button>(class)图标、提示文字和 zh-kbd外观像搜索框的按钮,点击后打开你自己的搜索;手机宽度自动隐藏
事件触发于detail说明
zh:nav-select导航条undefined点击链接(桌面或抽屉里)。href 为 # 或为空时库不跳转,只切换当前页

数字步进器

带加减按钮的数字输入,支持范围、步长、小数位数,键盘可操作,按住按钮连续加减。

<div class="zh-inp zh-num" id="qty" data-min="1" data-max="10" data-step="1">
  <button type="button" class="zh-num-btn" data-dir="-1" aria-label="减少" tabindex="-1"><i data-i="minus" data-s="16"></i></button>
  <input class="zh-inp-el" role="spinbutton" inputmode="numeric" value="3" aria-label="数量">
  <button type="button" class="zh-num-btn" data-dir="1" aria-label="增加" tabindex="-1"><i data-i="plus" data-s="16"></i></button>
</div>

<div class="zh-inp zh-num" data-min="0" data-max="9999" data-step="0.5" data-precision="2">
  <button type="button" class="zh-num-btn" data-dir="-1" aria-label="减少" tabindex="-1"><i data-i="minus" data-s="16"></i></button>
  <input class="zh-inp-el" role="spinbutton" inputmode="decimal" value="12.50" aria-label="金额">
  <button type="button" class="zh-num-btn" data-dir="1" aria-label="增加" tabindex="-1"><i data-i="plus" data-s="16"></i></button>
</div>

<script>
document.getElementById('qty').addEventListener('zh:number', function (e) {
  console.log(e.detail.value);
});
</script>
属性用在取值说明
data-min.zh-num数字最小值,不写则不限
data-max.zh-num数字最大值,不写则不限
data-step.zh-num数字,默认 1每次加减的步长
data-precision.zh-num整数小数位数;不写则按步长推断(步长 0.5 → 1 位)
data-dir.zh-num-btn1 或 -1按钮的方向:1 增加,-1 减少
事件触发于detail说明
zh:number.zh-num{ value }值被步进、回车或失焦后规范化时触发(冒泡),value 是数字

验证码输入

分格的一次性验证码输入。只写一个空的容器,格子由库生成;支持粘贴、退格回退、方向键。校验由你来做,用 ZhaoUI.otp 把结果显示出来。

<div id="otpLabel">输入 6 位验证码</div>
<div class="zh-otp" id="otp" data-length="6" aria-labelledby="otpLabel"></div>

<script>
document.getElementById('otp').addEventListener('zh:otp-complete', function (e) {
  // 换成你自己的校验
  if (e.detail.value === '123456') ZhaoUI.otp.ok('#otp'); else ZhaoUI.otp.error('#otp');
});
</script>
属性用在取值说明
data-length.zh-otp整数,默认 6验证码位数
aria-labelledby.zh-otp标签元素的 id容器会自动加 role="group",用它给整组命名
is-error / is-ok.zh-otpclass由 ZhaoUI.otp.error / ok 设置,也会在用户再次输入时自动清除
  • ZhaoUI.otp.value(el):读取当前已输入的内容
  • ZhaoUI.otp.error(el):标记为错误并播放抖动动画
  • ZhaoUI.otp.ok(el):标记为通过
  • ZhaoUI.otp.clear(el):清空所有格子并把焦点放回第一格
事件触发于detail说明
zh:otp-complete.zh-otp{ value }所有格子填满时触发(冒泡),value 是完整的验证码字符串

页头

纯样式组件:不需要 JS。后台内容区顶部的标题区:返回按钮、标题、说明、操作按钮和一行元信息。

<header class="zh-ph">
  <nav aria-label="当前位置"><ol class="zh-crumb"><li><a href="#">首页</a></li><li aria-current="page">文章详情</li></ol></nav>
  <div class="zh-ph-main">
    <div class="zh-ph-title"><button class="zh-ph-back" aria-label="返回"><i data-i="chevron-left"></i></button><h4>产品发布会回顾</h4><span class="zh-tag success">已发布</span></div>
    <div class="zh-ph-actions"><button class="zh-btn">预览</button><button class="zh-btn primary">编辑</button></div>
  </div>
  <dl class="zh-ph-meta"><div><dt>作者</dt><dd>李明</dd></div><div><dt>更新</dt><dd>2026-10-04</dd></div></dl>
</header>
属性用在取值说明
zh-ph<header>(class)页头容器,上方一般放面包屑
zh-ph-main容器标题和操作按钮所在的一行,空间不够会换行
zh-ph-title容器里面放 <h4>,可带 zh-ph-back 返回按钮与 zh-ph-sec 副标题手机宽度隐藏副标题
zh-ph-actions容器里面放 zh-btn右侧操作区
zh-ph-meta<dl>多个 <div><dt>…</dt><dd>…</dd></div>标题下面的一行元信息
zh-ph--compact.zh-ph紧凑版:标题、标签页、操作排在一行

分页

分页器,页码数量恒定不跳动,支持每页条数、跳转框、简洁模式、多种尺寸。声明式写 data-zh-pager,或用 ZhaoUI.pager 创建。库不管数据,只管页码,翻页后你自己取数据。

<nav class="zh-pg" id="pg1" data-zh-pager data-total="486" data-size="10" data-show-size-changer data-show-jumper aria-label="文章分页"></nav>
<nav class="zh-pg" data-zh-pager data-total="200" data-simple aria-label="简洁分页"></nav>

<script>
document.getElementById('pg1').addEventListener('zh:page-change', function (e) {
  console.log('第', e.detail.page, '页,每页', e.detail.size, '条');
});
</script>
属性用在取值说明
data-zh-pager.zh-pg 或任意容器无声明这是一个分页器,用下面的 data-* 配置
data-total / data-page / data-page-size(等同 data-size)容器整数总条数 / 当前页(默认 1)/ 每页条数(默认 10)。注意:data-size 和 data-page-size 是每页多少条,不是控件大小;控件大小用 data-size-name
data-sizes容器逗号分隔,如 10,20,50每页条数的可选项,配合 data-show-size-changer
data-show-total / data-show-size-changer / data-show-jumper容器布尔属性显示总数(默认开)/ 每页条数选择 / 跳转输入框
data-simple / data-bordered / data-disabled / data-hide-on-single-page容器布尔属性简洁模式 / 带边框 / 禁用 / 只有一页时隐藏
data-size-name容器sm | md(默认)| lg控件的大小,不是每页条数(每页条数用 data-page-size)
data-siblings / data-boundary容器整数,默认 1当前页两侧与首尾各保留几个页码
  • ZhaoUI.pager(el, { total, page, size, ..., onChange }):命令式创建,返回 { get(), set(partial), go(page) }。选项与上面的 data-* 一一对应(驼峰写法)
  • el.__zhPager.set({ total }):数据条数变化后更新(例如筛选后),页码会自动收敛到有效范围
事件触发于detail说明
zh:page-change容器{ page, size }页码或每页条数变化时触发(冒泡)

气泡确认

挂在触发按钮旁的小确认框,用于轻量的二次确认(删除一行、下线一篇)。比模态框轻;破坏性很大的操作仍用确认对话框。

<button class="zh-btn" data-zh-popconfirm="删除这个文件?" data-desc="删除后 30 天内可在回收站找回。" data-ok="删除" data-danger id="pc-del">删除</button>
<script>
  document.getElementById('pc-del').addEventListener('zh:popconfirm', function (e) {
    if (e.detail.action === 'ok') ZhaoUI.message('已删除');
  });
</script>
属性用在取值说明
data-zh-popconfirm触发按钮确认问题,如“删除这个文件?”声明气泡确认,值是标题
data-desc同上补充说明可选
data-ok / data-cancel同上按钮文字,默认“确定”“取消”确认按钮写动作,如“删除”
data-danger同上破坏性操作:图标和确认按钮用危险色
data-kind同上warning(默认)| info图标语气
data-placement同上top(默认)| bottom | left | right位置
  • ZhaoUI.popconfirm(anchor, options):命令式用法。options:title、desc、okText、cancelText、kind、danger、placement、onConfirm()。onConfirm 返回 Promise 时,确认按钮显示加载中,成功后关闭,失败则保持打开
事件触发于detail说明
zh:popconfirm触发按钮{ action: 'ok' | 'cancel' }用户点了确认或取消。在 ok 时执行真正的操作

气泡卡片

点击(或悬停、聚焦)触发的浮层,里面可以放任意内容:说明、小表单、操作列表。内容写在页面里的一个隐藏元素中,库会复制进浮层。

<button class="zh-btn" data-zh-popover="#pv-card" data-arrow>查看详情</button>
<div id="pv-card" hidden>
  <p><strong>李明</strong></p>
  <p>内容组 · 最近更新 2 小时前</p>
</div>
属性用在取值说明
data-zh-popover触发按钮内容元素的选择器,如 #panel该元素会被隐藏,打开时把它的内容复制进浮层
data-placement同上top | bottom | left | right,可加 -start / -end,默认 bottom-start位置,空间不够自动翻转
data-trigger同上click(默认)| hover | focus触发方式
data-arrow同上显示指向触发元素的小箭头
事件触发于detail说明
zh:popover-open触发按钮{ popover }浮层打开后,popover 是浮层元素
zh:popover-close触发按钮无浮层关闭

进度条

线性进度条,支持标签位置、状态、分步、缓冲、目标线和不确定进度。只写一个带 data-* 的空元素,内容由库生成。

<div class="zh-progress" id="pg" data-value="36" data-name="导入数据"></div>
<div class="zh-progress" data-label="top" data-title="存储空间" data-value="62" data-name="存储空间"></div>
<div class="zh-progress" data-status="exception" data-value="42" data-name="上传失败"></div>
<div class="zh-progress" data-steps="10" data-value="60" data-name="完成步骤"></div>
<div class="zh-progress" data-indeterminate="true" data-label="none" data-name="加载中"></div>

<script>
ZhaoUI.progress.set('#pg', 80);
</script>
属性用在取值说明
data-value.zh-progress0 到 100当前进度
data-label.zh-progressoutside(默认)| inside | top | none百分比文字的位置;top 时用 data-title 作标题
data-name.zh-progress字符串无障碍名称(aria-label),务必填写
data-status.zh-progressnormal | success | warning | exception状态色;进度到 100 且未指定状态时自动变为 success,data-auto="off" 可关闭
data-steps.zh-progress整数分步显示,如 10 表示十格
data-buffer.zh-progress0 到 100缓冲进度(比如视频缓冲),画在主进度之下
data-target / data-target-label.zh-progress0 到 1 / 字符串目标线位置(0.8 表示 80%)与它的文字
data-indeterminate.zh-progresstrue不确定进度:显示循环动画,无数值
data-size.zh-progresssm | lg尺寸
is-active / is-striped.zh-progressclass进行中的流光 / 条纹效果
  • ZhaoUI.progress.set(el, value):更新进度(0 到 100),同步更新文字、状态色和 aria-valuenow。el 可以是元素或选择器

评分

星形或心形评分,支持半星、只读、清除、文字标签。交互版本是一组 radio,键盘与读屏都可用。

<div class="zh-rating" id="rt" data-value="3" data-label="服务评分" data-labels="很差,较差,一般,满意,很好"></div>
<div class="zh-rating" data-value="3.5" data-half data-show-value data-label="口味评分"></div>
<div class="zh-rating" data-readonly data-value="4.27" data-show-value data-label="综合评分"></div>

<script>
document.getElementById('rt').addEventListener('zh:rating', function (e) {
  console.log(e.detail.value);
});
</script>
属性用在取值说明
data-value.zh-rating数字当前评分;只读时可以是小数(如 4.27),会按比例填充
data-count.zh-rating整数,默认 5总数
data-half.zh-rating布尔属性允许半星
data-readonly / data-disabled.zh-rating布尔属性只读展示 / 禁用
data-clear.zh-rating布尔属性再次点击同一个分数、或按 Delete / Backspace 清除评分
data-icon.zh-ratingstar(默认)| heart图标
data-labels.zh-rating逗号分隔的文字,如 很差,较差,一般,满意,很好每一档的文字,选中时显示在旁边
data-show-value.zh-rating布尔属性在旁边显示数字
data-label.zh-rating字符串无障碍名称,默认「评分」
  • el.zhRating.get():读取当前评分
事件触发于detail说明
zh:rating.zh-rating{ value }评分变化时触发(冒泡)

结果页

纯样式组件:不需要 JS。操作完成、失败或页面不存在时的反馈块:图标(或大号错误码)、标题、说明、补充信息、操作按钮,居中排列。用 data-tone 选语气。

<div class="zh-card" style="padding:0">
  <div class="zh-result" data-tone="success" aria-live="polite">
    <div class="zh-result-icon"><i data-i="circle-check"></i></div>
    <h4 class="zh-result-title">提交成功</h4>
    <p class="zh-result-desc">审批单已提交,预计 1 个工作日内处理。</p>
    <div class="zh-result-actions">
      <button class="zh-btn primary">查看详情</button>
      <button class="zh-btn">返回列表</button>
    </div>
  </div>
</div>
属性用在取值说明
zh-result容器(class)居中的结果块,通常放在 .zh-card 里
data-tone.zh-resultsuccess | error | info | warning(默认中性灰)决定图标圆底和图标的颜色
zh-result-icon图标容器里面放 <i data-i="...">圆形底色图标
zh-result-code大号文字如 404错误页用,装饰性,加 aria-hidden="true"
zh-result-title / zh-result-desc<h4> / <p>标题和说明,说明最宽约 46 个字符
zh-result-extra补充块里面放 <ul>失败原因、后续步骤等,左对齐的灰底块
zh-result-actions按钮容器里面放 zh-btn居中排列;手机宽度按钮各占一行
is-in.zh-result可选:加上后播放入场动画(图标弹出,其余依次升起)

环形进度

圆环、仪表盘(270°)、半圆三种形状的进度,中心显示百分比或自定义内容。

<div class="zh-ring" id="ring" data-value="65" data-name="完成率"></div>
<div class="zh-ring" data-size="lg" data-value="88" data-name="目标" data-caption="完成率"></div>
<div class="zh-ring" data-shape="gauge" data-value="72" data-name="仪表"></div>
<div class="zh-ring" data-value="42" data-status="exception" data-name="失败"></div>

<script>
ZhaoUI.ring.set('#ring', 90);
</script>
属性用在取值说明
data-value.zh-ring0 到 100当前进度;必须写,库据此初始化
data-shape.zh-ringfull(默认)| gauge | semi圆环 / 仪表盘 / 半圆
data-size.zh-ringsm | lg尺寸,默认中号
data-caption.zh-ring字符串百分比下方的小字
data-status.zh-ringnormal | success | warning | exception状态色;到 100 自动变为 success,中心换成对勾
data-name.zh-ring字符串无障碍名称
  • ZhaoUI.ring.set(el, value, centerHtml?):更新进度;centerHtml 可选,传入则替换中心内容。centerHtml 按 HTML 处理,不要放用户输入

下拉选择

增强原生 <select>:单选、多选、搜索、分组、清除、选项描述,选中值同步回原生 select 并触发原生 change,表单提交、name、required 照常工作。也可用 ZhaoUI.select 命令式创建,支持远程搜索和自动完成。

<label for="region">区域</label>
<select id="region" name="region" data-zh-select data-clearable required>
  <option value="">请选择区域</option>
  <option value="east">华东</option>
  <option value="south">华南</option>
  <option value="north">华北</option>
</select>

<label for="city">城市</label>
<select id="city" name="city" data-zh-select data-searchable data-empty-text="没有这个城市">
  <option value="">请选择城市</option>
  <optgroup label="直辖市">
    <option value="bj" data-keys="beijing bj" data-desc="首都">北京</option>
    <option value="sh" data-keys="shanghai sh">上海</option>
  </optgroup>
  <optgroup label="广东省">
    <option value="gz" data-keys="guangzhou gz">广州</option>
    <option value="sz" data-keys="shenzhen sz" disabled>深圳</option>
  </optgroup>
</select>

<label for="tags">标签</label>
<select id="tags" name="tags" multiple data-zh-select data-searchable data-clearable data-max-tags="2">
  <option value="fe" selected>前端</option>
  <option value="be" selected>后端</option>
  <option value="ux">设计</option>
</select>

<div id="remote"></div>

<script>
document.getElementById('region').addEventListener('change', function (e) {
  console.log(e.target.value);
});

// 远程搜索
ZhaoUI.select('#remote', {
  searchable: true, clearable: true, placeholder: '搜索技术',
  source: function (q) {
    return fetch('/api/tech?q=' + encodeURIComponent(q)).then(function (r) { return r.json(); });   // 返回 [{ value, label }, …]
  }
});
</script>
属性用在取值说明
data-zh-select<select>无声明要增强这个 select;选项、optgroup、disabled、multiple、selected 都从它读取
multiple<select>布尔属性多选:选中项显示为标签,列表保持打开
data-searchable<select>布尔属性可输入搜索;按选项文字、data-desc、data-keys 匹配并高亮
data-clearable<select>布尔属性有值时显示清除按钮
data-size<select>sm | md(默认)| lg尺寸
data-placeholder<select>字符串占位文字;不写则取值为空的那个 <option> 的文字,再没有则是「请选择」
data-empty-text<select>字符串搜索无结果时的提示,默认「无匹配项」
data-max-tags<select multiple>整数最多显示几个标签,其余折叠为「+N」
data-prefix-icon<select>图标名输入框前的图标
data-desc / data-icon / data-keys<option>字符串选项的第二行说明 / 图标 / 额外的搜索关键词(如拼音)
value=""<option>空值放一个空值的 option 作为占位;它不会出现在选项列表里,清除后 select 会回到它
aria-invalid="true"<select>true错误状态外观
  • selectEl.zhSelect.get() / .set(value):读取或设置值(多选传数组)。set 会同步原生 select,但不触发 change
  • selectEl.zhSelect.setOptions([{ value, label, desc?, icon?, disabled? }, …]):更换选项(支持分组:{ label, options: [...] })。原生 <select> 里的 <option> 会一起重建,已选且仍存在的值会保留,不再存在的会被移除
  • selectEl.zhSelect.setDisabled(bool) / .setInvalid(bool) / .open() / .close():禁用、错误状态、打开、关闭
  • ZhaoUI.select(host, config):命令式创建。host 是一个空 <div> 或选择器;config:options、value、multiple、searchable、clearable、size、placeholder、emptyText、maxTags、prefixIcon、disabled、invalid、labelledby、onChange(value, labels)。 远程搜索:source(query) 返回 Promise<选项数组>,可配 debounce(毫秒,默认 250)和 loading。 自动完成:freeText: true,输入的文字本身就是值,chevron: false 可隐藏箭头。返回同一套方法(get / set / setOptions / setDisabled / setInvalid / open / close)
事件触发于detail说明
change / input原生 <select>原生事件选中值变化时在原生 select 上触发(冒泡),已有的表单代码不用改
zh:select-change生成的控件(select 后面的 .zh-sel){ value, labels }单选时 value 是字符串,多选时是数组;labels 是对应的文字数组

侧边菜单

后台常见的左侧导航:多级分组、当前页标记、可折叠成 64px 图标栏,支持键盘(方向键、Home/End、字母)。用嵌套的 <ul> 写,库会补齐 ARIA 树结构。

<nav class="zh-side" data-zh-side aria-label="站点导航" style="height:420px">
  <div class="zh-side-head"><i aria-hidden="true"></i><span>ZhaoUI</span></div>
  <ul>
    <li class="zh-side-group">工作台</li>
    <li><a href="#" data-icon="layout-dashboard">概览</a></li>
    <li><a href="#" data-icon="file-text">内容管理</a>
      <ul>
        <li><a href="#" aria-current="page" data-badge="12" data-badge-tone="soft">文章</a></li>
        <li><a href="#">媒体库</a></li>
      </ul>
    </li>
    <li><a href="#" data-icon="settings">系统设置</a></li>
  </ul>
</nav>
属性用在取值说明
data-zh-side<nav class="zh-side">声明为侧边菜单;里面放一个嵌套 <ul>
data-icon每个 <a>图标名可选,菜单项前的图标
data-num每个 <a>序号文字,如 1可选,在名称前显示序号(文档导航常用);读屏读名称时忽略序号,折叠成图标栏时隐藏
data-badge<a>文字 | dot可选,右侧角标;dot 是小红点
data-badge-tone<a>soft角标用淡色底而不是红底
aria-current="page"当前页的 <a>page当前页;它所在的分组会默认展开
aria-disabled="true"<a>true禁用的菜单项,不可点、不可跳转
data-open含子菜单的 <li>该分组默认展开
data-collapsed<nav>初始为折叠的图标栏
data-collapsible<nav>false不显示折叠按钮
zh-side-group<li>(class)分组标题文字菜单里的分组小标题,不可点
  • ZhaoUI.side.setActive(nav, hrefOrElement):按链接地址或元素设置当前页,并展开它的祖先;找不到返回 false
  • ZhaoUI.side.collapse(nav, collapsed?):折叠(默认)或展开图标栏
事件触发于detail说明
zh:side-selectnav.zh-side{ item, href, label, path }点击叶子项时触发,可 preventDefault 取消;path 是从顶层到该项的名称数组。href 为 # 或为空时库不会跳转,只切换当前页
zh:side-collapsenav.zh-side{ collapsed }折叠或展开图标栏

滑块

单值或范围滑块,支持刻度、步进、竖直、颜色分档,键盘与指针都可操作。只写一个带 data-* 的空元素。

<div class="zh-slider" id="vol" data-value="40" data-label="音量" data-fmt="{v}%"></div>
<div class="zh-slider" data-value="50" data-step="25" data-label="清晰度" data-marks="0:流畅,25:标清,50:高清,75:超清,100:蓝光"></div>
<div class="zh-slider" data-value="800,3200" data-min="0" data-max="5000" data-step="50" data-gap="200" data-label="价格" data-fmt="¥{v}"></div>
<div class="zh-slider" data-value="78" data-label="存储用量" data-fmt="{v}%" data-colors="0:primary,70:warning,90:danger" data-status="0:正常,70:偏高,90:危险"></div>

<script>
document.getElementById('vol').addEventListener('zh:slider', function (e) {
  if (e.detail.type === 'change') console.log(e.detail.values[0]);
});
</script>
属性用在取值说明
data-value.zh-slider数字,或「下限,上限」写两个值即为范围滑块,如 800,3200
data-min / data-max / data-step.zh-slider数字,默认 0 / 100 / 1范围与步长,值会吸附到步长上
data-label.zh-slider字符串无障碍名称;范围滑块会自动加「最小值 / 最大值」
data-fmt.zh-slider含 {v} 的模板,如 {v}%显示与朗读时的数值格式
data-marks.zh-slider值:文字 逗号分隔,如 0:低,50:中,100:高刻度;点击刻度可直接跳到该值
data-colors / data-status.zh-slider值:颜色 或 值:文字 逗号分隔按数值分档变色(primary、warning、danger 等)或附加状态文字
data-gap.zh-slider数字范围滑块两个滑块之间的最小间隔
data-vertical / data-disabled.zh-slider布尔属性竖直 / 禁用;竖直滑块用 --zh-slider-len 设定长度
data-out.zh-slider<output> 的选择器把当前值写到这个元素里
data-link.zh-slider数字输入框的选择器,逗号分隔与输入框双向同步
  • el.zhSlider.get() / el.zhSlider.set([values]):读取或设置当前值(数组)
事件触发于detail说明
zh:slider.zh-slider{ type: 'input' | 'change', values }拖动中触发 input,松手或按键后触发 change(冒泡)

加载中

纯样式组件:不需要 JS,写好标记即可。一个转动的圆圈,表示正在加载。三个大小:sm、默认、lg。系统开启“减少动态效果”时圈会静止,所以旁边请同时写“加载中”之类的文字。

<div class="zh-row" role="status"><span class="zh-spinner" aria-hidden="true"></span><span>加载中…</span></div>
<span class="zh-spinner sm" aria-hidden="true"></span> <span class="zh-spinner lg" aria-hidden="true"></span>
属性用在取值说明
zh-spinner<span>(class)转动的圆圈;可加 sm 或 lg
role="status"外层容器读屏会读出里面的文字

步骤条

表示多步流程走到了哪一步:数字或图标标记、连接线、当前步骤、出错步骤。你只写每步的标题,库生成标记并按当前步骤算出完成、进行中、未开始。容器放不下时自动改成纵向。

<div class="zh-stack">
  <ol data-zh-steps data-current="1" aria-label="开通流程">
    <li data-title="填写信息" data-desc="企业与联系人"></li>
    <li data-title="资质审核" data-desc="约 1 个工作日"></li>
    <li data-title="开通完成"></li>
  </ol>
  <ol data-zh-steps data-mode="icon" data-current="2" data-error="1" data-error-text="文件过大" aria-label="上传流程">
    <li data-title="选择文件" data-icon="file-text"></li>
    <li data-title="上传" data-icon="settings"></li>
    <li data-title="完成" data-icon="check"></li>
  </ol>
</div>
属性用在取值说明
data-zh-steps<ol>里面放 <li data-title="…">声明为步骤条;<li> 里的内容会被替换
data-title / data-desc<li>文字步骤标题与说明(图标模式不显示说明)
data-icon<li>图标名图标模式下该步的图标;完成变成对勾,出错变成叉
data-current<ol>从 0 开始的序号,默认 0当前步骤;之前的算已完成,之后的未开始
data-error<ol>出错步骤的序号该步显示为错误;可配 data-error-text 写出错说明
data-done<ol>全部完成
data-mode<ol>icon用图标代替数字
data-clickable<ol>每一步变成按钮,点击触发 zh:step-click;data-reach 指定最远可点到哪一步,默认只到当前步
zh-steps--v / --sm / --lg / --dot<ol>(class)纵向 / 小尺寸 / 大尺寸 / 极简圆点;默认是横向
  • ZhaoUI.steps.set(el, { current, error, done }):更新状态并重绘;设置 current 会清除错误态
  • ZhaoUI.steps.get(el):返回 { current, error, done }
事件触发于detail说明
zh:step-click<ol>undefined可点击模式下点了某一步。是否真的跳转由你决定(比如先校验表单),然后调用 ZhaoUI.steps.set

表格

带圆角边框的数据表:表头底色、行悬停高亮、数字列右对齐。窄屏(≤ 767px)默认变成卡片列表,也可以改成横向滚动。样式部分不需要 JS;加 data-zh-table 后可以点表头排序、用复选框选择行。分页用 data-zh-pager。

<div class="zh-table-wrap">
  <table class="zh-table">
    <thead><tr><th scope="col">标题</th><th scope="col">状态</th><th scope="col" class="num">阅读</th><th scope="col">操作</th></tr></thead>
    <tbody>
      <tr><td data-label="标题">产品发布会回顾</td><td data-label="状态"><span class="zh-tag success">已发布</span></td><td data-label="阅读" class="num">12,480</td><td data-label="操作"><button class="zh-btn sm text">编辑</button></td></tr>
      <tr><td data-label="标题">季度运营复盘</td><td data-label="状态"><span class="zh-tag warning">待审核</span></td><td data-label="阅读" class="num">980</td><td data-label="操作"><button class="zh-btn sm text">编辑</button></td></tr>
    </tbody>
  </table>
</div>

<!-- 列很多、窄屏也想保持表格:加 zh-table-wrap--scroll -->
<div class="zh-table-wrap zh-table-wrap--scroll" tabindex="0" role="region" aria-label="订单列表(可横向滚动)">
  <table class="zh-table">
    <thead><tr><th scope="col">订单号</th><th scope="col">客户</th><th scope="col">地区</th><th scope="col">渠道</th><th scope="col">状态</th><th scope="col" class="num">金额</th><th scope="col">下单时间</th></tr></thead>
    <tbody>
      <tr><td>20261004-0086</td><td>张三</td><td>上海</td><td>官网</td><td>已发货</td><td class="num">1,286.00</td><td>2026-10-04 15:20</td></tr>
      <tr><td>20261004-0087</td><td>李四</td><td>北京</td><td>小程序</td><td>待发货</td><td class="num">329.00</td><td>2026-10-04 15:42</td></tr>
    </tbody>
  </table>
</div>

<div class="zh-table-wrap">
<table class="zh-table" data-zh-table id="tbl-sel">
  <thead><tr><th class="zh-col-check"><input type="checkbox" data-zh-all aria-label="全选"></th><th data-sort="text">标题</th><th data-sort="number" class="num">阅读量</th><th data-sort="text">更新时间</th></tr></thead>
  <tbody>
    <tr><td class="zh-col-check"><input type="checkbox" data-zh-row aria-label="选择产品发布会回顾"></td><td>产品发布会回顾</td><td class="num">12,480</td><td data-sort-value="2026-10-04T15:20">2 小时前</td></tr>
    <tr><td class="zh-col-check"><input type="checkbox" data-zh-row aria-label="选择季度运营复盘"></td><td>季度运营复盘</td><td class="num">980</td><td data-sort-value="2026-10-04T11:02">6 小时前</td></tr>
    <tr><td class="zh-col-check"><input type="checkbox" data-zh-row aria-label="选择新版设计规范说明"></td><td>新版设计规范说明</td><td class="num">8,326</td><td data-sort-value="2026-10-03T18:45">昨天</td></tr>
  </tbody>
</table>
</div>
属性用在取值说明
zh-table-wrap外层 <div>(class)里面放 <table class="zh-table">圆角边框与裁切;必须有,圆角靠它
zh-table<table>(class)正常的 thead / tbody / tr / th / td表格样式
num<th> / <td>(class)数字、金额右对齐
data-label每个 <td>该列的名称,如 data-label="状态"窄屏变卡片列表时显示在值的左侧;不写则左侧为空
zh-table-wrap--scroll外层 wrap 上(class)加在 zh-table-wrap 上窄屏不变卡片,保持表格并允许横向滚动,适合列很多的后台表格
data-zh-table<table>开启排序和行选择的行为
data-sort表头 <th>text | number该列可排序:表头变成按钮,点击在升序、降序、恢复原顺序之间切换。number 会忽略逗号、百分号和货币符号;空值始终排在最后
data-sort-value单元格 <td>用于排序的值单元格显示的文字不适合排序时(如“2 小时前”)用它指定排序值,如 ISO 时间
data-sort-mode<table>server只触发事件、不在页面里重排,用于服务端排序;你在事件里请求新数据
data-zh-all表头里的 <input type="checkbox">全选框;部分行选中时显示半选状态
data-zh-row每行里的 <input type="checkbox">行选择框;选中的行会加上 is-selected 高亮;加 disabled 的行不参与全选
zh-col-check选择框所在的 <th>/<td>把该列收窄到刚好放下复选框
  • ZhaoUI.table.selected(table):返回当前选中的 <tr> 数组
  • ZhaoUI.table.clear(table):取消全部选择
事件触发于detail说明
zh:table-sort<table>{ index, key, direction }排序变化。direction 为 ascending、descending 或 none;key 取 th 的 data-key,没有就用表头文字
zh:table-select<table>{ rows, count, all }选择变化。rows 是选中的 <tr> 数组

标签页

横向或纵向标签页。标签用 aria-controls 关联面板,面板的显示隐藏由库处理。也可以当「分段开关」用:几个标签指向同一个面板,在 zh:tab-change 里换内容。

<div class="zh-tabs" role="tablist" aria-label="分区">
  <button role="tab" aria-selected="true" aria-controls="p1">概览</button>
  <button role="tab" aria-selected="false" aria-controls="p2">正文</button>
</div>
<div id="p1">概览内容</div>
<div id="p2">正文内容</div>

<!-- 分段开关:三个标签共用一个面板,由事件换内容 -->
<div class="zh-tabs" role="tablist" id="range" aria-label="统计周期">
  <button role="tab" aria-selected="true" aria-controls="kpi">今日</button>
  <button role="tab" aria-selected="false" aria-controls="kpi">近 7 天</button>
  <button role="tab" aria-selected="false" aria-controls="kpi">近 30 天</button>
</div>
<section id="kpi" aria-live="polite">…</section>
<script>
function render(i) { document.getElementById('kpi').textContent = ['今日数据', '近 7 天数据', '近 30 天数据'][i]; }
// 初始化时也会触发一次 zh:tab-change,所以第一次进来就会按当前选中的标签渲染
document.getElementById('range').addEventListener('zh:tab-change', function (e) { render(e.detail.index); });
</script>
属性用在取值说明
role="tablist".zh-tabs必填标识这是标签列表,库据此初始化
aria-orientation.zh-tabsvertical纵向标签页:方向键改为上下
aria-controls每个 [role=tab]对应面板的 id对应面板的 id。多个标签可以指向同一个面板:只要当前选中的标签指向它,它就是显示的;不写则只切换选中状态,不管理面板
aria-selected每个 [role=tab]true | false初始选中项;都没写则选中第一个
事件触发于detail说明
zh:tab-change.zh-tabs{ index, tab }选中项变化时触发(冒泡),初始化时也会触发一次

标签

标签:普通的彩色标签、可关闭的标签,以及可选中的标签(单选组、多选)。普通标签只要 class="zh-tag" 加颜色类,不需要 zh-tag--x;zh-tag--x 才表示可关闭或可选。新增标签的输入框不在库里,按你的业务自己做。

<span class="zh-tag success">已发布</span>
<span class="zh-tag warning">待审核</span>
<span class="zh-tag">草稿</span>

<div id="tag-demo">
  <span class="zh-tag zh-tag--x primary"><span>设计</span><button type="button" class="zh-tag-close" aria-label="移除 设计"><i data-i="x"></i></button></span>
  <span class="zh-tag zh-tag--x"><span>研发</span><button type="button" class="zh-tag-close" aria-label="移除 研发"><i data-i="x"></i></button></span>
</div>

<div class="zh-tag-group" id="period" role="radiogroup" aria-label="选择周期">
  <button class="zh-tag zh-tag--x zh-tag--check" role="radio" aria-checked="true" tabindex="0"><i data-i="check" data-s="12" class="zh-tag-tick"></i>今天</button>
  <button class="zh-tag zh-tag--x zh-tag--check" role="radio" aria-checked="false" tabindex="-1"><i data-i="check" data-s="12" class="zh-tag-tick"></i>近 7 天</button>
</div>

<script>
document.getElementById('period').addEventListener('zh:tag-change', function (e) {
  console.log(e.detail.value);
});
</script>
属性用在取值说明
zh-tag<span>(class)可加 primary | success | warning | danger普通标签。不加颜色类就是中性灰(适合「草稿」「默认」这类状态);加 zh-tag--x 才有关闭按钮或选中行为
zh-tag-close.zh-tag--x 内的按钮(class)无点击后移除所在标签;按钮必须有 aria-label(如「移除 设计」)
role="radio".zh-tag--check放在 role="radiogroup" 里单选:点击或方向键切换,roving tabindex
role="checkbox".zh-tag--checkaria-checked="true|false"多选:点击切换
primary / success / warning / danger / info.zh-tagclass颜色
zh-tag--filled / zh-tag--outline / zh-tag--sm / zh-tag--lg.zh-tagclass填充或描边外观、小或大尺寸,可与颜色类组合
事件触发于detail说明
zh:tag-remove被点击的 .zh-tag{ label, tag }移除前触发(冒泡,可取消);调用 preventDefault() 则保留标签
zh:tag-change单选:radiogroup;多选:被点击的标签单选 { value, values };多选 { value, checked, values }选中状态变化时触发(冒泡)

多行文本

随内容增高的文本域,带字数统计。硬上限用 maxlength,软上限 data-soft-max 允许继续输入但提示超出。

<div class="zh-ta" data-max-rows="6">
  <textarea class="zh-textarea" rows="2" maxlength="80" placeholder="输入内容,最多 80 字" aria-label="简介"></textarea>
  <span class="zh-ta-count" aria-hidden="true">0 / 80</span>
</div>

<div class="zh-ta" data-soft-max="30" data-max-rows="6">
  <textarea class="zh-textarea" rows="2" placeholder="超过 30 字会标红,仍可继续输入" aria-label="备注">这段文字已经超过了三十个字的软上限,计数会变红并提示超出多少字。</textarea>
  <span class="zh-ta-count" aria-hidden="true"></span>
</div>
属性用在取值说明
data-max-rows.zh-ta整数,默认 6最多增高到几行,超过后出现滚动条
data-soft-max.zh-ta整数软上限:超过后计数变红、textarea 设 aria-invalid,但仍可输入
maxlengthtextarea整数硬上限:浏览器阻止继续输入,计数显示「当前 / 上限」
data-ta-msg.zh-ta 的兄弟元素带 hidden 的提示容器,内含一个 <span>可选:超出软上限时显示「超出 N 字」

时间选择

时、分(可选秒)三列滚动选择,也可以直接键入。分钟可按步长取整。

<div data-zh-time data-name="at" data-value="09:30" aria-label="提醒时间"></div>
<div data-zh-time data-seconds data-step-minutes="15" data-value="14:15:10" aria-label="精确时间"></div>

<script>
document.querySelector('[data-zh-time]').addEventListener('zh:time-change', function (e) {
  console.log(e.detail.value);   // 09:45
});
</script>
属性用在取值说明
data-zh-time<div>无声明这是时间选择
data-value容器HH:mm 或 HH:mm:ss初始时间
data-seconds容器布尔属性显示并允许选择秒
data-step-minutes容器整数,默认 1分钟的步长,如 15;键入的分钟会四舍五入到最近的步长
data-name容器字段名生成隐藏 <input name=…> 参与表单提交
data-size容器sm | md(默认)| lg尺寸
aria-label / data-labelledby / data-label容器字符串 / 标签元素 id / 弹层标题输入框的无障碍名称;data-label 是弹出面板的标题
<label for="容器 id">容器所在页面的 <label>for 指向容器的 idlabel 会成为输入框的无障碍名称,点击 label 会聚焦输入框;容器是空 <div>,label 的 for 指向它的 id 即可
  • ZhaoUI.timePicker(host, { value: { h, m, s }, seconds, stepM, size, label, onChange }):命令式创建;value 是 { h, m, s } 对象
  • el.zhTime.get() / .set(value) / .open() / .close():get 返回 { h, m, s } 或 null;set 接受对象或 HH:mm 字符串,不触发事件
事件触发于detail说明
zh:time-change容器{ value: 'HH:mm' | 'HH:mm:ss' | '', time: { h, m, s } | null }选择、键入确认、点「此刻」或清除后触发(冒泡)

时间线

按时间顺序展示事件:左侧一条轨道和圆点,右侧是标题、时间、说明。圆点颜色表示状态(primary、success、warning、danger、info),进行中的项显示转圈。可以带头像、引用、附件,以及点击展开的详情。

<ol class="zh-tl">
  <li class="zh-tl-item zh-tl-item--success"><div class="zh-tl-rail"><span class="zh-tl-dot" aria-hidden="true"></span></div>
    <div class="zh-tl-body"><div class="zh-tl-head"><span class="zh-tl-title">订单已创建</span><time class="zh-tl-time" datetime="2026-10-04T09:00">09:00</time></div><div class="zh-tl-desc">订单 #20261004</div></div></li>
  <li class="zh-tl-item zh-tl-item--primary"><div class="zh-tl-rail"><span class="zh-tl-dot" aria-hidden="true"></span></div>
    <div class="zh-tl-body"><div class="zh-tl-head"><span class="zh-tl-title">仓库已接单</span><time class="zh-tl-time" datetime="2026-10-04T09:12">09:12</time></div>
      <button type="button" class="zh-tl-toggle" aria-expanded="false" aria-controls="tl-d1"><i data-i="chevron-right" data-s="14"></i><span>展开详情</span></button>
      <div class="zh-tl-detail" id="tl-d1"><div class="zh-tl-detail-wrap"><div class="zh-tl-detail-in">上海仓 · 拣货员 A12 已接单,预计 20 分钟内出库。</div></div></div></div></li>
  <li class="zh-tl-item zh-tl-item--pending" aria-label="进行中:派送中"><div class="zh-tl-rail"><span class="zh-tl-dot zh-tl-dot--icon" aria-hidden="true"><i data-i="loader-circle" data-s="14" class="zh-tl-spin"></i></span></div>
    <div class="zh-tl-body"><div class="zh-tl-head"><span class="zh-tl-title">派送中</span></div></div></li>
</ol>
属性用在取值说明
zh-tl<ol>(class)里面放 zh-tl-item时间线容器
zh-tl-item--primary|success|warning|danger|info|pending<li>(class)圆点和轨道的状态色;pending 表示进行中,圆点换成转圈
zh-tl-dot--hollow圆点(class)空心点;zh-tl-dot--icon 放图标,再加 zh-tl-dot--lg 变大
zh-tl-quote正文里的容器引用块,如审批意见
zh-tl-toggle + zh-tl-detail展开按钮与详情容器点击按钮展开详情,库负责切换 aria-expanded 与文字;按钮上可用 data-open / data-close 自定义文字
zh-tl--alt / --right / --compact / --feed<ol>(class)左右交替 / 靠右 / 紧凑 / 动态流(带头像)
事件触发于detail说明
zh:timeline-toggle<ol>undefined详情展开或收起

文字提示

悬停或键盘聚焦时显示的小气泡,自动避让屏幕边缘,带箭头。适合解释图标按钮或被禁用的按钮。不用 JS 的简易版见「样式类」里的 zh-tip。

<div class="zh-row">
  <button class="zh-btn" data-zh-tt="保存当前草稿">保存</button>
  <span data-zh-tt="没有编辑权限,请联系管理员开通" data-zh-tt-placement="bottom"><button class="zh-btn primary" disabled aria-disabled="true">提交审核</button></span>
</div>
属性用在取值说明
data-zh-tt任意元素提示文字声明提示;元素不可聚焦时库会补 tabindex=0,使键盘也能看到
data-zh-tt-placement同上top | bottom | left | right(可加 -start / -end),默认 top出现的位置,空间不够会自动翻转
data-zh-tt-delay同上毫秒,默认 80悬停多久后出现

树

可展开的层级列表:文件目录、组织架构、分类。写一个嵌套的 <ul>,库渲染成带展开箭头的行;可选复选框(父级根据子级显示全选、半选、未选)、单选高亮和完整的键盘操作。

<div class="zh-tree" data-zh-tree data-checkable aria-label="文件" style="max-width:20rem">
  <ul>
    <li data-id="src" data-open data-icon="file-text">src
      <ul>
        <li data-id="a" data-checked>index.js</li>
        <li data-id="b">style.css</li>
        <li data-id="c" data-disabled>locked.txt</li>
      </ul>
    </li>
    <li data-id="docs" data-icon="file-text">docs
      <ul><li data-id="d">readme.md</li></ul>
    </li>
  </ul>
</div>
属性用在取值说明
data-zh-tree<div class="zh-tree">里面放一个嵌套的 <ul>声明为树;<li> 的文字是节点名称,子级写在它里面的 <ul> 里
data-id<li>唯一标识事件和方法里用它指代节点;不写会自动生成
data-icon<li>图标名节点前的图标
data-open<li>该节点默认展开
data-checked<li>默认选中(仅复选模式;父级的状态由子级算出)
data-disabled<li>禁用,不可选择、不可勾选
data-checkable容器显示复选框
data-no-select容器不要点击高亮(只做展开或勾选时用)
data-toggle-on-click容器点击整行也展开或收起,不只是点箭头
zh-tree--guides容器(class)显示缩进参考线
  • ZhaoUI.tree.checked(el):返回选中的叶子节点 id 数组
  • ZhaoUI.tree.expandAll(el) / collapseAll(el):全部展开 / 收起
  • ZhaoUI.tree.select(el, id):选中某个节点并展开它的祖先;找不到返回 false
事件触发于detail说明
zh:tree-select容器undefined点击或回车选中某个节点
zh:tree-check容器undefined勾选变化;ids 是当前全部选中的叶子节点的 data-id
zh:tree-toggle容器undefined展开或收起

文件上传

拖拽或点击选择文件,带文件列表、进度条、类型与大小校验、数量限制,失败可重试,上传中可取消。可以用 data-action 直接 POST 到接口,也可以用自己的函数上传;两者都不给时只收集文件,由你在提交表单时取。只有列表模式;头像上传和图片卡片墙没有做。

<div class="zh-upload" data-zh-upload data-accept=".pdf,.png,.jpg" data-max="3" data-max-mb="5" data-hint="PDF、PNG、JPG · 单个不超过 5 MB · 最多 3 个" style="max-width:32rem">
  <div class="zh-upload-foot"><span class="zh-upload-count zh-hint">已添加 0 / 3 个</span></div>
</div>
属性用在取值说明
data-zh-upload<div class="zh-upload">声明为上传;没有写 zh-upload-drop 时库会生成默认的拖拽区
data-accept容器如 .pdf,.png,image/*允许的类型:扩展名或 MIME;也会传给系统的文件选择窗口
data-max-mb容器数字单个文件大小上限(MB)
data-max容器数字最多文件数;设为 1 时选新文件会替换旧的
data-action容器接口地址用 XHR 以 multipart/form-data 上传到这里,文件字段名由 data-field 指定(默认 file);返回 2xx 算成功
data-credentials容器跨域上传时带上 Cookie
data-hint容器提示文字生成默认拖拽区时显示的限制说明
is-disabled容器(class)禁用,不能选择也不能拖入
  • ZhaoUI.upload.files(el):返回列表里的 File 数组,可自己放进 FormData 提交
  • ZhaoUI.upload.uploader(el, fn):设置自己的上传函数 fn(file, { onProgress(百分比), onAbort(回调) }),返回 Promise;会替代 data-action
  • ZhaoUI.upload.add(el, files):以代码方式加入文件(同样会校验)
  • ZhaoUI.upload.clear(el):清空列表
事件触发于detail说明
zh:upload-add容器undefined文件通过校验并加入列表
zh:upload-reject容器undefined文件没通过校验(类型、大小、数量)
zh:upload-done容器undefined上传成功;result 是服务器返回的文本或你的函数的返回值
zh:upload-error容器undefined上传失败
zh:upload-remove容器undefined从列表移除(含上传中取消)

色彩

品牌色输入一个值,自动生成 10 级色阶。主色按对比度自动挑选,保证按钮文字满足 WCAG AA(4.5:1)。中性色与功能色跟随亮暗模式。

品牌色阶 --zh-brand-1 … 10

语义色 --zh-color-*

中性色

品牌色提取

整套主题从一个主色出发。上传 Logo,系统提取主色,并派生色阶、按钮文字色、暗色主色,以及玻璃拟态风格的背景光斑。没有 Logo 时使用默认靛蓝。

试试示例

字体

系统字体栈,不引入网络字体,首屏更快,也利于 SEO 与性能。B 端基准 14px,C 端基准 16px,中控大屏整体放大 1.4 倍。

间距

4px 栅格。组件内间距随「密度」缩放:紧凑 ×0.8、标准 ×1、宽松 ×1.2。

圆角与阴影

B 端小圆角、中性阴影;C 端大圆角,阴影带一点品牌色,切换品牌色时能直接看到差别。

radius-sm
radius-md
radius-lg
radius-full
shadow-sm
shadow-md
shadow-lg

动效

B 端 120ms 求快,C 端 200ms 求柔和。统一缓动曲线;系统开启「减少动态效果」时去掉位移与循环,只保留淡入淡出。

悬停圆点所在轨道,切换 B/C 对比速度。当前时长

尺寸与层级

三件事:控件有三档尺寸;文字有五级强弱;界面元素有固定的叠放顺序。

组件尺寸 sm / md / lg

尺寸B 端C 端用在哪里
sm24px36px表格行内操作、工具栏、次要按钮
md(默认)32px44px表单、常规按钮、搜索框
lg40px52px页面主操作、登录、下单等关键动作

「密度」会整体加减 4px;sm 和 lg 始终是 md 减 8px 和加 8px。触屏设备最小 44px,所以 B 端手机上 sm 会被抬到 44px。

文字层级(强弱五级)

标题 · 页面、区块、卡片标题--zh-text
主文本 · 正文、表格内容--zh-text
次文本 · 说明、表头、字段名--zh-text-secondary
辅助 · 时间戳、图注、占位提示--zh-text-tertiary
禁用 · 不可用状态(不要求对比度)--zh-text-disabled—

右侧是该文字色在当前背景上的实测对比度。标题、主文本、次文本、辅助都要达到 4.5:1(WCAG AA)。

层级堆叠:z-index 与投影

层Tokenz-index投影
页面内容--zh-z-base0无 / sm
粘性顶栏、侧栏--zh-z-sticky100sm
下拉、选择面板--zh-z-dropdown1000md
抽屉--zh-z-drawer1100lg
模态框--zh-z-modal1200lg
气泡、确认框--zh-z-popover1300md
消息提示--zh-z-toast1400lg
工具提示--zh-z-tooltip1500md

业务代码只能引用这些 Token,不许写裸数字。投影只有 sm / md / lg 三档,层越高越大。

页面内容0
下拉面板1000 · md
模态框1200 · lg
消息提示1400 · lg
工具提示1500 · md

文字色与数字色

文字只用「文字令牌」,不随手取色。数字有两套颜色:涨跌色表示方向,好坏色表示对业务是好是坏。颜色永远不单独表达意思,必须配符号或图标。

文字色

品牌文字 · 强调、选中态--zh-color-primary-text
链接文字 · 可点击--zh-color-primary-text
成功文字 · 已完成、通过--zh-text-success
警告文字 · 待处理、即将到期--zh-text-warning
危险文字 · 错误、失败、必填提示--zh-text-danger
信息文字 · 提示、说明--zh-text-info
反色文字 · 深色底、主色底--zh-text-inverse—

状态文字用的是比状态填充色更深的「文字专用」版本(亮色下 5:1 以上),所以浅底上的小字也看得清。

特殊文本样式

搜索命中显示为 高亮;降价时旧价显示为 ¥199 ¥129; 邮箱 是必填项;提及同事用 @张三; 快捷键 Ctrl + K;行内代码 --zh-color-primary; 查看详情 用链接色并带下划线。

数字色:涨跌色与好坏色

涨跌色跟地区惯例走(顶部「涨跌色」可切换红涨绿跌 / 绿涨红跌);好坏色是固定的语义色。成本上涨是「涨」,但对业务是「坏」,所以两套不能混用。

指标涨跌色(方向)好坏色(业务判断)说明
股价+3.42%不适用行情类只看方向,用涨跌色
营收+12.4%向好上涨且是好事
成本+8.0%不利上涨但是坏事
退款率−2.1%向好下降且是好事
库存−15%关注下降,需要补货
订单数0.0%—无变化用灰色
  • 数字永远带符号(+ 或 −)和方向图标,色弱用户靠图标方向也能读懂
  • 负号用真正的减号「−」(U+2212),不用连字符「-」
  • 变化为零或无数据,用灰色的「—」,不用红绿

数字排版

metric-xl · 一屏只放一个
1,286
metric-lg · 指标卡主数字
4.2TB
metric-md / sm · 次要数字
38.6万
¥ 1,286.00
规则示例
千分位1,234,567
单位缩小为主数字的 45%4.2 TB
缩写:中文用万、亿12.9 万 3.4 亿
缩写:英文用 K、M12.9K 3.4M
百分比小数位固定12.40% 8.00%
空值—
大数字用比例数字,表格列用等宽数字并右对齐大数字 121 表格列见下方「数据表」

断点与容器

整个体系只有 4 个宽度档位加 1 个中控模式,所有组件和模板共用同一套。产品里用 @media 与容器查询实现,本页也用容器查询,所以拖动浏览器窗口,或切换顶部「屏幕」,这一页会实时变化。

档位定义

档位宽度栅格列外边距列间距内容最大宽典型设备
手机< 76841612100%手机竖屏、折叠屏外屏
平板768 – 119982416100%平板、手机横屏、Electron 窄窗口(最小 1024)
桌面1200 – 1919123224100%笔记本、台式机
大屏≥ 19201240241600 居中27 寸及以上、4K 显示器
中控大屏≥ 1920 + 中控模式124832100%展厅、拼接屏、远距离观看

外边距会乘以「密度」系数(紧凑 ×0.8 / 宽松 ×1.2)。触屏设备(手机、平板、pointer: coarse)控件高度不小于 44px,中控 56px。

栅格与布局原语

栅格随档位自动变成 12 / 8 / 4 列。每个格子用 --d、--t、--p 分别声明桌面、平板、手机占几列,没写就用默认值。

响应式栅格 .zh-grid + .zh-col

A · 桌面 8 / 平板 5 / 手机 4
B · 4 / 3 / 4
C · 3 / 4 / 2
D · 3 / 4 / 2
E · 3 / 4 / 2
F · 3 / 4 / 2

自动填充 .zh-auto-grid

不指定列数,卡片最小 220px,能放几个放几个。适合卡片墙、商品列表。

卡片 1
卡片 2
卡片 3
卡片 4
卡片 5
卡片 6

布局原语

.zh-stack 垂直堆叠
1
2
3
.zh-row 水平换行
1
2
3
4
5
.zh-split 两端对齐
标题
操作
.zh-with-aside 主 + 侧栏
主
侧

间距用 --g 传入,取值只能是间距 Token(4 / 8 / 12 / 16 / 24 / 32)。.zh-with-aside 在平板及以下侧栏自动落到主内容下方。

页面模板

6 套常用页面骨架,都由真实组件搭成,并跟随档位变化。切换模板后,再拖动窗口或切换顶部「屏幕」看各档位的样子。

  1. 首页
  2. 内容管理
  3. 文章管理
文章管理
文章列表
标题状态作者阅读量更新时间操作
产品发布公告已发布张三12,4802026-09-28
季度运营复盘待审核李四—2026-09-27
设备巡检规范草稿王五—2026-09-25
共 86 条

组件自适应规则

同一个组件在每个档位怎么变,是规范而不是各写各的。上面的表格、分页、面包屑、弹层、抽屉已经按这张表实现,缩小窗口就能看到。

组件手机 <768平板 768–1199桌面 1200–1919大屏 ≥1920中控大屏
导航底部 Tab(≤5 项,其余进「更多」)图标窄栏 64px,点击展开常驻侧栏 200–240px侧栏,可加右侧信息栏无侧栏,顶部一级导航
表格卡片列表,字段名在左隐藏次要列,可横向滚动完整列,固定首列与操作列同桌面,内容 1600 居中少列大字,不分页,自动轮播
表单单列,标签在上,底部固定操作栏两列两到三列,B 端可标签左置同桌面,限制最大宽度不适用(只读展示)
Modal底部弹层,顶部圆角,安全区留白居中,宽 480–560居中,宽 480–720同桌面居中大字确认
Drawer全屏,从右滑入宽 380–480宽 420–640同桌面不用,改分屏
Tabs横向滚动,不折行完整展示完整展示完整展示大号,自动轮播
分页「上一页 1 / 9 下一页」页码 ≤5 个页码 + 跳转 + 每页条数同桌面无分页
面包屑只留最近两级完整完整完整隐藏
按钮组主按钮通栏,次按钮并列行内行内行内仅保留必要操作
控件高度≥ 44px触屏 ≥ 44px按密度 28–40px同桌面≥ 56px
字号不缩小基准,大标题流式缩放同手机基准(B 14 / C 16)基准×1.4,高对比
悬停提示改为点击或长按触屏同手机,鼠标保留悬停悬停 + 聚焦同桌面不依赖悬停,信息常显
安全区顶部与底部用 env(safe-area-inset-*)同手机无无边缘留 3% 防过扫

通用组件

22 个第一版组件中的核心部分。所有组件默认带语义标签和键盘可达性。

Button

Tag / Badge

默认品牌成功 警告失败提示 8

表单

编号格式应为 ZH-0001

Tabs · Breadcrumb

Table · Pagination

文章列表
标题状态作者更新时间操作
产品发布公告已发布张三2026-09-28
季度运营复盘待审核李四2026-09-27
设备巡检规范草稿王五2026-09-25
共 86 条

窄于 768 时:表格变为卡片列表(字段名在左),分页精简为「1 / 9」,面包屑只保留最近两级。

反馈:Modal · Drawer · Message · Tooltip · Alert · Empty · Loading

系统将在今晚 23:00 进行维护。
暂无数据

数据展示

统计类页面先问「读者要做什么」再选形式。单个数字用指标卡,不要画一根柱子;重点只有一个时强调它、其余置灰,不给每个系列一个颜色。图表全部支持悬停和键盘方向键查看数值,并附数据表。

怎么选形式

读者要做什么用不用颜色
看一个当前值指标卡(数字 + 变化 + 迷你趋势)只有一根柱的柱状图数字用文字色,变化用数字色
看一个比例对上限进度条、环形进度只有两块的饼图品牌色,超限变警告/危险
看趋势折线;单系列可加面积双纵轴(一律禁止)重点用品牌色,对比期置灰
比大小柱状 / 横向条形榜按数值深浅给类目上色单色;只强调重点那一根
看占比(≤6 项)环形图 + 带数值的图例比较接近的两个值固定 8 色分类色板,按顺序取
看转化漏斗(有序色阶)多种颜色的漏斗品牌色阶,同色由浅到深
看时间 × 类目的分布热力图超过 7 档的分类色品牌色阶,暗色下反向
核对明细数据表把所有数字画进图里数字右对齐,变化用数字色

指标卡

本月营收 · 主指标(一屏只放一个)
128.6万
+12.4% 较上月
订单数
3,426
+6.1% 较上周
访问量
48.2万
目标完成率
72%
目标 178.6 万
本期 vs 上期
本期128.6
上期114.4

图表

时间范围(作用于折线图)

访问量趋势

查看数据表

渠道占比

月度营收(万)

热销商品 Top 5

  1. 1
    智能中控屏 Pro
    2,860
  2. 2
    边缘网关 X1
    2,231
  3. 3
    环境传感器套装
    1,745
  4. 4
    电源管理模块
    1,260
  5. 5
    扩展线缆包
    832

注册转化漏斗

资源用量

存储62%
CPU 82%
磁盘 94%

进度条填充色表示严重程度:品牌色 → 警告 → 危险;未填充的轨道是同色系的浅色。

每周活跃时段

数据表

各区域销售情况
区域销售额(万)环比占比达成率
华东386.2+12.4%36.4% 108%
华南254.8+6.1%24.0% 101%
华北198.5−3.2%18.7% 92%
西南142.0+18.9%13.4% 104%
东北78.3−8.7%7.4% 71%
合计1,059.8+7.3%100%—

销售额列右对齐并使用等宽数字;「环比」用涨跌色表示方向,「达成率」用好坏色表示业务判断,两者同时出现也不会混淆。

B 端与 C 端的展示差异

B · 运营看板:信息密度高,一屏看全

订单
3,426
6.1%
GMV
128.6万
12.4%
退款率
1.8%
2.1%
客单价
375
0.0%
渠道订单转化率
自然搜索1,4384.2%
直接访问9595.8%
社交媒体6172.9%

C · 个人月报:大数字、圆角、有温度

本月运动 连续 6 天
21.6km
目标 30 km

比上月多跑了 3.2 km,继续保持!

中控大屏的展示方式见「页面模板 · 中控大屏」:特大数字、少量图形、不依赖悬停。

数据状态

加载中…(刷新时保留旧数据并降低透明度,避免跳动)
暂无数据试试调整时间范围或筛选条件
加载失败
数据延迟
3,426
更新于 14:02,已延迟 12 分钟

图标

统一使用开源图标库 Lucide(ISC 许可,可商用,无需署名):24px 网格,圆角线性风格,约 1,700 个图标,Vue 有官方包 lucide-vue-next,按需引入不增加体积。禁止用 emoji 和字符(▲ ✓ ☰)当图标,禁止混用多套图标库。

规格

项规范
风格线性、圆头圆角;描边 B 端 1.75、C 端 2
网格24×24,主体在 20×20 内,四周留 2px
尺寸16(表格、文字旁)· 20(按钮、输入框内)· 24(导航、卡片)· 32(空状态、大按钮)· 64(空状态插图)
颜色currentColor 跟随文字色,不写死;状态图标用文字色令牌
对齐大小 1.15em 并向下偏移 0.2em,与文字基线对齐;图标与文字间距 6–8px
可访问性装饰图标 aria-hidden;纯图标按钮必须有 aria-label;状态图标旁必须有文字
自绘图标先查 Lucide 有没有;确需自绘,必须遵守同一网格、描边和圆角
加载方式页面用 SVG 精灵图(一次请求、可缓存);Vue 用 <zh-icon name="house" /> 按需引入;H5 与 Electron 同 Web
备选库Lucide 缺图标时可评估 Tabler Icons(MIT,5000+ 个,风格接近);不混用

尺寸

16
20
24
32

图标 + 文字

 已完成

常用图标 点击复制名称 ·

<script setup>
import { House, Settings } from 'lucide-vue-next'
</script>

<zh-icon :icon="House" :size="20" />          <!-- 装饰性:自动 aria-hidden -->
<zh-button icon aria-label="设置"><zh-icon :icon="Settings" /></zh-button>

应用骨架

同一套导航,随屏幕档位变形:桌面常驻侧栏(≥1200),平板收成图标栏(768–1199),手机变底部 Tab(<768)。用顶部「屏幕」开关切换查看。

  1. 首页
  2. 概览

在线设备

128

今日发布

36

待审核

7

移动组件(C 端 H5)

NavBar、TabBar、ActionSheet、BottomSheet、PullRefresh。触控热区不小于 44px,底部预留安全区。此处按 C 端标准展示,仍跟随品牌色、亮暗模式。

下拉刷新

本月消费

¥ 1,286.00
  • 订单 #20260928已完成
  • 订单 #20260921配送中
  • 订单 #20260915已取消

权限呈现

视觉语言只规定「没有权限时长什么样」:整块内容隐藏、操作禁用并说明原因、字段只读。切换下方角色查看。

当前角色

1 隐藏

用于整个模块或菜单。

2 禁用 + 原因

用于可见但不可执行的操作。

3 只读

用于可见不可编辑的字段。

弹出层

Tooltip、Popover、Popconfirm 共用同一个弹出引擎:贴边自动翻转和避让,点击外部或按 Esc 关闭,在模态框里打开时会挂在模态框内部,不会被遮住。

选哪一个

组件触发内容能否操作内容典型场景
Tooltip悬停或键盘聚焦一句话,可带快捷键否图标按钮说明、被截断的文字、按钮禁用的原因
Popover点击(也可悬停)标题 + 正文 + 操作是快速查看、轻量表单、用户名片
Popconfirm点击一句确认 + 两个按钮是删除、下线等轻量二次确认;不可逆的大操作改用 Modal

Tooltip

项规范
触发鼠标悬停或键盘聚焦都会出现;移开、失焦或按 Esc 消失;触屏设备不依赖它传达关键信息
延迟默认悬停 80ms 后出现、120ms 后消失;说明文字较长的场景可拉长到 400ms,避免鼠标划过时闪烁
位置12 个方位:上下左右各 3 个(居中、start、end);空间不够时自动翻到对面,并贴边避让
内容一句话,不超过 260px 宽;需要标题或快捷键时使用富内容,仍然不可操作
无障碍role="tooltip",触发元素通过 aria-describedby 关联;纯图标按钮本身必须有 aria-label,此时 Tooltip 只作视觉补充
禁用按钮禁用的按钮收不到鼠标事件,外面包一层可聚焦的容器来挂 Tooltip,并说明禁用原因

12 个方位(悬停或聚焦每个按钮)

纯图标按钮(自带 aria-label,Tooltip 仅作视觉补充)

富内容(标题 + 说明 + 快捷键)

禁用按钮:用可聚焦的外层说明原因

较长延迟(500ms),划过不会闪

Popover

项规范
触发默认点击;名片类内容可用悬停。点击触发时,再点触发按钮、点击外部或按 Esc 关闭,关闭后焦点回到触发按钮
结构标题(可省略)+ 正文 + 底部操作,宽度 280px,最宽不超过屏幕减 32px
无障碍role="dialog",触发按钮带 aria-expanded、aria-controls;含表单时打开后焦点移入

Popconfirm

项规范
什么时候用可以撤销或影响范围小的操作。影响很大或不可逆时用 Modal,并写清后果
按钮取消在左、确认在右;危险操作的确认按钮用危险色,按钮文字写动作(「删除」「下线」),不写「确定」
异步确认后按钮进入加载态并禁用,请求结束再关闭;期间不能通过 Esc 或点击外部误关
键盘打开后焦点在「取消」上,Tab 在两个按钮间循环,Enter 执行当前按钮,Esc 关闭并把焦点还给触发按钮

还没有操作

模态与通知

四种打断程度:Modal 必须处理才能继续,Drawer 在不离开当前页面的前提下处理一件事,Notification 是需要留意的消息,Message 是一闪而过的操作反馈。

选哪一个

组件打断程度会自动消失适合
Modal阻断,背景不可操作否必须确认的决定、聚焦的短表单
Drawer半阻断,可点遮罩关闭否详情、编辑、多字段表单、筛选面板
Notification不阻断,出现在角落默认 5 秒,悬停暂停系统消息、后台任务完成、需要点击处理的通知
Message不阻断,顶部居中默认 2.6 秒「保存成功」「已复制」这类操作反馈

Modal

尺寸宽度用在哪里
sm360px确认对话框:一句话 + 两个按钮
md(默认)520px短表单、设置项
lg720px较复杂的内容,例如选择器或预览
full整屏减 32px编辑器、长流程
  • 手机上自动变成底部弹层(顶部圆角、带拖拽条、底部留出安全区),全屏尺寸铺满整个屏幕
  • 打开后焦点移入并被限制在对话框内,关闭后回到触发按钮;Esc 与点击遮罩是否关闭可单独配置,不允许时对话框会轻微抖动提示
  • 确认类对话框用 role="alertdialog",标题和描述通过 aria-labelledby、aria-describedby 关联
  • 处理中不允许通过 Esc、遮罩或关闭按钮中途退出,按钮进入加载态

尺寸

确认对话框

场景

可关闭性

Drawer

项规范
方向右侧(默认,详情与编辑)、左侧(导航与筛选)、顶部与底部(批量操作、简短说明)
尺寸左右:sm 320 · md 420 · lg 640 · xl 880;上下:sm 30% · md 45% · lg 70% 屏高;手机上左右抽屉铺满宽度
结构标题栏(固定)+ 内容(独立滚动)+ 底部操作栏(固定)
行为可点击遮罩关闭;支持嵌套,后打开的在上层;焦点被限制在抽屉内,关闭后回到触发按钮

方向

尺寸与场景

Notification

项规范
位置右上(默认)、左上、右下、左下;手机上上方的左右合并为顶部通栏,下方为底部通栏
结构类型图标 + 标题 + 描述(可选)+ 操作按钮(可选)+ 关闭按钮;右侧带倒计时进度条
时长默认 5 秒;鼠标悬停或键盘聚焦时暂停;需要用户处理的通知设为常驻(不自动消失)
数量同一位置最多同时 4 条,超出时最早的一条被挤掉
无障碍成功和提示为 role="status",警告和错误为 role="alert",会被读屏软件朗读;关闭按钮有 aria-label

四个角落

类型

场景

Message

项规范
类型提示、成功、警告、失败、加载中;加载中的消息可以原地变成成功或失败
时长默认 2.6 秒;加载中不自动消失;需要用户看清的可以加关闭按钮并延长
数量最多同时 4 条,超出时挤掉最早的
文案说结果,不超过一行:「已保存」「复制失败,请重试」

预览

图片的加载与失败状态、点击放大的图片查看器、轮播。所有图片都是页面里现场生成的矢量图,不依赖网络。

图片

状态表现
加载中骨架闪烁,并按宽高比预留位置,图片出来时版面不跳动
加载完成淡入 200ms
加载失败图标 + 「图片加载失败」+ 重试按钮;alt 文字保留,读屏软件仍能读到
暂无图片品牌浅色底 + 图标,用于还没上传的位置
适配方式cover 裁切铺满(默认)· contain 完整显示 · fill 拉伸(慎用,会变形)
圆角sm / md / lg / full,跟随 B 端与 C 端预设;头像和圆形封面用 full
无障碍内容图必须有 alt;纯装饰图 alt="";加载中容器带 aria-busy
晨雾中的山脉插画
暂无图片
已加载
深海渐变插画
暂无图片
加载中(约 2 秒)
蔷薇色渐变插画
暂无图片
加载失败,可重试
暂无图片
暂无图片

适配方式(同一张 16:9 图放进方框)

裁切铺满示例
cover
完整显示示例
contain
拉伸示例
fill

圆角

小圆角
sm
中圆角
md
大圆角
lg
圆形
full

图片查看器

操作鼠标 / 触屏键盘
放大 / 缩小工具栏按钮 · 滚轮(以指针位置为中心)· 双击 · 双指捏合+ -
复位点击工具栏中的缩放比例0
拖动放大后按住拖动—
旋转 / 翻转工具栏按钮R 顺时针 · Shift+R 逆时针 · H 水平 · V 垂直
切换图片两侧箭头 · 缩略图 · 触屏左右滑动← →
关闭关闭按钮 · 点击图片外的空白Esc
下载 / 全屏工具栏按钮(浏览器不支持全屏时该按钮不显示)—
  • 打开后焦点进入查看器并被限制在里面,关闭后回到点击的缩略图;背景页面不能滚动
  • 读屏软件会朗读「第 3 张,共 8 张:标题」;到第一张或最后一张时对应箭头禁用
  • 缩放到 100% 之外时,才允许拖动;100% 时横向拖动是「切换上一张 / 下一张」

点击任意缩略图打开。

轮播

项规范
效果滑动(默认,方向感强)· 渐变(图片内容差异大、不想有位移时)
自动播放默认 4 秒一张;鼠标悬停、键盘聚焦、拖动、标签页在后台时暂停;必须提供暂停按钮;用户开启了「减少动态效果」时不自动播放
导航左右箭头、圆点(当前项拉长成胶囊);可循环;不循环时到头的箭头禁用
手势与键盘触屏与鼠标左右拖动切换;聚焦后 ← → 切换,Home End 跳到首尾
无障碍role="region" + aria-roledescription="carousel";每张 role="group" + aria-roledescription="slide",标签为「第 n 张,共 m 张」;圆点是 tablist;自动播放时 aria-live="off",停止后改为 polite

滑动效果 · 自动播放 · 循环

渐变效果 · 手动切换 · 不循环

日期与时间

日期选择由「输入框 + 日历浮层」组成:输入框支持直接键入,日历支持全键盘操作。日历一律从周一开始,中文月份与星期,不可选的日期用灰色并说明原因,不会隐藏。

日期选择 Date picker

可直接输入,如 2026-10-01 或 2026/10/1
不可选的日期悬停或输入时给出原因
超出范围的日期置灰

日期范围 Range picker

先点开始日,移动鼠标预览范围,再点结束日;先点了较晚的日期会自动对调。桌面并排显示两个月,手机显示一个月,快捷项变成横向滚动的标签。

—

时间选择 Time picker

三列滚轮:小时、分钟、秒。滚动停下、点击或按 ↑ ↓ 都会选中;← → 在列间切换。

日期时间 Date-time

选好日期和时间后点「确定」

内嵌日历、日程圆点与热度

圆点表示当天有几类日程(最多 3 类);热度用品牌色阶表示当天日程多少,适合看「哪几天忙」。

—
    按键(焦点在日历内)行为
    ← → ↑ ↓前一天 / 后一天 / 上一周 / 下一周;跨月时自动翻页
    Home / End本周周一 / 周日
    PageUp / PageDown上个月 / 下个月;加 Shift 为上一年 / 下一年
    Enter / Space选中当前日期(不可选日期无效)
    Esc关闭日历,焦点回到输入框且不改变已选值
    输入框内 ↓打开日历,焦点进入当前日期

    滑块

    在一段连续或离散的范围里选值。需要精确值时,旁边配一个数字输入框;只关心大致档位时,用刻度标记。拖动、点击轨道、键盘方向键三种方式都能操作。

    基础与步进

    基础 · 悬停或聚焦时显示数值
    步进 + 刻度 · 只能停在档位上
    数值联动 · 拖动与输入互相同步
    按值变色 · 颜色之外还有文字状态

    范围(双滑块)

    价格区间 · 两端最小间隔 200
    至
    时间段 · 带刻度

    垂直、尺寸与禁用

    垂直 · 均衡器
    低音
    中音
    高音
    尺寸 · sm / md / lg
    禁用

    规范

    项规则
    键盘方向键 ±步长;PageUp / PageDown ±10%;Home / End 跳到两端。双滑块的两个把手可分别聚焦,且不会越过对方
    命中区整条滑块高度都可点击;把手最小 14px,触屏 24px 以上;点击轨道会把最近的把手移过去
    数值显示拖动、聚焦、悬停时在把手上方显示数值;需要精确值时配数字输入框,两者双向同步
    颜色变色只表示严重程度,同时必须有文字状态(正常 / 偏高 / 危险),不靠颜色单独表达
    无障碍每个把手是 role="slider",带 aria-valuemin / max / now / valuetext 与 aria-orientation;禁用时 aria-disabled
    RTL根据文字方向自动镜像,方向键含义同步互换

    评分

    让用户给一个 1 到 N 的等级,或展示一个平均分。互动评分用单选组(方向键切换、Backspace 清除);纯展示的分数只读,可以带小数。

    互动评分

    基础 · 悬停预览,点击确认,文字随分数变化
    半星 · 点星星左半边或右半边
    允许清除 · 再点一次当前分数,或按 Backspace
    其他图标与数量 · 心形 / 10 分制

    展示、尺寸与禁用

    只读 · 支持小数
    尺寸 · sm / md / lg
    禁用

    评价汇总

    分布条只用一种品牌色,长短即数量;不给每个星级配一种颜色(图表规范:单系列一色)。百分比在右侧,具体条数在悬停提示和无障碍文本里。

    规范

    项规则
    交互悬停预览、点击确认;半星时左右半边各算一档;再点当前分数可清除(需开启)
    键盘单选组:方向键切换,Backspace / Delete 清除;焦点环画在被聚焦的那颗星上
    无障碍互动评分是原生单选组,每个选项标注「N 星 · 文案」;只读评分是 role="img",读出「4.3 分,满分 5 分」
    颜色选中为琥珀色(心形为红色),未选中为中性描边;满分不要靠颜色判断好坏,配文案
    展示分数小数按比例填充:4.3 分是 4 颗满星 + 第 5 颗填 30%

    输入框

    所有文字类输入共用一套外观:一个边框、一圈焦点环、统一的高度。前后缀、清除按钮、密码开关都放在同一个框里,不各画各的边。标签永远在框外、在框上方。

    状态

    提示文字放在框下方
    编号格式应为 ZH-0001
    该用户名已被使用较多,建议更换
    邮箱可用
    可选中、可复制,不可编辑

    前后缀、清除与密码

    https://.example.com
    尚未搜索

    数字步进

    按住按钮连续增减;↑↓ 步进,Shift 或 PageUp/Down ×10
    输入越界会在失焦时修正到范围内
    %

    验证码

    已发送到 138****8000,可直接粘贴

    多行文本

    标签、提示与校验

    用于接收登录验证与通知
    行内编辑 · 点击、Enter 或 F2 进入;Enter 保存,Esc 取消
     

    尺寸

    规范

    项规则
    结构标签在框上方;提示与错误在框下方;前后缀、清除、密码开关都在同一个边框里,整体一圈焦点环
    错误红框 + 图标 + 文字,三者同时出现;aria-invalid="true",错误文字通过 aria-describedby 关联到输入框
    清除按钮有内容才出现;点击后焦点回到输入框;键盘可 Tab 到达,读作「清除」
    数字步进±步长按钮仅作鼠标辅助(不进 Tab 序),键盘用 ↑↓;输入越界或非数字,在失焦时修正,不在输入中途打断
    验证码每格 1 位,自动跳格;Backspace 回退;整段粘贴自动拆分;第一格带 autocomplete="one-time-code"
    多行文本自动增高但有上限;字数计数不参与朗读,超限时另用播报区提示,避免逐字打断
    触控手机上高度不低于 44px;前置选择和后置按钮在窄屏保持同一行,输入框自动收缩

    上传

    选择本地文件、把它们放进列表、看到进度与结果。原则:选中即校验(类型、大小、数量在上传前就说清楚),过程可控(可取消、可重试、可移除),结果可读(成功、失败都有文字,不只靠颜色)。以下演示均为本地模拟,不发起任何网络请求;文件名含 fail 的文件第一次会失败,点重试后成功。

    拖拽上传 + 文件列表

    拖拽文件到此处,或 点击选择
    PDF、图片、Office 文档、ZIP、MP4 · 单个 ≤ 5 MB · 最多 5 个 · 聚焦后可 Ctrl+V 粘贴图片
      已添加 0 / 5 个

      图片墙

      已添加 0 / 6 张 · 悬停或聚焦图片可预览、删除

      头像上传

      个人头像
      JPG / PNG · ≤ 2 MB · 建议正方形
      文件夹上传 · 一次选一个目录
      尚未选择文件夹。选择后显示文件数量、总大小和前几项路径。

      禁用

      上传功能不可用
      当前账号没有上传权限,请联系管理员

      规范

      项规则
      入口拖拽、点击、键盘(Enter / Space)、粘贴四种方式都要能用;拖入时区域高亮并加深边框
      校验类型、大小、数量在上传前判断,被拒的文件逐条说明原因;通过校验的文件照常上传,不因个别失败而整批取消
      进度进度条 + 百分比 + 速度 + 剩余时间;最多同时上传 3 个,其余显示「等待上传」
      失败红色图标 + 文字「上传失败」+ 重试按钮;重试成功后回到正常状态
      预览图片在弹层中预览,Esc 或点击遮罩关闭,焦点回到触发按钮;非图片显示类型图标
      内存缩略图使用临时地址,移除文件或离开页面时立即释放
      无障碍拖拽区是 role="button";状态变化由播报区朗读(完成、失败、取消);进度条是 role="progressbar";所有文件名经过转义
      触屏图片墙的操作按钮在触屏上常显,不依赖悬停;手机上拖拽区改为整块点击

      徽章、标签与头像

      三个小组件解决三件事:徽章表示「有多少、有没有」,标签表示「属于什么、是什么状态」,头像表示「是谁」。颜色永远不单独表达意思:数字、文字或图标必须同时出现。

      徽章 Badge

      类型用在哪里规则
      数字未读消息、待办数量超过上限显示「99+」;为 0 时默认隐藏,需要强调「已清零」时才显示 0
      红点有更新但数量不重要只表示「有」,不带数字;尺寸为数字徽章的一半
      文字新功能、推荐不超过 3 个字符,大写;同一屏最多 2 个
      状态点在线、处理中、异常圆点 + 文字标签;「处理中」用脉冲,其余静态

      交互试验

      数字变化时徽章会弹一下(约 320ms),减少动态效果时自动关闭。

      样式一览

      count
      99+
      dot
      text
      text
      top-left
      bottom-right
      bottom-left
      size sm
      size md
      size lg
      on button
      on icon
      on avatar
      standalone

      附着在菜单与标签页上

      点击「未读」标签页,未读徽章会清零并收起。

      状态点

      未开始 处理中 已完成 待确认 失败 提示

      标签 Tag

      形态用在哪里规则
      柔和(默认)状态、分类低对比底色 + 语义文字色,最常用
      实心需要强调的少数标签同一屏最多 1–2 个,文字用反色令牌
      描边次要属性、筛选条件透明底 + 同色描边
      可关闭 / 可选中已选条件、多选筛选关闭按钮有独立的可访问名称;可选中是复选框或单选组,可用键盘操作
      类别色标记多类别数据圆点承载类别色,文字保持文字令牌颜色
      柔和品牌成功警告失败 实心品牌成功警告失败提示
      描边品牌成功警告失败提示 小 sm中 md大 lg 带图标需关注禁用

      可关闭与新增

      可选中:单选与多选

      单选 · role=radiogroup · 方向键切换

      多选 · role=checkbox · 空格切换

      类别色标记

      圆点取 8 色分类色板,按顺序使用;文字始终是文字令牌色,保证对比度。

      头像 Avatar

      规则说明
      尺寸xs 24 · sm 32 · md 40 · lg 56 · xl 72;圆形用于人,方形用于组织和应用
      回退顺序图片 → 姓名首字 → 通用人像图标;图片加载失败自动回退
      首字中文取姓氏首字,英文取前两个单词的首字母
      颜色由姓名哈希从 8 色分类色板中固定取一个,同一个人在任何地方颜色一致;底色只占 20%,文字保持文字令牌色
      xs
      sm
      md
      lg
      xl
      square
      image
      broken → 首字
      icon
      status
      status + badge

      头像组

      重叠 + 悬停抬起
      小尺寸

      超过上限的成员折叠为「+N」按钮,点击展开成员列表(Esc 关闭,焦点回到按钮)。

      面板与卡片

      面板把相关内容装进一个有边界的容器:卡片承载独立的一块信息,折叠面板收纳次要内容,描述列表展示「字段:值」,分栏面板并排浏览,结果页告诉用户一件事办成了没有。

      卡片 Card

      变体用在哪里规则
      基础 / 头部 + 操作 / 底部操作信息块、设置块、表单块标题 15px 半粗;头部操作最多 2 个;底部操作右对齐,主操作在最右
      封面文章、项目、商品封面 16:9;图片缺失时用品牌色阶生成的插图,不留空白
      可悬停整块可点击的入口整张卡片是一个链接;悬停上浮 3px,聚焦显示焦点环
      可选择套餐、模板、方案单选用 radio、多选用 checkbox;选中有描边与对勾,不只靠颜色
      加载中 / 统计看板、首页加载时保持卡片尺寸,用骨架屏占位并标记 aria-busy

      基础

      基础卡片

      容器负责边界与留白,内容由业务决定。

      品牌默认

      头部 + 操作

      本月概览

      订单 3,426,同比增长 6.1%。

      底部操作

      发布确认

      确认后文章将立即对外可见。

      封面

      设计语言 2.0

      新的色彩、字体与动效规范。

      已发布12 分钟阅读

      统计

      活跃用户
      12.4万
      +8.2%较上周

      可选择 · role=radiogroup · 方向键切换

      加载中

      折叠面板 Collapse

      规则说明
      模式手风琴(同时只展开一项)用于互斥内容;多开用于独立的补充信息
      键盘Enter / 空格展开收起;上下方向键在标题间移动,Home / End 跳到首尾
      动效高度用网格行 0fr → 1fr 过渡(约 200ms);收起后内容不可聚焦

      手风琴 · 描边

      设计语言是一组共享的视觉与交互规则,让不同产品在不同平台上依然像同一个家族。

      提供一个种子色,系统会生成 10 级色阶,并保证按钮文字的对比度。

      需要管理员权限。

      多开 · 无边框 · 可嵌套

      主题、密度与圆角。

      亮色、暗色、跟随系统。

      紧凑、标准、宽松。

      邮件、站内信与推送。

      数据导出与账号注销。

      描述列表 Descriptions

      形态规则
      描边标签列带底色,适合详情页的主信息;桌面 3 列,平板 2 列,手机 1 列
      无边框标签在上、值在下,适合卡片内的次要信息;值可放标签、链接、头像
      订单号
      20260929-0086
      状态
      已发货
      金额
      ¥ 1,286.00
      收货人
      张三
      电话
      138 **** 0086
      地址
      上海市浦东新区世纪大道 100 号 · 中控大厦 18 层
      负责人
      李四
      标签
      设计 研发
      创建时间
      2026-09-01 09:30

      结果页 Result

      分栏面板 Split

      文档
        概览

        拖动分隔条,或聚焦后用 ← / → 调整(Shift 步长更大,Home / End 到最小 / 最大),双击恢复默认。手机上上下堆叠。

        设置页面板

        左侧说明这一块是什么,右侧是可操作的卡片。有未保存的更改时,底部浮出保存栏。

        通知

        选择你希望接收提醒的方式。

        邮件通知每天汇总一封
        站内信有新消息时提示
        营销推送产品动态与活动

        个人资料

        这些信息会显示在你的主页上。

        显示名称最多 20 个字
        语言界面显示语言

        危险操作

        这些操作无法撤销。

        注销账号所有数据将被永久删除

        进度

        进度回答「还要多久」。能算出百分比就用确定进度(线性或环形),算不出来才用不确定进度。数值变化只用位移和描边偏移做动画,不会触发布局。

        形态用在哪里规则
        线性上传、导入、任务进度、资源用量高度 4 / 8 / 12px;百分比放在右侧或上方,行内文字只用于 ≥18px 的粗条
        环形 / 仪表目标完成率、健康分、使用率中心放结论数字;仪表盘 270°,半环 180°,两端标 0 与 100
        分段 / 堆叠步骤数、构成占比堆叠必须有带数值的图例;颜色取 8 色分类色板,按顺序使用
        不确定无法预估时长循环滑动条,并标记 aria-busy,不显示百分比
        状态完成、失败、警告颜色之外必须有图标或文字:完成显示对勾,失败显示叉;满 100% 自动变为完成

        交互试验

        状态
        尺寸
        标签

        线性进度

        尺寸 sm / md / lg

        标签:上方 / 内部 / 无

        状态:正常 / 成功 / 异常 / 警告

        动态:流光 / 条纹 / 不确定

        分段:10 段 / 5 段

        缓冲 + 目标线

        堆叠:构成占比(图例必须带数值)

        环形与仪表

        sm
        md
        lg + caption
        success
        exception
        warning
        gauge 270°
        semi 180°
        concentric

        表格中的迷你进度

        任务进度
        任务负责人进度
        设计规范张三
        组件库李四
        文档站王五
        发布检查赵六

        倒计时环

        剩余 3 秒时变为警告色,结束时变为红色。

        上传队列:暂停 / 继续 / 重试

          时间线与步骤

          时间线回放「发生了什么」,步骤条引导用户完成「接下来做什么」。两者都用有序列表 <ol> 表达先后;颜色只表达状态,永远配图标或文字;动效只用 transform 与 opacity。

          时间线布局

          left · 内容在右(默认)
            right · 内容在左,文字右对齐
              alternate · 左右交替,时间在对侧;手机上自动收成 left

                圆点类型与状态色

                solid / hollow / icon / large
                  带附件与展开详情

                    分组、紧凑与动态流

                    按日期分组
                      compact · 高密度
                        活动流 · 头像

                          待处理项与实时追加

                          最后一项是「进行中」:圆点为旋转图标。每次推进,它转为已完成并追加下一项。

                            规范

                            项规则
                            语义用 <ol>,时间用 <time datetime>;进行中的项加 aria-live 所在容器,变化时读出
                            状态色默认灰、品牌、成功、警告、危险;圆点旁必须有标题文字,警告与危险再配图标
                            时间位置left / right:时间跟在标题后;alternate:时间放在轴的另一侧;手机 <768 一律收成 left
                            展开详情按钮带 aria-expanded / aria-controls;折叠内容不可聚焦;高度动画只用 grid 行高过渡
                            间距项间距 20px × 密度系数;compact 为 10px,并隐藏描述以外的次要内容
                            数量超过 20 项时分组或改为「加载更多」,不要一次画出全部

                            步骤条 Steps

                            horizontal · 数字;窄于 768 自动变纵向
                              icon · 图标
                                error · 出错的步骤
                                  small · 24px
                                    dot · 极简
                                      vertical · 纵向

                                        连线进度动画

                                          连线用 scaleX / scaleY 填充,不动画宽高,也不触发布局。

                                          可点击 + 校验拦截

                                            规范

                                            项规则
                                            状态wait 灰色空心 · process 品牌色实心 · finish 浅色底加对勾 · error 危险色加 ×,标题同时变色
                                            语义<ol>;当前步骤 aria-current="step",整条只能有一个
                                            可点击只能回到已完成的步骤;越过当前步骤必须走「下一步」并通过校验;禁用的步骤加 disabled
                                            尺寸lg 40 · md 32(默认)· sm 24 · dot 12;标题永远与图标垂直居中
                                            响应式<768 横向自动变纵向,连线从水平改为竖直
                                            数量3–6 步最佳;超过 6 步分组,不要把步骤条拉成两行

                                            树 Tree

                                            树用来浏览和操作有层级的数据:组织架构、文件目录、分类、权限。遵循 WAI-ARIA tree 模式,整棵树只有一个 Tab 停靠点,其余用方向键移动。数据是一份受控模型,勾选、选择、展开、移动都通过事件抛出。

                                            勾选与选择

                                            已勾选:无

                                            事件日志 · 受控模型
                                            1. 操作左侧的树,这里会记录事件

                                            搜索过滤

                                            命中的名称高亮,祖先节点自动展开;清空后恢复原来的展开状态。

                                            懒加载与错误重试

                                            展开时才请求子节点 · PostgreSQL 第一次会失败
                                            节点操作 · 拖拽排序

                                            悬停出现「添加 / 重命名 / 删除」,双击或 F2 重命名;拖动改变位置(上 1/4 放到前面,下 1/4 放到后面,中间放进去);键盘用 Alt + ↑ ↓ 调整顺序。

                                            目录树

                                            文件类型图标 + 缩进参考线;点击文件夹同时展开

                                            已选择:无

                                            TreeSelect · 下拉里的树

                                            大数据:5,000+ 节点

                                            只渲染可视区附近的行,滚动时按位置换行;aria-setsize / aria-posinset 仍按完整数据计算,读屏软件读到的是真实位置。

                                            规范

                                            项规则
                                            语义role="tree" 下是 treeitem;每项带 aria-level / aria-setsize / aria-posinset;有子节点的带 aria-expanded;勾选树用 aria-checked(含 mixed)
                                            焦点漫游 tabindex:整棵树只有一个 Tab 停靠点;行内的操作按钮不进入 Tab 序列,键盘用快捷键代替
                                            键盘↑ ↓ 移动 · → 展开 / 进入子级 · ← 折叠 / 回到父级 · Home End · * 展开同级 · 输入字母跳转 · Enter 选择 · Space 勾选 · F2 重命名 · Delete 删除 · Alt+↑↓ 调整顺序
                                            勾选父子联动:全选为选中,部分为半选,全不选为未选;「父子不联动」时各自独立;禁用的节点不参与联动
                                            缩进每级 20px;有参考线时线只画在缩进区,不穿过文字
                                            动效展开时子节点依次淡入并下移 6px(间隔 30ms),折叠时整体淡出;只动 transform 与 opacity;「减少动态效果」下无动画
                                            状态悬停浅灰底 · 选中品牌浅底加品牌文字 · 焦点内描边 · 禁用降低对比并跳过联动 · 加载中显示旋转图标 · 失败显示原因与「重试」
                                            数量可见行超过 80 自动开启窗口化;一次性展示超过 5,000 行前先考虑搜索或分级加载

                                            流式加载

                                            内容不是一次到齐,而是边到边显示:分页追加、逐字输出、实时日志。核心原则三条:不让页面跳(追加不改变已有内容的位置)、不打扰用户(用户往上翻时不强行滚到底)、失败可恢复(保留已有内容,就地重试)。

                                            无限滚动

                                              状态
                                              0条

                                              • 距底部 120px 时提前加载,用户看不到空白
                                              • 加载中用与内容同高的骨架行占位,追加后位置不变
                                              • 失败保留已加载内容,只在末尾显示「重试」
                                              • 到底后停止请求并明确告知

                                              「加载更多」按钮

                                                什么时候用按钮而不是无限滚动
                                                • 页面底部还有页脚、表单或需要被触达的内容
                                                • 用户需要回到某一条内容(无限滚动会丢位置)
                                                • 键盘和读屏用户:按钮是可到达的,滚动触发不是
                                                • 加载完成后焦点移到第一条新内容,并读出「已加载 N 条」

                                                长列表:content-visibility

                                                  不需要虚拟滚动库:让浏览器跳过屏幕外行的布局与绘制,条数在几千以内足够。Safari 18 之前不支持,会退化为普通渲染,功能不受影响。上万行请分页或换成窗口化渲染(参见「树 · 大数据」)。

                                                  AI 流式输出

                                                  设计助手空闲
                                                  给我一份设计系统落地的建议

                                                  点击下方「生成」,体验逐字输出、停止、继续与重新生成。

                                                  每次刷新都把「已收到的全文」重新渲染,所有文本先转义再加标记,未闭合的代码块也安全;正在输出时容器标记 aria-busy,读屏只在开始与结束时播报一次,不逐字朗读。

                                                  实时日志

                                                  最多保留 200 行,更早的自动截断并提示;级别用图标加文字,不只靠颜色;用户向上翻看时新日志继续累计但不打断阅读;页面滚出可视区时自动停止产生日志。

                                                  渐进显示与乐观追加

                                                  骨架 → 内容,逐行错峰淡入
                                                    乐观追加:先显示,再确认

                                                    规范

                                                    项规则
                                                    占位骨架行与真实行同高同结构;数量等于预期条数(不超过 5 行);刷新时保留旧内容并降低透明度,不要闪一下骨架
                                                    触发无限滚动用 IntersectionObserver,提前 120px;同一时刻只允许一个请求在途;到底后断开观察
                                                    滚动追加不改变已有位置;流式内容只在用户位于底部 24px 内时才自动跟随,否则出现「跳到最新」
                                                    错误就地显示原因与「重试」;已收到的内容永远保留;流式中断提供「继续生成」,而不是清空重来
                                                    读屏加载中容器 aria-busy;完成、失败、到底用 role="status" 播报一次;不要给逐字更新的区域加 aria-live
                                                    控制权任何流式输出都必须能「停止」;日志必须能「暂停」;两者都不能因为再次点击丢内容
                                                    动效新内容淡入并上移 8px,多行按 30ms×2 错峰;光标闪烁;「减少动态效果」下全部关闭,光标变为常亮
                                                    数量DOM 里保留的行数设上限(日志 200);几千行用 content-visibility,上万行用窗口化

                                                    分页

                                                    分页解决「数据太多一屏放不下」。已知总数用页码分页;不知道总数或数据在不断增长,用游标分页或「加载更多」。页码控件始终占用固定数量的位置,翻页时不会左右跳动。

                                                    完整分页 + 数据表

                                                    共 86 条演示数据;翻页时会短暂显示骨架屏
                                                    文章列表(分页演示)
                                                    编号标题部门状态更新时间

                                                    模式与尺寸

                                                    标准 · 总数 + 每页条数 + 页码 + 跳转
                                                    简洁 · 只看上一页 / 下一页 / 当前页
                                                    迷你 · 用在卡片、弹层、表格内嵌
                                                    带边框 · B 端表格常用
                                                    禁用
                                                    三档尺寸 sm / md / lg
                                                    只有一页时隐藏

                                                    加载更多与游标分页

                                                    加载更多 · 内容流、评论、订单列表
                                                      游标分页 · 不知道总数,只能前后翻

                                                        规则

                                                        项规范
                                                        页码位置总页数 ≤ 7 全部显示;更多时固定 7 个位置(首页、末页、当前页及左右各 1 页、两个省略号),翻页不抖动
                                                        省略号本身是按钮:悬停或聚焦时变成双箭头,点击向前 / 向后跳 5 页,读屏读作「向前 5 页」
                                                        每页条数10 / 20 / 50 / 100;切换时保持当前第一行仍在页内(第 31 行所在页 → 每页 20 条时跳到第 2 页)
                                                        跳转回车确认;超出范围自动取最近的合法页,输入非数字标红并读出错误
                                                        状态首页禁用「上一页」、末页禁用「下一页」(保留可聚焦,用 aria-disabled);整体禁用用 disabled
                                                        加载翻页时保持表格高度,显示与每页条数相同的骨架行;慢请求返回时丢弃过期结果
                                                        空状态筛选无结果时显示空状态和「清除筛选」,隐藏分页;只有一页时可配置隐藏
                                                        手机 <768页码收成「‹ 3 / 20 ›」,隐藏每页条数与跳转,总数移到上方居中;触控热区 ≥ 44px
                                                        可访问性<nav aria-label>;当前页 aria-current="page";每个页码 aria-label="第 N 页";翻页后用 role="status" 播报「第 N 页,共 M 页」;键盘翻页后焦点留在当前页按钮上

                                                        动画与动态

                                                        动效的目的是解释变化、给出反馈、引导注意,而不是装饰。本规范只给出一套很小的词汇:5 档时长、4 条曲线、一批可复用的关键帧类,并规定了什么时候用、怎么退化。所有取值都来自 Token,B 端偏快偏克制,C 端偏柔和。

                                                        打开后,本节所有示例都会切换成「只保留淡入淡出」的降级版本。

                                                        时长阶梯

                                                        档位Token取值用在哪里
                                                        instant--zh-dur-instant按下反馈、复选框勾选、开关
                                                        fast--zh-dur-fast悬停、聚焦、提示气泡;B 端默认
                                                        base--zh-dur-base下拉、展开、切换、消息;C 端默认
                                                        slow--zh-dur-slow抽屉、模态框、翻转、进度条
                                                        slower--zh-dur-slower大面积转场、强调、成功反馈
                                                        stagger--zh-stagger列表逐项错开,每项延后一个步长

                                                        赛跑:同一起点,看差距

                                                        stagger ×5

                                                        B 端与 C 端的默认节奏

                                                        B 端 · 快而克制

                                                        默认 dur-fast

                                                        C 端 · 柔和有呼吸

                                                        默认 dur-base

                                                        现有组件的默认过渡读取的是 --zh-motion(B 端 120ms,C 端 260ms);新增的动效请直接用 --zh-dur-*,不要写死毫秒。

                                                        缓动曲线

                                                        左图是曲线,右下圆点用同一条曲线移动。

                                                        试验场

                                                        
                                                                  

                                                        规则

                                                        规则具体做法Token / 数值
                                                        进入减速,退出加速元素进入时先快后慢,退出时先慢后快,像自然的物体--zh-ease-enter / --zh-ease-exit
                                                        退出比进入快约 30%用户已经看过它了,别让他等;退出时长取进入的 0.7 倍calc(var(--zh-dur-base) * .7)
                                                        只动 transform 和 opacity它们不触发布局和重绘,低端设备也流畅。唯一例外:折叠面板的高度,用 grid-template-rows不要动 width / height / top / left / margin
                                                        位移要小入场位移 8–16px,大面板不超过 24px;缩放从 0.94–0.98 起步。只有全屏转场才用大位移--zh-anim-dist: 12px
                                                        一次只有一个焦点动画同一时刻不要让多个东西同时抢眼。列表错开进入,每项延后 30ms,最多 8 项--zh-stagger,总长 ≤ 240ms
                                                        不阻塞操作动画期间输入框、按钮仍可点击;常规反馈不超过 500ms;无限循环只用于「加载中」> 500ms 的动画需要有理由
                                                        时长随距离和面积变化小控件短,大面积长:控件 80–120ms,弹层 200ms,抽屉与模态框 320ms,全屏 480ms见上方「时长阶梯」
                                                        尊重「减少动态效果」位移、缩放、旋转全部降级为淡入淡出;无限循环停掉或改成静态.zh-reduce-motion / prefers-reduced-motion

                                                        过渡配方

                                                        淡入淡出

                                                        Aa
                                                        进入 base · enter;退出 ×0.7 · exit

                                                        滑动 · 四个方向

                                                        上滑入
                                                        下滑入
                                                        左滑入
                                                        右滑入
                                                        位移 12px · 淡入同时进行

                                                        缩放进入

                                                        中心
                                                        左上
                                                        scale .94 → 1,弹层从触发点展开

                                                        折叠展开

                                                        折叠内容。高度通过 grid-template-rows 在 0fr 与 1fr 之间过渡,内容自身只做淡入淡出。
                                                        唯一允许动高度的配方

                                                        翻转

                                                        正面
                                                        背面
                                                        rotateY 180°,slow · standard

                                                        共享轴

                                                        第 1 步
                                                        第 2 步
                                                        第 3 步
                                                        同方向的步骤沿同一水平轴移动

                                                        页面切换 · 交叉淡入

                                                        旧页面 ×0.7 淡出,新页面稍后淡入,没有位移

                                                        列表:错开进入 · 离开 · 重排

                                                          进入逐项延后 30ms;离开淡出 + 位移;重排用 FLIP(只用 transform)

                                                          关键帧库

                                                          点击类名复制;循环类默认暂停,点「播放」或把鼠标移上去查看。

                                                          可用变量:--zh-anim-dur(时长)、--zh-anim-delay(延迟)、--zh-anim-dist(滑动距离)。在「减少动态效果」下,位移和缩放会退化成淡入,循环动画会停止。

                                                          微交互

                                                          按压 · 悬浮 · 波纹

                                                          按下 instant;波纹纯 CSS,从中心扩散

                                                          点赞 · 粒子迸发

                                                          128
                                                          图标弹起 + 8 个粒子向外散开

                                                          开关 · 复选框

                                                          输入框 · 焦点线

                                                          标签上浮,底部线条从左展开

                                                          数字滚动

                                                          访问量
                                                          12,480
                                                          转化率
                                                          42.6%
                                                          easeOut 900ms,只更新文字

                                                          进度条填充

                                                          0%
                                                          用 scaleX 而不是 width

                                                          滚动显现 · 骨架屏

                                                          滚动显现(错开 30ms)

                                                          骨架屏 → 内容

                                                          赵
                                                          赵伟
                                                          内容加载完成,骨架屏淡出,真实内容淡入,没有布局跳动。
                                                          骨架与内容同尺寸叠放,只切换透明度

                                                          减少动态效果

                                                          动效常规减少动态效果
                                                          滑动 / 缩放 / 弹出位移 + 淡入只淡入
                                                          翻转rotateY 180°两面交叉淡入淡出
                                                          共享轴 / 页面切换沿轴位移 + 淡入淡出只交叉淡入淡出
                                                          折叠展开高度过渡立即展开 / 收起
                                                          列表进入 / 离开 / 重排逐项错开 + FLIP 位移同时淡入,不做位移
                                                          抖动(错误提示)左右抖动闪烁两次
                                                          加载旋转持续旋转缓慢明暗变化
                                                          脉冲 / 涟漪 / 弹跳 / 漂浮 / 心跳 / 波形 / 输入中无限循环静止,保留静态形态
                                                          骨架屏光带光带扫过静态底色
                                                          对勾描边 / 数字滚动逐步绘制 / 滚动直接显示 / 直接显示最终值
                                                          @media (prefers-reduced-motion: reduce) {
                                                            .zh-anim-slide-up, .zh-anim-scale-in, .zh-anim-pop { animation-name: zh-kf-fade-in; }
                                                            .zh-anim-pulse, .zh-anim-bounce, .zh-anim-float { animation: none; }
                                                          }
                                                          /* JS:动画前先判断 */
                                                          const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
                                                          • 「减少动态效果」不等于「没有动画」:淡入淡出仍然保留,用来告诉用户状态发生了变化
                                                          • 本节的开关加的是 .zh-reduce-motion 类;真实系统设置命中 prefers-reduced-motion 时,本页的基础样式会更保守,直接停用全部过渡与动画
                                                          • 无限循环的动画(加载、光带)必须能被系统设置停掉,并且不要超过页面上必要的数量

                                                          公共类

                                                          一套九百多个的原子类,用来做间距、对齐、显示、文字、颜色这类「微调」。组件自己的外观仍然由组件类负责,公共类只在组件之上补一笔,不用来重写组件内部。所有颜色、间距、圆角、层级都取自 Token,所以换品牌、亮暗、B/C 端后会自动跟着变。

                                                          命名规则

                                                          规则写法说明
                                                          前缀与结构zh-{属性}-{值}全部以 zh- 开头,如 zh-mt-4、zh-text-sm、zh-items-center
                                                          间距刻度0 1 2 3 4 5 6 8 10 12数字是 Token 序号,不是像素:zh-p-4 = var(--zh-space-4)(16px)。没有负值,需要时用 zh-m*-auto
                                                          颜色zh-text-* zh-bg-* zh-border-*只映射语义 Token:主 / 次 / 辅助、品牌、成功 / 警告 / 危险 / 信息、涨跌、图表 1–8
                                                          状态zh-hover-* zh-focus-*悬停类只在支持悬停的设备生效(@media (hover: hover)),触屏不会出现「粘住」的悬停态
                                                          响应式后缀-phone -tablet -desktop -large写在末尾,没有冒号。-phone = 手机(<768),-tablet = 平板及更小(≤1199),-desktop = 桌面及更大(≥1200),-large = 大屏(≥1920)
                                                          显示 / 隐藏zh-hide-* zh-show-*-only按档位精确生效:hide-phone / hide-tablet / hide-desktop(1200–1919)/ hide-large;另有 hide-tablet-down(≤1199)、hide-desktop-up(≥1200)
                                                          优先级单类选择器特异度固定为 (0,1,0),不使用 !important;样式表里排在组件之后,所以同一元素上工具类会胜出
                                                          • 优先用组件类,工具类只做微调;一个元素上超过 8 个工具类时,考虑抽成组件
                                                          • 不要用工具类去改组件的内部状态(悬停、禁用、选中),那是组件的职责
                                                          • 响应式工具类跟随浏览器视口,而组件内部用容器查询;本页顶部的「屏幕」开关会同时模拟两者

                                                          速查表

                                                          点击任意类名即可复制。列表由页面里真实的样式表在运行时解析生成,不会和实际样式脱节。

                                                          在线试用

                                                          示例:
                                                          ABC

                                                          效果示例

                                                          间距刻度

                                                          margin、padding、gap 共用同一把刻度(zh-mt-*、zh-px-*、zh-gap-*)。

                                                          对齐:justify × items

                                                          start / start
                                                          center / start
                                                          end / start
                                                          start / center
                                                          center / center
                                                          end / center
                                                          between / end
                                                          around / end
                                                          evenly / end

                                                          响应式显示

                                                          当前档位:
                                                          手机 < 768
                                                          平板 768–1199
                                                          桌面 1200–1919
                                                          大屏 ≥ 1920
                                                          zh-hide-phone zh-hide-tablet zh-hide-desktop zh-hide-large zh-hide-tablet-down zh-hide-desktop-up

                                                          上面每个标签只在没有被隐藏的档位出现。拖动浏览器窗口,或用顶部「屏幕」切换来观察。

                                                          flex-col-phone:手机上竖排
                                                          平板及以上横排
                                                          三块等宽

                                                          文字

                                                          zh-truncate:这是一段很长很长很长很长很长的单行文字,超出会显示省略号
                                                          zh-line-clamp-2:最多显示两行,多出的部分被省略。适合卡片摘要、列表副标题,能保持列表整齐一致,不会因为内容长短不一而参差不齐。
                                                          tabular-nums
                                                          1111
                                                          0000
                                                          proportional
                                                          1111
                                                          0000
                                                          zh-uppercase zh-tracking-widest
                                                          xssmbaselgxl2xl3xl

                                                          颜色

                                                          zh-text-primary
                                                          zh-text-secondary
                                                          zh-text-tertiary
                                                          zh-text-brand
                                                          zh-text-success
                                                          zh-text-warning
                                                          zh-text-danger
                                                          zh-text-info
                                                          zh-bg-subtle
                                                          zh-bg-brand-subtle
                                                          zh-bg-success
                                                          zh-bg-warning
                                                          zh-bg-danger
                                                          zh-bg-info
                                                          zh-border-brand
                                                          zh-border-strong
                                                          zh-bg-chart-1
                                                          zh-bg-chart-2

                                                          图表色只用于标记(圆点、色块),不要拿来染文字。

                                                          圆角 · 阴影

                                                          sm / md / lg 随 B / C 端切换而变化。

                                                          尺寸与比例

                                                          size-N、aspect-video、aspect-square。

                                                          状态

                                                          hover-lift

                                                          用 Tab 键聚焦第二个按钮可以看到焦点环。

                                                          分隔线

                                                          段落一
                                                          或者
                                                          段落二
                                                          首页文档更新日志

                                                          滚动吸附 · 安全区

                                                          1
                                                          2
                                                          3
                                                          4
                                                          zh-pb-safe-4:底部留白取 16px 与刘海屏安全区的较大者(桌面端安全区为 0)。

                                                          兼容说明

                                                          写法最低版本
                                                          Chrome / Safari / Firefox
                                                          兜底
                                                          :where()(响应式类的档位限定)88 / 14 / 78真实产品里只用 @media;:where(html[data-screen]) 仅用于本页模拟档位
                                                          line-clamp同时输出 -webkit-line-clamp所有浏览器通用
                                                          100dvh108 / 15.4 / 101前一行先写 100vh,不认识的浏览器自动沿用
                                                          env(safe-area-inset-*)69 / 11.2 / 65写了默认值 0,不支持时无副作用
                                                          text-wrap: balance / pretty114 / 17.5 / 121不认识时按普通换行,只是排版稍差

                                                          中后台交互模式

                                                          组件解决「长什么样」,交互模式解决「这个场景该怎么做」。下面按 B 端最常见的七类任务给出判断:先看场景,再选交互方式,最后才是组件。每个场景都带一个可以操作的最小示例。

                                                          判断表

                                                          场景典型页面判断交互方式核心组件
                                                          数据筛选列表页、日志条件 ≤ 4 个且改动频繁选中即生效,无需「查询」按钮筛选标签、分段控件、表格
                                                          条件多或需输入文本点「查询」后生效,可「重置」表单、按钮、表格
                                                          批量操作内容审核、设备管理先选后做勾选后,底部出现操作条并显示数量复选框、吸底操作条、消息提示
                                                          效果预览主题配置、文案改写结果需要先看再定并排对比,保存或放弃二选一预览面板、按钮
                                                          数据导入素材、设备清单一次多个文件逐个显示进度与结果,失败可单独重试上传、进度条、消息提示
                                                          状态流转审批、发布检查状态有限且有顺序在列表里直接流转,危险动作二次确认状态标签、操作菜单、确认框
                                                          任务引导工作台用户开始前需要知道做什么清单式卡片,完成一项划掉一项卡片、进度、文字提示
                                                          新手指引首次进入只提示一次,不阻断操作气泡指向目标,可跳过气泡、遮罩(可选)

                                                          示例

                                                          数据筛选 · 选中即生效

                                                          作品列表
                                                          作品状态

                                                          任务引导 · 清单

                                                            批量操作 · 吸底操作条

                                                              效果预览 · 保存或放弃

                                                              原文

                                                              他沉默地看着窗外,雨一直在下。

                                                              改写后

                                                              他望着窗外的雨,一言不发。

                                                              待确认

                                                              状态流转 · 列表内完成

                                                                新手指引 · 非阻断气泡

                                                                气泡指向目标,点「知道了」或按 Esc 关闭,不遮挡其他操作。

                                                                选项数量规则

                                                                选项数量用什么原因
                                                                2–5 个分段控件(单选)或单选组、复选组,全部平铺一眼看全,一次点击完成,不需要展开
                                                                6–12 个下拉选择平铺会占太多空间
                                                                13 个以上可搜索的下拉,或带分组的选择器需要搜索才能快速找到
                                                                选项会增减(数据来自接口)可搜索下拉,支持远程加载数量不可预知

                                                                按钮矩阵与状态

                                                                把按钮的「层级、尺寸、状态」和表单的「校验时机与文案」写成表,做设计和开发时直接对照,不需要再猜。

                                                                按钮层级 × 尺寸

                                                                层级smmdlg什么时候用
                                                                • 一个区域只放一个「主按钮」;其余用次按钮或文字按钮
                                                                • 危险操作用红色,但不要默认高亮;需要二次确认时,确认框里的确认按钮才用满色
                                                                • 图标按钮必须有 aria-label;文字按钮用在表格行内、卡片角落

                                                                按钮组 · 分裂按钮

                                                                按钮组(互斥选择)

                                                                分裂按钮(主动作 + 更多动作)

                                                                带计数

                                                                分裂按钮只用于「动作菜单」(导出、更多操作),不用于选择一个值。

                                                                交互状态矩阵

                                                                组件默认悬停按下聚焦禁用加载

                                                                上表每一格都是真实组件,用「强制状态」类固定,方便截图和评审。聚焦状态用键盘 Tab 也能看到同样的焦点环。

                                                                状态规则

                                                                状态视觉时长
                                                                悬停背景或边框加深一档;卡片可上浮 2px 并加深阴影;触屏不出现悬停态--zh-dur-fast
                                                                按下缩到 98%,或颜色再深一档--zh-dur-instant
                                                                聚焦2px 品牌色焦点环,与元素之间留 2px 间隙;只在键盘操作时显示无过渡
                                                                禁用透明度 45%,光标为禁止;需要告诉用户原因时改用「禁用 + 提示」—
                                                                加载按钮内显示旋转图标,文字保留,宽度不变,不可重复点击持续

                                                                表单校验规范

                                                                项规范
                                                                校验时机失焦时校验;出错后改为输入时实时校验;提交时再整体校验一次
                                                                提示位置输入框正下方,红色文字加图标;不用弹窗
                                                                提示文案说清楚怎么改:「手机号应为 11 位数字」,而不是「格式错误」
                                                                必填标记标签后加红色 *;全部必填时,改为标记「选填」
                                                                提交失败焦点移到第一个出错的字段,并在顶部汇总数量
                                                                成功反馈不必给每个字段打勾;提交成功用消息提示

                                                                试一试

                                                                11 位数字
                                                                例如 [email protected]

                                                                治理与规范

                                                                让规范能长期守住:色彩纪律有数字、旧写法有对照、代码里的硬编码能被扫出来,文案、无障碍、用与不用都有明确的写法。

                                                                色彩纪律 读取当前主题的实测值

                                                                规则要求当前实测结果
                                                                • 强调色(品牌、成功、警告、危险、信息)在同一明度带内,只变化色相,所以放在一起不会有一个特别「跳」
                                                                • 中性色(背景、边框、文字)几乎不带颜色,冲击力来自强调色,不靠背景饱和度制造
                                                                • 切换顶部的品牌色再看这张表:换任何品牌色,文字对比度都应保持通过

                                                                Token 对照 · 旧 → 新

                                                                旧写法(作废)新 Token说明
                                                                --zh-green / --zh-orange / --zh-red / --zh-cyan--zh-success / --zh-warning / --zh-danger / --zh-info用语义命名,不用颜色名;文字用 --zh-text-* 系列
                                                                状态浅底 color-mix(... 14%)--zh-success-bg 等 12 个构建期算成静态值,不依赖 color-mix
                                                                --zh-motion--zh-dur-fast / base / slow旧变量仍可用,新代码用时长阶梯
                                                                z-index: 20 / 50 / 60--zh-z-sticky / drawer / toast8 个层级,业务代码不写裸数字
                                                                阴影 0 8px 24px rgba(...)--zh-shadow-sm / md / lg三档,随 B / C 端切换
                                                                硬编码 #172126 一类文字色--zh-text / --zh-text-secondary用下面的扫描器批量找出
                                                                多种圆角数值(3 / 5 / 6 / 8 / 12 / 20px)--zh-radius-sm / md / lg胶囊型只保留在按钮和标签,不作为圆角 Token
                                                                多套按钮类名zh-btn + 层级修饰类统一到一套,见「按钮矩阵与状态」

                                                                硬编码扫描器

                                                                粘贴一段 CSS,找出颜色、圆角、层级、时长里可以替换成 Token 的写法。只在浏览器本地计算,不上传任何内容。

                                                                文案规范

                                                                位置写法不要这样写
                                                                按钮动词开头,2–4 个字:保存改动、新建作品确定、点击这里
                                                                报错说清楚原因和怎么办:手机号应为 11 位数字格式错误、操作失败
                                                                空状态说明为什么空,给出下一步:还没有作品,先新建一个暂无数据
                                                                危险确认标题写动作,正文写后果:删除这个作品?删除后无法恢复确定要删除吗?
                                                                成功提示说结果,不说过程:已保存您的操作已成功提交,请稍候
                                                                数字与单位数字和单位之间不加空格:128 万(中文)、12.9K(英文);千分位用逗号混用「w」「万」「K」
                                                                语气平实、直接,用「你」;不用感叹号,不用「请」堆叠「亲」「您好」「敬请」

                                                                用与不用

                                                                无障碍清单

                                                                上线前自查

                                                                  对象最低要求
                                                                  正文文字对比度 ≥ 4.5:1
                                                                  大字号 / 图标对比度 ≥ 3:1
                                                                  触控目标≥ 44 × 44px
                                                                  焦点环2px,对比度 ≥ 3:1
                                                                  动画可被系统设置关闭
                                                                  状态不只靠颜色,加图标或文字

                                                                  变更记录

                                                                  版本类型内容
                                                                  0.1.0新增Token 包:OKLCH 品牌色阶、亮暗双模式、B / C 端预设、密度、涨跌色、运行时与构建输出一致
                                                                  0.1.0新增演示页:色彩、布局、通用组件、数据展示、图标、公共类、动画与动态
                                                                  0.1.0调整文字三级色对比度提升到 4.5:1 以上;状态浅底改为静态值
                                                                  0.1.0作废--zh-green 等颜色名变量,见上方对照表

                                                                  规则:新增可以随时发;调整外观需要在这里写明;作废的 Token 至少保留一个小版本,并在对照表里给出替代。

                                                                  表单示例

                                                                  一张完整的「新建设备」表单。字段用自动列布局:每个字段至少 232px 宽,一行放得下几列就放几列,宽度变了列数跟着变;备注、标签这类需要整行的内容单独占满。拖动下面的滑块改变表单宽度,可以看到列数从 1 列到 4 列的变化。

                                                                  基本信息
                                                                  2–20 个字
                                                                  格式:ZH-加 4 位数字
                                                                  填写姓名
                                                                  IPv4,每段 0–255
                                                                  1–65535
                                                                  网络与安全
                                                                  通信协议
                                                                  加密方式
                                                                  开关
                                                                  通知渠道
                                                                  其他
                                                                  0 / 200
                                                                  为必填项

                                                                  布局规则

                                                                  规则做法
                                                                  列数不写死。字段容器用 repeat(auto-fill, minmax(232px, 1fr)),一行能放几列就放几列
                                                                  整行字段备注、富文本、表格类字段加 zh-fx-full 占满整行;多选组、单选组这类内容较长的字段可以占两格
                                                                  标签位置统一放在输入框上方,靠左对齐;不因列数变化而改变,扫读路径稳定
                                                                  分组每组用 fieldset 加 legend,组与组之间留 32px;一组不超过 8 个字段
                                                                  顺序必填在前,选填在后;相关字段挨着放;按阅读顺序从左到右、从上到下
                                                                  操作区提交按钮固定在表单底部右侧,取消和存草稿在左;手机上按钮通栏
                                                                  校验失焦时校验,提交时整体校验;焦点回到第一个出错的字段,并在顶部汇总数量

                                                                  风格对照

                                                                  同一批界面,左边扁平,右边玻璃拟态。两边共用同一套组件和 Token,唯一的区别是容器上的 data-style。品牌色、亮暗、密度、B/C 端开关对两边同时生效,可以直接切换来检验。

                                                                  取舍扁平玻璃拟态
                                                                  层级靠什么表达边框、留白、单层阴影半透明表面 + 背景模糊 + 亮边:越靠上的层越通透,弹层最亮
                                                                  可读性对比度高,密集数据也清楚文字对比度按最差情况(表面叠在最暗的光斑上)测试,同样达标;背景越花,越依赖光斑控制在低饱和
                                                                  适合B 端数据页、表格、长表单C 端品牌页、工作台、仪表盘、弹层与浮动面板、深色背景下的信息面板
                                                                  高对比度模式无变化自动退回实色表面加描边;不支持背景模糊、或系统要求「减少透明度」时同样退回实色

                                                                  AI 语义与 SEO

                                                                  这一页本身就遵循这些规则。GEO 与 AIO 的效果主要取决于内容质量,ZhaoUI 负责提供语义、结构化数据和机器可读文档这些载体。

                                                                  第一版内置

                                                                  • 语义 HTML:header / nav / main / section,标题层级不跳级
                                                                  • ARIA 与键盘可达:表单标签、状态提示、焦点环
                                                                  • SSR 安全:导入组件时不访问 window
                                                                  • JSON-LD 组件:Article、FAQ、Product、Breadcrumb、Organization
                                                                  • 机器可读文档:每个组件的 Markdown 与 props JSON,加 llms.txt

                                                                  AI 可操作性约定

                                                                  控件带稳定的可访问名称和固定标识,方便浏览器智能体理解页面。

                                                                  <button class="zh-btn primary"
                                                                    data-zh-component="button"
                                                                    data-zh-action="submit-order"
                                                                    aria-label="提交订单">提交</button>

                                                                  浏览器兼容

                                                                  支持策略分三级。本演示页为了预览响应式,用了容器查询;产品构建用媒体查询兜底,Token 里的颜色也已在构建期算成静态值,不依赖 color-mix。以下版本号是设计依据,上线前以 caniuse 与实测为准。

                                                                  支持等级

                                                                  等级范围保证
                                                                  一级Chrome / Edge ≥ 111(含最近 2 个版本)· Safari(macOS / iOS)≥ 16.4 · Firefox ≥ 113(含 ESR 115)· Electron ≥ 24 · Android WebView 与微信内置浏览器(Chromium ≥ 111)· iOS WKWebView ≥ 16.4完整功能,每次发版都测
                                                                  二级Chrome 90–110 · Safari 15.4–16.3 · Firefox 100–112可用但降级:无容器查询,布局走媒体查询;弹窗用兜底层;不保证动效
                                                                  不支持IE 11 · 旧版 Edge(EdgeHTML)· Safari < 15.4 · Android 4.x 内置浏览器不测试,也不做兜底

                                                                  用到的特性与兜底

                                                                  特性最低版本
                                                                  Chrome / Safari / Firefox
                                                                  兜底方案
                                                                  容器查询、cqi105 / 16.0 / 110产品构建把同一套规则输出为 @media;演示页仅作预览用
                                                                  color-mix()111 / 16.2 / 113Token 构建期预先算成 hex / rgba,运行时不使用
                                                                  <dialog>37 / 15.4 / 98引入 dialog-polyfill,或降级为固定定位层
                                                                  :focus-visible86 / 15.4 / 85先写 :focus,再用 :focus-visible 覆盖
                                                                  accent-color93 / 15.4 / 92复选框、单选框自绘
                                                                  appearance84 / 15.4 / 80同时写 -webkit-appearance
                                                                  flex gap84 / 14.1 / 63需要更老版本时改用 margin
                                                                  CSS mask(勾选图标)120 / 15.4 / 53同时写 -webkit-mask
                                                                  SVG <use href>50 / 12.1 / 51同时写 xlink:href
                                                                  env(safe-area-inset-*)69 / 11.2 / 65不支持时取 0,无副作用
                                                                  CSS zoom、ResizeObserver演示页专用产品不使用

                                                                  怎么保证

                                                                  • 统一浏览器目标:chrome >= 111, edge >= 111, safari >= 16.4, firefox >= 113, ios_saf >= 16.4, not dead,写入 Browserslist
                                                                  • 构建:PostCSS(autoprefixer + postcss-preset-env)按上述目标自动加前缀、降级
                                                                  • 自动化:Playwright 同时跑 Chromium、Firefox、WebKit,做截图对比
                                                                  • 真机:iOS Safari、Android Chrome、微信内置浏览器,每次发版前过一遍

                                                                  Token 输出

                                                                  当前主题组合下的实际取值,可复制。这是后续 @zhaoui/tokens 包的雏形。

                                                                  
                                                                        
                                                                  确认删除
                                                                  删除后无法恢复,确定要删除这篇文章吗?