1. Naming convention
In terms of convention we used to use dashes as word separators, two underscores to separate elements and one underscore for modifier and it's value:
- block-name
- block-name__some-elem
- block-name_mod-name_mod-value
- block-name__some-elem_elemmod-name_elemmod-value
But it worth nothing to use any other style. We even have special library to abstract from naming.
More about naming convention: https://github.com/bem/bem-naming#custom-naming-convention
2. Going beyond naming convention
Naming convention is important but UI component is more than just a piece of CSS.
All methodologies for CSS could be considered as a subset of BEM. But they tell nothing about how to implement other pieces: JS, templates, images, specs, documentation (and its translation to different languages), etc.
BEM as methodology does.
3. Component approach
Actualy it's not something absolutely unique nowadays.
We already have Web components as a standard speaking almost about the same:
- Split interface into independent blocks
- Each block knows everything about itself and hides it's inner implementation
- Declarative way to describe components
All these questions are solved in BEM without any polyfills. But what's much more important all these things are easy to use and tested in production on a lot of services with a really huge scale (in all sences of this word).
4. What's inside
4.1. DOM abstraction
DOM is too low level thing. It's like an assembler for browsers. We need to provide developers with high level abstraction. In BEM it's called BEM tree.
CSS and JS implementation of the block and it's templates work with it's elements. And for other blocks we provide API to work with.
So instead of waiting for shadow DOM roots to grow we just use current DOM tree for inner implementation of components.
4.2. Mixes
Mixes is a concept of having few different entities on the same DOM node. That gives a possibility to separate semantically different code:
<div class="header__user user link">
— it's simultaneously user
element of a header
block, user
block itself and also a link to use profile.
4.3. File system
Each block get's it's own folder. And all techs of the block — their own files. Modifiers and elements may also be separated into different folders to provide possibility to build just the parts files we need.
Example:
blocks/
b1/
b1.css
b1.js
b2/
__elem1/
b2__elem1.css
b2.css
b2.js
b2.en.md
b2.ru.md
b2.spec.js
b2.png
4.4. Build
BEM tree works as a declaration to build final runtime upon.
Example:
Consider following BEM tree:
<page>
<b1>
<b1__e1>
</b1>
</page>
or the same in JS:
{
block: 'page', content: {
block: b1, content: {
elem: 'e1'
}
}
}
Now we can collect all the entities mentioned in this declaration to get all the files from block folder and concat them together.
4.5. Levels of redefinition
With such approach we can build entire library of components for all the purposes (and we actualy did ;)
But it's impossible to meet all the needs. So end users will face situation when they need to change something. Of course they always may edit the source of the library, add modifiers and mix new blocks to old ones to make them look and work as they need. But first option result in a pain when you are to update library version and others require quite a lot of efforts.
But because of declarative nature of blocks implementation we can just put our code in our own block folder and ask build system to concat files from there after library files:
library/
blocks/
b1/
b1.css
.b1 { width: 100px; color: red; }
project/
blocks/
b1/
b1.css
.b1 { color: green; }
pages/
page/
page.css
@import url(../../../library/blocks/b1/b1.css);
@import url(../../blocks/blocks/b1/b1.css);
So we end up with b1 which is still 100px wide as in library but is green according to our company guidelines.
Absolutely the same approach is used for JS and templates. So developer may get some levels (layers) from a list of block libraries, decide which levels are needed for particular project, then add some specific code on project's level and update source libraries as easy as run bower update
.
5. Implementation
At Yandex we implemented and open sourced a full stack of tools written in node.js to work with BEM methodoly as well as JS framework (it uses jQuery under the hood) and template engines operating in BEM terms and some block libraries on top of it.
Quite a lot of companies in CIS in addition to Yandex are already using it and we can confidently say there's no interface tasks which can not be splited into blocks-elements-modifiers.