Sass vs Less vs CSS nativo: qual compila mais rápido
A mesma folha de 400 componentes escrita nas três abordagens, compilada na mesma máquina, com tempo e tamanho do CSS medidos.
CSS nativo ganhou porque não há pré-processador para executar. Entre os dois compiladores, Dart Sass teve mediana de 223,775 ms e Less de 643,824 ms neste teste: Sass foi 2,88 vezes mais rápido no workload escolhido. Esse resultado não é ranking universal. Ele descreve 400 componentes gerados, versões fixadas, processos novos e uma Apple M4 Pro em 24 de agosto de 2026.
O ponto mais útil não é declarar vencedor. É publicar fonte, método, amostras e tamanhos para você reproduzir e decidir se a diferença importa no seu build.
O mesmo componente escrito nas três abordagens
Cada componente tem regra base, título BEM e hover. O CSS sem pré-processador é direto:
.component-1 {
color: #6d28d9;
padding: 1rem;
border: 1px solid #d1d5db;
}
.component-1__title { font-weight: 700; }
.component-1:hover { transform: translateY(-2px); }Sass usa variáveis, loop e nesting para gerar a mesma estrutura:
$brand: #6d28d9;
$border: #d1d5db;
@for $i from 1 through 400 {
.component-#{$i} {
color: $brand;
padding: 1rem;
border: 1px solid $border;
&__title { font-weight: 700; }
&:hover { transform: translateY(-2px); }
}
}Less usa variáveis, mixin recursivo e nesting:
@brand: #6d28d9;
@border: #d1d5db;
.generate(@i) when (@i > 0) {
.generate(@i - 1);
.component-@{i} {
color: @brand;
padding: 1rem;
border: 1px solid @border;
&__title { font-weight: 700; }
&:hover { transform: translateY(-2px); }
}
}
.generate(400);As entradas não têm o mesmo tamanho: SCSS e Less descrevem um gerador; CSS nativo contém 400 expansões. A saída, porém, representa o mesmo conjunto de 1.200 seletores. O benchmark mede um caso em que pré-processadores oferecem geração programática de verdade, não apenas trocam a sintaxe de um arquivo.
Essa escolha favorece a pergunta “quanto custa gerar?”. Ela não mede tempo de autoria, facilidade de revisão ou um projeto típico. Uma base com poucos loops e muitos imports pode ter perfil diferente.
O banco de teste: 400 componentes gerados por script
O gerador versionado em examples/sass-less-css-benchmark/generate.mjs gravou
três entradas. Para CSS, um loop JavaScript expandiu as regras antes da medição;
esse tempo ficou fora do benchmark porque, num repositório real, o CSS já estaria
versionado. O trecho central foi:
let css = '';
for (let i = 1; i <= 400; i += 1) {
css += `.component-${i} {\n`;
css += ` color: #6d28d9;\n`;
css += ` padding: 1rem;\n`;
css += ` border: 1px solid #d1d5db;\n}\n`;
css += `.component-${i}__title {\n font-weight: 700;\n}\n`;
css += `.component-${i}:hover {\n transform: translateY(-2px);\n}\n`;
}
writeFileSync('bench.css', css);O ambiente foi registrado com comandos simples:
node --version
npx --yes sass@1.103.1 --version
npx --yes --package less@4.9.0 lessc --version
uname -sm
sysctl -n machdep.cpu.brand_stringOs pacotes já estavam no cache do npm. Antes de medir, o script executou uma
compilação de aquecimento por ferramenta. Depois chamou diretamente cada binário,
evitando que resolução do npx contaminasse a amostra.
Tempo de compilação medido: Sass, Less e nenhum
Foram 15 execuções por opção. A ordem girou entre três sequências para reduzir
viés térmico ou de posição. Cada amostra abriu um processo novo com spawnSync;
portanto, o número inclui startup da CLI e transformação. Para CSS, a tarefa foi
copiar o arquivo para o destino, pois não existe compilação.
O núcleo do medidor:
const orders = [
['sass', 'less', 'css'],
['less', 'css', 'sass'],
['css', 'sass', 'less'],
];
for (let round = 0; round < 15; round += 1) {
for (const name of orders[round % orders.length]) {
const start = performance.now();
jobs[name]();
samples[name].push(performance.now() - start);
}
}| opção | mediana | mínimo | máximo | interpretação |
|---|---|---|---|---|
| CSS, cópia | 0,332 ms | 0,294 ms | 0,580 ms | I/O, nenhuma transformação |
| Dart Sass 1.103.1 | 223,775 ms | 216,549 ms | 248,747 ms | processo novo + loop + emissão |
| Less 4.9.0 | 643,824 ms | 597,317 ms | 738,449 ms | processo novo + recursão + emissão |
O recorte timings do JSON bruto preservado pelo script foi:
{
"sass": { "medianMs": 223.775, "minMs": 216.549, "maxMs": 248.747 },
"less": { "medianMs": 643.824, "minMs": 597.317, "maxMs": 738.449 },
"css": { "medianMs": 0.332, "minMs": 0.294, "maxMs": 0.580 }
}O resultado diverge dos números sugeridos no plano editorial anterior, que colocavam Less à frente. Foi exatamente por isso que a medição foi refeita. A versão atual, o desenho recursivo do Less e o hardware alteram o perfil. Números sem fonte executável envelhecem; método reproduzível continua útil.
Há quatro limites importantes. Primeiro, o Sass usa @for, enquanto Less usa
recursão porque essa é uma forma idiomática de expressar a faixa na linguagem;
o custo do algoritmo faz parte do caso, mas não representa toda folha Less.
Segundo, cada amostra abre processo. Uma integração por API JavaScript ou um
servidor persistente pode reduzir startup. Terceiro, não foram ativados
sourcemaps, minificação nem plugins. Quarto, uma única máquina não descreve CI
Linux, notebook Intel ou runner com CPU compartilhada.
Por isso a tabela não oferece intervalo de confiança sofisticado nem casas decimais como promessa. Os três dígitos apenas preservam a leitura do relógio. A variação interna já aparece em mínimo e máximo: Less oscilou mais de 140 ms, Sass cerca de 32 ms e a cópia menos de 0,3 ms na segunda rodada. Mediana foi escolhida porque resiste melhor a uma amostra isolada lenta.
Um estudo para decisão de migração deveria acrescentar pelo menos três workloads: o projeto completo, uma alteração em folha raiz e uma alteração em partial profundo no modo watch. Também deveria rodar no CI usado pela equipe. O microteste serve para verificar uma hipótese controlada, não para substituir o perfil do produto.
Tamanho do CSS gerado: bruto e depois do gzip
O script leu bytes do filesystem e aplicou gzip nível 9 em memória. Também contou quebras de linha:
const content = readFileSync(file);
result[name] = {
bytes: statSync(file).size,
gzip9Bytes: gzipSync(content, { level: 9 }).byteLength,
lines: content.toString('utf8').split('\n').length - 1,
};| saída | bytes brutos | gzip -9 | linhas | seletores |
|---|---|---|---|---|
| Sass | 74.075 | 3.661 | 4.799 | 1.200 |
| Less | 73.676 | 3.663 | 4.400 | 1.200 |
| CSS nativo | 73.676 | 3.663 | 4.400 | 1.200 |
Sass adicionou uma linha em branco entre os 400 componentes: 399 bytes e 399 linhas a mais, aumento bruto de 0,542%. O gzip absorveu a repetição e o arquivo Sass terminou dois bytes menor — variação irrelevante. Less e CSS tiveram o mesmo SHA-256, portanto foram byte a byte iguais. Sass teve outro hash somente pela formatação. Depois de executar a reprodução ao fim do artigo no mesmo shell, conferi os arquivos temporários com:
(
cd "$BENCH_WORK"
wc -l -c sass.css less.css native.css
shasum -a 256 sass.css less.css native.css
)e3ca73a93529ec08beca6cd5fa415f67d84a35d9f8a4d0e508e4beae60e79aad sass.css 063ad0d3c4b43fb280b2021bac6e4dddc0d9b16349f19349a720fc8da333ddcd less.css 063ad0d3c4b43fb280b2021bac6e4dddc0d9b16349f19349a720fc8da333ddcd native.css
Comparar bytes sem olhar a semântica seria enganoso. Os três publicaram os mesmos seletores e declarações; a diferença Sass era espaço em branco. Em produção, minificação anterior ao gzip provavelmente elimina até isso.
Equivalência foi conferida de duas formas. O hash provou identidade entre Less e CSS. Para Sass, um diff mostrou apenas linhas vazias entre o hover de um componente e a regra base do próximo. Não houve valor reformatado, seletor reordenado nem unidade convertida. Em benchmark de cores ou matemática, esse passo precisa ser mais rigoroso: saída parecida pode ter valores numericamente diferentes.
Tamanho do fonte também conta para revisão. SCSS com 241 bytes e Less com 278 bytes expressaram uma regra geradora que o CSS expandido levou 73.676 bytes para registrar. Isso não torna os primeiros automaticamente melhores; torna a fonte mais abstrata. Uma alteração de uma linha tem alcance muito maior, exigindo teste da saída. CSS explícito é volumoso, mas cada regra é local e pesquisável. A escolha troca concisão por transparência, além de trocar tempo de compilação.
O que só o pré-processador faz em 2026
O benchmark usa geração de 400 famílias. Sass e Less transformam uma entrada de poucas linhas em uma API CSS inteira sem script separado. Sass oferece maps, loops, funções, mixins e módulos com namespace. Less oferece mixins, guards, avaliação preguiçosa e mecanismos presentes em bases históricas.
O ganho é legítimo quando a coleção é fonte de verdade de um design system.
Alterar $brand ou @brand recompila todos os componentes. Adicionar um item ao
map pode gerar documentação e classes de forma consistente. O custo aparece
quando a geração publica combinações que ninguém usa ou esconde seletores
difíceis de depurar.
No Sass, maps e loops oferecem uma forma mais expressiva que um loop numérico fixo. No Less, o padrão recursivo testado representa código que ainda aparece em bibliotecas antigas. A diferença de velocidade não autoriza migrar uma base sem medir o custo de comportamento.
O que o CSS nativo tomou de volta: nesting, var() e color-mix()
O CSS entregue ao navegador já possui custom properties, nesting, cálculo fluido, funções de cor, layers, container queries e mais. Uma página que precisa somente agrupar um hover e trocar tema não precisa gerar linguagem intermediária:
:root { --brand: #6d28d9; }
[data-theme="dark"] { --brand: #a78bfa; }
.button {
background: var(--brand);
&:hover {
background: color-mix(in srgb, var(--brand), black 15%);
}
}A cor permanece dinâmica no navegador; nenhuma recompilação é necessária para
trocar data-theme.
Essa capacidade é diferente de gerar 400 seletores. CSS nativo substitui muitos usos cotidianos de Sass, não todos os fluxos de build. Pergunte se o valor precisa responder ao DOM ou se a folha precisa ser produzida a partir de dados conhecidos.
O custo escondido: watch e o tempo entre Ctrl+S e a tela
O benchmark mede processo novo, como um build isolado. Desenvolvimento costuma usar watch, que mantém estado e recompila só após mudança. Startup deixa de dominar; tamanho do grafo de dependências e granularidade da alteração ganham peso.
sass --watch src/scss:public/css
less-watch-compiler src/less public/cssEsses comandos representam fluxos diferentes; o segundo depende de ferramenta
adicional, não do lessc puro. Para medir experiência real, automatize uma edição idêntica, capture o horário
do save e o momento em que o arquivo muda. Rode pelo menos 15 vezes, reporte
mediana e piores casos e mantenha plugins do projeto. Não extrapole os 223,775 ms
de processo novo para hot reload.
Um atraso de 400 ms pode incomodar em cada salvamento e ser irrelevante num build de CI de oito minutos. Antes de trocar ferramenta, encontre a fatia dominante no perfil total.
Ant Design e a razão pela qual Less não morreu
Ant Design 4 documenta variáveis Less e modifyVars para personalizar cor,
raio e outros tokens. Produtos nessa versão possuem tema, loader e overrides
acoplados à linguagem. Bootstrap 3 e sistemas internos antigos criam o mesmo
efeito: Less continua porque remover exige comparar milhares de regras e estados.
Ant Design 5 não sustenta a tese de dependência eterna: a versão atual usa tokens e CSS-in-JS. O legado, porém, não migra sozinho quando a documentação muda. A pergunta correta é “quanto custa atualizar o produto e preservar o tema?”, não “qual compilador venceu 420 ms?”.
A aula de Less em projeto legado
mostra modifyVars, divisão e erros atuais. Se a base está estável, manter Less
com versão fixada pode ser decisão mais segura que converter por moda.
A tabela de decisão: qual escolher em cada cenário
| cenário | escolha inicial | por quê | o que medir |
|---|---|---|---|
| página pequena, navegadores modernos | CSS nativo | zero linguagem extra e valores em runtime | compatibilidade e tamanho final |
| design system que gera famílias de tokens | Sass | maps, loops, funções e módulos | regras geradas, watch e API pública |
| Ant Design 4 ou Less consolidado | manter Less | menor risco de regressão | build, tema, cobertura visual |
| aplicação migrando de legado | fatia piloto | decisão depende de comportamento | diff CSS, screenshots e esforço |
| build lento com Sass | perfilar primeiro | compilador pode não ser gargalo | cold build, incremental e plugins |
| tema dinâmico | CSS custom properties | responde à cascata e ao DOM | contraste, fallback e hidratação |
Velocidade isolada não escolhe arquitetura. Sass foi mais rápido que Less neste loop, mas migrar milhares de linhas pode consumir meses e introduzir regressão. CSS evitou compilação, porém escrever manualmente uma escala gerada pode criar outra fonte de verdade. O problema concreto decide qual custo vale pagar.
Reproduzir a medição na sua máquina em cinco minutos
Na raiz blog/, fixe versões, gere as entradas dentro de um diretório temporário,
aqueça cada comando e rode amostras alternadas. O npx resolve os pacotes antes
de iniciar o medidor; bench.mjs chama sass e lessc diretamente pelo PATH
injetado, então a resolução não entra nas 15 amostras.
BENCH_WORK=$(mktemp -d)
node examples/sass-less-css-benchmark/generate.mjs "$BENCH_WORK"
npx --yes --package sass@1.103.1 --package less@4.9.0 -- \
node examples/sass-less-css-benchmark/bench.mjs "$BENCH_WORK" 15 >/dev/null
node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));for(const k of ["sass","less","css"]){const t=r.timings[k],a=r.artifacts[k];console.log(`${k}: mediana ${t.medianMs} ms | ${a.bytes} bytes | gzip ${a.gzip9Bytes} | ${a.selectors} seletores | ${a.sha256}`)}' \
"$BENCH_WORK/benchmark.json"Além do resumo acima, o script grava benchmark.json, sass.css, less.css e
native.css no diretório temporário. O JSON conserva máquina, versões e as 15
amostras de cada opção para a auditoria não depender apenas da mediana.
Depois mude uma variável de cada fonte e confirme que os 1.200 seletores continuam equivalentes. Acrescente o workload do seu projeto: imports, mapas, plugins e sourcemaps. Publique resultados que possam ser contestados e repetidos.
Guarde o script junto do resultado e imprima versões dentro da própria execução. Um JSON sem commit do gerador não permite saber se “400 componentes” continuou significando a mesma coisa. Se atualizar Sass ou Less, rode novamente em vez de editar somente a versão no texto. Mudanças de runtime e do sistema operacional também merecem nova linha de histórico.
Ao comunicar o resultado para o time, separe fato e decisão. Fato: neste teste, Sass teve mediana 223,775 ms e Less 643,824 ms. Inferência: a recursão e o startup contribuíram para a diferença. Decisão possível: não fazer nada, porque o build real é aceitável; otimizar o loop; ou testar uma migração. Essa separação impede que um número verdadeiro sustente uma recomendação que ele sozinho não prova.
Para decidir além do cronômetro, use o guia completo de Sass e compare o que a plataforma já resolve. O benchmark é uma evidência localizada; a escolha correta continua sendo a que deixa o fluxo explicável, reproduzível e seguro para a base real.
Perguntas frequentes
Sass é sempre mais rápido que Less?
CSS nativo tem tempo de compilação zero?
O menor tempo deve decidir a ferramenta?
Os três arquivos geraram o mesmo CSS?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Apple M4 Pro (Darwin arm64), Node 26.3.0, Dart Sass 1.103.1, Less 4.9.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- Sass — Command-Line Interface — sass-lang.com
- Less — Using Less.js — lesscss.org
- MDN — CSS nesting — developer.mozilla.org
- Node.js — Performance measurement APIs — nodejs.org


