Design Systems with Storybook, v2
使用Storybook构建可扩展的组件库和设计系统。借助Tailwind CSS创建具有可组合样式和变体的可复用UI组件。利用MDX进行文档编写,设置交互测试,并添加数据获取功能,为你的组件库打造一个强大的开发环境。
0-Introduction.txt
1. 自我介绍与主题概述
- 讲者介绍:Steve 是 Tempero 的前端工程负责人。
- 主题:在 StoryBook 中构建设计系统(Design Systems)。
2. 设计系统的概念与发展
- 设计系统的多样性:
- 在大公司(如 Twilio),有专门的设计系统团队,包括设计师和工程师,组织内各部门都会参与贡献。
- 在小型初创公司,即使没有明确计划,最终也可能会形成设计系统。
- 设计系统的形成方式:
- 初始阶段:从共享几个可复用组件开始,这是一个合理且有益的想法。
- 逐渐演变:随着项目发展,共享组件增多,可能出现额外的代码库,最终形成设计系统。
- 两种类型:有意构建的设计系统 vs. 有机形成的系统。
3. 构建设计系统的理由
- 价值主张:
- 设计系统不仅仅是共享组件,还有许多其他益处。
- 许多书籍和资源聚焦于如何说服上级允许构建设计系统,而较少关注具体的构建方法和思路。
- StoryBook 的作用:
- 展示组件:可以在网站和应用中查看和审查组件的不同内容和状态,脱离应用上下文进行操作。
- 测试支持:支持组件测试、可访问性测试、视觉回归测试等,这些在完整应用环境中可能较难实现。
- 开发便利:将组件拆分为独立部分,调整不同设置,查看各种排列组合,简化开发过程。
- 维护可靠性:通过视觉回归测试和组件测试,确保组件行为符合预期,提升应用的可靠性和可维护性。
- 代码库规模问题:
- 随着代码库增长,修改某部分代码可能影响其他部分。
- 设计系统的策略不仅用于共享组件,还能增加应用的维护性和可靠性,确保一致性。
- 重构和样式调整变得更简单。
4. 本次课程内容与技术栈
- 实践内容:
- 技术栈:
- 使用 React,但不要求熟悉 React,重点在于样式、结构和工具集成。
- 初期使用普通 CSS 和 CSS 模块,后期使用 Tailwind CSS,以便专注于 StoryBook 和设计系统的核心部分,而不必手动编写大量 CSS。
- Tailwind 相关内容会提及,但其他样式系统同样适用。
- 课程资源:
- 提供代码仓库和课程网站,包含所有讨论内容和代码示例,方便跟进。
- 网站包含逐行讲解和讲者提到的笑话。
- 仓库提供基础模板,包含基本的 StoryBook 实现,避免从头开始设置。
- 部分设计系统概念已移植到课程中,并有对应的 Figma 文件供参考。
5. 设计系统的现实情况
- 常见场景:
- 很少有从零开始(Greenfield)构建设计系统的机会。
- 通常是先开发应用,发现复用需求后,才逐步构建设计系统,并进行重构。
- 课程目标:
- 兼顾新应用和现有应用的场景,尊重更常见的设计系统构建情况(即从现有应用中逐步构建)。
总结
- 本次课程将通过 StoryBook 探讨设计系统的构建,强调其在组件展示、测试、开发效率和应用可靠性方面的价值。
- 提供实践机会和资源支持,适应不同背景的学习者。
1-Storybook-Setup-Tour.txt
1. Storybook 的框架无关性
- 框架支持:
- Storybook 是框架无关的,支持多种前端框架,包括 React、Svelte、Vue、Web Components、Angular 甚至 Ember。
- 讲者所在公司 Temporal 使用 Svelte,并成功结合 Storybook。
- 本课程选择 React 作为示例,因为它是通用语言(lingua franca),但不会涉及太多 React 特定内容。
- 初始化 Storybook:
- 在代码库中运行
npx storybook@latest init 命令,Storybook 会根据 package.json 自动检测框架,并安装相应支持(如 @storybook/svelte、@storybook/react 等)。
- 配置过程简单,Storybook 会提供默认设置。
2. Storybook 沙盒(Sandbox)
- 快速创建:
- Storybook 提供沙盒功能,快速生成一个 Storybook 实例,方便尝试新功能或集成到代码库。
- 版本信息:
- 录制时最新版本为 Storybook 8.0.8(Storybook 8 上个月发布)。
- 本课程将讨论 Storybook 的一些新功能,并使用最新版本。
- 跨框架结构一致性:
- 不同框架下的 Storybook 结构基本一致,服务器和特性略有差异,但核心概念相同。
- 讲者建议在课程间隙查看不同框架下的 Storybook,体会其一致性。
3. TypeScript 的使用
- 原因:
- 本课程使用 TypeScript,目的是为设计系统提供类型安全和使用提示,确保组件按预期使用。
- 如果不使用 TypeScript,可以简单地忽略类型声明(即“去 TypeScript 化”)。
- 从 TypeScript 逆向工程到 JavaScript 比反过来容易,因此提供 TypeScript 示例更有普适性。
4. Storybook 的核心概念与功能
- Story:
- Story 是组件的一个版本,包含特定的参数、属性(props)、设置等。
- MDX 支持:
- MDX(Markdown + React)允许在 Markdown 文件中嵌入组件或 JavaScript 逻辑。
- 即使使用非 React 框架,MDX 仍会涉及少量 React,但从 Storybook 8 开始,React 不再是依赖项,而是通过构建过程集成。
- 组件文档与交互:
- Storybook 允许嵌入组件使用说明,展示如何使用组件及其属性(props)。
- 支持实时调整组件参数(如按钮样式、背景颜色、大小等),便于文档编写和设计系统推广。
- 开发效率:
- 在开发时,可以在一台显示器上编写代码,另一台显示器上查看 Storybook 中组件的不同排列组合,形成高效工作流。
5. 设计系统的哲学与实践
- 从按钮到页面:
- Storybook 支持从简单的按钮到整个页面的展示。
- 可以模拟登录和未登录状态的体验,便于与设计师协作。
- 上下文与数据模拟:
- 支持上下文 API(如 React 或 Svelte 的上下文),解决组件依赖上下文的问题。
- 提供 mock 服务器功能,模拟真实数据(如 API 调用),便于开发时查看组件在真实数据下的表现。
- 讲者所在团队将 Storybook 与 CMS(如 Contentful)集成,可以预览最新博客卡片组件效果,确保发布前的准确性。
- 部署流程中的作用:
- Storybook 是部署流程的重要部分,每次提交 PR 时,不仅会构建网站版本,还会构建 Storybook 版本。
- 设计师可以单独查看特定组件并提供反馈,避免应用整体上下文的干扰。
- 测试与可靠性:
- 支持行为测试(如点击事件是否按预期触发),可结合 Jest 等工具验证。
- 允许创建不同场景的 Story(如按钮文本超长情况),减少代码库中的反复修改(churn)。
- 支持自动化测试,确保不破坏边缘用例或引入回归问题。
- 视觉回归测试(Visual Regression Testing):
- 用于检测意外的视觉变化,确保修改只影响预期部分。
- 特别适用于代码库老化或重构场景(如讲者团队最近六周实施暗黑模式时),避免手动检查所有边缘情况。
- 预设与常见用例:
- 支持创建不同预设的 Story 文件,展示组件的常见用例,避免每次手动调整参数。
6. Storybook 的默认设置与启动
- 默认脚本:
- 初始化后,Storybook 会创建 NPM 脚本(如
npm run storybook 或 pnpm storybook),默认启动端口为 6006。
- 工具无关性:
- 支持多种包管理工具(npm、yarn、pnpm),用户可根据喜好选择。
- 初始界面:
- 默认 Storybook 包含导航栏、按钮等示例,甚至可以展示完整页面。
- 提供文档页面和单独的 Story,展示组件的不同状态和控制选项。
总结
- Storybook 是一个框架无关的工具,支持快速初始化和沙盒创建,适用于多种前端框架。
- 核心功能包括 Story、MDX、实时交互、上下文支持、数据模拟等,极大地提升了设计系统开发、测试和部署的效率。
- 通过视觉回归测试和自动化测试,确保代码库的可靠性和一致性,特别适用于重构和老代码维护场景。
2-Creating-Your-First-Stories.txt
1. 项目初始化与故事(Story)创建
- 项目介绍:
- 项目基于一个名为“Anthology”的设计系统(名字灵感来自“Story”的同义词)。
- 使用
pnpm 或 npm install 安装依赖,默认在端口 6006 上启动 Storybook。
- 初始状态下,Storybook 中没有故事(Stories),需要手动创建。
- 目标:
- 学习如何编写 Storybook 中的故事(Story),从按钮(Button)组件开始。
2. 按钮组件作为案例研究
- 按钮的复杂性:
- 按钮看似简单,但设计系统中可能包含多种变体(如渐变按钮、圆角按钮、带有菜单的按钮、输入框末尾的按钮等)。
- 增加暗黑模式(Dark Mode)、高对比度模式(High Contrast Mode)等需求后,变体数量成倍增加。
- 输入框(Input Field)是比按钮更复杂的组件,但按钮是一个很好的起点。
- 任务:
- 完善按钮组件本身。
- 为按钮组件编写故事(Story)。
3. Storybook 配置与文件结构
- 初始化配置:
- 运行
npx storybook@latest init 会自动配置 package.json、添加脚本、创建初始故事和 .storybook 目录。
.storybook 目录包含配置文件,支持自动检测构建工具(如 Webpack 或 Vite)。
- 故事文件命名:
- 故事文件通常命名为
.stories.*(如 button.stories.tsx)。
- 扩展名根据框架不同而异(如 React 使用
.tsx 或 .jsx,Svelte 使用 .ts 或 .js,Vue 使用 .vue)。
- 可通过配置文件自定义故事文件匹配规则(如支持
.cjs 扩展名)。
- 自定义配置:
- Storybook 支持高度自定义,适用于从其他系统迁移或特殊需求场景。
- 默认配置会查找所有 MDX 文件和以
.stories.* 结尾的文件。
4. Storybook 插件与功能
- 默认插件:
- 初始化时会添加一些插件(如
onboarding、essentials、chromatic、interactions、themes、accessibility)。
Chromatic:用于视觉回归测试,由 Storybook 团队支持,提供免费和付费版本。
accessibility 和 themes:支持无障碍性和暗黑模式,部分是合同要求。
- 配置文件:
preview.ts:控制 Storybook 预览界面的 UI(如 Canvas 区域),可添加全局依赖(如 Google Fonts)、设置全局样式或环境变量。
- 可用于全局依赖注入(如 mock 服务器 URL 在开发和生产环境间的切换)。
- 其他设置:
- 讲者关闭了遥测(telemetry)功能,仅为课程长期有效性考虑,正常情况下建议开启以获取更新通知。
5. 编写第一个故事(Story)
- 创建故事文件:
- 创建
button.stories.tsx,支持 TypeScript(需要额外配置,但带来类型安全优势)。
- 引入 Storybook 类型:
Meta 和 StoryObj,用于类型检查和智能提示(IntelliSense)。
- Meta 配置:
- 定义
meta 对象,包含故事标题(title)和组件引用(component),如 title: "Button" 和 component: Button。
meta 适用于整个故事集合,可通过类型检查确保配置正确,特别适合从旧版 Storybook 迁移的用户。
- 导出
meta 作为默认导出,用于后续类型定义。
- 定义 Story 类型与导出:
- 定义
Story 类型,基于 StoryObj 和组件类型(如 Button)。
- 使用 ES 模块导出单个故事(如
export const Primary: Story = {}),非默认导出的模块即为故事。
- 故事展示:
- 创建完成后,Storybook 中会显示故事,体现出层级结构(如
Button > Primary)。
- 导出多个故事(如
Secondary)会自动在界面中显示,支持驼峰命名自动转换为空格。
6. 框架无关性与组件故事格式(Component Story Format)
- 框架无关性:
- Storybook 从版本 6 开始支持组件故事格式(Component Story Format),在版本 7 和 8 中进一步完善。
- 不同框架(如 React 和 Svelte)在故事编写上的差异很小(如 React 使用
children,Svelte 使用 slots)。
- 属性传递:
- 故事中的
args 作为组件的 props 传递(如 args: { variant: "primary" })。
- 支持对象形式的 props 传递,允许使用 JavaScript 特性(如扩展运算符
... 实现默认 props 的组合)。
- 渲染自定义:
- 默认使用组件故事格式,但也支持
render 函数自定义渲染逻辑,适用于需要特定 JSX 或其他框架语法的场景。
7. 完善按钮组件与故事
- 初始问题:
- 初始按钮组件为空,需添加内容(如
<button>Button</button>)以验证故事是否正常显示。
- 属性继承与覆盖:
- Storybook 支持层级配置:
preview.ts:全局配置,适用于所有故事。
meta:适用于当前故事文件中的所有故事。
- 单个
Story:适用于特定故事,可覆盖上层配置。
- 示例:在
meta 中设置默认 args: { children: "Button" },为所有故事提供默认内容;单个故事可覆盖此属性。
- 自动分析属性:
- Storybook 会通过静态分析(支持 TypeScript 和 JSDoc)识别组件的 props(如
children),并自动生成控件(Controls)。
- React 和 Vue 框架支持较好,其他框架可能有限,但任何使用的 props 都会被识别。
- 后续课程将讨论如何自定义这些控件。
总结
- 本节介绍了如何在 Storybook 中创建第一个故事,从初始化项目到编写
Button 组件的故事。
- 强调了 Storybook 的框架无关性、配置灵活性以及组件故事格式的优势。
- 通过
meta 和单个 Story 的层级配置,展示了如何高效设置默认属性并覆盖特定故事的属性,为后续设计系统开发奠定基础。
3-Example-Design-Systems.txt
1. 当前进展与设计系统的起点
- 当前状态:
- 已经创建了一个按钮组件和对应的 Storybook 故事(Story),但尚未形成设计系统的雏形,仅验证了 HTML 的基本功能。
- 下一步目标:
2. 设计系统的哲学
- 设计师视角:
- 设计师关注设计语言和视觉一致性,确保用户体验的统一性。
- 工程师视角:
- 工程师(如讲者)关注如何实现设计,同时确保代码库的可维护性。
- 讲者作为工程团队领导,需制定组件标准和最佳实践,确保团队成员能快速理解和使用组件。
- 一致性目标:
- 设计上:不同类型的按钮(如普通按钮和下拉菜单按钮)应保持视觉一致性。
- 工程上:不同组件的 API 应保持一致,避免因组件类型不同而导致使用方式迥异。
- 团队规模与复杂性:
- 讲者所在小团队(7 人工程团队,4 人设计团队)尚能较轻松地协调一致性。
- 在大型公司(如 Twilio)中,情况更复杂。Twilio 通过收购 SendGrid 和 Segment 等公司,整合了多个设计语言,但最终统一为一个设计系统,并通过主题(Theme)切换实现差异化。
3. 示例设计系统与灵感来源
- 知名设计系统:
- Microsoft Fluent:用于 Visual Studio Code。
- Atlassian:用于 Jira。
- IBM Carbon:功能完善且健壮的设计系统。
- Twilio Paste:讲者曾深度参与其开发。
- 其他系统如 Shopify Polaris、GitLab Pajamas、Adobe Spectrum 等。
- Storybook 作为通用语言:
- 许多设计系统(如 IBM、Microsoft、Twilio Paste)使用 Storybook 作为展示和开发工具。
- 讲者建议通过查看这些设计系统获取灵感,直接借鉴其结构和实现方式。
- 设计系统的命名:
- 设计系统通常有独特的名称,如 Twilio 的 Paste、讲者团队的 Holocene、GitLab 的 Pajamas,建议为设计系统取一个有趣的名字。
4. 按钮属性命名与选择
- 按钮类型命名差异:
- 不同设计系统对按钮类型的属性命名不同:
- Microsoft Fluent 和 Atlassian 使用
appearance。
- IBM Carbon 使用
kind。
- Twilio Paste、Shopify Polaris、GitLab Pajamas、Adobe Spectrum 使用
variant。
- 按钮类型通常包括:Primary、Secondary、Danger、Ghost、Tertiary 等。
- 选择
variant:
variant 是最流行的命名方式,讲者决定在课程中采用 variant 作为按钮类型的属性名。
- 强调团队决策的重要性,
variant 并非唯一选择,可根据团队偏好选择 appearance 或 kind,但需避免不同组件使用不同属性名导致混乱。
5. 设计系统的挑战与前瞻性设计
- 设计令牌(Design Tokens):
- 设计令牌定义了设计系统的基本元素(如主按钮颜色、色谱),确保视觉设计语言的一致性。
- 对工程师而言,设计令牌在重构或视觉语言变更时至关重要。
- 品牌颜色变更的案例:
- Twilio 早期将品牌颜色红色(Red)用作主按钮颜色,但红色通常表示“危险”,导致用户误解。
- 在 Twilio Paste 设计系统初期,需在多个微前端中统一更改按钮颜色,凸显设计系统统一管理样式的重要性。
- 其他场景包括品牌颜色从一种蓝色变为另一种蓝色,设计系统需具备应对此类变更的能力。
- 防御性代码结构:
- 设计系统应提前考虑可能的变更,构建防御性代码结构,以便快速适应调整(如品牌颜色变更)。
- 讲者幽默地提到,这样可以“欺骗老板”说任务需要几周时间,但实际上只需 10 分钟完成。
6. 实现按钮样式与后续计划
- 初步实现:
- 使用 CSS 模块为按钮定义样式,包含预先编写的 CSS。
- 后续将讨论如何将设计令牌(Design Tokens)集成到代码中,以便更好地管理颜色和样式。
- 按钮类型:
- 讲者设计系统包含三种主要按钮类型(Primary、Secondary 等)以及 Ghost 按钮(不明显像按钮的按钮)。
- 其他挑战:
- 网页开发中常见问题:存在两种“按钮”,即真正的
<button> 和外观像按钮的链接(<a>)。
- 需确保两种元素在设计语言上保持一致,后续课程将讨论相关策略,但当前暂不深入。
7. 属性命名选择的个人经验与建议
- 布尔属性 vs 字符串联合类型:
- 讲者早期(2017 年)在构建设计系统时,倾向于使用布尔属性(如
isPrimary、isSecondary)定义按钮类型,因其在代码上看起来美观。
- 但此方式在实际开发中带来问题:难以使用扩展运算符(Spread Operator)传递属性,且 TypeScript 虽可避免部分问题,但整体体验不佳。
- 讲者在 SendGrid 构建设计系统时对此深有体会,最终后悔使用布尔属性,建议使用字符串联合类型(如
variant: "primary" | "secondary")。
- 团队决策优先:
- 强调属性命名(如
variant、appearance)无绝对对错,关键是团队一致性。
- 讲者不强制使用
variant,鼓励团队根据实际情况和偏好选择。
总结
- 本节从设计系统的哲学出发,讨论了视觉一致性与代码可维护性的平衡,强调了设计与工程团队协作的重要性。
- 通过分析多个知名设计系统(如 Microsoft Fluent、Twilio Paste),总结了按钮类型属性命名的常见选择,并决定采用
variant 作为课程实现方式。
- 提出设计系统需具备前瞻性,通过设计令牌和防御性代码结构应对品牌变更等挑战,并分享了布尔属性命名的教训,建议使用字符串联合类型。
4-Adding-Variants.txt
1. 引入 Variant 概念与 TypeScript 类型定义
- 目标:
- 为按钮组件添加
variant 属性,以支持不同的样式变体(如 Primary、Secondary、Destructive)。
- TypeScript 类型定义:
- 创建
ButtonProps 类型,定义 variant 作为联合类型:"primary" | "secondary" | "destructive"。
- 导出
ButtonProps 类型,以便后续高阶组件(Higher-Order Components)复用。
- 使用 React 的
ComponentProps 工具类型,继承 HTML 按钮的所有原生属性(如 id、className、aria-label 等),并添加自定义属性 variant。
- 设计系统底层理念:
- 设计系统的底层组件是对 HTML 元素的样式包装和便捷包装,不需重新实现 HTML 规范。
- 通过继承 HTML 原生属性,避免手动定义所有属性(如
className 是字符串),减少工作量。
- 框架(如 React、Svelte、Ember)通常允许任意属性传递,确保兼容性。
- 属性命名注意事项:
- 避免与 HTML 原生属性冲突(如
id),以及避免使用保留字(如 type 可能引发问题)。
variant、appearance 或 kind 都是可选项,需根据团队共识选择。
2. 添加 CSS 样式模块
- CSS 模块引入:
- 使用 CSS 模块为按钮定义样式,从课程网站获取预定义的 CSS 代码。
- CSS 包含基础按钮样式(
button)、次级按钮样式(secondary)、破坏性按钮样式(destructive)以及幽灵按钮样式(ghost)。
- CSS 模块优势:
- CSS 模块确保样式作用域,避免全局 CSS 中的命名冲突(如
secondary、disabled)。
- 不需使用 BEM 等命名规范,因模块化已解决冲突问题。
- 导入与应用样式:
- 通过 Vite 或 Webpack 导入 CSS 模块,底层会将其转换为 JavaScript 对象并对类名进行哈希处理。
- 将基础样式
styles.button 应用到按钮的 className 属性,初步实现按钮外观。
3. 实现 Variant 逻辑
- 属性解构:
- 从 props 中解构
variant,其余属性作为 props 传递给 HTML 元素(框架无关,部分框架可能使用 args)。
- 利用 TypeScript 的智能提示(IntelliSense)确保属性正确性。
- 动态类名应用:
- 初始化
className 为 styles.button(基础样式)。
- 根据
variant 值动态追加类名:如 variant === "secondary" 追加 styles.secondary,variant === "destructive" 追加 styles.destructive。
- 可使用数组拼接或字符串连接方式,讲者计划后续替换为工具库以简化逻辑。
- 实时预览:
- Storybook 支持热模块重载(Hot Module Reloading),无需刷新页面即可查看样式变化。
- 建议开发时将编辑器和 Storybook 并排显示,提高效率。
4. Storybook 控件与类型推导
- 自动生成控件:
- Storybook 通过静态分析(React Doc Gen)识别
variant 属性及其可选值(primary、secondary、destructive),自动在控件面板中生成下拉选择框。
- 即使故事中未显式使用
variant,Storybook 仍能基于 TypeScript 类型推导控件。
- TypeScript 与 JSDoc:
- TypeScript 提供类型安全和智能提示,适合设计系统开发,即使项目其他部分不使用 TypeScript。
- 替代方案:使用 JSDoc 注释,同样支持类型检查,且无需编译为 JavaScript,适用于 CI/CD 脚本等场景,但语法稍显冗长。
- 控件局限性:
- Storybook 无法为所有属性自动生成控件,后续课程将讨论如何提供提示以完善控件。
5. 更新故事以展示 Variant
- 设置 Variant:
- 在故事中为不同按钮设置
variant 属性,如 Primary 故事设置为 variant: "primary",Secondary 故事设置为 variant: "secondary"。
- 默认未设置
variant 时,按钮样式为 Primary(基于初始 CSS 样式)。
- 实时交互:
- Storybook 控件面板显示
variant 属性值,用户可实时切换查看不同样式。
- 控件状态会存储在 URL 中,刷新页面后仍保留当前选择,方便开发调试。
- 任务:
- 讲者要求参与者添加
Destructive 故事,展示破坏性按钮样式。
6. 故事数量与控件使用的权衡
- 问题提出:
- 参与者提问:是创建单独的故事(Story)展示每个变体,还是依赖控件(Controls)让用户手动切换?
- 例如,若有 5 个属性,每个属性有 6 个值,可能导致 30 个故事,数量过多。
- 讲者的决策依据:
- 测试需求:若需要进行视觉回归测试(Visual Regression Test)、无障碍审计测试(Accessibility Audit Test)或集成测试(如输入字段交互),则必须创建独立故事。
- 成本与收益:故事创建成本低,尽管可能造成视觉 clutter,但不影响性能。
- 初始保守到适度扩展:讲者早期对故事数量较为保守,但逐渐认识到多写故事的价值,尤其在需要自动化测试时。
- 具体实践:
- 常用变体(如
primary、secondary、destructive)创建独立故事。
- 特定状态(如
disabled)可能需要故事,确保测试覆盖。
- 尺寸变体(如
small、medium、large)也可能有独立故事,但不需为每种组合(如 destructive + large)创建故事,除非遇到特定问题。
- 触发条件:若发现 bug 或需要自动化测试,则添加对应故事,类似单元测试的添加逻辑。
- 总结:
- 讲者建议以“是否需要测试”和“是否遇到问题”作为是否创建新故事的依据,平衡控件使用和故事数量。
总结
- 本节通过 TypeScript 定义
variant 属性,结合 CSS 模块实现了按钮的不同样式变体(Primary、Secondary、Destructive)。
- 利用 Storybook 的控件功能和类型推导,实现了实时交互预览,并讨论了 TypeScript 和 JSDoc 在设计系统中的应用。
- 针对故事数量与控件使用的权衡,讲者提出以测试需求和问题驱动为依据,合理分配故事创建,强调设计系统开发中自动化测试的重要性。
5-Using-Clsx-to-Compose-Class-Names.txt
1. 添加 Destructive 按钮故事
- 目标:
- 为按钮组件添加一个
Destructive 变体的故事(Story),展示破坏性按钮样式。
- 实现:
- 在 Storybook 中创建
Destructive 故事,设置 variant 属性为 "destructive"。
- 由于按钮组件已传递给所有子组件,仅需定义独特属性(即
variant: "destructive")。
- 结果:
- Storybook 成功显示
Destructive 按钮样式,控件面板保留当前状态,方便切换查看。
2. 引入 Clsx 库以简化类名组合
- 问题:
- 随着组件复杂性增加,需根据属性(如
variant、size)动态组合多个类名(Class Names)。
- 手动拼接类名(如使用字符串插值或三元运算符)易导致额外空格等问题,增加维护难度。
- 解决方案:
- 使用
clsx 库,一个轻量级的类名组合工具(已包含在项目中,可通过 npm install clsx 安装)。
- 类似工具还有
cx,但 clsx 是讲者常用库,适用于 React 项目的类名动态生成。
- Clsx 功能:
- 接受多个参数(字符串、数组等),组合类名。
- 自动忽略 falsy 值(如条件不成立的类名),无需手动处理空格。
- 支持三元运算符、函数等多种条件逻辑,方便程序化组合类名。
- 重构代码:
- 将类名逻辑重构为
clsx 调用,初始类名为 styles.button。
- 使用条件逻辑添加变体类名,如
variant === "secondary" && styles.secondary 和 variant === "destructive" && styles.destructive。
- 验证重构后效果,Storybook 显示结果与之前一致。
- CSS 模块特性:
- CSS 模块本质上是一个对象,键为类名,值经过哈希处理。
- 可直接通过对象键访问类名(如
styles[variant]),若键不存在则返回 undefined,clsx 会自动忽略。
- 讲者提到未创建
primary 类名,因默认样式已包含在 button 中,避免冗余。
3. 是否允许用户传递自定义类名
- 问题提出:
- 是否应允许组件使用者传递额外类名(
className)以覆盖或扩展样式?
- 实现方式:
- 从 props 中解构
className,与其他属性(如 variant)分开处理。
- 在
clsx 中将用户传递的 className 放在最后,确保其优先级高于默认样式和变体样式(CSS 层叠规则)。
- 这样用户可添加额外样式(如边距、颜色等),实现组件扩展性。
- 支持的理由:
- 允许用户传递类名可满足边缘案例需求,如添加特定边距或样式调整。
- 对于基础组件(Primitive Components),这种扩展性较为实用。
- 反对的理由:
- 允许自定义类名可能导致样式不一致,尤其当团队成员未更新设计系统而直接覆盖样式时。
- 讲者提到团队中最“鲁莽”的开发者(可能是自己)可能滥用此功能,导致维护问题。
- 对于复杂组件(如包含多个子元素的
InputWithLabel),类名应用位置不明确(是应用到容器、输入框还是标签?),所有选择都可能引发问题。
- 讲者立场:
- 对于基础组件,可考虑允许传递类名,但对于高阶组件(由多个原子组件组成)则不建议。
- 讲者团队目前倾向于认为此做法是坏主意,因设计系统变更时难以确保一致性(用户可能已覆盖样式)。
- 最终决定取决于具体用例,讲者不做强制建议,仅展示此模式供参考。
- 建议:
- 若采用此模式,仅限于设计系统最底层组件。
- 若需确保一致性,可禁止用户传递类名,强制通过设计系统更新样式。
总结
- 本节通过添加
Destructive 按钮故事,完善了按钮变体的展示。
- 引入
clsx 库简化了类名组合逻辑,避免手动拼接带来的问题,并利用其条件逻辑特性动态应用样式。
- 讨论了是否允许用户传递自定义类名的利弊,讲者提出对于基础组件可考虑此模式,但需谨慎使用,尤其在复杂组件中避免,因其可能破坏设计系统一致性。
6-Adding-Controls.txt
1. 问题与机会:添加 Disabled 按钮控件
- 问题描述:
- 当前 Storybook 中有
Primary、Secondary 和 Destructive 按钮变体,但缺少 Disabled 状态的展示。
- 讲者希望通过控件(Controls)快速切换按钮的启用/禁用状态,而无需为
Disabled 单独创建故事。
- 解决方案的思考:
- 可以为
Disabled 创建独立故事,但按钮的 disabled 属性是 HTML 原生属性,不需要单独测试其功能,仅需在 Storybook 中快速查看样式效果。
- 直接在类型中添加
disabled 属性会导致 TypeScript 报错(需从继承类型中移除),但讲者不想深入讨论类型问题。
- 目标:
- 在 Storybook 中添加一个控件,允许用户切换按钮的
disabled 状态。
2. 使用 argTypes 定义自定义控件
- Storybook 控件局限性:
- Storybook 会根据组件的 props 自动生成控件,但无法深入 JSX 类型或 HTML 属性(如
disabled)进行推导。
- 解决方法:
- 使用 Storybook 的
argTypes 配置,定义额外的控件或对现有控件进行自定义。
- 在
argTypes 中添加 disabled 控件,指定其类型为 boolean。
- 实现与优化:
- 初始设置
disabled 控件后,用户需手动点击“Set Boolean”并选择 true 或 false,操作繁琐。
- 优化:为
disabled 设置默认值 false(通过 args),使其直接显示为开关控件,默认关闭(未禁用)。
- 默认值的意义:
- Storybook 默认不设置
true 或 false,因为 undefined 有时与 false 含义不同,允许用户区分 undefined、true 和 false 三种状态。
- 若明确需要
true 或 false,设置默认值可提升用户体验。
- 效果:
- 所有故事中均显示
disabled 控件,用户可切换查看禁用状态下的按钮样式(如 Destructive 和 Secondary 按钮)。
- 禁用状态下,鼠标光标显示“禁止”图标,符合预期。
3. 控件推导与框架差异
- 框架支持:
- 对于 React、Vue 等主流框架,Storybook 会尽力根据 TypeScript 或 props 推导控件。
- 如果通过
args 传递属性(如字符串),Storybook 会根据类型进行推断(如字符串控件)。
- 框架差异:
- 在 React 中,控件推导较为完善,无需手动定义大部分
argTypes。
- 在其他框架中,工具支持可能不足,需手动定义更多控件类型。
- 未来学习:
- 后续课程将探讨更多调整控件的方式,增强 Storybook 的交互性。
4. 自定义现有控件的显示方式
- 问题:
- 当前
variant 控件显示为单选按钮(Radio Buttons),但若组件有大量变体(如 42 种类型),单选按钮会显得臃肿。
- 解决方法:
- 在
argTypes 中自定义 variant 控件的类型,将其设置为 select(下拉菜单),替代单选按钮。
- 通过这种方式,控件界面更加简洁,适合大量选项的场景。
- 效果:
variant 控件变为下拉菜单,用户可从列表中选择 primary、secondary 或 destructive。
5. 控件与文档化的额外细节
- 必填属性标记:
- Storybook 中
variant 控件旁有星号(),表示其为必填属性。
- 讲者质疑其准确性,因代码中默认将
variant 设置为 primary,理论上应为可选属性。
- 调整与文档化:
- 通过将
variant 设置为可选属性(在类型定义中),并明确默认值为 primary,可移除星号标记。
- 这种调整为 Storybook 提供了隐式文档化(Implicit Documentation),用户可直观了解属性是否必填及默认值。
总结
- 本节通过 Storybook 的
argTypes 配置,解决了无法自动推导 HTML 原生属性(如 disabled)的问题,添加了布尔控件以切换按钮禁用状态。
- 优化了控件用户体验,通过设置默认值简化操作,并讨论了
undefined 与 false 的区别。
- 针对现有控件(如
variant),通过自定义类型(如 select)改善了大量选项时的显示效果。
- 最后提到控件配置与文档化的关系,强调通过类型和默认值调整可提升 Storybook 的信息传达效果。
1. 任务目标:添加按钮尺寸属性
- 任务描述:
- 为按钮组件添加尺寸(Size)属性,支持不同的按钮大小变体。
- 讲者已预定义了一些 CSS 样式,用户可自由调整尺寸或使用讲者提供的 CSS。
- 目的:
- 通过添加
size 属性,扩展按钮组件的功能,使其支持多种尺寸(如 small、medium、large)与现有变体(primary、secondary、destructive)组合使用。
2. 实现步骤:添加尺寸属性及样式
- 定义尺寸属性:
- 在按钮组件中添加
size 属性,支持 small、medium、large 三种尺寸。
- 讲者个人偏好使用完整单词(如
small 而非 sm),但设计系统可根据需求使用缩写(如 xl、xxl)。
- 设置默认值:
- 将
medium 设置为默认尺寸,符合讲者设计系统的常见用例。
- 用户可根据需求将
size 设置为必填属性或可选属性。
- 添加 CSS 样式:
- 从讲者提供的资源中获取
small、medium、large 对应的 CSS 类,添加到 CSS 模块中。
- 确保样式已加载到项目中,以便组件动态应用。
- 动态应用样式:
- 使用
clsx 库动态应用尺寸样式,通过 styles[size] 直接获取对应类名。
- 相较于手动拼接类名或使用多个条件语句(如
if 或数组 join),clsx 结合 CSS 模块的方式更为简洁高效。
- 从 props 中解构
size:
- 在组件中从
props 解构出 size 属性,确保其与 variant 等属性一起用于样式组合。
3. 验证与控件显示
- 热模块重载(HMR):
- 组件更新后,Storybook 通过热模块重载显示最新的控件和样式。
- 控件中显示
small、medium、large 选项,以及 disabled 状态切换。
- Storybook 控件效果:
- 在 Storybook 中,切换到
Small 故事时,small 单选按钮被选中;切换到 Large 故事时,large 单选按钮被选中。
- 若未传递
size(即 undefined),控件不选中任何选项,体现了 undefined 的独立意义。
4. 是否为每种组合创建故事
- 问题:
- 按钮有多个属性(如
variant、size、disabled),组合数量庞大(例如 3 种变体 × 3 种尺寸 × 2 种禁用状态 = 18 种组合)。
- 是否需要为每种组合创建独立故事?
- 讲者建议:
- 不建议为每种组合创建故事(如
Primary Large、Secondary Large 等),因数量过多且测试意义不大。
- 可选择创建代表性故事,如
Small 和 Large(Medium 为默认值可省略),以及一个 Disabled 故事以验证禁用状态。
- 具体故事数量和内容取决于验证需求,而非盲目覆盖所有组合。
5. 添加尺寸相关故事
- 实现:
- 在 Storybook 中添加
Small 和 Large 故事,分别设置 size 属性为 small 和 large。
- 未添加
Medium 故事,因其为默认值,已在其他故事中体现。
- 目的:
- 通过少量故事验证尺寸样式效果,同时借助控件切换查看其他组合。
6. 默认值与控件显示优化
- 问题:
- 当
size 或 variant 未定义(undefined)时,Storybook 控件不选中任何选项,可能影响用户体验。
- 讲者对此感到困扰,希望控件显示默认选中状态。
- 解决方法:
- 在
args 中设置默认值:variant 默认为 primary,size 默认为 medium。
- 设置默认值后,所有故事的控件会显示选中状态(如
medium 和 primary),提升一致性。
- 注意事项:
- 若
undefined 有特殊意义(如区别于默认值),则不应设置默认值。
- 若默认值仅为视觉优化需要,可在代码中程序化设置默认值(如未传递
size 时应用 medium 样式)。
- 讲者立场:
- 默认值设置与否不影响功能,仅影响用户体验,取决于个人偏好。
- 无论是否设置默认值,代码逻辑仍可确保未定义属性时应用默认样式。
总结
- 本节通过添加
size 属性(small、medium、large)扩展了按钮组件功能,并结合 CSS 模块和 clsx 动态应用样式。
- 在 Storybook 中添加了代表性故事(
Small 和 Large),通过控件切换验证尺寸与变体组合效果,避免为所有组合创建故事。
- 针对控件显示问题,提供了设置默认值(如
variant: "primary" 和 size: "medium")的优化方案,强调选择取决于 undefined 是否有特殊意义及个人偏好。
8-Adding-Tailwind.txt
1. 背景与问题:全局样式与 Storybook 的集成
- 问题描述:
- 目前为止,项目中使用的是 CSS 模块(CSS Modules)为按钮组件添加样式,这种方式是局部的、按文件导入的。
- 然而,实际项目中通常存在全局样式表(如 CSS Reset 或背景色定义),这些样式无法通过逐个文件导入的方式应用到 Storybook 中。
- 如果全局样式(如
index.css 中的背景色)未被 Storybook 引入,则预览界面将无法正确展示这些样式,缺失默认样式或 CSS Reset 的效果。
- 目标:
- 将全局样式表引入 Storybook,确保预览界面与实际应用一致。
- 使用 Tailwind CSS 作为示例,展示引入全局样式的通用模式。
2. 全局样式的通用性:不仅限于 Tailwind
- 适用范围:
- 本节讨论的内容不仅适用于 Tailwind CSS,也适用于任何全局样式表(如 CSS Reset)、Google Fonts 脚本或其他外部资源。
- 讲者选择 Tailwind CSS 作为示例,但强调所有步骤和模式对其他样式表同样有效,避免重复讲解类似流程。
- 核心思想:
- 无论使用何种样式系统或资源,引入全局样式的模式是相同的:将资源导入到 Storybook 的预览配置中。
3. Tailwind CSS 安装与初始化
- 前提条件:
- 讲者已将 Tailwind CSS 作为依赖安装在项目中。
- 如果用户在自己的代码库中操作,需执行以下步骤:
- 安装 Tailwind CSS:
npm install tailwindcss。
- 初始化 Tailwind 配置:
tailwindcss init。
- 补充说明:
- Tailwind CSS 的详细使用和配置不在本节讨论范围内,建议观看专门的 Tailwind 课程。
- 本节仅关注如何将 Tailwind(或任何全局样式)集成到 Storybook 中。
4. 引入全局样式到 Storybook
- 样式文件位置:
- 全局样式文件(如
index.css)通常位于项目的 src 目录或 public 目录中,具体位置因项目结构而异。
- 讲者的
index.css 文件位于 src 目录,并已配置好 Tailwind CSS 的相关内容。
- Vite 的 CSS 支持:
- 项目使用 Vite 作为构建工具,Vite 原生支持 CSS 文件的导入(包括模块化的
.module.css 文件)。
- 这种支持并非 Storybook 或 React 特有,Webpack 等其他构建工具也提供类似功能。
- 引入步骤:
- 在 Storybook 的
preview.ts 文件中添加导入语句:import '../src/index.css';。
- 此操作将全局样式文件引入到 Storybook 的预览环境中,确保样式应用于所有故事(Stories)。
5. 验证全局样式效果
- 测试方法:
- 为了验证样式是否正确引入,讲者在
index.css 中添加了一条明显的 CSS 规则:设置背景色为红色(background-color: red;)。
- 验证结果:
- 打开 Storybook 预览界面,背景色成功变为红色,证明全局样式已正确加载。
- 注意事项:
- 全局样式仅作用于 Storybook 的预览区域(Preview),不会影响其他非预览部分,确保样式作用域的隔离。
- 当前引入的 Tailwind CSS 主要提供的是 CSS Reset 效果,尚未涉及具体 Tailwind 工具类(Utility Classes)的使用。
总结
- 本节解决了全局样式无法自动应用到 Storybook 预览界面的问题,通过在
preview.ts 中导入样式文件(如 index.css)实现了全局样式的集成。
- 强调了该方法的通用性,不仅限于 Tailwind CSS,也适用于任何全局样式表或外部资源。
- 验证了样式引入的效果,确保 Storybook 预览界面与实际应用环境一致,为后续使用 Tailwind 的工具类或其他全局样式奠定了基础。
9-Adding-Dark-Mode-Theme.txt
1. Storybook 工具栏功能概览
- 工具栏选项:
- 刷新按钮:用于刷新预览界面。
- 缩放功能:支持放大和缩小预览内容(未详细讨论)。
- 明暗模式切换:Storybook 自带明暗背景切换功能,但目前仅影响背景色,用于对比组件在不同背景下的显示效果,不涉及实际主题逻辑。
- 网格视图:提供网格背景选项,讲者表示从未主动使用。
- 视口切换:支持不同设备视口(如移动端、桌面端),对响应式设计和视觉回归测试非常重要。
- 可强制故事(Story)以特定视口显示,避免因小调整导致移动端布局问题。
- 特别适用于设计系统的分子(Molecules)、有机体(Organisms)或整页布局的测试。
- 旋转与测量工具:类似 Chrome DevTools 的测量功能,支持旋转视口和检查布局。
- 轮廓与辅助功能插件:轮廓功能用于调试布局,辅助功能插件用于无障碍测试(后续讨论)。
- 全屏与链接:支持全屏查看故事或生成分享链接。
- 视口切换的重要性:
- 确保组件在不同设备上的显示一致性,防止因小改动(如热修复)破坏移动端布局。
- 讲者提到曾因 CEO 在 Android 手机上查看 UI 时发现问题而受到影响,强调视口测试的必要性。
2. 实现暗模式(Dark Mode)的两种哲学方法
- 方法 1:切换开关(Toggle):
- 提供用户手动切换明暗模式的按钮。
- 实现方式:在文档的
body 元素上添加 CSS 类或数据属性(如 class="dark" 或 data-mode="dark"),通过切换类或属性值控制主题。
- 讲者选择此方法,原因包括:
- 项目处于 Beta 阶段,暗模式尚未完全完善,切换开关便于测试和用户反馈。
- 允许用户根据偏好手动控制主题。
- 方法 2:媒体查询(Media Query):
- 根据操作系统设置自动应用暗模式(如
prefers-color-scheme: dark)。
- 优点是无需用户干预,自动适配系统主题。
- 讲者未选择此方法,但指出若仅使用媒体查询,Tailwind 配置无需额外调整。
- 混合策略:
- 讲者项目中结合了两种方法,既支持手动切换,也考虑了系统偏好,但以切换开关为主。
- 暗模式的实现比预期复杂,涉及许多边缘情况(Edge Cases),需要持续优化。
3. 配置 Tailwind CSS 以支持暗模式切换
- Tailwind 默认行为:
- Tailwind CSS 默认使用媒体查询(
prefers-color-scheme: dark)实现暗模式。
- 自定义配置:
- 讲者选择使用 CSS 类或数据属性而非媒体查询,以便手动控制主题切换。
- 在
tailwind.config.js 中设置 darkMode 为 'class',表示通过自定义选择器启用暗模式。
- 进一步自定义为数据属性(
data-mode="dark")而非类名(class="dark"),原因如下:
- 项目中已广泛使用
dark 类名表示特定样式(如深色卡片),直接用作主题切换会导致冲突。
- 数据属性避免了类名冲突,尤其适用于从单一模式向多主题过渡的代码库。
- 配置示例:
darkMode: ['class', '[data-mode="dark"]'],表示 Tailwind 使用指定的数据属性选择器来应用暗模式样式。
- 注意事项:
- 若使用自定义 CSS 而非 Tailwind,则无需调整
darkMode 配置,可直接通过 CSS 实现主题逻辑。
- 数据属性或类名的选择不影响功能,仅影响代码组织和冲突管理,用户可根据项目需求选择。
4. 安装与配置 Storybook 主题插件
- 安装插件:
- 使用命令安装 Storybook 主题插件:
npx storybook@latest add @storybook/addon-themes。
- 该命令不仅安装插件,还自动将其添加到
main.ts 文件中。
- 若手动安装:
npm install @storybook/addon-themes,然后手动添加到 main.ts 的 addons 数组中。
- 检查配置:
- 在
main.ts 中确认 addons 包含 @storybook/addon-themes,确保插件已启用。
- 装饰器(Decorators)概念:
- 装饰器是一种包裹故事(Story)的机制,用于为每个故事添加通用上下文或父组件。
- 使用场景:
- 为故事提供 React 上下文(如主题上下文或动画框架上下文)。
- 为故事添加包裹元素(如将组件放入列表项中)。
- 应用范围:
- 可在
main.ts 或 preview.ts 中全局应用,影响所有故事。
- 可在特定故事集的
meta 或单个故事中应用,仅影响特定范围。
- 配置主题装饰器:
- 在
preview.ts 中导入主题插件的工具函数:import { withThemeByDataAttribute } from '@storybook/addon-themes';。
- 添加装饰器到
decorators 数组,使用 withThemeByDataAttribute 配置主题切换逻辑:
defaultTheme: 'light':设置默认主题为明亮模式。
themes: { light: 'light', dark: 'dark' }:定义支持的主题及其名称(讲者实际项目中使用 day 和 night,此处简化)。
attributeName: 'data-mode':指定用于切换主题的数据属性。
- 配置完成后,装饰器会自动为每个故事应用主题切换功能,并通过数据属性控制主题样式。
5. 验证暗模式效果
- 预览效果:
- 配置完成后,Storybook 工具栏中出现主题切换选项,支持在明亮(Light)和暗黑(Dark)模式间切换。
- 初始切换可能因背景设置问题无效,需清除背景设置(Clear Selection)或重置工具栏状态。
- Tailwind 的作用:
- 当前暗模式仅影响背景色和文本色,Tailwind 主要用于响应式断点和主题切换语法(如
dark:small: 前缀)。
- 讲者项目中大部分 Tailwind 主题被自定义样式替换,未完全依赖 Tailwind 的默认主题。
- 其他优势:
- Tailwind 能识别未使用的 CSS 类,便于代码清理。
- 简化媒体查询编写,提升开发效率。
6. 关于多主题支持的讨论
- 是否需要超过两种主题:
- 讲者当前项目支持明亮和暗黑模式,计划增加高对比度模式(High Contrast Mode)以提升无障碍性。
- 项目设计语言以黑白色为主,辅以少量靛蓝色,因此针对色盲的无障碍调整面较小。
- 过去讲者曾参与项目,支持用户自定义主题,但表示不会向当前客户提及此功能以避免复杂需求。
- 多主题场景:
- 若公司通过收购拥有多个品牌,可能需要为每个品牌定制主题,导致主题数量增加。
- 主题数量和复杂度取决于业务需求和设计系统规模。
7. Storybook 自带背景切换与自定义主题的冲突
- 问题:
- Storybook 工具栏自带背景切换功能(明暗背景),与自定义主题切换功能可能冲突。
- 若之前设置了背景色,需清除背景设置(Clear Selection),否则自定义主题切换可能无效。
- 解决:
- 讲者通过关闭并重启切换功能解决冲突问题。
- 指出背景设置存储在 URL 中,可能导致持久性问题,需注意清除设置以确保自定义主题正常工作。
总结
- 本节通过 Storybook 的
@storybook/addon-themes 插件实现了暗模式切换功能,支持明亮和暗黑模式,并为每个故事应用主题逻辑。
- 讲者选择切换开关而非媒体查询作为暗模式实现方式,通过 Tailwind 配置数据属性(
data-mode="dark")避免类名冲突。
- 介绍了 Storybook 工具栏的多种功能,强调视口切换对响应式设计和视觉回归测试的重要性。
- 配置过程中使用装饰器为故事添加主题切换功能,并讨论了多主题支持(如高对比度模式)和潜在冲突问题(Storybook 自带背景切换),为设计系统的主题管理提供了实用方案。
10-Query-Params-Overrides.txt
1. 为按钮组件添加暗模式支持并迁移到 Tailwind CSS
- 当前问题:
- 按钮组件目前使用 CSS 模块(CSS Modules)进行样式定义,未使用 Tailwind CSS。
- 调整 Tailwind 配置对按钮样式无影响,因为尚未将其集成到组件中。
- 迁移目标:
- 将按钮样式从 CSS 模块迁移到 Tailwind CSS,以便利用 Tailwind 的工具类(Utility Classes)和暗模式支持。
- 迁移方式:
- 选项 1:直接应用 Tailwind 类:
- 在组件中直接使用 Tailwind 的工具类,例如
bg-indigo-500 设置背景色,hover:bg-indigo-400 设置悬停效果。
- 优点:Tailwind 提供智能提示(IntelliSense),简化伪类(如
hover)的编写,减少手动 CSS 代码。
- 最终可完全移除单独的 CSS 文件,将样式内联到组件中,提升开发体验。
- 选项 2:暂不完全重构:
- 讲者选择暂时不完全重构 CSS 模块,因为后续将展示一种更高效的变体(Variants)创建策略。
- 避免花费大量时间(35-40 分钟)手动重构,优先展示设计系统构建中的更好工具和方法。
- Tailwind 的优势:
- 即使在不需要 Tailwind 的场景中,讲者也常因其便捷性(如智能提示、伪类简化和内联样式)而最终使用它。
- Tailwind 能自动处理样式逻辑,减少开发者手动编写 CSS 的负担。
2. 使用参数(Parameters)强制暗模式
- 参数(Parameters)概念:
- 参数是传递给 Storybook 故事(Story)的设置,用于自定义故事的显示或行为。
- 可通过查询参数(Query Params)或代码配置实现,之前已偶然接触过初始参数。
- 强制暗模式的目的:
- 在特定故事中强制应用暗模式,便于视觉回归测试(Visual Regression Testing)和无障碍测试(Accessibility Testing)。
- 手动切换主题适合开发和调试,但不适合自动化测试套件,因为:
- 明亮模式下的颜色对比度合格,不代表暗模式下也合格。
- 测试需确保组件在不同模式下均符合设计和无障碍标准。
- 实现方式:
- 单个故事级别:
- 整个故事集级别:
- 可在
preview.ts 或故事文件的 meta 对象中设置参数,影响整个故事集。
- 语法相同,仅作用范围不同。
- 灵活性:
- 可为不同故事文件分别设置明亮或暗模式,或在同一文件中混合设置。
- 取决于参数是放在
meta(整个故事集)还是单个故事中。
- 优势:
- 强制暗模式的参数配置确保视觉回归测试和无障碍测试能覆盖不同主题。
- 开发时也便于查看特定模式下的组件表现,无需手动切换。
3. 使用参数强制视口(Viewport)
- 视口参数的目的:
- 强制故事以特定视口(如移动端)显示,便于测试响应式设计。
- 确保设计系统修改不会意外破坏特定断点的布局,避免因小调整导致生产环境问题。
- 实现方式:
- 查找视口名称:
- 若不确定 Storybook 内置视口名称,可通过以下方法查看:
- 在工具栏中切换到目标视口(如
mobile1)。
- 检查 URL 中的查询参数,参数值即为视口名称。
- 应用场景:
- 强制故事在不同响应式断点下显示,结合暗模式设置,确保设计系统在各种场景下均正常工作。
- 讲者强调此功能对避免因小改动引发生产问题(如被紧急呼叫修复)非常有用。
4. 总结与未来计划
- 当前进展:
- 本节通过参数配置实现了故事的暗模式和视口强制设置,为视觉回归测试和无障碍测试奠定基础。
- 按钮组件尚未完全实现暗模式支持,讲者计划在后续内容中作为更大叙事的一部分完成。
- 后续策略:
- 讲者提到将展示一种比长串 Tailwind 类更好的设计系统变体创建方法,提升构建效率。
- 暗模式和视口设置将结合更多工具和流程,确保设计系统在开发和测试中均表现一致。
- 核心价值:
- 参数配置(如
themeOverride 和 viewport)为 Storybook 提供了灵活性和确定性,确保组件在不同主题和设备下均可测试。
- 这些工具帮助开发者避免因设计系统调整导致的生产环境问题,提升开发和维护效率。
11-Color-Naming-Convention.txt
1. 颜色命名的挑战与问题
- 初期问题:
- 讲者作为团队首位前端工程师,与设计师合作开发应用的初期版本时,使用了通用样式工具(如 Tailwind CSS)中默认的颜色名称(如
red、blue)。
- 团队成员(包括讲者自己)在整个 UI 中广泛使用这些颜色名称,导致颜色与具体语义(如状态或功能)未解耦。
- 例如,
red 既表示“错误/取消”状态,也用于“必填按钮”等其他场景;blue 则有更多不同含义,增加了混淆。
- 设计团队介入后的挑战:
- 设计团队加入后,希望重新定义 UI 中颜色的含义。
- 由于代码中直接引用了具体颜色(如
red),修改颜色含义需要逐一查找和替换所有相关引用,增加了重构难度。
- 暗模式下的额外复杂性:
- 在引入主题(Theming)后,颜色名称可能不再适用。例如,
light-blue 在暗模式下可能不再是浅蓝色,导致命名与实际效果不符。
- 因此,单纯依赖颜色名称(如
green、blue)不再合适,需要一种新的命名哲学。
2. 语义化颜色命名的重要性
- 从颜色到语义的转变:
- 建议不再直接引用颜色名称,而是基于语义(Semantic Meaning)命名颜色。
- 常见的语义化颜色分类包括:
primary(主要)、secondary(次要)、warning(警告)、danger/error(危险/错误)、information(信息)、success(成功)。
- 语义化命名便于维护,例如修改所有“成功”状态的颜色时,只需调整
success 类或变量,而不会影响其他使用 green 的非成功场景。
- 避免颜色名称的直接引用:
- 讲者团队从代码库中清除了所有直接引用颜色(如
red、green)的代码。
- 虽然颜色本质上存在,但通过语义化命名或别名(Alias)间接引用,降低了重构难度。
3. 限制颜色选择以维护一致性
- 问题的根源:
- 如果团队成员(或自己在不谨慎时)能直接访问所有颜色、边框半径(Border Radius)或阴影(Box Shadow)等样式选项,必然会滥用这些选项。
- 例如,Tailwind CSS 默认提供多种颜色、8-9 种边框半径和阴影选项,团队会倾向于随意使用,导致 UI 风格不一致。
- 当尝试在代码库的不同部分复用样式时,可能会发现整体视觉效果不佳(“everything looks like trash”)。
- 设计系统的核心原则:
- 设计系统的一个重要目标是限制选择范围(Surface Area of Choice),通过约束可选样式确保代码库的可维护性和一致性。
- 通过隐藏或限制访问不必要的样式选项,避免团队成员随意使用非标准样式。
4. 实现语义化颜色的多种策略
- 策略 1:扩展 Tailwind 主题:
- 在 Tailwind CSS 中,可以通过扩展主题(Extend Theme)自定义颜色集,替换默认颜色。
- 将颜色映射到语义名称(如
primary、success),而非直接使用 red 或 blue。
- 策略 2:使用 CSS 变量:
- 讲者团队使用 CSS 变量来定义语义化颜色,并在代码中引用变量而非具体颜色值。
- 例如,定义
-button-primary-default、-button-primary-hover 等变量,映射到设计系统中的颜色。
- 好处是只需修改变量值即可全局更新样式,例如调整所有主要按钮(Primary Buttons)的颜色。
- 策略 3:隐藏底层颜色:
- 在代码库中隐藏实际颜色值,只允许引用语义化命名的变量或类。
- 例如,开发者只能使用
-button-primary-default,而无法直接访问对应的十六进制颜色值(如 #3B82F6)。
- 暗模式支持:
- 针对暗模式,讲者团队不依赖 Tailwind 的
dark: 前缀,而是通过切换 CSS 变量的值来实现主题变化。
- 例如,切换到暗模式时,
-button-primary-default 的值从明亮主题的颜色变为暗模式对应的颜色。
5. 与设计团队协作及自动化流程
- 设计与代码协作:
- 语义化颜色系统的成功依赖于与设计团队的协作。
- 如果设计团队在 Figma 中随意使用十六进制颜色代码(Hex Codes),则语义化努力会失效。
- 需要与设计团队共同制定颜色命名规范,确保设计稿中的颜色与代码中的语义化变量一致。
- 自动化工具:
- 讲者团队使用 Figma 插件(如
variables2css)从设计稿中提取颜色,并生成 Tailwind 主题或 CSS 变量。
- 步骤:
- 在 Figma 中定义语义化颜色(如
primary、success)。
- 使用插件生成对应的 CSS 变量或 Tailwind 配置。
- 将生成的变量集成到代码库中。
- 组件级颜色命名:
- 讲者团队进一步将颜色映射到组件级别,例如
button-primary-default、button-primary-hover、input-disabled 等。
- 这些命名的制定耗时两个月,因为找到一个不令人困惑的命名约定非常困难,且因项目而异,无通用解决方案。
- 代码中只允许引用这些组件级颜色变量,进一步限制了直接使用底层颜色的可能性。
6. 添加新主题的流程
- 扩展多主题支持:
- 若需添加更多主题(如高对比度模式或夏季模式),流程如下:
- 设计师在 Figma 中完成新主题的颜色设计。
- 使用
variables2css 插件生成新主题的 CSS 变量,忽略别名(Ignore Aliases),直接获取组件级颜色变量。
- 在代码中添加新主题的选择器(如
[data-theme="high-contrast"]),并为该选择器定义新的 CSS 变量值。
- 效率提升:
- 初始重构代码库以支持语义化颜色和 CSS 变量耗时 6 周。
- 添加新主题则非常高效,仅需约 6 小时,包括运行测试和少量手动验证。
- 一旦自动化流程和测试覆盖完善,新增主题几乎无需额外工作,只需更新变量值和 Storybook 故事。
7. 总结与价值
- 核心价值:
- 通过语义化颜色命名和 CSS 变量,讲者团队将颜色管理从直接引用具体颜色(如
red)转变为引用语义和组件级变量(如 button-primary-default),极大降低了重构难度。
- 限制样式选择范围是设计系统维护的重要原则,确保 UI 一致性和代码可维护性。
- 自动化与协作:
- 与设计团队协作制定命名规范,并通过 Figma 插件自动化颜色变量生成,减少手动工作。
- 在 Storybook 中设置自动化测试和故事展示,确保多主题支持的可持续性。
- 未来扩展:
- 系统设计支持快速添加新主题(如 3-4 个主题),通过 CSS 变量切换和自动化流程实现高效扩展。
- 讲者强调,这种结构化的颜色管理方式是设计系统成功的关键,尤其在需要频繁更新或扩展主题时。
12-Customizing-Tailwind-Colors-Font.txt
1. 自定义 Tailwind 颜色
- 目标:
- 将默认的 Tailwind 颜色(如
red、blue、green)替换为自定义的语义化颜色,以避免团队成员直接使用非语义化的颜色名称。
- 实现方式:
- 导入自定义颜色:
- 从一个 JSON 文件(
./source/tokens/colors)中导入自定义颜色集。
- 由于是完全替换(Wholesale Replacement)Tailwind 默认颜色,必须手动包含一些特殊颜色和 CSS 属性,例如
white、black(或自定义的 space-black)、transparent 和 currentColor。
- 替换而非扩展:
- 在 Tailwind 配置中,使用
colors 属性直接替换默认颜色,而非使用 extend 属性添加额外颜色。
- 原因:如果只是扩展,团队成员仍可能使用默认颜色(如
green),破坏设计系统的一致性。
- 其他设计令牌(Design Tokens):
- 讲者建议审查设计系统,限制其他样式选项的使用范围(Surface Area),例如只使用 3 种边框半径(Border Radii)、1-2 种阴影(Box Shadows)、少量字体和间距(Spacing)。
- 限制选择范围可避免设计师和工程师在不谨慎时(“bad day”)随意使用非标准样式,导致 UI 不一致。
- 结合 CSS 变量:
- 讲者团队同时使用 Tailwind 自定义颜色和 CSS 变量,两种策略结合。
- 自定义颜色基于从设计系统导出的文件生成 CSS 变量,并映射到语义化名称。
- 虽然具体文件有 300 行代码(未展示),但核心理念是相同的:通过语义化命名管理颜色。
2. 自定义字体
- 目标:
- 在 UI 中使用特定的字体(如
Inter),替换默认字体,并确保与设计系统一致。
- 字体选择:
- 讲者团队在 UI 中使用
Poppins 和 Inter,但由于设计师不喜欢 Poppins,最终决定全部切换到 Inter。
- 使用 FontSource 工具:
- 简介:
FontSource 是一个开源工具,提供多种免费字体(不仅限于 Google Fonts),并通过 npm 发布。
- 可通过
npm install 安装字体包,简化字体集成流程。
- 安装方式:
- 安装命令示例:
npm install @fontsource-variable/inter(针对可变字体,Variable Font)或 npm install @fontsource/inter(针对非可变字体)。
- 可变字体支持调整宽度(Variable Width)等属性。
- 集成到项目:
- 在 CSS 文件(如
index.css)中导入字体:import '@fontsource-variable/inter';。
- 通过 PostCSS、Vite 或 Webpack 等工具,字体文件会自动包含到项目中。
- 应用到 Tailwind:
- 在 Tailwind 配置中,通过
extend 属性扩展字体设置,替换默认的无衬线字体(Sans-serif):
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
},
}
- 讲者选择
extend 而非完全替换,因为希望保留 Tailwind 的等宽字体(Monospace)等其他字体选项。
- 其他方式:
- 如果不使用 Tailwind,可以直接在 CSS 中为
body 元素设置 font-family: 'Inter', sans-serif;。
- 两种方式均可行,取决于个人偏好或项目需求。
- FontSource 的额外优势:
- 延迟加载(Lazy Loading):
- 通过 JavaScript 导入字体时,若字体仅用于特定组件,可实现延迟加载(与组件的 Tree Shaking 结合),提升性能。
- 许可证灵活性:
- 开源项目中只能使用免费字体,而在公司网站等场景中可能使用专有字体。
- 使用
FontSource 和编程方式切换字体(如基于环境变量)简化了字体管理的复杂性。
3. 限制设计令牌选择的必要性
- 问题:
- 设计师和工程师在压力下或不谨慎时,可能会忽略设计系统的变量,直接使用非标准样式(如直接硬编码 Hex 颜色代码)。
- 即使是团队中最配合的工程师,也可能因设计师的要求而妥协,导致设计系统一致性问题。
- 解决方案:
- 通过代码和工具限制设计令牌的选择范围,确保团队成员只能访问语义化命名的颜色、字体等资源。
- 例如,完全替换 Tailwind 默认颜色,避免访问
red 或 green 等非语义化名称。
4. 针对 Sass 的方法
- 问题:
- 观众提问:如何在 Sass 中实现类似的设计令牌管理?
- 回答:
- 从 Figma 导出:
- 与 Tailwind 或 CSS 变量类似,可以使用 Figma 插件导出 Sass 变量或别名(Aliases)。
- 例如,导出组件级颜色变量(如
button-primary-default)到 Sass 文件。
- 设置变量:
- 在 Sass 中,可以定义变量(如
$button-primary-default: #3B82F6;)并在代码库中引用。
- 也可以结合 CSS 变量使用,引用方式根据技术栈略有不同。
- 核心理念:
- 无论是 Tailwind、CSS 变量还是 Sass,核心在于概念上的方法:使用语义化命名的设计令牌,而非具体技术实现。
- 变量的具体形式会因使用的技术(如 Sass、CSS)而异,但目标都是通过语义化管理和限制样式选择。
5. 后续计划与总结
- 当前进展:
- 已完成 Tailwind 颜色和字体的自定义,引入了语义化颜色和
Inter 字体。
- 项目中已设置了基础的颜色和字体,但仍需进一步重构以处理按钮变体(如
primary、secondary、ghost)。
- 未来方向:
- 讲者提到后续将讨论更好的策略来管理每个变体的样式,确保设计系统的高效性和一致性。
- 核心价值:
- 通过自定义 Tailwind 颜色和字体,并限制设计令牌的选择范围,确保 UI 一致性和代码可维护性。
- 使用工具(如
FontSource)和自动化流程(如 Figma 导出)简化设计系统集成,减少手动工作量。
- 无论使用 Tailwind、CSS 变量还是 Sass,语义化命名和限制选择范围是设计系统成功的关键。
13-Real-World-Design-Systems.txt
1. 设计系统随时间增长的复杂性
- 背景:
- 讲者回顾了之前讨论的内容,包括 Tailwind CSS 的主题化(Theming)、何时使用或不使用 Tailwind,以及按钮组件随时间演变的复杂性。
- 展示了来自实际代码库的按钮组件示例,指出两个关键问题:
- 类名数量的增长:随着按钮需要处理更多功能和变体,Tailwind 工具类(Utility Classes)的数量显著增加。
- 按钮变体数量:代码库中包含多种按钮类型(如
primary、secondary、destructive、ghost、table-header)以及多种尺寸(Sizes)。
- 问题:
- 即使 Tailwind 工具类对处理媒体查询(Media Queries)等场景很有帮助,但随着项目规模扩大,类名数量和复杂度会逐渐失控。
- 初步解决方案:
- 使用工具如
CLSX 来管理复杂的样式组合,但这仍不足以完全解决设计系统中的样式管理问题。
2. 引入 Class Variance Authority (CVA)
- 简介:
- 讲者推荐了一个名为
Class Variance Authority(简称 CVA)的库,用于管理设计系统中组件的样式变体。
- CVA 是一个非常有趣且强大的工具,支持以结构化的方式定义组件样式变体(如按钮的不同类型和尺寸)。
- 名字“Class Variance Authority”是《洛基》(Loki)剧集中的一个梗,意指“变体权威”。
- 核心功能:
- 允许开发者为组件(如按钮)定义多种变体(Variants),并以语义化的方式组织样式。
- 支持将样式逻辑从框架中分离出来,便于管理和复用。
- 价值主张:
- 框架无关性(Framework Agnostic):
- CVA 不依赖于任何特定框架(如 React、Svelte),可以在不同技术栈中复用。
- 讲者团队的代码库中,核心应用使用 Svelte 和 SvelteKit,而营销网站使用 React 和 Next.js。
- 此外,文档站点(Doc Site)也在 React 中,核心应用和营销网站的样式需求有重叠(如注册流程从营销网站跳转到应用)。
- 因此,使用框架无关的工具(如 CVA)可以轻松地在不同框架间共享样式逻辑,避免重复编写样式代码。
- 一致性需求:
- 无论是否计划构建设计系统,随着项目需求增长(如跨属性的一致用户体验),设计系统往往会自然形成。
- 讲者提到,即使文档站点可能不会立即迁移到新框架(如 Svelte),仍需确保设计系统能在多个框架间应用。
- 简化开发:
- 将样式逻辑与框架代码分离,使用普通 JavaScript 对象和函数定义样式,带来多项好处:
- 更易于单元测试(Unit Testing)。
- 更好的 IDE 智能提示(IntelliSense)。
- 整体开发体验更顺畅。
- 是否自建工具:
- 讲者认为,虽然理论上可以自己编写类似 CVA 的工具,但现有工具已经足够强大,开发者应专注于创造股东价值(Shareholder Value),而非重复造轮子。
3. CVA 的使用案例与功能详解
- 初步展示:
- 讲者计划通过实际代码展示 CVA 的用法,包括在核心应用和设计系统中的按钮组件示例。
- 强调 CVA 的核心理念在不同场景下基本一致。
- CVA 基础用法:
- CVA 支持传入字符串或类名数组(类似 CLSX),用于定义基础样式。
- 真正的强大之处在于“变体(Variants)”的概念,尽管“Variants”一词在不同上下文中可能有重载含义(如按钮类型和尺寸)。
- 例如,可以定义:
primary 按钮的样式类(如 bg-interactive、text-white),其中 interactive 是语义化颜色,表示可交互元素(如按钮、单选框的焦点色)。
secondary、destructive、ghost、table-header 等其他按钮类型的样式。
- 尺寸(Sizes)相关的样式类。
- 语义化颜色过渡:
- 讲者提到之前讨论的语义化颜色(如
interactive 用于可交互元素,danger 用于危险状态)。
- 团队仍在从旧的颜色引用(如
white)向语义化颜色过渡,初始选择带来的遗留问题需要大量时间解耦。
- CVA 的两个核心部分:
- CVA 函数:
- 用于创建样式变体(如
buttonVariants),定义不同变体(如 primary、secondary)和属性(如 size)的样式。
- TypeScript 类型支持(Variant Props):
- CVA 可以根据定义的变体生成对应的 TypeScript 类型。
- 例如,定义
variant 为 primary、secondary、destructive 和 size 为不同尺寸值,CVA 会生成一个类型,确保传入的 props 符合这些选项。
- 这使得代码在不同框架(如 React 和 Svelte)中保持一致,并提供类型安全。
- 跨框架复用:
- CVA 定义的样式逻辑可以与框架解耦,适用于 React、Svelte,甚至普通 HTML(需少量 JavaScript 支持)。
- 样式逻辑可以导出并在多个代码库间共享。
- 虽然某些交互逻辑(如下拉菜单在 Svelte 和 React 中的实现)可能需要框架特定的代码,但样式部分完全可复用。
- 学习曲线:
- 讲者承认 CVA 的概念可能需要多次理解(“two mental spiritual passes”),因此计划在 React 和 Svelte 代码中各展示一次,确保观众能掌握其用法。
- 在 React 中,类型处理可能比 Svelte 更简单,但整体逻辑一致。
4. 总结与价值
- 设计系统的必然性:
- 无论是否计划,随着项目需求增长(如跨框架一致性、跨属性用户体验),设计系统往往会自然形成。
- 讲者团队通过工具和策略(如 CVA)应对这些需求,确保样式逻辑的可复用性和一致性。
- CVA 的核心优势:
- 框架无关性:支持在 React、Svelte 等不同框架间共享样式逻辑。
- 样式分离:将样式逻辑从框架代码中解耦,使用普通 JavaScript 对象和函数定义,提升开发体验和测试能力。
- 类型安全:通过 TypeScript 类型生成,确保代码的正确性和一致性。
- 未来展示:
- 讲者计划通过实际代码示例(核心应用和设计系统中的按钮组件),进一步展示 CVA 的用法和优势。
- 强调 CVA 在真实世界设计系统中的应用价值,尤其是在多框架、多属性项目中。
14-Class-Variance-Authority.txt
1. 使用 Class Variance Authority (CVA) 管理 Tailwind 样式
- 背景:
- 讲者提到不想现场输入大量 Tailwind 类名(如果观众感兴趣,可以观看讲者的其他课程),因此展示了一个已经重构的按钮组件。
- 该按钮组件基于之前提到的“Anthology 设计系统”,将 CSS 模块重构为 Tailwind 样式。
- Tailwind 的优势与使用方式:
- Tailwind 不同于 Bootstrap 等传统框架,它并非固定主题,而是提供默认主题,开发者可以完全替换为自定义主题。
- 讲者喜欢 Tailwind 的原因:
- 简化伪选择器和媒体查询:如
focus:、disabled: 等工具类,处理焦点状态、禁用状态等,无需手动编写 CSS 伪选择器。
- 响应式设计与暗模式:无需手动编写媒体查询,直接使用 Tailwind 提供的工具类。
- 组选择器(Group Selector):支持父元素悬停时影响子元素样式(如
group-hover:),方便处理卡片悬停效果等场景。
- Tree Shaking:Tailwind 能自动移除未使用的 CSS,优化性能。
- 团队使用 Tailwind 更多是作为“工具刀”(Utility Knife),用于简化繁琐的 CSS 编写,而非完全依赖其默认样式。
- CVA 实现按钮变体:
- 使用 CVA 定义按钮的基础样式(Base Styles)以及变体(Variants)。
- 变体示例:
primary:使用之前引入的语义化颜色 primary(而非硬编码如 purple),通过主题文件控制具体颜色值。
secondary:白色背景,带有特定边框和激活颜色。
destructive:破坏性操作按钮样式。
- 默认变体(Default Variant)和默认尺寸(Default Size)也可以设置。
- 优势:
- 如果需要更改
primary 颜色,只需修改主题文件,所有使用该颜色的组件都会自动更新。
- 可以进一步抽象为更语义化的名称(如
interactive),指向按钮的主要颜色,增强设计系统一致性。
2. CVA 与 TypeScript 类型的集成
- 类型自动推导:
- CVA 可以从定义的样式变体中自动推导出 TypeScript 类型(如
ButtonProps)。
- 例如,新增一个变体时,类型会自动更新,无需手动维护。
- 文件组织方式:
- 讲者展示了两种组织方式:
- 将 CVA 定义直接写在组件文件中。
- 将 CVA 定义单独放在一个文件(如
button-variants.ts)中,以便跨代码库共享。
- 推荐第二种方式,使用独立文件(如
button-variants.ts),并导出变体和类型(如 export const variants 和 type ButtonVariants)。
- 类型应用:
- 在按钮组件中,导入 CVA 定义的变体和类型,将其与 HTML 按钮的默认属性结合(如
ButtonProps & ButtonVariants)。
- 这样,组件类型包含 HTML 按钮的所有默认属性以及设计系统支持的变体属性,且无需频繁更新类型定义。
- Storybook 也会自动识别这些类型,方便调试和文档化。
- 类型安全:
- 使用 CVA 后,传入无效的变体值时,TypeScript 会报错(显示红色波浪线),避免拼写错误或无效配置。
- 这种类型安全在现场编码或团队协作中尤其有用,避免因小错误导致问题。
3. 添加尺寸(Size)变体
- 实现尺寸变体:
- 在 CVA 定义中添加
size 变体,包含 small、medium、large 等选项。
- 每个尺寸定义对应的 Tailwind 类,如内边距(
px-4 py-1.5)等,基于设计系统中的 Figma 规范。
- 设置默认尺寸(如
medium),确保未指定尺寸时有回退值。
- 自动更新类型:
- 添加
size 变体后,ButtonVariants 类型自动更新,包含 size 属性(可选,值为 small、medium、large,或 null/undefined 回退到默认值)。
- 与 Figma 设计一致性:
- 尺寸和字体大小等样式基于团队产品设计师在 Figma 中定义的变量,与主题文件保持一致。
- 当设计师更新 Figma 变量时,只需导出并更新主题文件,代码中的样式会自动同步,无需额外调整。
4. 样式管理与视觉回归测试
- 简化文件结构:
- 使用 CVA 后,删除了单独的样式表文件,所有样式逻辑集中在 CVA 定义中,减少文件数量。
- 视觉回归测试(Visual Regression Testing):
- 讲者计划后续设置视觉回归测试,验证样式变更是否符合预期。
- 测试价值:
- 捕获意外变更:如果某些样式不应变更,测试会发现问题。
- 验证有意变更:确保变更影响了所有相关组件(如按钮在警告、表格等场景中的表现),避免遗漏使用场景。
- 在团队的工作流程中,每次 PR 都会运行视觉差异测试(Visual Diffs),确认变更是否符合预期,特别是在网站项目中。
- 测试还能发现未预料到的影响(如表格标题中按钮尺寸变大不符合预期),在客户反馈前解决问题。
- 测试理念:
- 讲者强调,测试是不可避免的,区别仅在于:
- 手动测试(耗时且易出错)。
- 自动化测试(如视觉回归测试)。
- 客户通过提交 Bug 报告进行“测试”(最差情况)。
- 自动化测试是最佳选择,确保质量和效率。
5. 更多 CVA 示例与可维护性
- 徽章(Badge)组件示例:
- 讲者展示了另一个使用 CVA 的组件——徽章(Badge),定义了默认样式和变体(如
primary)。
- 借助多光标编辑等工具,修改 CVA 定义非常简单。
- 示例场景:如果暗模式下徽章对比度不足(假设),可以快速调整所有相关样式(如暗模式下的对比度)。
- 大规模变更的简易性:
- 使用 CVA 后,大规模样式调整(如调整所有变体的对比度)变得易于管理。
- 如果采用更抽象的“Galaxy Brain”策略,可以进一步将样式映射到单一类或变量,简化变更。
6. 颜色管理与主题化进阶
- 颜色文件(colors.ts):
- 讲者展示了团队的
colors.ts 文件,与之前提到的类似,包含大量颜色(如 blue、cyan、green),用于状态显示等场景。
- 虽然颜色数量多于理想值,但通过抽象,代码库中禁止直接使用具体颜色(如
purple),而是映射到语义化名称(如 primary)。
- CSS 变量与 Tailwind 映射:
- 颜色定义为 CSS 变量(如
-color-primary),与 Tailwind 类(如 bg-primary)双重映射。
- 既可以通过 CSS 变量引用(如
var(--color-primary)),也可以使用 Tailwind 类。
- 讲者编写了辅助函数简化颜色值获取,并通过 TypeScript 接口提供类型安全,避免拼写错误导致的
undefined 问题。
- 主题切换:
- 支持多主题(如暗模式),通过切换 CSS 变量值实现,而无需修改大量代码。
- 例如,暗模式下使用
dark: 前缀(Tailwind 默认方式),或直接引用 CSS 变量并覆盖值。
- 如果颜色在多个主题中相同,只需定义基础 CSS 变量,主题切换时仅覆盖差异部分,减少重复定义。
- 支持多模式:
- 可以轻松添加更多模式(如 6 种不同主题),只需切换对应的键值对(Key-Value Store)。
- 使用 Tailwind 的动机:
- 将 CSS 变量映射到 Tailwind 类主要是为了开发体验(Ergonomics),如智能提示(IntelliSense)显示所有颜色选项及其预览。
- 如果不使用 Tailwind,仍然可以直接使用 CSS 变量,方法并无特殊要求,Tailwind 只是为了提升开发效率。
- 主题一致性:
- 所有变量定义在 CSS 文件的
:root 选择器中,暗模式通过 body 类切换。
- 定义了语义化类(如
surface-primary、surface-secondary),确保开发者不会直接引用具体颜色。
- 调整主题或颜色时,只需修改一个文件,变更会自动应用到整个代码库。
7. 自动化验证与未来计划
- 自动化测试:
- 讲者提到后续会结合 Storybook 进行测试,验证样式变更是否正确应用,避免意外错误。
- 自动化测试套件确保主题切换或样式调整后,UI 表现符合预期。
- 总结:
- 使用 CVA 管理样式变体,结合 Tailwind 和 CSS 变量,显著提升了设计系统的可维护性和一致性。
- 通过自动化工具(如视觉回归测试)和类型安全(如 TypeScript),减少手动错误,提升开发效率。
- 语义化命名和抽象(如禁止直接引用颜色)确保代码库与设计系统(如 Figma)保持同步,变更成本极低。
15-Using-MDX-with-Storybook.txt
1. 设计系统文档化的重要性
- 背景:
- 讲者提到要进行一个小的“支线任务”(Side Quest),讨论如何通过文档化来增强设计系统的价值。
- 文档化不仅帮助开发者理解设计系统的使用规则,也促进设计与开发之间的协作。
- 设计系统的文档化目标:
- 明确设计系统的使用场景和规则,例如 Twilio Paste 等设计系统会详细说明“何时使用某个组件”。
- 文档化不仅是给设计师看的(作为他们的目标受众),开发者也需要将其转化为网站或代码实现。
- 通过 Storybook 等工具,设计系统成为设计与开发协作的桥梁(如 Microsoft、Atlassian、Twilio 的设计系统都在 Storybook 中展示)。
- Storybook 的价值:
- Storybook 提供强大的功能,帮助开发者记录、展示和测试设计系统组件。
- 讲者计划先介绍 Storybook 的文档化功能,然后再回到之前提到的“Callout 组件”,以展示完整功能。
2. 使用 MDX 在 Storybook 中创建文档
- 基础用法:
- 讲者之前的工作主要集中在
stories.tsx 文件中,用于定义组件的故事(Stories)。
- 现在介绍
MDX 文件(Markdown 扩展格式),用于创建更丰富的文档。
- 示例:在
button.mdx 文件中写入简单的 Markdown 内容(如 Hello world! I am a button),然后在 Storybook 中查看。
- 初始效果较为简单,仅显示 Markdown 文本,未体现太多价值。
- MDX 的特性:
- MDX 是 Markdown 的扩展,支持在 Markdown 中嵌入 JavaScript 和 JSX(React 语法)。
- 讲者提到,即使项目使用 Svelte 或 Vue,MDX 仍基于 React 语法(Storybook v8 已内置 React,无需额外安装依赖;v7 则需要)。
- 如果尝试在 MDX 中使用 Svelte 或 Vue 语法会导致问题,但 React 语法易于处理。
- MDX 文件能被 Storybook 自动识别并加载(除非手动修改默认配置)。
- 嵌入组件:
- 在 MDX 中可以导入并嵌入组件,例如导入
Button 组件并在 Markdown 中渲染 <Button>I am a button</Button>。
- 效果:在 Storybook 中,Markdown 文档内会显示实际的按钮组件,增强文档的表现力。
- 讲者提到其核心网站使用 Svelte,但仍通过类似方式嵌入组件,展示文档与交互结合的强大功能。
3. Storybook 的 Docs Blocks
- 简介:
- Storybook 提供一组称为“Docs Blocks”的工具(讲者简称为“Blocks”以避免口误),用于增强 MDX 文档功能。
- 这些 Blocks 帮助开发者创建更结构化的设计系统文档。
- 常用 Blocks 示例:
- Meta:用于定义文档的元数据,关联到特定组件的故事集。
- 示例:导入
Meta 和 ButtonStories(从 button.stories.tsx 中导出),然后在 MDX 中使用 <Meta of={ButtonStories} />,将文档与按钮故事关联。
- 效果:文档会与按钮故事归类到同一位置,显示在 Storybook 的“Docs”标签下。
- Title:设置文档标题,提供额外的样式支持。
- Primary:展示“主要故事”(Primary Story),即
stories.tsx 中导出的第一个故事(与命名无关,按导出顺序确定)。
- 示例:使用
<Primary /> 并添加说明文字,展示主要按钮样式及其使用说明。
- Stories:展示所有故事(Stories Plural),在一个页面中列出所有变体(如
Primary、Secondary)。
- 示例:使用
<Stories />,显示所有按钮变体,方便一次性查看。
- Controls:展示组件的属性控制面板,显示所有 props、类型和默认值。
- 示例:使用
<Controls />,列出按钮组件的所有 props(如从 Class Variance Authority 自动生成的类型),并允许实时调整。
- 这些信息直接从 TypeScript 类型中提取,减少手动维护成本。
- 优势:
- 使用 Class Variance Authority (CVA) 生成类型后,这些类型会自动反映到 Storybook 的 Controls 中,进一步展示如何使用组件。
- 文档与交互结合:调整 Controls 中的属性会实时更新 Primary Story 的展示效果。
- 高度自定义:开发者可以基于 MDX 编写自定义展示逻辑,支持任何 JavaScript 和 React 组件,满足团队对设计系统文档的特定需求。
4. 文档化的实际案例与协作价值
- Twilio 设计系统示例:
- 讲者展示了 Twilio 设计系统在 Storybook 中的文档化示例,包括使用指南(如“何时使用按钮”、“按钮与锚链接的区别”)。
- 这种文档化方式为设计与开发提供了清晰的沟通工具,确保团队对组件使用有一致理解。
- 自定义与标准化:
- MDX 允许团队根据自身需求标准化设计系统的沟通方式,文档内容和样式可以完全自定义。
- 讲者鼓励团队根据具体项目需求调整文档风格,Storybook 提供的基础功能只是起点。
5. 特殊用例:展示设计令牌(Design Tokens)
- 背景:
- 讲者展示了如何在 Storybook 中使用 MDX 文档化设计令牌(如颜色、排版、图标),这是设计与开发协作的重要部分。
- 示例:在顶层创建
colors.mdx 文件,用于展示颜色调色板(Color Palette)。
- 实现方式:
- 导入 Storybook 提供的
ColorPalette 和 ColorItem 组件,用于展示颜色。
- 定义元数据
<Meta title="Colors" />,不与特定故事关联,单独作为设计令牌文档。
- 使用
<ColorPalette> 和 <ColorItem> 展示具体颜色(如蓝色),包括标题和颜色值。
- 动态生成颜色文档:
- 讲者进一步展示了如何从之前定义的
colors.ts 文件导入颜色数据,遍历对象生成颜色调色板。
- 示例:使用
Object.entries(colors).map() 动态创建多个 <ColorItem>,展示所有颜色名称和值。
- 工作流程:
- 颜色数据源于 Figma 导出的 CSS 变量或 JavaScript 对象(如 Tailwind 主题)。
- 通过导入和映射,自动生成调色板,避免手动硬编码。
- 优势:可以添加额外说明(如“何时使用某颜色”),增强文档实用性。
- 其他设计令牌:
- 类似地,Storybook 提供
Typeset 组件用于展示排版(Typography)。
- 提供
IconGallery 组件用于展示图标集(Icon Set),方便开发者快速查看所有图标及其名称。
- 如果不满意 Storybook 提供的默认组件,开发者可以自定义替代方案(如自定义颜色调色板)。
- 协作价值:
- 设计令牌文档帮助设计师与开发者共享同一套视觉语言,减少沟通障碍。
- 开发者可以在第二屏幕上查看图标或颜色列表,无需记忆具体名称或值,提升开发效率。
6. 总结与扩展
- 文档化的核心价值:
- 使用 MDX 和 Storybook 的 Docs Blocks,开发者可以创建丰富的设计系统文档,结合交互组件、属性控制和设计令牌展示。
- 文档化不仅是记录工具,更是设计与开发协作的桥梁,确保团队对组件和设计规则有一致理解。
- 灵活性与自定义:
- Storybook 提供的 Blocks 只是起点,开发者可以基于 MDX 和 React 语法创建完全自定义的文档体验。
- 团队可以根据自身需求调整文档风格和内容,满足特定项目或组织的要求。
- 扩展资源:
- 讲者提到课程网站中包含更多关于
Typeset 和 IconGallery 的文档和示例。
- 由于这些内容与当前主题不完全相关,讲者将其作为扩展练习,供感兴趣的观众深入学习。
- 总结:
- 通过 MDX 和 Storybook,设计系统的文档化变得强大而灵活,不仅提升开发效率,也增强团队协作。
- 讲者鼓励观众根据自身需求探索更多 Storybook 功能,构建适合团队的设计系统文档。
16-Organizing-and-Customizing-Blocks.txt
1. 组织 Storybook 中的内容
- 背景:
- 讲者提到,随着设计系统组件和文档的增加,Storybook 的内容可能会变得混乱,因此需要更好的组织方式。
- 组织化展示有助于提升可读性和管理效率。
- 组织方式:
- 通过在
Meta 或故事标题中设置路径(如 components/button 或 tokens/colors),可以将内容分门别类地展示在 Storybook 中。
- 示例:将按钮故事归类到
components/button,将颜色令牌归类到 tokens/colors。
- 效果:在 Storybook 界面中,组件和令牌会分别组织在“Components”和“Tokens”分类下,带来更好的结构和清晰度。
- 适用场景:
- 对于小型项目,组织化的价值可能不明显,但随着组件数量增加(如添加更多基础组件),组织化变得至关重要。
- 讲者提到“原子设计”(Atomic Design)理念,即将设计系统分为原子(Atoms)、分子(Molecules)、有机体(Organisms)等层级,可以根据此理念设置层级结构。
- 组织方式因团队而异,讲者鼓励根据团队偏好自定义分类方式,Storybook 提供了灵活性。
2. 自定义 Storybook 文档中的表格和控件
- 自定义 ArgTypes:
- Storybook 允许在故事文件中通过
argTypes 自定义控件(Controls)和文档中的属性表格(Table),以增强文档的表现力。
- 示例:
- 修改
children 属性的显示名称为“Button Label”(而不是默认的 children)。
- 设置
control 类型为 text,定义描述(Description)说明其用途。
- 通过
table.disable 设置为 true,隐藏该属性在文档表格中(不会影响故事中的控件,仅影响文档展示)。
- 适用场景:
- 对于不需要在文档中展示的属性(如私有属性或显而易见的属性如 React 的
children),可以选择隐藏。
- 自定义名称和描述有助于提升文档的可读性,特别是为非技术人员(如设计师)提供更友好的说明。
- 自定义其他属性:
- 类似地,可以自定义其他属性(如
variant 或 size)的显示方式:
- 修改显示名称(例如将
variant 首字母大写)。
- 设置控件类型为
select,限制可选值范围。
- 标注属性是否为必填(Required),或其他具体说明。
- 这些微调虽小,但有助于根据团队需求优化文档和控件体验。
- 资源支持:
- 讲者提到这些自定义选项在课程网站和 Storybook 官方文档中均有详细说明,方便进一步学习。
3. 从 Class Variance Authority (CVA) 自动获取变体选项
- 问题:
- 观众提问:是否可以从 CVA 定义中自动获取变体(Variants)的选项,而不是手动将控件类型设置为字符串(String)?
- 解答:
- 讲者确认,Storybook 可以从 TypeScript 类型(由 CVA 生成)中自动推导出变体选项。
- 示例:
- 在 CVA 定义中,如果
size 变体包含 small、medium、large,Storybook 会自动识别这些选项并在控件中展示。
- 如果从 CVA 中移除
large 选项,Storybook 控件会立即更新,仅显示 small 和 medium。
- 效果:无需手动指定选项,Storybook 通过类型推导自动同步 CVA 定义的变体值,确保控件与代码一致。
- 手动控制选项:
- 如果需要进一步自定义,开发者也可以手动设置控件类型为
select,并指定可选值列表。
- 示例:将
children(或重命名为 Label)的控件类型设为 select,并提供一组预定义选项(如 Button 1、Button 2),限制用户输入范围。
- 这种方式适用于需要严格控制输入值的场景。
4. 协作与最佳实践
- 完全控制控件:
- 讲者强调,Storybook 提供了对控件和文档的“完全控制”(Complete Control),开发者可以根据需求调整所有细节。
- 这些调整不仅影响开发体验,也影响文档如何呈现给其他团队成员。
- 与设计团队协作:
- 自定义控件和文档的最佳实践应与设计团队协作完成,确保文档和控件反映设计系统的意图。
- 通过与设计伙伴沟通,确定如何表达设计系统的目标和使用规则,提升文档的实用性和一致性。
5. 总结
- 组织化:
- 通过在 Storybook 中设置分类路径(如
components/button、tokens/colors),可以有效组织设计系统的组件和令牌,避免内容混乱。
- 组织方式灵活,支持原子设计等理念,团队可根据偏好自定义。
- 自定义化:
- Storybook 允许通过
argTypes 自定义控件和文档表格,包括修改名称、描述、控件类型,以及是否在文档中显示。
- 自定义选项可以从 CVA 自动推导类型,也支持手动设置,确保控件与代码一致并满足特定需求。
- 协作价值:
- 自定义和组织化不仅是技术优化,也是设计与开发协作的一部分,确保设计系统的文档对所有团队成员都清晰易用。
- 讲者鼓励与设计团队合作,共同定义文档和控件的最佳实践,提升设计系统的整体质量。
17-Implementing-a-Callout-Component-Exercise.txt
1. 练习目标与背景
- 目标:
- 讲者布置了一个练习任务,要求参与者基于 Figma 设计文件实现一个
Callout 组件。
- 目的是让参与者从第一性原理(First Principles)出发,实践之前学习的内容,动手实现组件。
- 资源:
- 提供了 Figma 设计文件的查看权限,参与者可以访问设计稿,了解
Callout 组件的样式和变体。
- 讲者提供了颜色值的“备忘单”(Cheat Sheet),包含光模式和暗模式的语义化颜色值,方便实现。
- 实现方式的选择:
- 参与者可以自由选择实现方式:
- 使用 CSS 模块(CSS Modules)和 CSS 变量。
- 使用 Class Variance Authority (CVA) 和 Tailwind CSS 类。
- 讲者鼓励选择让自己感到舒适的方式,重点在于理解和实践。
- 要求与注意事项:
- 暂时忽略图标部分,稍后可以单独处理。
- 关注组件的变体(Variants)、颜色以及对暗模式(Dark Mode)的支持。
- 不强制要求像素完美(Pixel Perfect),重点是掌握设计到代码的转换过程和相关实践。
- 讲者提到设计中使用了之前讨论的主题工具(Theme Portal),以便参与者感受一致性。
2. 讲者的实现过程
- 初始步骤:
- 讲者选择使用 CVA 和 Tailwind CSS 类来实现
Callout 组件,主要是因为其便捷性和之前课程中的一致性。
- 创建文件
callout-variants.tsx(尽管可以是 .ts,但习惯性使用了 .tsx)。
- 定义 CVA 变体:
- 导入
cva 和 VariantProps 从 class-variance-authority。
- 定义
calloutVariants(注意与之前按钮组件保持命名一致性,使用 variants 作为后缀,尽管 VS Code 自动导入建议更具体的命名)。
- 讲者提到命名具体化(如
CalloutVariants)在某些场景下有帮助,但由于该变量仅在当前文件使用,影响不大。
- 设置基础样式(Base Classes):
- 参考 Figma 设计,设置基础样式(尽管不追求像素完美,但大致匹配设计)。
padding: 4(对应 Tailwind 的 p-4)。
border-radius: lg(圆角,稍后可能调整)。
border: 1(边框宽度)。
- 添加少许阴影(
shadow)。
- 元素间距
gap: 1rem。
- 讲者提到 Tailwind 的便利性:可以悬停查看具体值(如
p-4 对应的像素值),并通过 VS Code 的自动补全快速选择类名。
- 同时指出,若 Tailwind 默认值不匹配设计,可以通过项目设置调整,或者直接替换为自定义主题值。
- 定义变体样式(Variants):
- 初始只实现
primary 变体,逐步验证,避免一次性完成所有工作后发现早期错误。
- 根据备忘单设置颜色:
- 背景色:
bg-primary-200。
- 边框色:
border-primary-500。
- 文本色:
text-primary-900。
- 讲者选择
variant 作为变体键名(也可以是 appearance 或 kind,取决于个人偏好)。
- 组件实现:
- 在
Callout.tsx 文件中导入 calloutVariants 和类型 CalloutVariants。
- 定义组件属性
CalloutProps,包括:
children:使用 React 的 PropsWithChildren 类型,确保支持子元素(可能是 ReactNode 或文本等)。
title:必填字段,类型为 string。
variant:从 CVA 导出的变体类型。
- 组件结构:
- 使用
div 作为容器,应用 calloutVariants 类名,并传入 variant 属性。
- 包含
title(使用 h3 标签)和 children(包裹在 p 标签中)。
- 讲者提到:
- 暂时不处理复杂的边缘情况(如允许用户传入自定义标记)。
- 不将
div 的所有属性开放给组件,避免滥用(例如,仅允许特定属性如 data-test-id 用于测试)。
- 通过限制属性范围,避免团队成员(包括讲者自己)在紧急情况下添加不合适的属性。
- 开发流程与调试:
- 讲者强调分步验证的开发习惯:先实现基础功能和一个变体,确认无误后再扩展其他变体。
- 创建故事文件(Story)以测试组件,确保样式和变体正确应用。
- 提到开发环境限制:使用 14 英寸 MacBook,无法像平时使用双 27 英寸显示器(包括一个垂直显示器)那样高效地并排查看 Figma 和代码。
3. 语义化颜色与暗模式支持
- 语义化颜色的价值:
- 讲者指出,设计中的变体名称与颜色名称一致(如
primary 对应 primary 颜色系列),体现了一种统一理论(Unifying Theory)。
- 语义化颜色(如
primary-200、primary-500)有助于代码与设计的对齐,便于理解和维护。
- 光模式与暗模式:
- 备忘单中包含了光模式和暗模式的颜色值,尽管当前实现中仅设置了光模式颜色,但后续可以扩展支持暗模式。
- 讲者提到后续可能需要调整样式以适配暗模式,但当前专注于基础实现。
4. 总结与反思
- 练习核心:
- 本练习旨在让参与者将设计稿(Figma)转化为代码实现,实践 CSS 变量、Tailwind 类和 CVA 等工具的使用。
- 重点是理解设计到代码的转换过程,而非追求像素完美。
- 讲者的实现策略:
- 选择 CVA 和 Tailwind 作为实现工具,注重代码一致性和开发效率。
- 逐步实现:先定义基础样式和单个变体,验证后再扩展,避免早期错误导致大量返工。
- 限制组件属性范围,确保代码可控性和团队协作的一致性。
- 协作与学习:
- 讲者鼓励参与者动手实践,通过“弄脏双手”(Get Hands Dirty)加深对设计系统开发的理解。
- 后续将与团队一起完成实现,确保学习效果和团队协作。
- 技术细节:
- 使用语义化颜色和 Tailwind 类简化样式管理。
- 通过 CVA 实现变体逻辑,支持后续扩展(如暗模式和其他变体)。
- 开发流程注重分步验证,确保代码质量和可维护性。
18-Creating-the-Callout-Component-Story.txt
1. 创建 Callout 组件的 Storybook 故事
- 目标:
- 讲者开始为之前实现的
Callout 组件创建 Storybook 故事文件(callout.stories.tsx),以便在 Storybook 中展示和测试组件的不同变体。
- 初始设置:
- 创建文件
callout.stories.tsx。
- 从
Callout 组件文件导入 Meta 和 Story 类型,以及 Callout 组件本身。
- 定义
Meta 对象:
- 设置
title 为 Components/Callout,以便在 Storybook 中组织到 “Components” 分类下。
- 设置
component 为 Callout 组件。
- 导出
Meta 作为默认导出。
- 定义
Story 类型为 StoryObj<typeof Callout>,为故事添加 IntelliSense 支持。
- 组织结构:
- 讲者提到,通过设置标题(如
Components/Callout),可以将组件归类到 Storybook 的特定分类中。
- 组织方式可以根据团队偏好调整,例如将低级组件(如按钮、输入框)归类为 “Atoms” 或 “Base Components”,而将更复杂的组件归类为 “Molecules”。
- 这种分类是团队或个人决定,取决于设计系统的组织逻辑。
2. 调整组件样式
- 样式微调:
- 讲者对
Callout 组件的基础样式进行了一些调整:
- 添加
font-semibold 类名到标题(title),使其字体加粗。
- 调整圆角(
border-radius),认为当前圆角(rounded-lg)过于圆润,可能会调整为较小的值。
- 讲者提到,作为设计者和开发者,可以根据个人喜好随时调整样式。
- 间距处理:
- 考虑使用 Flexbox 布局来管理组件内部元素的间距。
- 或者使用 Tailwind 的
space-y 或 space-x 类(为除最后一个子元素外的所有元素添加底部或右侧边距),避免全用 Flexbox。
- 初始设置
space-y-8(约 32 像素),但觉得过多,调整为 space-y-4。
- 讲者参考自己的 Figma 设计稿,确认间距值,但强调不必完全精确。
3. 创建故事变体
- 默认内容:
- 为
children 属性设置默认内容,使用 Lorem Ipsum 文本(通过工具生成),避免在每个故事中手动输入。
- 多变体故事:
- 讲者使用多光标编辑(Multicursor)快速创建多个故事,分别对应
Callout 组件的变体:
Primary
Danger
Warning
Success
Information
- 提到最初设计中可能包含第六个变体(
Default),但后来从设计中移除,认为五个变体已足够,添加更多无助于学习。
- VS Code 辅助:
- 讲者依赖 VS Code 的智能提示和 GitHub Copilot 辅助完成重复性任务。
- 强调 Copilot 不适合直接生成代码,但擅长根据已写代码预测下一步操作,帮助加速开发。
4. 添加暗模式支持
- 暗模式样式:
- 讲者为每个变体添加暗模式(Dark Mode)样式,基于之前备忘单中的颜色值:
- 背景色:
dark:bg-primary-800(暗模式背景色)。
- 边框色:
dark:border-primary-900。
- 文本色:
dark:text-primary-50。
- 使用多光标编辑快速为所有变体添加暗模式类名。
- 验证效果:
- 在 Storybook 中切换光主题(Light Theme)和暗主题(Dark Theme),确认变体在两种模式下均正确显示。
- 讲者提到,虽然未同时打开设计稿和代码窗口,但通过 Storybook 的热重载(Hot Reload)功能,可以实时查看样式变化,非常适合专注单个组件开发。
- 开发流程优势:
- 讲者强调 Storybook 提供了一个高效的工作流,尤其是在不需要加载整个页面(包括用户数据获取等复杂逻辑)的情况下,直接测试组件非常方便。
5. 自定义控件(Controls)
- 问题:
- 初始故事中,
variant 属性以字符串(String)形式输入,讲者不满意这种方式,认为不够直观。
- 解决方案:
- 在故事文件中通过
argTypes 自定义控件:
- 将
variant 控件类型设置为 select(下拉选择),也可以是 radio(单选按钮)。
- 设置
options,列出所有支持的变体值(primary、danger、warning、success、information)。
- 效果:用户在 Storybook 中可以通过下拉菜单选择变体,而不是手动输入字符串,提升交互体验。
6. 总结与反思
- 整体流程:
- 讲者完成了
Callout 组件故事的创建,从基础设置、样式调整到多变体实现和暗模式支持。
- 通过 Storybook 验证组件在不同主题和变体下的表现,确保实现符合设计意图。
- 效率提升:
- 使用多光标编辑和智能工具(如 VS Code IntelliSense 和 GitHub Copilot)加速重复性任务。
- 强调分步验证和实时预览(通过 Storybook 热重载)的重要性,避免一次性完成大量工作后发现错误。
- 学习节奏:
- 讲者承认前期准备工作看似繁琐,但随着实践增加,开发者会逐渐进入节奏,开发效率和熟练度将显著提高。
- 鼓励参与者适应这种工作流,将设计转化为代码,并通过 Storybook 进行测试和调整。
- 后续方向:
- 当前实现了基础功能和暗模式支持,后续可能进一步优化样式或添加更多功能(如图标支持)。
- 讲者提到未来可能举办关于多光标编辑技巧的专题工作坊,分享更多开发效率技巧。
19-Play-Functions.txt
1. 引言:Storybook 中的测试与交互
- 目标:
- 讲者介绍如何利用 Storybook 简化组件测试,尤其是交互测试(Interaction Testing)和视觉测试(Visual Testing)。
- 强调 Storybook 能够帮助开发者验证组件行为,减少手动编写复杂测试的工作量。
- 测试挑战:
- 开发者通常擅长编写单元测试(Unit Tests),对大型端到端测试(如 Playwright 测试)或组件测试(Component Tests)可能不够熟练。
- 随着组件数量增加(从 2 个到 60 个),手动验证或大规模样式调整(如颜色变更)变得困难,Storybook 可以帮助解决这一问题。
- 测试类型:
- 视觉测试(Visual Testing):验证组件外观是否符合预期。
- 交互测试(Interaction Testing):验证用户与组件的交互行为是否正确,例如点击、输入等。
- 某些交互无法通过静态故事(Stories)直接展示,例如禁用状态下输入框不可点击,或输入后页面内容更新。
- Storybook 的价值:
- Storybook 提供隔离环境,便于测试组件交互和功能。
- 可以减少上下文切换(避免在编写组件测试时分心),提高测试效率。
2. 示例场景:文本区域组件
- 组件描述:
- 讲者以一个类似旧版 Twitter 的文本区域(Text Area)组件为例,模拟字数限制功能(限制为 140 个字符)。
- 目标是验证:
- 输入时字数计数器(Count)实时更新。
- 超过字数限制时,组件状态变化(如颜色变红)。
- 背景说明:
- 讲者提到当前限制为 140 字符(尽管 Twitter 现为 280 字符),并开玩笑说如果限制为 10 字符会更简单,但仍选择 140 字符作为示例。
- 文本区域组件已基本实现,包括禁用状态(Disabled)、必填标记(Required)、占位符(Placeholder)、暗模式支持等。
3. 创建故事(Stories)
- 基础故事:
- 创建默认故事(
Default),展示无参数时的文本区域组件。
- 在 Storybook 中预览,确认基础功能(如禁用、必填、暗模式)正常。
- 禁用状态故事:
- 创建一个故事(
Disabled),展示禁用状态的文本区域。
- 带字数限制的故事:
- 创建故事(
WithCount),设置 maxLength 属性为 140,显示字数计数器。
- 讲者提到,HTML 原生
maxLength 属性在文本区域中超出限制时不会触发 DOM 的 :invalid 伪选择器,因此需要自定义逻辑。
- 验证计数器是否随输入更新,以及超限时是否显示红色警告(当前在光模式下未变红,但在暗模式下有效,讲者表示稍后调试)。
4. 使用 Play 函数进行交互测试
- Play 函数简介:
- Storybook 提供
play 函数,用于在故事中模拟用户交互,验证组件行为。
- 讲者提到 Storybook 7 及以前版本使用 Testing Library,Storybook 8 可能有所不同,但底层实现对用户是透明的。
- Testing Library 是一个框架无关的测试库,支持多种前端框架,提供选择器查找和模拟用户事件(如点击、输入)的功能。
- 测试工具:
- 使用
@storybook/test 提供的工具:
expect:用于断言,验证测试结果。
within:限定选择器范围,仅在特定 DOM 容器内查找元素。
userEvent:模拟用户事件,如输入、点击等。
- 实现 Play 函数:
- 在
WithCount 故事中添加 play 函数,步骤如下:
- 获取 Storybook 的画布元素(
canvasElement),限定测试范围。
- 使用
canvas.getByRole('textbox') 查找文本区域(textarea 元素的 ARIA 角色为 textbox)。
- 使用
userEvent.type 模拟用户输入 “Hello World”,注意需要 await 等待异步操作完成(否则测试可能失败)。
- 获取输入值(
inputValue = 'Hello World'),避免使用魔法数字(Magic Numbers)。
- 使用
canvas.getByTestId('length') 获取字数计数器(通过 data-testid 属性定位)。
- 断言计数器的文本内容等于输入值的长度(
expect(count).toHaveTextContent(String(inputValue.length))),使用 String 转换以避免 TypeScript 类型错误。
- 测试运行与验证:
- 在 Storybook 的 “Interactions” 面板中查看测试执行过程:
- 确认成功找到文本区域并输入 “Hello World”。
- 验证字数计数器显示为 11(“Hello World” 的字符数)。
- 讲者强调,测试会自动运行,每次代码变更后在浏览器中重新执行,方便实时验证功能。
- 如果测试失败(如无法找到元素或输入失败),会立即反馈,便于调试。
- 调试与环境限制:
- 讲者提到因使用 14 英寸笔记本且缩放设置不当,无法并排查看代码和 Storybook,但正常情况下可以并排工作。
- Storybook 提供步进调试功能(Step Through),允许开发者逐步查看交互测试的每一步。
5. 总结与反思
- Storybook 交互测试的优势:
- 通过
play 函数,可以轻松模拟用户交互,验证组件功能(如输入后计数器更新)。
- 隔离环境减少上下文切换,避免编写繁琐的组件测试。
- 测试与故事集成,便于维护和扩展,同时支持视觉回归测试(Visual Regression Testing)。
- 测试细节与注意事项:
- 使用
await 确保异步操作完成,否则可能导致测试失败(讲者分享了个人经验,忘记 await 导致浪费时间)。
- 通过
data-testid 属性定位元素,简化测试选择器的编写。
- 交互测试不仅验证外观(设计),还验证功能(行为),是组件开发的重要环节。
- 学习价值:
- 讲者通过实际示例展示如何在 Storybook 中实现交互测试,帮助开发者理解从静态故事到动态交互验证的完整流程。
- 鼓励开发者利用 Storybook 的强大工具集,提升测试效率,确保组件质量。
20-Validating-Attributes-Styles.txt
1. 引言:测试策略与分离关注点
- 目标:
- 讲者继续探讨如何在 Storybook 中进行组件测试,重点是验证属性和样式。
- 强调通过分离测试用例,可以更清晰地定位问题,提高调试效率。
- 测试分离的必要性:
- 将多个断言(expectations)塞入一个测试用例可能会导致失败时难以定位具体问题。
- 通过创建独立的测试故事(如
TextTooLong),当 CI/CD 管道失败时,开发者可以快速识别问题来源,而不是在一个包含大量断言的测试中逐一排查。
2. 创建超长文本测试故事
- 故事设置:
- 创建新故事
TextTooLong,设置 maxLength 为 140 字符。
- 定义输入值
inputValue 为一个超长字符串(通过重复字符生成 140 个字符的字符串加上额外字符,总长 142 字符)。
- 讲者选择手动拼接字符串而非变量复用,保持简单。
- 测试目标:
- 验证文本区域在输入超出限制时:
- 显示正确的字数计数。
- 设置
aria-invalid="true" 属性,表示无效状态。
- 应用特定的样式类(如
ring-danger-500),以视觉方式提示用户。
- 测试实现:
- 在
play 函数中:
- 模拟用户输入超长字符串(使用
userEvent.type)。
- 断言文本区域具有
aria-invalid="true" 属性(讲者提到此属性是手动添加的,因为 DOM 原生 maxLength 不会自动触发 :invalid 状态)。
- 断言文本区域应用了
ring-danger-500 类(表示红色边框)。
- 验证字数计数器的内容是否正确反映输入字符数。
- 测试结果与问题:
- 在 Storybook 的 “Interactions” 面板中观察测试执行:
- 成功输入超长字符串,字数计数正确。
aria-invalid 属性正确设置为 true。
ring-danger-500 类已应用,但视觉上未显示红色边框(仅在暗模式下生效)。
- 讲者推测样式未生效可能是 Tailwind CSS 类冲突或层级顺序问题,计划稍后解决。
3. 样式冲突的调试与解决尝试
- 问题分析:
- 红色边框未在光模式下显示,可能是由于 Tailwind CSS 类冲突(多个类同时作用于同一元素,导致优先级问题)。
- 讲者提到,CSS 类(如
ring-*)可能被其他类覆盖,影响最终样式。
- 解决方案尝试:
- 讲者介绍
tw-merge 工具(Tailwind Merge),用于解决 Tailwind CSS 类冲突问题。
tw-merge 可以去重并处理类名顺序,确保最终样式符合预期。
- 检查
package.json,确认是否已安装 tw-merge,若未安装则需执行 npm install tw-merge。
- 讲者习惯将
tw-merge 重命名为 merge,以简化使用。
- 由于时间限制,未深入解决样式问题,但强调这是一个常见问题,测试验证了功能逻辑(类名已应用),只是视觉效果未达预期。
- 反思:
- 讲者开玩笑称此问题可能是故意设置的,用于展示现实开发中的调试场景。
- 强调虽然样式问题未解决,但测试验证了系统功能,证明 Storybook 测试的有效性。
4. 进一步测试样式与属性
- 样式验证尝试:
- 讲者尝试验证文本区域的边框颜色(
border-color),但未记住确切语法,现场直播编码(Live Coding)时遇到困难。
- 计划将设计系统的颜色值存储为变量(如蓝色按钮的特定颜色),以便在测试中复用,避免硬编码。
- 测试失败,因为边框颜色未显示为预期红色,讲者决定暂不深入,但指出这是一个真实开发场景。
- 其他测试可能性:
- 讲者提到,Storybook 测试可以覆盖组件测试中的各种细微差别,开发者可以应用以往学到的前端测试知识。
- 通过 Storybook 的步进调试(Step Through)功能,可以更直观地查看测试执行过程,提升测试体验。
- 讲者表示,现在开始喜欢编写组件测试,因为 Storybook 提供了便捷的工具和环境。
5. 挑战练习:测试禁用状态
- 练习目标:
- 讲者根据笔记提出一个快速练习,挑战参与者为禁用状态的文本区域(
Disabled 故事)编写 play 测试。
- 测试目标:
- 验证文本区域是否真正处于禁用状态(
disabled 属性)。
- 验证尝试输入时,文本区域内容不会变化。
- 讲者提到,虽然这是测试 DOM 原生功能,但仍有助于理解测试逻辑。
- 测试实现:
- 在
Disabled 故事中添加 play 函数:
- 使用
expect(textArea).toBeDisabled() 断言文本区域处于禁用状态。
- 使用
userEvent.type 模拟输入文本。
- 使用
expect(textArea).not.toHaveValue()(或其他方式)断言文本区域内容未变化。
- 测试结果:
- 测试自动运行,在 Storybook 中查看执行过程:
- 成功找到文本区域,确认其禁用状态。
- 模拟输入后,确认内容未变化。
- 讲者强调,这种测试简单易写,且每次查看故事时自动运行,提供即时反馈。
- 反思:
- 讲者提到,即使是测试 DOM 原生功能,也能帮助开发者建立信心,确保组件行为符合预期。
- 参与者可能会因简单问题感到困惑,但讲者鼓励大家,指出每个人都会遇到类似问题,测试是积累经验的过程。
6. 总结与反思
- Storybook 测试的优势:
- 通过
play 函数,可以轻松验证组件属性(如 aria-invalid)、样式(如类名应用)和交互行为(如禁用状态)。
- 分离测试用例(如
TextTooLong 和 Disabled)有助于快速定位问题,提高 CI/CD 管道的诊断效率。
- 自动运行和步进调试功能提升了测试体验,使组件测试变得更直观和有趣。
- 现实开发中的问题:
- 样式冲突(如 Tailwind CSS 类优先级问题)是常见挑战,讲者通过
tw-merge 工具提供了一个解决方案方向。
- 测试验证了功能逻辑,即使视觉效果未完全符合预期,也证明了系统的正确性。
- 学习价值:
- 讲者通过实际示例和现场调试,展示了如何在 Storybook 中进行属性和样式验证,帮助开发者理解测试的完整流程。
- 鼓励参与者通过练习(如禁用状态测试)积累经验,提升对组件测试的信心和熟练度。
21-Importing-Variants.txt
1. 引言:问题与目标
- 问题背景:
- 讲者回应之前的一个问题:对于
callOut 组件的变体(variants),之前使用了数组来手动定义选项(如 primary, info, success, danger, warning),但这些变体的类型已经通过 CVA(Class Variance Authority)或其他工具自动生成。
- 问题在于:能否直接从类型中获取这些变体的值,而不必手动硬编码数组?
- 目标:
- 讲者介绍一种通用技巧,用于从类型或对象中动态获取值,并在 Storybook 和整个代码库中使用。
- 这种技巧不仅适用于设计系统或 Storybook,也适用于处理 TypeScript 类型和值的场景。
2. 挑战:类型与值的分离
- 问题描述:
- 在 TypeScript 中,类型(Type)和值(Value)是分开的。类型仅存在于编译时,无法在运行时直接从中派生 JavaScript 对象或值。
- 例如,讲者无法直接从 CVA 定义的类型中提取变体值(如
primary, info 等),因为这些类型不是运行时的 JavaScript 对象。
- 虽然类型可以用于静态检查,但无法通过类型生成可迭代的值(如数组)用于
select 选项或其他运行时逻辑。
- 需求:
- 既需要类型用于静态类型检查,又需要值用于运行时操作(如迭代、传递给 Storybook 控件)。
3. 解决方案:从值派生类型
- 通用技巧:
- 讲者介绍的技巧是:先定义值(通常是一个数组或对象),然后基于这些值派生类型,而不是反过来。
- 这样可以在运行时使用值(例如迭代数组),同时通过 TypeScript 基于这些值生成联合类型(Union Type)用于类型检查。
- 实现步骤:
- 定义值:
- 定义一个数组,包含所有变体值,例如:
const variations = [
"primary",
"info",
"success",
"danger",
"warning",
] as const;
- 使用
as const 断言,将数组转换为只读元组(Readonly Tuple),确保数组元素是字面量类型(Literal Types),而不是普通的 string 类型。
- 派生类型:
- 使用值和类型:
- 现在,
variations 数组可以在运行时用于迭代、传递给 Storybook 的 select 控件等。
Variations 类型可以用于类型检查,确保代码中使用的变体值符合预定义的选项。
- 代码库中的应用:
- 讲者提到,这种技巧在整个代码库中广泛使用,特别是在需要同时处理类型和值的场景。
- 例如,可以在 JavaScript 中对数组进行迭代、字符串插值等操作,同时通过类型确保代码安全。
4. 为什么不直接在示例中使用此技巧?
- Tailwind CSS 的限制:
- 讲者解释了为什么在当前示例中没有直接使用这种动态生成变体值的方式,原因与 Tailwind CSS 的工作机制有关。
- Tailwind CSS 的类检测机制:
- Tailwind CSS 通过正则表达式(regex)扫描代码库,检测使用的 CSS 类名(如
bg-primary-200, ring-danger-500)。
- 如果类名是通过字符串插值或其他动态方式生成的,Tailwind 无法在构建时检测到这些类名,会将它们从最终的样式表中移除。
- 例如,如果使用数组迭代生成类名(如
bg-${variant}-200),Tailwind 不会识别这些类,导致样式丢失。
- 后果:
- 如果动态生成变体值并用于 Tailwind 类名,可能会导致样式(如文本区域的
ring-danger-500)未应用,开发者会困惑为什么样式未生效。
- 权衡:
- 因此,在涉及 Tailwind 类名的场景中,讲者选择硬编码变体值,而不是使用动态生成的方式,以确保 Tailwind 能够正确检测和保留所需的 CSS 类。
- 但在其他不涉及 Tailwind 的场景中,这种技巧非常有用,可以同时处理类型和值,避免重复硬编码。
5. 总结与反思
- 技巧的核心:
- 通过先定义值(如数组),再基于值派生类型(如联合类型),可以在 TypeScript 中同时使用运行时值和编译时类型检查。
- 使用
as const 断言确保数组元素是字面量类型,从而生成精确的联合类型。
- 适用场景:
- 这种方法适用于需要迭代值并确保类型安全的场景,如 Storybook 控件选项、枚举值处理等。
- 讲者在代码库中广泛使用此技巧,以减少硬编码和提高代码可维护性。
- 限制与注意事项:
- 在使用 Tailwind CSS 时,需谨慎使用动态生成类名的方式,因为 Tailwind 依赖静态类名检测,动态插值可能导致样式丢失。
- 在设计系统或 Storybook 中,如果变体值与 Tailwind 类相关,建议直接硬编码值以避免问题。
- 学习价值:
- 讲者通过实际问题和 Tailwind 的限制,展示了类型与值分离的挑战,并提供了一种实用的解决方案。
- 鼓励开发者在代码库中尝试此技巧,以提高类型安全性和代码复用性,同时注意工具(如 Tailwind)的具体限制。
22-Storybook-Test-Runner.txt
1. 引言:Storybook 测试的问题与需求
- 问题背景:
- 讲者指出当前 Storybook 测试的一个局限性:测试仅在开发者查看特定故事(Story)时运行。
- 提出了一个哲学问题:如果写了测试但没有查看它们,这些测试是否真正运行过?
- 手动查看每个故事以触发测试的方式并不实际,尤其是在大型项目中。
- 需求:
- 需要一种方法,能够自动运行所有 Storybook 测试,特别是在 CI/CD 管道中,以确保代码变更不会破坏现有功能。
- 目标是避免手动点击每个故事,确保测试覆盖全面且自动化。
2. 解决方案:Storybook Test Runner
- 安装 Storybook Test Runner:
- 讲者介绍
@storybook/test-runner,这是一个用于自动运行 Storybook 测试的工具。
- 在当前仓库中,讲者不确定是否已安装该工具,因此尝试使用命令
npm install -D @storybook/test-runner 进行安装。
- 结果显示工具已存在于
package.json 中,因此无需重新安装。
- 讲者提到,不同开发者可能使用不同的包管理器(如
npm, yarn, pnpm),但他因习惯使用 npm 而选择它,强调包管理器的选择并不重要。
- 依赖 Playwright:
- Storybook Test Runner 使用 Playwright 模拟浏览器行为(如点击、交互等)来运行测试。
- 如果是首次在项目中使用 Playwright,需要运行
npx playwright install 来下载所需的浏览器(如 Chromium, Firefox, WebKit)。
- 讲者提到,在当前仓库中 Playwright 已经安装,因此无需执行此步骤。
- 运行测试:
- 可以通过两种方式运行测试:
- 在
package.json 中添加一个 npm 脚本,例如 "test-storybook": "test-storybook"。
- 直接运行命令
npx test-storybook。
- 讲者选择直接运行
npx test-storybook,并展示测试执行过程。
- 测试执行结果:
- Test Runner 会遍历所有定义了
play 函数的故事,执行其中的测试逻辑。
- 测试可以包含断言(
expect 语句)以验证功能,也可以是简单的交互(如点击),如果目标元素不存在,测试会失败。
- 讲者提到一个已知的失败测试(与
ring-danger-500 样式相关),并暂时将其删除以展示其他测试的运行情况,幽默地称“解决失败测试的方法就是删除它们”。
3. CI/CD 集成与自动化
- CI/CD 中的应用:
- Storybook Test Runner 非常适合集成到 CI/CD 管道中,确保每次代码提交或拉取请求(PR)时自动运行所有测试。
- 讲者提到,课程网站中提供了相关链接,或者可以直接使用 GitHub Actions 的配置文件,复制粘贴后即可实现自动化测试。
- 额外功能:
- 除了功能测试,Test Runner 还可以用于其他自动化检查,如可访问性审计(Accessibility Audits),以确保组件符合无障碍标准。
- 通过 CI/CD 集成,开发者无需手动干预,测试结果会自动报告问题。
4. 浏览器调试与自动化测试的平衡
- 两种测试方式的优势:
- 浏览器中调试:
- 在 Storybook 界面中查看和调试测试非常直观,适合开发过程中快速验证和排查问题。
- 开发者可以实时观察测试执行,检查交互行为和断言结果。
- 自动化测试(Test Runner):
- 通过
test-storybook 运行所有测试,适合 CI/CD 环境,确保全面覆盖,无需手动干预。
- 能够批量处理所有故事的
play 函数,节省时间并提高可靠性。
- 建议:
- 讲者建议开发者同时使用两种方式:在开发时利用浏览器调试测试,在提交代码或部署前通过 Test Runner 自动化运行全部测试。
- 强调两者结合使用可以提升开发体验和代码质量。
5. 总结与反思
- Storybook Test Runner 的价值:
- 解决了手动查看故事以运行测试的局限性,通过自动化运行所有
play 函数,确保测试覆盖全面。
- 依赖 Playwright 模拟真实用户交互,测试结果更接近实际使用场景。
- 易于集成到 CI/CD 管道,支持功能测试和可访问性检查,提升项目质量。
- 使用注意事项:
- 首次使用时需确保 Playwright 安装正确,下载必要的浏览器。
- 已知失败测试可能会影响整体测试结果,讲者通过临时删除问题测试展示工具功能,但实际开发中应修复而非删除。
- 学习价值:
- 讲者通过实际操作展示了如何安装和使用 Storybook Test Runner,强调了自动化测试在现代开发流程中的重要性。
- 鼓励开发者结合浏览器调试和自动化测试,充分利用 Storybook 的工具生态,确保代码可靠性和开发效率。
23-Visual-Testing-with-Chromatic.txt
1. 引言:视觉测试与 Chromatic 简介
- 视觉测试的意义:
- 讲者介绍了视觉测试(Visual Testing)的重要性,特别是在确保组件外观一致性和捕获意外变化方面。
- 视觉回归测试(Visual Regression Testing)通过比较组件当前外观与之前基线(Baseline)的差异,帮助开发者识别代码变更是否导致了意外的视觉变化。
- Chromatic 简介:
- Chromatic 是一个用于视觉回归测试的工具,与 Storybook 集成紧密。
- 提供免费版本,限制为每月约 5000 次测试或推送(讲者不确定具体数字),适合小型项目或个人开发者。
- 讲者强调使用 Chromatic 是可选的,并非强制要求。
2. Chromatic 设置与项目集成
- 设置 Chromatic 账户和项目:
- 讲者提到,由于已经拥有 Chromatic 账户,无法现场演示账户注册和项目设置过程。
- 提供了一个图文指南(Illustrated Guide)供参考,并访问了
chromatic.com/start 展示初始步骤。
- 通过 GitHub 授权,选择项目类型为 Storybook,并完成安装和项目关联。
- 项目令牌(Project Token):
- 设置过程中会生成一个项目令牌,用于将 Storybook 项目与 Chromatic 关联。
- 讲者提到会稍后删除令牌,以避免账户滥用。
- Chromatic 的工作原理:
- Chromatic 通过存储组件的视觉基线(即上次确认的外观快照),在每次运行测试时与当前外观对比,检测是否有变化。
- 首次运行时,Chromatic 会记录所有故事(Stories)的初始外观,作为后续比较的基准。
3. 视觉测试的工作流程
- 初始运行与基准建立:
- 首次运行 Chromatic 时,它会遍历所有 Storybook 故事,记录每个组件的视觉外观,形成基准。
- 讲者在 Chromatic 界面中展示了当前项目的组件,包括
Button、Callout 和 TextArea,这些是已有故事的组件。
- 检测变化:
- 讲者以
Callout 组件为例,修改其样式(将文本大小从默认改为 text-2xl),以演示视觉测试如何捕获变化。
- 修改后,运行测试,Chromatic 会更新并显示组件的新外观与旧基线的差异。
- 界面提供覆盖视图(Overlay View),直观展示新旧组件的差异(例如,字体变大导致组件高度增加)。
- 审核与批准变化:
- 视觉回归测试不仅检测变化,还允许开发者决定是否接受这些变化。
- 讲者解释,变化可能是故意的(例如修改字体大小),也可能是意外的,Chromatic 提供选项:
- 接受(Accept):确认变化是预期的,更新基线为新外观。
- 拒绝(Reject):表示变化是意外的,需要修复代码。
- 支持批量操作,可以接受单个组件的所有变化,或一次性接受所有组件的变化。
- 重新运行测试:
- 接受变化后,Chromatic 将新外观作为新的基线。如果再次运行测试且无进一步修改,则不会报告差异。
- 如果撤销修改(例如移除
text-2xl),Chromatic 会再次检测到视觉回归变化。
4. Chromatic 的应用场景与优势
- 开发与调试:
- 讲者强调 Chromatic 在开发过程中的便利性,开发者可以在本地实时运行测试,查看变化并决定是否接受。
- 例如,修改
Callout 组件时,可以边开发边运行测试,立即看到视觉差异。
- CI/CD 集成:
- Chromatic 通常集成到 CI/CD 管道中,每次代码推送(Push)或拉取请求(PR)时自动运行视觉测试。
- 讲者提到其团队在 CI/CD 中设置了审批流程,需要手动确认变化,确保只有预期的修改被接受。
- 捕获意外变化:
- 视觉测试特别有助于发现代码库中未预期的视觉影响。例如:
- 修改某个按钮或图标时,可能未意识到其他地方也在使用相同样式,Chromatic 会捕获这些未覆盖到的变化。
- 移除某个图标或样式时,可能会影响代码库的其他部分,视觉测试可以帮助识别这些问题。
- 与类型检查的互补:
- 虽然类型系统(如 TypeScript)可以捕获某些逻辑错误,但视觉测试提供了额外的保障,确保外观一致性。
- 讲者提到,即使类型检查很完善,视觉测试仍能提供有价值的信息。
5. Chromatic 的限制与使用建议
- 成本与免费限制:
- Chromatic 不是完全免费,免费版本有测试次数限制(约 5000 次比较)。
- 讲者幽默地建议,如果能说服雇主支付费用,那是更好的选择。
- 非强制性:
- 讲者明确表示不强制使用 Chromatic,也不是为其做广告,仅是展示其功能和流程。
- 开发者可以根据需求选择是否使用 Chromatic,或者探索其他视觉测试工具。
- Storybook 8 的新功能:
- 讲者提到,Chromatic 的部分功能(如开发中的实时测试)在 Storybook 8 中是较新的特性。
- 虽然功能在 beta 阶段已存在,但正式集成到 Storybook 8 后使用体验更佳。
- 不论是通过 GitHub Actions、Chromatic UI,还是新的开发界面,核心概念和流程都是相同的。
6. 总结与反思
- Chromatic 的核心价值:
- 提供视觉回归测试,捕获组件外观变化,确保代码修改不会引入意外的视觉问题。
- 支持开发过程中的实时测试和 CI/CD 集成,适应不同工作流程。
- 允许开发者审核并接受或拒绝变化,确保只有预期的修改被纳入基线。
- 使用注意事项:
- 首次使用需设置账户和项目,并理解视觉基线的建立和更新过程。
- 免费版本有使用限制,需根据项目规模评估是否需要付费版本。
- 学习价值:
- 讲者通过实际操作展示了 Chromatic 的设置、测试运行和变化审批流程,强调了视觉测试在现代前端开发中的重要性。
- 鼓励开发者根据项目需求尝试视觉测试工具,提升组件质量和用户体验,同时结合其他测试方法(如功能测试、类型检查)构建全面的质量保障体系。
24-Accessibility-Testing.txt
1. 引言:无障碍测试(Accessibility Testing)的重要性
- 无障碍测试的背景:
- 讲者介绍了在 Storybook 中进行无障碍测试(Accessibility Testing)的方法,强调其对确保组件对所有用户(包括残障人士)可用的重要性。
- 提到无障碍测试并非 Storybook 的默认功能,但可以通过插件(Add-on)实现,这一点被讲者认为有些“不可思议”(criminal)。
- 目标:
- 通过无障碍测试工具,检查组件是否符合无障碍标准(如 ARIA 属性、颜色对比度等)。
- 帮助开发者在组件级别发现并修复问题,避免问题累积到整个应用或页面级别。
2. Storybook 无障碍插件与基本功能
- 无障碍插件(Add-on):
- Storybook 提供了一个无障碍测试插件,可以模拟不同视觉障碍(如模糊视觉或色盲)下的组件外观,帮助开发者理解组件在不同用户视角下的体验。
- 讲者未详细说明插件名称,但提到它不在默认安装中,需要手动添加。
- 界面与功能:
- 插件会在 Storybook 底部工具栏中添加一个无障碍(Accessibility)选项,与其他工具(如 Interactions)并列。
- 提供基础检查功能,包括:
- 是否有正确的 ARIA 属性(用于辅助技术,如屏幕阅读器)。
- 是否通过颜色对比度测试(确保文本和背景之间的对比足够明显)。
- 示例问题发现:
- 讲者以一个
TextArea 组件为例,展示插件如何检测问题:
- 在浅色模式(Light Mode)下,组件通过了对比度测试。
- 在深色模式(Dark Mode)下,字符计数(Character Count)部分的对比度不足,触发了无障碍违规(Violation)警告。
- 讲者幽默地表示这是“故意为之”,并以此为契机展示如何修复问题。
3. 修复无障碍问题
- 调整颜色对比度:
- 针对深色模式下字符计数对比度不足的问题,讲者调整了文本颜色:
- 初始颜色
slate-600 对比度不够。
- 尝试
slate-500,仍不足。
- 最终使用
slate-400,通过了对比度测试。
- 调整后,组件在深色模式下不再触发违规警告。
- 组件级测试的优势:
- 讲者强调,在组件级别进行无障碍测试非常高效,因为可以在问题影响整个应用之前解决。
- 相比于在完整页面或应用中发现问题(如标题顺序 h1、h2、h3 不正确),组件级测试避免了重复问题在多个页面中出现。
- 例如,一个有问题的头部组件(Header)可能在每个页面上重复失败,通过在 Storybook 中修复单个组件,可以一次性解决所有页面的问题。
4. 无障碍测试的自动化与 CI/CD 集成
- Playwright 与 Axe 工具:
- 讲者介绍了如何通过 Storybook Test Runner 结合 Playwright 和 Axe(一个无障碍审计工具)进行自动化测试。
- 在
.storybook 目录下可以创建一个 test-runner.ts 文件,用于配置 Test Runner 的行为。
- 配置中可以注入
axe-playwright,在每次测试故事(Story)时自动运行无障碍审计。
- 讲者提到代码已预先配置好,未手动输入,但展示了相关配置逻辑。
- 多浏览器测试:
- 默认情况下,Playwright 使用 Chrome 运行测试(免费版本),但讲者指出 Chrome 通常不是问题所在,因为开发者多在 Chrome 上开发。
- 真正的问题往往出现在其他浏览器(如 Safari),付费版本的 Chromatic 支持多浏览器测试,包括 Safari 和 Firefox。
- 讲者幽默地提到,Safari 用户虽然不多,但团队中某些“声音大”的人使用 Safari 和 Firefox,因此多浏览器测试对他们很重要。
- 自动化测试的必要性:
- 讲者强调,如果无障碍测试没有集成到 CI/CD 管道中,就很难持续保证无障碍性。
- 小问题往往是无意中引入的(如一次善意的代码变更导致回归),如果没有自动化测试,这些问题会累积,直到下一次手动审计时才被发现。
- 自动化测试可以防止“系统退化为混乱”(entropy),确保无障碍性不会因疏忽而恶化。
5. 团队协作与无障碍问题管理
- 团队流程:
- 讲者分享了团队在处理无障碍问题时的经验,称之为“无障碍死亡行军”(Accessibility Death March),即集中修复所有问题。
- 在设计系统中,通过 Storybook 对组件逐一检查,比在完整页面中排查更高效。
- 讲者提到,作为经理和工程师的双重角色,他会列出所有组件文件(使用
ls 命令),然后通过多光标编辑为每个文件创建 JIRA 票(如“修复此文件的无障碍问题”),分配到冲刺(Sprint)中。
- 这种逐个组件修复的方式,避免了盯着整个页面盲目排查的低效,确保问题被快速解决。
- 与设计团队协作:
- 在项目后期,团队与设计团队一起进行了手动审计,确保整体无障碍性。
- 但讲者指出,整体网站的审计往往不可行,因为同一组件问题会在多页面重复出现,因此在设计系统中修复组件更高效。
6. 无障碍测试的直观功能与新问题发现
- 高亮问题功能:
- 讲者展示了插件的一个强大功能——“高亮结果”(Highlight Results),可以直接在组件上标记出无障碍问题所在。
- 例如,在一个名为
LengthTooLong 的故事中,发现了字符计数部分的对比度问题,点击高亮后,问题区域被清晰标注出来。
- 讲者认为这一功能“物有所值”(worth the price of entry),极大地提升了问题排查效率。
- 修复新问题:
- 针对新发现的问题,讲者再次调整颜色至
slate-400,成功消除违规警告(Zero Violations)。
- 讲者感叹,工具捕捉到了他未察觉的问题,强调自动化测试的实用性:“它确实能捕捉到你不知道的问题,这真是太棒了。”
- Storybook 开发的附加价值:
- 讲者认为,在 Storybook 中开发组件并进行无障碍测试,结合设计系统的其他优势(如一致性、共享性),极大地提升了开发效率和质量。
- 无障碍测试工具不仅帮助修复问题,还增强了开发者对无障碍标准的意识。
7. 总结与反思
- 无障碍测试的核心价值:
- 通过 Storybook 插件和自动化工具(如 Axe 和 Playwright),开发者可以在组件级别发现并修复无障碍问题,避免问题扩散到整个应用。
- 提供直观功能(如高亮问题区域)和多浏览器测试支持,确保组件对所有用户都可用。
- 集成到 CI/CD 管道,确保无障碍性不会因代码变更而退化。
- 使用注意事项:
- 需要手动安装无障碍插件,并配置 Test Runner(如
test-runner.ts)以实现自动化测试。
- 免费工具(如 Chromatic 免费版)可能限制浏览器范围,付费版本提供更全面的多浏览器测试。
- 团队协作和问题管理流程(如 JIRA 票分配)对大规模修复无障碍问题至关重要。
- 学习价值:
- 讲者通过实际操作展示了如何使用 Storybook 无障碍测试工具发现、定位和修复问题,强调了自动化测试和团队协作在无障碍性保障中的作用。
- 鼓励开发者将无障碍测试融入日常开发流程,结合设计系统和 CI/CD 自动化,提升产品质量和用户体验,同时避免问题累积导致的后期修复成本。
25-Using-Decorators-for-Context.txt
1. 引言:上下文问题与设计系统的挑战
- 背景与目标:
- 讲者将这一部分称为“胜利巡礼”(victory lap),旨在分享一些实用技巧,解决开发者在使用 Storybook 和设计系统时可能遇到的“墙”(hit a wall)问题。
- 重点讨论如何处理组件依赖上下文(Context)的情况,尤其是在非理想或遗留代码环境中。
- 设计系统的理想与现实:
- 理论上,设计系统中的组件应该是完全独立的,不依赖任何上下文(如 React 的 Context API),以便于复用和测试。
- 在绿地项目(Greenfield Project)中,这种独立性可能实现,但在现有项目中,组件往往依赖上下文、API 调用或其他外部资源。
- 讲者建议的理想解决方案是重构代码,使用依赖注入(Dependency Injection)传入 mock 对象(如 mock fetch 函数),以便在 Storybook 测试中控制行为。但他也幽默地指出,现实中开发者往往不会立即重构代码,因此需要其他解决方案。
2. 上下文依赖问题及解决方案
- 上下文依赖的场景:
- 组件可能依赖 React 或其他框架的上下文(Context),例如:
- React 的 Context API。
- CSS-in-JS 库(如 Emotion),其样式可能依赖全局上下文。
- Redux 状态管理,组件需要被 Store 包裹。
- 如果直接在 Storybook 中渲染这些组件,会因缺少上下文而报错(如
useContext 无法获取值)。
- 解决方案:使用 Decorators:
- 讲者提出使用 Storybook 的
decorators(装饰器)来解决上下文依赖问题。
decorators 是一种机制,可以在渲染故事(Story)时“包裹”组件,为其提供所需的上下文或父级元素。
- 这种方法比在每个故事中手动设置
render 函数更优雅,适合为一组故事统一提供上下文。
3. 示例:为 TaskList 组件提供上下文
- 示例组件:TaskList:
- 讲者以一个
TaskList 组件(一个待办事项应用)为例,说明上下文依赖问题。
- 该组件依赖
TaskListProvider(一个 React Context Provider),如果直接在 Storybook 中渲染,会因缺少上下文而报错。
- 讲者提到,类似问题也可能出现在 Emotion CSS 或其他需要上下文的场景中(尽管他本人未使用 CSS-in-JS,仅通过截图了解)。
- 设置 Decorator:
- 在 Storybook 中,可以通过
decorators 属性为特定故事或一组故事添加上下文包裹。
- 讲者展示了如何在
TaskList 的故事文件中添加 decorators:
- 导入
TaskListProvider。
- 定义一个装饰器函数,将故事包裹在
TaskListProvider 中,并传入必要的上下文数据(如 tasks)。
- 代码示例(简化):
import { TaskListProvider } from "../path/to/TaskListProvider";
export default {
title: "Components/TaskList",
component: TaskList,
decorators: [
(Story) => (
<TaskListProvider tasks={[]}>
<Story />
</TaskListProvider>
),
],
};
- 结果:组件成功渲染,因为它被包裹在所需的上下文提供者中。
4. Decorators 的工作原理与灵活性
- Decorators 的定义:
decorators 是一个数组,可以包含多个装饰器,按顺序执行。
- 装饰器函数接收两个参数:
Story:故事本身的渲染函数。
context:包含故事元数据的对象,如 args(参数)、argTypes(参数类型)、故事名称、组件信息等。
- 装饰器可以返回一个新的 React 元素,将
Story 包裹在所需的上下文或 DOM 结构中。
- 执行顺序:
- 装饰器按以下层级顺序应用:全局(
preview.js)、元数据(Story 的 default export)、单个故事(具体 Story)。
- 在同一层级中,按数组中的顺序执行。
- 其他用途:
- 讲者提到,之前在主题化(Theming)示例中也使用了类似方法,通过装饰器为故事添加 CSS 类或数据属性(如深色模式)。
- 装饰器不仅限于上下文提供者,还可以用于主题提供者(Theme Provider)或其他需要包裹组件的场景。
5. 调试与上下文对象
- 调试上下文对象:
- 讲者在装饰器中添加了
console.log(context),展示 context 对象的内容。
context 包含丰富的信息,包括:
args:传递给故事的参数(示例中为空)。
canvasElement:与 Play 测试相关的画布元素。
id:故事的唯一标识符(与 URL 中的 ID 对应)。
- 其他元数据:如故事名称、组件信息等。
- 开发者可以利用
context 对象进行更复杂的逻辑或调试。
- 灵活性:
- 讲者强调,
context 对象提供了大量信息,开发者可以根据需要在装饰器中实现自定义逻辑。
6. 实践价值与扩展
- 实践机会:
- 讲者提到
TaskList 组件是一个完整的交互组件,包含按钮、输入框、禁用/启用状态等,适合作为练习对象。
- 鼓励开发者在此基础上编写测试,验证交互行为,确保组件在上下文包裹下正常工作。
- 代码已添加到“直播编码分支”(live coding branch),供开发者参考和实践。
- 从简单到复杂:
- 讲者指出,学习 Storybook 和组件开发可以从简单的按钮开始,逐步扩展到复杂的组件(如
TaskList),逐步掌握上下文、交互测试等高级功能。
7. 总结与反思
- Decorators 的核心价值:
- 提供了一种优雅的方式,为 Storybook 中的故事添加上下文或父级元素,解决组件依赖外部环境的问题。
- 比手动在每个故事中设置
render 函数更高效,适合处理一组故事的共享需求。
- 支持多层次应用(全局、元数据、单个故事),并提供丰富的上下文信息,增强灵活性和调试能力。
- 使用注意事项:
- 装饰器函数需返回一个包裹了
Story 的新元素,确保组件获取所需上下文。
- 理解装饰器的执行顺序和
context 对象的结构,以便在复杂场景中实现自定义逻辑。
- 虽然装饰器是解决上下文依赖的实用工具,但理想情况下仍应考虑重构代码,使用依赖注入等更可持续的方法。
- 学习价值:
- 讲者通过实际示例(
TaskList 组件)展示了如何使用 decorators 解决上下文依赖问题,强调了 Storybook 在处理现实项目挑战时的实用性。
- 鼓励开发者在实践中探索装饰器的更多用途(如主题化、状态管理),并结合上下文对象实现更复杂的逻辑,提升组件开发和测试的效率。
- 提醒开发者,尽管有临时解决方案(如装饰器),长期目标仍是优化代码结构,减少上下文依赖,以符合设计系统的独立性原则。
26-Using-Loaders-to-Fetch-Data.txt
1. 引言:数据加载问题与组件依赖 API 的挑战
- 背景与目标:
- 讲者讨论了在 Storybook 中开发组件时可能遇到的第二个“墙”(wall):组件依赖 API 调用或需要外部数据。
- 虽然理论上组件不应该直接调用 API(应通过依赖注入等方式处理),但现实中这种依赖很常见,因此需要解决方案。
- 现实场景:
- 讲者以自身项目为例,提到他们的网站 CMS 使用 Contentful 存储博客文章和页面文案。
- 营销团队和设计团队希望在 Storybook 中看到组件加载真实内容(而不仅是占位数据),以便预览新页面或设计效果。
- 因此,尽管讲者开玩笑说“故意这样做”,但在特定情况下,组件确实需要调用 API 或加载真实数据。
- 目标:
- 介绍 Storybook 的
loaders 功能,用于在故事(Story)渲染前加载数据。
- 讨论如何处理 API 调用或文件系统数据加载,并提及相关工具(如 Mock Service Worker)以增强开发体验。
2. Loaders 的概念与应用
- Loaders 的定义:
loaders 是 Storybook 提供的一种机制,用于在故事渲染之前加载数据。
- 讲者幽默地表示无法用其他词语解释
loaders,它就是“加载”(loads)数据的工具。
- 使用场景:
- API 调用:组件需要从远程 API 获取数据以渲染。
- 文件系统数据:加载本地固定数据(fixture data),如 JSON 文件,用于模拟不同的 API 响应或状态。
- 讲者提到自身项目的一个潜在应用:他们的分布式系统工具仪表盘需要加载各种事件或异常情况的固定数据,以便在 Storybook 中展示不同场景。
- 实现计划:
- 虽然讲者的团队尚未实现基于文件的
loaders,但计划在未来一个月内完成,并认为这是一个好主意。
3. 示例:使用 Loaders 加载 API 数据
- 设置 Loaders:
- 在故事文件中,
loaders 被定义为一个数组(可以包含多个加载器)或一个函数。
- 讲者展示了一个示例,使用
fetch 从一个假 API(fake API)加载任务数据:
export default {
title: "Components/TaskList",
component: TaskList,
loaders: [
async () => {
const response = await fetch("<https://fake-api/tasks>");
const tasks = await response.json();
return { tasks };
},
],
};
- 数据加载后,通过
context.loaded.tasks 传递给故事的上下文(Context),供组件使用。
- 结果:
- 组件成功加载 API 数据并渲染,证明
loaders 可以有效处理外部数据依赖。
- 讲者提到 API 返回了 200 条任务数据,但未进行裁剪(slice),直接使用全部数据。
4. 增强工具:Mock Service Worker (MSW)
- MSW 简介:
- 讲者提到一个外部工具——Mock Service Worker(MSW),用于在本地开发中拦截 API 调用并返回模拟数据。
- MSW 使用服务工作者(Service Worker)技术,拦截所有 API 请求,并在本地响应,适用于 Storybook 及更广泛的开发场景。
- 使用场景:
- 组件开发:虽然对于单个设计系统组件可能不那么重要,但对于测试整个页面或复杂应用非常有用。
- 离线开发:MSW 允许开发者在没有网络连接的情况下(如飞机上)继续开发,因为所有 API 调用都被本地拦截。
- 测试稳定性:避免因后端服务不可用导致测试失败(讲者提到“没人希望测试因后端宕机而失败”,并得到听众的共鸣)。
- 模拟多种状态:可以加载不同的模拟数据,覆盖各种场景(如基于历史 bug 报告的异常状态)。
- 讲者经验:
- 讲者在开源项目中较少使用 MSW,因为可以通过 CLI 在本地运行产品。
- 但在过去的工作中,MSW 对于模拟 API 响应非常重要,尤其是在无法控制后端环境时。
5. 其他工具与反思
- Storybook 与 Figma 集成:
- 讲者提到一些 Storybook 和 Figma 集成插件(如 Storybook-Figma),但认为它们作用有限。
- 这些插件只是将 Figma 设计与 Storybook 故事并排显示,讲者认为直接打开两个窗口即可,不必过度依赖集成工具。
- 总结:
loaders 提供了一种在 Storybook 中加载外部数据的方式,适用于 API 调用或本地固定数据,解决组件依赖外部资源的问题。
- MSW 是一个强大的补充工具,允许开发者在本地模拟 API 响应,提升开发和测试的灵活性和稳定性。
- 虽然有一些集成插件(如 Storybook-Figma),但讲者认为它们并非必需,开发者应根据实际需求选择工具。
6. 使用注意事项
- Loaders 配置:
loaders 可以是数组或函数,需返回一个对象,数据通过 context.loaded 传递给故事。
- 确保异步操作(如
fetch)正确处理,并将响应解析为可用格式(如 JSON)。
- MSW 应用:
- MSW 需在开发环境中安装和配置,适用于本地开发和测试,但可能增加项目复杂性。
- 对于简单组件,MSW 可能不是必需,但对于复杂页面或需要频繁模拟 API 的场景非常有用。
- 平衡真实与模拟数据:
- 在 Storybook 中加载真实 API 数据(如 Contentful)可以帮助营销和设计团队预览效果,但可能引入依赖外部服务的风险。
- 使用模拟数据或 MSW 可以提高测试稳定性,但可能无法完全反映真实环境。
7. 学习价值
- 核心价值:
- 讲者通过
loaders 示例展示了如何在 Storybook 中处理数据依赖问题,强调了在现实项目中平衡理想与实际需求的重要性。
- 介绍了 MSW 作为补充工具,增强了开发者在离线环境或不稳定后端情况下的开发能力,避免测试因外部依赖失败。
- 实践建议:
- 开发者可以尝试在 Storybook 中使用
loaders 加载 API 或本地数据,模拟组件在不同数据状态下的表现。
- 探索 MSW 的使用,特别是在测试复杂页面或需要多种模拟数据场景时,提升开发效率和测试可靠性。
- 对于设计和营销团队的需求,考虑在特定场景下加载真实数据,但同时准备好模拟方案以应对外部服务不可用情况。
- 反思:
- 尽管
loaders 和 MSW 提供了解决数据依赖的实用方法,长期目标仍应是重构组件,减少对外部 API 的直接依赖,使用依赖注入等更可持续的模式。
- 讲者的分享提醒开发者,工具和方法应服务于团队的实际需求(如营销团队预览真实内容),而不仅是追求技术上的“纯净”设计。
27-Wrapping-Up.txt
1. 引言:设计系统与 Storybook 的价值
- 背景与目标:
- 讲者在结束时总结了 Storybook 在构建设计系统中的重要作用,并强调设计系统的实施过程往往被忽视。
- 提到设计系统的相关资源(如书籍)多由设计师为设计师编写,聚焦于说服管理层采用设计系统的价值,而对工程师的实际操作指导较少。
- 设计系统的现实:
- 讲者指出,无论是否意识到,每个项目实际上都已经有了一个“设计系统”,即一组被分解到某种程度的组件(即使分解不够彻底)。
- 设计系统的构建不仅是与设计团队协作的工具,也是提升日常工程实践的重要手段。
2. Storybook 对设计系统和工程实践的益处
- 组件隔离与测试:
- 使用 Storybook 将组件分解为独立单元,并通过故事(Stories)测试各种变体,促进了代码的模块化。
- 讲者强调,这种做法让开发者能够遵循最佳实践(如组件复用、隔离关注点),而不仅仅是为了设计系统本身。
- Storybook 使得运行组件测试、辅助功能(Accessibility)测试以及其他检查变得简单,这些都是日常开发中应遵循但往往被忽视的实践。
- 提升开发体验:
- 将组件整合到 Storybook 中,形成一个组件库(即使只是简单地给组件文件夹取个名字),可以显著提高开发效率。
- 讲者提到从 CMS(如 Contentful)拉取数据以支持设计和营销团队预览真实内容,说明 Storybook 的基础功能如何扩展到更广泛的用途。
- 减少代码恐惧:
- 通过 Storybook 进行可视化回归测试(Visual Regression Testing)和组件管理,开发者可以更有信心地重构代码。
- 讲者以自身经历为例,提到过去对某些代码(如按钮组件)的重构充满恐惧,但现在借助 Storybook 和相关工具,处理这些问题变得更加容易。
3. 设计系统对一致性与协作的促进
- 一致性决策:
- 设计系统通过提供统一的组件目录,帮助开发者和设计师做出更一致的决策。
- 讲者指出,设计师可能无意中设计出与现有按钮略有不同的新按钮,仅仅因为他们不知道已有组件的存在;同样,开发者也可能因缺乏组织化的系统而重复造轮子。
- 跨团队协作:
- 设计系统和 Storybook 提供了一个共享的组件目录,避免了因信息不对称导致的设计或开发冗余。
- 讲者提到,拥有一个清晰的组件目录后,设计师和开发者都能更好地了解现有资源,避免重复创建类似组件。
- 提升生产力:
- 设计系统的创建过程本身就能提升生产力,简化测试和开发流程。
- 讲者特别提到喜欢在第二块显示器上运行 Storybook,以便实时预览组件,这进一步提高了开发效率。
4. 设计系统的隐性价值
- 承认已有设计系统:
- 讲者强调,即使公司没有正式的设计系统团队,每个项目实际上都已经有了一个非正式的设计系统(即现有组件集合)。
- 承认并组织这个系统(通过工具如 Storybook),可以显著改善日常开发实践。
- 支持不那么“光鲜”的工作:
- 设计系统不仅带来一致性和美观,还支持测试、代码维护等“幕后”工作,这些虽然不显眼,但对项目质量至关重要。
5. 总结与致谢
- 核心观点:
- Storybook 和设计系统不仅仅是构建组件库的工具,更是提升工程质量、促进跨团队协作、减少重复工作的强大手段。
- 通过将组件组织到 Storybook 中,开发者可以更轻松地遵循最佳实践,同时为设计团队提供清晰的资源目录,避免不必要的重复设计或开发。
- 设计系统的价值在于其对一致性、生产力和日常开发实践的深远影响,即使没有正式团队,也可以从现有组件开始,逐步完善。
- 致谢:
- 讲者感谢听众的参与,并以掌声结束,表达了对分享机会的感激。
6. 使用注意事项与反思
- 设计系统的实施:
- 工程师在设计系统实施中往往缺乏指导,需借助工具如 Storybook 填补这一空白,主动与设计团队协作。
- 设计系统不仅是设计师的工作,工程师通过分解组件、编写故事和测试,也在塑造系统的质量和可用性。
- Storybook 的多重角色:
- Storybook 不仅是组件展示工具,也是测试、协作和维护的平台,开发者应充分利用其功能(如可视化回归测试、辅助功能检查)。
- 通过将 Storybook 融入日常开发(如讲者提到的第二显示器预览),可以显著提升效率和代码信心。
- 长期价值:
- 设计系统的真正价值在于其隐性影响——提升一致性、减少重复工作、改善代码质量,这些效果可能在短期内不明显,但对项目长期健康至关重要。
- 即使没有正式设计系统团队,开发者也应意识到现有组件即是设计系统的起点,借助 Storybook 组织和优化这些资源。
7. 学习价值
- 核心价值:
- 讲者的总结强调了 Storybook 和设计系统在现代前端开发中的重要性,不仅限于组件复用,更在于提升工程实践和跨团队协作。
- 提醒开发者,设计系统并非遥不可及的概念,每个项目已有组件即是起点,关键在于组织、测试和持续改进。
- 实践建议:
- 开发者应尝试将现有组件整合到 Storybook 中,编写故事覆盖各种变体,并运行测试以提升代码质量。
- 与设计团队密切合作,确保组件目录对双方可见,避免重复设计或开发,提升一致性和生产力。
- 即使资源有限,也可从小处着手(如整理现有组件),逐步构建设计系统,享受其带来的隐性收益。
- 反思:
- 讲者的分享揭示了设计系统文献中对工程师视角的忽视,鼓励开发者主动探索工具和实践,弥补这一空白。
- 设计系统的价值不仅在于美观或一致性,更在于其对测试、维护和协作的支持,这些“幕后”工作对项目成功同样关键。
- 通过 Storybook 等工具,开发者可以在日常工作中践行设计系统原则,逐步改善代码库和团队协作,而无需等待正式团队或资源到位。