Response to "Things to Avoid When Writing CSS"

This post is in response to Heydon's articles "Things to Avoid When Writing CSS" and "Things to Avoid When Writing CSS (Part 2)".

They're both well written articles that make some excellent points. If you haven't read them yet none of this post will make sense, so go read them now. I'll wait.

Ok, a few notes before we get started. I use LESS, so I'm going to reference LESS, and use examples in LESS. I make no claims as to whether LESS or Sass or Stylus or your-preprocessor-of-the-week is better or worse, it's just the tool I happen to prefer. The points I make using LESS hold for every preprocessor I've used.

I also use BEM syntax, but specifically I use the early variant described in "MindBEMding - getting your head 'round BEM syntax". Again, I make no claims as to whether BEM or OOCSS or SMACSS or your-modular-CSS-organization-technique-of-the-week is better or worse, it's just the tool I happen to prefer. The points I make using BEM hold for every organizational technique I've used.

Multiple Files

For each discrete bit of javascript functionality or each HTML partial I might make a dedicated file, then organize related files into folders named javascript and html or controllers and templates.

This doesn’t work so well for CSS.

I strongly disagree with this sentiment.

Javascript function definitions can go either before or after where they are called and HTML modules can be inserted wherever you see fit in the flow of the document.

Splitting up CSS is similar to splitting up JS, and here Heydon even acknowledges this similarity. What's also similar for modularization is dependencies. Consider the following JavaScript:

<script src="jquery.js"></script>
<script>
    $(function () {
        $('.hello').click(function () {
            console.log('Hello World!');
        });
    });
</script>

It would be reasonable to organize this script into two separate files:

<script src="jquery.js"></script>
<script src="hello-world.js"></script>

It would be reasonable to use a tool like browserify to merge them into a single file:

//script.js
var $ = require('jquery');
$(function () {
    $('.hello').click(function () {
        console.log('Hello World!');
    });
});

But it is unreasonable to expect the original script to work in the reverse order:

<script>
    $(function () {
        $('.hello').click(function () {
            console.log('Hello World!');
        });
    });
</script>
<script src="jquery.js"></script>

The same is true of CSS.

CSS, on the other hand, is a chronology. It matters a great deal in what order you declare styles.

In JS it also matters what order functions are called in.

Heeding the language features of inheritance and specificity, you should start with generic styles (like a font-family set on body) and progress to more specific definitions.

I agree with this

CSS is an ordered, exception based language and there is no easy way for a file listing (which is typically organised alphabetically) to represent it coherently.

I disagree with this. The trick to organizing multiple CSS files is similar to organizing multiple JS files. I recommend using a build tool to reduce HTTP requests, but at the end of the day the organization is the same whether organized via a series of <link> elements, or a series of @import statements in a LESS file.

In JavaScript, base dependencies must exist before any functionality that depends on them can execute. The same is true for CSS. The difficulty is in determining what the base dependencies are. These should generally be element selectors and resets.

Identifying the separate files is generally pretty simple. reset.css or normalize.css is an industry standard at this point, if you're using NPM you can even leave it in node_modules. Core styles depend on the reset, so my root LESS files often start with:

@import (inline) 'normalize.css/normalize.css';
@import 'core.less';

So you have two choices: You can either live in denial, grumbling that “specificity shouldn’t be part of CSS” as you try to force a square peg into a round hole, or you can work in one, well-commented file that clearly represents the arc of your inheritance-harnessing cascade.

This is a false dichotomy. Multiple stylesheets works well when dependencies are clear.

Continuing my example from above. When you take the modular approach to CSS by splitting it into multiple files, you also need to make sure to obey the same rules as you would in other programming languages. Specifically I'm referring to SOLID. The first being SRP, each CSS file must represent a single module. No more, no less.

If you've got two components, widget and gizmo, you should separate them into separate files.

@import (inline) 'normalize.css/normalize.css';
@import 'core.less';

//components
@import 'components/widget.less';
@import 'components/gizmo.less';

In widget.less you should expect to see some code along the lines of:

.widget {
    ...
}

and in gizmo.less you should expect to see some code along the lines of:

.gizmo {
    ...
}

These selectors have the same specificity. The order that you @import them in doesn't matter.

Of course, the immediate counter-argument that comes to mind is:

But what if I want a gizmo that is a widget?

To which the answer is: don't ever do that.

If you want every gizmo to be a widget, then you extend widget:

//here the dependency is now made clear
@import (reference) 'widget.less';
.gizmo {
    .widget;
}

Otherwise, if you want an item that inherits from both gizmo and widget in OOP you need to make a new class:

@import (reference) 'widget.less';
@import (reference) 'gizmo.less';
.gadget {
    //here the order is solidified
    .widget;
    .gizmo;
}

Summary: You shouldn’t organise CSS by splitting it into separate files any more than you should organise a pane of glass by dropping it on a concrete floor.

anchor in stained glass

"Anchor in Stained Glass" photo by Steve Snodgrass

Sometimes you can make beautiful things by using smaller component parts.

Nesting (with Sass)

Other features, like the ability to nest declarations infinitely, aren’t so hot.

I agree that infinite nesting is a terrible idea. I disagree that it makes nesting a bad feature.

Nesting in general is great. However, nesting for descendant selectors is overused.

Let someone who writes crappy CSS (that could be anyone, including me at times) have nesting and you’ve licensed them to branch out into a second crappy dimension. Great! Now the pandemonium spans from top to bottom and left to right.

This reeks of mismanagement. It's a bad idea to assign a front-end developer to make changes to a database. It's likewise a bad idea to assign a server-side developer to make CSS changes.

What do you call a "full stack developer" who can't write CSS?
A server-side developer.

Most "full stack" developers are not.

I've seen the same levels of crazy descendant selectors from projects using raw CSS as projects using a preprocessor. The takeaway here is to not let someone who writes crappy CSS touch the CSS. Best solution is usually to train them on how it's done and lead by example.

Considering it’s a point of professional pride to avoid nested structures as much as possible in other languages, it seems rather silly to bring this capability to a language which doesn’t need it.

I agree that we should always look to flatten arrow code, but the way this is phrased makes it sound like the problem is with nesting, rather than the overuse of nesting. When devs nest too many if..else blocks, the first reaction should not be to tell them not to use if..else.

To keep specificity low, you generally want to avoid descendant selectors. As nesting typically turns into descendant selectors, you want to avoid nesting if you don't need it. It's most useful to nest when you use the & operator:

.widget {
    ...

    &:hover,
    &:focus {
        ...
    }
}

With BEM syntax this can be used to easily chain elements from blocks:

.widget {
    ...

    &__header {
        ...
    }
    &__body {
        ...
    }
}

Just like with if..else, loops, switch, and anything else that introduces a new block in C-style languages, a few levels of nesting are useful. More than that and the code quickly becomes hard to follow, which makes it hard to reason about, which makes it more prone to errors and bugs.

Pixel units

I mostly agree with this one. Avoid px if you are able and it makes sense.

The catch tends to be if you're not in control of the designs, and there's no clarity on what relative units should be used. I've seen many clients ask for significant amounts of pixel pushing where it's just easier to leave things as px than try to deal with converting to em. This argument falls flat as soon as you start using rem. If you can, use rem for everything.

tl;dr: Avoid px units.

Device breakpoints

I agree with this one.

Photography

I disagree that photography should be avoided in the general sense. I agree that photography should be avoided when it's not part of the main content on the page.

The internet is a series of tubes, and those tubes are full of cats. Say yes to cat photos.

Giving anything a set height ever

Set heights are great for icons, images that must span a certain number of lines, and content previews that make good use of text-overflow: ellipsis. Otherwise, use flexbox for those god-awful multi-column marketing CTA module flavor text thingers so that content can flex properly (and have room to breathe).

Calling it "semantic"

I don't disagree with Heydon on this one exactly, but I would like to add some additional points that I think are important:

The [class] attribute doesn't add system-level meaning to any element. <div class="rose"> is equivalent to <div class="tulip"> as far as the browser is concerned. Anyone who believes that adding classes to markup makes it "semantic" is mistaken.

For example:

<div class="button">lorem ipsum</div>

Is not semantically equivalent to:

<button type="button">lorem ipsum</button>

If you want to add non-standard semantics (browser-level meaning) to an element, you need to use the role attribute and follow the WAI-ARIA spec.

<div role="button">lorem ipsum</div>

Is semantically equivalent to:

<button type="button">lorem ipsum</button>

Making it just about the code

If you make it possible to work on the appearance of different parts of a product in isolation, how do you maintain a conceptual relationship between those parts?

A living styleguide is a good resource for any project. More importantly, the tricks tend to be to build pieces so that they fit in whatever container they're placed in. I'm anxiously awaiting container queries as it will help modularize CSS even more.

A good styleguide acts as a catalogue of building blocks that are available to be used within the site. You can quickly look through everything that's available to see which pieces you already have for building new features. It also helps the onboarding process when bringing additional devs into a project so that they can visualize each piece and have an understanding of how it works and what it does.

If it’s desirable to “modularize” your styling so that one part of the interface does not influence another, is this a symptom that you’re creating a fragmented or complex UX?

Books are broken into chapters to make them easier to digest. The complexity of the story is not necessarily related to the number of chapters in it. CSS that is modularized should acknowledge dependencies and separate pieces so that they're easy to understand and maintain. Sometimes it means making a LESS file that has a single selector in it. This isn't really any different from creating a separate file for an Enum in C#.

Is the UX actually perfectly seamless and consistent...?

Seamless UX depends more on the designs of the component parts than the quantity. I've seen sites go both ways, where a monolithic CSS file has fifteen different shades of blue, and where a modularized collection of LESS files all call @import (reference) 'variables.less' to depend on the same core set of brand-approved colors and sizes.

Modularization also helps reduce effort for new features if they're reusing designs. When modularization is done right, new features are built using component parts that were implemented for older features. Then, to implement the new feature only the pieces that are genuinely new need to be created and documented, which can amount to significant time savings.

if so, isn’t this a rather piecemeal and inefficient way to construct it?

If you find that you're inefficient, it's probably from duplication. If you're suffering from duplication it's probably from either a lack of documentation (multiple devs reinventing the wheel) or a lack of proper inheritance (not using mixins or &:extend).

Is your team really working on seven different products at the same time but you’re the only one who’s realised this and everyone else seems to be on drugs?

Good communication skills are vital for the success of any team. If I wrote a monolithic CSS file, it's possible that another dev will come in and reinvent the wheel somewhere else because the file is so long that no one person can possibly understand it all at once. If I wrote modularized CSS files, it's possible that another dev will come in and reinvent the wheel somewhere else because the files are so numerous that no one person can possibly understand it all at once. The issues are the same, because the problem isn't the number of files.

It's certainly important to simplify. It's important to push back on designers when they're introducing a third or fourth variant of a feature that will only ever be used twice. Encourage people to look at what's already in place and throw out anything that's no longer relevant. This is much easier to do when you've modularized your LESS files because deleting a file will give you convenient error messages as to which other files had a dependency you'd forgotten about.

I think the way we choose to manage our code buttresses a toxic design culture, persisting a dysfunctional and disconnected way of working.

There is a tendency for code changes to be additive. In CSS this is especially true. Developers avoid removing CSS because they're not quite sure what they might be breaking that they can't see. With modular CSS you can make it so that chunks of CSS can be added and removed on a whim. When you know that no more widget components exist on the site, you can delete @import 'components/widget.less' and the entire widget.less file (there's always version control to bring it back) and know for certain that you won't be breaking any other modules because of the dependency hierarchy. If there is breakage after such a change, you can find the developer responsible for those side-effects and retrain/chastise them.

Summary: We should invest in the maintainability of our code, unless it’s not worth maintaining.

Focus on team maintenance first, and your team will be an asset in maintaining code.