本文节选自Tiffany新书《CSS大师,第二版》。我们将探讨两种CSS命名方法论。这两种方法都是为了改进大型网站和大型团队的开发流程而创建的;然而,它们同样适用于单人团队。您可以选择其中一种、两种都不选,或者混合使用,这取决于您自己。介绍它们的目的是帮助您思考编写自己的CSS的方法。
关键要点
BEM,或块-元素-修饰符,是一种方法论、命名系统和一套相关的工具。BEM诞生于Yandex,旨在由大型开发团队进行快速开发。在本节中,我们将重点关注概念和命名系统。BEM方法论鼓励设计师和开发人员将网站视为可重用组件块的集合,这些块可以混合和匹配以创建界面。块只是文档的一部分,例如标题、页脚或侧边栏,如下图所示。也许令人困惑的是,“块”在这里指的是构成页面或应用程序的HTML片段。
块可以包含其他块。例如,标题块可能还包含徽标、导航和搜索表单块,如下所示。页脚块可能包含站点地图块。
比块更细粒度的是元素。正如BEM文档所解释的:
元素是执行特定功能的块的一部分。元素依赖于上下文:它们只有在其所属的块的上下文中才有意义。
例如,搜索表单块包含文本输入元素和提交按钮元素,如下图所示。(为了明确起见,我们使用的是设计元素意义上的“元素”,而不是HTML元素意义上的“元素”。)
另一方面,主要内容块可能具有文章列表块。此文章列表块可能包含一系列文章推广块。每个文章推广块可能包含图像、摘录和“阅读更多”元素,如下所示。
块和元素共同构成了BEM命名约定的基础。根据BEM规则:
块名称和元素名称通常用双下划线(.block__element)分隔。块和元素名称通常用双连字符与修饰符名称分隔(例如,.block--modifier或.block__element--modifier)。以下是使用搜索表单示例的BEM外观:
<div class="search"> <label for="s" class="search__label">Search for: </label> <input type="text" id="s" class="search__input"> <button class="search__submit">Search</button> </div>
具有深色背景的此表单的变体可能使用以下标记:
<div class="search search--inverse"> <label for="s" class="search__label search__label--inverse">Search for: </label> <input type="text" id="s" class="search__input"> <button class="search__submit search__submit--inverse">Search</button> </div>
我们的CSS可能如下所示:
.search { color: #333; } .search--inverse { color: #fff; background: #333; } .search__submit { background: #333; border: 0; color: #fff; height: 2rem; display: inline-block; } .search__submit--inverse { color: #333; background: #ccc; }
在我们的标记和CSS中,search--inverse和search__label--inverse是附加的类名。它们不是search和search__label的替代品。类名是BEM系统中使用的唯一选择器类型。可以使用子选择器和后代选择器,但后代也应该是类名。元素和ID选择器是被禁止的。强制执行块和元素名称的唯一性还可以防止命名冲突,这在团队中可能会成为问题。这种方法有几个优点:
BEM的内容远不止本章节中的一节所能容纳的。BEM网站更详细地描述了这种方法,并且还提供了工具和教程来帮助您入门。要了解有关BEM命名约定方面的更多信息,另一个极好的资源是Get BEM。
如果说BEM是行业宠儿,那么原子CSS就是它的叛逆者。雅虎的Thierry Koblentz在其2013年的文章“挑战CSS最佳实践”中命名并解释了原子CSS,它使用一个紧凑的类名库。这些类名通常是缩写的,并且与它们影响的内容脱节。在原子CSS系统中,您可以知道类名是什么作用——但是类名(至少,样式表中使用的类名)和内容类型之间没有关系。让我们用一个例子来说明。以下是我们在所谓的传统CSS架构中可能称之为的一组规则。这些规则集使用描述其应用内容的类名——全局消息框,以及“成功”、“警告”和“错误”消息框的样式:
<div class="search"> <label for="s" class="search__label">Search for: </label> <input type="text" id="s" class="search__input"> <button class="search__submit">Search</button> </div>
要创建错误消息框,我们需要将msg和msg-error类名都添加到元素的class属性:
<div class="search search--inverse"> <label for="s" class="search__label search__label--inverse">Search for: </label> <input type="text" id="s" class="search__input"> <button class="search__submit search__submit--inverse">Search</button> </div>
让我们将其与原子系统进行对比,在原子系统中,每个声明都成为它自己的类:
.search { color: #333; } .search--inverse { color: #fff; background: #333; } .search__submit { background: #333; border: 0; color: #fff; height: 2rem; display: inline-block; } .search__submit--inverse { color: #333; background: #ccc; }
这是更多的CSS。现在让我们重新创建我们的错误消息组件。使用原子CSS,我们的标记变为:
.msg { background-color: #a6d5fa; border: 2px solid #2196f3; border-radius: 10px; font-family: sans-serif; padding: 10px; } .msg-success { background-color: #aedbaf; border: 2px solid #4caf50; } .msg-warning { background-color: #ffe8a5; border-color: #ffc107; } .msg-error { background-color: #faaaa4; border-color: #f44336; }
我们的标记也更冗长。但是当我们创建警告消息组件时会发生什么?
<p class="msg msg-error">An error occurred.</p>
两个类名发生了变化:bg-d和bc-d被bg-c和bc-c替换。我们重用了五个规则集。现在,让我们创建一个按钮:
.bg-a { background-color: #a6d5fa; } .bg-b { background-color: #aedbaf; } .bg-c { background-color: #ffe8a5; } .bg-d { background-color: #faaaa4; } .bc-a{ border-color: #2196f3; } .bc-b { border-color: #4caf50; } .bc-c { border-color: #ffc107; } .bc-d { border-color: #f44336; } .br-1x { border-radius: 10px; } .bw-2x { border-width: 2px; } .bss { border-style: solid; } .sans { font-style: sans-serif; } .p-1x { padding: 10px; }
嘿!在这里,我们重用了四个规则集,并且避免向我们的样式表中添加更多规则。在一个强大的原子CSS架构中,添加新的HTML组件(例如文章侧边栏)不需要添加更多CSS(尽管实际上,它可能需要添加更多内容)。原子CSS有点像在CSS中使用实用程序类,但达到了极致。具体来说,它:
但是,原子CSS并非没有争议。
原子CSS与我们学习的几乎所有关于编写CSS的内容都背道而驰。它感觉几乎和到处粘贴样式属性一样糟糕。事实上,对原子CSS方法论的主要批评之一是它模糊了内容和演示之间的界限。如果将元素浮动到左侧并添加十像素的边距,当我们不再希望该元素浮动到左侧时该怎么办?当然,一个答案是将fl类从我们的元素中删除。但是现在我们正在更改HTML。使用CSS的全部原因是为了使标记不受演示的影响,反之亦然。(我们也可以通过从样式表中删除.fl {float: left;}规则来解决此问题,尽管这会影响每个类名为fl的元素。)尽管如此,更新HTML可能是为了获得更简洁的CSS而付出的少量代价。在Koblentz的原始文章中,他使用了诸如.M-10(margin: 10px)和.P-10(padding: 10px)之类的类名。这种命名约定的问题应该很明显。更改为五像素或20像素的边距意味着我们需要更新我们的CSS和HTML,否则可能会导致类名无法准确描述其效果。使用诸如p-1x之类的类名,如本节所述,解决了这个问题。类名中的1x部分表示比率,而不是定义的像素数。如果基本填充是五像素(即,.p-1x { padding: 5px; }),则.p-2x将设置十像素的填充。是的,这不太能描述类名所做的工作,但这同时也意味着我们可以更改CSS而无需更新HTML,并且不会创建误导性的类名。原子CSS架构不会阻止我们使用描述内容的类名在我们的标记中。您仍然可以向代码中添加.button-close或.accordion-trigger。对于JavaScript和DOM操作,此类类名实际上更可取。
当您有大量开发人员并行构建CSS和HTML模块时,BEM效果最佳。它有助于防止大型团队创建的错误和bug。它可以很好地扩展,部分原因是命名约定具有描述性和可预测性。BEM不仅仅适用于大型团队,但它非常适合大型团队。当有一个小型团队或一名工程师负责开发一组CSS规则,并由一个更大的团队构建完整的HTML组件时,原子CSS效果更好。使用原子CSS,开发人员只需查看样式指南——或CSS源代码——即可确定特定模块所需的类名集。
在实践中,您的CSS可能包含多种方法的混合。除了影响布局的实用程序类名之外,您可能还有一些描述内容或组件的类名。如果您无法完全控制标记(例如使用CMS),那么这两种方法都可能没有用。您甚至可能需要使用冗长且特定的选择器来实现您想要的目标。
BEM(块、元素、修饰符)和原子CSS都是组织和构建CSS代码的方法论。BEM专注于一种命名约定,使CSS更容易阅读和理解。它将设计划分为块、元素和修饰符,以在CSS和HTML之间创建清晰、严格的关系。另一方面,原子CSS是关于编写反映视觉功能的小型、单一用途的CSS类。它鼓励可重用性并旨在减少代码量。
BEM通过在CSS和HTML之间提供清晰而严格的关系来提高CSS的可扩展性。它使用特定的命名约定,使理解不同元素之间的关系更容易。这使得代码更易于维护和扩展,因为更容易添加新功能或修改现有功能而不会破坏任何东西。
是的,可以同时使用BEM和原子CSS。一些开发人员发现,结合这两种方法可以获得两全其美的好处。BEM的严格命名约定可用于构建CSS,而原子CSS的单一用途类可用于设置单个元素的样式。这种组合可以产生高度组织化和易于维护的CSS代码库。
原子CSS提供了许多好处。它鼓励可重用性,这可以大大减少您需要编写的CSS数量。它还提高了设计的一致性,因为相同的类用于不同的组件。此外,原子CSS可以使您的样式表更易于管理和理解,因为每个类都有一个单一、明确定义的目的。
BEM通过鼓励开发人员使用类选择器而不是ID选择器来帮助处理CSS特异性问题。这导致整个项目中的特异性保持一致,因此在必要时更容易覆盖样式。此外,BEM的命名约定清楚地表明哪些元素是相关的,从而减少了意外样式冲突的可能性。
是的,原子CSS适用于大型项目。它对可重用性和单一用途类的关注可以帮助保持CSS的可管理性,即使项目不断发展壮大。但是,它需要一种严谨的方法来确保类保持一致和有意义。
BEM清晰而严格的命名约定使团队成员更容易理解CSS代码,无论他们何时加入项目。这可以改善协作,因为开发人员可以轻松理解和修改其他人编写的代码。
原子CSS的一个潜在缺点是它可能导致HTML中出现大量类。这可能使HTML更难阅读和理解。此外,原子CSS需要一种严谨的方法来确保类保持一致和有意义。
要开始实现BEM,您需要将设计划分为块、元素和修饰符。然后,使用BEM的命名约定来命名您的CSS类。这将在您的CSS和HTML之间创建清晰的关系,使您的代码更易于阅读和维护。
是的,BEM和原子CSS都可以与Sass或Less等CSS预处理器一起使用。这些预处理器可以使管理CSS更容易,并且它们与BEM和原子CSS的组织原则非常契合。
以上是CSS体系结构:Block-Element-Modifier(BEM)和Atomic CSS的详细内容。更多信息请关注PHP中文网其他相关文章!