编写模块化CSS:命名空间

本文翻译自Zell的博客——【Writing modular CSS (Part 2) — Namespace】,原文地址:https://zellwk.com/blog/css-architecture-2/

本篇翻译的原文地址:

https://www.w3cplus.com/css/css-architecture-2.html

上周,我分享了如何使用BEM创建一个合理的CSS架构。 虽然BEM很棒,但它只是解决方案的一部分。 还有另一部分我还没有提到 —— 命名空间。

在今天的这篇文章中,我想与大家分享一下为什么只用BEM还是不够的,以及如何使用命名空间来弥补一些不足。

为什么BEM不能满足我们

我上周给大家展示的例子很简单。 我只向大家展示了如何处理单个块中不同的修饰符和子代(或孙子代)元素。 但是如果有多个区块咱怎么办呐?

事情有点复杂。 我们使用一个网站范围的导航来说明两个块之间的关系。

好啦。 现在有两个区块。 一个叫.main-nav,另一个叫.button。 .button存在于.main-nav内。

然后现在你想把这个button的颜色从blue变成green。同时你也想给.button加些左边距来和home链接分开

那么问题来了,你应该怎样书写CSS代码呢?这下面有几种可能的答案:

  • 给.main-nav .button添加margin和background-color
  • 给button--modifier添加margin和background-color
  • 给.main-nav .button添加margin,给button--modifier添加background-color
  • 给.main-nav a添加margin,给.main-nav .button添加background-color
  • 给.main-nav a添加margin,给button--modifier添加background-color

哪一种方式最能引起你的情感共鸣呢? 你又如何能确保您的项目中的每个开发人员都以同样的方式来接受呢?

即使您的所有开发人员都拷贝了这个方案(因此也是以同样的方式),您如何知道您是否没有引入副作用(破坏了网站的另一部分)?

老实说,很难保证!如果我们只有BEM,有太多可能的因素导致。

这就是之所以引入命名空间。它可以帮助你创建一个结构来控制CSS属性的写入。 如果您遵循惯例,您将能够无惧副作用地编写CSS。

这里是一个示例。

假设我把上面的代码转换成一个带有命名空间的代码。HTML将完全相同(只加了少数的class前缀)。 在这个例子中要特别注意.o和.c前缀:

.o-和.c-是什么意思呢?从这个代码来看,我知道如果我想,我可以改变.o-button的颜色,但我不应该添加任何边距到.o-button。

啥!? 那么我必须来解释这些命名空间,gogogo~ :)

我使用的命名空间

以下是我使用的命名空间列表:

  • .l-: 布局(layouts)
  • .o-: 对象(objects)
  • .c-: 组件(components)
  • .js: js的钩子(JavaScript hooks)
  • .is-|.has-: 状态类(state classes)
  • .t1|.s1: 排版大小(typography sizes)
  • .u-: 实用类(utility classes)

我们来看看具体每个命名空间是什么,以及咱应该怎么用。

在继续之前,如果您对命名空间不了解,我强烈建议您查看Harry Robert的具有命名空间的更透明的ui代码。(有趣的事实:Harry启发我使用命名空间)。

如果你阅读Harry的文章,要注意一下我的命名空间不同于他的。(待会儿我会分享有哪些不同的内容)。

以上,让我们先进入第一个命名空间 —— 布局(layouts)

“.l-” —— 布局(layouts)

我很确定你听说过@Nicole Sullivan的Object Oriented CSS(面向对象的CSS)(OOCSS)。 如果您还没有深入了解,那得知道OOCSS背后的主要思想是表层和结构的分离。 换句话说,影响块或其元素的位置的属性应该被抽象为一个单独的类用于重复利用。

在CSS中,定位块的行为也称为布局块。 在一般意义上,定位是布局。

也许这只是一个快乐的巧合(也许),但@Jonathan Snook在SMACSS中为布局规则建议一个.l-前缀。这两个范例在布局方面有着相同的原则。 因此,我很高兴地从SMACSS中窃取.l-作为布局命名空间。

既然你已经了解了命名空间的起源了,它可能会帮助你了解它的使用方式。 当涉及到布局时,我将布局分为两个不同的类别 —— 全局布局和块级布局。

全局布局

全局布局是应用于所有页面的布局。在我的用例中,它们通常是在任何地方都使用的大型网格容器。 一个例子是.l-wrap 类:

我将在每个地方都使用.l-wrap类,比如在header和footer里来对齐内容:

由于这些class在全局使用,所以我更喜欢把它们写在_layouts.scss部分。

块级布局

每个块(对象或组件,我们将在后面讨论)可能有自己的布局。 通过个人经验,我发现这些布局通常独立于全局布局。

让我来举个栗子。

当我为Mastering Responsive Typography建站后,我添加了一个如下所示的付款表单:

响应式排版的付款表单

在上面的设计中,您可以看到该表单包含两行输入元素。 第一行中有两个相等大小的输入框,第二行中有两个不同大小的输入框。

为了区分这三个不同大小的输入框,我选择了布局前缀:

你注意到了我是怎样同时保持BEM的实现还有布局的? 这种实现对我来说使我更加清楚了。 你瞄一眼就可以看到我的CSS将写些啥。 清晰明了。

因为.l-form,.l-form__item,.l-form__item - small和.l-form__item - large与其他块无关,我在_form.scss中写这些class来保持上下文。

顺便说一句,有些人不同意我在前一篇文章里讲到当出现.block - modifier时删除.block这一观点。 那么,看看在这种情况下插入所有“必需”BEM class的情况下会发生什么,你会注意到“HTML开始膨胀”:

最后一点:Harry使用对象命名空间(.o-)来表示这样的结构布局。 我只是将它们分组成.l-,并使用.o-来代替别的东西。

以上,让我们转移到对象(objects)上(我的版本)。

“.o-”——对象(Objects)

Objects(.o-)是Website的最小构建块。可以把它们想成是可以在网站各个地方拼凑的【乐高】块(译者注:‘乐高’玩具,没玩过的可以淘宝搜看看)。 如果您曾听过@Brad Frost的Atomic Design,也可以将对象视为元素和分子的混合物。

对象物们都有着以下的属性:

  • 对象使用.o-前缀
  • 它们的里面不能包含其他对象或组件
  • 它们之于上下文是独立的
  • 某些对象可以在有意义的情况下忽略.o-前缀。

对象不能包含其他对象或组件

对象可大可小。对象中的HTML元素的数量是不相关的。 解释以下。

举个例子,buttons就是对象。它们是很小的而且可以放到任何地方。这是不言而喻的:

一个较大的对象的例子是我为Mastering Responsive Typography构建的倒计时器:

一个大对象的例子。仍然被认为是一个对象,因为它不包含对象和组件。

倒计时器的HTML结构如下:

注意.o-countdown包含三层HTML元素。虽然它很大了,但它仍然是一个对象,因为它不包含任何其他对象或组件。.o-countdown中的元素的实际数量是无关紧要的,因为所有内部元素只能在.o-countdown里存在。

对象独立于上下文

当我说对象是上下文独立的时候,我的意思是他们不知道在哪里会被使用。 你可以选择任何的对象,并把它放在你喜欢的地方,而且并不会破坏你的网站的结构。

这也意味着对象不应该更改外部任何结构。 因此,对象块不能包含任何这些属性/值:

  • absolute 和 fixed 定位。
  • margin
  • padding (除非你用了background-color。 在这种情况下,它不会中断对象外部的对齐)。
  • float.
  • 等等…

既然你知道对象需要与上下文无关,你马上知道我们站点范围的导航示例中的.button不能包含任何边距。

以下是我的样式表中典型的.o-button对象的示例:

虽然对象不能影响外部结构,但它改变其内部结构是很合理的。 例如,我提到的.o-countdown计时器可以具有以下HTML和CSS:

你可以自由地设计一个对象,底线是只要它不影响任何外面的东西。(另外,请确保您不要意外添加'padding'使其看起来不规整)。

合理情况下,某些对象可以忽略 .o- 前缀

哇,我们是否已经违反了规定? 哎是呀!。

一些对象包含.o-前缀(甚至是一个类)本身就没有意义,因为它们被使用得太多了。 举一个这样的例子——输入元素:

当然,如果你喜欢的话,你可以将一个class标记给input,但是如果你不能访问

我觉得另一个对象不应该使用.o-前缀的例子是字体。 他们得到特别待遇(我稍后会解释)。 在这一点上,你可以自由地反对。

对象使用总结

对象(.o-)是一个网站的最小的构建块。

对象物们都有着以下的属性:

  • 对象使用.o-前缀
  • 它们的里面不能包含其他对象或组件
  • 它们之于上下文是独立的
  • 某些对象可以在有意义的情况下忽略.o-前缀。

接下来我们转移到组件上

“.c-”——组件(Components)

如果对象是最小的构建块,则组件是您可以在整个站点中使用的更大的构建块。 如果您已阅读《原子设计》,请将组件视为有机体。 (除了这种生物体可以含有其他生物体 )。

组件有着以下属性:

  • 组件使用'.c-'前缀
  • 组件可以包含其他对象和组件。
  • 组件是上下文感知的

让我们来看看这些属性,我会补充你所需要的例子。

组件可以包含其他对象和组件

让我们回到我所说的关于布局的形式。 下是组件的完美示例。

响应式排版的付款表单之前我提到过这段HTML:

我实际上省略了很多代码,使其在布局部分中看起来合理。 如果我们深入挖掘,你会看到有input和.o-button对象。

看看.c-form现在是否包含其他对象? :)

组件是上下文感知的(一般而言)

组件是相当大的,所以您需要特别注意将它们放置在不同的地方。 例如,这个.c-form组件可以放在整个宽度栏中或侧边栏中。

以下是放在侧栏上下文中的表单:

表单组件放在侧边栏上

马上就可以看到三件事情改变了:

  • 标签被隐藏
  • input和o-button对象的布局变为百分百宽度
  • 文本的Font-size和line-height在按钮对象上变小。

此更改表单的HTML可能是:

并且各自的(S)CSS更改是:

还有一件事。 注意到了我混合了一个对象和组件类在.c-form__button里么? 这被称为BEM混合,它允许我使用组件的类来创建一个对象,而不影响原始按钮。

组件的总结

组件(.c-)是您可以在整个站点中使用的更大的构建块

组件有着以下属性:

  • 组件使用'.c-'前缀
  • 组件可以包含其他对象和组件。
  • 组件是上下文感知的

接下来我们来说下一个命名空间。

“.js”——JavaScript的钩子

Javascript 钩子(.js)表示对象/组件是否需要JavaScript。 举个栗子,我之前提到的倒计时器

使用JavaScript命名空间的好处是可以将JS功能与样式分开,这使得它们更易于维护。

例如,您刚刚看到.jsCountdown类就可以立即知道,.o-countdown需要JavaScript才能正常工作。 如果将来有需要将o-countdown更改为c-countdown,我也不必担心破坏任何JS功能。

JavaScript钩子很简单,所以让我们继续吧。

“.is-/.has-” ——状态类

状态类表示对象/组件的当前状态。当应用状态类时,您可以立即知道对象/组件是否具有下拉(.has-dropdown)或当前处于打开状态(.is-open)。 这些可爱的课程来自SMACSS(如果你想知道的话)。

当您在CSS中设计状态类时,建议您尽可能保持样式接近所讨论的对象/组件。 例如:

如果您不用Sass,你可以用这种方式来书写CSS:

由于@Jonathan早已介绍了这点,所以你可能会了解状态类。 所以我不再多说:)

让我们继续。

“.t”或“.s”——排版类(Typography)

在排版中最好的做法是在网页上只使用少数样式(大小,字体等)。 现在,你可能会在标题<h1>-<h6>中写出这样的排版风格:

如果您的网站很简单,那么这是一个很好的开始,并且不需要为多个对象/组件使用相同的标题样式。

但是举个栗子哈,如果你有一个带链接的导航样式和你的h5样式一致怎么办?

你会这样做吗?

显然咱不能这么干。那么更好的方式就是改变我们的CSS样式。所以或许这么改?

虽然改动CSS的版本稍微好一点,但是在排版风格方面,解决问题方式定不会只有一种。你能找出30种不同的组合也只是一个时间的问题。

下面是一个潜在的解决方案。

你可以分别创建.h1到.h6的样式来应用到你的HTML,而不是利用-样式,像这样:

我喜欢这种解决方案的简单性,其中有一个排版真理的来源。 您只需访问一个_typography.scss文件即可在网站上显示不同排版大小的数量。

现在,虽然.h1 - .h6类的解决方案很棒,但我强烈建议不要用.h1 - .h6为你的类,只是因为它们被隐含地绑在<h1>-<h6>对象上。

如果你有一个<h2>元素,但决定用.h3来写样式它会发生什么? 接管你的代码库的另一个开发人员可能会遇到一个最初的不和他们去“为什么是.h3 和<h2>写在一起了?

所以,不是写.h1到.h6的样式,我给排版类不同的前缀,这取决于它们是比我的基本font-size大或更小。 以下是一个例子:

  • .t1 - 最大的字体大小。
  • .t2 - 第二大字体大小。
  • .t3 - 第三大字体大小。
  • .s1 - 第一字体大小较小的基本字体大小。
  • .s2 - 第二字体大小较小的基本字体大小。
  • ...

这五个class通常是我每个项目所需的一切(到目前为止)。 这样一个惯例的好处就是能够一目了然地告诉元素的大小。 在下面的例子中,我确定这个链接的尺寸小于我的基本字体大小。

现在,如果您无法控制HTML,但想要控制排版类的大小呢?

对于这种情况,我建议您创建和使用mixins,如下所示:

在我们进入下个话题的最后一件事。 要特别注意这一点。

排版类是对象的子集。您应该像排列对象那样将相同的一套规则应用于排版类。 这意味着你不应该在排版类中添加margin或padding。而这些margin或padding应该直接添加到组件。(阅读Harry的在大型应用上管理排版了解为什么我推荐这个)。

让我们继续。

“.u-” ——实用类(Utility)

实用类是用来表现样式的一个非常好的辅助类。它们做得很好,并且其优先级高超过了其他样式。 因此,它们通常只包含一个属性,并且包含!important声明。

例子如下:

我刚才在这里说的几乎是我用于实用类的一切。 我从来没有发现有了这些类还有做不好的事。

唷。闲话不说,咱回到工作/玩耍/学习或任何你正在做的事情,所以让我们来回顾一下。

结语

在本文中,我向您展示了如何使用命名空间填补BEM的遗憾。通过包含命名空间,我终于实现了一个好的架构中寻找的所有四个标准:

  • 类必须尽量少地添加避免HTML膨胀。
  • 我必须立即知道组件是否使用JavaScript。
  • 我必须立即知道是否可以安全地编辑一个类而不会影响其他任何其他CSS。
  • 我必须立即知道每个class是适合于什么,以防止大脑过载。

总之,我总共使用了七个不同的命名空间。 他们是:

  • .l-: 布局(layouts)
  • .o-: 对象(objects)
  • .c-: 组件(components)
  • .js: js的钩子(JavaScript hooks)
  • .is-|.has-: 状态类(state classes)
  • .t1|.s1: 排版大小(typography sizes)
  • .u-: 实用类(utility classes)

每个命名空间都有一个功能,可以在整个事物的宏伟计划中进行,进一步加强了样式表中的层次结构

原文发布于微信公众号 - 较真的前端(gh_7af41a2be77e)

原文发表时间:2018-03-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏专注 Java 基础分享

Java ---自定义标签

     本篇文章介绍自定义标签,可能在工作中很少涉及到自己来定义一个标签库,因为我们基本上都是使用的大神写的标签库,基本上直接使用即可,但是从自身的发展来看,...

2525
来自专栏王鹤的专栏

Vue.js 2.0源码解析之前端渲染篇

Vue.js框架是目前比较火的MVVM框架之一,简单易上手的学习曲线,友好的官方文档,配套的构建工具,让Vue.js在2016大放异彩,大有赶超React之势。...

5.7K0
来自专栏偏前端工程师的驿站

WebComponent魔法堂:深究Custom Element 之 从过去看现在

前言  说起Custom Element那必然会想起那个相似而又以失败告终的HTML Component。HTML Component是在IE5开始引入的新技术...

21110
来自专栏jiajia_deng

设置 Notepad++ 制表符(Tab 缩进)宽度为2个空格大小

1442
来自专栏练小习的专栏

如何让一个层位于iframe之上.flash之上

蓝色理想 goos 摘录一段CSS参考手册的话: z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。 注释:元素...

2079
来自专栏性能与架构

什么是 CSS 预处理器 与 后处理器

CSS处理器是做什么的? CSS本身不是编程语言,所以在项目越来越大时,开发和维护就会越来越复杂 CSS处理器做的事情 就是帮助我们提高大规模开发时的效率 CS...

4036
来自专栏IMWeb前端团队

:before,:after伪元素妙用

本文作者:IMWeb 黎清龙 原文出处:IMWeb社区 未经同意,禁止转载 这两个伪元素分别表示元素内容的【前】【后】,利用这两个伪元素可以在元素内容...

26610
来自专栏向治洪

java的双缓冲技术

Java的强大特性让其在游戏编程和多媒体动画处理方面也毫不逊色。在Java游戏编程和动画编程中最常见的就是对于屏幕闪烁的处理。本文从J2SE的一个再现了屏幕闪...

3468
来自专栏hightopo

原 基于 HTML5 WebGL 的 3D

2014
来自专栏前端知识分享

第80天:jQuery插件使用

jQuery其他补充 + 4.1 链式编程: end()补充 * 补充五角星 评论案例 * 第一步:鼠标移入,当前五角星和前面的五角星变实体。后...

821

扫码关注云+社区

领取腾讯云代金券