📜 ⬆️ ⬇️

Taste BEM!

This article describes how to create a project using BEM technologies.
Step by step, we will create a product catalog page using the BEM principles in CSS, the ability to write declarative JavaScript on the i-bem.js and using the BEMHTML template BEMHTML . Helping to do all this will be bem tools , in particular - a tool for developing bem server .



Important: the article does not contain specific details, its goal is to get the project as quickly as possible. A text that reveals more information will pass the next post.
')

What is BEM?


For a start, a small lyrical digression for those who do not know what this abbreviation means.
BEM stands for "Block, Element, Modifier." This is a methodology for developing web projects, a way to conveniently divide the interface into separate things, applicable to any technology. In addition, BEM is a set of tools for automating work. Finally, BEM is the ability to create interface libraries for fast and efficient development.

If you have not previously encountered BEM, you should first look at the materials on the site bem.info , and then return to this article.
For those who like video more, I can offer a recording of the report from WebConf Riga 2012 (in English) or a speech by Sergey Berezhnoy (@veged) at RIT 2011 .

Required Tools


To go through all the steps of this manual, you need to install bem tools . This is a set of tools with a command line interface for operating BEM entities and building a project. Installation instructions are in the description of the repository.
At the time of this writing, version 0.5.21 was relevant.

Creating your own project repository


The easiest way is to create your project by simply copying an existing repository with a suitable structure. For a project using the full BEM stack, the project-stub repository will do. At the time of writing this article, revision 5ac5d2d2567ca6d52d82f95b219bca6f49ef5cc4 .

 $ git clone git://github.com/bem/project-stub.git my-pretty-project $ cd my-pretty-project/ $ git reset --hard 5ac5d2d2567ca6d52d82f95b219bca6f49ef5cc4 $ rm -rf .git $ git init 

Then the project needs to be collected. To do this, run the command

 make 

It takes some time, because at this very moment all the necessary npm-packages are installed in the project directory.
At the end you will see the following message:

 info: Server is listening on port 8080. Point your browser to http://localhost:8080/ 

A bem server is launched on your computer - a development tool that will automatically rebuild your project if you make changes to it.

Making a change to the page


Now there is one index.html page on your project that you can open in a browser.
The first request to the page will be processed for a noticeable time, because at this moment the bem server loads the libraries necessary for its assembly.

The project structure assumes that the blocks will be placed in the desktop.blocks directory, and the pages in the desktop.bundles directory.
Generally, strictly speaking, desktop.bundles stores a “set” of blocks. These can be frequently used blocks of several pages (what is commonly called common ), sets that combine several pages ( all , if all pages are combined) or - the simplest case - sets of blocks, each of which corresponds to one page. Here will be considered the last, simple option.

You can edit the page by changing the file desktop.bundles/index/index.bemjson.js .

Description of the block in bemjson


First we will place on the header page. In BEM terms, this is the head block:

 { block: 'head' } 

Hereinafter, the full code of the page at different stages can be found on the Gist: https://gist.github.com/4175550 .

After reloading the page, you will see that the corresponding <div> appeared in it.

 <!DOCTYPE html> <html class="i-ua_js_yes i-ua_css_standard"> <head>...</head> <body class="b-page b-page__body"> <div class="head"></div> </body> </html> 

We fill the cap with the content: the search form, the logo and layout, positioning the content as needed.

First, in the BEMJSON description of the page, inside the head block we place a layout block with two elements: left and right .

 content: [ { block: 'head', content: { block: 'layout', content: [ { elem: 'left', content: 'left here' }, { elem: 'right', content: 'right here' } ] } } ] 

https://gist.github.com/4175573

 <!DOCTYPE html> <html class="i-ua_js_yes i-ua_css_standard"> <head>...</head> <body class="b-page b-page__body"> <div class="head"> <div class="layout"> <div class="layout__left">left here</div> <div class="layout__right">right here</div> </div> </div> </body> </html> 

This will create the necessary markup (you can see it by refreshing the page) to which you want to write styles. That is, implement the layout block in CSS technology.

Block creation


To create a technology file, use the bem create command.

 $ bem create -l desktop.blocks/ -T css -b layout 

The command will create a file desktop.blocks/layout/layout.css , in which there is already a selector corresponding to the block file. The rule must be supplemented according to the purpose of the block.
Now you can just copy: https://gist.github.com/4175598

Using blocks from libraries


Nested in the layout search form and logo blocks do not need to be implemented independently. They are already implemented in the library bem-bl , simply declare them on the page. That is, insert the BEMJSON description of the block into the desktop.bundles/index/index.bemjson.js

For our page we will use the b-search and b-logo blocks.
https://gist.github.com/4175640

You can take a picture for a logo from here or specify your own.



Adding Library Blocks


CSS extension

The b-logo block we use provides only the necessary markup. CSS for it, each developer can write himself, because everyone needs a different markup.

We will put this markup in the b-logo block at our override level.

 $ bem create -l desktop.blocks/ -T css -b b-logo 

The markup for the block can be taken from here: https://gist.github.com/4175675

The same for the b-search block:

 $ bem create -l desktop.blocks/ -T css -b b-search 

https://gist.github.com/4195433



Extension of BEMHTML



To make the page centered, you need an additional container. To do this, we will finish defining the b-page block templates, creating the same block at our level. BEMHTML is used as a template engine.

 $ bem create -l desktop.blocks/ -b b-page -T bemhtml 

In the resulting desktop.blocks/b-page/b-page.bemhtml you need to write code that wraps the content of the block into an additional container.

 block b-page, content: { elem: 'body-i', content: this.ctx.content } 

https://gist.github.com/4175742

 <!DOCTYPE html> <html class="i-ua_js_yes i-ua_css_standard"> <head>...</head> <body class="b-page b-page__body"> <div class="b-page__body-i"> <div class="head"> <div class="layout">...</div> </div> </div> </body> </html> 

For the resulting markup, you create your own CSS rules:

 $ bem create -l desktop.blocks/ -T css -b b-page 

The content for the resulting desktop.blocks/b-page/b-page.css can be copied from here: https://gist.github.com/4175763

And in order to make the block of caps visible on the page, I will give it a border:

 $ bem create -l desktop.blocks/ -T css -b head 

Content for desktop.blocks/head/head.css : https://gist.github.com/4175776 .



BEMHTML templates


BEMHTML templates can not only define tags that represent a block, and their attributes, but also generate design content.

For example, let's place on the page a list of products. It is presented in the BEMJSON page declaration by the goods block, and the declaration contains the necessary data.

 { block: 'goods', goods: [ { title: 'Apple iPhone 4S 32Gb', image: '1450827127820366493466', price: '259', url: '/' }, { title: 'Samsung Galaxy Ace S5830', image: 'http://mdata.yandex.net/i?path=b0206005907_img_id5777488190397681906.jpg', price: '73', url: '/' }, ... } 

https://gist.github.com/4176078

In order for this data to become the necessary markup, the block must be implemented in the BEMHTML technology. For appearance - technology CSS. Therefore, you can create a block in all technologies provided by default.

 $ bem create -l desktop.blocks -b goods 

In the BEMHTML template for the desktop.blocks/goods/goods.bemhtml you need to write code that turns JSON with data into block elements. And also, using the tag mode, specify which DOM elements to represent the block and its elements.

 block goods { tag: 'ul' ... elem item, tag: 'li' elem title, tag: 'h3' } 

https://gist.github.com/4176118

 <!DOCTYPE html> <html class="i-ua_js_yes i-ua_css_standard"> <head>...</head> <body class="b-page b-page__body"> <div class="b-page__body-i"> <div class="head">...</div> <ul class="goods"> <li class="goods__item"> <h3 class="goods__title">Apple iPhone 4S 32Gb</h3> <img class="goods__image" src="1450827127820366493466"/> <span class="goods__price">259</span> </li> <li class="goods__item">...</li> <li class="goods__item">...</li> </ul> </div> </body> </html> 

A template can create not only block elements, but also other blocks. For example, the price of a product can be wrapped in a link using the b-link block from the bem-bl library.

 { elem: 'price', content: { block: 'b-link', url: item.url, content: item.price } } 

https://gist.github.com/4176996

In addition, in order to avoid a cascade when styling this link with styles, it can be marked as an element of the goods block.

 { block: 'b-link', mix: [{ block: 'goods', elem: 'link' }], url: item.url, content: item.price } 

https://gist.github.com/4177113

 ... <ul class="goods"> <li class="goods__item"> <h3 class="goods__title">Apple iPhone 4S 32Gb</h3> <img class="goods__image" src="1450827127820366493466"/> <span class="goods__price"> <a class="b-link goods__link" href="/">259</a> </span> </li> <li class="goods__item">...</li> <li class="goods__item">...</li> </ul> 

You also need to mark items about new products with a modifier and add alignment elements.
https://gist.github.com/4177157

The CSS for the block can be copied from here https://gist.github.com/4177163 .
Creating a block separately in CSS technology is not necessary, because it was originally created with all the necessary files.



You need CSS for IE. It is not included in the list of technologies by default.

 $ bem create block -l desktop.blocks/ -T ie.css goods 

The content for the resulting desktop.blocks/goods/goods.ie.css can be taken at Gist https://gist.github.com/4177174

Block dependencies


In addition to the declaration, you need to guarantee the connection to the page of templates, CSS and JavaScript block. For this, the block can describe dependencies, this is done by representing the block in the deps.js technology.

 $ bem create -l desktop.blocks/ -T deps.js -b goods 

You can use the non-strict dependency of shouldDeps by specifying that a b-link block is needed.

 ({ shouldDeps: [ { block: 'b-link' } ] }) 

https://gist.github.com/4177031

Connecting libraries


I would like to present a hat and every product with fashionable rectangles with a shadow. I will borrow the block for this from my friend 's libraries .
There is only one block called box that does what I need.

To get the library code, I need to specify its address in ./bem/make.js , by analogy with neighboring libraries.

 getLibraries: function() { return { 'bem-bl': { type: 'git', url: 'git://github.com/bem/bem-bl.git', treeish: '0.3' }, 'bemhtml' : { type: 'git', url: 'git://github.com/bem/bemhtml.git' }, 'john-lib' : { type: 'git', url: 'git://github.com/john-johnson/j.git' } }; } 

https://gist.github.com/4177229

And also indicate in the settings of bundles (pages) that this level should be used during assembly. This is done in the file desktop.bundles/.bem/level.js .

 exports.getConfig = function() { return BEM.util.extend(this.__base() || {}, { bundleBuildLevels: this.resolvePaths([ '../../bem-bl/blocks-common', '../../bem-bl/blocks-desktop', '../../bemhtml/common.blocks', '../../john-lib/blocks/', '../../desktop.blocks' ]) }); }; 

https://gist.github.com/4177250

Unfortunately, while changing the configuration of the project, you have to restart the bem server . The current process will have to be interrupted and typed make again.
In future versions of the need to restart promise to remove.

Mixes of blocks and elements


Now you can use the box . I can just wrap my blocks with them. But in order to save markup, you can mix 2 blocks on one DOM node. This is called a mix .

One way of mixing is to describe it in the input (BEMJSON).
In this case, you need to mix the head block with the box block by changing the page code.

 { block: 'head', mix: [ { block: 'box' } ], content: ... } 

https://gist.github.com/4177292

 <!DOCTYPE html> <html class="i-ua_js_yes i-ua_css_standard"> <head>...</head> <body class="b-page b-page__body"> <div class="b-page__body-i"> <div class="head box"> <div class="layout">...</div> </div> <ul class="goods">...</ul> </div> </body> </html> 

Do not forget to write down the box block depending on the head block.

 $ bem create -l desktop.blocks/ -T deps.js -b head ({ shouldDeps: [ { block: 'box' } ] }) 

https://gist.github.com/4235143

box

You can mix not only blocks, but also elements with blocks.
In the goods block template, we mix each item element with the box block.

 content.push({ elem: 'item', mods: mods, mix: [{ block: 'box' }], content: ... 

https://gist.github.com/4177350

 <!DOCTYPE html> <html class="i-ua_js_yes i-ua_css_standard"> <head>...</head> <body class="b-page b-page__body"> <div class="b-page__body-i"> <div class="head box"> <div class="layout">...</div> </div> <ul class="goods"> <li class="goods__item box">...</li> <li class="goods__item box">...</li> <li class="goods__item box">...</li> <li class="goods__item goods__item_new_yes box">...</li> <li class="goods__item box">...</li> <li class="goods__sizer">...</li> ... </ul> </div> </body> </html> 

box

Declarative javascript


Blocks with javascript functionality


The box , which appeared on my project due to the connected third-party library, also provides dynamic JavaScript functionality — it can collapse.

For the message that I want to use this JavaScript functionality in the header, I need to change the description of the head block, indicating that the admixed box has a JavaScript implementation:

 mix: [{ block: 'box', js: true }] 

https://gist.github.com/4202622

It is also required to place a switcher element inside the block.

 content: [ { block: 'layout', ... }, { block: 'box', elem: 'switcher' } ] 

https://gist.github.com/4202651

It turns out a block with an arrow that can collapse and expand it.



Redefining JavaScript


The JavaScript functionality of the box not enough for me. I want it to fold not only vertically, but horizontally. At the same time, I cannot make changes to another library. But due to the fact that the JavaScript block is written using a declarative framework from the i-bem block , I have the opportunity to change (redefine or define) the behavior of the block at my level.

 bem create -l desktop.blocks -T js -b box 

In the resulting file desktop.blocks/box/box.js you need to leave only the onSetMod section, which describes the response to the installation of modifiers.

 onSetMod : { } 

https://gist.github.com/4195865

In this case, you need to respond to the installation and removal of the modifier closed :

 onSetMod : { 'closed': { 'yes': function() { // some functionality here }, '': function() { // some functionality here } } } 

https://gist.github.com/4195879

Creating new pages


Pages are also blocks, at their override level. Therefore, to create them, you can also use the bem create command:

 bem create -l desktop.bundles -b contact 

The -T flag can be omitted, because bem create knows that the blocks created at this level must be represented in the BEMJSON technology due to the settings of the desktop.bundles level. So, the file desktop.bundles/contact/contact.bemjson.js appears with minimal content for the page.

A new page can be viewed at http: // localhost: 8080 / desktop.bundles / contact / contact.html
bem server will collect its HTML, JS and CSS files at the time of the first appeal.

Rolling out


All the time while we were developing the project, the bem server working and reassembled those parts of the project that need to be changed when the pages are updated.

For rolling out in production, you also need to build a project, but the entire project is already complete, regardless of whether something has changed or not. To do this, you can use the bem make .
It is recommended to run the local version of the package for this project:

 ./node_modules/bem/bin/bem make 


thanks For preparing the site markup, thank you very much tyv and gela-d .

Source: https://habr.com/ru/post/162385/


All Articles