Skip to main content Accessibility Feedback

Is it time to drop Sass?

I’ve been using Sass for a decade, but recently, I’ve been wondering if it’s time to ditch it and move on.

Let’s dig in!

Why use Sass?

Sass is a preprocessor for extension of CSS. What drew me to it originally were all of the features it had that native CSS lacked out-of-the-box.

With Sass, I could define variables once and reuse them through my code…

// Colors
$color-primary: #007ab8;
$color-white: #ffffff;
$color-black: #272727;
$color-gray-light: #e5e5e5;

// Font Stacks
$font-primary: "PT Sans", sans-serif;

// ...

body {
	background: $color-white;
	border-top: 0.5em solid $color-gray-light;
	color: $color-black;
	font-family: $font-primary;
	font-size: $font-size;
	line-height: 1.5;
}

a {
	color: $color-primary;
	text-decoration-skip-ink: auto;
	word-wrap: break-word;
}

With Sass, I could nest properties that belonged together…

a {
	color: $color-primary;
	text-decoration-skip-ink: auto;
	word-wrap: break-word;

	&:active,
	&:focus,
	&:hover {
		color: $color-primary-dark;
	}
}

And it would render them out properly, like this…

a {
	color: #007ab8;
	text-decoration-skip-ink: auto;
	word-wrap: break-word;
}

a:active,
a:focus,
a:hover {
	color: #00476c;
}

Sass could import files and compile them into a single file…

@import "config";
@import "components/reset";
@import "components/grid";
@import "components/typography";

It could do math!

But in the last few years, much of what Sass provided can be handled natively with CSS. And much of what can’t can be handled by a lightweight compiler using native CSS.

Vanilla CSS can do a lot!

Variables have been in CSS for years!

That Sass example I shared earlier? In vanilla CSS, I can do this…

:root {
	--color-primary: #007ab8;
	--color-white: #ffffff;
	--color-black: #272727;
	--color-gray-light: #e5e5e5;

	--font-primary: "PT Sans", sans-serif;
}


/* ... */

body {
	background: var(--color-white);
	border-top: 0.5em solid var(--color-gray-light);
	color: var(--color-black);
	font-family: var(--font-primary);
	font-size: $font-size;
	line-height: 1.5;
}

a {
	color: var(--color-primary);
	text-decoration-skip-ink: auto;
	word-wrap: break-word;
}

Vanilla CSS has had math in it for years, too, thanks to the calc() function. Yes, a function. In CSS!

It’s a programming language, silly!

Sass has functions for darkening and lightening colors: darken() and lighten() respectively. In vanilla CSS, you can use hsl() to do the same thing (the l stands for lightness).

:root {

	/* regular blue */
	--color-primary: hsl(200 100% 36%);

	/* darkened blue */
	--color-primary-dark: hsl(200 100% 21%);

}

A leaner compiler and extending native features

I’ve been using Rollup for my JavaScript for a few years, but the latest version breaks a lot of my old builds, and I’m not loving the new updates.

I was taking a look at ESBuild as a faster, smaller alternative, and discovered that it can process CSS, too.

Why bother? Well… CSS now supports nesting!

a {
	color: var(--color-primary);
	text-decoration-skip-ink: auto;
	word-wrap: break-word;

	&:active,
	&:focus,
	&:hover {
		color: var(--color-primary-dark);
	}
}

It currently works in Chromium and Safari, but not Firefox. However, with ESBuild, you can write fully native CSS in plain old vanilla CSS files, and it will compile them into unnested code for you.

CSS has had a native @import feature since before Sass existed.

But like native imports in JavaScript, it’s bad for performance and can cause substantial delays in rendering (especially if you got multiple levels deep).

With ESBuild, you can write native CSS with native @import, and it will complile it into a single bundled file for you, just like with JavaScript bundling!

What can’t it do?

Sass has mixins, which are like functions that accept arguments and spit out predefined chunks of CSS with your parameters included. They were useful to generating a bunch of custom font declarations with different weights and typefaces.

Sass also has the @extend rule, which lets you extend one CSS declaration with the properties of another.

.text-large {
	font-size: 1.1875em;
	line-height: 1.4;
}

.text-xlarge {
	@extend .text-large;
	font-size: 1.7em;
}

In this example, .text-xlarge has the same line-height property as .text-large, but the font-size property is overwritten.

Of course, you can do the same thing with vanilla CSS and just a touch more work.

.text-large {
	font-size: 1.1875em;
}

.text-xlarge {
	font-size: 1.7em;
}

.text-large,
.text-xlarge {
	line-height: 1.4;
}

Frankly, none of these seem like deal breakers. I think it’s time to go vanilla CSS.