考虑放弃CSS框架:一个可能的选择

发表时间: 2024-05-15 15:08

开发者使用 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 起始库:

  • 你已有的代码库(当然,没有使用框架);
  • 一个拥有清晰样式的开源代码库;
  • 一个类似 Pico 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/