Illustration by

We Write CSS Like We Did in the 90s, and Yes, It’s Silly

As web developers, we marvel at technology. We enjoy the many tools that help with our work: multipurpose editors, frameworks, libraries, polyfills and shims, content management systems, preprocessors, build and deployment tools, development consoles, production monitors—the list goes on.

Article Continues Below

Our delight in these tools is so strong that no one questions whether a small website actually requires any of them. Tool obesity is the new WYSIWYG—the web developers who can’t do without their frameworks and preprocessors are no better than our peers from the 1990s who couldn’t do without FrontPage or Dreamweaver. It is true that these tools have improved our lives as developers in many ways. At the same time, they have perhaps also prevented us from improving our basic skills.

I want to talk about one of those skills: the craft of writing CSS. Not of using CSS preprocessors or postprocessors, but of writing CSS itself. Why? Because CSS is second in importance only to HTML in web development, and because no one needs processors to build a site or app.

Most of all, I want to talk about this because when it comes to writing CSS, it often seems that we have learned nothing since the 1990s. We still write CSS the natural way, with no advances in sorting declarations or selectors and no improvements in writing DRY CSS.

Instead, many developers argue fiercely about each of these topics. Others simply dig in their heels and refuse to change. And a third cohort protests even the discussion of these topics.

I don’t care that developers do this. But I do care about our craft. And I care that we, as a profession, are ignoring simple ways to improve our work.

Let’s talk about this more after the code break.

Here’s unsorted, unoptimized CSS from Amazon in 2003.

.serif {
  font-family: times, serif;
  font-size: small;
}

.sans {
  font-family: verdana, arial, helvetica, sans-serif;
  font-size: small;
}

.small {
  font-family: verdana, arial, helvetica, sans-serif;
  font-size: x-small;
}

.h1 {
  font-family: verdana, arial, helvetica, sans-serif;
  color: #CC6600;
  font-size: small;
}

.h3color {
  font-family: verdana, arial, helvetica, sans-serif;
  color: #CC6600;
  font-size: x-small;
}

.tiny {
  font-family: verdana, arial, helvetica, sans-serif;
  font-size: xx-small;
}

.listprice {
  font-family: arial, verdana, sans-serif;
  text-decoration: line-through;
  font-size: x-small;
}

.price {
  font-family: verdana, arial, helvetica, sans-serif;
  color: #990000;
  font-size: x-small;
}

.attention {
  background-color: #FFFFD5;
}

And here’s CSS from contemporary Amazon:

.a-box {
  display: block;
  border-radius: 4px;
  border: 1px #ddd solid;
  background-color: #fff;
}

.a-box .a-box-inner {
  border-radius: 4px;
  position: relative;
  padding: 14px 18px;
}

.a-box-thumbnail {
  display: inline-block;
}

.a-box-thumbnail .a-box-inner {
  padding: 0 !important;
}

.a-box-thumbnail .a-box-inner img {
  border-radius: 4px;
}

.a-box-title {
  overflow: hidden;
}

.a-box-title .a-box-inner {
  overflow: hidden;
  padding: 12px 18px 11px;
  background: #f0f0f0;
}

Just as in 2003, the CSS is unsorted and unoptimized. Did we learn anything over the past 15 years? Is this really the best CSS we can write?

Let’s look at three areas where I believe we can easily improve the way we do our work: declaration sorting, selector sorting, and declaration repetition.

Declaration sorting#section2

The 90s web developer, if he or she wrote CSS, wrote CSS as it occurred to them. Without sense or order—with no direction whatsoever. The same was true of last decade’s developer. The same is true of today’s developer, whether novice or expert.

.foo {
  font: arial, sans-serif;
  background: #abc;
  margin: 1em;
  text-align: center;
  letter-spacing: 1px;
  -x-yaddayadda: yes;
}

The only difference between now and then: today’s expert developer uses eight variables, because “that’s how you do it” (even with one-pagers) and because at some point in their life they may need them. In twenty-something years of web development we have somehow not managed to make our CSS consistent and easier to work on by establishing the (or even a) common sense standard to sort declarations.

(If this sounds harsh, it’s because it’s true. Developers condemn selectors, shorthands, !important, and other useful aspects of CSS rather than concede that they don’t even know how to sort their declarations.)

In reality, the issue is dead simple: Declarations should be sorted alphabetically. Period.

Why?

For one, sorting makes collaborating easier.

Untrained developers can do it. Non-English speakers (such as this author) can do it. I wouldn’t be surprised to learn that even houseplants can do it.

For another reason, alphabetical sorting can be automated. What’s that? Yes, one can use or write little scripts (such as CSS Declaration Sorter) to sort declarations.

Given the ease of sorting, and its benefits, the current state of affairs borders on the ridiculous, making it tempting to ignore our peers who don’t sort declarations, and to ban from our lives those who argue that it’s easier—or even logical—not to sort alphabetically but instead to sort based on 1) box dimensions, 2) colors, 3) grid- or flexbox-iness, 4) mood, 5) what they ate for breakfast, or some equally random basis.

With this issue settled (if somewhat provocatively), on to our second problem from the 90s.

Selector sorting#section3

The situation concerning selectors is quite similar. Almost since 1994, developers have written selectors and rules as they occurred to them. Perhaps they’ve moved them around (“Oh, that belongs with the nav”). Perhaps they’ve refactored their style sheets (“Oh, strange that site styles appear amidst notification styles”). But standardizing the order—no.

Let’s take a step back and assume that order does matter, not just for aesthetics as one might think, but for collaboration. As an example, think of the letters below as selectors. Which list would be easiest to work with?

c, b · a · a, b · c, d · d, c, a · e · a

c · b · a, b · a · c, d · a, c, d · a · e

a, b · a, c, d · a · b, c · c, d · e

The fact that one selector (a) was a duplicate that only got discovered and merged in the last row perhaps gives away my preference. But then, if you wanted to add d, e to the list, wouldn’t the order of the third row make placing the new selector easier than placing it in either of the first two rows?

This example gets at the two issues caused by not sorting selectors:

  • No one knows where to add new selectors, creating a black hole in the workflow.
  • There’s a higher chance of both selector repetition and duplication of rules with the same selectors.

Both problems get compounded in larger projects and larger teams. Both problems have haunted us since the 90s. Both problems get fixed by standardizing—through coding guidelines—how selectors should be ordered.

The answer in this case is not as trivial as sorting alphabetically (although we could play with the idea—the cognitive ease of alphabetical selector sorting may make it worth trying). But we can take a path similar to how the HTML spec roughly groups elements, so that we first define sections, and then grouping elements, text elements, etc. (That’s also the approach of at least one draft, the author’s.)

The point is that ideal selector sorting doesn’t just occur naturally and automatically. We can benefit from putting more thought into this problem.

Declaration repetition#section4

Our third hangover from the 90s is that there is and has always been an insane amount of repetition in our style sheets. According to one analysis of more than 200 websites, a median of 66% of all declarations are redundant, and the repetition rate goes as high as 92%—meaning that, in this study at least, the typical website uses each declaration at least three times and some up to ten times.

As shown by a list of some sample sites I compiled, declaration repetition has indeed been bad from the start and has even increased slightly over the years.

Yes, there are reasons for repetition: notably for different target media (we may repeat ourselves for screen, print, or different viewport sizes) and, occasionally, for the cascade. That is why a repetition rate of 10–20% seems to be acceptable. But the degree of repetition we observe right now is not acceptable—it’s an unoptimized mess that goes mostly unnoticed.

What’s the solution here? One possibility is to use declarations just once. We’ve seen with a sample optimization of Yandex’s large-scale site that this can lead to slightly more unwieldy style sheets, but we also know that in many other cases it does make them smaller and more compact.

This approach of using declarations just once has at least three benefits:

  • It reduces repetition to a more acceptable amount.
  • It reduces the pseudo need for variables.
  • Excluding outliers like Yandex, it reduces file size and payload (10–20% according to my own experience—we looked at the effects years ago at Google).

No matter what practice we as a field come up with—whether to use declarations just once or follow a different path—the current level of “natural repetition” we face on sample websites is too high. We shouldn’t need to remind ourselves not to repeat ourselves if we repeat code up to nine times, and it’s getting outright pathetic—again excuse the strong language—if then we’re also the ones to scream for constants and variables and other features only because we’ve never stopped to question this 90s-style coding.

The unnatural, more modern way of writing CSS#section5

Targeting these three areas would help us move to a more modern way of writing style sheets, one that has a straightforward but powerful way to sort declarations, includes a plan for ordering selectors, and minimizes declaration repetition.

In this article, we’ve outlined some options for us to adhere to this more modern way:

  • Sort declarations alphabetically.
  • Use an existing order system or standardize and follow a new selector order system.
  • Try to use declarations just once.
  • Get assistance through tools.

And yet there’s still great potential to improve in all of these areas. The potential, then, is what we should close with. While I’ve emphasized our “no changes since the 90s” way of writing CSS, and stressed the need for robust practices, we need more proposals, studies, and conversations around what practices are most beneficial. Beneficial in terms of writing better, more consistent CSS, but also in terms of balancing our sense of craft (our mastery of our profession) with a high degree of efficiency (automating when it’s appropriate). Striving to achieve this balance will help ensure that developers twenty years from now won’t have to write rants about hangovers from the 2010s.

25 Reader Comments

  1. Well, I think you’ve managed to convince me to alphabetise my declarations. I like logical groupings cuz I’m a logical guy, but I’ve yet to create a set of groupings for CSS that’s consistent enough for me to remember. Maybe it’s time to give up 😀

    And I agree on principle that using declarations multiple times all over the place is ugly and un-DRY. Of course, DRY CSS would be better; it creates fewer maintenance problems.

    But I’ve got two problems with super-granular CSS like ACSS and Tachyons. The first is that, every time I want to add a CTA button to a page, I don’t want to have to remember… “was that last one a <button class="padding-1-em rounded-0.5-em font-size-large"> or was it a <button class="padding-0.75-em rounded-0.25-em font-size-large">?” In this case I’m just pushing the duplication (and the maintenance hassles) from one place to another. This is where I think a CSS preprocessor can come in handy — your atomic classes get mixed into logical/’semantic’ classes, and it’s the latter that get used in your markup. Duplication in the outputted code, but DRYness and composability in your source files.

    The second case where the atomic approach is troublesome is when you don’t control the markup. I’m thinking of the weary masses of WordPress, Drupal, Magento, and Joomla developers out there who are stuck with CMS-created classnames, or plugin-created markup that you can’t even create your own replacement templates for. They don’t even have the option to use atomic classnames in their markup.

    I had a similar situation on a project from a few years ago. We had one codebase but built and deployed websites for many clients, each with their own themes. There was so much good stuff (UI patterns, accessibility, semantic embellishments) in our templates that to duplicate and modify as needed for each client would’ve been a maintenance nightmare. And let’s not forget that templates are usually tightly coupled to domain models, and what happens when you refactor the domain model? In this case, the only sane thing I could think of was to think up good logical classnames for all the elements.

    In both of these latter cases, there’s plenty of room to be atomic and DRY in your Sass/Less source files (which is what we did), and accept a bit of duplication in the compiled CSS. (That stuff gets gzipped anyway.)

  2. Though I do appreciate the simplicity of alphabetical, alphabetical makes it difficult to understand at first glance what is being done. Width and height go together. Separating bottom and right is an obstruction to efficiency. I prefer to group by relationship. Positioning, Display/Box Model, Typography, Colors, … CSScomb can help for teams.

  3. Paul has it right, pushing style into HTML tags is not the answer. It’s a huge regression infact. There have been decades of work toward the ideal of having zero styling in HTML.

    sherrybcreative, I get your point that alphabetising CSS can make it difficult to understand what the CSS is doing. I’d argue that nobody knows what the hell CSS is doing, just from reading it. CSS is *not designed* to be understandable by humans. CSS is designed purely and specifically for browsers to interpret in some reasonably efficient way. The cascade is difficult to interpret from a straight reading, even for simple sites.

    Alphabetising is therefore not adding any extra complexity to the understanding of CSS, but it is adding some ease of maintenance and development.

  4. The author seems wary of CSS pre-processors, yet they help solve many of the issues he describes.

    The two features of pre-processors such as SASS / LESS that are most relevant to this discussion are the ability to compile multiple SASS / LESS files into single CSS sheets, and the ability to nest selectors. Both are strong organizational tools.

    With respect to the first, developers can organize styles into separate files for things like reset, base, specific components, etc. This is superior to taking monolithic raw CSS files and using something arbitrary like alphabetizing to determine where selectors should go. It also makes it easier to kill off legacy markup. For example, consider a codebase with a “foo” component that corresponds to a foo.scss file. If the component is deprecated you simply delete the foo.scss file!

    With respect to nested selectors, they reduce the chances that developers will reuse the same selectors. Consider a group of selectors like .foo {}, .foo ul {}, and .foo ul li {}. In pure CSS those could end up being sprinkled in anywhere leading to duplicate declarations. For example, we might have a .foo ul {} in the middle of the CSS file and then another .foo ul {} down at the bottom. The likelihood of this kind of mistake increases as CSS files get bigger and bigger.

    In SASS (or LESS) .foo{} would be a top-level selector in a file. Things like .foo ul {} and .foo ul li {} would be nested within .foo {}. It’s not completely idiot-proof, but it’s much less likely that one will have duplicate top-level .foo {} selectors in a small nested file — and these files generally ARE small because they are not whole CSS sheets; they are modular pieces that compile to whole CSS sheets.

    With respect to alphabetizing declarations, it’s not so important to me that every selector’s styles are declared in the exact same order. What is important to me is that things like display, position, and width are placed at the top of a selector’s styles because those are generally the most important values I need to ascertain when tweaking something. I don’t want to have to wade through a dozen declarations to find out that an element was absolutely positioned.

    In terms of big-picture methodology, SMACSS, BEM, and OOCSS are a couple of good mental models for how to write your styles. I’m not going to argue which is best. People have their preferences. But reading up on any of those will surely lead to writing better CSS than just winging it.

    My final piece of advice is to avoid overly specific use of selectors. The more specific you make your selectors, the harder they are to override. This is one of the things that leads to developers using !important, which should be avoided at all costs! Overly complex selectors can also slow down paint time. I find that this is especially important to enforce when people use SASS or LESS. Just because you CAN write nested selectors with SASS / LESS does not mean that everything SHOULD be nested. It’s quite easy to go into auto-pilot mode and just keep nesting and nesting when you write SASS. My rule-of-thumb is to never nest more than four levels.

  5. This is the one I have the biggest problem with

    > Try to use declarations just once.

    There is no reason for DRY to be goal in itself. DRY is a tool, to achieve some real goal, like smaller file sizes, better maintainability, etc.
    But I don’t see any real benefits to use it just for the sake of it.

    And your listed benefits are not benefits at all:

    > 1. It reduces repetition to a more acceptable amount.

    This is simply not a statement of a benefit. It would be if repetition was an absolute evil by itself, but it is not. DRY is not a gospel. It is, again, a tool, which is applicable somewhere, and not applicable in other places.
    And even when it is – it is only to a point.
    And yes, redundancy is not the same as repetition. You seem to conflate these two in your reasoning a bit, maybe even intentionally since redundancy has nothing to do with repetition.

    > 2. It reduces the pseudo need for variables.

    The need for variables being ‘pseudo’ is pretty outlier position that you had to link to your own reasoning about it. Which in turn relies back on this. Color me surprised.
    But in any way it is not a benefit for any person, who does not need native css variables.
    And even if I need native css variables, there are better ways around it than mangling whole css codebase. And not even to solve the need, just to reduce it. Wow.

    > 3. Excluding outliers like Yandex, it reduces file size and payload (10–20% according to my own experience—we looked at the effects years ago at Google).

    Link is broken, and I don’t believe that gzip wont solve every repetition problem that you might have.
    1% of savings sounds more believable. What a benefit.
    And Yandex example proves you wrong, but you are quick to declare it as an outlier.

    > but we also know that in many other cases it does make them smaller and more compact.

    Where are these cases? The only case you show proves you wrong.

    So you haven’t listed any real benefits for fighting this windmill, besides very suspicious 10-20% savings, for which the link is broken, and excuse me if I don’t believe you on your words on that after all of this.
    Nothing in your approach indicates any potential increase in maintainability. But is screams about sacrificing maintainability to the god of DRY for the sake of it.

    No, thanks.

  6. I completely agree with you, we need to get organised!
    I’ve been around for those 20 years you’re talking about and that’s true, I still see people coding in the order it comes to their mind. Nice for them but hard to read for others or their later self.
    But thanks to this great idea of the alphabetical order, I can now see large declarations where the “colour” is just beside the clearing, “text-transform” sits with “top”, and “font-size” chats with “floats”.
    You understand that I completely and strongly disagree on the choice or the alphabetical order.
    The alphabetical order has one and only advantage: everybody knows/understands/can reproduce it. But it has absolutely no meaning. Alphabetical ordering is the choice when you precisely have no idea how to order things.
    Yes, then everybody can find what he’s looking for, but that’s about it. It doesn’t help developing, it doesn’t help understanding what you’re trying to achieve, and if you have a bit of logic when you build something, you end up writing a line, jumping 2 lines above to add something, go back at the end for a new line, change something in the first line…
    Practical example:
    “position: absolute” will surely be followed in your mind with something for left and top. Do you have to jump around your declaration to put them in the alphabetical order? I don’t think you should, it should be one single movement.
    When you deal with the appearance of your text, why would “position” be between font- and text- declarations?

    My personal choice is what I would call the box model (what I guess you threw in the bin with the breakfast thing): going from the outside of the element, how it sits in the layout, regarding the context, then the element itself, then the content of the element, and finally what may happen because of the element. More precisely:
    1) How the element will behave in the layout (display, position => top/left/z, floating/clearing…),
    2) From the outside to the inside of the element (margins, borders, dimensions, background, padding),
    3) Text (it’s the content of the element) (text-, word-, font-, letter-, etc.),
    4) All the rest: content, cursor, animations, etc.

    This way, when I try to achieve a positioning, everything gets together, one thought, one global coding. And when I come back on it, everything that is in relation sits together, you don’t have to parse the whole declaration to see if there is not a float somewhere of if you forgot the positioning.

    I do use the alphabetical order as well, between “similar” declarations like font-size, font-style, font-weight as they apply to the same kind of things and I see no logical precedence in any of them.

    There is no need to remind a perfect order of declarations to achieve this ordering, the understanding of what you do and how it applies is enough. From the outside to the inside. Period.

  7. Thanks, everyone! I’ve been curious whether you and other peers would agree on our situation here; I like that we share some of the concerns. For criticism, there are good points. I want to take a bit more time to read through everything so to give a more detailed response.

  8. I agree with mcbenny on the sorting idea. We’ve been using the box-model at my company and through the aid of a linter don’t actually have to remember what that exact ordering is. Neat side effect is how quickly you start to just write in that order without the linter having to correct you. This has another benefit for juniors who come aboard. Just enforcing that order teaches them about the box model. This grouping also helps identify parts that can be abstracted. If you’re always redeclaring the same the set over and over, you’re going to notice it right since it’s always in the same order!

  9. Lots of people are speaking out against the alphabetical ordering because it seems unnatural. When you get used to it, it feels totally natural, and it’s much easier to find things. I’ve been doing this since 2009 and it’s made my code much easier to read—and when my team did the same, it made things easier for all of us. I don’t think a lot of people are just haphazardly throwing their properties in there—most people have their own system—but no two systems look alike. A standard organizational system like the alphabetical one is great because everyone will code it the same way and there’s no guesswork on what goes where. A lot of people are scared to try it, but as someone who has done it for years and had a team who did the same, it really works.

  10. Hmm.. I don’t write like the 90’s.

    I use ITCSS, BEM, smaller and organized files, I order alphabetically… repeat when I think I should and rewrite to abstract something before things get ugly.

    Tachions and others are good ideas and have my respect.
    But still, push to HTML the definition of a button it is just not right.

    You have to define the frickin’ button somewhere.
    You have to define its appearance somewhere. As far as I remember, CSS is used to do that.

    With so many classes, you are defining it in HTML. Please, it is no use to think “But the [group of] classes are defined in CSS” because padding alone won’t define the button.

    As Paul d’Aoust said: what was like, the last button I used?

    I agree with Mikhail about the lack of examples that worked… more info, better debate.

    Thank you for writing.

  11. yes its true, but i think we used 90s css type its because to many people has been learned this css type, and doesnt feel need more change.

  12. Thanks for this the blog post.It’s true about the points you mentioned about CSS.

  13. Sorting solely alphabetically is naive.

    Sort selectors by likely order of appearance in the page. If you’re creating a general purpose style sheet, group selectors by function and use commenting.

    Group declarations by effect, sort by how they’re customarily ordered. For example, `height` and `width` go together and `width` comes first.

  14. This article is funny. CSS preprocessors are a godsend. Who sells a client on the idea of being able to edit the CSS themselves? This style of old-school stylesheet composure is anti page speed and brought the standards up to about the early 2000’s – not 2018.

    Atomic design principals like ITCSS combined with standardized naming conventions like BEM make your style declarations 1) logical, 2) readable, and 3) maintainable by any new developer (or a regular person who can google “BEM”).

    Modularly componentizing CSS is a real thing now. Make your own framework. Go lightweight if you want or get comprehensive and creative.

    You’re right, you don’t need to use a pre-processor to make a web page, but if you can then why wouldn’t you?

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career