Work in progress
By using the Block-Element-Modifier (BEM) CSS methodology, the chances of undesired and conflicting style overrides is virtually eliminated, and the need for nesting is diminished (which usually results in better performance, by eliminating excessive selector depth).
.block {
}
.block__element {
}
.block__element--modifier {
}
.block--modifier {
}
A good example is a simple card:
// Product block
.product {
color: black;
}
// Product elements
.product__image {
opacity: 1;
}
// Product modifier
.product--out-of-stock {
color: red;
.product__image {
opacity: 0.5;
}
}
<!-- Normal product / color is black -->
<div class="product">
<img class="product__image" src="image.jpg" /> <!-- image is 100% opaque -->
</div>
<!-- Out of stock product / color is red -->
<div class="product product--out-of-stock">
<img class="product__image" src="image.jpg" /> <!-- image is 50% opaque -->
</div>
That will make it much harder to debug and find the line of code.
// Wrong
.product {
color: black;
&__image {
opacity: 1;
}
}
// Right
.product {
color: black;
}
.product__image {
opacity: 1;
}
That means the following code should be avoided. Not only because it produces more code, it also makes it harder to keep track of the parent-children chain, and harder to debug in some cases. The longer the rules, the harder it gets to keep track of the parent when deep nesting is used.
// Unnecessary nesting
.product {
color: black;
.product__image {
opacity: 1;
}
&.product--out-of-stock {
color: red;
.product__image {
opacity: 0.5;
}
}
}
// Would result in:
.product { color: black }
.product .product__image { opacity: 1 }
.product.product--out-of-stock { color: red }
.product.product--out-of-stock .product__image { opacity: 0.5 }
// As opposed to how it should be (without the unnecessary nesting):
.product { color: black }
.product__image { opacity: 1 }
.product--out-of-stock { color: red }
.product--out-of-stock .product__image { opacity: 0.5 }
In the above example, .product .product__image
is an over-qualification, as .product__image
itself is sufficient to target the correct element.
In slightly more complex situations, blocks can also be elements of other blocks:
// Price block
.price {
font-weight: 700;
}
// Product price element
.product__price {
font-weight: 400;
}
<!-- Price used isolated / 700 -->
<p><span class="price">$100</span></p>
<!-- Price used as a product element / 400 -->
<div class="product">
<p><span class="price product__price">$100</span></p>
</div>
.block {
padding: $spacing-md;
}
.block {
font-size: rem(12px);
@include media-max($large) {
font-size: rem(16px);
}
@include media-min($large) {
font-size: rem(24px);
}
}
Whenever possible and where it makes sense.
<!-- HTML -->
<button class="button button--primary" data-toggle-button>
Open menu
</button>
/* CSS */
.button {
}
.button--primary {
}
// JS
const toggleButton = container.querySelector('[data-toggle-button]');