On This Page
The first time this warning shows up in your build output, it doesn't stop anything. The build finishes, the CSS comes out fine, the exit code is 0. So the reflex is to glance at it, decide it's noise, and scroll past. That's the wrong move, and it's the reason this warning has been sitting in build logs, unaddressed, for two years running.
The @import warning your build has been printing for two years
Here's exactly what Dart Sass prints, character for character, the first time it hits an @import:
DEPRECATION WARNING [import]: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
More info and automated migrator: https://sass-lang.com/d/import
╷
1 │ @import 'variables';
│ ^^^^^^^^^^^
╵
src/main.scss 1:9 root stylesheetI pulled that straight from a sass compile in this session, not from memory or from someone else's blog post. Same wording you'll get. If your codebase has a few dozen @import statements, you won't see all of them. Dart Sass runs in terse mode by default and only prints five occurrences of a given deprecation before it collapses the rest:
WARNING: 9 repetitive deprecation warnings omitted.
Run in verbose mode to see all warnings.That's a real count from a repo I built with seven partials, each pulling in one shared file. Fourteen total @import occurrences, five printed, nine folded away. Which is exactly why this warning is so easy to under-estimate: your terminal tells you "five," and your actual surface area might be fifty.
This isn't a browser thing. @import here is a Sass at-rule, resolved entirely by the compiler before any CSS reaches a browser, so there's no Chrome-vs-Safari angle. The thing that matters is which Sass implementation and version you're running, which is section three.
One thing worth saying up front, because I spent longer confirming it than I expected to: this is a compile-time deprecation, not a runtime bug. Your site isn't broken right now. It will be, on whatever day your team finally bumps to Dart Sass 3.0.0 and finds out @import doesn't compile anymore. I've watched that exact scenario play out on a design-system package that hadn't been touched in eighteen months. A routine npm update pulled in a newer sass, the CI log went from clean to two hundred warning lines overnight, and the ticket sat for a sprint because nothing was actually failing.
Reproduce it: four files, no framework
You don't need a bundler for this. Four files and the sass CLI:
scss
// src/_variables.scss
$brand-color: #3b4cca;
$spacing-unit: 8px;scss
// src/_buttons.scss
@import 'variables';
.button {
background: $brand-color;
padding: $spacing-unit * 2;
}scss
// src/main.scss
@import 'variables';
@import 'buttons';
body {
color: $brand-color;
}bash
npm install sass
npx sass src/main.scss out.cssRun that with any Dart Sass from 1.80.0 onward and you'll get three copies of the warning shown above, one for each @import line, including the one nested inside _buttons.scss. The CSS still compiles correctly. That's the trap: it looks done.
If you want to confirm you're actually running something that cares about this at all, check the compiler first:
bash
npx sass --version
# 1.105.0 compiled with dart2js 3.13.4If that command instead resolves to node-sass, you won't see this warning at all. You'll see a different one, because node-sass itself has been dead since mid-2024 and its own package metadata says so plainly: "Node Sass is no longer supported. Please use sass or sass-embedded instead." That's a separate migration, not today's, but it's worth ruling out first if your warning looks nothing like the block above.
Which Dart Sass, which framework, which flag
Sass isn't a browser feature, so there's no caniuse table here. The relevant "support matrix" is which compiler and which build tool you're on, and since when each behavior applies.
| Where | Status |
|---|---|
| Dart Sass < 1.80.0 | No @import warning at all; you're on a version that predates the deprecation. |
| Dart Sass 1.80.0 – current (1.105.0, published 2026‑09‑22) | Warns on every @import, terse-collapsed after 5 occurrences by default. @use/@forward compile clean. |
| Dart Sass 3.0.0 | @import of Sass partials removed outright. No release date has been announced; by Sass's own two-year policy it can't land before 2026‑10‑17, which, at the time I'm writing this, is about three weeks out. |
node-sass | Dead. Last published July 2024, package itself says to switch to sass/sass-embedded. Irrelevant to this warning because it never implemented the module system to begin with. |
sass-embedded (native Dart Sass, used by Vite's api: 'modern-compiler', Angular CLI, others for speed) | Same deprecation policy as sass. It's a wrapper around the same compiler, not a fork. |
Vite (css.preprocessorOptions.scss) | Options pass straight into the Sass JS API. silenceDeprecations: ['import'] works exactly as documented for the plain sass package. |
webpack (sass-loader) | Same story. sassOptions/api are forwarded verbatim to whichever of sass/sass-embedded you have installed. |
Next.js (next.config.js → sassOptions) | Next.js's own docs (checked against the current 16.3.6 release) say plainly that sassOptions properties other than implementation "are not typed... because Next.js does not maintain" them. They go straight to the underlying package, same rules apply. |
stylelint (core, at-rule-disallowed-list) | Real, shipping rule. {"at-rule-disallowed-list": ["import"]} fails the lint on any @import, including SCSS partials. No SCSS-specific plugin required for this part. |
Progressive enhancement doesn't really apply to a compiler deprecation. There's no @supports query for "my build tool still understands @import." Either you're on a version that warns, or a future version that refuses to compile it.
What @import does that @use refuses to
The short version: @import dumps everything from the imported file into one shared global namespace, and it does that dump again, from scratch, every single time the file gets imported anywhere in your tree. @use loads a file exactly once, keeps its contents in a namespace you have to name explicitly, and shares that one load across your whole build no matter how many files @use it.
That's the whole reason this migration isn't a find-and-replace.
One flat, global namespace
Under @import, a variable defined in _variables.scss becomes a plain global the instant anything imports that file, anywhere, at any depth. Two partials can define $spacing-unit with different values and the second one silently wins, because there's only one namespace and nothing stops the collision. Worse, Dart Sass has to re-parse and re-evaluate the imported file's AST at every import site. That's not a style problem, it's a real compile-time cost that scales with how tangled your @import graph is.
@use fixes both at once by treating each file as a module: loaded once, memoized, and exposed only through the namespace you assign. @use 'variables'; gives you variables.$brand-color, not a bare global. Want the members without a prefix, the way @import used to feel? @use 'variables' as *; does that on purpose, as an opt-in, rather than as the only available behavior.
There's a positioning rule that trips people up here, and it's not cosmetic. The Sass spec is explicit that @use rules "must come before any rules other than @forward," and the compiler enforces it. Anywhere later in the file and it's a hard error, not a warning. More on that in the next section, because it's one of the two things that ate real time when I did this migration for real.
--quiet-deps is worth understanding precisely here too, because its name oversells what it does. It silences deprecation warnings whose origin file was reached through a load path or package importer, genuinely third-party code. It does not silence the warning attached to your own @import statement just because that statement happens to point into node_modules. I verified this directly: importing a fake dependency via a relative ../node_modules/... path still printed the warning pointing at my own file even with --quiet-deps on; only the warning whose stack frame originated inside the dependency's own .scss disappeared. If your logs are still noisy after turning that flag on, the noise is telling you something real: that @import line is yours to fix, not something to wait out.
So, which @import is actually yours to fix?
Dead end one. I assumed --quiet-deps would blanket-silence anything touching node_modules and moved on without reading past the flag's name. It didn't. Running the build again with the flag on, the warning count barely dropped, because almost all of my imports referenced dependencies through relative paths inside my own source tree, not through Sass's load-path/package resolution, which is the only case that flag actually covers. The fix wasn't a flag; it was going file by file.
Dead end two. I did a mechanical @import → @use find-and-replace across one legacy partial, confident it was a syntax swap. First run: Error: @use rules must be written before any other rules. The replaced line had landed in the middle of the file, after a stray comment block with a style rule above it. Moved it to the top, ran again: Error: Undefined variable. That's because @use doesn't dump into global scope, and every bare $brand-color reference downstream needed either a namespace prefix or an explicit as *. Two separate hard failures from what looked like one search-and-replace.
Once you know what you're looking for, the actual diagnostic path is short:
- Confirm the compiler:
npx sass --version. If it's not Dart Sass, you're chasing a different bug. - Run your build once with
--verboseinstead of the default terse mode, so nothing gets collapsed. That gives you a true count, not five-and-a-guess. - Count and locate every occurrence before touching anything:
bash
grep -rn "^@import" --include="*.scss" src | wc -l
grep -rln "^@import" --include="*.scss" src | sort- For each file that list turns up, preview the migration without changing anything:
bash
npx sass-migrator module --dry-run --migrate-deps src/main.scss- Re-run the build with
--fatal-deprecation=importlocally, once, as a sanity check. If it exits 65 instead of 0, you've got at least one@importleft standing, and the error output tells you exactly which file and line.
Ranked by how often each one is actually the cause, worst offender last:
Most common: the codebase just hasn't been touched since a scaffold from before 2019, when the module system didn't exist yet, and nobody's gone back through it. Mechanical migration, done in one pass, fixes it.
Second: a handful of @import lines got reintroduced later, copy-pasted from an old Stack Overflow answer or blog post. Most Sass content written before 2019 still teaches @import, because that's all there was.
Least common, but the one that actually cost me the most time: a third-party package, in my case an old component-library theme pulled in as a dependency, still ships raw .scss partials that use @import internally. I was @import-ing their file from my file. The warning's stack trace showed my line as the "root stylesheet" frame, which made it look like my problem to fix immediately, when the real fix (getting the dependency to publish @use-based partials) wasn't mine to make. The tell that separates this case from the first two: run --quiet-deps and see if the count barely moves. If it does, you're waiting on someone else's package, not missing something in your own code.
Migrating @import to @use without breaking the cascade
For anything bigger than a handful of files, don't do this by hand. sass-migrator is maintained by the Sass team specifically for this:
bash
npm install -g sass-migrator
sass-migrator module --dry-run --migrate-deps src/main.scssDry run first, always. It prints which files it would touch without writing anything. Drop --dry-run once the plan looks right:
diff
--- src/_buttons.scss (before)
+++ src/_buttons.scss (after)
- @import 'variables';
+ @use 'variables';
.button {
- background: $brand-color;
- padding: $spacing-unit * 2;
+ background: variables.$brand-color;
+ padding: variables.$spacing-unit * 2;
}That's the real diff from the four-file repro above, run through the actual migrator, not typed by hand. It rewrote every @import to @use, rewrote every bare variable reference to a namespaced one, and the rebuilt output matched the original CSS byte-for-byte with zero warnings.
Three ways to land this, best first:
Automated, reviewed in small PRs. Run the migrator per entry point, review the diff like any other refactor, land it in pieces rather than one repo-wide commit nobody can meaningfully review. This is what I'd actually ship.
Manual, for a handful of files. Fine if you're touching two or three partials. Just remember the ordering rule (@use before any other rule except @forward) and that every previously-global reference now needs either a namespace or as *.
Silence it and move on, for now. --silence-deprecation=import on the CLI, or silenceDeprecations: ['import'] in the JS API that Vite, webpack, and most bundlers forward straight through. I want to be blunt about what this actually is: it's not a fix, it's turning off the smoke alarm. It buys you time. It does nothing for the day @import is actually removed in 3.0.0. At that point the build doesn't warn, it fails, and silencing a deprecation you've already stopped hearing about is a worse position to discover that from than a noisy log you've been ignoring.
Sass that Dart Sass 3.0 can't break
The shape that survives this, and the next Sass major after it, uses @forward as a deliberate public interface rather than letting every partial leak into one shared bucket. A small design-system layer looks like this:
scss
// _index.scss: the only file consumers ever @use
@forward 'variables';
@forward 'mixins';
@forward 'buttons';scss
// consumer.scss
@use 'design-system' as ds;
.card {
background: ds.$brand-color;
}One entry point, one namespace, and every internal file free to be renamed or restructured without touching a single consumer. Configure shared values at the point of use instead of predefining globals before an @import, the way the old pattern forced you to:
scss
@use 'design-system' with (
$brand-color: #1c2340
);That's not possible with @import at all. There's no configuration step, just whatever globals happened to exist in scope by the time the file got pulled in, in whatever order.
Wiring stylelint to catch the next @import
Once the migration is done, keep it done. Add the rule to your stylelint config:
json
{
"rules": {
"at-rule-disallowed-list": ["import"]
}
}That's stylelint core, not a SCSS-specific plugin. It treats @import as just another at-rule name and fails the lint the moment one reappears, whether it's a copy-pasted snippet or a new contributor who learned Sass from an old tutorial. Pair it with a CI-only compile check using --fatal-deprecation=import, so a regression fails the build even if it somehow slips past review. Keep --fatal-deprecation out of local dev scripts, though. You want a hard stop in CI, not a developer's afternoon derailed by a flag they didn't know was set.
If you maintain a package that other teams @use or @import, add a Renovate or Dependabot watch on your own sass/sass-embedded major-version bumps specifically, with the changelog linked in the PR description. That's the signal that tells you whether this quarter's update is routine or whether it's the one that removes @import outright.
One line so it's on your radar and not a surprise later: Dart Sass has a second, newer, unrelated deprecation around mixing declarations after nested rules (mixed-decls), plus an older one retiring the / division operator in favor of math.div(). Different warnings, different fixes, both worth their own read when you get there. And if your @import warning showed up alongside a PostCSS error about Tailwind's plugin package having moved, that's a separate, unrelated toolchain break, not this one.
What actually transfers to the next Sass migration
A build that exits 0 with warnings in the log is not a build that's fine. It's a build that's currently fine, on a clock nobody's watching. --quiet-deps silences warnings from genuine dependencies loaded through load paths; it does not silence warnings attached to your own @import statements, even when they point into node_modules. @use rules have to come before any other rule in the file, and that's an error, not a style preference, the first time you get it wrong. sass-migrator module --dry-run will show you the whole blast radius before you commit to it, so run it before you touch anything by hand. And the earliest Dart Sass 3.0.0 can legally remove @import, under Sass's own stated policy, is October 17, 2026, which by the time you're reading this may already have come and gone.
CODELZ Newsletter
Join the newsletter to receive the latest updates in your inbox.