CSS代码规范--BEM

BEM

基于组件方式的web开发方法,基本思想是将用户界面分成独立的模块。

Block(块)

通常指模块,组件

Block 是一个逻辑上和功能上独立的页面组件,等同于网页组件中的部件(等同于网页中的组件)。Block 封装了行为(Javascript)、模板、样式(CSS)和其他实现技术。独立状态的 Block 可供复用,并且促进项目开发和维护。

内容

  • 模块名称:描述了它的目的(“它是什么?” —— 菜单或者按钮),而不是它的状态(“它看起来是什么样子?” —— 红色或者大的)。
  • 模块不应该影响它所在的环境,这意味着你不应该为模块设置会影响到外部的形状(影响大小的 padding 或边框)和定位
  • 你也不应该在使用 BEM 的时候使用 CSS 标签选择器和 ID 选择器

使用

嵌套关系

  • 模块与模块之间可以彼此嵌套
  • 你可以有任意级别的嵌套层次
<!-- 'head' 模块 -->
<header class="header">
    <!-- 嵌套 'logo' 模块 -->
    <div class="logo"></div>

    <!-- 嵌套 'search-form' 模块 -->
    <form class="search-form"></form>
</header>

特点

1、嵌套式的构造

Block 可以被嵌套到任何其他 block 里面去。例如,一个头部 block 可以包含一个 logo、一个搜索表单和一个登录 block。

image

2、随意放置

Block 可以在一个页面内任意移动,也可以在页面之间或项目之间移动。Block 作为独立的实体来实现,这使得在页面上改变 block 的位置 并确保其功能和外观一切正常 成为可能。

image
image

3、可复用

一个界面可以包含同一个 block 的几个实例。

image

Element(元素)

内容

<h2 id="element">元素</h2>

元素(Element)是一个模块(block)的组成部分,且不能脱离模块单独地被使用。例如,一个菜单项(a menu item )不会在一个菜单块(a menu block )范围之外使用,因此它是一个元素(element)。

image
  • 元素名称:描述了它的目的(用处)(“这是什么?” —— item,text,等等。),而不是它的状态(“什么类型的,或者它看起来是什么样的?” —— 红色,大的,等等。)
  • 完整的元素名的结构是 block-name__element-name。元素的名字与模块的名字使用双下划线分隔(__)

使用

1、嵌套关系

  • 元素之间可以彼此嵌套
  • 你可以拥有任意层次的嵌套级别
  • 一个元素总是一个模块的一部分,而不是另一个元素的一部分,这意味着元素的名称不能被定义为 block__elem1__elem2 这样的层次结构。
<!--
     正确的。完整的元素名的结构符合如下模式:
     'block-name__element-name'
 -->
<form class="search-form">
    <div class="search-form__content">
        <input class="search-form__input"/>
        <button class="search-form__button"></button>
    </div>
</form>

 <!--
     不正确的。完整的元素名的结构不符合如下模式:
     'block-name__element-name'
 -->
<form class="search-form">
    <div class="search-form__content">
        <!-- 推荐:'search-form__input' 或者 'search-form__content-input' -->
        <input class="search-form__content__input"/>
        <!-- 推荐:'search-form__button' 或者 'search-form__content-button' -->
        <button class="search-form__content__button"></button>
    </div>
</form>

如果在模块名称上定义了命名空间,也要保证元素名称是依赖于模块的(block_elem)。
在 DOM 树中,一个模块可以有元素嵌套结构:

<div class="block">
    <div class="block_elem1">
        <div class="block_elem2">
            <div class="block_elem3"></div>
        </div>
    </div>
</div>

在 BEM 的方法论中,这样的模块结构通常表示为一个并列的元素列表:

.block {}
.block_elem1 {}
.block_elem2 {}
.block_elem3 {}

你可以在不改变每个单独的元素的情况下改变一个模块的 DOM 结构:

<div class="block">
    <div class="block_elem1">
        <div class="block_elem1"></div>
    </div>
    <div class="block_elem3"></div>
</div>

2、组成部分
一个元素总是一个模块的一部分,不可以单独使用

<!-- 正确的。元素都位于 'search-form' 模块内 -->
<!-- 'search-form' 模块 -->
<form class="search-form">
    <!-- 在 'search-form' 模块内的 'input' 元素 -->
    <input class="search-form__input" />
    <!-- 在 'search-form' 模块内的 'button' 元素 -->
    <button class="search-form__button"></button>
</form>

<!-- 不正确的。元素位于 'search-form' 模块的上下文之外 -->
<!-- 'search-form' 模块 -->
<form class=""search-block>
</form>

<!-- 在 'search-form' 模块内的 'input' 元素 -->
<input class="search-form__input"/>

<!-- 在 'search-form' 模块内的 'button' 元素 -->
<button class="search-form__button"></button>

3、可选性
一个元素是一个可选的模块组件。并不是所有的模块都必须有元素。

<!-- 'search-form' 模块 -->
<div class="search-form">
    <!-- 'input' 模块 -->
    <input class="input"/>

    <!-- 'button' 模块 -->
    <button class="button"></button>
</div>

①如果这段代码可能被重用,并且它不依赖于页面上的其他组件,那你应该创建一个模块。

②如果这段代码在没有父实体(模块)的情况下不能使用,那你应该创建一个元素。

③为了简化开发,元素应该被分割成一小部分-子元素。在 BEM 方法论中,你不能创建元素的元素,在这种情况下,你需要创建一个服务模块,而不是创建一个元素。

Modifier(修饰符)

Modifier 是一个 BEM 实体,它定义了一个 block 或 element 的外观和行为。
Modifier 可用也可不用(即不一定要用到 modifier)。
Modifier 本质上与 HTML 的属性很相似。同一个 block 会因为 modifier 的使用而 看起来与之前有所不同。
例如,菜单块(the menu block )的外观可能会因为在它身上用了一个 modifier 而改变。

内容

一种用于定义模块和元素的外观,状态和行为的实体。

  • 修饰符的名称:描述了它的外观(“多大?”或者“它的主题是什么?”等等—— size_s 或者 theme_islands),它的状态(“它与其他有什么不同?” —— disabledfocused,等等)以及他的行为(“它的行为什么?”或者“它如何响应用户?”——比如 directions_left-top)。
  • 修饰符的名字与模块或者元素的名字使用单下划线分隔(_)

修饰符的类型

1、Boolean

  • 当修饰符的存在或不存在是重要的,与它的值无关时使用这种类型的修饰符。比如:disabled。如果一个布尔类型的修饰符是可见的,它的值被假定为 true。
  • 修饰符的全名的结构遵循如下模式:
  • Modifier(修饰符 )

block-name_modifier_name

block-name__element-name_modifier-name

<!-- 'search-form' 模块有一个 ‘focused’ 的布尔类型的修饰符 -->
<form class="search-form search-form_focused">
    <input class="search-form__input"/>

    <!-- 'button' 元素有一个 'disabled' 的布尔类型修饰符 -->
    <button class="search-form__button search-form__button_disabled">Search</button>
</form>

2、键-值

  • 当修饰符的值是重要的使用键值对类型。

“一个 islands 设计主题的按钮”:menu_theme_islands

  • 这种类型的修饰符的全名的结构遵循如下模式:

block-name_modifier-name_modifier-value

block-name__element-name_modifier-name_modifier-value

<!-- The `search-form` 模块有值为 'islands' 的 `theme` 修饰 -->
<form class="search-form search-form_theme_islands">
    <input class="search-form__input">

    <!-- The `button` 元素有值为 'm' 的 `size` 修饰 -->
    <button class="search-form__button search-form__button_size_m">Search</button>
</form>

<!-- 你不能同时使用两个具有不同值的的相同修饰符 -->
<form class="search-form
             search-form_theme_islands
             search-form_theme_lite">

    <input class="search-form__input">

    <button class="search-form__button
                   search-form__button_size_s
                   search-form__button_size_m">
        Search
    </button>
</form>

使用

一个修饰符不能被单独使用。

  • 从 BEM 的角度,一个修饰符不能脱离模块或元素而被使用。一个修饰符应该改变实体的外观,行为或者状态,而不是替换它。
<!-- 正确的。'search-form' 模块有值为 'islands' 的 'theme' 修饰符 -->
<form class="search-form search-form_theme_islands">
    <input class="search-form__input">

    <button class="search-form__button">Search</button>
</form>

<!-- 不正确的。'search-form' 丢失了 -->
<form class="search-form_theme_islands">
    <input class="search-form__input">

    <button class="search-form__button">Search</button>
</form>

混合模式:一种在单一的 DOM 节点上使用不同 BEM 实体的技术

混合模式允许

  • 结合多个实体的行为和样式,而不是重复编写代码
  • 在现有代码的基础上创建具有新语义的UI组件
<!-- 'header' 模块 -->
<div class="header">
    <!--
        将 'header' 模块的 'search-form' 元素与 'search-form' 模块混合在一起使用
    -->
    <div class="search-form header__search-form"></div>
</div>

在这个例子中,我们将 header 模块的 search-form 元素与 search-form 模块的行为和样式结合在一起。这种方式允许我们在 header__search-form 元素上设置额外的形状和定位,而 search-form 模块仍然是通用的。因此,我们可以在任何环境中使用模块,因为模块没有指定任何填充。这正是我们可以独立调用模块的原因。

文件系统:在 BEM 方法论中采用的组件概念同样适用于项目的文件结构中。模块、元素和修饰符的实现可以被分在独立的文件中,这意味着,我们单独地使用它们。

  • 一个单独的模块对应一个单独的目录
  • 模块和其对应的目录拥有相同的名字。比如, header 模块放置在 header/ 目录中,menu 模块放置在 menu/ 目录中。
  • 一个模块的实现分为单独的文件。比如, header.cssheader.js
  • 模块目录是其元素和修饰所在目录的根目录。
  • 元素目录的名称以双下划线(__)开始。比如,header/__logo/menu/__item
  • 修饰目录的名称以单下划线(_)开始。比如,header_fixedmenu/_theme_islands/
  • 元素和修饰的实现分为不同的文件。比如,header__input.jsheader_theme_islands.css
search-form/                           # Directory of the search-form

    __input/                           # Subdirectory of the search-form__input
        search-form__input.css         # CSS implementation of the
                                       # search-form__input element
        search-form__input.js          # JavaScript implementation of the
                                       # search-form__input element

    __button/                          # Subdirectory of the search-form__button element
        search-form__button.css
        search-form__button.js

    _theme/                            # Subdirectory of the search-form_theme modifier
        search-form_theme_islands.css  # CSS implementation of the search-form block
                                       # that has the theme modifier with the value
                                       # islands
        search-form_theme_lite.css     # CSS implementation of the search-form block
                                       # that has the theme modifier with the value
                                       # lite

search-form.css                        # CSS implementation of the search-form block
search-form.js                         # JavaScript implementation of the
                                       # search-form block

这样的文件结构可以很好地支持我们重用代码。

BEM entity(BEM 实体)

Block,element 和 modifier 合起来就被成为 BEM entity。它是一个 既可以用来指代单独的 BEM 实体又可以作为 block、element 和 modifier 的总称的 概念。

Mix(混合体)

Mix 是被托管在(being hosted on)一个单独的 DOM 节点上的 不同 BEM 实体(混合而成)的一个实例。

Mix允许我们

  • 把几个 BEM 实体的功能(behavior)和样式 组合在一起,同时避免重复代码
  • 在现有的 BEM 实体的基础上 创建语义上的新界面组件。让我们想一下这种 mix 情形:把一个 block 与 另一个 block 的一个 element 组合在一起。

我们假设,项目里的链接(links)通过一个链接块(a link block)来实现。我们需要把菜单项(menu items )格式化成链接(links)。这里有几种实现方法:

  • 创建一个 可以把菜单项(item)转变成链接(link)的 modifier。实现这样一个 modifier 即必然牵涉到 复制链接块的功能和样式。这样一来就会导致代码重复。
  • 取一个 把一个通用的链接块(link block )与一个菜单块的一个链接元素(a link element ) 组合在一起的 mix。两个 BEM 实体的混合体(mix)可以让我们不用复制代码,就可以使用链接块的基本链接功能 和 菜单块的 CSS 规则。

BEM tree(BEM 树 )

BEM tree 是网页结构在 block、element 和 modifier 方面的表示(representation)。这是一个在 DOM 树之上的抽象概念,它描述了 BEM 实体的名称、它们的状态、顺序、嵌套和辅助数据。在现实生活中的项目,BEM tree可以呈现在任何支持树结构的形式(format)中。

一个DOM树

<header class="header">
<img class="logo">
<form class="search-form">
<input type="input">
<button type="button"></button>
</form>
<div class="lang-switcher"></div>
</header>

BEM tree

header
    ├──logo
    └──search-form
        ├──input
        └──button
    └──lang-switcher

在 XML 和 BEMJSON 格式中,该 BEM tree 则是这样的
XML

<block:header>
    <block:logo/>
    <block:search-form>
        <block:input/>
        <block:button/>
    </block:search-form>
    <block:lang-switcher/>
</block:header>

BEMJSON

{
    block: 'header',
    content : [
        { block : 'logo' },
        {
            block : 'search-form',
            content : [
                { block : 'input' },
                { block : 'button' }
            ]
        },
        { block : 'lang-switcher' }
    ]
}

Block implementation(BEM实现 )

Block implementation 是指一组各不相同的 技术,这些技术决定着 BEM 实体以下几方面:

  • 行为/功能(behavior)
  • 外观
  • 测试
  • 模板
  • 文档(documentation)
  • 依赖描述
  • 附加数据(例如:图片)

Implementation technology(实现技术)

Implementation technology 是一种用于实现一个 block 的技术。Block 可以用一种或多种技术来实现,例如:

  • 行为/功能(behavior)-- JavaScript, CoffeeScript
  • 外观-- CSS, Stylus, Sass
  • 模板-- BEMHTML, BH, Jade, Handlebars, XSL
  • 文档(documentation)-- Markdown, Wiki, XML

例如,如果一个 block 的外观是用 CSS 来定义的,这意味着 block 是用 CSS 技术实现的。同样地,如果一个 block 的文档是用 Markdown 格式写的,block 就是用 Markdown 技术来实现的。

Block redefinition(块重定义)

Block implementation 是指通过在不同的层级上增加新的功能到 block 来修改 block。

Redifinition level(重定义等级)

Redefinition level 是指一组 BEM 实体和它们的部分实现。

一个 block 的最终实现 可以被分成 不同的重定义层级。每一个新的层级都会扩展或覆盖原始的 block implementation。最终的结果由 来自所有按照预设的连续的顺序排列的重定义层级的独立的 block implementation technologies 组合而成。
任何 BEM 的实现技术都可以被重新定义。

例如,有一个连接到项目的第三方库。这个库包含现成的 block implementation。该项目指定的 block 保存在一个另一个重定义层级。比方说,我们需要修改这个库里的某一个 block 的外观。这并不需要在库的源代码里修改 block 的 CSS 规则 或者 在项目里复制代码。我们只需在项目里为 那一个 block 创建额外的 CSS 规则。在生成过程中,最终实现将会结合库级别的原有规则和项目级别中新的样式规则。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259