I Removed Sass From My Build Pipeline (And You Probably Should Too)
After years of defending preprocessors, I've watched native CSS absorb every feature that justified Sass—and do it better.
An opinionated take on when Sass stopped being necessary for most projects. Learn what native CSS features replace preprocessors, what you actually lose by dropping Sass, and why simpler toolchains ship faster.
Jay McBride
Software Engineer
Introduction
I deleted 4,000 lines of Sass from a production codebase last month and replaced it with native CSS. Build time dropped by 40%. Bundle size shrunk. The code got easier to debug.
Nobody noticed. Not the users. Not the other developers. The site worked exactly the same.
Sass was a critical tool for a decade. It gave us variables before CSS had them. Nesting before the spec supported it. Functions for color manipulation. All of this made stylesheets maintainable when vanilla CSS was genuinely painful.
But CSS evolved. Everything Sass gave us is now native—or has a better native alternative. Meanwhile, Sass stuck around out of habit, inertia, and the assumption that “this is how we do stylesheets.”
This article is for developers who’ve used Sass for years and are wondering if they still need it. If you’re learning CSS for the first time, this isn’t your starting point. Go learn CSS properly first, then come back when you’re evaluating build tools.
I’m going to tell you what native CSS actually replaced, what Sass still does better, and when you should drop the preprocessor entirely. No “it’s a matter of preference” hedging. Just what I’d build today.
Enjoying this? 👉 Tip a coffee and keep posts coming
Here’s who this is for: Frontend developers maintaining Sass codebases. Teams questioning why they’re still compiling stylesheets. Anyone who’s tired of node-sass breaking every six months.
Not for: Beginners choosing their first CSS approach. This assumes you know what Sass does and why it existed.
The question isn’t “which is better?” It’s “do we still need this?”
The Core Judgment: Native CSS Is Good Enough for Most Projects Now
Here’s my default recommendation after shipping both approaches: use native CSS unless you have a specific reason to add Sass to your build pipeline.
Not theoretical. Specific.
Most projects add Sass because “that’s what we’ve always done” or “we might need mixins later.” Then they use variables, nesting, and nothing else. They’re paying the cost of a preprocessor—build configuration, compilation time, debugging compiled output—for features CSS now provides natively.
I’ve done this. Multiple times. Every time, I eventually regret it.
Sass is build infrastructure. You need a compiler. You need to configure your bundler. You need to maintain dependencies that break when Node versions change. You need source maps or debugging is hell. You need to explain to new developers why the CSS they write isn’t the CSS that runs.
Native CSS gives you none of these problems. It works directly in the browser. No compilation. No dependencies. No build step. Just write CSS and ship it.
The mistake people make is thinking Sass features are superior because they’re more powerful. They’re not. They’re older solutions to problems CSS now solves differently. Custom properties are more powerful than Sass variables because they’re runtime-dynamic. Native nesting works without compilation. Container queries and :has() enable layouts Sass couldn’t touch.
I see this constantly: teams keep using Sass for variables and nesting, features CSS has supported for years. They’re maintaining build complexity for zero benefit.
The decision isn’t “which has more features?” It’s “which complexity am I willing to maintain when both solve my actual problems?”
If you’re generating thousands of utility classes with loops, keep Sass. If you need color manipulation functions that CSS doesn’t provide yet, keep Sass. But if you’re just using variables, nesting, and calc()? Drop the preprocessor.
How This Works in the Real World
The reason Sass feels necessary is that we learned CSS when it was legitimately limited.
You think CSS variables are just Sass variables without the $. You think nesting is the same thing but with different syntax. You’re missing what actually changed.
Here’s what actually happens with native features:
You define a custom property in :root. You reference it with var(). So far, normal. But then you change it dynamically with JavaScript. Or you override it in a media query. Or you set different values per component without specificity wars. None of this works with Sass variables because they compile away.
You nest selectors using &. Looks like Sass. But there’s no build step, so when you inspect in DevTools, you see the actual source. No source maps. No “this came from line 47 of _components.scss.” Just the CSS you wrote.
You use :has() to style a parent based on its children. Sass can’t do this. Never could. It’s a runtime selector that requires the browser’s layout engine. This single feature replaces entire JavaScript-based styling patterns.
What surprised me when I finally dropped Sass:
Debugging got dramatically easier. When your stylesheets are native CSS, DevTools shows exactly what you wrote. No mental translation between source and compiled output. No confusion about which Sass file generated which rule.
Build times dropped significantly. No Sass compilation means one fewer step in the pipeline. This matters most in watch mode during development. Hot reload is instant.
New developers onboarded faster. When I showed them the stylesheets, I could say “this is CSS” instead of “this is Sass, which compiles to CSS, and here’s how source maps work.” Less cognitive load.
A Real Example: When I Finally Let Go
I maintained a design system built with Sass for three years. Massive mixins for typography scales. Loops generating color utilities. Functions for spacing calculations. The whole Sass toolkit.
We updated to use CSS custom properties for theming. Then we added container queries for responsive components. Then we needed :has() for conditional layouts. Each feature worked around Sass instead of with it.
Eventually I realized: we’re using Sass as a worse build tool for CSS it can’t fully support anymore.
I rewrote the core system in native CSS over two weeks. Variables became custom properties. Nesting stayed nesting but without compilation. Mixins became utility classes or were deleted when I realized we weren’t using them. The loop-generated utilities stayed in Sass temporarily, but we questioned whether we even needed them.
What I’d do differently: Ask “do we actually use Sass features that CSS doesn’t support?” before starting any project. If the answer is “just variables and nesting,” don’t add Sass. If the answer is “loops and color functions,” evaluate whether simpler alternatives exist first.
The design system didn’t need Sass. It needed custom properties for theming and modern CSS for layouts. Sass was legacy infrastructure we kept out of habit.
Common Mistakes I Keep Seeing
Using Sass variables when custom properties work better. Sass variables are compile-time constants. Custom properties are runtime values you can change dynamically. For theming, responsive design, and user preferences, custom properties win every time.
Nesting too deeply because you can. Just because Sass lets you nest ten levels deep doesn’t mean you should. Native CSS nesting works the same way, but the lack of a compilation step makes you feel the pain of over-nesting sooner. This is good. Deep nesting creates specificity problems and unmaintainable code.
Building color utilities with Sass functions. Sass has darken(), lighten(), and mix(). CSS now has color-mix() which works in modern browsers. Yes, it’s newer. Yes, browser support matters. But for new projects targeting modern browsers, the native solution is better because it’s runtime-dynamic.
Keeping Sass “just in case we need it later.” This is how technical debt accumulates. You pay the cost of build complexity now for features you might never use. Start without Sass. Add it later if you hit a concrete limitation. Going from CSS to Sass is easier than going from Sass to CSS.
Tradeoffs and What Sass Still Does Better
I’m not saying Sass is obsolete for everyone. I’m saying it’s unnecessary for most projects.
Keep Sass when:
- You’re generating large sets of utility classes with
@eachor@forloops (though question whether you need this or should just use Tailwind) - You need color manipulation functions and can’t use
color-mix()due to browser support requirements - You have a massive existing Sass codebase and migration cost exceeds maintenance cost
- Your team is deeply experienced in Sass and starting fresh on a project where familiarity outweighs simplicity
Drop Sass when:
- You’re only using it for variables, nesting, and basic math
- You’re starting a new project without legacy constraints
- Your target browsers support custom properties, nesting, and modern CSS features
- You want faster builds and simpler tooling
Real limitations of native CSS:
- No loops for generating utilities. You write each class manually or use a utility framework like Tailwind.
- Color manipulation functions are newer and less mature.
color-mix()works but doesn’t have feature parity with Sass yet. - No true “mixins” for reusable chunks of CSS, though
@layerand utility classes handle most use cases.
The honest answer: if you’re using more than 20% of Sass’s feature set, keep it. If you’re using less, you’re paying for infrastructure you don’t need.
Best Practices I Actually Follow
Start with native CSS by default. Don’t add Sass preemptively. Build the first iteration with vanilla CSS and custom properties. You’ll discover whether you actually need preprocessing.
Use custom properties for all theming and design tokens. Colors, spacing, typography scales—these belong in :root as custom properties, not Sass variables. You get runtime flexibility and no build step.
Embrace modern CSS features aggressively. Container queries, :has(), :where(), cascade layers—these solve problems Sass can’t touch. Learn them. Use them. Stop working around CSS limitations that no longer exist.
Question utility generation patterns. If you’re using Sass loops to generate utilities, consider whether Tailwind or UnoCSS solve this better. They’re purpose-built for utility generation. Sass is a general preprocessor being used for a specific job.
Measure actual pain points, not hypothetical needs. Don’t keep Sass because “we might need mixins someday.” Keep it when you actively use features CSS doesn’t provide. Drop it when you realize you’re not.
Conclusion
Sass solved real problems. It filled gaps in CSS that were genuinely painful for years. Variables, nesting, and functions made stylesheets maintainable when vanilla CSS wasn’t up to the task.
But CSS evolved. The features we needed are now native. The tradeoff shifted. What used to be “Sass complexity vs CSS limitations” is now “Sass complexity vs CSS simplicity.”
After years of using Sass by default, I’ve learned that native CSS ships faster, debugs easier, and scales just fine. The preprocessor isn’t bad—it’s just unnecessary for most projects now.
The future isn’t “Sass or CSS.” It’s recognizing when the tool that used to be essential became optional.
Start simpler. Add build steps only when you hit real limitations. Your build pipeline will be shorter, your debugging faster, and your onboarding smoother.
Frequently Asked Questions (FAQs)
Are CSS custom properties actually faster than Sass variables?
Sass variables compile away, so they have zero runtime cost. Custom properties have a tiny runtime lookup cost. But this cost is negligible for real applications and is vastly outweighed by the flexibility of runtime-dynamic values. For theming and responsive design, custom properties are objectively better.
What about browser support for CSS nesting?
Native CSS nesting works in all modern browsers as of 2023. If you’re supporting IE11 or very old Safari versions, you’ll need Sass or PostCSS to compile it. But for projects targeting modern browsers, native nesting works fine.
Can I mix Sass and native CSS features?
Yes. You can use CSS custom properties inside Sass files. You can use native nesting in Sass. But this often creates a confusing mix of compiled and native features. It’s usually cleaner to go all-in on one approach.
How do I handle color manipulation without Sass functions?
Use color-mix() in modern browsers. For legacy browsers, define your color variations as custom properties instead of calculating them dynamically. Most designs don’t actually need runtime color manipulation—they need a consistent palette.
What if I’ve already built a large Sass codebase?
Don’t rewrite it unless it’s causing real pain. But for new features or components, consider writing them in native CSS. Gradually migrate as you touch files. Rewrites are expensive; incremental improvements are practical.
Your turn: Look at your current Sass usage. How many files use features beyond variables and nesting? Would dropping Sass actually break anything, or is it just habit?
Enjoying this? 👉 Tip a coffee and keep posts coming
