有人可能会说 CSS 很容易,但这种“容易”会导致代码混乱。在使用 Sass 或 Less 等预处理器时尤其如此,如果您不小心,您的 CSS 会变得更难处理,而不是更容易。Sass?更难?此 Gist 展示了 Sass 嵌套地狱的一个很好的例子。
如果您的 Sass 代码看起来像那样,您绝对可以通过 **SEM & BIO** 来改进您的代码,这是一种我将在本文中向您介绍的 CSS 技术!
在本文中,我将使用下面的代码示例来解释 SEM 和 BIO 的工作原理以及它们如何帮助增强您的 CSS 策略。
查看 thebabydino 在 Pen 上的代码 (@thebabydino),位于 CodePen 上。
通常,SEM 关注的是高级 CSS 理念,而 BIO 是一种实际的技术,可以帮助您编写更好的 CSS 来实现 SEM。SEM 和 BIO 的主要目的是更好地处理 CSS 特定性,这是您应该了解 CSS 的最重要概念之一。

首先,让我们谈谈 SEM。
SEM
SEM 代表
- 可扩展性
- 可扩展性
- 可维护性
努力实现这三个因素肯定会改善您的 CSS 代码,并帮助您构建更可靠的组件。
让我们更详细地讨论每个因素。
可扩展性
可扩展(或可重用)组件意味着,无论您想在何处使用外观相同的组件,都无需进行任何代码更改。

从上面的 CodePen 示例中,标题中的“搜索”按钮与侧边栏中的“链接”按钮看起来完全相同。当我们比较 HTML 标记时,
- “搜索”按钮是
<button>
元素 - 但“链接”按钮是
<a role="button" ...>
元素
…即使标记不同,样式也通过使用相同的类保持一致:.c-btn
和 .c-btn--yellow
。
按钮样式是可扩展的,它允许您在需要的地方添加外观相同的组件,因为它的父级或同级不会污染它。这可以免除您不知道为什么完全无关的组件会损坏的巨大麻烦,即使这些更改是在完全不同位置的另一个组件上进行的。

可扩展性
可扩展组件可以轻松地提供额外的功能/功能,而不会破坏自身或需要从头开始编写。
让我们再次看一下 CodePen 示例。
查看 iamryanyu 在 Pen 上的代码 (@iamryanyu),位于 CodePen 上。
标题和主部分中的按钮看起来非常相似,除了 3D 效果。在这种情况下,我们可以通过只添加 3D 效果来扩展普通按钮样式,而不是创建两个具有完全不同代码库的不同按钮集。
页脚中的按钮也是如此。即使按钮具有不同的颜色和大小,我们也可以通过添加或删除新的或不同的功能来轻松地扩展它。

可维护性
对于大多数前端开发人员来说,最大的挑战之一可能是理解其他人或过去自我的 CSS 代码。我们有时花费更多时间试图理解现有的代码,而不是添加精心编写的新的代码。
问题通常来自
- 没有任何注释
- 过度设计
- 没有单一的事实来源
- 没有考虑编码标准/最佳实践
- 或以上所有

使用 SEM 和 BIO,我们绝对可以改进代码,并让其他人(包括我们自己!)免受混乱、难以维护的代码的困扰。
BIO
有很多很棒的技术可以改进我们编写 CSS 的方式,根据我的经验,我发现以下三种构成 BIO 首字母缩略词的技术可以很好地协同工作。
- BEM
- ITCSS
- OOCSS

许多开发人员/工程师已经了解这些著名的技术,但我想逐一介绍它们,并讨论我使用这些技术的方式。
BEM
BEM 是一种非常流行的方法,它一直在帮助我们显著改进我们对 CSS 和 Sass/Less 的思考方式。
BEM 代表
- 块
- 元素
- 修饰符

正如上面的糟糕示例所示,我们倾向于过度使用 Sass/Less 的功能,从而陷入嵌套地狱。但使用 BEM,我们通过在 CSS 特定性中保持一到两层嵌套的样式,开始拥有非常低的 CSS 特定性。
如果您经历过任何与更高 CSS 特定性作斗争的战斗,您就会知道赢得胜利有多么痛苦。
回到我们的示例,HTML 标记看起来像这样
<div class="o-grid">
<div class="o-grid__item o-grid__header">
...
</div>
<div class="o-grid__item o-grid__main">
...
</div>
<div class="o-grid__item o-grid__sidebar">
...
</div>
<div class="o-grid__item o-grid__footer">
...
</div>
</div>
该示例包含
- 一个块:
.o-grid
- 元素:
.o-grid__item
、.o-grid__header
、.o-grid__main
、.o-grid__sidebar
、.o-grid__footer
因为 BEM 提供了一种命名约定,强调独特的类,所以我们不必进行像
.o-grid {
.o-grid__item {
...
}
}
相反,我们可以使用较少的层级定义它的样式
.o-grid__item {
...
}
这是 BEM 最大的优点;降低 CSS 特定性,从而提高整个 CSS 编码效率和体验。
即使使用 BEM,我仍然偶尔会看到一个问题,即命名不当。如果您不够注意,您最终可能会得到一个非常长的类名,例如
/* Yikes! */
.o-grid__item-search-button-text-svg-icon {
...
}
创建类名时,遵循 BEM 的核心概念:您的组件是一个块,块内部的所有元素都分别附加到该块。
再次从我们的示例中,我命名了 .o-grid__form
而不是 .o-grid__item-form
,因为表单本身是一个单独的组件,不需要绑定到 o-grid__item
并作为其子级。
此外,为了更有效地控制样式,我在 .o-grid__item
旁边添加了另一个类名 o-grid__header
来扩展样式。此外,按钮包含使用 OOCSS 方法的 BEM 风格类,我们将在下一节中讨论。
OOCSS
正如我们已经讨论的那样,有很多很棒的 CSS 方法和策略可以帮助我们改进编写 CSS 的方式。但是,我看到很多人强迫自己从一堆方法中选择一种方法。
根据我的经验,结合多种方法实际上可以通过结合多个世界的精华来增强它们的好处。例如,我个人发现 BEM 和 OOCSS 可以很好地协同工作。
OOCSS 代表 **面向对象的 CSS**,您可以将其想象成像乐高积木一样工作。

OOCSS 分别创建每个单独的部分,然后将它们组合在一起以构建组件。
从我们的示例中,我使用 OOCSS 命名约定创建了按钮
.c-btn
.c-btn--yellow
.c-btn--blue
.c-btn--3d
.c-btn--large
为了在我们的示例标题中呈现黄色的搜索按钮,我们将这些类组合在一起
.c-btn
.c-btn--yellow
如果我们想要在主部分中使用 3D 按钮,则添加 3D 类 .c-btn--3d
,然后就可以了。
对于页脚中的蓝色按钮,我们可以将黄色的修饰符切换为蓝色,以及大的修饰符。如您所见,按钮不依赖于标题块,这使我们能够更灵活地使用和重用组件。而且,通过这样做,我们可以构建按钮而不会影响任何其他组件或模式,同时获得轻松扩展新的表示性功能的益处,例如备用颜色和形状。
以下是使用 OOCSS 创建按钮集合以创建变体的示例
查看 CodePen 上 Ryan Yu (@iamryanyu) 的 现代按钮集合。

在 BEM 和 OOCSS 的基础上,借助 ITCSS,我们可以进一步改进我们的 CSS 策略。 让我们接下来看看这种方法。
ITCSS
ITCSS 代表 **倒三角形 CSS**,它通过应用确定特定组件的具体程度的结构来帮助组织 CSS。 Lubos Kmetko 撰写了 关于 ITCSS 的优秀概述,值得一读。
您可以在 此 Gist 中看到我如何通过将样式按分组的特定级别拆分来使用 ITCSS。
根据该示例,您可以看到我如何通过向类添加命名空间来命名 **组件**。 例如,"按钮" 组件以 "c" 为前缀 (.c-button
) 来指示组件状态,并防止将其误认为其他项目。 这样,所有参与项目的人员都知道该特定类的功能以及更改其属性可能会对其他区域造成的影响。
以下是一个说明所有 ITCSS 级别 的可视化图表

让我们逐节看一下。
设置
设置通常是一组不会生成 CSS 但会应用于类的变量。 一些示例包括
- 基础
- 颜色
- 排版
- 动画
工具
工具也不会产生任何 CSS,它们通常是预处理器函数,有助于编写或扩展类上的属性
- 函数
- 占位符
- 混合
- 媒体查询
供应商
供应商是项目中使用的第三方样式。 想象一下像 reset.css、normalize.css 甚至 Foundation 和 Bootstrap 之类的东西。
这些样式在结构中处于较高的位置的原因是,以便我们可以在需要时覆盖它们。 如您所知,如果两次调用相同的类,级联将呈现第二个实例的属性,假设属性完全相同。
.btn--large {
padding: 3em;
}
/* This one wins out */
.btn--large {
padding: 5em;
}
作为旁注,在 Sass 中,您可以使用 ~
指向 node_modules
文件夹,以便您可以从源代码导入样式资产,而不必将其移动到您自己的目录中。
@import '~modern-normalize/modern-normalize';
对象
对象 (命名空间:o-
) 用于设计模式,例如布局,其中项目被排列而不是装饰。 对象类在所有页面中使用,因此,如果您对对象类进行任何更改,您应该非常小心,因为任何更改都会影响整个网站的每个页面。
我使用的最常见的对象类是
.o-page
:最外层的容器,通常包含max-width: 100vw
和overflow: hidden
。.o-main
:主区域的外层容器。.o-container
:组件的外层容器,通常提供固定宽度。.o-content
:如果实际内容区域需要额外的配置。.o-grid
:如果需要具有不同列数的网格布局。
您使用其他对象类吗? 如果是,请与我分享。 😃
元素
元素 (命名空间:e-
) 是我们不会根据类名设置样式的 HTML 本机元素。 例如,我们应该为 <a>
元素提供默认样式,而不是 .link
类。
// Do this for the default link style
a {
text-decoration: none;
&:hover {
background-color: blue;
color: white;
}
}
// Don’t provide the default link style to a class
.link {
text-decoration: none;
&:hover {
background-color: blue;
color: white;
}
}
这是因为,尤其是在 WordPress 等 CMS 中,您不希望每次在内容中使用链接时都添加一个类。 因此,我们为 <a>
元素提供默认样式,因此无需任何类,链接将仍然具有美观的样式。
组件
组件 (命名空间:c-
) 是构成网站一部分的小功能。 想象一下按钮、手风琴、滑块、模态对话框等。 每个组件本身都完全可行,并且不依赖于任何其他组件。 在命名组件时应考虑这一事实。
例如,以上示例中主部分中的按钮不应称为 .c-main-button
,因为主部分将其限定在 main
部分内,并限制了它在其他地方(如侧边栏)的使用。 像 .c-btn
这样的东西要好得多,因为按钮不再与页面的任何其他特定部分绑定。
如果您需要任何额外功能,您始终可以使用 BEM 修饰符(结合力量!)或使用 **范围** 来扩展属性,这将在稍后介绍。
模式
许多开发人员/工程师将 *组件* 和 *模式* 作为同义词使用,如果您对此感到更舒适,那完全没问题。 我只是更喜欢将这两个术语分开。
作为一般规则,我认为模式(命名空间:p-
)是组件的组合,但它 **不可** 扩展。
例如,我认为手风琴是一个组件。 它本身是可扩展的和可重用的,这意味着它可以在网站的其他部分使用而无需进行任何更改,即使手风琴包含其他组件,例如按钮。
另一方面,例如,页眉将是一个模式,因为它 **不可** 扩展(页眉不能在内容或侧边栏区域使用),并且还包含其他组件,例如按钮、手风琴、菜单、徽标、搜索表单等。
范围
请注意,我 *只* 在绝对必要时使用范围。 范围(命名空间:s-
)的目的是为我们提供最高的特定性,以便我们可以覆盖特定目的的任何样式。
请记住,如果您发现自己多次使用 *范围类*,那么您可能正在编写 *过于* 特定的样式,您应该考虑重构您的 CSS 结构。
以下是如何使用范围类 .s-home
的简单示例。
.c-accordion {
.s-home & {
// Changing the background color specically on the homepage
background-color: tomato;
}
}
作为旁注,上述示例实际上可以通过为手风琴提供一个修饰符(例如,.c-accordion--bg-tomato
)来重构,而不是使用范围类。 这将是更可扩展的编写方式,并使组件更模块化。
实用程序
有时您可能只想在特定位置更改特定样式。 在这种情况下,实用程序(命名空间:u-
)类可以帮助我们更新它,而无需更改整个 CSS 结构。
例如,手风琴标题的字体大小设置为 32px。
.c-accordion__heading {
font-size: rem(32);
}
但是,如果字体大小只在您网站的新闻部分有所不同,而在其他任何地方都没有改变,那么您可能希望应用实用程序类,而不是使用父类或范围类设置更高的特异性。
<button aria-expanded="false" class="c-accordion__heading u-font-size--24" aria-controls="sect1" id="accordion1id" type="button">...</button>
.u-font-size--24 {
font-size: rem(24) !important;
}
请注意,我们都知道!important
很糟糕,但我将!important
添加到值中。这是因为当我们使用实用程序类时,我们绝对确定我们希望以我们想要的方式更新特定的样式。此外,实用程序类应该覆盖任何其他样式,因此在这里使用!important
实际上非常适合实用程序类。也就是说,实用程序类只应该作为助手发挥作用。它不应该用于构建您的 CSS 结构。
与范围类类似,如果您使用太多实用程序类,您应该与设计师确认设计是否可以在整个网站上保持一致。
额外命名空间
除了我们上面讨论的命名空间之外,我还经常使用另外两个命名空间。
is-
:这表示块或元素的状态。最常用的类是.is-active
,例如导航中的活动链接。js-
:这表示特定元素绑定到 JavaScript 事件。例如,js-menu-click
表示该元素绑定到点击事件。
代码风格检查
最后,使用.stylelint
和.eslint
制定规则可以显着提高代码质量。
在前端工作流程中,我不把它作为建议,而是作为强制性要求,这样违反规则的行为将不会被批准。
通过这种方式,我们可以确保代码质量保持最佳状态,并为其他开发人员提供更好的代码,包括您未来的自己。
实际应用
在本节中,我想讨论如何使用 SEM 和 BIO。我做了一个简单实用的例子来帮助我们开始。
查看 iamryanyu 在 CodePen 上的 示例 (@iamryanyu)。
该示例的主要实践是构建一个手风琴,它可以作为以下内容使用:
- 普通手风琴,但在主部分使用不同的颜色主题
- 侧边栏中的菜单
- 在页脚中显示社交媒体图标的块
我们正在实现的是一个具有以下特点的组件:
- 可扩展的:因为它可以添加到页面的任何部分而无需任何代码更改
- 可扩展的:因为它可以在核心功能不变的情况下提供不同的功能
- 可维护的:因为它以一种有意义的方式进行了组织
为了实现 SEM,BIO 已被使用,包括:
- BEM:
.c-accordion
作为块,其子元素作为元素,还使用修饰符,例如,.c-accordion--light
和.c-accordion--dark
- ITCSS:SASS 文件的排序/排序很好地处理了 CSS 特异性。例如,侧边栏中的手风琴按钮包含
class="c-accordion__trigger p-sidebar-menu__button"
,其中模式 (p-
) 覆盖了组件 (c-
) 没有任何问题。 - OOCSS:手风琴由多个类构建,例如,
class="c-accordion c-accordion--dark c-accordion--single"
,它创建了一个深色主题,每次只打开一个面板。
最后的想法
我几乎在我的所有项目中都使用了这种方法,包括大学、政府部门、商业零售商以及许多其他网站。在每种情况下,我都成功地将所有项目交付给客户(在客户审批阶段几乎没有问题,并且按时交付);到目前为止,这种方法对我很有效,我认为它也可能对您有效。
也就是说,技术总是在变化(尤其是在前端),我很乐意听到和讨论您对您有效的任何想法/方法/策略。请在评论中告诉我!
我们一直在 Sky 使用类似的方法来处理 CSS,它对我们来说非常有效!
我在我们的 CSS 样式指南中写了一些这些概念
https://github.com/sky-uk/css
不要害怕混合方法
感谢分享
很高兴听到它对您很有效,Joe!
另外感谢您分享您的 CSS 样式指南
这确实代表了我的项目结构。
您认为实用程序类是否也适合这些方法?
如果是,您将如何将实用程序类与您的方法结合起来?
嗨,Simon,
感谢您指出这一点,是的,当然。 (我怎么会漏掉这一点,尽管它在 ITCSS 图片中 :o)。
我在额外命名空间部分之上添加了实用程序部分。请告诉我您的想法 :)
很棒的文章,谢谢
谢谢!
很棒的文章,对于许多有用的 CSS 技巧和窍门来说这是一个很好的参考点,当我需要解释它们时,我可以链接到人们!我唯一的问题是,使用 `~` 在 sass 中对 `node_modules` 进行加倍,这在最基本的 sass 中受支持,还是在 webpack 等支持下受支持?
感谢 Luke!
对于波浪号 (~),我使用 Sass Loader 与 Webpack,您可以在此处找到详细信息
https://github.com/webpack-contrib/sass-loader#imports
对于其他构建工具,您可能需要查看此内容
https://npmjs.net.cn/package/node-sass-tilde-importer
几乎与我的工作方式相同。我个人不使用范围类,制作修改后的版本几乎总是最好的选择。我也不会使用模式,只会使用组件。但我可能会在将来采用它,它似乎是一个很好的分离。我从未听说过这种技术叫做 SEM & BIO。我认为它很棒!感谢这篇文章。
很高兴看到许多人正在使用类似的方式,并且它有效!
对于 SEM & BIO 的命名,实际上是我命名的 :)
希望它容易记住。
哇…这篇文章改变了我对组件和对象之间区别的理解。这么久以来,我一直认为像按钮这样的东西是一个对象……因为它在多个地方使用,如果更改,可能会影响您当前查看的上下文之外的地方。
在最后的例子中,你为什么没有在你的 JavaScript 选择器中使用 `js-` 前缀?
嗨,James,很高兴听到它有所帮助 :)
对于 `js-` 前缀,这是一个很好的问题!
我没有使用 `js-` 前缀,实际上你可以使用它,这是因为我需要不断地将其添加到每个标题中,我认为这很冗余。另外,我相信我的团队知道手风琴标题是可点击的,因此如果它有任何问题,他们很可能会查看 JS 代码。
但再次,决定将取决于项目以及您和您的团队如何协作。
非常有趣……不过要记住很多规则。
嗨,Jon,一开始,它可能有点让人不知所措,但一旦尝试了 1-2 个项目,根据我的经验,它更有意义 :)
ABEM 怎么样?你用过吗?
我认为 ABEM 很棒,因为它可以避免 BEM 中经常遇到的长类名。
不过,我个人更倾向于降低 CSS 特定性,因此 ABEM 不太适合我的 CSS 策略。
话虽如此,再次强调,没有正确或错误的答案,如果它适合你和你的团队,并且你的代码能够满足 SEM 的要求,那就用吧 :)
你可以在这里找到关于 ABEM 的优秀文章
很棒的文章,对于开始学习前端开发的人来说是个很好的参考资料。我一定会与我的同事分享。
你介意分享一下你的构建过程吗?或者至少部分内容?我看到你在评论中写道,你将 webpack 与 sass 结合使用。你是将这种方法与 patternlab 结合使用,还是从头开始开发一切?
感谢 Quentin!
我自己的样板代码(使用 Webpack 构建)和模式库(使用 SEM & BIO 构建)。我计划在它更稳定后与公众分享 :)
我甚至不会考虑将类名用作 JS 钩子。
我一直构建和整理了一系列我称为行为的东西,它们会挂钩到数据属性,并通过类似前缀的数据属性进行配置。
事实上,在普通的 CMS 驱动的网站中,我们无法有效地使用 vuejs 或 react,而且你的团队可能也不理解它们,因此你只能做一些基本的类切换/添加/删除操作。
这就是为什么像 jsblocks 或 stimulus 这样的框架非常有用的原因。
我不是 Stimulus 专家,但根据我之前的经验和其他工程师的评论,它是一个很棒的框架。我相信 Stimulus 与其他框架一起使用会非常有用。感谢你提到它!
以上是更纯粹的 CSS 策略,通过使用 JS 钩子,我们可以告诉其他开发人员,特定元素具有 JS 绑定。
我只是想知道,除了用你提到的框架来处理之外,还有什么其他原因让你不使用 JS 钩子吗?
我对这一点有点犹豫,因为它让维护一些 HTML 代码变得稍微困难了一些。
举个例子,客户的品牌决定改变颜色,因此所有黄色的按钮现在都是蓝色的。你需要要么在 HTML 中更改类名,要么添加一个名为“yellow”的类,而它实际上是蓝色的。
与其使用不太具体的名称,例如“.c-btn–primary-color”怎么样?名称会取决于客户,但这意味着在更大的项目中,当品牌决定进行品牌重塑时,它更容易维护。
我实际上测试过使用
primary-color
,我发现这样做对于大型项目来说并不安全/稳定,比如大学、银行、政府部门和电子商务网站。这是因为
1)大型组织通常不会更改他们的品牌,如果他们进行品牌重塑,通常会彻底改造他们的网站。
2)大型组织通常也有自己的开发团队,这些团队也在他们自己的方面处理 CSS 和 JS。问题在于沟通。即使提供了精心编写的文档,他们也经常各行其事,导致一些颜色没有 primary-color 变量或类。这意味着,如果你将 primary-color 更改为其他颜色,我们无法保证所有我们认为是 primary-color 的颜色都会被更新。
3)拥有 primary-color 类,其中包含不只是颜色,还有其他样式(例如,宽度、填充、字体大小、字体粗细、阴影等),如果一个特定的按钮具有略微不同的样式,则更新起来会更难。
4)对于最终用户来说,很难在 CMS 中控制样式。用户可能希望为 A/B 测试或任何营销目的创建不同颜色的变体。特别是,电子商务网站会进行大量拆分测试,因此能够创建不同变体的模式对他们来说非常重要。
5)如果有一个“primary-color”,那么也会有一个“secondary-color”,可能还有一个“tertiary-color”。如果这 3 个按钮样式差异很大,我认为设计需要解决,我会与设计师讨论,因为我认为一致性在 web 开发中起着重要作用。如果这 3 个按钮看起来相似,那么很可能这 3 种样式(primary、secondary 和 tertiary)重复了相同的属性。
在较小的项目中它可能效果很好,但根据我的经验,我更倾向于在大型项目中不使用“primary-color”。
很棒的文章,Ryan,感谢你!尤其是 ITCSS 部分。虽然我仍然发现对象和组件之间的区别在大多数情况下都很模糊,无论我读了多少关于这个概念的文章。我通常最终只是将所有内容都扔到组件命名空间中。
另外还想说一下,波浪号展开来从 Node 模块中导入,并不是像你的措辞所暗示的那样,是 Sass 标准。可能会让一些人感到困惑。
嗨,Matthias,
对象和组件可能会让人困惑,但你可以这样想:对象是在所有页面中绝对使用的(例如,布局),而组件可能不会在某些页面中使用(例如,手风琴)。希望不会让你更困惑 :)
非常感谢你指出来,我在上面的评论中添加了一些解释:@Luke
我喜欢 ITCSS 结构,它在我的项目中与 BEM 结合使用效果很好。但我对“模式”这个概念很陌生,这个名字真的让我很困惑。对我来说,更好的名字应该是部分、区域或区域。
很高兴听到 ITCSS 与 BEM 的结合使用效果很好。
如果你感到困惑,你不必使用“模式” :)
这是我的偏好,你可以使用更简单的术语。我建议与你的团队进行沟通,确保每个人都在同一页上。
感谢你,很棒的纯 CSS 技巧。
谢谢!
嘿,Ryan,
.is-active
类是不是太泛了?例如,按钮或其他类型的链接可能是活动的,并且使用相同的类,但可能具有完全不同的外观。你会如何处理这种情况?嘿,Morgan,你说得对,感谢你提出这个问题!
我应该在文章中提到,但有一些可能的解决方法。
1. 使用更具体的
.is-active
类。2. 合并两个类。
3. 使用 BEM 修饰符。
#1 是我通常的做法,因为类名更具描述性,我们还可以分离样式和状态。
如果需要在
<body>
中添加状态钩子(例如,模态的页面覆盖),它也能很好地工作。你能分享一下你是如何处理的吗?
嘿,Ryan,感谢你的示例!
这确实取决于用例;例如,独自处理自己的代码,或者在团队中工作。独自工作意味着我负责确保我没有在示例 #2 的范围之外编写
.is-active
。但是,在团队中工作意味着任何人都可以编写.is-active
,因为它已经成为一种约定俗成的说法。因此,在团队中工作的情况下,我会选择 #1 或 #3,因为不太可能有人会编写出带前缀的类,比如:.is-accordion-active
。再次感谢你,很棒的文章!
感谢你分享,Morgan!
是的,我发现无论我们采用哪种方法,重要的是要确保团队中的每个人,包括我自己,都在同一页上,并且了解团队的最佳实践,这样可以减少出现混乱代码的可能性。
这太棒了,Ryan,感谢你花时间把它整理出来。你让我对我一直试图做但做错了的事情有了更好的理解 :)
感谢 Bekah,很高兴听到它对你有帮助!
绝对棒极了,Ryan!这里有一些很棒的标准,我从现在开始就要采用它们了!
非常感谢你,James :D !
在多年的编码之后,我也使用了一些有用的技巧。
也许这个分享可以给你一些想法 :-)
/* 在纯 CSS 中使用变量 */
:root
{
/* 定义主题 */
–color-primary : hsl(30, 70%, 60%) ;
}
/* 设置响应式变量,减少工作量,代码更简洁! */
@media (min-width:50em)
{
:root
{
–responsive-type : “tablet” ;
–responsive-indice : 1.2 ;
}
}
@media (min-width:72em)
{
:root
{
–responsive-type : “small-screen” ;
–responsive-indice : 1.3 ;
}
}
@media (min-width:100em)
{
:root
{
–responsive-type : “medium-screen” ;
–responsive-indice : 1.4 ;
}
}
/* 在页面 url 中添加一个简单的键,即可实现可访问的白色模式(index.html?debug) */
@-moz-document regexp(“.+(update|debug)”)
{
:root
{
–debug-visibility : visible ;
}
}
/* 使用可扩展类! /
/ @sample
test
test
*/
/* 构建你的 UI 类名 /
[class^=”ui-“]
{
box-sizing : border-box
}
/ 轻松扩展你的 UI 类名 /
[class^=”ui-“][class=”-dark”]
{
background-color : silver
}
/* 等等… /
[class=”-3d”]
{
border : 5px outset silver ;
}
[class*=”-right”]
{
float : right ;
}
/* 然后,编写你的通用代码 */
/* 我的 CSS 助手 */
body::after
{
position : absolute ;
background-color : var(–color-primary) ;
bottom : 0 ;
right : 0 ;
z-index : 1000 ;
display : block ;
padding : 0.5em;
content : “Css message”;
content : “Css responsive type: ” var(–responsive-type) ;
visibility : hidden ;
visibility : var(–debug-visibility) ;
font-family : courier ;
font-size : 80%
}
/* 我的主题 */
body
{
font-size : calc( 1em * var(–responsive-indice) );
}
嗨,Xavier,
你的代码的 Markdown 格式似乎没有正常工作。你能重新发布一下你的代码,以便我们都能看到你的技术吗?
感谢你写了这篇很棒的文章,但我有一个问题。你创建了不同类型的按钮,并说它是 OOCSS。但你也可以使用修饰符来创建所有类型,并说它是 BEM 方法。它们之间有什么区别?
谢谢!
BEM 和 OOCSS 之间的区别在于它们主要关注的方面。
BEM 帮助我们通过使用唯一的类名来降低 CSS 的特异性。例如,使用 BEM 的手风琴中的标题将看起来像
但如果不使用 BEM,我们会这样做
这最终会导致更高的特异性。
OOCSS 为我们提供了一种更好的方法来构建组件,如上文所述。
在 BIO 中,我将这两种方法结合在一起,因此语法看起来像 BEM,这使我们能够获得更低的 CSS 特异性,但同时能够使用 OOCSS 以更好的方式构建组件。
这就是为什么你看到像
.c-button--red
这样的代码,它就是 BEM + OOCSS。希望这有帮助:)
嗨,Ryan,很棒的文章!
我很好奇的一件事是:你对辅助类有什么看法?
自从 Bootstrap 4 出现后,我的同事们就大量使用它们,比如用于边距、填充、flex 等。
但到目前为止我还没有被说服,因为我发现它们不够灵活。
谢谢 Sebastian!
关于辅助类,可能取决于项目。
辅助类可能与 Bootstrap 这样的框架配合得很好,因为它们在大多数情况下都希望提供某些样式,以便最终用户能够构建具有不同变体的组件。但是,这通常会导致框架的大小变得不必要地大。
毫无疑问,Bootstrap 在某种程度上是一个很棒的框架,但我个人从来不使用任何 HTML/CSS 框架,除非是绝对需要的(例如,客户要求等)。主要原因是大小(包含未使用的 CSS)以及我通常进行的设计与框架通常提供的设计非常不同。
根据我的经验,过度使用辅助类可能会导致一些问题,特别是以下两个问题。
1. 维护
例如,假设你拥有这个辅助类
并将其添加到所有按钮中。
有一天,客户要求我们将按钮填充改为 15px。
在理想情况下,我们可以直接将
.u-padding-10
替换为.u-padding-15
,但在现实世界中,与直接更新按钮样式相比,找到所有按钮并替换辅助类实际上非常耗时。2. 响应式
如果你希望在较小的屏幕尺寸下具有
padding: 10px
,而在较大的屏幕尺寸下具有20px
,那么很难覆盖辅助类,因为 1) 辅助类应该是最高的 CSS 特异性,以及 2).u-padding-10
也可能用于其他组件,这些组件在较大的屏幕尺寸下不应为 20px。总的来说,如果你正在进行一个类似框架的项目,辅助类实际上可能很有帮助,但如果你正在从头开始构建网站/组件,我肯定会避免过度使用它。
你对此有什么想法?
嗨,我想知道为什么不使用带有媒体查询的实用程序类呢… 因此,例如,你可以使用
class=”u-padding-sm-10 u-padding-lg-20″
。嗨,James,
是的,我们当然可以。
但是,使用这种方法,我们应该在设计师和工程师之间达成一致,确定我们要使用的间距。
我在 Campaign Monitor 的设计系统中使用类似的方法,我发现确保设计师和工程师在同一页面上非常重要。如果不是这样,最终可能会导致许多实际上可以合并的实用程序(例如,
.u-padding-sm-20
与.u-padding-lg-21
等)。这个项目的结构(文件结构)会是什么样子?你有一个例子吗?
嗨,Fredrik,
这可能取决于你使用的样板。我使用我自己的样板,并使用 Webpack,因为静态前端项目和 React 项目之间存在不同的规格,但对于静态前端项目,一般的结构看起来像
https://www.dropbox.com/s/9h3eu03d0k2xi87/file-structure.png?dl=0
你想分享一下你如何构建你的项目吗?
嗨,Ryan,
这是一篇很棒的文章,有助于深入理解 CSS。
不过,我想问一下你如何构建你的 CSS 类?
.class {
定位
显示(盒子模型)
样式
排版
外观
动画
等等
}
也许在文章中添加这部分内容会是个好主意:)
谢谢 zngiron!
关于 CSS 属性的排序,这里有一篇很棒的文章你可以看看
我个人遵循字母顺序,因为我几乎总是知道自己想要更新什么,而且更容易找到。
而且我的团队也不必制定/记住额外的规则来了解如何分组属性。
我认为,如果 CSS 本身能自然地将属性名分组,这将有助于我们更好地组织属性。
例如,如果所有与文本相关的属性都以
text-
开头,我们就不必担心如何分组属性。但是,正如 Jen Simmons 在这里提到的 (https://twitter.com/jensimmons/status/1001242807758196736),现在我们有font-
和text-
;还有color
。但这仅仅是我的个人观点/愿望:)
你对此有什么想法?你将如何排序属性?
嗨,Ryan Yu,
我还没有看到任何使用这种模式的特定示例,所以我不确定我是否可能“做错了”。
假设我有一个按钮
<button class="c-button c-button--large">Click Me</button>
我想在卡片内使用这个按钮,但我不想重复样式。我宁愿在标记中清楚地表明卡片正在扩展按钮的样式——而不是在 CSS 中使用 SASS 的
mixin
或@extend
。c-card__button
类仍然具有低特异性,而且我在 CSS 中不使用这种选择器因此它实际上并不是按传统意义上的嵌套——它不会通过嵌套来提高特异性。
c-card__button
将添加仅在卡片中相关的样式——可能是定位或一些额外的间距或其他什么——但最终这些样式只会在按钮在c-card
内使用时才相关。嗨,James,
这将取决于卡片组件的设计/结构方式,但对于简单的情况,我会说这样可以正常工作;只需将按钮放在卡片范围内,使用
.c-card__button
(并添加一条注释,说明为什么使用卡片范围,以便其他工程师也能理解)。但是,如果卡片中有多种不同的按钮样式呢?难道我们不需要覆盖每个按钮吗?我想这可能会在以后堆积起来,导致 CSS 特异性问题。
在这种情况下,我会与设计师交谈,并以 OOCSS 的方式设计按钮,使其更具可扩展性。
写得很好!这与我阅读 ITCSS 后大约一年来一直使用的方法类似,它确实帮助我保持项目的组织性。一开始可能需要适应一下,但做一两个项目之后就会变得自然了。
我唯一想批評的是关于Elements及其
e-
前缀。 因为只有对于没有类别的元素(例如<a>
)才使用它,所以当你不使用e-
前缀时,命名空间是否有意义呢?除此之外,这篇文章很棒,概览也很不错。
谢谢Bernard,很高兴你喜欢这篇文章。
关于Elements,我通常在需要与实际标记不同的样式的情况下使用
e-
前缀。例如,假设我们有
<h2>This is heading</h2>
,但在某些地方,<h2>
可能看起来像<h1>
。在这种情况下,我会添加
e-
前缀的类,例如<h2 class=“e-h1”>This is heading</h2>
。 这样,我们就可以保持标记和样式都正确。对于
<a>
,就像你说的,我通常不使用元素前缀(例如e-link
),但我确实使用组件前缀(例如c-link
),并且将其用于链接和按钮。@Ryan 我猜这应该是个人喜好,但我一直将该用例应用于实用程序部分/前缀。 它是ITCSS中最具体的组成部分,所以我在需要语义上使用
h2
元素但希望它像h4
一样大小的情况下使用它。 在这些情况下,我只需使用u-h4,就像你提到的那样,所以这可能仅仅是个人对应该多具体地进行覆盖的个人喜好问题。无论如何,这篇文章很棒,干杯!
是的,我同意。
有效地使用实用程序可以减少CSS的重量,而且看起来Sara也在使用BEM :) https://mobile.twitter.com/SaraSoueidan/status/1019157731796799488