开发者使用 CSS 框架(如 Material UI、Bootstrap 或 Pico)来减少样板代码,提高质量,并确保一致性。然而,随着应用程序代码库的变化,这些收益难以维持。应用程序的外观逐渐偏离框架,新组件被添加进来,已有的布局和组件被修改,开发者必须通过覆盖框架来适应这些变化。然而,覆盖框架比从头开始实现要困难得多。
我建议开发者不要使用 CSS 框架,而是使用自定义 CSS。随着应用程序需求的演变,开发者可以修改现有的样式或复制新样式,而不是覆盖已有的样式。现代 CSS 提供了许多特性,使得编写可维护的样式成为可能。将样式保留在代码库中,而不是作为外部依赖,随着时间的推移,CSS 代码库可以持续保持简洁易懂。
CSS 框架的缺点
覆盖
覆盖框架非常耗时、难以维护且容易出错。在框架规定的范围内,它为开发者提供了最大的好处。许多框架提供了一定程度的自定义能力,但应用程序的定制需求往往会超出框架内置的自定义选项。开发者必须成为覆盖框架的专家,而不是使用 CSS 的专家。覆盖 CSS 框架通常需要使用非公开的 API,在升级框架时这些覆盖内容容易被破坏。
不久之后,覆盖内容越来越多,以至于团队最终得到了一个自定义框架,其中包含了许多覆盖、自定义和扩展内容。这个自定义框架使用自己的约定,难以保持可维护性。即使对于精通底层 CSS 框架的专家来说,它看起来也很陌生。用纯 CSS 很容易解决的问题因为必须在框架中解决而变得棘手。
难以保持一致性
团队之所以使用 CSS 框架,有时候是因为整个产品团队都承诺使用框架的设计系统,并且永远不会偏离。许多团队以这个目标为起点,但几乎没有一个团队能坚持很长时间。框架的设计系统非常通用,它们试图满足大多数应用程序的大部分需求,而不是满足一个应用程序的所有需求。随着时间的推移,应用程序的设计需求总是会偏离框架所提供的。
与其他 Web 框架的区别
我们不能将 CSS 框架的缺点泛化到其他类型的框架,例如 Flask、Rails 或 Spring 等 Web 框架。开发者经常会覆盖 CSS 框架,但在使用 Web 框架时很少会这么做。例如,通过阅读 Flask 源代码来修改 Flask 的路由或会话管理逻辑,这种情况就很罕见。然而,使用 MUI 的开发者通常会使用 styleOverrides 来修改滑块的渲染方式。开发者经常会覆盖 CSS 框架代码,这就是为什么使用 CSS 框架如此危险。
编写自己的 CSS
如果你编写自己的 CSS,通常会从重置、主题、基本 CSS 样式和组件开始。我会选择每次都从头开始编写这些东西,但许多开发者认为这太费时了。为了减少样板代码,你可能会考虑使用 CSS 起始代码库来获得基本样式。开发者将起始 CSS 直接添加到代码库中,而不是将其作为外部依赖。起始 CSS 带来了框架所能提供的好处(减少样板、提升质量和一致性),但没有缺点。
我维护了一个 CSS 起始库,任何人都可以使用,但如果你不喜欢,可以选择下面的选项作为起点创建自己的 CSS 起始库:
请记住,无论选择哪一个,你都是从其中的一小部分 CSS 开始,然后随着时间的推移逐渐添加新的内容。随着设计的演变,逐渐修改起始库的样式而不是去覆盖它们。
使用 CSS 构建
我相信,编写应用程序样式的最佳语言是 CSS。新的 CSS 特性,如变量、作用域、嵌套和值函数,意味着像 SCSS 或 JS-to-CSS 这样的语言所提供的价值无法抵消它们带来的复杂性。IDE 对 CSS 的支持非常出色,而对 SCSS 或 JS-to-CSS 的支持往往滞后。此外,开发者需要对 CSS 有深入的了解,才能编写和维护自定义样式,而不管使用哪种语言。
主题化、编写作用域 CSS、编写表达性的 CSS 和修改 CSS 值是以前很难用纯 CSS 解决的问题。CSS 中的这些不足曾经迫使开发者远离 CSS,转向 SCSS 和 JS。然而,新的 CSS 特性已经帮助弥补了这一差距,减少了对其他解决方案的需求。
主题化
开发者现在可以使用 CSS 自定义属性(变量)向 CSS 中添加主题。使用 prefers-color-scheme 媒体查询,主题可以根据用户对暗色或亮色模式的偏好做出反应。
在构建主题时,在主题文件的顶部将原始 CSS 颜色声明为变量。接下来,为基本主题声明语义化变量,比如 --text-color 和 --background-color。最后,根据需要(比如暗色主题)覆盖语义化变量。在代码的其余部分使用语义化变量作为所有颜色的值,确保应用程序对主题做出正确的反应。
:root { --black: #222222; --white: #FFFFFF; --text-color: var(--white); --background-color: var(--black);}@media (prefers-color-scheme: dark) { :root { --text-color: var(--white); --background-color: var(--black); }}body { color: var(--text-color); background-color: var(--background-color);}
CSS 作用域
CSS 作用域可以实现将样式限定在给定的元素或组件内。作用域允许开发者为特定组件创建样式,而不必担心它们会影响代码库的其他区域(也不需要定义过于具体的规则)。浏览器对作用域的支持正在迅速改善,因此很快就能不受限制地使用它们。下面的代码将 h1 颜色设置为红色,但不影响给定 section 之外的 h1 元素。
<section> <style> @scope { h1 { color: red; } }</style> <h1>Hello world</h1></section>
嵌套语法
CSS 支持类似于 SCSS 的嵌套语法,这使得 CSS 的编写变得更加方便,并且有助于提高可读性。嵌套样式有助于表达样式的逻辑分组,并减少跨多个规则重复使用公共选择器。
hgroup { margin-bottom: 1rem; h1 { font-weight: 700; }}
辅助函数
CSS 有越来越多的值函数,如 calc 和 color-mix,允许开发者在定义 CSS 值时执行计算或处理逻辑。这有助于减少 CSS 变量的数量,并使开发者能够定义易于扩展的灵活样式。
button { --button-size: 1rem; font-size: var(--button-size); padding: calc(.5 * var(--button-size)) calc(1.25 * var(--button-size)); background: var(--button-color);}button.large { --button-size: 2rem;}button.disabled { background: color-mix(in srgb, var(--button-color) 65%, transparent);}
非 CSS 复杂性
像 SCSS 或 JS-to-CSS 这样的非 CSS 解决方案存在重大缺点,使得它们成为应用程序样式的糟糕选择。最大的缺点是编译步骤。如果在构建时将样式编译为 CSS,开发者的工作流程和设置就会变得复杂。如果在运行时将样式编译为 CSS,性能可能会受影响,并且编译失败可能会影响到用户。无论哪种情况,浏览器都是通过 CSS 运行(和调试)样式,因此开发者需要理解生成的 CSS。
此外,开发者需要考虑 CSS 编译解决方案与他们现有的软件是如何进行交互的。像 NextJS 或 Remix 这样的框架可以在浏览器和服务器端运行客户端代码。这意味着样式编译必须能够在浏览器和服务器端运行,可能是 Node 或类似于 Cloudflare Workers 或 Deno 的环境。目前的许多 CSS 编译解决方案都不能很好地支持这些环境。此外,许多流行的框架,如 React,开始支持流式 HTTP 响应,这使得运行时编译样式变得非常复杂。
使用语义化 CSS
使用语义类名(基于语义命名的可重用类)来组织常用样式。语义类表达了一组样式的意图。对于开发者来说,命名是一件很重要的事情。作为开发者,我们应该在命名 CSS 类上下一些功夫,特别是在开发一个可以被其他人修改和扩展的系统时(毕竟,软件被阅读的次数要比被编写的次数多)。
开发者还可以借助语义类名灵活地确定模板策略。原子 CSS 类名(基于视觉功能命名的单一目的类名,如 Tailwind CSS 所推广的那样)强制开发者通过创建细粒度的 UI 组件或部分来减少标记重复。开发者必须通过拆解组件来封装样式,这导致组件过于一般化,有一大堆令人困惑的选项。
使用现代布局
像 Flexbox 和 Grid 这样的现代布局解决方案允许开发者使用干净的标记和 CSS 实现响应式布局。这意味着我们不再需要使用过时的 12 列网格布局,它不仅限制了灵活性,还导致标记混乱。一个好的经验法则是在进行一维布局时使用 Flexbox,在进行二维布局时使用 Grid。
如何组织自定义 CSS 结构
首先,编写或复制最小的样式集,为应用程序构建基本的全局样式。这可能包括 CSS 重置、颜色主题样式、基本布局和排版样式。当你需要更复杂的组件(如按钮、下拉菜单、表格、模态框、工具提示等)时,直接编写或添加这些样式到代码库中。
将应用程序样式视为代码库的一部分,而不是外部依赖。如果应用程序样式变得与起始的样式不一样,修改基本样式而不是覆盖它们,这样有助于让样式变得简洁易懂。
首选全局样式,并根据需要编写局部样式
全局样式是应用到整个应用程序的 CSS 样式,没有全局样式,就很难保持一致的外观。你编写的第一个样式可能是全局样式,这些样式适用于整个应用程序,并且很少会被覆盖。
在编写新样式时,花一些时间确定它们的作用域。在一开始,它们的作用域可能是有限的,因此可以使用类或 @scope 编写具有狭窄作用域的样式。随着时间的推移,作用域中常用的模式可能会被提取到全局样式中,所以你需要经常重构你的 CSS!
自己编写 CSS
总之,尽管 CSS 框架很流行,但我认为它们通常不是样式化应用程序的好选择。所以,在下一个项目中使用纯 CSS 进行构建吧,可以从头开始,也可以使用我的 CSS 起始库或类似的东西作为起点。你会发现,你可以快速地构建应用程序的初始样式,并在后续的开发中维护它们。
查看英文原文:
https://www.infoq.com/articles/no-need-css-framework/