Semantic Grid Class Naming With Placeholder Selectors in Sass 3.2
CSS frameworks are known for taking a club to the idea of semantic HTML. This is such a problem that developers everywhere have entirely sworn off the idea of grid-based frameworks solely on the principle that it leads to non-semantic class names.
With Sass, it becomes quite easy to work around this problem and use a predefined grid without resorting to wonky class names. In fact, the latest version of Sass (3.2) has a new feature that makes this task easier than ever. Read on to see what it is and how it works.
The Ultimate Designer Toolkit: 2 Million+ Assets
Envato gives you unlimited access to 19+ million pro design resources, themes, templates, photos, graphics and more. Everything you'll ever need in your design resource toolkit.
The Problem
Personally, I love setting up and using grids in CSS. I’m a nerdy, analytical kind of guy so layout and all if its intricacies (annoyances) are literally my favorite thing about CSS.
Admittedly though, I totally see your point if you’ve sworn off grids. Here’s an example of the problem, as illustrated by the Twitter Bootstrap grid:
<div class="row"> <div class="span4">...</div> <div class="span8">...</div> </div>
Ideally, your class names would be restricted to something that tells you something about the flow of the page. You might have a “featured” section or maybe a “copyright” class. These class names tell you what they’re for while not being so specific that they describe the nature of how the element is being styled. Using this method, the aesthetics of the design could completely change with a redesign, but the class names could stay the same.
With Bootstrap and other grids though, you’re forced to insert all these grid class names that actually communicate and define your layout very specifically right in your HTML. Semantic or not, I think grids are super fun and quite easy to pick up, but ultimately we are using classes in a different way than what’s really intended.
Wouldn’t it be nice if we could have the best of both worlds? Grid frameworks are extremely easy to implement, but semantic coding is arguably the better way to go for the industry as a whole. It’s a classic battle between individual need vs. the greater good.
Sass @extend
When we bring Sass into the picture, we really can have our cake and eat it too. The reason behind this is the entire point of Sass. Behind the scenes, you get to break the rules and do all kinds of crazy stuff because ultimately, the output is going to be vanilla CSS.
The Sass feature that really saves the day here is called @extend, and it’s hands down the best Sass feature (it’s also one of the biggest reasons that many LESS users give in and switch to Sass). Sass @extend is a simple feature with major implications.
To appreciate how @extend works, let’s jump right into an example that I coded up for a previous tutorial:
.comment { background-color: #eee; border: 1px solid #bbb; color: #4e4e4e; float: right; font: 20px/30px Georgia, serif; margin: 20px 0 20px 20px; padding: 20px; width: 250px; } .reply { @extend .comment; background-color: #ccc; font-style: italic; }
Here we have two classes that essentially need to share most of their styles. Basically, the “reply” class is exactly like the “comment” class, only with italic text and a different background color. To pull this off, we simply use an @extend statement within our “reply” class, which points to our “comment” class.
This tells Sass that when the CSS is output, all of the styles from “comment” should also be applied to “reply.” The best part here is that Sass is smart enough to output this in a way that eliminates redundancy:
.comment, .reply { background-color: #eee; border: 1px solid #bbb; color: #4e4e4e; float: right; font: 20px/30px Georgia, serif; margin: 20px 0 20px 20px; padding: 20px; width: 250px; } .reply { background-color: #ccc; font-style: italic; }
I know what you’re thinking, why the heck didn’t we just code this way in the first place? We absolutely could. For many though, the Sass way is a little bit more linear and easier to set up on the fly.
Extending Grid Classes
Now that we know what @extend does, let’s see how we can apply it to some grid CSS. Let’s take a look at some grid code loosely based on Chris Coyier’s Don’t Overthink It Grids.
[class*='col-'] { float: left; padding-right: 20px; } .grid [class*='col-']:last-of-type { padding-right: 0; } .col-2-3 { width: 66.66%; } .col-1-3 { width: 33.33%; }
This code is beautifully concise and very easy to implement, but it does require that we apply non-semantic class names in our CSS (.col-1-2, etc.). If we rethink these ideas with @extend, we might come up with something like the following:
/*Classes That Only Exist to Extend*/ .col-2-3 { width: 66.66%; } .col-1-3 { width: 33.33%; } .column { float: left; padding-right: 20px; } .lastcolumn { @extend .column; padding-right: 0; } /*Actual Grid Code*/ .features { @extend .column; @extend .col-2-3; } .clients { @extend .lastcolumn; @extend .col-1-3; }
Trouble in Paradise
As you can see in the example above, @extend isn’t quite the hero that we want it to be in this situation. Our Sass version may yield semantic class names for our HTML, but our final CSS is going to be filled with bloat.
.col-2-3, .features { width: 66.66%; } .col-1-3, .clients { width: 33.33%; } .column, .lastcolumn, .clients, .features { float: left; padding-right: 20px; } .lastcolumn, .clients { padding-right: 0; }
The code that Sass spits out, though more concise, is still filled with our various unused class names such as “column” and “.col-1-3”. We really just wanted to set these up as templates to @extend and don’t need or want them in our final CSS.
Placeholder Selectors in Sass 3.2
This is where Sass 3.2 comes in. There’s a new feature called “placeholder selectors” that will solve our little problem nicely. The good folks behind Sass realized that coders frequently set up a sort of “base class” that only existed for the sake of an @extend and created placeholder selectors as a way to encourage this behavior without the unfortunate side effect of an unused class in the final CSS.
In the example below, we can see how this works. This time I inserted both the Sass input and the CSS output right next to each other so you can easily see the transformation. Read the comments to see which is which.
/*SCSS Input*/ %col-2-3 { width: 66.66%; } .features { @extend %col-2-3; } /*CSS Output*/ .features { width: 66.66%; }
As you can see, by using a percent symbol in place of a period, we were able to define “col-2-3” as a placeholder selector that can be extended but won’t be carried over into our final output.
Using Placeholder Selectors in Our Grid
Let’s implement these on a larger scale with all of the code that we were working with before. Here’s what this might look like:
/*Placeholder Selectors*/ %col-2-3 { width: 66.66%; } %col-1-3 { width: 33.33%; } %column { float: left; padding-right: 20px; } %lastcolumn { @extend %column; padding-right: 0; } /*Grid CSS*/ .features { @extend %column; @extend %col-2-3; } .clients { @extend %lastcolumn; @extend %col-1-3; }
As you can see, now most of our classes are actually just placeholder selectors that won’t get output to our final CSS. This allows us to set up really simple and easy to implement class names in our Sass, while sticking with semantic output! Finally, the best of both worlds!
Bad Form Sass
Not so fast, why didn’t I show the output from the code above? Because, in theory, it should be awesome. If everything went smoothly, the output would be this:
.features { width: 66.66%; } .clients, .features { float: left; padding-right: 20px; } .clients { width: 33.33%; padding-right: 0; }
Unfortunately, Sass has a little bit weirdness when it comes to using multiple extends. Here’s the actual output:
.features { width: 66.66%; } .clients { width: 33.33%; } .clients, .features { float: left; padding-right: 20px; } .clients { padding-right: 0; }
If you’re astute, you’ve already noticed the redundant selectors on lines four and eleven. Because the two extends actually extend two different placeholder selectors, Sass treats them as separate items in the output, even though they could easily be combined.
As you can imagine, if your entire site was built on the premise of multiple extends and placeholder selectors, then the redundancies would likely be plentiful in the final output.
Because of how cascade and inheritance work, I can see that this would be a really tricky problem to code around, so I’ll cut the Sass guys a break. Still, I hope this is something that is addressed in the future.
What Do You Think?
Sass @extend holds tons of promise for drastically improving your efficiency with CSS. It’s also a really handy tool if you’re trying to set up a semantic grid system, especially when combined with the new placeholder selector feature from Sass 3.2.
Unfortunately, as we saw above, there are downsides to using this technique as well. Do you think the pros are worth the cons? Can you think of a better way to pull this off? I want to see it, so leave me a comment.