BEM,SASS,LESS,bootstrap:如何有效地將這些方法,工具和框架聰明地整合?

世有因果知因求果發表於2015-10-06

https://medium.com/@andersonorui_/bem-sass-and-bootstrap-9f89dc07d20f

Bootstrap是一個“HTML,CSS和Javascript的框架,用於開發responsive,mobile first project";

SASS是一個css擴充套件預編譯工具;

BEM是一個解決css可維護可擴充套件的方法原則

我通常使用LESS,一個原因是Bootstrap本身是Less寫的。注意SASS和LESS有一些區別可能會讓你感覺很奇怪:

變數覆蓋的原則不同: LESS:後面定義的會覆蓋前面的,並且在整個程式碼中都以後面定義的值為準;SASS則是先定義的會先生效直到遇到重新覆蓋定義為止。

注意這個區別對你使用LESS/SASS來開發bootstrap的定製設計時,比如variable.less/sass檔案(也就是所謂bootstrap.theme.less),其位置就很重要了,對於less,則需要你的客製化variable放到後面引入,而對於sass則需要最早引入。

Cleaning up css classes

現在已經很少使用photoshop或者illustrator了,幾乎所有工作從專案開始時就直接在html/css/js中進行,當然有時可能我會使用sketch3來做一下brainstrom或者建立ui elements。也正因為此,我的程式碼越來越亂,以至於不得不重構程式碼。當然這個workflow也是我所喜歡的,因為在專案啟動時我們幾乎不知道我們打算如何去解決問題,我們無法看到所有的patterns,深思熟慮有些浪費時間,所以我往往喜歡在當專案有一個始終一致的模樣時才來做這個工作。這樣可能更有效果。

重構之前我可能有下面的html markup,

<div id=”social-newsletter”>
    <div class=”container”>
        <header class=”text-center”>
            <h1 class=”bottom top”>Acompanhe as novidades</h1>
        </header>
        <div class=”row”>
            <div class=”social col-xs-6 col-sm-2 col-md-2">
                <div class=”facebook block”>
                    <div class=”centered”>
                        <a href=”http://www.facebook.com" title=”Facebook”>
                            <span class=”sr-only”>facebook</span>
                            <span class=”fa fa-facebook fa-4x”></span>
                        </a>
                    </div>
                </div>
           </div>
           <div class=”social col-xs-6 col-sm-2 col-md-2">
               <div class=”twitter block”>
                   <div class=”centered”>
                       <a href=”http://www.twitter.com" title=”Twitter”>
                           <span class=”sr-only”>twitter</span>
                           <span class=”fa fa-twitter fa-4x”></span>
                       </a>
                   </div>
               </div>
           </div>
           <div class=”social col-xs-6 col-sm-2 col-md-2">
               <div class=”youtube block”>
                   <div class=”centered”>
                       <a href=”http://www.youtube.com" title=”YouTube”>
                           <span class=”sr-only”>youtube</span>
                           <span class=”fa fa-youtube fa-4x”></span>
                       </a>
                   </div>
               </div>
           </div>
           <div class=”social col-xs-6 col-sm-2 col-md-2">
               <div class=”instagram block”>
                   <div class=”centered”>
                       <a href=”http://www.instagram.com" title=”Instagram”>
                           <span class=”sr-only”>instagram</span>
                           <span class=”fa fa-instagram fa-4x”></span>
                       </a>
                   </div>
               </div>
           </div>
           <div class=”newsletter col-xs-12 col-sm-4 col-md-4">
               <form>
                    <div class=”block”>
                        <label class=”centered”>Assine nossa newsletter</label>
                    </div>
                    <input type=”email” placeholder=”Insira seu email” class=”col-xs-12 col-sm-12 col-md-12"></input>
                    <button type=”submit” class=”btn btn-danger col-xs-12 col-sm-12 col-md-12">Cadastrar email <i class=”fa fa-paper-plane fa-2x pull-right”></i></button>
                </form>
            </div>
        </div>
    </div>
</div>

通過以BEM方法論,SASS工具支援,可能優化為下面的樣子:

<div class=”social-links js-social-links”>
    <header class=”social-links—header”>
        <h1>Acompanhe as novidades</h1>
    </header>
    <div class=”social-links—content”>
        <div class=”social-links—link js-social-link”>
            <a class=”link—facebook” href=”http://www.facebook.com" title=”Facebook”>
                <span class=”sr-only”>facebook</span>
                <span class=”fa fa-facebook”></span>
            </a>
        </div>
        <div class=”social-links—link js-social-link”>
            <a class=”link—twitter” href=”http://www.twitter.com" title=”Twitter”>
                <span class=”sr-only”>twitter</span>
                <span class=”fa fa-twitter”></span>
            </a>
        </div>
        <div class=”social-links—link js-social-link”>
            <a class=”link—youtube” href=”http://youtube.com" title=”YouTube”>
                <span class=”sr-only”>youtube</span>
                <span class=”fa fa-youtube”></span>
            </a>
        </div>
        <div class=”social-links—link js-social-link”>
            <a class=”link—instagram” href=”http://www.instagram.com" title=”Instagram”>
                <span class=”sr-only”>instagram</span>
                <span class=”fa fa-instagram”></span>
            </a>
        </div>
        <form class=”social-links—newsletter js-newsletter”>
            <label class=”newsletter—label”><span>Assine nossa newsletter</span></label>
            <input class=”newsletter—input” type=”email” placeholder=”Insira seu email”/>
            <button class=”newsletter—submit” type=”submit”><span>Cadastrar email</span></button>
        </form>
    </div>
</div>

得到的UI效果是這個樣子的:

 

為了達成上面的畝i奧--更加可讀,可理解和富含語義,我需要理解SASS是如何工作的,以及BEM後面所隱含的概念。

BEM: Block. Eelement . Modifier

BEM是block,element,modifier的首字母縮寫。它的核心想法是通過遵循一套規則使得everything modular---這樣將易於重用易於維護,也更加容易理解和自描述。從BEM網站上,我們摘抄以下:

“A block is A logically and functionally independent page component, the equivalent of a component in Web Components. A block encapsulates behavior (JavaScript), templates, styles (CSS), and other implementation technologies. Blocks being independent allows for their re-use, as well as facilitating the project development and support process.Blocks can be implemented in one or more technologies, for example:
  • behavior — JavaScript, CoffeeScript
  • appearance — CSS, Stylus, Sass
  • templates — BEMHTML, BH, Jade, Handlebars, XSL
  • documentation — Markdown, Wiki, XML.
"
A block can be either simple or compound(containing other blocks),比如下面的例子是一個search form block:
“An element is a part of a block that performs a certain function. Elements are context-dependent: they only make sense in the context of the block they belong to.”
例如:一個input field和一個button是構成search block的elements:
“A modifier is a property of a block or an element that alters its look or behavior.”

Means of Describing Pages and Templates

blocks和elements一起構成了page content。除了簡單地布放在頁面上,他們的安排也非常重要。Blocks(or elements)可能按照一定的順序一個挨著一個的排列。例如,在一個電商網站上,商品一個個羅列:

或者比如menu items:

Blocks也可能被包含在其他的block中,比如,一個Head Block包含了logo,searchbox,authblock,menu block。

而且,我們的building blocks需要一種使用plain text的方式來描述頁面的佈局。為了實現這一點,每一個block和element都需要很好的命名。Block names應該在整個專案範圍內是唯一的;只有相同的block的不同例項化需要使用完全相同的block類,Element名稱必須在所屬block範圍內唯一,一個element可以被在block範圍內被重複使用任意次數。

希望瞭解更多,可以直接訪問BEM的網站: http://bem.info/method/definitions/

總結以下,BEM的想法就是要建立一個下面的元素組織架構:

- block

- block__element

- block__element__modifier

<div class="menu menu_hidden">  <span class="menu__item"></span> </div> <div class="menu menu_theme_morning-forest"> <span class="menu__item"></span></div>

 

BEM TREE

{
  block: 'page',
  content: {
    block: 'head',
    content: [
      { block: 'menu', content: ... },
      {
        elem: 'column',
        content: { block: 'logo' }
      },
      {
        elem: 'column',
        content: [
          {
            block: 'search',
            content: [
              { elem: 'input' },
              {
                elem: 'button',
                content: 'Search'
              }
            ]
          }
        ]
      },
      {
        elem: 'column',
        content: {
          block: 'auth',
          content: ...
        }
      }
    ]
  }
}

element和block可以互相包含。。。

一般來說,隨著專案的發展,blocks傾向於被新增,被刪除或者在頁面上被移動。比如,你可能希望調換logo和auth block的位置,或者希望將menu放到search block的下方,為了讓這個變更過程更加方便簡單,要求blocks必須是independent互相獨立的

所謂independent block是以允許放置在頁面中的任何地方的方式來實現的,包括隨意地嵌入到其他的block中。

Independent CSS

從css角度來看這意味著:一個block(or an element)必須有一個唯一的"name"(a css class);HTML elements必須不能在CSS selectors(.menu td)中使用,因為這些包含html tag的selectors固有地不具有context-free的特性;Cascading selectors for several blocks should be avoided:不要使用層疊特性!

Naming for independent CSS classes

一種可能的命名方案是:

  • 一個block的css class name和他的block name是一致的
  • 一個element的css class name由block name+element name組成
<ul class="menu">
  <li class="menu__item">
    ...
  </li>
  <li class="menu__item">
    ...
  </li>
</ul>

很有必要將block name包含在一個element的css class name中,因為這將最小化層疊的可能。同時注意使用一致的seperator(這裡使用的是__),這對於允許自動化的工具介入開發流程很有幫助。

當然你可以使用其他的命名方式,我們推薦的方式是:

  • Block name: block name is a keyword that makes sence what is a block about. A block name may be composed of serveral words seperated with hyphen(我這裡推薦是blockname由一個或多個單詞直接連線而成): bbbb
  • Block prefix: 一個block name通常有一個prefix來幫助指示block的purpose
    •   b-  :比如b-menu-horiz  有實實在在的apperance;   b-bbbb
    •       i-   :比如i-menu  這是一個抽象的block它自己並沒有外觀,主要用於實現某種功能而存在,比如它提供了一個功能,被b-menu-horiz block加以使用 i-bbbb
    •       l-   : l-bbbbb  表示一個layout的block
  • Element name: 全名稱的element name用於指示這個元素屬於那一個block,比如 b-blockname__elementname,b-menu-horiz__item, b-popup__content bbbb_eeee
  • Block modifier: 全名稱的modifier block用於指示它屬於哪個block, 比如b-block-name_modifier-name_modifier-value, b-link_type_pseudo,b-menu-horiz_type_simple,b-popup_direction_up   bbbb__mmmm
  • Element modifier's name: 全名稱用於指示他屬於哪一個元素(並且哪一個block),b-block-name__element-name_modifier-name_modifer-value,比如b-menu-horiz__item_state_current  bbbb_eeee__mmmm-vvvv

Independent templates

從模版引擎的角度來看,block independence意味著:

  • blocks and elements必須在input data中描述,blocks(or elements)must have unique "names" to make things like "Menu" should be placed here"expressible in our templates;
  • Blocks may appear anywhere in a BEM tree

Modifiers For Elements And Blocks

我們如果需要建立一個和已經存在的一個block非常接近的block,但是可能外觀稍微有些區別,比如,我們有這樣一個任務:

在footer區域增加一個menu block,使用另外一種layout

為了避免再開發另外一個block,我們可以使用一個Modifier.

一個Modifier是一個block或者element的屬性,該屬性僅僅改變block/element的外觀或者行為。一個modifier有一個name和value,多個modifier可以同時使用。

比如,一個block modifer specifies background color,再比如一個元素的modifier更改look of the "current"item.

從input data角度來看,在一個BEM tree中,modifier是一個描述block/element的實體屬性

<b:menu m:size="big" m:type="buttons">
  ...
</b:menu>

同樣地,可以是有那個json來描述

{
  block: 'menu',
  mods: [
   { size: 'big' },
   { type: 'buttons' }
  ]
}

從css角度來看,一個modifier是一個額外的css 類用於修飾block或者element

<ul class="menu menu__size-big menu__type-buttons">
  ...
</ul>
.menu_size_big {
  // CSS code to specify height
}
.menu_type_buttons .menu__item {
  // CSS code to change item's look
}

同樣地,對於element modifier可以以類似的方式來實現,比如current menu item可以這樣來實現:

<b:menu>
  <e:item>Index<e:item>
  <e:item m:state="current">Products</e:item>
  <e:item>Contact<e:item>
</b:menu>
{
  block: 'menu',
  content: [
    { elem: 'item', content: 'Index' },
    {
      elem: 'item',
      mods: { 'state' : 'current' },
      content: 'Products'
    },
    { elem: 'item', content: 'Contact' }
  ]
}

 

<div class="menu">
  <ul class="menu__layout">
    <li class="menu__layout-unit">
      <div class="menu__item">Index</div>
    </li>
    <li class="menu__layout-unit">
      <div class="menu__item menu__item_state_current">Products</div>
    </li>
    <li class="menu__layout-unit">
      <div class="menu__item">Contact</div>
    </li>
  </ul>
</div>
.menu__item_state_current {
  font-weight: bold;
}

Blocks Consistency

一個website有一個Button block,該block可能包含特定的動態行為,比如當一個block被hover時,要求更改它的appearance.

manager可能會問:在另外一個page中使用同樣的button.

雖然對於一個block有了css implementation,但是這是不夠的。重用一個block也意味著重用它的行為,而該行為使用javascript來描述。

所以一個block必須知道關於它自己的所有事情。為了實現一個block,我們使用各種技術來描述他的外觀和行為---我們稱之為multilingualism.

Multilingualism presentation是一個對block從各個programming languages的角度來描述的方法,該方法能夠準確描述清楚該block的view和functionality。

To have a block present on a page as a UI element, we need to implement it in the following techs:

  • Templates(XSL,TT2,Javascript,etc), which turn block declarations into HTML code;
  • CSS that describe apperance of the block;

如果一個block有動態的behaviour,我們還需要新增

  • 一個javascript implmentation for the block, 

everything that constitues a block is a technology, including images.

http://www.smashingmagazine.com/a-new-front-end-methodology-bem-blocks-reiteration/

我們先來理解我們想達到的目標並且理解layout的結構:

然後我們需要定義屬於不同context的我們的類了:

1.首先定義整個context block .social-links

2.在.social-links裡面,建立兩個其他的blocks: .social-links--header和.social-links--content

3.在.social-links--header中只有一個h1,唯一的元素;

4。在.social-links--content block中,有四個elements(.social-links--link)和一個block(.social-links--newsletter)

5.在.social-links--newsletter block中,我有3個elements: .newsletter--label,.newsletter--input,.newsletter--button

或許,嚴格按照BEM命名規範,我需要使用.social-links--newsletter--label,.social-links--newsletter--input,但是我知道我不會在其他地方使用這個newsletter block,所以就簡化為一個短小的class了(實際上這個假設本身應該是有問題的!)

BEM方法論使得建立一個class是很簡單的,你只需要知道context(block level),然後按照規則來套用就可以了。

Bootstrap

Bootstrap有很多漂亮的功能,但是我只想演示以下如何在SASS中使用guid class.

在bootstrap中,我們有下面一些@mixins來建立一個grid系統:

container-fixed()
make-row()
make-xs-column()
make-xs-column-offset()
make-xs-column-push()
make-xs-column-pull()
make-sm-column-offset()
make-sm-column-push()
make-sm-column-pull()
make-md-column-offset()
make-md-column-push()
make-md-column-pull()
make-lg-column-offset()
make-lg-column-push()
make-lg-column-pull()
clearfix()

這樣我們可以像下面的程式碼一樣來做設計:

section {
    @include make-row();
    article {
        @include make-xs-column(12);
        @include make-sm-column(7);
        @include make-md-column(8);
    }
    aside {
        @include make-xs-column(12);
        @include make-sm-column(3);
        @include make-md-column(4);
    }
}

在不使用BEM方法論之前,我們可能這樣書寫HTML markup:

<section class=”row”>
    <article class=”col-xs-12 col-sm-7 col-md-8">
        Article content
    </article>
    <aside class=”col-xs-12 col-sm-3 col-md-4">
        Aside content
    </aside>
</section>

 

而當我們引入BEM以及借用BOOTSTRAP LESS/SASS的程式碼重構後,這樣書寫html markup:

<section>
    <article>
        Article content
    </article>
    <aside>
        Aside content
    </aside>
</section>

 

這樣的HTML更加清晰和易於理解。

SASS

我們再來看看下面兩片程式碼:

<div class=”social-links—content”>
    <div class=”social col-xs-6 col-sm-2 col-md-2">
        <div class=”twitter block”>
            <div class=”centered”>
                <a href=”http://www.twitter.com" title=”Twitter”>
                    <span class=”sr-only”>twitter</span>
                    <span class=”fa fa-twitter fa-4x”></span>
                </a>
            </div>
        </div>
    </div>
</div>

以及去除bootstrap的預定義class後的程式碼:

<div class=”social-links—content”>
    <div class=”social-links—link js-social-link”>
        <a class=”link—twitter” href=”http://www.twitter.com" title=”Twitter”>
            <span class=”sr-only”>twitter</span>
            <span class=”fa fa-twitter”></span>
        </a>
    </div>
</div>

既然我們的想法是清理html程式碼,使得其更加易讀,我刪除了所有的bootstrap grid class,取而代之的是通過@extend,@include直接把grid class插入到css中去:

.social-links—content {
    @extend .container;
 
    .social-link {
        @include make-xs-column(6);
        @include make-sm-column(2);
    }
}

然後,我建立一個@mixin來在垂直方向向中間對齊icons:

@mixin vertical-align() {
    display: block;
    &:before {
        content: ‘’;
        display: inline-block;
        height: 100%;
        vertical-align: middle;
        margin-right: -0.25em;
    }
    > * {
        display: inline-block;
        vertical-align: middle;
        width: 98%;
    }
}

這樣我就可以這樣更新.social-links--content:

.social-links—content {
    @extend .container;
 
    .social-link {
        @include make-xs-column(6);
        @include make-sm-column(2);
        a {
            @include vertical-align();
        }
    }
}

從而將HTML中的div.block和div.centered刪除,也就是從下面的HTML:

<div class=”social col-xs-6 col-sm-2 col-md-2">
    <div class=”twitter block”>
        <div class=”centered”>
            <a href=”http://www.twitter.com" title=”Twitter”>
                <span class=”sr-only”>twitter</span>
                <span class=”fa fa-twitter fa-4x”></span>
            </a>
        </div>
    </div>
</div>

 

變成了下面的BEM風格的程式碼:

<div class=”social-links—link js-social-link”>
    <a class=”link—twitter” href=”http://www.twitter.com" title=”Twitter”>
        <span class=”sr-only”>twitter</span>
        <span class=”fa fa-twitter”></span>
    </a>
</div>

 

 使用LESS 巢狀feature實現BEM命名方法:

在css開發中,我們非常喜歡使用nested方式來組織css元素,因為這種方式我們可以清晰地看到元素間的層次關係(儘管嚴格使用BEM命名方式也可以看出來),但是如果一般性地巢狀,則輸出的css為後代選擇器,一種可行的方法是使用&特殊字元(SASS中使用

@at-root #{&}__element

 

.btn{
  width: 100px;
  &__icon{   //可以看到icon作為btn block的一個元素
    color: blue;
    &--big{  //可以看到.btn__icon--big作為btn block下面的icon元素的一個modifier
        font-size: 20px; 
    }
  }
  &--primary{ //可以看到.btn--primary作為btn block的modifier
    background-color: blue;    
  }
}
上面的LESS程式碼將按照BEM模式的組織方式生成出來的CSS程式碼如下:
.btn{width:100px}
.btn__icon{color:#00f}
.btn__icon--big{font-size:20px}
.btn--primary{background-color:#00f}

 

http://www.smashingmagazine.com/2014/07/bem-methodology-for-small-projects/

http://www.smashingmagazine.com/2013/02/the-history-of-the-bem-methodology/

 

相關文章