Semantic Remapping

with CSS Pre-Processors


tldr; Use the extend method of your favorite CSS Pre-Processors (i.e. LESS, SASS, Stylus, etc.) to abstract layout patterns and assign those patterns to true semantic markup. The result will be CSS stylesheets whose source is well organized and easy to maintain, while keeping the compiled output very small. All without having to give up your favorite framework.

…you might even get to use a few…


“CSS is hard™”

That’s the common mantra, right? I mean, it rings pretty true. I’ve been building web interfaces for about 10 years now, which is kind of a frightening thing to read as I type it. I’ve studied countless tutorials from yesteryear of how to get a Holy Grail layout, or organize your stylesheet for maximum effectiveness (some random examples: one, two, three); read many many iterations of the arguments over how this strategy or that strategy is the one true savior and all other strategies are false.

These days, we have design systems like SMACSS, ACSS, OOCSS, BEM, or whatever your favorite acronym might be. We have Pre-Processors like LESS, SASS, and Stylus, and the frameworks that sit on top of them like Bootstrap, Foundation, and Pure. While the sheer number of solutions can be a little overwhelming, things have actually gotten a LOT easier.

Now you can take your tried and true tools, include them (or their output) into your <head> tag, start pumping out markup with some well crafted classes, and voila! Your layout is complete.

…and yet somehow, CSS is still deceptively hard.

Mo’ Classes, Mo’ Problems

Once you’ve put your classes in place and you’ve got the basic shell-interface doing what you want, additions and design changes start rolling in. They should all be just like what you’ve done, but just a little different. Just move that box over there at this breakpoint, move this over here… that can’t be hard — right? It took the designer only 5 minutes to do it in Illustrator!

Of course, the reality is that the more you start to veer from the defaults provided by your framework, or create variations on your original implementation, the more refactoring is required to go back and update those early assumptions. The time required to do these refactors is rarely afforded on most projects, forcing your hand towards bad hacks and code-atrocities just to ‘get it done’.

There are other problems too. Lets say you finish your awesome design, your design team is happy, and you’ve successfully run the project gauntlet and kept your code clean...

— First, take a moment to pat yourself on the back with those clean hands. Now that that’s done, if your site is responsive, how many of your framework’s classes make sense at every breakpoint? Depending on the framework you’re using, probably not too many, right? Sure, your grid holds up, but that’s because you’re stuffing different classes in to every element that uses your grid for every breakpoint. Have a ‘pull-left’ or ‘text-right’ class anywhere? Those still getting used on your phone layout?

If you’re using Bootstrap, you’ll probably have to override many of the classes you’re using to define your default breakpoint (which if you’re not developing ‘Mobile First, is probably Desktop). This can sometimes result in minor code atrocities like:

.some-selector.pull-left {
@media (max-width: @screen-sm-max) {
float: none;
}
}

or

.btn.btn-primary.btn-lg.btn-block {
@media (min-width: @screen-md) {
display: inline-block;
width: auto;
}
}

Sometimes, the frameworks are kind enough to give you a list of breakpoint-friendly classes so you’re not just stuck with a single class regardless of your active media-query. Lets take a look at Foundation’s list for text-alignment

Look how helpful Foundation is being!

…is this how you want to live your life?

Remember, this is all to encapsulate the following rule at different media queries:

text-align: [direction] !important;

Now you might think after reading the above, “yeah, but I would never do that,” which is good. But can you be sure no one else on your team will?

Additionally, if you put the class ‘pull-left’ in your markup, you are inviting other developers that work with the same code-base to assign rules to that class selector that have nothing to do with floats.

But lets not just pick on CSS Frameworks. Ask yourself how many times have you seen a #sidebar div displaying at 100% width in phone or tablet views? Wordpress has done this for years. Even the venerable CSS Zen-Garden, where they encourage you to create crazy new and imaginative layout possibilities using the same markup, has .sidebar as a class for it’s otherwise beautifully described “complementary content” container.

What if I want that sidebar’s contents to run full width?

Definitely not a sidebar

Your Tools Are (only slightly) Dark and Full of Terrors

Contrary to how all this may sound, I’m actually a big proponent of frameworks. I think in addition to being a common vernacular for teams to use to talk about developing an interface, they can be great learning tools.

Moreover, these frameworks are not necessarily written with veteran interface developers in mind. A lot of these utility classes are intended for designers who want to prototype an interface or ‘live-wireframe’ without having to get too deep into the code, or developers who live more on the architectural side of the fence and are interested in developing a quick piece of logic rather than the look and feel of a site.

So, it’s not that these tools aren’t doing their job well. Though they tend to get a lot of flak out in the world of developers, I think they’re all pretty great when used thoughtfully! It’s more the job we’re asking them to do that, I think, demands a moment of pause and reflection.

The real underlying problem is that Bootstrap, Pure, Foundation, SMACSS, BEM, ACSS, and whatever boilerplate framework or design system philosophy you can think of, are all similar in the way they encourage the use of utility classes that describe what the outcome looks like, not what it is. As a result, our code becomes completely unsemantic and violates of the Separation of Concerns™ principle.

If you haven’t looked at the definition of Semantic HTML in a while, lets review it together so we’re all on the same page. From Wikipedia:

Semantic HTML is the use of HTML markup to reinforce the semantics, or meaning, of the information in webpages rather than merely to define its presentation or look. […]

In spite of the industry-wide reverence towards Semantic HTML, it has become fairly commonplace to implement markup whose attributes describe the visual outcome. And when you start adding in 7 classes to describe the layout at different breakpoints many of which use !important flags in their corresponding rules, how much hassle have we really saved ourselves from just putting style attributes directly in our markup? Do we really need to use a .hide class or .hidden-[xs/sm/md/lg/etc] when we can just use display: none in a media query?

For the design systems layer (SMACSS, ACSS, etc) it actually makes a good deal of sense to use visual classes. How can Brad Frost, Jonathan Snook, or any visual system’s designer anticipate what your markup will refer to semantically? And more to the point, how can we talk about visual systems without a visual language.

We also can’t expect or really even want the Frameworks leveraging these design systems to anticipate our semantic intentions. But for each Framework to claim that their tool is semantic (which they all do) and then encourage the use of layout classes like .row, or .show-for-small-only seems a little disingenuous, at best.

This is not to say we should never use utility classes and you’re a bad person if you do, but there needs to be a sanity check on our current systems and practices. And even though there are good workflows that involve a site architect or designer quickly prototyping out a site using these classes, there needs to be some process that we as a community identify and mature so that once the prototype is handed off to the front-end team for further development, it is expected that those utility classes will be replaced by some system that is easier to maintain, expand, override, and refactor.

The rest of this article will look at one possible way to do this.


The droids you are looking for

What if I told you there is a way to have our markup stay super clean and just have our semantic markup point to our visually described utility classes?

Salvation lies within our Pre-Processors. Specifically, the combination of the extend method and placeholders.

NOTE: SASS and Stylus have a few extra nuances which will make this article too long, so while I pinky-swear promise I’ll come back to them in another article, I’m going to stick with LESS for the rest of this one as it appears to be the best suited for the Semantic Remapping process.

The use of mixins and the extend method have been viewed as somewhat controversial for heavy usage. Used poorly and most of your extra power and reusability gets quickly eroded by bloat. And while extend tends toward lower output sizes than heavy mixin use, extending little snippets from the cascade will more often than not yield a kind of “Spaghetti-Code for CSS.”

With both strategies we also risk accumulating fragile chains of inheritance where small changes to our mixins or extended classes can cause breakage across the whole project. Unfortunately every example I’ve found on the LESS, SASS, and Stylus websites encourage this kind of piecemeal extending.

What we really want to extend is general, reusable patterns and components. Luckily that’s what CSS Frameworks are made out of!

Lets see what this might look like in practice…

A modified layout from Yahoo Pure’s website

I’ve modified a layout using Yahoo’s ‘Pure’ framework. Here’s a link so you can see it in action. If you look at the complete source code for this layout, in the index.html file you’ll see all the standard utility classes there. I have a few small overrides in the main.css file and I’ve utilized some subtle placeholder image services but otherwise it’s all ‘Pure’.

Here’s a small excerpt of the markup:

<div class="pure-menu pure-menu-open pure-menu-horizontal">
<a href="#" class="pure-menu-heading">Your Logo</a>
<ul>
<li><a href="#">Home</a></li>
<li class="pure-menu-selected"><a href="#">Blag</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>

Pure in particular is one of my favorite frameworks due to it’s diminutive size, default use of flexbox and percentages for their grid, and that they just don’t try to tackle too much. This leaves me less to override, and more freedom to pick and choose different systems and approaches for things like modals, accordions, or what-have-you.

On their homepage they advertise a minified and gzipped footprint of 4.0k. Just minified it’s around 18k which is still nothing to shake a stick at (you stick shaker, you). We also want responsive grids, so adding Pure’s responsive grid stylesheet is another 9.7k minified. Add in my overrides and we’re in at around 30k minified for a nice responsive layout, 5.8k gzipped.

NOTE: I’m using the following tools for minification and gzipping if you want to see where I’m getting these numbers from.

Here is the semantically remapped version and it’s markup in it’s entirety. For this to work, we have to make minor tweaks to the vendor files. First, I’ve renamed Pure’s source CSS partials from the .css extension to .less so the LESS parser can use our classes as mixins.

The page itself should look and behave the same as the original ‘pure-classed’ page when our LESS file compiles out.

This is what our snippet from above looks like now:

<nav>
<a href="#">Your Logo</a>
<ul>
<li><a href="#">Home</a></li>
<li class="active"><a href="#">Contact</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>

Lets compare that again to the original:

<div class="pure-menu pure-menu-open pure-menu-horizontal">
<a href="#" class="pure-menu-heading">Your Logo</a>
<ul>
<li><a href="#">Home</a></li>
<li class="pure-menu-selected"><a href="#">Blag</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>

Notice in our updated snippet all the utility classes are gone. The only non-semantic class left in the entire html file is .active which describes state and not appearance. Elsewhere in the file where new classes have been used, I’ve tried to keep them focused on either what the content is, or in the case of the wrapping containers, what specifically it’s wrapping.

We’ll go over what’s happening in the LESS file that generates our stylesheet in a second, but just to compare while we’ve got numbers on the table, lets take a look at our remapped output’s size. We’ve got a single stylesheet called processed.css which contains all the compiled rules we need from Pure — including the responsive grid rules — as well as my overrides. Yet it weighs in at a svelte 5.5k minified and 1.8k gzipped (!!!).

Pretty sweet, right?

So let’s walk through how I got there (…and if you haven’t been opening up these links to follow along, you should definitely do so to get the complete picture).

If we look at the LESS file, the first thing we see is a bunch of import rules. Here I’m borrowing from Bootstrap’s strategy for assembling their compiled output, but with a twist.

@import (reference) ‘base.less’;
@import (reference) ‘buttons.less’;
@import (reference) ‘grids.less’;
@import (reference) ‘grids-responsive.less’;
@import (reference) ‘buttons.less’;
@import (reference) ‘menus.less’;
@import (reference) ‘forms.less’;
@import (reference) ‘tables.less’;
[...]

If you aren’t familiar with the (reference) option, LESS’s documentation describes it thusly:

Use @import (reference) to import external files, but without adding the imported styles to the compiled output unless referenced.

Another way of describing it for the SASSier among you is (reference) is like using a placeholder for an entire stylesheet or partial. So where I import ‘buttons.less’, when the (reference) option is set, only rules from buttons.less that I specifically reference or extend will show up in my output. This alone, gives us incredible control over our css output, and allows us a sort of a la carte approach towards using CSS libraries.

Once we have that in place, for every class you would otherwise add to the markup to get your desired outcome, you can pretty much extend from the imported LESS partials to get the same effect.

This can help spare us from some tedium for common elements that we might want to dress up. For example, how much easier does this look than having to add class=”pure-button pure-button-primary” to every button element in our markup?

button { 
&:extend(.pure-button all);
&:extend(.pure-button-primary all);
}

If you want an even more compact rule, you can do the following:

button:extend(.pure-button all, .pure-button-primary all) {}

…but for easier reading I’ll stick with the more verbose version for now.

The classes we removed from the navigation excerpt above now live in our LESS like so:

body > nav {
&:extend(.pure-menu all);
&:extend(.pure-menu.pure-menu-horizontal all);
&:extend(.pure-menu-open all);
    > a {
&:extend(.pure-menu .pure-menu-heading all);
&:extend(.pure-menu.pure-menu-horizontal .pure-menu-heading);
&:extend(.pure-menu.pure-menu-horizontal > .pure-menu-heading all);
margin-top: 0;
}
    > ul {
&:extend(.pure-menu.pure-menu-horizontal > ul all);

li {
&:extend(.pure-menu-horizontal li all);
}
        .active a {
&:extend(.pure-menu .pure-menu-selected a);
}
}
}

…and here we’ve set up our grid columns.

.featured-items,
.feature-descriptions {
&:extend(.pure-g all);
max-width: 980px;
margin: 0 auto;
}
.featured-items > div,
.feature-descriptions > div {
&:extend(.pure-u-1 all);
}
.featured-items > div {
&:extend(.pure-u-md-1–3 all);
}

The classes that start with .pure-g reference the grid container, and the classes marked .pure-u-[…] refer to columnar information. The .pure-u-1 class denotes a single-column, full-width block. The .pure-u-md-1–3 class indicates that at the medium breakpoint, this item will display at 1/3rd the width of it’s container.

The end result is that, because we use (reference) to import our dependencies and then extend those imported rules from semantic selectors, our compiled output will tell us how .featured-items behave in our interface at each breakpoint. In doing so we’ve returned our layout and appearance back to the stylesheet leaving our markup unburdened by the clutter and constraints of utility classes, while still getting to quietly utilize them.


Let’s walk through another example, but this time using Bootstrap. Bootstrap is the 800 lb gorilla of the framework jungle, and as it’s minified CSS alone weighs in at ~117k, it handles a LOT of stuff you might need in your project. But let’s be real, most people use a fraction of Bootstrap for their sites. In fact, I’m willing to bet that most of you have come across (or even built; I know I’m guilty) a website that’s pretty much just using Bootstrap’s buttons and grid and shook your head at how much library was being downloading only to use such a small percentage of it’s capability. Lets see what we can do to improve that scenario.

This is what our example will look like when it’s done.

Horizontal Row at md breakpoint and above
Grid at sm breakpoint
Block at xs breakpoint

It’s not going to win any awards, surely, but should illustrate the point.

We’ve already discussed how Bootstrap imports partials into their primary less file and discussed the (reference) option, so that’s where we’re going to start. And like before we’re going to use the (reference) option on every partial. Here’s what that less file looks like.

// Core variables and mixins
@import (reference) "../node_modules/bootstrap/less/variables.less";
@import (reference) "../node_modules/bootstrap/less/mixins.less";
// Reset and dependencies
@import (reference) "../node_modules/bootstrap/less/normalize.less";
@import (reference) "../node_modules/bootstrap/less/print.less";
@import (reference) "../node_modules/bootstrap/less/glyphicons.less";
// Core CSS
@import (reference) "../node_modules/bootstrap/less/scaffolding.less";
@import (reference) "../node_modules/bootstrap/less/type.less";
@import (reference) "../node_modules/bootstrap/less/code.less";
@import (reference) "../node_modules/bootstrap/less/grid.less";
@import (reference) "../node_modules/bootstrap/less/tables.less";
@import (reference) "../node_modules/bootstrap/less/forms.less";
@import (reference) "../node_modules/bootstrap/less/buttons.less";
// Components
@import (reference) "../node_modules/bootstrap/less/component-animations.less";
@import (reference) "../node_modules/bootstrap/less/dropdowns.less";
@import (reference) "../node_modules/bootstrap/less/button-groups.less";
@import (reference) "../node_modules/bootstrap/less/input-groups.less";
@import (reference) "../node_modules/bootstrap/less/navs.less";
@import (reference) "../node_modules/bootstrap/less/navbar.less";
[...and so on...]

The outcome of this is that we now have a version of Bootstrap that we can access a la carte, and we’re going to apply it to this markup:

<div class="outer-wrapper">
<section class="my-content-wrapper">
<div>
<img src="http://placesheen.com/300/300" />
<button>Button</button>
</div>
<div>
<img src="http://fillmurray.com/300/300" />
<button>Button</button>
</div>
<div>
<img src="http://nicenicejpg.com/300/300" />
<button>Button</button>
</div>
<div>
<img src="http://placecage.com/300/300" />
<button>Button</button>
</div>
</section>
</div>

Here’s what our primary LESS file looks like:

@import 'bootstrap';
@import (reference) 'compiled-grid';
* {
box-sizing: border-box;
}
img:extend(img all) {
display: block;
height: auto;
margin: 1em auto;
max-width: 100%;
}
button:extend(.btn, .btn-primary) {}
.outer-wrapper:extend(.container-fluid all) {}
.my-content-wrapper:extend(.row all) {
> div:extend(.col-xs-12 all, .col-sm-6 all, .col-md-3 all) {
text-align: center;
}
}

So first thing to mention about the above code block. This is not an excerpt. This is my whole LESS file. We are referencing Bootstrap’s LESS source files (we don’t have to use the (reference) option in this case as it’s already being ‘referenced’ in our bootstrap.less file), and I pre-compiled out the grid for reasons I’ll get to in a moment, but once we had access to the Bootstrap’s source files as placeholders I just extended what I wanted and we’re done.

I didn’t need their scaffolding or normalize, the tables, modals, and accordions aren’t in the output; only what we needed to build our interface. And because we’re using CSS selectors, we can target large groups of our markup and apply Bootstrap rules to them in a very lean and composable way instead of having to place several classes on every element that we want to dress up.

When the dust settles, the CSS that comes out the other end of our compiler weighs in at 1.2k minified and 928bytes gzipped. Granted it’s not as complete of an interface as our previous example, nor is it a perfect solution (yet), but 1.2k minified is a lot smaller than 117k if all you need are a few pieces of the library.


Considerations and hacks

In our previous example where we were using Pure, everything originally came from plain old CSS so the grid system was all spelled out for us. Simply renaming the file-extensions to .less and importing them with the (reference) option gave us clean access to whatever grid classes we wanted to extend. In Bootstrap’s LESS files the grid is generated from mixins (if you are unsure of what I mean, look in the mixins folder at grid.less and grid-framework.less), so those classes don’t really exist yet and therefore can’t be extended without using those mixins somewhere first.

In theory, we should be able to manually generate the columnar definitions to our own utility classes using the .make-[size]-column mixins and then extend our custom utility class, but for reasons which are not immediately clear to me, that doesn’t work.

While I am still actively looking for a way around this (please don’t be shy if you know of one), at this point our quickest way forward is to set up a different less file which imports only the variables, mixins, and grid partials. When compiled, you’ll have a new CSS file which contains grid classes that reads much like Pure’s grid partial, or like what is traditionally found in Bootstrap’s compiled css file.

@import (reference) "../node_modules/bootstrap/less/variables.less";
@import (reference) "../node_modules/bootstrap/less/mixins.less";
@import (reference) "../node_modules/bootstrap/less/grid.less";

Rename the file-extension to .less, @import the newly compiled file with the (reference) option, and voila! We have a la carte Bootstrap grids.

A few extra steps, but all in all, a small price to pay for what I think is a much cleaner option.

If you don’t need to tap into Bootstrap’s mixins or variables system, you could also just rename the non-minified version of Bootstrap’s CSS file to .less, and import it using the (reference) option as we’ve done with the other partials, and skip the more explicit importing rules. Choose your own adventure.

(Update: turns out you don’t need to rename the file to use ‘.less’ at all. Instead modifying the statement to `@import (reference, less) filename.css` will treat all file types imported as less files)

For the buttons, I just did a simple extend on .btn and .btn-primary for all buttons, but if you wanted to do a more complete button system, you could conceivably use .btn-primary for form button selectors and then create a ‘call-to-action’ class that would extend .btn.btn-success, and .btn-block.

I didn’t use the ‘all’ option to extend every iteration of the .btn or .btn-primary classes, mostly because I was getting the effect I wanted without doing so, so why add to the file size? But if you find you aren’t able to inherit a property you want without using the ‘all’ option, feel free. Let human judgment prevail.


I hope you found these examples useful. There’s a lot more that can be done with the ‘Semantic Remapping’ concept than it makes sense to try and fit here, and we’ve already covered a lot. Over the next few weeks and months, I will go into some other aspects and nuances of this strategy; in particular, usage with SASS/SCSS and Stylus, usage without frameworks, nesting extends to create web-component-y layout abstractions, and using Semantic Remapping as a methodology for graceful degradation, progressive enhancement, mixing and matching components from different frameworks, and generally turning the cascade of CSS-water into layout-wine.

In the meantime, I have to give a quick shout-out. I had the great pleasure of working out several of these concepts in collaboration with a Mr. Corbin Swagerty, whose contributions I would be remiss in not mentioning. All of what you read above came out of months of tinkering, testing, optimizing, and expanding on a few core ideas that we hammered out together at my previous place of employment while trying to figure out how to implement new pages based on Bootstrap 3 to a large enterprise site without breaking it’s already existing 100 or so pages based on Bootstrap 2. We should probably cover how we did that in a future article too.

Lastly, special thanks to Jim, Chris, and again Corbin for proof-reading and providing feedback.