BEM-compliant responsive grid with SCSS and include-media

K-Majick @ Cyberleaf Studio
3 min readMay 1, 2023

--

Responsive design is one of the basics of modern web development. Chances are, one of your first attempts ever to make a responsive website was by pulling in some third-party grid from a UI library like Bootstrap or Foundation. But then you found out about BEM and got really frustrated, because mixing Bootstrap classes with other naming conventions looks really ugly…

For me it was the moment when I decided to have my own, reusable and BEM friendly solution. So let me tell you how I did it using flex-box and some SCSS functions combined with the popular include-media library to make the responsive behavior of the grid easily configurable.

What is the include-media library?

Let’s begin by explaining what the include-media is. According to its author it is a library for writing CSS media queries in an easy and maintainable way, using a natural and simplistic syntax. And that’s precisely what it does!

You start using it by defining a variable holding the list of breakpoints:

$breakpoints: (
l: 1366px,
m: 1080px,
s: 768px,
);

Then, instead of making a regular media query, you just start using the @include media() mixin to set the responsive threshold where the desired property should apply:

@include media(">l", ">=m") {
width: auto;
}

Biggest advantage — by defining your breakpoints in one place and making use of comparison operators you gain an easy to use, yet very explicit and precise tool that seriously diminishes chances of making a mistake.

Making the grid container

Now that we have all the tools, we need to define a grid container. Let’s do it by using the flex box and setting the flex-wrap property to ensure the responsive behavior of the grid item:

.grid {
display: flex;
flex-wrap: wrap;
margin-left: -15px;
margin-right: -15px;
}

We will also add a negative margin on both sides to compensate for grid items’ own padding.

Grid items

Just like a classic Bootstrap-style grid, our grid will be divided into 12 columns. So each column would take 1/12 of the 100% of available container space. To specify the number of columns for a given grid item we will use a BEM modifier.

In order to do so, let’s calculate the width of each of 12 cases by using the SCSS loop:

@for $i from 1 through 12 {
.grid__item--#{$i} {
padding: 0 15px;
width: calc(100% / 12 * $i);
}
}

Ok, so now we can make an item which for example takes 4 out of 12 columns, we would just give it a class name like .grid__item–4.

Add responsiveness by extending BEM modifier

All right, we now have a basic, working and BEM compliant grid. Now let’s add responsive behavior. Our goal is to extend the BEM modifier by an additional parameter, which will describe the number of item columns according to a given screen width, for example .grid__item–4@m

For that we will make use of our include-media breakpoints. We need to cycle through their name-value pairs using the @each function and define a media mixin for each breakpoint:

@each $name, $value in $breakpoints {
@include media("<=#{$name}") {

}
}

Then, inside the mixin we add what we already did before for each column, but this time extended by the breakpoint parameter:

@each $name, $value in $breakpoints {
@include media("<=#{$name}") {
@for $i from 1 through 12 {
.grid__item--#{$i}\@#{$name} {
width: calc(100% / 12 * $i);
}
}
}
}

And voila, that’s it! For clarity, let’s see the example HTML markup with 3-column grid, wrapping to a single column layout below a smallest breakpoint:

<div class="grid">
<div class="grid__item grid__item--6 grid__item--12@m">
Left column
</div>
<div class="grid__item grid__item--6 grid__item--12@m">
Middle column
</div>
<div class="grid__item grid__item--6 grid__item--12@m">
Right column
</div>
</div>

Going further

By sticking to the BEM convention our piece can be easily extended with extra features. For example you could add an extra modifier for columns with centered text:

.grid {
&__item {
&--center {
text-align: center;
}
}
}

Or specify some additional styling (an extra margin) for one of the column widths inside the loop:

@for $i from 1 through 12 {
@if $i == 12 {
.grid__item--#{$i}\@#{$name} + .grid__item--#{$i}\@#{$name} {
margin-top: 30px;
}
}
}

--

--

K-Majick @ Cyberleaf Studio
0 Followers

Full Stack Web Developer | JavaScript, Vue.js / Nuxt.js, Node.js / Express.js | https://cyberleaf.pl