Login with github

In the BEM documentation, they make reference to something called a "service block":

... In a case like this, instead of creating an element, you need to create a service block.

I can't find any mention of a service block. What does this actually mean?

AFAIK, somewhere I have seen proof of the following things. Please confirm or disprove.

1) As DOM-tree and BEM-tree aren't required be the same, it's possible that the BEM-element is physically located outside its BEM-block in DOM and it is valid.

<div class="block">
…
</div>
<div class="block__element"> … </div>

2) For the same reason the elements of one block can be nested in another block, making intersection in the DOM-tree like:

.block1
     .block2
         .block1__el
         .block2__el

or even like this:

.block1
     .block2
         .block1__el
             .block2__el

Hello!

I've been using the BEM approach for front-end web development a couple of years now (and it's just great!) but only yesterday I started to try out the BEMHTML template language. So far I like it a lot!

There's a couple of things though that I can't yet wrap my head around and can't find anything about in the docs either. So I hope to have some more luck here and hopefully also help others that might be facing the same problems.

Basically I would like to know how to be able to add custom CSS for a webpage that isn't directly block related. For example declarations for webfonts, normalize/reset css code, etc. I would still like to be able to use Stylus as preprocessor and have bem-tools generate and minify this CSS together with my blocks' CSS.

The same question goes for JS. What is the correct way to add JS libraries and custom JS code, preferably maintaining the possibility to have this combined/concatenated with the block level JS?

Thanks! :)

The thinner the footprint the better.
Without all the heavy lifters like enb, only bem-tools maybe.

This is what I try:

npm install -g bem
mkdir bem-playground
cd bem-playground
bem create level my-blocks
bem create -b foo-block -l my-blocks -t <WHAT TYPE TO START WITH?>

html type is deprecated, list of types, I guess, is here: https://github.com/bem/bem-tools/tree/master/lib/techs/v2
Default types are empty so no block is created without -t.

Ok, I can create block manually. But what's next?
To assemble blocks into a page, I guess, I need some bemdecl and some assembler (enb again).
So I can't escape complexity footprint of enb onto my stack, right?

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.

In the simplest form a block is represented by a single DOM node but generally blocks and DOM nodes are not the same thing.

Mixes are just the way to use several blocks and/or elements on the same DOM node.

That makes possible

  • combining behaviour and/or styling of different BEM entities without copy/paste
  • building new semantic components based on existed blocks.

For instance, we have a universal block logo (a block with a logotype) that could be used anywhere on a page: in header, inside copy, in the footer, etc. We can specify its width, height and a background image but what we do with its margins that differ from case to case?

Of course, we could use modifiers like logo_type_header or logo_type_content to do so. However it is wrong semantically, because such things should lay down within a scope of parent block’s responsibility. And giving the right to define such things to an inner block contradicts the whole idea of blocks independency.

That's where mixes shine:

<div class="header">
    <div class="header__logo logo"></div>
</div>
.logo
{
    width: 120px;
    height: 60px;
    background-image: url(logo.svg);
}

.header__logo
{
    margin: 12px 26px;
}

As you know there's no global modifiers in BEM methodology but again — mixes gives us possibility to achieve the same result with several different blocks on same DOM node.

Also you may want to use mixes to provide some arbitrary block with JS logic.

A single DOM node can represent:

  • several blocks <div class="menu head-menu"></div>
  • a block and an element of the same block <div class="menu menu__layout"></div>
  • a block and an element of another block <div class="link menu__link"></div>
  • elements of different blocks <div class="menu__item head-menu__item"></div>
  • a block with a modifier and another block <div class="menu menu_layout_horiz head-menu">
  • several different blocks with modifiers <div class="menu menu_layout_horiz head-toolbar head-toolbar_theme_black"><div>

So try to develop your blocks as small and reusable as possible and then combine them in any way you want to build huge applications!