Recently, I came across a challenge during my work. Our designers had the following idea:
We want to use multiple color sets for things such as poll results or charts. We want to switch the colors based on quantity, so for example, if a chart has six parts, we want to use a smaller color set, but if it has seven or more parts, we want to use the larger color set.
Reasonable request. Given that we use the colors of each color set in order — because we certainly wouldn’t want to go down the path and start assigning orders for colors at each quantity breakpoint, that just sounds wrong — , using a larger color set doesn’t work well visually with smaller amounts of colored entries.
Take a look at this example picture. Using the first five items of the larger color set makes it more difficult for a reader to recognize which data set the color belongs to.
Also, add to that, our team is responsible for the development of multiple sites, some of which share a good amount of code. It is possible that while on one of our publications, the number of items in the color sets are six and ten, but on others, it could be four and nine, with completely different colors. One of our sites has three color sets, with six, ten and sixteen items.
Being the enthusiastic front end developer that I am, I started thinking about ways to implement a CSS-only solution for this problem using Sass.
What I ultimately set out to achieve was to have a selector and simply be able to call a mixin, supply what properties I want “settified” and then lean back and enjoy the system taking care of the rest, automatically. Let’s jump in!
I wanted to keep the data structure easily recognizable and simple. Since we are dealing with sets here, what makes the most sense in my opinion is a Sass list. In fact, I ended up creating a multidimensional list.
The order in which we assign the list items to our main list is important — we go from largest to smallest. I will explain the reason of that later.
After setting up my list — and the lists within my list — I needed to figure out a way of creating selectors. What am I even going for?
Fortunately, our industry consists of great minds and this problem has already been figured out by Heydon Pickering and published in his A List Apart article. I won’t go into much detail here, so I strongly recommend reading that article before you go on so you get an idea of what we are aiming for here.
The idea is simple: by default, we want to style each item based on the colors of the first — the largest — color set, so the first item in our $sets list. In case the number of items in our markup is less or equal than the amount of colors in the next — smaller — color set, we switch the styling to that color set, and so on.
For this logic to work, we need to use the :nth-child or :nth-of-type pseudo selectors. Which one you select is ultimately up to your HTML structure and may change case-by-case, so using an argument to make our mixin capable of switching between the two is recommended.
Example structure and CSS
Let’s see a very simple example with an unordered list and list items, using class names that may remind you of the BEM methodology:
As for the CSS, I’ll separate it into to parts. The first part of the code we want to achieve is where we basically list a bunch of nth selectors and declare the colors according to the largest set, something like this:
And the second part is where the quantity queries kick in:
As I mentioned before, we order our color sets from largest to smallest in our $sets variable and at this point you can probably see why: quantity selectors are significantly longer and take up more bytes than the regular nth selectors.
We could use the nth selectors from 1 to the length of the smallest color set and then build quantity queries from 1 to the length of the larger sets, but that is a lot more output.
In some cases, a performance consideration of this magnitude may be irrelevant, but since the selectors can work either way by turning the logic of Heydon’s post around, we might as well do it in a fashion that puts out less code.
This CSS gives us the effect we want, but it is static and hard to maintain. With each iteration, such as changing colors, adding another color set or adding or removing colors from an existing set, we have to find each instance where we use the set colors, change the colors, add or remove a whole bunch of code or the one we’d always forget: change the expression inside the nth selectors.
Time to create a Sass mixin
Let’s define a mixin! We’ll call it settify and we give it two arguments: a $selector argument, expecting a string of either “child” or “of-type”, and a $properties argument of unknown size, expecting strings of CSS property names.
Our mixin, at first, needs to iterate through our $sets list with a for loop. Remember, the $sets variable is a list itself, containing even more lists. We are using for instead of each for the convenience of the $i variable.
Inside this for loop, we have to examine whether or not we are at the first color set. Remember, the quantity queries only kick in after we establish the base selectors.
In each case, we need to iterate through the given color set. Once again, a for loop.
In the first part of the if statement, inside the loop, our job is fairly simple. All we have to do basically, is generate a bunch of nth selectors, based on the $selector argument of the mixin, and output the necessary properties.
In the second part of the if statement however, we have some more things to do. If you take another look at the second part of our oh-so-wished-upon CSS, the quantity queries all look the same and they use the general sibling combinator, except from the first one. We have to accomodate our else branch for that.
What is inside that else branch is particularly disgusting. In order to achieve our quantity queries, we can’t just simply reference the parent selector after the general sibling combinator. In cases where the selector is nested, that leads to faulty selectors. Instead, we need to take the parent selector apart and use only the last part of it. Lucky for us, Sass handles selectors as list types, so this works, but may not work in all the Sass versions out there.
I created a CodePen demo to showcase the final mixin. Play around with it, try adding more color sets to the $sets list, or adding colors to existing sets or changing the number of items in the HTML structure, see how it works.
You can get the code from CodePen or from the GitHub repository. Feel free to share, use, fork this project.
This method saved us a lot of time during development and simplified a lot of things, but this might not be the case for you. I haven’t made any measurements, but I’d imagine this puts some weight on your Sass compiler and may slow down your build time significantly if used excessively. Either way, it’s fun as hell!
Using this mixin too excessively has the potential to produce a large amount of selectors and bloat your CSS output if you are not careful.
One workaround could be to create reusable selectors, but that leads to a predetermined HTML structure, which can be limiting in some situations.