Feeling Sassy Again

Feeling Sassy Again

At 4/19/2024

Official logos for CSS3, Sass and PostCSS

Since 2015, our team has defaulted to PostCSS as our CSS processor of choice. Overall, we like it a lot. But we’ve recently considered re­introducing Sass to our stack.

Our first taste of PostCSS was the Autoprefixer plugin, which we ran after Sass finished compiling. Since we were already limiting our use of nesting, loops, mixins and extends in Sass, we were able to transition fully to PostCSS with just a few more plugins. Our projects felt leaner and more focused, our compile times improved, and we appreciated philosophically that the syntax adopted by plugins was usually based on a future CSS standard.

But a lot can change in four years. Here’s why we’ve reevaluated the value Sass may add to our projects.

Some of the most popular PostCSS plugins are written like polyfills, enabling syntax that is on track to becoming a web standard. In theory, you could remove these plugins once the feature makes it to browsers.

But there’s inherent risk in adopting a syntax from standards that aren’t finalized or implemented. PostCSS users learned this the hard way when it came to color functions.

In 2014, a PostCSS plugin for a color function was written based on the working draft for CSS Color Module Level 4. But as that draft was revised, the function was renamed to color-mod, resulting in the original plugin being deprecated and replaced. By 2016, the function was removed entirely from the editor’s draft for the color specification. This meant that it was also removed from popular PostCSS plugin packs like cssnext and its successor Preset Env, confusing users who attempted to upgrade.

There’s value in the idea of polyfilling future standards. But drafted standards are by nature too volatile to depend on. Sass’s proprietary but comparatively stable color functions would have offered the same results with a lot less maintenance.

Even a finalized syntax may not always be a good candidate for compilation. Variables are a great example. In Sass, they work like this:

$color-default: #111;

body {
  color: $color-default;
}
Code language: SCSS (scss)

Which compiles to this:

body {
  color: #111;
}
Code language: CSS (css)

You could recreate the same thing with CSS custom properties:

:root {
  --color-default: #111;
}

body {
  color: var(--color-default);
}
Code language: CSS (css)

But that’s just the tip of the iceberg when it comes to what CSS custom properties are capable of, as expertly explained by Lea Verou in this excellent talk:

To retain as many of those superpowers as possible, the official PostCSS custom properties plugin adds more CSS than it removes by default:

:root {
  --color-default: #111;
}

body {
  color: #111;
  color: var(--color-default);
}
Code language: CSS (css)

Which is great if you wanted those properties to be accessible. But if your intention was to keep things DRY, it’s probably the opposite of what you’d expect.

Recently, we’ve started adding a Sassy variables plugin to our stack to differentiate variables (which we want to compile) from properties (which we want to expose to the browser so they benefit from the cascade):

/* Compile the project's brand color palette */
$color-blue: #456BD9;
$color-white: #fff;

/* Use brand color as custom property fallback */
a {
  color: var(--link-color, $color-blue);
}

/* Define property in different context using brand color */
.Theme--dark {
  --link-color: $color-white;
}
Code language: SCSS (scss)

While this solves the problem, it also suggests that there may be benefits in differentiating processor features from browser features… another argument in favor of Sass.

“Do one thing, and do it well.” That’s the second of the PostCSS Plugin Guidelines, and it’s served the ecosystem well. The majority of the plugins we use are fast, focused and reliable.

But some features might benefit from an awareness of one another. Mixins, loops, conditionals and variables all output CSS rules based on property values, for example. In cases like this, PostCSS is kind of a mess:

While each of these plugins may work well on their own, they lack the consistency and interoperability of Sass’s feature set.

I can’t in good conscience finish this article without acknowledging the hard work of the Sass community. Its syntax has continued to improve and evolve, it compiles in a fraction of the time it used to, it no longer requires Ruby or C bindings, the documentation is as well-written and approachable as ever, and its community guidelines are excellent.

I think for our next project we’ll probably try using Sass for compiled, proprietary features that require some direct authorship… variables, mixins, loops, etc. Then we’ll use PostCSS for automatic transformations, optimizations and polyfills… Preset Env, FOFT Classes, Autoprefixer, minification, etc.

Maybe we’ll allow both .scss and .css files, with the latter skipping straight to PostCSS when you don’t need any fancy features. Maybe we’ll find the line isn’t super clear and we’re just using the best of both ecosystems. Time will tell, but I’m excited to find out!

What CSS processors (if any) do you use? What do you like about it? Let us know in the comments!

Copyrights

We respect the property rights of others and are always careful not to infringe on their rights, so authors and publishing houses have the right to demand that an article or book download link be removed from the site. If you find an article or book of yours and do not agree to the posting of a download link, or you have a suggestion or complaint, write to us through the Contact Us, or by email at: support@freewsad.com.

More About us