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
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.
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.
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
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
Then the project needs to be assembled. To do this, run the command
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:
Launched on your computer
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
Generally speaking, strictly speaking, it
You can edit the page by changing the file
First, we will place the Hat on the page. In terms of BEM, this is a block
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
https://gist.github.com/4175573
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
To create a technology file, use the command
The command will create a file
Now you can simply copy: https://gist.github.com/4175598
Nested in
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.
We will put this markup in a block
The markup for the block can be taken from here: https://gist.github.com/4175675
The same for the block
https://gist.github.com/4195433
To make the page centered, you need an additional container. To do this, we will redefine the block templates by
In the resulting file,
https://gist.github.com/4175742
For the resulting markup, CSS rules are created:
The content for the resulting file
And in order for the header block to be visible on the page, I will set it to border:
Content for the file
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
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.
In the BEMHTML block template,
https://gist.github.com/4176118
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
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
https://gist.github.com/4177113
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.
You’ll also need CSS for IE. It is not included in the default technology list.
The contents for the resulting file
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
You can take advantage of a lax dependency
https://gist.github.com/4177031
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
To get the library code, I need to specify its address in
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
https://gist.github.com/4177250
Unfortunately, for now, when you change the configuration of the project, you have to restart
In future versions, the promise of a restart is promised to be removed.
Now you can use the block
One way to mix is to describe it in the input (BEMJSON).
In this case, you need to mix the block
https://gist.github.com/4177292
Remember to write a block
https://gist.github.com/4235143
Смешивать можно не только блоки, но и элементы с блоками.
В шаблоне блока
https://gist.github.com/4177350
Блок
Для сообщения, что я хочу использовать эту JavaScript-функциональность в шапке, мне нужно изменить описание блока
https://gist.github.com/4202622
Также требуется разместить внутри блока элемент
https://gist.github.com/4202651
It turns out a block with an arrow, which can collapse and expand it.
JavaScript block functionality is not enough for me
In the resulting file,
https://gist.github.com/4195865
In this case, you need to respond to the installation and removal of the modifier
https://gist.github.com/4195879
Pages are also blocks, at their own level of redefinition. Therefore, to create them, you can also use the command
The flag
The new page can be viewed at http: // localhost: 8080 / desktop.bundles / contact / contact.html
All the time while we were developing the project, we worked
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
It is recommended to run the local version of the package for this project:
gratitude For the preparation of the layout of the site, many thanks to tyv and gela-d .
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.js
and using the template engine BEMHTML
. All this will be helped by bem tools
, in particular, a development tool bem server
. 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.bundles
stores a “set” of blocks. These can be frequently used blocks of several pages (what is usually called common
), sets that combine several pages ( all
if 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
head
put a block layout
with two elements inside the block : 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
...
left hereright 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
layout
in 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.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 simply copy: https://gist.github.com/4175598
Using blocks from libraries
Nested in
layout
blocks 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.
Redefining library blocks
CSS Redefinition
The block we useb-logo
provides 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-logo
at 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
Redefining BEMHTML
To make the page centered, you need an additional container. To do this, we will redefine the block templates by
b-page
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 file,
desktop.blocks/b-page/b-page.bemhtml
you 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.css
can be copied from here: https://gist.github.com/4175763And 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 .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.bemhtml
you need to write code that turns JSON with data into block elements. And also, using the mod, tag
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
...
...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-link
from 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.
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.css
can be taken on Gist https://gist.github.com/4177174Block 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
shouldDeps
by 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
box
and 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
head
with the block box
by changing the page code.{
block: 'head',
mix: [ { block: 'box' } ],
content: ...
}
https://gist.github.com/4177292
...
......
Remember to write a block
box
depending on the blockhead
$ bem create -l desktop.blocks/ -T deps.js -b head
({
shouldDeps: [
{ block: 'box' }
]
})
https://gist.github.com/4235143
Смешивать можно не только блоки, но и элементы с блоками.
В шаблоне блока
goods
смешаем каждый элемент item
с блоком box
.content.push({
elem: 'item',
mods: mods,
mix: [{ block: 'box' }],
content: ...
https://gist.github.com/4177350
...
...- ...
- ...
- ...
- ...
- ...
- ...
...
Декларативный 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.
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.js
you need to leave only the section onSetMod
that 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
-T
can be omitted, because bem create
thanks to the level settings it desktop.bundles
knows that the blocks created at this level must be represented in BEMJSON technology. So, a file appears desktop.bundles/contact/contact.bemjson.js
with 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 server
and 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 .