在你充分理解 CSS 之前,你实际上无法真正理解“实用优先”的 CSS,因此本文主要将讨论 CSS。然而,矛盾的是,你对 CSS 的了解越深,你对“实用优先”的 CSS 的欣赏可能就越少。你可能会开始质疑为什么它应该存在。你甚至可能会开始质疑为什么 你 存在。
我会尽力解释。
在为 Web 设计时,CSS 负责许多事情,包括排版、布局和动画。但重要的不仅是它能做什么,还有 如何 做。
在 CSS 出现之前,我们不得不使用现在早已过时的展示性元素(如 <center>
和 <font>
)以及展示性属性(如 bgcolor
和 face
)逐个为 HTML 元素设置样式。这种样式设置方法非常麻烦,每个人都讨厌,但他们别无选择。
然后 CSS 引入了 选择器:一种用于将多个元素应用一组规则进行样式设置的模式匹配机制。很难过分强调选择器的 实用性,但遗憾的是,“实用优先”的 CSS 并不是指的选择器。
相反,在当代 CSS 术语中,“实用类”具体指的是一个 HTML 类,对应于单个 CSS 声明,附加到类选择器。这不完全是像 bgcolor="black"
或 style="background-color: black"
这样的内联样式,但它是一种 零碎的 样式,只为一个属性应用一个具体的值——它确实需要在 HTML 中添加类。
.margin-inline-start-20 {
margin-inline-start: 20px;
}
通常,大多数 CSS 将使用广泛的选择器(:root
、body
、p
、figure
等)和继承来应用。事实上,我相信有一种 CSS 的 80/20 规则,大约 80% 的样式应该使用仅占 CSS 20%(或更少!)的部分完成。
实用类的作用在于它们允许你偶尔对这些一般化的样式规则进行例外。这就是它们的实用性,也是它们唯一的实用性。它们不具有 本身 的实用性,也不是 CSS 中最有用的东西。
“实用优先”的 CSS 更加激进。实用优先的 CSS 是 例外 优先的 CSS。但这并不是例外的工作方式,无论是在 CSS 中还是在一般情况下。
以段落为例。你的段落几乎都会共享相同的 font-family
、font-size
、line-height
和 margin
。例外很少。那么你会如何为段落元素设置样式呢?好吧,font-size
和 line-height
通常已经处理好了,因为 <p>
元素会从祖先元素(如 :root
)那里继承这些值。所以你在这里最好的做法是,我不知道,泡杯茶,出去散步,做点别的,只是不要写任何代码。至于 margin
,你可以在一个地方一次性为 100 个、1000 个,甚至百万个段落设置样式,只需要一个简单的声明:
p {
margin-block: 1rem;
}
事实上,直接在任何元素或一类元素上设置 margin 都是一种不好的模式。你可能应该使用 Stack 组件 通过兄弟选择器设置 上下文 margin
(不要对“类”为中心的实用优先拥护者讨论兄弟选择器)。
无论如何,实用优先的 CSS 的做法是直视你,眨眨眼,说这种样式设置方法更好:
<p class="font-sans text-base leading-6 my-16">And</p>
<p class="font-sans text-base leading-6 my-16">on</p>
<p class="font-sans text-base leading-6 my-16">and</p>
<p class="font-sans text-base leading-6 my-16">on</p>
<p class="font-sans text-base leading-6 my-16">and</p>
<p class="font-sans text-base leading-6 my-16">on</p>
<p class="font-sans text-base leading-6 my-16">and</p>
<p class="font-sans text-base leading-6 my-16">on</p>
<p class="font-sans text-base leading-6 my-16">etc</p>
<p class="font-sans text-base leading-6 my-16">etc</p>
<p class="font-sans text-base leading-6 my-16">etc</p>
<p class="font-sans text-base leading-6 my-16">etc</p>
<p class="font-sans text-base leading-6 my-16">etc</p>
实用优先的反对者抱怨这种方式很啰嗦,因此很丑陋。而确实如此。但如果它真正解决了问题,你会原谅它的。但事实并非如此。它毫无疑问是一种次优的制作方式,用它来使 相似 的事物 看起来 相似,这本来应该是的。它只能用于复制 不一致 的设计,其中所有重复的值将会不同。
换句话说,拿出一个界面
设计,从使用实用优先的 CSS 编码中受益,我将向你展示一个基本上是 搞砸了 的界面——在它落入一些可怜的前端开发者手中之前。
如果你的组织或客户使用类似于 markdown 或所见即所得文本编辑器的语法生成内容,将输入转换为任意、未装饰的 HTML——这在大多数情况下都是如此,那么,你有点难办,对吧?除非你创建了一个,比如,专门的 markdown 插件,它结合了实用样式并使用,比如,Tailwind 的 @apply
指令来应用它们,这… 只是以一种荒谬地模糊的方式编写 CSS。
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addBase, theme }) {
addBase({
'p': { fontSize: theme('fontSize.base'), marginBlock: '1rem', lineHeight: '1.5' },
})
})
]
}
Tailwind 是一个特别难以捉摸的家伙,因为它是一个号称实用优先工具,但却有多个逃生口,比如 @apply
,用于编写 CSS,将实用类组合成框架最初想要你认为它避免的那种语义声明块。
.select2-dropdown {
@apply rounded-b-lg shadow-md;
}
.select2-search {
@apply border border-gray-300 rounded;
}
.select2-results__group {
@apply text-lg font-bold text-gray-900;
}
Tailwind 甚至使用了我十年前提出的 猫头鹰选择器 概念来应用子元素之间的空间。这意味着使用了可怕的 子选择器!
那么 Tailwind 究竟是什么?它只是带有额外步骤和品牌名称的 CSS。不过,你也可以这样说大多数任何 CSS 框架。
为什么这种实用优先的方法目前如此受欢迎?部分原因是因为我们被要求编码的设计通常是 搞砸了 的,我们需要同样 搞砸的 工具来处理它们。部分原因是因为我们采用的用于编写 搞砸的 JavaScript 的工具不太适用于 CSS 或 HTML。而主要原因是开发者的不安全感和新生事物崇拜很容易被利用:“你有没有写过你 不太满意的 CSS?那么这里有一个激进的、范式转移的、准专有的解决方案!你再也不会尴尬了!”
事实证明,科技界的人特别不擅长区分范式转移和范式 屁。这就是为什么我们有了暴跌的加密货币、积灰的猴子 JPEG 投资组合,以及 AI 生成的教孩子们关于不存在的粉红色双头恐龙的童书。
并不是说实用优先的 CSS 不能用来为事物设置样式。从技术上讲,它是可以的。如果你不理解 CSS,这可能是你的最佳选择。至少如果你完全使用 JSX。并且沉浸在几个月和几千兆字节的工具中。如果你 理解 CSS,像 Tailwind 这样的框架包括一些功能和工具,可以帮助你摆脱从一开始就鼓励你使用的零碎、实用优先的方法。
但是,只是作为一个思想实验,想象一下,如果 CSS 今天不存在,无论以什么形式(包括作为实用类框架内部的逃生口),你用于样式设置的唯一两个选择是:
- 1990 年代的展示性 HTML
- 类属性和实用类语法(参见Tailwind 营销和误导引擎以获取此示例):
<button class="
[&>[data-slot=icon]]:-mx-0.5
[&>[data-slot=icon]]:my-0.5
[&>[data-slot=icon]]:shrink-0
[&>[data-slot=icon]]:size-5
[&>[data-slot=icon]]:sm:my-1
[&>[data-slot=icon]]:sm:size-4
[&>[data-slot=icon]]:text-[--btn-icon]
[--btn-bg:theme(colors.zinc.900)]
[--btn-border:theme(colors.zinc.950/90%)]
[--btn-hover-overlay:theme(colors.white/10%)]
[--btn-icon:theme(colors.zinc.400)]
after:-z-10
after:absolute
after:data-[active]:bg-[--btn-hover-overlay]
after:data-[disabled]:shadow-none
after:data-[hover]:bg-[--btn-hover-overlay]
after:inset-0
after:rounded-[calc(theme(borderRadius.lg)-1px)]
after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)]
before:-z-10
before:absolute
before:bg-[--btn-bg]
before:data-[disabled]:shadow-none
before:inset-0
before:rounded-[calc(theme(borderRadius.lg)-1px)]
before:shadow
bg-[--btn-border]
border
border-transparent
dark:[--btn-bg:theme(colors.zinc.600)]
dark:[--btn-hover-overlay:theme(colors.white/5%)]
dark:after:-inset-px
dark:after:rounded-lg
dark:before:hidden
dark:bg-[--btn-bg]
dark:border-white/5
dark:text-white
data-[active]:[--btn-icon:theme(colors.zinc.300)]
data-[disabled]:opacity-50
data-[focus]:outline
data-[focus]:outline-2
data-[focus]:outline-blue-500
data-[focus]:outline-offset-2
data-[hover]:[--btn-icon:theme(colors.zinc.300)]
focus:outline-none
font-semibold
forced-colors:[--btn-icon:ButtonText]
forced-colors:data-[hover]:[--btn-icon:ButtonText]
gap-x-2
inline-flex
isolate
items-center
justify-center
px-[calc(theme(spacing[3.5])-1px)]
py-[calc(theme(spacing[2.5])-1px)]
relative
rounded-lg
sm:px-[calc(theme(spacing.3)-1px)]
sm:py-[calc(theme(spacing[1.5])-1px)]
sm:text-sm/6
text-base/6
text-white"> Button
</button>
在这种悲惨的情况下,真正的 CSS 的引入会让整个 Web 开发社区在自己高度兴奋的咖啡尿液中集体淹没。一个月内,它的出现将产生十万个 “大家好!!” 的 Youtube 视频,67 本仓促写成的书籍,和由神秘的、打扮整洁的白人家伙主持的 31 个视频课程。每天都会听到 “选择器引擎”、“关注点分离” 和 “层叠” 的讨论,而不是抱怨 CSS 很难,每个人都会在同行之前努力掌握它。因为它是 新的,所以 CSS 会被拥抱和喜爱。
但是 CSS 不是新的,它只是 好的。在这个倒退、优化垃圾和胡言乱语的经济中,好并不够坏。