Practice: converting a component to BEM
Let us put together everything from this chapter in one example. Refactoring means changing code without changing its behavior: the page looks the same, but the code becomes clearer. We will take a product card written "however it turned out" and convert it to BEM.
The original code
Here is the card in the shape it often has in old projects:
<style>
#catalog div { padding: 12px; border: 1px solid silver; border-radius: 8px; max-width: 240px; margin-bottom: 8px; }
#catalog div h3 { margin: 0 0 6px; font-size: 18px; }
#catalog div span { color: seagreen; font-weight: bold; }
#catalog div button { margin-top: 8px; padding: 6px 12px; border: none; border-radius: 6px; background: seagreen; color: white; }
.sale { border-color: crimson !important; }
.sale span { color: crimson; }
</style>
<section id="catalog">
<div>
<h3>Backpack</h3>
<span>€40</span><br>
<button>Buy</button>
</div>
<div class="sale">
<h3>Tent</h3>
<span>€72</span><br>
<button>Buy</button>
</div>
</section>Notice: the second card has a red border, but its price stayed green — the .sale span rule lost to the selector with an id.
What is wrong here
- The selectors depend on the
catalogid: the card cannot be shown outside the catalog. - The styles are tied to tags: replace
<h3>with<h2>and the styling is gone. #catalog divhits any<div>that appears inside the card.- The modifier relies on
!importantand still did not recolor everything.
Step 1: finding the blocks
First we decide which parts are standalone. The card is the product block. The button appears all over the site — a separate button block. Its place in the card will be set by an element through a mix.
Step 2: elements and modifiers
| Before | After | What it is |
|---|---|---|
#catalog div | .product | block |
#catalog div h3 | .product__title | element |
#catalog div span | .product__price | element |
#catalog div button | .button and .product__buy | block and element (a mix) |
.sale | .product--sale | modifier |
The <br> is no longer needed: display: block on the price is enough.
The result
<style>
.button { padding: 6px 12px; border: none; border-radius: 6px; background: seagreen; color: white; }
.product { padding: 12px; border: 1px solid silver; border-radius: 8px; max-width: 240px; margin-bottom: 8px; }
.product--sale { border-color: crimson; }
.product__title { margin: 0 0 6px; font-size: 18px; }
.product__price { display: block; color: seagreen; font-weight: bold; }
.product--sale .product__price { color: crimson; }
.product__buy { margin-top: 8px; }
</style>
<section>
<div class="product">
<h3 class="product__title">Backpack</h3>
<span class="product__price">€40</span>
<button class="button product__buy">Buy</button>
</div>
<div class="product product--sale">
<h3 class="product__title">Tent</h3>
<span class="product__price">€72</span>
<button class="button product__buy">Buy</button>
</div>
</section>The cards look as before, but now the price in the sale card is really red. There is no id and no !important in the code, and the card can be placed anywhere on the site.
Common mistake
Renaming the classes but keeping nested selectors:
.catalog .product .product__title { margin: 0 0 6px; }
.catalog .product .product__price { color: seagreen; }The names look like BEM now, but the specificity is still high, and the card still works only inside .catalog. Check the result of a refactoring: almost every selector should be a single class.