
1. 为什么我需要从别人的网站里“扒”出设计风格先说个场景我去年接手一个老项目客户指着隔壁竞品的官网说“就照这个风格改但我也说不清具体是啥风格”。和客户反复确认的过程非常痛苦——他说“高级感”你理解为深色他想要的其实是白底细线加大量留白。美术字说不清开发也不可能靠感觉一遍遍试。这时候我就意识到比起“模仿”不如把那个网站的设计语言当成一套数据来解构、量化、转录。而这一切的起点就是浏览器里最不起眼的两个工具DOM和计算样式最终落成一份叫DESIGN.md的文档。所谓的“提取设计风格”不是截图也不是调色板工具抓五个颜色而是通过分析目标网站的 DOM 结构和getComputedStyle返回的最终样式反向推导出字号体系、间距规律、圆角半径、阴影层级、色彩变量甚至组件在不同状态下的交互反馈。这套东西本身就是一套可以复用的设计规范。我也见过不少前端做这件事的方式F12 打开看到哪个颜色就复制哪个截两张图丢给设计师说“照着来”。但零散的色值拼不出规则只有把信息结构化才能形成可执行的产出。本文要讲的是用工程化手段把“我觉得很好看”变成“字号是 14/16/20/28 四档、主色是#2F6BFF、卡片阴影分三个层级”这样可量化的描述然后沉淀成DESIGN.md。这套方法适合谁前端开发、独立开发者、UI 设计师、以及一切需要“参考竞品但不想只靠眼睛看”的人。2. 从静态 DOM 里读出的视觉线索class、内联样式与语义结构2.1 为什么先看 DOM 而不是直接看样式表我见过很多人一上来就去翻 CSS 文件结果被几千行代码绕晕。真正高效的路径是反过来的先看 DOM 结构再去对样式。因为 CSS 文件是“配方”而 DOM 是和实际渲染结果一一对应的“成品”。从成品反推配方比从配方想象成品容易得多。打开 DevTools 的 Elements 面板第一件要做的事不是看样式而是“通读结构”。注意三个层面的东西命名体系class 的命名方式能直接反映设计系统。BEMBlock Element Modifier风格如card__title--large说明设计系统成熟度高原子化 CSS如flex items-center p-4说明是实用优先的方案还有大量无意义哈希类名如_3x9f2a说明经过了构建工具处理这时就得靠计算样式来兜底。语义标签用的是h1/h2还是清一色div按钮用的是button还是a标签模拟前者反映了一个团队对可访问性a11y的重视程度后者往往是历史包袱或偷懒的痕迹。内联样式如果内联样式占比高比如stylemargin-top: 32px说明这个页面可能是活动页或营销页设计规范执行得比较松散反之如果类名清晰且内联样式极少说明有严格的组件库约束。比如在一家 SaaS 公司的官网首页我观察到所有卡片都遵守类似card card--featured card--compact的命名规则配合少量内联间距基本就能判断它的组件体系运行在一套成熟的变量系统之上。这个判断直接决定了后续提取工作要跑到哪个深度。2.2 快速筛选关键节点的实操套路面对一个节点很多的页面不可能每个都看。我的习惯是三个步骤锁目标看head里引用的字体与图标库这是最外层的设计资产清单扫一遍正文区的标题层级h1→h3和按钮这是风格的核心表现区域看表单、弹窗、提示条这类状态组件因为它们的边框、阴影、过渡动画最能体现细节用心程度。如果页面是 React/Vue 写的通常会发现根节点下面挂着一个巨大的#root或#app然后是一层一层组件嵌套。这种结构下“设计意图”往往封装在组件里静态快照看不出状态变化。这就要配合后面的计算样式来挖掘。2.3 DOM 采集时的版权与合规边界这点必须单独拎出来说。提取设计风格是分析和学习不是搬运。你可以参考它的栅格比例、色板逻辑、字号阶梯但不要直接照抄对方的 SVG 图标、品牌插画、文案内容更不能把提取出来的 DESIGN.md 连带素材一起用于商用产品尤其当对方的设计资产受版权保护时。另外高频抓取或自动化脚本访问对方站点要控制频率遵守robots.txt的约定别把别人的站点拖垮。我们自己写脚本只做一次性分析不上规模。3. getComputedStyle 的深层使用为什么元素面板里的颜色有时候是假的3.1 元素面板的“Styles”页签和真实计算值之间的差异把元素面板当作权威是提取过程中的一个大坑。你明明看到color: #333把它记录进文档结果一上线发现颜色不对。原因在于DevTools 的 Styles 面板展示的是“匹配到的 CSS 规则”也就是样式表里关于这个元素的所有声明而真实渲染出来的效果是被getComputedStyle计算后的最终值它可能已经被同一优先级下的另一个规则覆盖也可能继承了父级或者被动画/过渡临时改写。举一个实际例子某个卡片文字在页面上显示为深蓝色rgb(31, 78, 199)但它的样式表里从头到尾没有这个值。打开 Styles 面板只看到.card-text { color: #222; }。原来颜色来自更上层的继承链——父容器设置了color: #1F4EC7子元素没有自己的颜色声明于是继承了。如果你只扒了.card-text规则抓到的#222就是彻头彻尾的错误数据。所以我的准则是一切以getComputedStyle的返回值为准。它反映的是浏览器经过级联、继承、层叠之后真正绘制到屏幕上的结果也是唯一和用户最终所见一致的数据源。3.2 在 Console 里快速提取目标的计算样式最简单的做法是在 Elements 面板选中目标元素然后在 Console 里执行getComputedStyle(document.querySelector(.card__title))它会返回一个CSSStyleDeclaration对象包含上百个属性。但直接用比较冗长我会封装成一个小函数只输出关心的属性const pickStyle (sel, props) { const el document.querySelector(sel); if (!el) return null; const style getComputedStyle(el); const result {}; props.forEach(p result[p] style[p]); return result; }; pickStyle(.card__title, [fontSize, fontWeight, lineHeight, letterSpacing, color, fontFamily] );也有直接在 Elements 面板右键 → Copy → Copy Computed Style 的方式能拿到全部计算值但信息量过大反而不适合快速决策。我一般只在研究某个具体组件时用完整复制做全站规范提取时还是以函数筛选为主。3.3 伪类状态与伪元素的计算样式提取普通元素的计算样式好拿但按钮的 hover、focus 状态以及::before、::after伪元素的样式是纯静态分析容易漏掉的部分。这些恰恰是判断一个设计系统精致程度的关键——一个连按钮 hover 色和过渡动画都精心设计的网站与一个交互反馈粗糙的网站风格质感差距巨大。提取伪类状态可以这样操作在 Elements 面板中右键点击元素选择 Force State 里的:hover、:focus、:active再在 Console 里执行getComputedStyle(...)。也可以直接用 JavaScript 触发const btn document.querySelector(.btn); btn.matches(:hover); // 配合 DevTools 的强制状态使用伪元素的计算样式和普通元素几乎是同一个 API 路径只是选择器要稍微麻烦一点。实际写的时候没法直接把伪元素传给querySelector需要用一个变通的方式const card document.querySelector(.card); const beforeStyle getComputedStyle(card, ::before); console.log(beforeStyle.content, beforeStyle.width, beforeStyle.height);getComputedStyle的第二个参数就是伪元素名字这个用法很多前端不知道但它对提取图标字体、装饰底纹、角标这些设计细节非常有用。4. 设计风格提取的实操路径从视觉采样到数据整理4.1 确立一个完整的采集维度清单每次提取设计风格前我会先定一套维度然后按维度去采集避免东一榔头西一棒子。通常分四层层级关注内容数据来源色彩层主色、辅助色、背景色、文字色、边框色、渐变方向计算样式 背景图分析字体层字体族、字号阶梯、行高、字重、字间距计算样式布局层栅格列数、容器最大宽度、内边距节奏、外边距节奏、区块间距盒模型数据组件层圆角半径、阴影深度、边框宽度、过渡时长、动画缓动曲线计算样式 动画面板每一层都需要尽量多的采样点。比如色彩层不能只看首页还应该看列表页、详情页、表单页、错误页。同一个按钮在明暗两种背景下呈现的颜色可能完全不同这种细节只有多页面采样才能发现。4.2 自动采集脚本一个可复制的 Console 工具集手动点击一个个元素太慢了我通常先在 Console 里跑一段小脚本批量收集信息。下面这段是一个用于提取全页面标题字号体系的示例const headingTags [h1, h2, h3, h4]; const result {}; headingTags.forEach(tag { document.querySelectorAll(tag).forEach(el { const style getComputedStyle(el); const key ${tag}|${style.fontSize}|${style.fontWeight}|${style.lineHeight}; if (!result[key]) { result[key] { tag, fontSize: style.fontSize, fontWeight: style.fontWeight, lineHeight: style.lineHeight, color: style.color, margin: [style.marginTop, style.marginBottom].join( ), count: 0 }; } result[key].count; }); }); console.table(result);跑完后一张清晰的“这个网站到底用了多少种标题样式”的表格就出来了。你会惊讶地发现很多网站的标题字号并不规律——同一层级在不同页面有不同大小这本身就是一个重要的设计系统诊断结论。同样的思路还可以扩展为“按钮提取器”“卡片提取器”“表单输入框提取器”核心套路是一样的定位一组同类元素批量读取计算样式汇总去重看分布的规律性。4.3 视觉采集过程中的干扰项处理字体加载前后不一致如果页面使用了 web font在字体尚未加载完成时拿到的fontFamily可能是 fallback 字体字号行高也会有轻微偏移。等字体加载完成后再采集或对比两次采集结果。视口宽度变化导致的流动性布局差异同一个元素在桌面端和移动端的 padding、字号可能根本不同。采集时固定视口宽度比如 1440px或者分别记录断点避免混合记录导致数据混乱。CSS 变量在计算样式中的表现getComputedStyle返回的是计算后的值而不是变量名。这意味着如果你想知道它原本用了--color-primary这个东西需要在 Styles 面板或源文件里另外查证。计算样式适合拿“结果”CSS 变量适合拿“抽象层”。5. DESIGN.md 的成文规范一份可用、可评审、可迭代的设计风格文档5.1 文档结构怎么搭才不变成色板粘贴簿很多人在“把收集到的数据整理成文档”这一步翻车因为做成了流水账红橙黄绿青蓝紫色值一列圆角列表一堆没有规则感。真正的 DESIGN.md 应该具备三个特征分层全局/组件/状态、定量用数字说话、可追溯引用来源节点。一份标准的 DESIGN.md 我习惯这样组织# DESIGN.md — [站点名称] 设计风格提取 ## 1. 设计原则概况 基于页面观感总结的 3-5 条风格特征如“整体走圆润亲和路线圆角偏大阴影柔和” ## 2. 色彩系统 ### 2.1 主色板 列出主色、辅助色、功能色附色值和用途说明 ### 2.2 文本色阶 主文本、次级文本、占位文本的颜色与使用场景 ## 3. 字体系统 ### 3.1 字体族 ### 3.2 字号与行高阶梯 建议用表格把 h1-h6、正文、辅助文字的字号/行高/字重对齐 ## 4. 间距与布局 ### 4.1 间距节奏 ### 4.2 容器与栅格 ## 5. 组件规范 ### 5.1 按钮体系 默认/hover/focus/disabled 四态的颜色、阴影、圆角、过渡 ### 5.2 卡片 ### 5.3 表单控件 ## 6. 动效与交互反馈 过渡时长、缓动函数、hover 位移幅度等 ## 7. 附录提取信息源页面清单及节点路径这个结构的核心是它把“风格”拆成了可执行的模块设计师看了知道怎么延续开发看了知道怎么实现。5.2 用表格量化关键参数别给读者一堆模棱两可的描述DESIGN.md 里的描述必须精确。比如阴影这一项不要写“阴影比较柔和类似材质设计的感觉”而要写成层级参数值卡片默认阴影0 2px 8px rgba(15, 23, 42, 0.06)卡片悬浮阴影0 8px 24px rgba(15, 23, 42, 0.12)弹窗阴影0 16px 48px rgba(15, 23, 42, 0.18)同理过渡动画写成场景属性时长缓动按钮 hoverbackground-color, box-shadow0.2sease-out卡片 hover 上浮transform, box-shadow0.3scubic-bezier(0.22, 1, 0.36, 1)弹窗进入opacity, transform0.25sease-in-out这些参数从哪来计算样式拿静态值动画参数要去 DevTools 的 Performance 面板录一段交互或者直接在 Console 里读getComputedStyle(el).transition拿默认值。5.3 记录“反模式”比记录“正模式”更重要提取风格时除了记录“人家是怎么做的”还要记录“哪里有分歧”。比如同是按钮首页的大按钮圆角是 8px弹窗里的小按钮圆角却是 4px这种不统一要写下来同样是卡片标题列表页用的是 16px/600详情页却是 18px/700这种差异可能导致后续实现时拿不准有的页面滚动条样式被改了有的页面还是默认样式。我习惯在 DESIGN.md 中加一个“不一致记录”的区块。这不是为了吐槽对方而是警示自己当你要复刻这套风格时这些不一致是“设计的一部分”还是“历史遗留的 bug”识别不出这一点照搬风格时很容易把这些矛盾也一起搬进自己的项目。6. 把 DESIGN.md 用在项目里的两种方式手工对照与自动校验6.1 手工对照审查组件开发时逐项核对最常见的用法是把 DESIGN.md 当作组件开发的设计验收文档。让设计师按文档里的规范评审开发完的界面字号是否一致圆角是否对得上阴影层次是否匹配交互反馈是否规范。这时候文档里越量化验收效率越高。我以前在一个团队里推行过“设计评审前先对 DESIGN.md”的流程开发实现完组件不要直接喊设计师看先对着文档自查一遍。能解决掉七成问题剩下的才是真正需要设计师做判断的事。这既是对设计规范的尊重也是把设计评审时间用在刀刃上。6.2 自动校验用 Puppeteer 做运行时检查更进阶一种玩法是把它做成自动化的视觉回归。拿 Puppeteer 写一段脚本加载页面读取一组关键元素的计算样式和 DESIGN.md 里的期望值做对比超出阈值就报 diff。这在大型团队里非常有用相当于把你的“风格规范”转换成可执行的测试用例const puppeteer require(puppeteer); (async () { const browser await puppeteer.launch(); const page await browser.newPage(); await page.goto(https://example.com, { waitUntil: networkidle2 }); const styles await page.evaluate(() { const btn document.querySelector(.btn-primary); const cs getComputedStyle(btn); return { fontSize: cs.fontSize, borderRadius: cs.borderRadius, backgroundColor: cs.backgroundColor, boxShadow: cs.boxShadow }; }); console.table(styles); await browser.close(); })();把这份脚本跑在多个页面上收集结果再与 DESIGN.md 对比就能快速判断目标网站的设计风格是否发生了大改动或者你的复刻版页面是否始终贴合原版风格。不过CSS 属性在计算样式里输出的是标准化格式比如颜色是rgb(47, 107, 255)阴影是多段空格分隔字符串和文档里写的 hex 格式不一样。写比对逻辑时要么两边统一格式要么用容差比较不要做字符串全等。7. 提取过程中遇到的反爬与“假风格”干扰7.1 动态加载和骨架屏造成的“样式缺失期”现在很多大型网站是 SSR 客户端水合或者是纯 CSR 页面。当你打开页面的一瞬间可能渲染的还是骨架屏真正的组件样式还没挂载。这时候读取计算样式拿到的是骨架屏的数据不是最终页面的数据。处理方式很简单等。在脚本里使用waitForSelector或轮询某个重要元素的文字内容确认页面渲染完成后再采集。比如await page.waitForFunction(() { const el document.querySelector(.card); return el getComputedStyle(el).display ! none; });也可以直接等document.fonts.ready确保字体加载完成。这是最容易被忽略的细节字体加载前后版式和颜色虽然不会变但行高和间距的计算结果会因为 fallback 字体的替换而完全不同。7.2 广告位、第三方组件对风格数据的污染有些页面上会混入第三方组件聊天插件、广告位、Cookie 同意弹窗、调查问卷浮层。这些组件通常是白标产品样式风格与站点主体完全不同。采集时如果不小心选中了它们得到的数据会把整个文档搞乱——圆角突然来个 20px背景色突然变成橙黄字体族变成系统默认字体。我的处理原则采集前先人工分辨区域归属把目标元素限定在站点自有组件的范围内。自动化采集脚本里尽量通过选择器和 DOM 位置过滤比如限定在main或#__next容器内排除iframe和固定的第三方浮层。7.3 遇到“假风格”页面如何判断要不要参考它还有一种情况有些网站的主题是卖主题的模板站或者用了现成的 UI 库但没有任何自定义。这类页面的设计风格提取出来本质上只是 Bootstrap、Tailwind 默认主题的参数不代表任何团队的设计智慧。参考价值很低。判断方法很简单看它的类名和计算样式是否高度趋同于某个公开 UI 框架的默认值。比如你会发现所有按钮的borderRadius都是0.375remfontFamily全是默认系统字体栈间距全是0.5rem的整数倍——这就是“框架原装风格”而不是“自定义设计风格”。这种页面提取出来做练习可以但别当成竞品设计分析报告来写。8. 进阶玩法CSS 变量反向追踪与设计 Tokens 的自动生成8.1 从计算值反查 CSS 变量定义getComputedStyle拿到的是最终计算值但如果只是想分析设计系统更高效的角度是摘出 CSS 变量本身。比如一个按钮背景是rgb(47, 107, 255)但在样式表里它引用的是var(--brand-primary)。直接从计算值入手你会丢失“原来整个设计系统里有一个叫品牌主色的变量”这个关键信息。反查的方法是在 Styles 面板里点击属性值旁的链条图标就能跳转到变量定义处。或者在 Console 里把目标元素涉及到的所有自定义属性都取出来const style getComputedStyle(document.querySelector(.btn-primary)); const brandColor style.getPropertyValue(--brand-primary); console.log(brandColor); // 拿到变量名对应的值更彻底的玩法是遍历整个页面的:root选择器把站点声明的所有 CSS 自定义属性及其值拉出来构成一个 token 清单。这几乎就是对方设计系统的“源代码备份”。提取出这些 token再对照它们在一个页面里的实际使用位置就可以反推出设计系统的组织逻辑。8.2 从 DESIGN.md 到设计 Token一份文档如何驱动跨平台落地DESIGN.md 的最终价值在于把零散数据结构化那再往前走一步它就应该是设计 Token 的原料。设计 Token 是平台无关的名字-值对比如color.brand.primary #2F6BFF。它可以直接映射到CSS 变量--color-brand-primary: #2F6BFF;Tailwind 配置colors.brand.primary #2F6BFFiOS 的Color扩展Android 的colors.xml所以我在写 DESIGN.md 时不只写值还会顺带标注每个值的 token 命名建议设计语义Token 名称值来源节点品牌主色color.brand.primary#2F6BFF首页顶部导航 logo 旁按钮品牌悬浮色color.brand.primaryHover#1F56DB任意按钮 hover 态主文本色color.text.primary#1F2937正文 p 标签商标辅助灰color.text.secondary#6B7280卡片描述文本这样一来DESIGN.md 不仅仅是“笔记”它变成了设计系统落地的中间层左手连着别人的网站右手连着你的跨端代码。8.3 实测案例从零到 DESIGN.md 的完整时间线最后分享一个实际执行过的流程供参考时间预期。目标站点是一个中型 SaaS 官网大约 6 个主要页面。我在固定视口 1440px、网络限速不快的环境下完成全部提取和文档工作大约花了两小时其中前半小时在梳理页面结构和确定目标元素中间一小时写脚本批量跑数据最后半小时整理成文档结构。如果目标站点只有一个页面且组件类型单一半小时就能搞定如果是大型电商站涉及多模板、多品牌分区那大概率要花上一个工作日甚至更久。执行顺序上我不建议边采集边写文档效率太低。正确流程是先定维度、建脚本、批量采集数据跑完再统一整理。这样思路不会被打断脚本也能重复利用。积累几套模板后面对新站点基本可以做到“换选择器就能复用”的状态。这个能力最大的价值在于它让我从一个“凭感觉抄作业”的开发者变成了一个能清晰解释“为什么这个设计看起来高级”的实践者。毕竟把视觉翻译成数据才是设计与工程协作的通用语言。