SCSS: Harnessing @for and @each to build reusable components

George Treviranus
4 min readJul 2, 2017

--

Scale your CSS frameworks without the cruft of Bootstrap or Foundation using native SCSS functions!

CSS can be annoying to use. By all means, it’s a “designer” language in that it’s very explicit, or WET (Write Everything Twice). With SCSS, you already know it provides some great ways to reduce that clutter with nesting.

What about when you need a more dynamic way of outputting a large number of selectors for global use? @for and @each provide a powerful way to reduce code on the development side, and return identical styles once compiled. This saves tons of time for projects looking to build their own custom components.

If you’re familiar with javascript for and forEach loops, then these should come easy to you. If you’re a designer looking to deepen your programming skills, this is a great way to learn two core programming concepts!

@for

To start, we’ll use this small 10 column grid:

.column-1  { width: 10%; }
.column-2 { width: 20%; }
.column-3 { width: 30%; }
.column-4 { width: 40%; }
.column-5 { width: 50%; }
.column-6 { width: 60%; }
.column-7 { width: 70%; }
.column-8 { width: 80%; }
.column-9 { width: 90%; }
.column-10 { width: 100%; }

The styles are very explicit in traditional CSS. As a designer/front-end developer joining a new project, you would know exactly how to use it.

But what if we instead dynamically generated each of these columns? Luckily, SCSS gives us tools to do just that. Unfortunately, the explicit nature will dwindle as we proceed, but that can easily be made up for with documentation in the component file.

To start our conversion to a @for loop, let's define how many columns we want:

$columns: 10;

Then using this function, we can define the core of our class names, in addition to the number of columns from the new variable:

@for $i from 1 through $columns {
.column-#{$i} { width: percentage($i / $columns); }
}

First, we’re generating a series of classes with a column number $i at the end. Then, we divide $i by $columns to return a percentage width. E.g., when $i = 3, then we get:

.column-3 { width: 30%; }

This continues until all 10 columns have been generated. Cool!

@each

To add on to our example, what if you wanted to add media queries for various breakpoints? Well, you could always write each @for loop within its own media query, then append a size indicator onto the class. Like so:

@media screen and (min-width: 640px) {
@for $i from 1 through $columns {
.column-small-#{$i} { width: percentage($i / $columns); }
}
}

But that is a lot of duplication for something that SHOULD be smarter. So let’s create an @each loop that saves us from the repetition!
Let’s store our breakpoint names and corresponding values in a map. Here’s how that might look:

$breakpoints: (
small: 640px,
medium: 960px,
large: 1200px
);

We store two pieces of information here: a key and a value separated by a colon. We can access both by using two arbitrary variable names, such as $break (key) and $size (value):

@each $break, $size in $breakpoints {
@media screen and (min-width: $size) {
@for $i from 1 through $columns {
.column-#{$break}-#{$i} { width: percentage($i / $columns); }
}
}
}

Like that, we’ve generated 10 columns in three separate breakpoints:

@media screen and (min-width: 640px) {
...
.column-small-5 { width: 50%; }
... etc
}
@media screen and (min-width: 960px) {
...
.column-medium-5 { width: 50%; }
... etc
}
@media screen and (min-width: 1200px) {
...
.column-large-5 { width: 50%; }
... etc
}

With a little extra leg work on standardizing the column structure with needed styles (see how bootstrap does it, for example), we can begin writing columns like so:

<div class="column-small-12 column-medium-6 column-large-3"></div>

You could easily take this a step further by first defining a custom breakpoint mixin and then reusing that in your column generator function. There are lots of possibilities here!

Worth noting is that this approach isn’t exactly the best if you’re only looking to output 5 or less total selectors; in that case, it would be more intuitive to developers if you list each out individually.

Another potential downside to using @each and/or @for is that you inadvertently raise the barrier to entry for front end designers unfamiliar to the programming concepts or syntax. If that's the case, take extra precaution and document thoroughly. If possible, even build a components documentation page for folks who want to learn without digging into the code right away.

Thanks for reading! If you have tips on how to improve this method, if I missed something, or you want to talk about nerdy SCSS things, feel free to tweet at me: @gwtrev

--

--

George Treviranus
George Treviranus

No responses yet