Every block
should be in separated file named as block.
Filename: rating-star.scss
.rating-star {
$font-size: 0.5em;
display: inline-block; // `display` style may be set freely
width: $font-size;
height: $font-size;
// do not override all the properties with short syntax if you don't really need it
background-color: antiquewhite;
// Do you need to change background color only? Use `background-color`
// Do you need to change `margin` to `auto` at `left` and `right`? Use verbose syntax: `margin-left: auto; margin-right: auto;`
// But of course, feel free to do: `margin: 0 1em 0.3em` if you are really need it.
border-radius: 100%;
// Adds `modifier` for `block`
&--highlighted {
background-color: yellow;
}
}
Filename: product-rating.scss
.product-rating {
// `font-size` property is prohibited for block with children (means with `elements`)
// Every `block` containing `elements` should inherit `font-size` from its parent
display: flex;
justify-content: center;
align-items: center;
&__caption {
font-style: italic;
margin-right: 0.5em;
// Caption is ready to be shown with siblings, and as stand alone caption without trailing margin
&:last-child {
margin-right: 0;
}
}
}
Filename: product-item.scss
.product-item {
$b-root: &;
display: flex;
background-color: red;
// `margin` and `padding` is prohibited for `blocks` at all
// `margin` and `padding` must be set only via cascade (from parent `block` or `element`, not by current block itself)
&__title {
font-size: 2em; // only `em` values should be used wherever possible
// Fixed sizes (px, rem) have to be used only if you are really understand what are you going to do
margin-bottom: 0.3em; // don't use `padding` to add offset between nodes
// Btw, for some one could be new to know about margin collapsing
// More info here: https://css-tricks.com/what-you-should-know-about-collapsing-margins/
// We can use `block` variable to create cascade in this case
& + #{$b-root}__title-secondary {
margin-top: 1em;
}
}
// One more possible way to keep `block` name for `elements`
&__title + &__title-secondary {
margin-top: 1em;
}
&__title-secondary {
font-size: 1.3em;
}
.product-rating {
justify-self: flex-end;
font-size: 1.1em;
}
&--rating-pull-left {
.product-rating {
justify-self: flex-start;
}
}
}
HTML markup:
<div class="product-item product-item--rating-pull-left">
<h2 class="product-item__title">Product Title</div>
<div class="product-rating">
<div class="rating-star rating-star--highlighted"></div>
<div class="rating-star rating-star--highlighted"></div>
<div class="rating-star rating-star--highlighted"></div>
<div class="rating-star"></div>
</div>
</div>
CSS output will be:
.rating-star {
display: inline-block;
width: 0.5em;
height: 0.5em;
background-color: antiquewhite;
border-radius: 100%;
}
.rating-star--highlighted {
background-color: yellow;
}
.product-rating {
display: flex;
justify-content: center;
align-items: center;
}
.product-rating__caption {
font-style: italic;
margin-right: 0.5em;
}
.product-rating__caption:last-child {
margin-right: 0;
}
.product-item {
display: flex;
background-color: red;
}
.product-item__title {
font-size: 2em;
margin-bottom: 0.3em;
}
.product-item__title + .product-item__title-secondary {
margin-top: 1em;
}
.product-item__title + .product-item__title-secondary {
margin-top: 1em;
}
.product-item__title-secondary {
font-size: 1.3em;
}
.product-item .product-rating {
justify-self: flex-end;
font-size: 1.1em;
}
.product-item--rating-pull-left .product-rating {
justify-self: flex-start;
}
Filename also could be as product-rating--inverted.scss
(suffix with modifier name) and contain only block modifier`s code.
Filename: product-rating.scss
.product-rating {
// common styles here
}
Filename: product-rating--inverted.scss
.product-rating--inverted {
// inverted styles here
}
Filename: product-item.scss
.product-item {
// Actually, it's not about syntax.
// But don't forget, this is bad way
font-size: 14px; // if really needed at least convert it to `em`
&--title {
font-size: 2em;
&-secondary { // never do this, impossible to debug
font-size: 1.3em;
}
}
}
Filename: product-item.scss
.product-item {
&--title {
font-size: 2em;
}
&--title-secondary {
font-size: 1.3em;
}
}
In this case we need to set different background-color
for .rating-star
, for normal and highlighted states.
Take a look on __iverted
modifier:
<div class="product-rating product-rating__inverted">
<div class="rating-star rating-star--highlighted"></div>
<div class="rating-star rating-star--highlighted"></div>
<div class="rating-star"></div>
</div>
Filename: product-rating--inverted.scss
.product-rating--inverted {
.rating-star { // yes, allowed to use cascade for inner `block`, not a mistake
background-color: black; // everything is still ok
// We can freely override child `block` from parent `block` or even from `element`
&--highlighted {
background-color: black; // wrong usage
// Never change style for block `modifier` directly from parent `block`
}
}
}
To resolve this, we need to change html markup and .rating-star
block.
<div class="product-rating">
<div class="rating-star rating-star--inverted rating-star--highlighted"></div>
<div class="rating-star rating-star--inverted rating-star--highlighted"></div>
<div class="rating-star"></div>
</div>
Filename: rating-star.scss
.rating-star {
$b-root: &; // store pointer to root `block` selector
background-color: antiquewhite;
&--highlighted {
background-color: yellow;
}
&--inverted {
background-color: white;
opacity: 0.5;
}
&--highlighted#{&}--inverted { // same as rule below; `#{&}` is important in this case/
opacity: 1;
}
#{$b-root}--highlighted#{$b-root}--inverted {
// same selector as above in different notation
}
}
CSS output will be
.rating-star {
background-color: antiquewhite;
}
.rating-star--highlighted {
background-color: yellow;
}
.rating-star--inverted {
background-color: white;
opacity: 0.5;
}
.rating-star--highlighted.rating-star--inverted {
opacity: 1;
}
Actually more BEM-way to do this is to declare completely new modifier
:
.rating-star {
&--inverted {
background-color: white;
opacity: 0.5;
}
&--highlighted-inverted { // same as rule below; `#{&}` is important in this case/
opacity: 1;
}
}
And html markup will be:
<div class="product-rating">
<!-- This `modifier` naming also could be used, -->
<!-- But it will be painful to interact with this `block` by JS without two way binding (react, vue) -->
<div class="rating-star rating-star--highlighted-inverted"></div>
<div class="rating-star rating-star--highlighted-inverted"></div>
<div class="rating-star"></div>
</div>