Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIntermediáriocódigo testado

@use e @forward no Sass: o fim do @import e os partials

O aviso de depreciação que o @import imprime hoje, como o namespace muda suas chamadas e a estrutura de partials que sustenta o projeto.

Rodolfo Mori5 min de leitura

O sistema de módulos do Sass resolve dois defeitos do antigo @import: nomes globais que colidiam e arquivos avaliados repetidamente. @use carrega um módulo com namespace; @forward constrói a API pública; partials mantêm arquivos internos fora da lista de entradas compiladas.

Os exemplos foram gravados em arquivos reais e compilados com Dart Sass 1.103.1. Quando um módulo não emitia seletor, o processo encerrava com status 0 e stdout vazio; as seções abaixo mostram separadamente as saídas que continham CSS ou diagnóstico.

Partial: por que o arquivo começa com _

O underscore marca um arquivo auxiliar. _tokens.scss não deve virar _tokens.css sozinho; ele será consumido por uma entrada. No caminho do módulo, omita underscore e extensão:

text
scss/
├── _tokens.scss
├── _buttons.scss
├── _index.scss
└── app.scss
@use "tokens" resolve _tokens.scss no mesmo diretório.

Essa convenção diferencia fonte interna de entrada compilável. Ela não torna o arquivo privado por segurança; a API pública depende dos membros expostos e do ponto de entrada escolhido.

@use: namespace obrigatório por padrão

O partial define tokens configuráveis e um mixin:

css
// _tokens.scss
$brand: #6d28d9 !default;
$space: 1rem !default;

@mixin focus-ring {
  outline: 3px solid $brand;
}

O consumidor usa tokens. antes da variável ou mixin:

css
@use "tokens";

.button {
  color: tokens.$brand;
  padding: tokens.$space;
  @include tokens.focus-ring;
}
.button { color: #6d28d9; padding: 1rem; outline: 3px solid #6d28d9; }

O namespace torna a origem visível e permite que dois módulos publiquem $brand sem colisão. É possível trocar o nome com as, mas as * remove a proteção e deve ficar restrito a módulos controlados com API pequena.

Configurar !default com with

Valores marcados com !default no topo podem ser configurados na primeira carga do módulo:

css
@use "tokens" with (
  $brand: #0f766e,
  $space: 1.25rem
);

.button {
  color: tokens.$brand;
  padding: tokens.$space;
}
.button { color: #0f766e; padding: 1.25rem; }

Configuração é parte do contrato da biblioteca. Como o módulo é carregado uma vez, faça o @use ... with antes de qualquer outra carga dele. Não dependa de ordem acidental entre arquivos.

@forward: criar um único ponto de entrada

Um pacote pode reexportar apenas membros públicos. _index.scss escolhe o que passa:

css
// _index.scss
@forward "tokens" show $brand, $space, focus-ring;

// app.scss
@use "index" as ui;
.button {
  color: ui.$brand;
  @include ui.focus-ring;
}
.button { color: #6d28d9; outline: 3px solid #6d28d9; }

O consumidor conhece index, não a pasta interna. Assim, você pode mover _tokens.scss sem mudar todos os projetos, desde que preserve a API encaminhada. show funciona como lista de permissão; hide remove membros específicos.

Membros privados com _ ou -

Nomes iniciados por underscore ou hífen são privados ao módulo. Tentar acessar tokens.$_secret falhou no teste:

css
// _tokens.scss
$_secret: #111827;

// private.scss
@use "tokens";
.secret { color: tokens.$_secret; }
Error: Private members can't be accessed from outside their modules. ╷ 2 │ .secret { color: tokens.$_secret; } │ ^^^^^^^^^^^^^^^ ╵ private.scss 2:18 root stylesheet

Privacidade permite refatorar helpers sem prometer compatibilidade. Marque como público apenas o que o consumidor realmente deve usar.

O aviso real do @import

O estilo antigo coloca membros no escopo global. Ele ainda compilou, mas a versão testada imprimiu depreciação:

css
@import "tokens";
.button { color: $brand; }
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 “tokens”; │ ^^^^^^^^ ╵ legacy-import.scss 1:9 root stylesheet

Silenciar o aviso adia a mudança, não remove a quebra futura. Migre uma entrada por vez e compare o CSS gerado antes e depois.

A mesma dependência: três emissões com @import, uma com @use

Para testar a avaliação repetida, _shared.scss continha apenas a regra .module-loaded. Três partials intermediários pediram esse arquivo; a entrada antiga importou os três, enquanto a moderna usou os três módulos:

css
// _shared.scss
.module-loaded { color: #6d28d9; }

// _import-a.scss, _import-b.scss e _import-c.scss
@import "shared";

// _use-a.scss, _use-b.scss e _use-c.scss
@use "shared";

Compilei as duas entradas em estilo expandido. --silence-deprecation=import foi usado somente na medição; o aviso sem esse sinalizador está copiado acima.

bash
npx --no-install sass import-entry.scss import.css --no-source-map --style=expanded --silence-deprecation=import
npx --no-install sass use-entry.scss use.css --no-source-map --style=expanded
rg -c '^\.module-loaded' import.css
rg -c '^\.module-loaded' use.css
wc -c import.css use.css
3 1 113 import.css 37 use.css 150 total

No mesmo processo de compilação, @import emitiu a regra três vezes e produziu 113 bytes; @use avaliou o módulo compartilhado uma vez, emitiu uma regra e produziu 37 bytes. Isso mede este caso mínimo, não promete a mesma proporção em uma aplicação inteira.

O erro clássico da migração: esquecer o namespace

Depois de trocar @import por @use "tokens", $brand não está global. A referência correta é tokens.$brand. Sem prefixo, o compilador aponta a variável:

css
@use "tokens";
.button { color: $brand; }
Error: Undefined variable. ╷ 2 │ .button { color: $brand; } │ ^^^^^^ ╵ undefined-variable.scss 2:18 root stylesheet

O namespace é a correção, não as * em todos os módulos. Preservar origem explícita reduz colisões e torna busca e refatoração confiáveis.

@use precisa vir antes de qualquer outra regra

Também reproduzi o erro de ordem com uma regra CSS antes da carga do módulo:

css
.button { color: #111827; }
@use "tokens";
Error: @use rules must be written before any other rules. ╷ 2 │ @use "tokens"; │ ^^^^^^^^^^^^^ ╵ use-after-rule.scss 2:1 root stylesheet

O processo encerrou com status 65. Mova todos os @use para o início da folha, antes de seletores, declarações e outras regras que emitam CSS; trocar a ordem dois arquivos abaixo não corrige a posição inválida nesta entrada.

Uma arquitetura pequena que escala

Comece com poucos pontos: tokens sem CSS, helpers privados, componentes que emitem regras e um index público. Não replique uma árvore de sete pastas para três arquivos. Separe quando existe fronteira de responsabilidade.

Uma distinção útil é configuração contra saída. O módulo de tokens define valores e contratos; o módulo de componente decide seletores. Se importar tokens já gera centenas de regras, qualquer helper passa a ter efeito colateral. Mantenha módulos de dados silenciosos e deixe a aplicação escolher quais componentes emitir.

Dependências também devem apontar numa direção. Tokens não conhecem botões; botões podem conhecer tokens. Ferramentas não conhecem páginas; páginas podem usar ferramentas. Um ciclo entre módulos indica que responsabilidades foram misturadas. Extraia o contrato compartilhado para uma camada menor em vez de tentar ordenar imports até compilar.

Na publicação de uma biblioteca, trate _index.scss como fachada versionada. Adicionar membro é compatível; remover ou renomear membro público quebra consumidores; alterar um valor !default pode mudar a aparência sem erro de compilação. Registre essas mudanças e mantenha uma folha de teste que exercite a configuração padrão e uma configuração personalizada.

text
scss/
├── tokens/_colors.scss
├── tools/_focus.scss
├── components/_button.scss
├── _index.scss
└── app.scss
app.scss usa a API pública; componentes internos usam dependências diretas.

Tokens não devem emitir CSS por acidente, e um módulo público deve documentar variáveis !default, funções e mixins suportados. Leia funções e módulos nativos antes de criar uma API grande. O guia de Sass mostra como módulos sustentam o restante da trilha sem transformar pastas em objetivo por si só.

Durante a migração, liste primeiro os globais usados por cada entrada. Troque um @import por @use, adicione namespaces, compile e compare a saída antes de seguir. Se a base configura tokens, transforme-os em variáveis com !default e carregue o módulo com with. Essa cadência pequena mantém o diff legível e mostra qual dependência era implícita.

Depois que todos os imports Sass desaparecerem, remova compatibilidade antiga e volte a rodar o compilador sem silenciar warnings. O objetivo não é apenas obter status zero hoje; é deixar fronteiras que permitam atualizar Dart Sass sem redescobrir a ordem global da aplicação.

  • sass
  • use
  • forward
  • import
  • partials
  • módulos
  • arquitetura

Perguntas frequentes

@use substitui @import diretamente?
Ele carrega módulos, mas muda o escopo: membros recebem namespace e cada módulo é avaliado uma vez. A migração exige ajustar referências.
Para que serve @forward?
Ele reexporta membros de outros módulos e permite montar um ponto público sem obrigar consumidores a conhecer a estrutura interna de pastas.
O underscore do partial entra no @use?
Não. O arquivo _tokens.scss é carregado como @use "tokens". A extensão também pode ser omitida.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Dart Sass 1.103.1 (dart2js 3.13.1), Node 26.3.0, macOS, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. Sass — @use — sass-lang.com
  2. Sass — @forward — sass-lang.com
  3. Sass — @import deprecation — sass-lang.com

Continue por aqui