本文探讨如何利用CSS级联层提升组件的可定制性、效率以及易用性和易理解性。
我热衷于代码组织,发现级联层是明确组织代码的绝佳方式,因为它直接遵循级联的读取顺序。更妙的是,除了帮助进行“顶级”组织外,级联层还可以嵌套,允许我们根据级联编写更精确的样式。
唯一的缺点是你的想象力——没有什么能阻止我们过度设计CSS。需要明确的是,你很可能会认为我即将展示的内容是一种过度设计。但我认为我已经找到了平衡点,保持简单而有序,并乐于分享我的发现。
让我们以按钮为例,探讨使用CSS编写组件的模式。按钮是几乎每个组件库中最流行的组件之一。这种流行是有原因的,因为按钮可用于各种用例,包括:
而且按钮有多种不同的标记形式,例如<button></button>
、input[type="button"]
和<a></a>
。如果你相信的话,制作按钮的方法甚至更多。
最重要的是,不同的按钮执行不同的功能,并且通常会相应地进行样式设置,以便一种操作的按钮与另一种操作的按钮区分开来。按钮还会响应状态更改,例如当它们悬停、活动和聚焦时。如果你曾经使用BEM语法编写过CSS,我们可以在级联层的上下文中沿类似的思路思考。
<code>.button {} .button-primary {} .button-secondary {} .button-warning {} /* etc. */</code>
好的,现在让我们编写一些代码。具体来说,让我们创建几种不同类型的按钮。我们将从一个.button
类开始,我们可以将其设置为任何我们想要设置为按钮的元素!我们已经知道按钮有多种不同的标记形式,因此通用的.button
类是选择一个或所有这些按钮最可重用和最可扩展的方式。
<code>.button { /* 所有按钮的通用样式 */ }</code>
在这里,我们可以插入我们的第一个级联层!记住,我们首先想要级联层的原因是它允许我们在评估样式时设置CSS级联的读取顺序。我们可以告诉CSS先评估一层,然后是另一层,然后再是另一层——所有这些都按照我们想要的顺序进行。这是一个令人难以置信的功能,它赋予我们超级能力来控制浏览器应用哪些样式“胜出”。
我们将此层命名为components
,因为按钮是一种组件。我喜欢这个名称的原因是它足够通用,可以支持我们将来在决定扩展设计系统时添加的其他组件。它随着我们一起扩展,同时保持与我们将来编写的可能不是特定于组件的其他样式的良好关注点分离。
<code>.button {} .button-primary {} .button-secondary {} .button-warning {} /* etc. */</code>
事情变得有点奇怪的地方在这里。你知道你可以在类内部嵌套级联层吗?这完全是一件事。所以,看看这个,我们可以在已经位于其自身层内的.button
类内部引入一个新的层。这就是我的意思:
<code>.button { /* 所有按钮的通用样式 */ }</code>
最终,浏览器就是这样解释层内层的:
<code>/* 组件顶级层 */ @layer components { .button { /* 所有按钮的通用样式 */ } }</code>
这篇文章不仅仅是关于嵌套样式的,所以我只想说,当你这样做时,你的里程可能会有所不同。查看Andy Bell最近关于谨慎使用嵌套样式的文章。
到目前为止,我们已经在一个级联层内建立了一个.button
类,该层旨在容纳我们设计系统中的任何类型的组件。在该.button
内部是另一个级联层,这一层用于选择我们可能在标记中遇到的不同类型的按钮。我们之前讨论过按钮是<button></button>
、<input>
或<a></a>
,这就是我们如何单独选择样式化每种类型的方法。
我们可以使用:is()
伪选择器函数,这类似于说:“如果这个.button
是一个<a></a>
元素,则应用这些样式。”
<code>/* 组件顶级层 */ @layer components { .button { /* 组件元素层 */ @layer elements { /* 样式 */ } } }</code>
我将用适用于所有按钮的通用样式填充我们的代码。这些样式位于元素层的顶部,因此无论标记如何,它们都将应用于任何和所有按钮。可以将它们视为默认按钮样式。
<code>@layer components { @layer elements { .button { /* 按钮样式... */ } } }</code>
当用户与默认按钮交互时,它们应该做什么?这些是按钮在用户与之交互时可能采用的不同状态,我们需要相应地设置它们的样式。
我将在元素子层正下方创建一个新的级联子层,创造性地称为“states”(状态):
<code>/* 组件顶级层 */ @layer components { .button { /* 组件元素层 */ @layer elements { /* 所有按钮的通用样式 */ &:is(a) { /* <a>特定样式 */ } &:is(button) { /* <button>特定样式 */ } /* etc. */ } } }</button></a></code>
在这里暂停并思考一下。我们应该针对哪些状态?我们想为这些状态中的每一个更改什么?
某些状态可能共享相似的属性更改,例如:hover
和:focus
具有相同的背景颜色。幸运的是,CSS 为我们提供了解决此类问题的工具,使用:where()
函数根据状态对属性更改进行分组。为什么使用:where()
而不是:is()
?:where()
具有零特异性,这意味着它比:is()
更容易覆盖,:is()
采用其参数中特异性得分最高的元素的特异性。在编写可扩展、可维护的CSS时,保持低特异性是一种美德。
<code>.button {} .button-primary {} .button-secondary {} .button-warning {} /* etc. */</code>
但是我们如何以有意义的方式更新按钮的样式呢?我的意思是,我们如何确保按钮看起来像悬停或聚焦?我们只需在其上添加一个新的背景颜色,但理想情况下,颜色应该与元素层中设置的background-color
相关。
因此,让我们稍微重构一下。之前,我将.button
元素的background-color
设置为darkslateblue
。我想重用该颜色,因此最好将其转换为CSS变量,以便我们可以一次更新它并使其应用于所有地方。依赖变量是编写可扩展和可维护CSS的另一个优点。
我将创建一个名为--button-background-color
的新变量,它最初设置为darkslateblue
,然后将其设置为默认按钮样式:
<code>.button { /* 所有按钮的通用样式 */ }</code>
现在我们已经将颜色存储在变量中,我们可以在其他层中将相同的变量设置为按钮的悬停和焦点状态,使用相对较新的color-mix()
函数将darkslateblue
转换为更浅的颜色,当按钮悬停或聚焦时。
回到我们的状态层!我们首先在一个名为--state-background-color
的新CSS变量中混合颜色:
<code>/* 组件顶级层 */ @layer components { .button { /* 所有按钮的通用样式 */ } }</code>
然后,我们可以通过更新background-color
属性来应用该颜色。
<code>/* 组件顶级层 */ @layer components { .button { /* 组件元素层 */ @layer elements { /* 样式 */ } } }</code>
除了元素和状态层之外,你可能还在寻找组件中某种类型的变化,例如修饰符。这是因为并非所有按钮都会像你的默认按钮一样。你可能想要一个带有绿色背景颜色的按钮,供用户确认决策。或者你可能想要一个红色的按钮,在单击时表示危险。因此,我们可以采用现有的默认按钮样式并为这些特定用例修改它们。
如果我们考虑级联的顺序——总是从上到下流动——我们不希望修改后的样式影响我们刚刚制作的状态层中的样式。因此,让我们在元素和状态之间添加一个新的修饰符层:
<code>@layer components { @layer elements { .button { /* 按钮样式... */ } } }</code>
与我们处理状态的方式类似,我们现在可以为每个按钮修饰符更新--button-background-color
变量。当然,我们可以进一步修改样式,但我们保持相当简单,以演示此系统的工作原理。
我们将创建一个新的类,该类将默认按钮的background-color
从darkslateblue
修改为darkgreen
。同样,我们可以依赖:is()
选择器,因为在这种情况下我们需要增加的特异性。这样,我们就可以用修饰符类覆盖默认按钮样式。我们将此类称为.success
(绿色是“成功”的颜色)并将其提供给:is()
:
<code>.button {} .button-primary {} .button-secondary {} .button-warning {} /* etc. */</code>
如果我们将.success
类添加到我们的一个按钮中,它将变成darkgreen
而不是darkslateblue
,这正是我们想要的。由于我们已经在状态层中进行了一些color-mix()
操作,因此我们将自动继承这些悬停和焦点样式,这意味着在这些状态下darkgreen
会变浅。
<code>.button { /* 所有按钮的通用样式 */ }</code>
我们可以将需要修改的任何CSS属性重构为CSS自定义属性,这为我们提供了很大的定制空间。
<code>/* 组件顶级层 */ @layer components { .button { /* 所有按钮的通用样式 */ } }</code>
附注:仔细查看该演示,并查看我是如何使用light-dark()
调整按钮背景的——然后阅读Sara Joy的“来到light-dark()一边”,以全面了解其工作原理!
你怎么看?你会用它来组织你的样式吗?对于一个组件很少的小项目来说,创建级联层系统可能是过度的。但即使像我们刚才那样稍微尝试一下,也能说明我们在管理——甚至驯服——CSS级联方面拥有多大的能力。按钮具有欺骗性地复杂,但我们看到了处理从默认样式到编写其状态和修改版本的样式需要多少样式。
以上是使用CSS级联层组织设计系统组件图案的详细内容。更多信息请关注PHP中文网其他相关文章!