Designing Between Ranges

Avatar of Robin Rendle
Robin Rendle on

CSS is written slowly, line by line, and with each passing moment, we limit the space of what’s possible for our design system. Take this example:

body {
  font-family: 'Proxima-Nova', sans-serif;
  color: #333;
}

With just a couple of lines of CSS we’ve set the defaults for our entire codebase. Most elements (like paragraphs, lists and plain ol’ divs) will inherit these instructions. But what we rarely think about when we write CSS is that from here on out we’ll have to continuously override these rules if we want another typeface or color. And it’s those overrides that cause a lot of issues in terms of maintenance and scalability. And it’s those issues that cost us heartbreak, time and money.

In the example above that’s probably not an issue but I think in general we don’t recognize the true strength and dangers of the cascade and what it means to our design systems; most folks think that the cascade is designed to let us overwrite previous instructions but I would warn against that. Every time we override a style that is inherited we are making our codebase vastly more complex. How many hours have you spent inspecting an element and scrolled through each rule in order to understand why an element looks the way it does or how the long chain of inheritance has messed up your styles completely?

I think the reason for this is because we often don’t set the proper default styles up for our elements. And when we want to change the style of an element, instead of taking the time to question and refactor those default styles, we simply override them – making matters worse.

So I’ve been thinking a lot about how we ought to make a codebase that incentivizes us all to write better code and how to design our CSS so that it doesn’t encourage other people to make hacky classes to override things.

One of my favorite posts on this subject was written earlier this month by Brandon Smith where he describes the ways in which CSS can become so complicated (emphasis mine):

…never be more explicit than you need to be. Web pages are responsive by default. Writing good CSS means leveraging that fact instead of overriding it. Use percentages or viewport units instead of a media query if possible. Use min-width instead of width where you can. Think in terms of rules, in terms of what you really mean to say, instead of just adding properties until things look right. Try to get a feel for how the browser resolves layout and sizing, and make your changes and additions on top of that judiciously. Work with CSS, instead of against it.

One of Brandon’s suggestions is to avoid using the width property altogether and to stick to max-width or min-width instead. Throughout that post he reminds us that whatever we’re building should sit between ranges, between one of two extremes. So here’s an example of a class with a bad default style:

.button {
  width: 300px;
}

Is that button likely to be that width always, everywhere? On mobile? In every media query? In every state? I highly doubt it and in fact, I believe that this class is just the sort that’s begging to be overwritten with yet another class:

.button-thats-just-a-bit-bigger {
  width: 310px;
}

That’s a silly example but I’ve seen code like this in a lot of teams – and it’s not because the person was writing bad code but because the system encouraged them to write bad code. Everyone is on a deadline for shipping a product and most folks don’t have the time or the inclination to dive into a large codebase and refactor those core default styles I mentioned. It’s easier to just keep writing CSS because it solves all of their problems immediately.

Along these lines, Jeremy Keith wrote about this issue just the other day:

Unlike a programming language that requires knowledge of loops, variables, and other concepts, CSS is pretty easy to pick up. Maybe it’s because of this that it has gained the reputation of being simple. It is simple in the sense of “not complex”, but that doesn’t mean it’s easy. Mistaking “simple” for “easy” will only lead to heartache.

I think that’s what’s happened with some programmers coming to CSS for the first time. They’ve heard it’s simple, so they assume it’s easy. But then when they try to use it, it doesn’t work. It must be the fault of the language because they know that they are smart, and this is supposed to be easy. So they blame the language. They say it’s broken. And so they try to “fix” it by making it conform to a more programmatic way of thinking.

I can’t help but think that they would be less frustrated if they would accept that CSS is not easy. Simple, yes, but not easy.

The reason why CSS is simple is because of the cascade, but it’s not as easy as we might first think because of the cascade, too. It’s those default styles that filter down into everything that is the biggest strength and greatest weakness of the language. And I don’t think JavaScript-based solutions will help with that as much as everyone argues to the contrary.

So what’s the solution? I think that by designing in a range, thinking between states and looking at each and every line of CSS as a suggestion instead of an absolute, unbreakable law is the first step forward. What’s the step after that? Container queries. Container queries. Container queries.