Andrei Pfeiffer logo
Back to Articles

The evolution of scalable CSSPart 3: CSS Processors

5 min read

The evolution of scalable CSS is a multi-part chronicle intended to document the progress of tools, practices and techniques that enable us to write maintainable CSS at scale.

  1. Introduction
  2. Part 1: CSS scalability issues
  3. Part 2: Good practices
  4. Part 3: CSS Processors
  5. Part 4: Methodologies and Semantics
  6. Part 5: Styles Encapsulation
  7. Part 6: Atomic CSS
  8. Part 7: CSS-in-JS
  9. Part 8: Type-safe CSS
  10. Epilogue

In the previous article, Part 2: Good practices, we've covered several essential techniques that help us write more maintainable CSS. They represent the first tactics used in the fight against scalability problems.

In this part, we'll turn our attention to the first tools that addressed CSS maintainability issues, namely CSS processors. There are two categories, each of them serving different purposes:

  • CSS preprocessors introduce support for contextual styles, removing a lot of the source code duplication.
  • CSS postprocessors optimize the final CSS output while paving the way for future tools that we'll touch upon in later chapters.
NoteThe separation between "pre" and "post" processors is not that clear nowadays because most modern tools perform both pre and post-processing. Therefore it's better to simply call them all CSS processors.

In this article, I'm sticking with the improper de facto naming to distinguish between the two categories of tools because that's how they're colloquially known.

For more details, you can checkout Deconfusing Pre- and Post-processing.
Timeline of scalable CSS evolution, highlighting the main timeline or Semantic CSS (in blue) and pinpoiting the most popular CSS processors: SASS in 2006, LESS in 2009, Stylus in 2010 and PostCSS in 2013
Timeline of the most notable and popular CSS processors

CSS preprocessing

There was a time when CSS variables or CSS math functions like calc() were not implemented natively in browsers. Nowadays, they are considered de-facto standard in front-end development, being well supported for years.

However, before we had full browser support for such useful features, we had to rely on alternative methods. That's when the age of CSS preprocessors began.

Any CSS authoring tool that uses non-standard CSS syntax, allowing developers to write code more comfortably and outputs valid CSS code, could be considered a CSS preprocessor.

  • SASS was the first one, released in 2006;
  • LESS was the second one, released in 2009;
  • Stylus followed along in 2010.

Thinking about it, many CSS preprocessor features are focused on avoiding code duplication. For instance, variables allow us to re-use concrete values, while mixins enable us to re-use entire blocks of CSS rules.

CSS preprocessors also introduced numerous additional features such as interpolation, conditionals, operators, functions, and much more. We won't go into their details, as most of them are outside the scope of this article.

However, there are two particular features that we're interested in, which completely changed the way we author CSS styles even today, namely nesting and parent selector, as they enable contextual styles definition.

Contextual styles

Nesting allows us to define CSS rules for a selector inside another selector, simplifying descendant declarations. In addition, the parent selector enables us to refer to the outer selector during nesting.

Combining these two features, we can easily avoid the selector duplication issue that we've previously covered:

.product_title {}

/* 🤌 verbose duplication with plain CSS */
.product_title:hover {}
.product_title::after {}
/* 👌 single source of truth with SCSS */
.product_title {
  &:hover {}
  &::after {}

Contextual styles provide obvious conveniences during development: we write less code when defining selectors, and we group related styles.

Nevertheless, other obscure benefits also surface during long-term maintenance:

  • renaming selectors is straightforward because they're defined only once;
  • code is easy to understand because all pseudo-classes and elements are colocated;
  • deleting code is trivial because all related elements are grouped together.
Maintainable media queries

Another major benefit of contextual styles is that media queries could also be nested inside their respective selector:

.product_title {}

@media (min-width: 768px) {
  /* 🤌 duplication with plain CSS */
  .product_title {}

@media (min-width: 1280px) {
  /* 🤌 duplication with plain CSS */
  .product_title {}
/* 👌 single source of truth with SCSS */
.product_title {

  @media (min-width: 768px) {}

  @media (min-width: 1280px) {}

With contextual styles, all definitions related to a particular selector are grouped within a single code block. Even though UI components were not a thing back then, CSS preprocessors introduced a syntax that enabled that mindset.

Nowadays, contextual styles are widely used in most CSS-in-JS libraries, either supporting the parent selector & or using some custom syntax that provides the same behavior.

To keep in mindAs of 2021, there's a proposal which introduces native support for CSS Nesting.

We can clearly see the power of the crowd here. Tools created by individuals and used by many can set the status quo and influence the standards. Therefore, CSS preprocessors played a crucial role in the evolution of scalable CSS.

CSS postprocessing

In contrast with preprocessors, postprocessors don't typically add new CSS syntax. Instead, they receive regular CSS as input.

Any tool that transforms valid CSS code, either by improving or optimizing it, can be considered a CSS postprocessor. There used to be numerous CSS postprocessors, but lately, they seem to have converged to a single tool.

PostCSS was released in 2013. It's still maintained today and is by far the most popular tool for CSS postprocessing. PostCSS includes over 300 plugins that we can combine and automate. I'll mention only a couple of popular ones:

  • Autoprefixer automates the process of adding vendor-specific prefixes to the required CSS rules, allowing us to author CSS using the standard syntax.
  • Stylelint is a modern linter that highlights CSS errors and enforces conventions in our styles, including specificity limiters.
Paving the way for CSS Modules

PostCSS is widely used today either as a separate tool or working under the hood for many other libraries and frameworks.

One thing worth mentioning is that PostCSS opened the doors for CSS Modules, which started as a PostCSS plugin experiment. We'll cover CSS Modules in more detail in later chapters, as they played a critical role within the CSS ecosystem.

To recap, CSS processors are essential tools, widely used even today. Moreover, they are cornerstones in the evolution of scalable CSS. Preprocessors introduced contextual styles, while postprocessors provided the foundation for styles encapsulation covered in later chapters.

In the next part of this series, Part 4: Methodologies and Semantics, we'll explore several approaches to organize CSS code, consisting of structured and cohesive sets of rules.

Scroll to top