Any project developed using the BEM methodology has a multi-level file structure. Such a structure results from the component-based approach. In BEM, blocks can be implemented in one or more technologies. Each technology is stored in a separate file. Consequently, a build process is required in order to obtain general project files from individual files. You can use any of the available build tools.
Here is an example of a file structure organization of a BEM project:
blocks/ # Project level
input/ # input block directory
input.css # input block implementation in CSS technology
input.js # input block implementation in JavaScript technology
icon/
icon.css
library/ # Library level
input/
input.js # input block basic implementation in JavaScript technology
button/
To find out more about the reasons for dividing a block implementation into separate files, read File system organizaation of a BEM project.
The build process takes care of the following:
In order to obtain a finished project part (build result), such as a web page, from individual files, we need to:
We can get different final sets of files as the outcome of the build:
hello.css
and hello.js
);project.css
file and one project.js
file);header
and footer
, which are used on different pages of the project (e.g., header.css
and header.js
). The common part is built separately and linked during the build process.In the BEM methodology, such file sets resulting from the build process are commonly called bundles.
Note that in this document the build process is illustrated on the example of a page, which is an instance of a bundle.
In the file system, build results for individual files are placed in a default directory carrying the page name (e.g., hello
):
blocks/ # Directory containing the blocks
bundles/ # Directory containing all build results (optional)
hello/ # Directory for the hello page (created manually)
An example of a pre-build file structure of a BEM project:
blocks/ # Directory containing the blocks
bundles/ # Directory containing all build results
hello/ # Directory for the hello page
hello.decl.js # List of BEM entities required for the hello page
An example of a post-build file structure of a BEM project:
blocks/ # Directory containing the blocks
bundles/ # Directory containing all build results
hello/ # Directory for the hello page
hello.decl.js # List of BEM entities required for the hello page
hello.css # Built CSS file for the hello page
hello.js # Built JavaScript file for thehello page
Either of the following can be included in the project during the build process:
Building a project with only the relevant BEM entities makes use of the following (all are optional):
To start the build process for a page, the build tool must know all of its components.
The first step in the build process is making a list of the necessary BEM entities. The following example explains the purpose of such a list and how it is created.
Let's say there is a library linked to your project, and some of the blocks from that library are used on the page. There is no need to include the entire library in the build. You can use the page description to make a list of the specific items required. This can be done either automatically or manually. Only the listed blocks will end up in the build. In the BEM methodology, such a list is called a declaration.
The main purpose of the declaration is to define what and in what order should be included in the build.
Find out more about different ways of building a declaration.
In the BEM methodology, blocks are often built on the basis of other blocks. For instance, the search form block (search-form
) is built using the input
and button
blocks. There is no need to implement a block again if it is already stored in the library. You can build the new block on the basis of the existing one.
To create a block based on another block that already exists, you need to specify dependencies between the two. For instance, the above-mentioned search-form
block is dependent on input
and button
.
The build tool receives the dependency data and adds all the entities and technologies necessary for implementing the block during the build process. The order of including entities is also specified in the dependencies.
Dependencies can be specified in a number of ways:
Directly in the block code.
In a separate file.
The BEM platform uses a technology called DEPS for specifying dependencies.
blocks/
input/
input.css
input.js
input.deps.js # input block dependencies
button/
button.css
button.js
The order of including BEM entities in the build depends upon:
specified dependencies
Dependencies determine the order in which extra entities should be added to the build.
redefinition levels used
It is important to ensure that levels are linked to the build in a correct order. If we compare levels to layers, then the basic layer is the original implementation of the block, such as supplied from the library, and each next layer is added on top and complements (inherits) or modifies the basic implementation. That is why it is important that the original implementation is included in the build first, and then the changes from all the redefinition levels.
The diagram illustrates the principle of applying redefinition levels to the build: components common to all platforms are linked to the common
level, while platform-specific components are linked from the desktop
and touch
levels.
To find out more about using redefinition levels, read these examples:
The choice of a particular build tool depends on the complexity of your BEM project, on whether it has redefinition levels and whether it uses dependencies.
The BEM methodology does not limit your choice of tools — you can use any build tool (e.g., Gulp, Grunt, Brunch, Broccoli) that meets the requirements of your project.
An example of building a BEM project using Gulp — Declarative JavaScript with BEM.
The BEM platform uses ENB, which is a tool suitable for building BEM projects of any level of complexity.
An example of building a BEM project using ENB — Starting your own BEM project.