tadhg.com
tadhg.com
 

LESS CSS

21:32 Sun 26 May 2013
[, , , , , , ]

LESS is the name of a stylesheet language that “compiles” to CSS. It’s been around for a while, as have similar projects such as Sass. I finally decided to start using it today, prompted by fairly common issues with CSS repetition. Even though the project I used LESS for is quite small—a little under 1,000 lines of CSS—it made an immediate difference.

I generally prefer Python libraries since that’s the language I use most, but decided to try LESS because it has a client-side option, where you include JavaScript that converts the LESS file into CSS[1]. I grabbed that, started investigating the syntax, and liked it—it feels very much like CSS, and not like a new technology.

Really it feels like features that should be in CSS already. The critical one for me was “mixins”, which are like the ability to add a CSS class to a selector from within the selector’s definition. This drastically reduces repetition, and that in itself makes things vastly easier. My use case should provide a good example.

Working on a page where I had awkward access to the source but total control over the CSS[2], I wanted to apply a modest set of rules to control table display. I had to use nth-child selectors to apply these rules instead of putting identifiers or classes on the tables in the HTML. Here’s the traditional CSS way of doing it:

table {
  float: left;
  margin-right: .7em;
  border: 1px solid black;
  border-collapse: collapse;
  margin-bottom: 5px;
}

table td,
table th {
  border-bottom: 1px solid black;
  border-right: 1px solid black;
  padding: 0;
  padding-right: .2em;
  padding-bottom: .1em;
  padding-left: .2em;
}

table th {
  white-space: nowrap;
}

table td:first-child {
  white-space: nowrap !important;
}

table td:last-child {
  border-right: none;
}

table:nth-child(12) tr:nth-child(2n) {
  background-color: #CCCCCC;
}

table:nth-child(14) tr:nth-child(2n) {
  background-color: #CCCCCC;
}

table:nth-child(16) {
  font-size: .8em;
  width: 48%;
  border-collapse: collapse;
}

table:nth-child(16) tr:nth-child(2n) {
  background-color: #CCCCCC;
}

table:nth-child(16) td,
table:nth-child(16) th {
  padding: 1px;
  border: 1px solid black;
}

table:nth-child(16) th {
  padding: 1px;
}

That doesn’t look that bad, and is actually fewer lines than the LESS that I replaced it with… but I only got it down to so few lines after seeing the LESS output. Prior to that it was a confused mishmash that emerged from the ongoing CSS tinkering process—which is how I suspect most CSS ends up. Converting it to LESS made it much more comprehensible to me and made it far easier to see where the repetition was ocurring.

With the LESS version, I was able to first set up definitions for what types of styles I wanted to use. First, a table with specific border rules and a first column that didn’t have word wrap:

.borderedtable { //table
    float: left;
    margin-right: .7em;
    border: 1px solid black;
    border-collapse: collapse;
    margin-bottom: 5px;

    td, th {
        border-bottom: 1px solid black;
        border-right: 1px solid black;
        padding: 0;
        padding-right: .2em;
        padding-bottom: .1em;
        padding-left: .2em;
    }

    th {
        white-space: nowrap;
    }

    td:first-child {
        white-space: nowrap !important;
    }

    td:last-child {
        border-right: none;
    }
}

Second, alternating shaded rows:

.shadedrows { // table
    tr:nth-child(2n) {
        background-color: #CCCCCC;
    }
}

And then a shaded table with a set width and some different padding:

.skilltable { //table
    .shadedrows;
    font-size: .8em;
    width: 48%;
    border-collapse: collapse;

    td, th {
        padding: 1px;
        border: 1px solid black;
    }
}

Note that in the last one, I can give it alternating shaded rows simply by adding in .shadedrows;. While I could achieve the same thing in standard CSS by adding , .skilltable to the line the .shadedrows selector is on, it takes more effort for me to think about it that way—I want to define the style for an element in a single place, rather than to divide a style into different chunks and then apply those chunks to elements.

Having defined those styles, I could then apply them to the target tables:

table:nth-child(12)  {
    .shadedrows;
}

table:nth-child(14)  {
    .shadedrows;
}

table:nth-child(16)  {
    .skilltable;
}

table {
    .borderedtable;
}

After many years of unhappily dealing with CSS, writing the above code was a relief. I took to the LESS approach immediately, and found it far easier to experiment and to change approaches. LESS makes it extremely easy to refactor CSS, and that’s one of the things it’s been missing for me.

Once that flexibility was present, it felt fun to play with the CSS again, and I didn’t have that fear that moving something would break the style that I’d finally gotten to work. Since LESS is technically an additional abstraction layer, it theoretically makes things more fragile, but that’s not how it feels.

It’s much easier for me to see the repetition, and what’s being applied where, when using LESS syntax than when using traditional CSS syntax, and that’s despite my having used CSS since it was first supported versus my having used LESS for the first time this afternoon.

LESS also support variables, something that it’s amazing CSS still doesn’t have[3], and mixins that accept arguments, and pattern-matching, making mixins essentially like functions.

It also supports nesting, as seen in my first example; instead of:

div {
    border: 1px solid black;
}

div p {
    margin-bottom: 1em;
}

div dl {
    border-top: 1px dotted black;
}

div dl dt {
    font-weight: bold;
}

You can write:

div {
    border: 1px solid black;

    p {
        margin-bottom: 1em;
    }

    dl {
        border-top: 1px dotted black;

        dt {
            font-weight: bold;
        }
    }
}

Again, that makes more sense to me organizationally, and while in that small example might not look better, when you get to large definitions it really helps. It also encourages grouping related definitions together—inside the parent element’s definition—while it takes considerable discipline with standard CSS to prevent related definitions ending up far apart in the code.

It also explicitly supports namespacing using the nesting approach.

LESS has plenty of other functionality that I haven’t explored yet, including functions for mathematical operations and for color handling, operations on numbers and colors, and import support.

I highly recommend it. I’m using the official JavaScript version (a Node.js package), but a variety of parsers/compilers are supported, and it was trivial to install.

[1] This didn’t end up working for me, because I like to develop against local files and it ran afoul of JavaScript security restrictions, so I installed the Node.js module for it.

[2] In this case, working against the HTML output of reStructuredText, where I didn’t want to put a lot of .. class:: whatever lines into the reST documents themselves, which are supposed to be for human reading/editing.

[3] Although I’m quite sympathetic to browser makers not wanting to turn CSS into a Turing-complete language, and having now worked with LESS I’m sympathetic to the notion that such manipulation should happen via third-party modules that developers use rather than being built into the browser.

Leave a Reply