最近跟团队聊原型设计,大家不约而同提到一个字:一致。视觉稿、原型演示、最终上线页面,这三者之间总有一条若有若无的风格裂缝,颜色差半度、间距偏几像素,说不上哪里不对,但整体就是“味不对”。后来我们试用了一种把设计决策全部写进Markdown文档的做法,内部管这种文档叫Design.md,再配合一套渲染方案直接生成可交互原型。跑通之后,我只能说这条路比想象中靠谱得多。
这篇文章把我从零搭建Design.md原型体系的完整过程整理出来,包括项目结构怎么规划、设计令牌怎么定义、组件和页面怎么写、渲染工具链怎么搭、团队怎么基于它做评审和版本管理,以及我实际操作中踩过的几个真实的坑。想解决“设计规范落地难、原型和产品风格对不上”问题的产品经理、UI设计师和前端开发,都可以往下看。
1. 别急着写代码:先用Design.md管住“风格漂移”
1.1 传统原型交付链条里,风格是怎么悄悄跑偏的
传统做原型的路线,基本是先在界面设计工具里画高保真稿,导出一份漂亮的图片或者互动链接,然后前端照着稿子写代码。这条路线看着顺,其实每个环节都在悄悄漏信息。设计稿里的颜色值躺在设计软件的图层里,标注规则可能挂在某个附件上,组件库更新了十个版本但团队手里用的还是旧素材,开发桌上的规范文档又写了三个月。每一环都只差一点点,叠加起来就是产品上线后那种说不清道不明的“味不对”。
我把这种现象叫“风格漂移”:设计规范、视觉稿、代码实现三方各说各话。根子在于没有一个单一事实来源。传统方案里,规范是文档、视觉稿是图片、实现是代码,三个地方互相之间靠人肉翻译和同步。翻译次数一多,信息损耗就上来了,风格自然守不住。尤其是团队一扩大,新成员加入之后每人理解的“产品风格”都有一点点偏差,最后就变成了大家心照不宣的“差不多就行”。
1.2 把设计决策变成文本:Design.md的核心逻辑
Design.md的思路刚好相反:把设计决策从图片和散落文档里打捞出来,用Markdown形式的纯文本集中描述。颜色、字体、间距、圆角、组件状态、页面布局,全部拆成结构化的文本单元并存进同一个仓库,再由渲染方案把这些文本变成可点击的原型页面。
这样做有两个直接的收益。第一,纯文本可以进版本控制,每一次改动的历史清清楚楚,谁在什么时候改了什么颜色、为什么改,都查得到。第二,设计与开发开始共用同一套描述方式——设计师在Design.md里定义的--color-primary,前端可以直接拿来当CSS变量用,中间少了一层人工翻译。这相当于让设计和开发使用同一种语言对话,而不是各说各话再靠开会弥合。
1.3 认清边界:这套方案适合什么团队
先说清楚,Design.md不是要取代视觉设计工具。需要精细排布、复杂插画、特殊视觉效果的页面,还是得靠专业工具处理。以我的经验,这套方案最舒服的场景是:以表单、列表、详情、弹窗这类结构化界面为主的业务系统,尤其是管理后台、SaaS控制台、内部工具。这类产品页面风格高度统一、信息密度高、交互模式相对固定,正好是文本驱动设计的强项。
对团队规模和性质,我的判断很简单:团队里至少有一名成员能看懂并愿意维护Markdown和简单样式语法;设计规范已经或者打算沉淀为可复用的令牌体系;产品迭代节奏快,需要频繁调整原型做验证。满足这三条,Design.md大概率能给你带来实打实的效率提升。如果团队还没有任何设计规范沉淀,我建议先拿一个页面试点,跑通了再铺开。
2. 搭建Design.md项目:设计令牌是地基
2.1 三层目录结构:令牌、组件、页面
第一次搭Design.md仓库的时候,我按照一个比较顺手的结构来组织:
design/ ├── README.md ├── tokens/ │ ├── color.md │ ├── typography.md │ ├── spacing.md │ ├── radius.md │ └── shadow.md ├── components/ │ ├── button.md │ ├── input.md │ ├── card.md │ ├── table.md │ └── modal.md ├── pages/ │ ├── login.md │ ├── dashboard.md │ └── settings.md └── snippets/ ├── 布局模板.md └── 状态演示模板.md这个结构对应的是“令牌—组件—页面”三层:tokens存放所有基础设计变量,components描述可复用组件的样式与状态,pages用组件拼装出完整业务界面。snippets放一些常用写法,比如布局模板、状态演示模板,方便复制。
tokens是三层里最关键的,也最容易被低估。我见过有人把颜色直接写进组件文档里,短期没问题,但改一个品牌色要翻几十个文件,这就失去Design.md的意义了。一定要让颜色只活在令牌里,组件和页面引用令牌,不要出现游离的裸值。
2.2 颜色、字体、间距、圆角和阴影的写法
Design.md里的令牌写法各家渲染方案略有差异,但核心都是“变量加赋值”。我们用的是贴近CSS变量的方式:
# 颜色令牌 semantic: --color-primary: #3B6EF6 --color-success: #22C55E --color-warning: #F59E0B --color-danger: #EF4444 --color-text: #1F2937 --color-text-secondary: #6B7280 --color-border: #E5E7EB --color-bg: #F9FAFB --color-surface: #FFFFFF写的时候有两条原则建议坚持。一是语义化优先于描述性,--color-primary比--color-blue好用,因为改主题时直接换值就行,不用改引用。二是把“品牌色”和“功能色”分开,品牌色表达产品气质,功能色表达交互状态,混在一起之后组件里会写得很别扭。
字体和间距同理,间距我习惯用4px的倍数做基数,也就是常说的间距系统:
# 字体令牌 typography: --font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif --font-size-xs: 12px --font-size-sm: 14px --font-size-md: 16px --font-size-lg: 20px --font-weight-regular: 400 --font-weight-medium: 500 --font-weight-bold: 600 --line-height-base: 1.6 # 间距令牌 spacing: --space-1: 4px --space-2: 8px --space-3: 12px --space-4: 16px --space-5: 24px --space-6: 32px --space-8: 48px间距用4px基数,好处是你最终用到的8、16、24、32、48都落在同一张格子里,页面再怎么拼,视觉上都稳稳的,不会跳出一个突兀的17px。圆角和阴影我习惯放在一起看,因为它们共同决定产品的“圆润程度”和“层次感”:
# 圆角与阴影 radius: --radius-sm: 4px --radius-md: 8px --radius-lg: 12px --radius-full: 9999px shadow: --shadow-sm: 0 1px 2px rgba(0,0,0,0.05) --shadow-md: 0 4px 6px rgba(0,0,0,0.07) --shadow-lg: 0 10px 20px rgba(0,0,0,0.1)2.3 用一份主题概览锁住产品风格
“符合产品风格”这件事,落到Design.md里就是一个清晰的主题定义。我的做法是单独维护一个主题概览文件,把品牌印象拆解成几个可感知的属性:主色、辅助色、字体气质、圆角程度、阴影强度。
比如一个工具型产品,主色是稳重的蓝,圆角中等偏小,阴影克制;面向C端的年轻产品,主色可以更大胆,圆角更大,阴影更有存在感。这份概览不要只写“我们用了蓝色”,要具体到值,并且说明为什么:
# 产品风格概览 风格定位:专业、高效、可信赖 - 主色:#3B6EF6,传达专业与稳定 - 强调色:#7C3AED,用于引导性操作和重点提示 - 字体:系统字体为主,标题用中等字重,正文用常规字重 - 圆角:中小圆角(8px为主),塑造干脆利落的工具感 - 阴影:弱阴影,以边框和间距建立层级,避免浮夸 - 间距:4px基数,页面留白以24px/32px为主这份概览文件是整个Design.md体系里最有“产品感”的一份文档,它回答了“这个产品看起来应该是什么样”的问题。后面所有组件和页面在写法上出现分歧的时候,回到这个文件做裁决,效率高很多。
3. 组件与页面写法:从定义到可演示的业务界面
3.1 组件文档怎么写才不白写
组件是Design.md里承上启下的部分。一个组件的文档建议包含:组件名称、使用场景、结构描述、样式状态、示例代码。拿按钮举例:
# 按钮 Button 使用场景:触发一个操作,如表单提交、页面跳转。 变体: - primary:主要操作,页面中视觉权重最高,最多使用一个 - secondary:次要操作,用于辅助主要操作 - outline:弱化操作,用于低频动作 - danger:危险操作,用于删除、解绑等 状态: - default / hover / active / disabled / loading 样式: - 高度:36px - 横向内边距:16px(由 --space-4 控制) - 圆角:--radius-md - 字体:--font-size-sm,font-weight medium - 主按钮背景:--color-primary,悬停时提高饱和度 - 幽灵按钮:无背景,边框用 --color-border 示例(渲染到原型页面): [b-button variant="primary"]保存[/b-button] [b-button variant="secondary"]取消[/b-button] [b-button variant="danger"]删除[/b-button] [b-button disabled="true"]不可点击[/b-button]这种写法的价值在于,组件的“说明”和“示例”放在一起,渲染出来的原型和文档描述来自同一份文件,不会出现“文档说一套、页面显示另一套”。写清楚变体和状态,前端拿到手之后也能直接对应到实现,减少来回确认。
3.2 页面布局:栅格、间距与对齐的文本化
页面级的写法重点在布局。Design.md里我一般用栅格加块区的方式来描述:页面分成顶部导航、侧边栏、主内容区几个块区,块区内部再用行和列组织内容。拿登录页举例:
# 页面:登录 布局: - 栅格:12列,列间隙 24px(--space-5) - 对齐:表单区域居中,宽度 400px - 外层内边距:32px(--space-6) 内容区块: 1. Logo区:高度 48px,主色图标 + 产品名称 2. 表单区:账号输入框、密码输入框、记住我勾选 3. 提交区:主按钮 100% 宽度,高度 36px 4. 辅助区:忘记密码入口,文字使用 --color-text-secondary 示例: [b-card width="400px" style="margin: 80px auto; padding: 32px;"] [b-input label="邮箱" placeholder="请输入邮箱"][/b-input] [b-input type="password" label="密码" placeholder="请输入密码"][/b-input] [b-button variant="primary" width="100%"]登录[/b-button] [/b-card]刚上手的时候很容易把每个像素都写进文档里,我建议克制一下。只描述相对位置、间距关系、对齐方式,不要去写“距离顶部120px”这种绝对定位式描述,否则换个屏幕尺寸整个文档就站不住了。
3.3 状态与交互反馈:风格里最容易露馅的地方
原型最能体现产品风格的地方,其实是状态和反馈。一个按钮从默认到悬停到加载到底,一个输入框从正常到聚焦到错误,这些细节决定了产品是不是“有质感”。组件文档里的状态块,我会配合交互描述来写,说明什么时候切换什么状态:
# 表单输入框 Input 交互规则: - focus:点击后边框变为 --color-primary,显示聚焦光晕(阴影 --shadow-sm) - error:输入校验失败时边框变为 --color-danger,下方展示错误文字 - disabled:背景置灰,文字使用 --color-text-secondary,不可交互 - placeholder:使用 --color-text-secondary 示例: [b-input label="邮箱" placeholder="name@example.com" state="default"][/b-input] [b-input label="邮箱" value="abc" state="error" error-message="邮箱格式不正确"][/b-input] [b-input label="禁用状态" value="只读内容" state="disabled"][/b-input]状态写清楚以后,原型里自然就有了边界场景的演示。评审的时候大家讨论的就不是“这个按钮是绿是蓝”,而是“错误状态下用户能不能准确感知到问题”。这个层面才是产品风格里真正见功夫的地方,也是Design.md相比静态设计稿的最大优势。
4. 渲染工具链:把Markdown变成能点的原型
4.1 三种渲染路线,选一个适合自己的
Design.md本身只是一套文档规范,必须配一个渲染方案才能变成HTML页面。目前常见的做法有三类,各有取舍:
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 纯Markdown解析器加样式 | 轻量、简单、部署极快 | 组件表达力弱,复杂交互做不了 | 文档型原型、快速草稿 |
| Markdown加自定义组件标签 | 文档可读性好,组件复用强,能做交互 | 需要自己维护渲染管线 | 业务系统、管理后台 |
| 接入前端组件库文档框架 | 与代码耦合紧密,天然同步 | 书写门槛高,依赖特定技术栈 | 前端主导、组件库成熟团队 |
我采用的是第二类:基于一个支持自定义组件语法的Markdown渲染管线,在文档里写[b-button]这类自定义标签,渲染时映射成对应的HTML组件。选它的原因有两个:第一,保留了Markdown的易读性,文档里看到的就是语义清晰的标签;第二,它是文本到HTML的单向渲染,不依赖重型编辑器,团队任何人用任何编辑器都能改。
4.2 构建脚本与令牌注入
不管选哪种渲染管线,构建过程的核心都是三个步骤:读入Markdown文件、解析自定义组件、输出静态页面。我这边用一个简单脚本组织:
design:build这个命令拆开来看,做了这些事:
1. 读取 design/ 目录下的所有 .md 文件 2. 合并 design/tokens/*.md 中的令牌 3. 解析 pages/*.md 中的自定义标签 4. 映射为预先定义好的 HTML/CSS 组件 5. 生成 dist/ 目录下的 index.html为了让渲染效果和真实产品尽量接近,建议把全局样式单独提出来:字体、间距、颜色都从令牌文件里读取,渲染时统一注入CSS变量。这样原型和最终产品的视觉基础就是同一套变量,前端实现时甚至可以直接把这些变量复制进项目的样式文件里,从根上避免“两套风格”。
4.3 多端预览与分享链路
原型的一个特殊价值是“可交互”,所以渲染出来的页面最好支持不同设备宽度预览。我的做法是利用响应式样式:桌面端按1280px展示,平板端按768px,移动端按390px。Design.md里描述布局时尽量用流式布局和相对单位,这样切换宽度时页面不至于错乱。
分享方面,纯静态文件的好处是部署极轻。每次构建完把dist目录上传到静态托管服务,或者挂在版本控制的静态站点服务上,提交即更新。团队拿到一个链接之后,在手机、平板、电脑上分别打开,看看不同尺寸下的表现。这一步看似不起眼,但对那些“桌面和移动端表现差异大”的产品非常关键。
5. 团队协作中的Design.md工作流
5.1 设计资产的单一事实来源
Design.md让设计资产第一次可以被放进同一个版本控制仓库里管理。这里说的资产不只是Markdown文件,还包括渲染后的静态原型、关联的设计草稿、需求说明。团队约定一个主分支作为稳定设计基线,改版走分支,合并前必须完成原型验收。
最直接的便利是:任何人都能查看任意历史版本的原型长什么样。我遇到过不止一次“这个风格是什么时候变的”这种灵魂拷问,以前靠翻聊天记录也说不清,现在直接看提交历史,某个提交里改动了tokens/color.md,一行命令就定位到当时的设计决策。这个能力对产品快速演进的团队尤其重要。
5.2 三步评审闭环:提案、评论、验收
以前评审原型面对一张图,大家凭印象讨论,改来改去全靠记忆。用Design.md之后,我把评审流程改成三步:
- 提案:设计师或产品经理基于主分支拉新分支,修改对应的组件或页面文档。
- 评审:在合并请求里直接对Markdown的diff做评论,讨论的是具体某一行颜色、某一个间距值,而不是笼统的整体感受。
- 验收:评审通过后构建出原型链接,与会人员实际点击走查,确认没问题再合并。
这个流程最大的变化,是把“改图”变成“改文”。评论直接挂在具体行上,结论明确,不用反复对着图说话。设计师改一个令牌值,渲染后所有用到的地方同时更新,评审效率翻倍。
5.3 和前端开发的双向同步约定
Design.md跟开发侧的对接,是整个体系能否长期跑通的关键。我的经验是双向同步:设计侧把渲染环境里用的样式变量导出给前端;前端在实现组件时,如果发现某个令牌满足不了真实需求,就回来改Design.md里的对应定义,而不是自己在代码里另写一份。
为此我们定了一个硬约定:前端组件库的样式里,不允许出现未经Design.md登记的颜色值、间距值和字体值。刚开始执行有点别扭,时间长了反而觉得轻松——所有样式问题都回到同一条决策链上解决,不会出现“这个是历史遗留页面里的特例”这种无解局面。同步可以进一步自动化,设计令牌一改就导出成新的样式变量文件,前端那边拉一下就能看到。
6. 踩坑记录:Design.md项目里的几个真实教训
6.1 令牌膨胀:200个变量是怎么失控的
我第一个Design.md项目跑到第三周,令牌数量从刚开始的60个涨到了200多个。翻了一下,大量形如--color-blue-light-2、--space-12-5的一次性令牌混在里面。这个现象的本质是命名没有体系,大家遇到新场景就新造一个变量,最后令牌表成了垃圾场。
后来强制推行语义化规则才刹住车:每个令牌必须回答“它是给什么用的”,不允许出现纯描述“颜色名字加深浅”的令牌。语义化令牌的好处不只是可读性强,更关键的是它会倒逼你去复用,而不是无节制扩张。一旦允许--color-blue-2这种命名,很快就会出现--color-blue-2-light这种四不像。
6.2 语义化命名要第一天就定死
踩过一次最大的坑,是把一个渐变背景写成了--color-gradient-header。看起来没问题,后来品牌改版把渐变取消了,这个令牌留在仓库里三个月没人动,渲染出来的原型却一直带着过期样式。换成语义化命名比如--color-header-bg之后,改版时一眼就能识别“头部背景色变了”,不会漏改。
我的实际经验是,命名规则在项目第一天就要定下来,而且要在评审合并请求时盯着执行。一旦放水,之后要么花大力气全局替换,要么忍受一仓库的历史遗留命名。这个约束要写进团队约定里,新人加入时第一时间同步。
6.3 不可能所有页面都文本化,留好逃生舱
Design.md适合描述结构化界面,但总有一些页面天生不适合文本化表达,比如营销首页、复杂数据可视化、大段自定义视觉设计。我一开始试图把所有页面都写成Design.md,结果那些页面写了很久,渲染效果还是差口气,团队成员也不愿意维护。
后来定了一个规矩:Design.md覆盖核心业务流和标准组件,特殊页面允许“逃生舱”——单独用静态设计稿或代码实现,但必须挂进同一个设计仓库里统一管理。这样做既保住了体系的统一性,又不会让文本描述变成负担。体系不是用来绑架所有内容的,它应该服务于八成常规场景,剩下的两成保留灵活性。
6.4 用注释记录设计决策,等于免费做资产沉淀
最后分享一个小技巧。在Design.md里,我会在关键位置留下设计决策注释,解释“为什么这么定”,而不只是“定的是什么”。比如:
<!-- 决策记录:间距基数统一用4px,是为了让8/16/24/32等常用间距都落在基数格上, 浏览器缩放时视觉效果更稳定。曾尝试6px基数,但不支持一半数值,弃用。 -->这些注释对后来加入的同学特别有用。他们要理解某个设计为什么要这样做,不需要翻聊天记录,打开文档就能看到当年的权衡过程。时间越长,这份设计决策日志越值钱,它让Design.md不止是一套样式描述,而是真正沉淀下来的产品设计资产。
从尝试Design.md到现在,我最直观的感受是,它没有让设计流程变得更冷冰冰,反而把那些以前只能靠默契传递的风格细节,变成了可以讨论、可以追溯、可以复用的资产。如果你也在被“设计与实现两张皮”折磨,我建议先挑一个流程规整的页面试点写一份Design.md,渲染成原型跟旧流程的产出并排对比一次,差别会非常直观。试完记得回来交流一下你的目录结构是怎么规划的。