以下是来自 Tom Genoni 的客座文章。Tom 将向我们介绍 Optimizely 新 UI 库/Sass 框架背后的思路和流程。 第 2 部分 由 Daniel O’Connor 撰写,介绍了一些技术和集成方面的内容。
当我第一次开始从事 Web 项目时,样式表被视为一个必要的弊端。它既不是一种被计算机科学导向的工程师重视的 *真实* 语言,也不是一种简单到足以让设计师完全拥有和理解的语言。由于最佳实践很少,CSS 的组织一直是临时性的——“在这一部分中输入样式,在那一部分中输入颜色”,每家公司都以不同的方式进行。但是随着 Web 应用程序及其构建团队越来越大、越来越复杂,在维护跨团队和项目的一致性的同时管理不断膨胀的代码库变得越来越困难。
在出现的第一个流行的 CSS 框架中,旨在解决此问题的框架是 Bootstrap。许多类似的框架随后出现,但目的始终相同:与其在每个项目中从头开始编写 CSS,不如从一套最常见组件的样式化集合开始——网格、按钮、表单元素、面包屑——这些组件跨浏览器兼容,并且可以轻松地组合成更大的界面。
在 Optimizely,我们编写并积极维护自己的 Sass 框架,名为 OUI(发音与法语单词“yes”相同),该框架基于 Web 社区无数成员(包括 Mark Otto、Jonathan Snooks、Nicole Sullivan 和 Harry Roberts)的工作,以及由 BEM 和 SMACSS 提倡的可扩展、面向对象的 CSS 和 HTML 哲学。
OUI 的持续目标是提供以下代码…
- 抽象的。组件名称不应从其包含的内容中派生。类名应传达结构意义。
- 可重用的。组件应足够通用,可以在整个网站中重复使用。它们不应假设它们将在哪个页面/视图上使用。在一个区域解决的问题应轻松地应用到其他地方。
- 可混合的。组件应该能够组合在一起,以创建更大的块。
- 由变量驱动。所有常见的设计元素——颜色、字体、间距、阴影——都应使用预先存在的变量来定义。
- 可扩展的。重复使用模式意味着可以更快地创建新元素,并且需要最少的额外 CSS。
- 一致的。开发人员将能够更好地阅读彼此的代码,并有助于创建更可靠的最终用户体验。
- 小巧且 DRY。由于我们正在重复使用低级组件来构建更大的对象,因此我们减少了 CSS 膨胀。更少的代码意味着更少的错误。
在这篇文章中,我将讨论构建它所需的实际步骤以及与工程师和设计师的合作关系,以及我们在过程中遇到的某些问题,以及在第 II 部分中,我的同事 Daniel O’Connor 和 UI 工程师同事 UI 工程师 将描述我们如何对其进行测试、版本化并将其集成到项目中的技术细节。
阶段 1:让您的设计师参与进来
可以说,创建 UI 库最重要的阶段是与设计团队建立密切的协作关系。使用框架进行设计通常需要从像素级精确的模型中转移工作流程,这可能会导致看似设计自由度降低的情况。幸运的是,有一种方法可以解决此问题:审核您的网站并展示您的发现。
花一两天时间拍摄您的网站的屏幕截图。仔细检查每个角落,查找按钮、表单、表格、字体大小、颜色、标签、图标等的全部实例,并将它们分组在一起。您可能会发现的可能性会让您的设计师感到震惊:形状和大小各不相同的按钮、标题缺乏规律性、27 种蓝色阴影,以及许多其他良好意图变糟的例子。让您的设计师负责整合这些不一致之处将使他们成为框架构建的合作伙伴,并成为在整个团队中宣传这一努力的盟友。
阶段 2:拥有前端
与您的工程团队协商,确定谁“拥有”视觉前端。由于您的框架的 CSS 和 HTML 是相互关联的,我们发现拥有一个较小的团队(熟悉框架的模式)负责交付代码给经常心怀感激的工程师很重要,这些工程师不再需要与不合作的 z-index 作斗争。
在 Optimizely,我们的 UI 工程师 担任此角色,他们正式 成为设计团队的一部分。通过这种更严格的控制,我们大幅减少了需要编写的 CSS,我们的代码更简洁、更一致,并且遇到了更少的错误。随着时间的推移,随着工程师理解如何有效地使用库以及框架成熟,编写 HTML 和 CSS 的责任可以扩大。
阶段 3:识别您的组件
Bootstrap 包含构建大多数网站所需的一切。但是,您可能不想要面包屑,或者您可能可以使用更新的 CSS 属性,因为您的浏览器支持情况。无论哪种情况,我们建议参考现有的框架,并创建一个仅包含满足您的用例所需的组件的列表。通过自己构建它,您将更容易识别潜在的麻烦点,并在此过程中学到很多东西。
OUI 仅使用我们认为通用的组件和变量构建。每个使用 OUI 的 Optimizely 项目都在其之上添加任何自定义 CSS 代码,仅在该项目的存储库中。这允许每个项目满足其独特的设计需求,同时保持 OUI 简洁且不受影响。如果不同的项目引入了类似的组件,我们可以选择将它们“升级”到 OUI,尽管这种情况很少发生。在第 II 部分中,我们将更详细地描述我们的集成和版本控制系统。
阶段 4:组织和构建
CSS 预处理器通过支持仅包含特定组件所需的局部文件,极大地简化了代码组织。但如何命名事物以及将它们放在哪里仍然是一个挑战。对于 OUI,我们目前使用以下结构
- elements:混合和函数。由于我们使用 node-sass 进行编译,因此我们还包含用于动画、色调和前缀的混合,这些混合通常来自 Compass。
- base:重置和最小的 HTML 元素样式(链接、表格、列表)
- components:帮助形成对象的低级组件(网格、媒体、导航)
- objects:完全形成并具有样式的组件(按钮、加载器、下拉菜单)
- trumps:用于布局的辅助类(边距、填充、字体样式)
在 Sass 中,我们不使用 ID,选择器深度保持在 最低限度,源代码顺序有助于我们保持 低特异性。您可以仔细查看 OUI 存储库。
阶段 5:记录和推广
所有这些工作都无法帮助简化您的代码,如果没有人知道它或如何使用它。以下是一些帮助 OUI 在 Optimizely 获得关注的建议。
- 选择一个代码名称。 就像任何好的工程项目一样,您的框架应该有一个内部代码名称。这可能不是您通常对 CSS 项目所做的,但嘿,这个很重要!如果它足够短,您可以将其用作命名空间类的前缀。
- 提供良好的文档。 任何尝试过的人都知道,编写和更重要的是维护带有代码示例、使用指南和变更日志的良好文档并不容易。不完整或过时的文档只会让您的开发人员感到沮丧,并可能损害对项目的信任。为了解决这个问题,指定所有者,并对要记录的内容以及如何保持其可访问性和最新性有明确的期望。
这对我们来说是一个持续的挑战,但我们正在逐渐接近。对现有解决方案不满意,Daniel O'Connor 一直在领导构建一个强大的动态样式指南脚本,名为 ScribeSass,它解析 Sass 源文件中的注释和代码,并渲染代码示例和示例。我们很快就会将它作为开源项目发布。
- 宣传。 您的简短代码名称和文档到位后,就开始传播。使用展示重复使用现有样式和模式的速度和一致性优势的真实示例进行技术演讲。如果您有预算,可以制作贴纸、T 恤或海报。每月收集关于 CSS 的大小、规则和特性的统计数据,以及您的团队修复错误所花费的时间,并分享结果。我们看到了各方面的下降。
经验教训
就像任何网络项目一样,我们在过程中犯了很多错误,并进行了修复和添加。以下是我们面临的一些挑战、我们做出的调整以及我们继续面临的问题。
- 命名空间。 OUI 对新项目运行良好,但当我们开始重构现有项目时,我们意识到可能会发生类名冲突。为了避免这种情况,我们添加了命名空间 Sass 变量的选项,该变量将在最终 CSS 输出中为大多数类添加前缀。例如,如果“oui-”是命名空间值,则类“.media”将变为“.oui-media”。这让我们能够更自由地将 OUI 与旧代码混合使用。
- 版本控制。 一段时间以来,我们并不相信引入正式版本控制的开销是否值得付出努力。只有几个项目使用 OUI,我们不想在进行更改时增加太多摩擦。但是推动修复(尤其是可能导致中断的修复)以及传达其影响变得很困难。我们决定采用语义版本控制,在维护良好的变更日志和几个 Gulp 包的帮助下,我们能够添加功能并修复错误,而无需担心破坏使用 OUI 的项目。在第二部分中,Daniel O'Connor 将更详细地讨论设置版本控制。
- Sass 混合 vs 扩展。 最初 OUI 是使用相当多的扩展设置的。我们知道它们的缺陷,并谨慎地引入了它们,始终使用占位符。但是使用扩展可能会以不希望的方式移动 CSS 类,尽管我们尽了最大努力,但最终我们确实在源代码顺序和特异性方面遇到了一些问题。我们决定将许多扩展转换为混合,尽管代码量略多,但我们对未来出现意外事件的担忧较少。
- 响应式。 这个有点棘手。我们在少数模式(如网格)中包含了媒体查询混合和可选断点。但是,由于响应式设计中有很多不可避免的复杂性,通常需要大量的自定义代码,因此 OUI 除提供基本构建块之外,无法提供更多内容。尽管存在这种固有的挑战,但我们将继续评估使框架更易于响应的方法。
- 自定义与可重复使用模式。
随着新的设计和布局的创建,从重复使用现有模式到完全自定义,UI 工程师需要决定如何最佳地构建它。最终构建 UI 库的目标是使您的 CSS 代码库尽可能小且易于管理,但您还需要权衡品牌和产品所需的需要和灵活性。设计是否可以稍作调整以符合现有模式?如果不是这样,这应该是一个新的模式,还是完全独特?协商这一点更多的是艺术而不是科学,它突出了设计师和 UI 工程师之间协作和妥协的重要性。 - 坚持下去。 随着时间的推移,很容易让标准下降。一些硬编码的值和魔法数字在这里和那里不会让您的应用程序崩溃。但是,由于许多 CSS 错误是由于很久以前编写的代码的意外后果造成的,因此重要的是要避免这些小定时炸弹。这种纪律将得到回报。
迄今为止的结果
尽管以下比较并非完全“苹果对苹果”,但这些 CSS 统计数据* 来自 Optimizely 的两个视觉上同样复杂的类似产品。第一个是 OUI 之前构建的旧应用程序,第二个是专门使用 OUI 的应用程序。
没有 OUI 的产品 | 使用 OUI 的产品 | 更改 | |
---|---|---|---|
Gzip 大小 | 101 KB | 33 KB | -68% |
样式表大小 | 618 KB | 193 KB | -69% |
规则 | 3259 | 1757 | -46% |
选择器 | 5183 | 2407 | -54% |
标识符 | 21375 | 4009 | -81% |
声明 | 9356 | 4429 | -53% |
每个选择器的特异性 | 87 | 15 | -83% |
最高选择器特异性 | 641 | 50 | -92% |
ID 选择器 | 3135 | 0 | -100% |
* 大多数值使用Parker 生成,这是一种“样式表分析工具”。
总结
在您的公司管理框架(无论是通过分叉现有框架还是编写自己的框架)都可能令人生畏。但是对于关心维护项目之间一致性的设计和工程团队来说,其好处是太多而无法忽视。通过依赖预定义的模式,您将获得视觉和代码上的统一性、更快的 HTML 构建、更少的错误、更少的 CSS 膨胀,并且它允许产品设计师将更少的时间花在规格上,而将更多时间花在用户体验和信息架构等更大的挑战上。
感谢您分享 OUI,并分享您的目标和实施方法。
我只注意到一个很小的细节:与 Harry Roberts 的使用方法以及 OOCSS(对象作为抽象、可重复、结构化模式)的扩展方式相比,您似乎颠倒了“对象”和“组件”的含义。您真的将它们颠倒使用了吗(为什么?),还是只是在文章中混淆了?
嗨,Valentin,感谢您的评论。是的,它们与 Roberts 的使用方法相反。我不记得我们是什么时候做出这个决定的,但对我来说,他的方法似乎倒退了。我认为组件是“更大整体的一部分或元素”,而对象则是更完整的东西,通常由组件组成。
我认为 OOCSS 是一种鼓励代码重用、将内容与容器分离以及保持 DRY 原则的哲学。但我不会不同意采用您提到的更具体的 OOCSS 定义以及由此隐含的命名方法的团队。
嗨,Tom,感谢您的澄清。是的,我明白您的意思——虽然我喜欢 OOCSS 作为一种方法,但我认为“对象”这个名字选择得很糟糕……但我已经接受了被困住的现实 ;) “组件”也很模糊,我想我们只能接受根据项目/方法定义的术语。
这似乎是一个非常有趣的库。但是,它需要一些好的文档才能供其他人使用。
感谢您分享这些,我知道这需要时间,而且找到确证 rolling your own frameworks 仍然是一个有效的概念,以及阅读您如何以及为什么这样做是很有价值的。
在过去的 15 年里,我一直避免使用所有可能的 CSS 技巧、短期修复和 polyfils。(很少使用)当 CSS 中出现新技术时,我一直迫不及待地等待着能够毫无顾忌地使用它的一天,而不必担心破坏客户网站。
此外,这种理念帮助我避免了当 CSS 框架更改版本时不得不重新编写数百个网站。(foundation、bootstrap 等……)
我发现,多年来,几乎所有已知的人类网站设计都有基本结构,定义这些结构的 CSS 越简单,它们就越容易使用、扩展和改进。而且目前流行的框架都不简单。
这意味着设计师需要真正掌握一个非常庞大和复杂的 CSS 库,而且这种投资意味着他们在未来对库的任何更改都束手无策,否则就会浪费这种投资。
由于我从未见过一个超过 3 列的布局,因此拥有一个显示超过 3 列的网格系统似乎过于复杂且浪费。而且在罕见的情况下需要它们,自定义 CSS 非常有效。
我希望看到更多定制框架,因为我喜欢简单而有效的解决方案,我们可以从中汲取长期的通用目标,定制的解决方案是有意义的,而哲学可以让我们在混乱的技术世界中享受设计带来的乐趣。
干杯!