EN
Forum

Methodology

Toolbox

Platform

Redefinition levels

What is a redefinition level?

A redefinition level is a directory in a BEM project that contains files for implementing blocks, elements, and modifiers.

Any BEM project consists of redefinition levels. Every project must have at least one level, but the maximum number of levels is unlimited.

Example of the file system for a BEM project with one redefinition level:

project/ 
    common.blocks/ # redefinition level with project blocks 
        header/ 
        footer/

Redefinition rules allow you to:

What are redefinition levels used for?

Redefinition levels are used for the following purposes:

Adding blocks to a project

You can use blocks from any level in a project without making changes.

The example below shows how to use a button from a third-party library in a project. To do this, you just need to connect the library with the button block on a separate level. You don't need to copy the code for the button block to the level with the project blocks.

The project's file system with the connected library level:

project/ 
    common.blocks/  # redefinition level with project blocks 
        header/ 
        logo/ 
    library.blocks/ # redefinition level with library blocks 
        button/     # button block 

As the result of building the project, the button block will be included in the project:

@import "common.blocks/header/header.css";  /* header from the common project block level */
@import "common.blocks/logo/logo.css";      /* logo from the common project block level */
@import "library.blocks/button/button.css"; /* button from the library level */

More about building BEM projects and integrating BEM entities into a project.

Changing the block implementation

You can change blocks from any level to meet the needs of a project on a different redefinition level:

Important In a BEM project, any block implementation technology can be extended or redefined. For more information, see Platform.

You can use any number of levels in any order for assembling the final block implementation. The original block implementation is extended or redefined by implementations on the subsequent levels. This is why it's important for the original implementation to be included in the build first, and then changes can be applied from all the redefinition levels.

Important The original implementation of the block does not change when it is extended or redefined.

The diagram shows how to add BEM entities from different redefinition levels to the build:

how redefinition levels work

The example below shows how to change the button block from a third-party library that is connected to the project on a separate level (library.blocks):

project/ 
    common.blocks/  # redefinition level with project blocks 
        header/ 
        logo/ 
    library.blocks/ # redefinition level with library blocks 
        button/     # button block 

Original CSS implementation of the button block:

CSS implementation:

/* The button block in CSS on the library.blocks level*/ 

.button { 
    position: absolute; 
    border: 1px solid rgba(0,0,0,.2); 
    border-radius: 3px; 
    background-color: #fff;
}

Rendered result:

button-default

To make changes, you need to:

To do this, create the button block on the common.blocks project level and place the button.css file in it with the new button styles.

The project's file system with the button block on the common.blocks level:

project/ 
    common.blocks/     # redefinition level with project blocks 
        header/ 
        logo/ 
        button/ 
            button.css # new rules for the button block 
    library.blocks/    # redefinition level with library blocks 
        button/        # the button block 
            button.css 
            button.js

New CSS rules:

/* The button block in CSS on the common.blocks level */

.button {
    background-color: #ffdf3a;            /* New button color */
    width: 150px;                         /* Button width */
    box-shadow: 0 0 10px rgba(0,0,0,0.5); /* Shadow parameters */
}

After the build, the implementation of the button block will consist of the original CSS rules from the library.blocks level and the additional rules from the common.blocks level:

@import "library.blocks/button/button.css";  /* Original CSS rules from the library level */
@import "common.blocks/button/button.css";   /* Properties from the common.blocks level*/

The duplicated property (background-color) is redefined (the background color changes to yellow), and the new properties (width and box-shadow) are added. The following set of properties is applied to the button block:

.button {
    position: absolute;
    border: 1px solid rgba(0,0,0,.2);
    border-radius: 3px;
    background-color: #ffdf3a;             /* New button color */
    width: 150px;                          /* Button width */
    box-shadow: 0 0 10px rgba(0,0,0,0.5);  /* Shadow parameters */
}

New button appearance:

Redefined button

Result:

How to use redefinition levels

You can configure different builds in the same project: define the order and number of levels for each separate case. For example, you can individually configure the set of levels to use for each page in the project.

The example shows how to divide up a project by platform using redefinition levels.

The image shows the project build for different platforms, depending on the user agent:

Redefinition levels

Examples using redefinition levels

Common ways to use redefinition levels:

Dividing a project into platforms

In a project that supports multiple platforms (for instance, mobile and desktop, part of the code defines the overall functionality, and part of it is specific to each platform. To avoid copying the shared code for each of the platform implementations, you can use redefinition levels.

The general implementations of blocks that are used for all platforms are located on the same level, such as common.blocks, while the device-specific implementations are on other levels:

Example of the file system for a project with different platforms:

project/ 
    common.blocks/ 
        button/ 
            button.css   # basic CSS button implementation 
    desktop.blocks/ 
        button/ 
            button.css   # custom button for desktop 
    mobile.blocks/ 
        button/ 
            button.css   # custom button for mobile

As a result of the build, the desktop.bundles/bundle/bundle.css file gets all the basic CSS rules for the button from the common.blocks level and the redefinition rules from the desktop.blocks level.

@import "common.blocks/button/button.css";   /* Basic CSS rules */
@import "desktop.blocks/button/button.css";  /* Desktop version */

The mobile.bundles/bundle/bundle.css file gets all the basic CSS rules for the button from the common.blocks level and the redefinition rules from the mobile.blocks level.

@import "common.blocks/button/button.css";   /* Basic CSS rules */
@import "mobile.blocks/button/button.css";   /* Mobile version */

Dividing the code into separate redefinition levels lets you have different builds of the same project at the same time, so you can offer the correct version based on the user agent.

Updating connected libraries of blocks

Redefining or extending blocks from a library on a separate level allows you to keep the changes you make for a project, even when the library is updated.

In the example, a library is integrated into the project as the library.blocks redefinition level:

project/ 
    common.blocks/   # redefinition level with project blocks 
        header/ 
        logo/ 
    library.blocks/  # redefinition level with library blocks 
        button/                          

To use a button from the library (the button block) in the project, you need to change the button height from 18 px to 24 px. To do this, redefine the button block on the project level:

project/ 
    common.blocks/      # redefinition level with project blocks 
        button/ 
            button.css  # redefined rules for the button 
        header/ 
        logo/ 
    library.blocks/     # redefinition level with library blocks 
        button/         # button implementation in the library

When the library is updated, the redefined rule for the button block (height 24 px) is saved, since the redefined property doesn't affect the original block implementation and is located on a different redefinition level.

Developing projects with common blocks

Blocks that are used in multiple projects can be moved to a separate level and added during the build.

The example below shows the file system for a project where blocks that are shared between two projects are isolated on the separate common.blocks level:

projects/ 
    common.blocks/    # shared blocks for multiple projects 
        button/ 
        input/ 
    project-1/        # project 1 
        button/       # redefined button block for project 1 
        logo/ 
        modal/ 
    project-2/        # project 2 
        button/       # redefined b1 block for project 2 
        search/ 
        spin/         

Creating design themes

A project's logic and layout can be separated into separate redefinition levels. This allows you to create different versions of the design, switch between themes in the project, and combine styles, without affecting the project's behavior.

In the example, different design themes are implemented on separate levels. To change the project's visual appearance, you just need to add the desired level to the build.

project/ 
    common.blocks/    # shared blocks for describing the project's business logic 
        button/ 
        input/ 
        ... 
    alpha/            # alpha design theme 
        button/ 
        input/ 
    beta/             # beta design theme 
        button/ 
        input/

Running experiments on a live project

Redefinition levels allow you to perform A/B testing directly in a live project. The code of the live project doesn't change during the experiments, since each experiment is on a separate redefinition level.

You can run experiments by changing a block's style, behavior, or page markup. For example, to ensure site accessibility (a11y), you need to perform experiments with adding new tags to a page. To do this, redefine the templates and the JavaScript code on the experiment level.

To remove an unsuccessful experiment from the project, just delete the directory that contains this redefinition level.

The example below shows how to add multiple experiments to the file system in a live project:

project/ 
    common.blocks/    # project blocks 
        header/ 
        user-name/ 
        user-pic/ 
        ... 
exps/ 
    exp-1/            # level for experiment 1 
        header/       # new offsets in the header 
        user-name/    # new font for the user name 
        user-pic/     # new type of profile picture 
    exp-2/            # level for experiment 2 
        header/       # new offsets in the header 
        user-name/    # new font for the user name 
        user-pic/     # new type of profile picture 
    exp-n/            # level for any new experiment 
        header/       # new offsets in the header 
        user-name/    # new font for the user name 
        user-pic/     # new type of profile picture       

To see the changes, just add the experiment level to the build.

Rate the article
Report an error on GitHub or correct using prose.io.