Taste the BEM!

  • Tutorial
This article talks about how to create a project using BEM technologies.
We will create a product catalog page step by step , using the principles of BEM in CSS, the ability to write declarative JavaScript in the framework i-bem.jsand using the template engine BEMHTML. All this will be helped by bem tools, in particular, a development tool bem server.

Shop online

Important: in the article there are no particular details, its purpose is to get the project as quickly as possible. The text revealing more information will be held in the next post.

What is a BEM?


To begin with, a small digression for those who are not aware of what this abbreviation means.
BEM stands for "Block, Element, Modifier." This is a web project development methodology, a way to conveniently divide the interface into separate pieces, applicable to any technology. In addition, BEM is a set of tools for automating work. And finally, BEM is the ability to create front-end libraries for fast and efficient development.

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

Necessary 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, the version was current 0.5.21.

Creating your own project repository


The easiest way to create your project is to simply copy an existing repository with a suitable structure. For a project using the full BEM stack, the project-stub repository is suitable . At the time of writing, the revision was used 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 assembled. To do this, run the command

make

This takes some time, because at that 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/

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

Changing Pages


Your project now has one index.html page 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 directory desktop.blocks, and the pages in the directory desktop.bundles.
Generally speaking, strictly speaking, it desktop.bundlesstores a “set” of blocks. These can be frequently used blocks of several pages (what is usually called common), sets that combine several pages ( allif all pages are combined) or, in 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.

Block description in bemjson


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

{ block: 'head' }

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

By reloading the page, you will see that the corresponding page appears in it.
.



    ...
    
        

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

First, in the BEMJSON description of the page, we headput a block layoutwith two elements inside the block : leftand right.

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

https://gist.github.com/4175573



    ...
    
        
left here
right here

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

Block creation


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

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

The command will create a file desktop.blocks/layout/layout.cssin 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 simply copy: https://gist.github.com/4175598

Using blocks from libraries


Nested in layoutblocks of the search form and logo do not need to be implemented independently. They are already implemented in the bem-bl library , it is enough to simply declare them on the page. That is, insert the BEMJSON description of the block into the page. 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 the logo from here or specify your own.

Using blocks from the library

Redefining library blocks


CSS Redefinition

The block we use b-logoprovides only the necessary markup. Each developer can write CSS for it himself, because everyone needs different markup.

We will put this markup in a block b-logoat our redefinition 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 block b-search:

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

https://gist.github.com/4195433

Hat with styles

Redefining BEMHTML



To make the page centered, you need an additional container. To do this, we will redefine the block templates by b-pagecreating 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 file, desktop.blocks/b-page/b-page.bemhtmlyou need to write code that wraps the block content in an additional container.

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

https://gist.github.com/4175742



    ...
    
        
...

For the resulting markup, CSS rules are created:

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

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

And in order for the header block to be visible on the page, I will set it to border:

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

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

Hat with frame

BEMHTML Templates


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

For example, let's put a list of products on a page. It is presented in the BEMJSON declaration of the page in a block goods, 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 turn into the necessary markup, the block must be implemented in BEMHTML technology. For appearance - CSS technology. Therefore, you can create a block in all technologies provided by default.

$ bem create -l desktop.blocks -b goods

In the BEMHTML block template, desktop.blocks/goods/goods.bemhtmlyou need to write code that turns JSON with data into block elements. And also, using the mod, tagspecify 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



    ...
    
        
...
  • Apple iPhone 4S 32Gb

    259
  • ...
  • ...

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 a block b-linkfrom the library bem-bl.

{
    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, it can be marked as a block element goods.

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

https://gist.github.com/4177113

...
  • Apple iPhone 4S 32Gb

    259
  • ...
  • ...

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

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.

Product List

You’ll also need CSS for IE. It is not included in the default technology list.

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

The contents for the resulting file desktop.blocks/goods/goods.ie.csscan be taken on 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. To do this, you can describe the dependencies of the block, this is done by representing the block in the technology deps.js.

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

You can take advantage of a lax dependency shouldDepsby indicating that a block is needed b-link.

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

https://gist.github.com/4177031

Connecting libraries


I would like to present a hat and each product with fashionable rectangles with a shadow. The block for this I will borrow from my friend ’s library .
There is only one block, which is called boxand 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, for now, when you change the configuration of the project, you have to restart bem server. The current process will have to be interrupted and the team re-formed make.
In future versions, the promise of a restart is promised to be removed.

Mixes of blocks and elements


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

One way to mix is ​​to describe it in the input (BEMJSON).
In this case, you need to mix the block headwith the block boxby changing the page code.

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

https://gist.github.com/4177292



    ...
    
        
...
    ...

Remember to write a block boxdepending on the blockhead

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

https://gist.github.com/4235143

Hat with box

Смешивать можно не только блоки, но и элементы с блоками.
В шаблоне блока goods смешаем каждый элемент item с блоком box.

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

https://gist.github.com/4177350



    ...
    
        
...
  • ...
  • ...
  • ...
  • ...
  • ...
  • ...
  • ...

Products in box

Декларативный JavaScript


Блоки с JavaScript функциональностью


Блок box, который появился у меня на проекте благодаря подключенной сторонней библиотеке, предоставляет также и динамическую JavaScript-функциональность — он умеет сворачиваться.

Для сообщения, что я хочу использовать эту JavaScript-функциональность в шапке, мне нужно изменить описание блока head, указав, что у примешиваемого блока box есть JavaScript-реализация:

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

https://gist.github.com/4202622

Также требуется разместить внутри блока элемент switcher

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

https://gist.github.com/4202651

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

Arrow

JavaScript override


JavaScript block functionality is not enough for me box. I want it to fold not only vertically, but also horizontally. However, I can’t make changes to someone else’s 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 (override or redefine) the behavior of the block at its own level.

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

In the resulting file, desktop.blocks/box/box.jsyou need to leave only the section onSetModthat describes the reaction 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

Create New Pages


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

bem create -l desktop.bundles -b contact

The flag -Tcan be omitted, because bem createthanks to the level settings it desktop.bundlesknows that the blocks created at this level must be represented in BEMJSON technology. So, a file appears desktop.bundles/contact/contact.bemjson.jswith minimal content for the page.

The 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 call.

Roll out


All the time while we were developing the project, we worked bem serverand reassembled those parts of the project that need to be changed when updating the pages.

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

./node_modules/bem/bin/bem make


gratitude For the preparation of the layout of the site, many thanks to tyv and gela-d .

Also popular now: