Tailwind vs CSS puro e CSS Modules: qual gera menos CSS
A mesma página portada três vezes e compilada: bytes de CSS, número de regras e tempo de build na mesma máquina, mais a tabela de quando usar cada um.
No experimento pequeno desta comparação, CSS puro gerou menos CSS: 657 bytes contra 720 de CSS Modules e 12.102 de Tailwind completo. Isso não encerra a discussão. O caso tem um cartão e, por isso, o piso de tema e Preflight domina o Tailwind; a curva pode mudar quando dezenas de telas reutilizam as mesmas utilities.
A resposta útil não é “qual framework vence”, mas qual custo você está medindo: artefato, escopo, remoção, velocidade de alteração, consistência ou integração.
O mesmo cartão e a fronteira do teste
As três versões renderizam um cartão centralizado da clínica, com título, parágrafo e botão. Mesmos valores de cor, tamanho, padding, raio e foco. Não é uma landing page de 14 seções porque eu não poderia validar visualmente essa extensão neste ambiente; reduzi o ângulo a um componente controlado e declarei a limitação.
Tailwind:
<main class="grid min-h-screen place-items-center bg-slate-50 p-8">
<article class="flex w-full max-w-lg flex-col gap-4 rounded-xl bg-white p-6 shadow-lg">
<h2 class="text-3xl font-bold text-slate-900">Agenda</h2>
<p class="leading-7 text-slate-600">Três consultas confirmadas.</p>
<button class="rounded-lg bg-cyan-600 px-4 py-2 font-bold text-white hover:bg-cyan-700">
Abrir agenda
</button>
</article>
</main>CSS puro:
.painel { display:grid; min-height:100vh; place-items:center; padding:2rem; background:#f8fafc; }
.cartao { display:flex; flex-direction:column; gap:1rem; width:min(100%,32rem); padding:1.5rem; }
.acao { border:0; border-radius:.5rem; padding:.5rem 1rem; background:#0891b2; color:#fff; }
.acao:hover { background:#0e7490; }CSS Modules usa as mesmas declarações, importadas como objeto de classes:
import s from './styles.module.css';
document.querySelector('#app').innerHTML =
`<main class="${s.painel}"><article class="${s.cartao}">...</article></main>`;Metodologia reproduzível
Usei Node 26.3.0. Tailwind 4.3.3 compilou CSS com fonte explícita. Vite 8.2.2 construiu as três variantes para produção. Depois um script Node leu o único asset CSS de cada pasta e calculou bytes crus, gzip nível 9, Brotli e quantidade de blocos por chaves.
npx @tailwindcss/cli -i styles.css -o compiled.css --minify
npx vite build bench-tailwind
npx vite build bench-puro
npx vite build bench-modulos
node measure.mjsAs fontes e saídas ficaram no mesmo diretório temporário, no mesmo volume e na mesma execução. Não houve download de fonte, imagem ou CSS externo.
const bruto = readFileSync(arquivoCss);
console.log(bruto.length);
console.log(gzipSync(bruto, { level: 9 }).length);
console.log(brotliCompressSync(bruto).length);Contagem de “blocos” inclui regras e at-rules com chaves; não equivale a número de seletores simples. O método serve para comparar os três artefatos medidos, não para afirmar complexidade cognitiva.
Bytes cru, gzip e Brotli
Os resultados foram:
| variante | CSS cru | gzip -9 | Brotli | blocos contados |
|---|---|---|---|---|
| Tailwind completo | 12.102 B | 3.489 B | 3.041 B | 166 |
| CSS puro | 657 B | 379 B | 290 B | 9 |
| CSS Modules | 720 B | 391 B | 306 B | 9 |
Tailwind inclui Preflight, tokens de tema e propriedades auxiliares. Para medir
utilities sem Preflight, compilei theme e utilities; ainda foram 8.374 bytes
crus e 2.537 gzip, porque tokens usados e infraestrutura continuam presentes.
@import "tailwindcss/theme";
@import "tailwindcss/utilities";
@source "./main.js";Remover Preflight muda a base visual e não é otimização gratuita. Você precisa substituir normalização e verificar controles, mídia e tipografia.
Por que o cartão favorece CSS manual
No CSS puro, cada declaração aparece uma vez e atende exatamente o exemplo. Não há paleta, escala tipográfica ou propriedades preparadas para outros estados. Esse minimalismo é vantagem real num artefato pequeno e estável.
Tailwind paga um piso para oferecer uma API coerente. O mesmo p-6 usado em cem
cartões continua uma regra CSS. Contudo, o HTML repete a string, e o documento
também trafega bytes. Compressão reduz repetição, mas não zera. Uma comparação
de aplicação precisa somar HTML ou JavaScript, CSS e cache, não apenas a folha.
CSS Modules adicionou 63 bytes sobre o CSS puro neste teste, principalmente por
transformação de escopo. Seu benefício é evitar colisão entre .cartao de dois
componentes, não gerar o menor arquivo possível.
Quando a página dobra
Duplicar o mesmo componente sem introduzir classes novas não aumenta as regras Tailwind; também não aumenta CSS puro se o seletor é reutilizado. A diferença surge quando a segunda página pede variações.
<article class="rounded-xl bg-white p-6">...</article>
<article class="rounded-xl bg-slate-900 p-8 text-white">...</article>Tailwind adiciona somente utilities ainda ausentes. CSS puro adiciona ou amplia seletores conforme a arquitetura. Um sistema bem desenhado pode reutilizar tokens e modificadores; um sistema apressado duplica blocos. Ferramenta não determina disciplina.
Para medir curva, gere versões com 1, 10 e 100 componentes representativos, mantendo conteúdo e estados. Separe crescimento do HTML e do CSS. O número desta página não responde essa pergunta; ele estabelece um baseline honesto.
for quantidade in 1 10 100; do
gerar-casos "$quantidade"
medir-assets "$quantidade"
doneEsse pseudocomando descreve a próxima medição; não há saída alegada para ele. Num experimento real, versione o gerador para que as três implementações recebam o mesmo conteúdo e as mesmas variações.
Tempo de build foi observado, não tratado como benchmark
As saídas individuais mostraram 45ms no CLI e builds Vite de 68ms, 32ms e 53ms em uma execução. Não apresento isso como ranking. Uma amostra única sofre com cache, aquecimento de processo e agendamento do sistema.
Para comparar tempo de verdade, rode dezenas de iterações, descarte aquecimento, registre mediana e percentis e teste rebuild em watch. O título desta página promete bytes e regras; tempo aparece como limite metodológico, não como conclusão inflada.
O que números de bundle não medem
Eles não medem quanto tempo uma pessoa leva para localizar estilo, remover uma tela, revisar um token ou impedir uma cor fora do sistema. Também não medem qualidade semântica e acessibilidade. É possível produzir um bundle mínimo com foco invisível e contraste ruim.
Tailwind aproxima estilo do componente e oferece escala compartilhada. CSS puro permite qualquer recurso nativo sem traduzir para uma classe. Modules fornece escopo local com CSS familiar. Cada ganho responde a uma dor diferente.
O custo de dependência também importa. Tailwind adiciona uma etapa de build e uma versão a manter. CSS puro ainda passa por bundler em muitos projetos, mas pode funcionar sem ele. Modules depende da transformação do framework. Em uma página estática duradoura, menos pipeline pode valer mais que uma API extensa.
Cascata e override
CSS puro dá controle direto de seletor, camada e ordem. Modules reduz colisões de nome, mas a cascata ainda existe. Tailwind organiza utilities em layers e mantém especificidade baixa; duas classes conflitantes ainda dependem da ordem gerada.
<div class="p-2 p-8">...</div>Não conte com a posição no atributo. Em API React que aceita override,
tailwind-merge pode reduzir o conflito para p-8, como mostra a
lição de componentes Tailwind.
No CSS puro, uma convenção de layers e escopo cumpre papel equivalente em outro
nível.
O custo humano de apagar estilo
Tailwind detecta uso por texto. Quando uma classe desaparece de todas as fontes, a regra deixa o bundle. CSS puro e Modules dependem de o seletor ser removido; bundlers não conseguem provar com segurança que todo CSS global está morto.
Por outro lado, uma utility pode estar numa string estática reservada para um estado raro e continuar gerada mesmo sem aparecer na sessão de teste. CSS Modules cria vínculo de import mais explícito, mas um módulo inteiro importado continua no grafo. Em todos os casos, cobertura de CSS é indício, não verdade absoluta.
const tons = {
sucesso: 'bg-emerald-600',
erro: 'bg-rose-600',
};Manter opções estáticas é necessário para detecção; remova variantes que o produto abandonou. Uma API de componente pequena ajuda mais que qualquer minificador.
Como projetar uma comparação maior sem favorecer uma opção
O próximo experimento deve começar por uma especificação de interface, não pelo código de uma das variantes. Liste as mesmas quatorze seções, os mesmos estados, breakpoints, tokens e critérios de acessibilidade. Defina também quais recursos ficam fora. Se a versão Tailwind usa dark mode e a versão CSS não, bytes deixam de comparar a mesma entrega.
Escolha conteúdo congelado. Títulos, imagens e mensagens de erro precisam ser idênticos, porque uma linha extra pode ativar layout e utilidades diferentes. Use assets locais e desative downloads durante o build. Fixe versões no lockfile e registre sistema operacional e arquitetura da máquina.
Separe três conjuntos de métricas. O primeiro é transferência: CSS, HTML ou JavaScript, gzip, Brotli e cache entre páginas. O segundo é build: primeira compilação, rebuild, uso de memória e variância em várias execuções. O terceiro é manutenção: tempo para alterar tema, remover seção, adicionar estado e localizar regra vencedora. Misturar os três num único placar esconde o trade-off.
Para tempo, aqueça as ferramentas e execute uma quantidade suficiente para calcular mediana e percentis. Uma média pode ser puxada por um processo do sistema. Rebuild precisa manter o watcher vivo; iniciar o processo toda vez mede startup, não feedback durante desenvolvimento. Rode as alternativas em ordem alternada para reduzir efeito de cache e temperatura.
Para tamanho, compare artefatos servidos. Source map não entra no payload de produção se não é publicado; não some por acidente. CSS crítico inline precisa entrar no HTML. Classe repetida em JSX entra no JavaScript transformado. O cache compartilhado entre rotas pode favorecer uma folha global, enquanto CSS por rota reduz primeiro acesso. Registre os dois cenários.
Para manutenção, use tarefas cegas com critérios claros. “Trocar a ação primária e garantir contraste nos dois temas” é melhor que “editar o botão”. Conte tempo, arquivos tocados, regressões e perguntas necessárias. Não peça a uma pessoa especialista em Tailwind para representar CSS Modules e trate a diferença como propriedade da ferramenta; familiaridade é uma variável do estudo.
Faça revisão cruzada sem dizer qual resultado você espera. Uma implementação pode ficar artificialmente ruim porque tenta imitar a arquitetura da outra. CSS puro deve usar tokens e layers de forma competente; Modules deve aproveitar escopo; Tailwind deve usar componentes e tema. Comparar uma versão madura com duas versões ingênuas só mede experiência do autor.
Ao publicar resultados, mantenha as planilhas e scripts junto do commit. Informe mediana, dispersão e data. Uma atualização de bundler ou framework pode mudar os números. Conclusões precisam ser condicionais: “neste projeto, com estas fontes” em vez de “Tailwind sempre”.
Curva de crescimento e ponto de cruzamento
O cartão medido não encontra um ponto de cruzamento; ele só revela o piso. Para estudar crescimento, adicione uma seção por rodada e registre o delta, não apenas o total. Uma nova seção que reutiliza todas as utilities pode aumentar markup sem aumentar CSS Tailwind. Uma seção que introduz animação, filtro e nova paleta ativa infraestrutura maior.
CSS puro também tem degraus. Uma seção pode reutilizar .cartao e acrescentar
quase nada, ou exigir um bloco inteiro de seletores. Modules pode duplicar
declarações em módulos separados se o time não extrai tokens e primitivas. Por
isso, ajuste uma linha à curva e inspecione os saltos; um único total final
esconde onde cada abordagem ficou cara.
Observe ainda a remoção. Apague a terceira, a sétima e a décima quarta seção em rodadas separadas. Tailwind deixa de gerar utilities sem outros usos. CSS manual precisa que o seletor morto seja removido. Modules pode sair do grafo quando o import desaparece. Esse teste mede reversibilidade, uma qualidade importante em produto que experimenta muito.
Não procure necessariamente um vencedor global. Pode existir uma faixa em que CSS puro é menor, outra em que as diferenças comprimidas ficam irrelevantes e uma em que o custo de manutenção domina. A decisão profissional escolhe a faixa do produto esperado e reconhece incerteza.
Tabela de decisão
| situação | Tailwind | CSS puro | CSS Modules |
|---|---|---|---|
| página pequena sem build | adiciona infraestrutura | escolha direta | geralmente indisponível sem bundler |
| app com muitos componentes | utilities e tokens escalam bem | exige convenção forte | escopo local ajuda |
| design muito experimental | valores arbitrários e CSS coexistem | liberdade total | liberdade com escopo |
| migração gradual | pode entrar por componente | já é a base | bom para preservar legado |
| biblioteca pública | exige contrato de fontes ou CSS pronto | interoperável | hashes e bundler exigem contrato |
| equipe iniciante em CSS | nomes ajudam, mas podem esconder mecanismo | ensina mecanismo diretamente | adiciona conceito de módulo |
Eu escolheria CSS puro para uma página pequena, estável e sem pipeline. Tailwind para uma aplicação de produto com identidade própria, muitos estados e equipe disposta a padronizar. Modules para uma base React existente que já usa escopo local e não ganha nada com migração total.
A lição de @apply mostra que sequer dentro do
Tailwind existe uma única forma de organizar. Medimos quatro botões e @apply
ficou 31 bytes gzip menor; a decisão continuou dependendo de fronteira e
manutenção.
Decisão prática, não torcida
Pegue uma tela representativa do seu produto, implemente um componente nas duas opções finalistas e meça bundle, tempo de alteração e clareza da revisão. Não porte apenas o botão mais simples nem a tela mais excepcional. Documente o que ficou fora.
Se Tailwind vencer, siga o guia completo e defina tokens antes de espalhar valores arbitrários. Se CSS comum vencer, mantenha uma arquitetura de cascade layers e tokens. Se Modules vencer, estabeleça como temas globais entram. O próximo passo é um commit com o experimento e os números, não uma escolha baseada em preferência de rede social.
Perguntas frequentes
Tailwind sempre gera mais CSS que CSS escrito à mão?
CSS Modules melhora o tamanho do CSS?
Posso combinar Tailwind e CSS Modules?
Tempo de build deve decidir a escolha?
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 @tailwindcss/cli 4.3.3, Vite 8.2.2, gzip -9 e Brotli do Node 26.3.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- Tailwind CSS — Compatibility — tailwindcss.com
- Vite — CSS Modules — vite.dev
- MDN — CSS cascade — developer.mozilla.org


