SCSS and BEM
Summary
Tips, rules and suggestions for writing better CSS / SCSS using BEM methodology + how to use breakpoints with vanilla CSS / SCSS.
BEM
- methodology that helps to create reusable components and code sharing in front-end development
- acronym for Block Element Modifier
Naming
- Block
- standalone entity that is meaningful on its own
- furthest (most outer) parent element of component
- example:
card
- Element
- a part of a block that has no standalone meaning and is semantically tied to its block
- inside of the component may be one or more children called elements
- example:
card__button
- Modifier
- a flag on a block or element
- used to change appearance or behavior
- example:
card__button--active
If all three are used in a class name it would look something like this:
block__element--modifier
SCSS
Below is example of React SideDrawer component content with class names based on BEM methodology.
// SideDrawer.tsx
<div className={`side-drawer ${isOpened ? "side-drawer--active" : "side-drawer--hidden"}`}>
<ul className="side-drawer__menu">
{sideDrawerLinks.map((sideDrawerLink) => (
<li className="side-drawer__item" key={sideDrawerLink}>
<a href="#" className="side-drawer__link">
{sideDrawerLink}
</a>
</li>
))}
</ul>
</div>
// sideDrawer.scss
.side-drawer {
background-color: green;
width: 25rem;
height: calc(100vh - 6rem); // 6rem = navbar height
padding: 2rem;
position: fixed;
z-index: 2;
&--hidden {
display: none;
}
&--active {
display: flex;
}
&__item {
margin-bottom: 1rem;
}
}
If we have an example where parent element (token-journey-segment
) has grandchildren that has long class name (token-journey-segment__list-item
):
<div className="token-journey-segment">
<ul className="token-journey-segment__list">
<li className="token-journey-segment__list-item">I am a list item</li>
<li className="token-journey-segment__list-item">I am a list item</li>
<li className="token-journey-segment__list-item">I am a list item</li>
<li className="token-journey-segment__list-item">I am a list item</li>
</ul>
</div>
Don't nest it in a scss file like this:
.token-journey-segment {
&__list {
&-item {
}
}
}
Instead, do this:
.token-journey-segment {
&__list {
}
&__list-item {
}
}
That way we have a leaner structure that is easier to manage, it's more readable and it will be more scalable in complex projects.
Breakpoints
We can use breakpoints.ts file for writing our variables and mixin directives for showing content based on the width of the screen. In this example we are using pixels
, but em
units would be better choice for managing breakpoints.
// breakpoints.scss file
$screen-xs: 340px;
$screen-sm: 600px;
$screen-md: 960px;
$screen-lg: 1280px;
$screen-xl: 1920px;
// Extra small devices
@mixin xs {
@media (max-width: #{$screen-xs}) {
@content;
}
}
// Small devices
@mixin sm {
@media (max-width: #{$screen-sm}) {
@content;
}
}
// Medium devices
@mixin md {
@media (max-width: #{$screen-md}) {
@content;
}
}
// Large devices
@mixin lg {
@media (max-width: #{$screen-lg}) {
@content;
}
}
// Extra large devices
@mixin xl {
@media (max-width: #{$screen-xl}) {
@content;
}
}
Depending on the agreement within the team we can either use max-width
or min-width
media query for showing/hiding elements. If we use max-width
in combination with display: none;
- it means that all elements above, for example small screen, would not be displayed. In contrast, if we use min-width
in combination with display: none;
- it means that all elements below, for example small screen, would not be displayed.
// hide element above or below small screen, based on using max-width or min-width
@include sm {
display: none;
}
Tips, rules & suggestions
- always have in mind: BLOCK is the most outer parent HTML element of component that consists of children (ELEMENTS) and both of those can be MODIFIED
- try to avoid having more than one child element in a class name (don't do
card__content__item
for example) - don't use modifier class by itself - it is intended to add variations of the block/element, not to be used as a base class
- try not to have nested blocks - difficult to maintain and read - BUT - if you find yourself in a situation where you have complex components with multiple blocks that have long (multi) class names - add another block to avoid having word sausage class names (but keep in mind to nest those blocks class names in the right way in css/scss to avoid clash of styles and use of
!important
) - If you find yourself in a situation where your elements are more than 3 levels deep, you might need to reconsider the structure of the block, potentially even breaking the block into smaller chunks
- BEM Naming Cheat Sheet by 9elements
- BEM by Example: Best Practices for BEM CSS Naming