# 赵子民设计系统 · zzm v4 · 全文 > 赵子民全部产品共用的界面设计系统:纸 · 墨 · 朱三色、8pt 网格、五层光照的微拟物按键。每个页面都有 Markdown 版本,样张的 HTML 是构建时由 React 源码渲染出的真实标记,可原样粘贴。同步自 edu.zhaozimin v23.5.1(086937d)。 站点 v0.1.1 · 2026-09-23 · 共 38 页。页与页之间以一行 ======== 分隔。 --- title: 一块材料切出来的界面 url: https://ui.zhaozimin.com/ markdown: https://ui.zhaozimin.com/index.md group: 开始 origin: 规范文档 synced: edu v23.5.1 (086937d) · 2026-09-23 scope: .zzm-v4 # 根节点必须带这个类,否则样式不生效 css: https://ui.zhaozimin.com/zzm.css --- # 一块材料切出来的界面 > 赵子民全部产品共用的设计系统。三种颜色、一把 8pt 梯子、一个「光从正上方来」的光照模型——每个令牌和组件都同时有人读版和机读版。 ## 给 Agent 的一行 在 Cursor、Claude Code 或任何能联网的工具里,把下面这个地址交给它,说「按这个设计系统做」。它会从目录读到每一页的 Markdown 版本,包括令牌、铁律和可以直接粘贴的代码。 Agent 入口: ```text https://ui.zhaozimin.com/llms.txt ``` 写静态页时,一行引入全部质感,然后在根节点加上作用域类名: 静态页: ```html
``` ## 从这里开始 ## 这套系统相信什么 - **一屏一枚朱印。** 朱砂红只出现一次,永远标「你在这里 / 下一步」。红是导航信号,不是装饰。 - **圆是徽章,方角是控件。** 文字按钮一律圆角矩形;999 胶囊只留给不可点的徽章。看形状就知道能不能点。 - **一个光源。** 光从正上方来,每个控件都是一枚真实的键。阴影只从质感类取,组件里不许各写各的光。 - **一把梯子。** 间距、高度、行高、圆角都从 4 的倍数里取,不发明数字。整站看起来才像一块材料切出来的。 - **留白即上手。** 能收进次级入口的,绝不摆在主动线上。 完整说明见[设计原则](/foundations/principles)。 ## 如果你是 Agent 1. 先读 [llms.txt](https://ui.zhaozimin.com/llms.txt) 拿目录,需要全文时读 [llms-full.txt](https://ui.zhaozimin.com/llms-full.txt)。 2. 引入 [zzm.css](https://ui.zhaozimin.com/zzm.css),根节点加 `class="zzm-v4"`。不加这个类,样式一点都不生效。 3. 按钮用 `.zzm-btn` 家族,卡片用 `.zzm-card` / `.zzm-cardlink`,输入框用 `.zzm-input`;颜色只用 `var(--zzm-*)`。 4. 不要自己写 `box-shadow`,不要把按钮做成胶囊,不要引入网络字体。 5. 每个组件页都有 `.md` 版本,里面的 HTML 是构建时渲染出来的真实标记,可以原样粘贴。 本站同步自 edu.zhaozimin v23.5.1(086937d),那是赵子民在线课程站的生产代码。 ## 本页用到的 CSS 按样张里出现的类名,从源文件原样裁出(完整版见 zzm.css): ```css /* base.css */ .zzm-v4 input.zzm-input:focus, .zzm-v4 textarea.zzm-input:focus { border-color: #161616 !important; } .zzm-v4 .zzm-skel { position: relative; overflow: hidden; background: #EFEFEC; border-radius: 8px; } .zzm-v4 .zzm-skel::after { content: ''; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .65), transparent); animation: zzmShimmer 1.4s ease infinite; } /* controls.css */ .zzm-v4 .zzm-btn { -webkit-appearance: none; appearance: none; display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 36px; padding: 0 16px; font-family: inherit; font-size: 13px; font-weight: 600; letter-spacing: .2px; white-space: nowrap; cursor: pointer; user-select: none; border-radius: 8px; /* 圆角矩形——按钮禁胶囊 */ border: 1px solid rgba(0, 0, 0, .14); color: #3F3F3B; background: linear-gradient(180deg, #FFFFFF 0%, #F2F2EF 100%); box-shadow: 0 0 0 3px rgba(255, 255, 255, .55), /* ④ 反光光晕环 */ 0 1px 2px rgba(0, 0, 0, .09), /* ③ 近影 */ 0 2px 6px rgba(0, 0, 0, .06), /* ③ 远影 */ inset 0 1px 0 rgba(255, 255, 255, .92), /* ② 顶部反光 */ inset 0 -1px 0 rgba(0, 0, 0, .05); /* ① 底缘收沉 */ transition: box-shadow .16s ease, transform .16s ease, background .16s ease, border-color .16s ease, filter .16s ease, opacity .16s ease; } .zzm-v4 .zzm-btn:hover { transform: translateY(-1px); border-color: rgba(0, 0, 0, .18); box-shadow: 0 0 0 4px rgba(255, 255, 255, .8), 0 2px 4px rgba(0, 0, 0, .10), 0 4px 12px rgba(0, 0, 0, .09), inset 0 1px 0 rgba(255, 255, 255, .95), inset 0 -1px 0 rgba(0, 0, 0, .05); } .zzm-v4 .zzm-btn:active { transform: translateY(0); background: linear-gradient(180deg, #ECECE9 0%, #F4F4F1 100%); /* ⑤ 凹面翻转 */ box-shadow: 0 0 0 3px rgba(255, 255, 255, .45), 0 1px 0 rgba(255, 255, 255, .65), inset 0 2px 4px rgba(0, 0, 0, .13), inset 0 1px 2px rgba(0, 0, 0, .09); } .zzm-v4 .zzm-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(166, 64, 47, .20), /* 键盘焦点=朱色光晕 */ 0 1px 2px rgba(0, 0, 0, .09), 0 2px 6px rgba(0, 0, 0, .06), inset 0 1px 0 rgba(255, 255, 255, .92); } .zzm-v4 .zzm-btn:disabled { cursor: not-allowed; transform: none; } .zzm-v4 .zzm-btn:disabled:not(.zzm-btn--busy) { color: #A2A29C; background: #F2F2EF; border-color: rgba(0, 0, 0, .08); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7), 0 1px 2px rgba(0, 0, 0, .04); } .zzm-v4 .zzm-btn--ondark:disabled:not(.zzm-btn--busy) { color: rgba(255, 255, 255, .45); background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .16); box-shadow: none; } .zzm-v4 .zzm-btn--busy { position: relative; overflow: hidden; pointer-events: none; opacity: .92; filter: saturate(.72); transform: none; } .zzm-v4 .zzm-btn--busy::before { content: ""; position: relative; z-index: 1; /* 旋转环压在反光层之上 */ width: 12px; height: 12px; flex: none; border-radius: 50%; border: 2px solid currentColor; border-top-color: transparent; opacity: .7; animation: zzmBtnSpin .7s linear infinite; } .zzm-v4 .zzm-btn--busy::after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; transform: translateX(-180%); background: linear-gradient(105deg, transparent 40%, rgba(255, 255, 255, .45) 50%, transparent 60%); animation: zzmPillShine 4.4s ease-in-out infinite; } .zzm-v4 .zzm-btn--primary { color: #FFFFFF; border-color: rgba(0, 0, 0, .85); background: linear-gradient(180deg, #3A3A36 0%, #1D1D1B 58%, #141412 100%); box-shadow: 0 0 0 3px rgba(22, 22, 22, .06), 0 1px 2px rgba(0, 0, 0, .18), 0 3px 10px rgba(22, 22, 22, .24), inset 0 1px 0 rgba(255, 255, 255, .20), inset 0 -1px 0 rgba(0, 0, 0, .38); } .zzm-v4 .zzm-btn--primary:hover { filter: brightness(1.12); border-color: rgba(0, 0, 0, .85); box-shadow: 0 0 0 4px rgba(22, 22, 22, .09), 0 2px 4px rgba(0, 0, 0, .20), 0 5px 14px rgba(22, 22, 22, .28), inset 0 1px 0 rgba(255, 255, 255, .24), inset 0 -1px 0 rgba(0, 0, 0, .38); } .zzm-v4 .zzm-btn--primary:active { filter: none; background: linear-gradient(180deg, #101010 0%, #22221F 100%); box-shadow: 0 0 0 3px rgba(22, 22, 22, .06), inset 0 2px 5px rgba(0, 0, 0, .55), inset 0 1px 2px rgba(0, 0, 0, .4); } .zzm-v4 .zzm-btn--accent { color: #FFFFFF; border-color: rgba(122, 42, 29, .9); background: linear-gradient(180deg, #C25743 0%, #A6402F 56%, #99392A 100%); box-shadow: 0 0 0 3px rgba(166, 64, 47, .10), /* 朱色反光光晕 */ 0 1px 2px rgba(0, 0, 0, .14), 0 3px 10px rgba(166, 64, 47, .28), inset 0 1px 0 rgba(255, 255, 255, .32), inset 0 -1px 0 rgba(0, 0, 0, .20); } .zzm-v4 .zzm-btn--accent:hover { filter: brightness(1.06); border-color: rgba(122, 42, 29, .9); box-shadow: 0 0 0 4px rgba(166, 64, 47, .16), 0 2px 4px rgba(0, 0, 0, .16), 0 5px 14px rgba(166, 64, 47, .34), inset 0 1px 0 rgba(255, 255, 255, .36), inset 0 -1px 0 rgba(0, 0, 0, .20); } .zzm-v4 .zzm-btn--accent:active { filter: none; background: linear-gradient(180deg, #8E3425 0%, #A03D2C 100%); box-shadow: 0 0 0 3px rgba(166, 64, 47, .10), inset 0 2px 5px rgba(0, 0, 0, .34), inset 0 1px 2px rgba(0, 0, 0, .24); } .zzm-v4 .zzm-btn--ghost { border-color: transparent; background: transparent; box-shadow: none; color: #4A4A46; } .zzm-v4 .zzm-btn--ghost:hover { transform: translateY(-1px); border-color: rgba(0, 0, 0, .14); background: linear-gradient(180deg, #FFFFFF 0%, #F2F2EF 100%); box-shadow: 0 0 0 3px rgba(255, 255, 255, .55), 0 1px 2px rgba(0, 0, 0, .09), 0 2px 6px rgba(0, 0, 0, .06), inset 0 1px 0 rgba(255, 255, 255, .92); } .zzm-v4 .zzm-btn--ghost:active { transform: translateY(0); border-color: rgba(0, 0, 0, .14); background: linear-gradient(180deg, #ECECE9 0%, #F4F4F1 100%); box-shadow: inset 0 2px 4px rgba(0, 0, 0, .12); } .zzm-v4 .zzm-video-play { display: grid; place-items: center; width: 72px; height: 72px; padding: 0; border: 1px solid rgba(0, 0, 0, .16); border-radius: 50%; cursor: pointer; color: #1D1D1B; background: linear-gradient(180deg, #FFFFFF 0%, #F2F2EF 100%); box-shadow: 0 0 0 4px rgba(255, 255, 255, .24), 0 2px 4px rgba(0, 0, 0, .16), 0 6px 16px rgba(0, 0, 0, .22), inset 0 1px 0 rgba(255, 255, 255, .96), inset 0 -1px 0 rgba(0, 0, 0, .08); transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease; } .zzm-v4 .zzm-video-play:hover { border-color: rgba(0, 0, 0, .3); box-shadow: 0 0 0 5px rgba(255, 255, 255, .3), 0 3px 6px rgba(0, 0, 0, .18), 0 9px 22px rgba(0, 0, 0, .28), inset 0 1px 0 rgba(255, 255, 255, .98), inset 0 -1px 0 rgba(0, 0, 0, .08); } .zzm-v4 .zzm-video-play:active { transform: scale(.98); background: linear-gradient(180deg, #E9E9E6 0%, #F4F4F1 100%); box-shadow: 0 0 0 4px rgba(255, 255, 255, .24), inset 0 2px 5px rgba(0, 0, 0, .16), inset 0 1px 2px rgba(0, 0, 0, .10); } .zzm-v4 .zzm-video-play:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(166, 64, 47, .28), 0 2px 4px rgba(0, 0, 0, .16), 0 6px 16px rgba(0, 0, 0, .22), inset 0 1px 0 rgba(255, 255, 255, .96); } .zzm-v4 .zzm-video-play__icon { width: 24px; height: 24px; margin-left: 4px; fill: currentColor; } .zzm-v4 .zzm-btn--sm { height: 28px; padding: 0 12px; font-size: 12px; gap: 4px; } .zzm-v4 .zzm-btn--icon.zzm-btn--sm { width: 28px; } .zzm-v4 .zzm-cardlink { background: #FFFFFF; border: 1px solid rgba(0, 0, 0, .09); border-radius: 16px; padding: 12px; cursor: pointer; box-shadow: 0 1px 2px rgba(0, 0, 0, .04); transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease; } .zzm-v4 .zzm-cardlink:hover { transform: translateY(-3px); border-color: rgba(0, 0, 0, .3); box-shadow: 0 10px 30px rgba(0, 0, 0, .09), 0 1px 2px rgba(0, 0, 0, .04); } .zzm-v4 .zzm-card { background: #FFFFFF; border: 1px solid rgba(0, 0, 0, .09); border-radius: 16px; box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 6px 18px rgba(0, 0, 0, .05), inset 0 1px 0 rgba(255, 255, 255, .85); } .zzm-v4 input.zzm-input, .zzm-v4 textarea.zzm-input { background: #FDFDFC; box-shadow: inset 0 1.5px 3px rgba(0, 0, 0, .06), inset 0 1px 1px rgba(0, 0, 0, .04); } .zzm-v4 input.zzm-input:focus, .zzm-v4 textarea.zzm-input:focus { box-shadow: inset 0 1.5px 3px rgba(0, 0, 0, .06), 0 0 0 3px rgba(22, 22, 22, .07); /* 聚焦光晕 */ } .zzm-v4 .zzm-pill-shine { position: relative; overflow: hidden; } .zzm-v4 .zzm-pill-shine::after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; transform: translateX(-180%); background: linear-gradient(105deg, transparent 34%, rgba(255, 255, 255, .85) 50%, transparent 66%); animation: zzmPillShine 4.6s ease-in-out infinite; animation-delay: var(--zzm-shine-delay, 0s); } /* keyframes */ @keyframes zzmShimmer { 100% { transform: translateX(100%) } } @keyframes zzmBtnSpin { to { transform: rotate(360deg) } } @keyframes zzmPillShine { 0% { transform: translateX(-180%) } 14% { transform: translateX(180%) } 100% { transform: translateX(180%) } } ``` --- 上一页:无 · 下一页:[快速接入](https://ui.zhaozimin.com/start/quickstart.md) · 全站目录:https://ui.zhaozimin.com/llms.txt ======== --- title: 快速接入 Quickstart url: https://ui.zhaozimin.com/start/quickstart/ markdown: https://ui.zhaozimin.com/start/quickstart.md group: 开始 origin: 规范文档 synced: edu v23.5.1 (086937d) · 2026-09-23 scope: .zzm-v4 # 根节点必须带这个类,否则样式不生效 css: https://ui.zhaozimin.com/zzm.css --- # 快速接入 Quickstart > 三种接法:只要样式(任何技术栈)、直接用 React 组件、或者把地址交给 Agent。先读完这页的四条前提,能省掉九成的对不上。 ## 先读:四条前提 - ✓ 要:根节点加 `class="zzm-v4"`——全部样式只在这个作用域里生效 - ✓ 要:用系统字体,不引入网络字体;中文靠 PingFang SC,Windows 落到微软雅黑 - ✓ 要:只有浅色:v4 已废除暗色分支,界面永远是纸白底 - ✓ 要:不需要 Tailwind:zzm.css 自带组件依赖的那部分基线(box-sizing、零外距等) ## 一、只要样式(任何技术栈) 引入一个 CSS 文件,用类名写 HTML。Vue、Svelte、纯静态页、服务端模板都一样。 index.html: ```html ``` 不方便给根节点加类名时,改用 `https://ui.zhaozimin.com/zzm.global.css`:同样的规则,去掉了 `.zzm-v4` 前缀,对整页生效。 ### 样张 · 一张用类名拼出来的表单卡 看「HTML」页签——那就是你要写的全部 React: ```jsx export function StaticPage() { return (