Como usar mixins nativos de CSS em vez de Sass
Veja como migrar mixins do Sass para CSS nativo com @mixin e @apply, usar parâmetros e identificar recursos que ainda exigem Sass.
Um mixin nativo de CSS é um bloco nomeado de declarações. Você o define com @mixin --name e o insere em uma regra de estilo com @apply --name. Isso o torna o equivalente, no navegador, ao @mixin e ao @include do Sass.
Se suas folhas de estilo já usam propriedades personalizadas (custom properties) e aninhamento nativo, os poucos mixins compartilhados em um partial _mixins.scss podem ser o único motivo para você ainda usar o Sass. Este artigo converte um mixin real para CSS nativo, mostra as duas versões lado a lado e lista os tipos de mixin que não podem ser migrados.
Principais pontos
- Mixins nativos de CSS usam
@mixin --namepara definir um bloco e@apply --namepara utilizá-lo. Os nomes são dashed idents (identificadores iniciados por dois hífens), e o@applycumpre o papel do@includedo Sass. - O Sass copia as declarações de um mixin em cada regra que o inclui, em tempo de compilação. Um mixin nativo é expandido pelo navegador, então a folha de estilo entregue contém uma única definição.
- Segundo o MDN, nenhum navegador oferece suporte a mixins CSS por padrão. Portanto, eles não fazem parte do Baseline, e o Sass continua sendo a escolha para produção.
- Mixins que dependem de maps do Sass, de loops
@eachou@for, de condicionais@if/@elseou de cálculos em tempo de build não têm equivalente nativo.
O mixin Sass que você já tem
Um mixin cover fixa um elemento nas quatro bordas do seu ancestral posicionado. É praticamente o mixin mais simples que existe, o que o torna um bom caso de teste. Aqui ele é usado duas vezes: uma vez em um backdrop e outra em um overlay criado com pseudoelemento. (Para mais contexto sobre o lado do Sass, consulte este guia sobre mixins SCSS.)
@mixin cover {
position: absolute;
inset: 0;
}
.dialog-backdrop {
@include cover;
background: rgb(0 0 0 / 0.6);
}
.media-card.is-loading::after {
@include cover;
content: "";
background: rgb(255 255 255 / 0.7);
}
Sozinha, a definição do mixin não gera nenhum CSS. Cada @include copia as declarações para a regra que o chama:
.dialog-backdrop {
position: absolute;
inset: 0;
background: rgb(0 0 0 / 0.6);
}
.media-card.is-loading::after {
position: absolute;
inset: 0;
content: "";
background: rgb(255 255 255 / 0.7);
}
Quanto mais regras incluem o mixin, mais cópias dessas duas declarações vão parar no resultado final.
A reescrita como mixin nativo de CSS, lado a lado
Converter um mixin Sass composto apenas de declarações para CSS nativo exige três passos mecânicos: adicionar dois hífens no início do nome, transformar os parâmetros $ em parâmetros -- e substituir @include por @apply.
Sass
@mixin cover {
position: absolute;
inset: 0;
}
.dialog-backdrop {
@include cover;
background: rgb(0 0 0 / 0.6);
}
CSS nativo
@mixin --cover {
position: absolute;
inset: 0;
}
.dialog-backdrop {
@apply --cover;
background: rgb(0 0 0 / 0.6);
}
O @apply substitui o @include, e o nome do mixin precisa ser um dashed ident. Como é o navegador que faz a expansão, a folha de estilo que você entrega contém uma única definição de --cover. O corpo contém apenas declarações, sem o wrapper @result, porque o CSSWG decidiu remover o @result dos mixins. O próprio rascunho alerta que os mixins estão bem menos consolidados do que as funções personalizadas, então a sintaxe ainda pode mudar.
Suporte dos navegadores a mixins CSS
Mixins nativos de CSS não podem ser usados em produção. O guia do MDN sobre funções personalizadas e mixins informa que nenhum navegador oferece suporte a mixins por padrão, o que significa que o recurso não faz parte do Baseline. O Chromium está à frente dos outros engines: ele registrou um Intent to Prototype para mixins CSS, e esse trabalho é experimental.
Mixins nativos de CSS também não funcionam como aprimoramento progressivo. O tratamento de erros do CSS descarta construções que o parser não entende. Assim, um navegador sem suporte ignora o @apply, e um elemento que recebe position e inset apenas pelo mixin acaba sem nenhum dos dois. Mantenha a versão Sass em produção e experimente os mixins nativos em um arquivo .css comum, onde o compilador Sass nunca os processe.
Convertendo parâmetros de mixins
Parâmetros de mixins Sass que apenas repassam valores simples podem ser convertidos sem problemas para mixins nativos de CSS. Já mixins que calculam valores, usam condicionais ou loops em tempo de build não podem. Veja o cover com um argumento de inset:
// Sass
@mixin cover($inset: 0) {
position: absolute;
inset: $inset;
}
.frame::before { @include cover(0.5rem); content: ""; }
/* Native */
@mixin --cover(--inset) {
position: absolute;
inset: var(--inset);
}
.frame::before { @apply --cover(0.5rem); content: ""; }
Cada parâmetro se torna uma propriedade privada dentro do corpo do mixin, e você a lê com var(). Os demais estilos da página não conseguem enxergá-la. Os fallbacks de var() funcionam no corpo do mixin da mesma forma que em qualquer outra declaração. O rascunho CSS Custom Functions and Mixins também define uma sintaxe própria para tipar parâmetros e atribuir valores padrão a eles. Consulte o rascunho para ver a forma atual e não presuma que um fallback se comporte exatamente como um valor padrão. O First Public Working Draft (primeiro rascunho público) de 15 de maio de 2025 exigia parênteses até em mixins sem parâmetros, e rascunhos posteriores permitem omiti-los. Builds de protótipo mais antigos exigiam @mixin --a() e @apply --a();, então você encontrará as duas formas.
Estes recursos de mixins Sass não têm equivalente em CSS nativo:
- loops
@eache@for - condicionais
@if/@elsee@error - maps e
map.getdo módulosass:map - cálculos em tempo de build, como
math.divou conversão de unidades
O mixin de breakpoints mostra onde fica esse limite:
@use "sass:map";
$breakpoints: (sm: 40rem, md: 64rem);
@mixin bp($name) {
@if not map.has-key($breakpoints, $name) {
@error "Unknown breakpoint: #{$name}";
}
@media (min-width: map.get($breakpoints, $name)) {
@content;
}
}
A especificação define @contents como o equivalente ao @content do Sass, então, em princípio, um mixin nativo pode receber um bloco de quem o chama e envolvê-lo em um @media. As partes que não podem ser convertidas são a consulta ao map, o desvio com @error e a largura variável dentro da media query.
O que você ganha com mixins nativos de CSS?
Mixins nativos de CSS oferecem duas vantagens em relação aos mixins Sass: nenhuma etapa de build e argumentos resolvidos no navegador, e não em tempo de compilação. A primeira é que o navegador lê @mixin e @apply diretamente, então o CSS que você escreve é o CSS que você entrega.
A segunda vantagem é mais sutil. O Sass também pode gerar referências var(), então um mixin Sass pode emitir declarações que respondem a propriedades personalizadas em tempo de execução. O que o Sass não consegue é alterar um argumento depois da compilação: cover(0.5rem) vira inset: 0.5rem uma única vez, durante o build. Mixins nativos são resolvidos no navegador, e a especificação prevê o tratamento de argumentos cujos valores dependem do elemento ao qual o mixin é aplicado. Esse comportamento pertence à especificação, e não a algum engine já disponível. As funções personalizadas, um recurso irmão do mesmo módulo definido com @function, retornam um único valor, enquanto os mixins retornam um bloco de declarações.
Quando você deve manter o Sass?
Mixins compostos apenas de declarações, como cover, centralização e visually-hidden, podem ser convertidos sem problemas. Mixins que geram seletores, percorrem listas ou calculam valores em tempo de build não podem.
| Mixin | Conversão direta? | O que muda | Por que não |
|---|---|---|---|
| Cover / inset | Sim | Nome com --, @apply, var(--inset) | n/a |
| Visually-hidden | Sim | Nome com --, @apply | n/a |
| Centralização | Sim | Nome com --, @apply | n/a |
| Breakpoints | Não | Envolvimento de blocos via @contents, em princípio | Consulta ao map, @error, largura variável na media query |
Continue usando @mixin e @include do Sass para loops que geram classes utilitárias, tokens baseados em maps e helpers de breakpoints. E continue usando o Sass para tudo em produção até que os mixins estejam disponíveis por padrão nos navegadores.
Conclusão
Converter um mixin composto apenas de declarações, como o cover, para a sintaxe nativa é rápido e, em grande parte, mecânico. Decidir quando fazer a troca depende do suporte dos navegadores, e esse suporte ainda não existe. Um próximo passo prático: separe seus mixins em dois grupos, os compostos apenas de declarações e os que dependem da lógica do Sass. Reescreva o primeiro grupo em um arquivo .css separado e teste-o em um build experimental do Chromium. Mantenha as versões Sass até que o MDN indique suporte padrão.
Perguntas frequentes
Como testar mixins nativos de CSS no Chrome?
O protótipo de mixins do Chromium fica atrás da feature flag CSSMixins. Para testá-lo, inicie o Chrome Canary pela linha de comando com o argumento --enable-features=CSSMixins. A implementação está incompleta e muda à medida que o CSSWG resolve questões da especificação, então alguns exemplos podem falhar ou se comportar de forma diferente do rascunho. Use-a apenas para experimentos e feedback sobre a especificação, nunca para estilos de produção.
Por que não posso substituir uma variável de breakpoint do Sass por var() em uma media query?
A função var() funciona apenas dentro de valores de propriedades, e condições de media queries não são valores de propriedades. As propriedades personalizadas são resolvidas para cada elemento por meio da cascata, enquanto uma media query é avaliada em relação à viewport ou ao dispositivo, e não a um elemento específico. Por isso, uma condição como min-width: var(--md) é inválida, e uma largura de breakpoint armazenada em uma variável não pode ser migrada do Sass para uma media query nativa.
Um mixin nativo de CSS pode incluir seletores aninhados ou media queries?
Sim, segundo a especificação. O rascunho CSS Custom Functions and Mixins permite regras de estilo aninhadas, como um bloco com e comercial seguido de ::after, e regras condicionais, como @media, dentro do corpo de um mixin, de forma semelhante ao que um mixin Sass pode gerar. Nenhum navegador oferece mixins por padrão, então isso descreve o comportamento previsto pela especificação, e não algo que funcione em um engine estável.
O postcss-mixins é a mesma coisa que os mixins nativos de CSS?
Não. O plugin postcss-mixins define um mixin com @define-mixin, o chama com @mixin e usa parâmetros com cifrão, que são expandidos em tempo de build. O CSS nativo usa @mixin para definir um mixin e @apply para chamá-lo, e é o navegador que faz a expansão. A palavra-chave @mixin tem significados opostos nos dois sistemas, então migrar do postcss-mixins para a sintaxe nativa exige reescrever tanto as definições quanto os pontos de chamada.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Star on GitHub12k