This One Weird Trick to Make Better Websites

Miles Blackwood's picture

Software Architect


This One Weird Trick to Make Better Websites

Apr 17, 2014

Miles Blackwood


Preprocessor is a dirty word. There, I said it. We all secretly love our abstractions of abstractions, but when it comes down to it, the real question is, which one are you going to use? CoffeeScript? TypeScript? Dart?

Ok, I'll admit, I pulled you in with an Upworthy-worthy headline, and a leading paragraph talking about JavaScript (which is certainly the language du jour), but really this isn't about scripting—this is an intervention—we're going to have a talk, you and I. A talk about style sheets. Now, wait, don't leave yet, I haven't even started! CSS is one of the most important programming languages out there.

That's right, CSS is a programming language. I mean, it's a programming language in the same way that Pluto is a planet--in which a die-hard contingent of fans weep over the fact that it will never get it's deserved status and name in the sun.

We could get all in a twist about semantics here. For example, is CSS Turing Complete (meaning does it fulfill all of the base necessities of a programming language, i.e. being able to simulate another program)? And the answer would be... well, some say yes and some say no, definitely not. But more importantly the question is: How should we expect CSS to behave like a programming language? It doesn't even follow a few simple rules:

  1. Don't repeat yourself.
  2. Don't reinvent the wheel.
  3. Be scalable.

Okay, I'd love to get to all of these in one go, but these are big problems. But this is the gist of what I'm getting at: CSS doesn't do these things by itself. You need a preprocessor in order to get the same sort of magnitude that any other programming language might give you out of the gate for a comparable problem.

What it really comes down to is this: CSS has been put by the wayside while the business logic has been getting sorted. But this is certainly a shame, because as Lea Verou puts it, and I tend to agree: CSS is for developers!

"The skills required to write good CSS code are by and large the same skills required to write good code in general." - Lea Verou

Seeing as how Verou is coming from a background in C, C++, C#, PHP, and JavaScript before even touching CSS, this statement shouldn't be taken for granted.

Lastly, let me touch on this: the CSS spec doesn't move unless we as developers act on it. If you're frustrated by the language, its because the spec writers all haven't been hearing our concerns. Case and point, be witness to this Twitter reply by a CSS evangelist from no other organization than Mozilla on the subject of preprocessor myth.

Well there you go. CSS has to be goaded into being useful--but unfortunately the preprocessors have moved faster and the chips are down: we ought to be choosing efficiency. Here's a couple tricks using SASS from the latest theming I did on Project Aware:

Use Interpolation!

$imagePath: "/sites/all/modules/custom/aware_debris_data_map/images/debris-node";

Above is a relatively simple image path, turned into a variable.

Go ahead. Put it in your .scss file!

.about .table-title:before { background: url(#{$imagePath}/about.png) no-repeat; }
.survey-info .table-title:before { background: url(#{$imagePath}/survey-info.png) no-repeat; }

Compiles to:

.about .table-title:before { background: url(/sites/all/modules/custom/aware_debris_data_map/images/debris-node/about.png) no-repeat; }
.survey-info .table-title:before { background: url(/sites/all/modules/custom/aware_debris_data_map/images/debris-node/survey-info.png) no-repeat; }

Cool, yeah?

Use Nesting!

Ever wished you could easily write the equivalent of an anonymous scope for a particular navbar, widget or area of a site in your CSS? Usually by the time you dig down a couple layers in the DOM tree though, you've either had to add really specific class names to your elements or every rule is a chain of perfectly placed selectors that you've had to test against the browser ten times just to get right.

.survey-team img#diver-image {
  margin-left: -6px;
  margin-right: 10px; }
.survey-team h3.heading, .survey-team .value {
  color: #fff !important; }
.survey-team .heading, .survey-team .label {
  text-transform: uppercase; }

Those selectors are pretty ugly, right? However the code that output it was much cleaner:

 .survey-team {
   img#diver-image {
        margin-left: -6px;
        margin-right: 10px;
   h3.heading, .value {
        color: #fff !important;
  .heading, .label {
        text-transform: uppercase;

Its much more obvious what my intent was here because I don't have to read the same parent selector name more than once just to get that everything is scoped to the parent .survey-team element. Nesting can technically go on for as many parent selectors you have, but a good rule of thumb is to only go three levels deep (...wasn't this the same rule in Inception?).

Use Mixins!

Need to repeat a rule in many places, but pass in a variable argument? Here ya go:

@mixin table-default($color) { 
    background: $color;
    border: {
        left: 1px solid $color; 
        right: 1px solid $color; 

This is actually a wonderful twofold example. I needed the title header of each of several differently-colored tables to have the same width as its relative table body--which had a 1px border in between both of its two cells and on the right sides of those cells--and then emulate the same width on the header by applying a border to the left and right of the same color (a bottom border would cause the inner table-cell borders to bleed into the header).

$color is a variable that gets passed in as an argument to the function; the border: rule is a nice way that SASS separates a single orign rule into separate parts (but stil outputs in semantic border-left and border-right.) --note that the border: parent can still accept CSS rules as well.

Here's how those rules (with arguments) would get passed to two different selectors with two different colors:

.group-debris-materials-plastic .table-title { @include table-default(#D81E2A); }
.group-glass-ceramics .table-title { @include table-default(#263D8F); }

The result:

.group-debris-materials-plastic .table-title {
  background: #d81e2a;
  border-left: 1px solid #d81e2a;
  border-right: 1px solid #d81e2a; 

.group-glass-ceramics .table-title {
  background: #263d8f;
  border-left: 1px solid #263d8f;
  border-right: 1px solid #263d8f; 

There ya go. Two simple examples, with repeatable, readable, maintainable, and most importantly expected results.

CSS may not be your go-to language to write a program, but that doesn't mean we shouldn't expect it to behave intelligently. When you apply these techniques, the next time you have to generate any CSS rule with different browser prefixes, you'll be thanking the preprocessor gods for your good fortune.