CSS-in-JS está morto? Análise do movimento de volta ao CSS puro
1. Contexto histórico: a ascensão e queda do CSS-in-JS
Há cerca de uma década, o desenvolvimento front-end enfrentava um problema crítico: o CSS tradicional, com seu escopo global e regras de especificidade imprevisíveis, tornava a manutenção de grandes SPAs um pesadelo. Uma simples mudança de estilo poderia quebrar componentes distantes sem aviso prévio. Foi nesse cenário que o CSS-in-JS emergiu como a solução salvadora.
Bibliotecas como Styled Components (2016), Emotion e JSS prometiam escopo verdadeiro, co-localização de estilos com componentes e eliminação de dead code. A adoção foi massiva — em 2019, praticamente todo novo projeto React usava alguma forma de CSS-in-JS. Parecia o futuro inevitável da estilização na web.
No entanto, os primeiros sinais de desgaste começaram a aparecer por volta de 2022. A comunidade passou a relatar cansaço com runtime overhead, complexidade em SSR e dificuldades de integração com ferramentas de build. O que antes parecia uma bala de prata começava a mostrar suas fissuras.
2. As promessas não cumpridas do CSS-in-JS
O maior mito do CSS-in-JS era sua suposta "performance nativa". Na prática, bibliotecas como Styled Components injetam estilos dinamicamente no runtime, o que significa:
// Exemplo de custo oculto do Styled Components
const Button = styled.button`
background: ${props => props.primary ? 'blue' : 'gray'};
padding: 12px 24px;
border-radius: 4px;
`;
Cada renderização deste componente exige:
1. Parsing do template literal
2. Geração de um hash único para a classe
3. Injeção do estilo no <head> via JavaScript
4. Hidratação no servidor (SSR) com duplicação de estilos
Em aplicações com centenas de componentes, esse custo se acumula. Estudos mostram que o CSS-in-JS pode adicionar de 10KB a 50KB ao bundle inicial, além de impactar negativamente o First Contentful Paint (FCP).
Para novos desenvolvedores, a curva de aprendizado é íngreme. É preciso entender não apenas CSS, mas também a API específica de cada biblioteca, os patterns de temas e a integração com TypeScript. Sem falar na dificuldade com ferramentas externas: linters CSS como Stylelint perdem eficácia, design systems precisam de adaptações complexas e a extração estática de estilos (critical CSS) se torna um desafio técnico.
3. O movimento de volta ao CSS puro: o que mudou?
A grande virada veio com a maturação do CSS moderno. Recursos que antes exigiam soluções complexas agora são nativos:
/* Escopo real com @scope (CSS 2024) */
@scope (.card) {
:scope {
border: 1px solid #ddd;
padding: 16px;
}
h2 {
font-size: 1.5rem;
color: #333;
}
}
/* Seletores poderosos sem classes extras */
.article:has(img) {
grid-template-columns: 1fr 300px;
}
.card:is(:hover, :focus-within) {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
CSS Modules resgatam o escopo sem runtime. Com a integração nativa em bundlers como Vite e Next.js, o uso é simples:
/* Button.module.css */
.button {
background: var(--color-primary);
padding: 12px 24px;
composes: rounded from './shared.module.css';
}
/* Componente React */
import styles from './Button.module.css';
export function Button({ children }) {
return <button className={styles.button}>{children}</button>;
}
CSS Layers (@layer) finalmente dão aos desenvolvedores controle declarativo sobre especificidade, eliminando a necessidade de hacks como !important ou namespacing manual.
4. Alternativas híbridas: o melhor dos dois mundos
O movimento de volta ao CSS puro não significa abandono total dos princípios que tornaram o CSS-in-JS popular. Novas ferramentas oferecem o melhor dos dois mundos:
CSS Modules + TypeScript proporciona tipagem segura sem runtime:
// types.module.css
export const button: string;
export const primary: string;
export const disabled: string;
Zero-runtime CSS-in-JS como Linaria e vanilla-extract compilam estilos em tempo de build:
// vanilla-extract example
import { style } from '@vanilla-extract/css';
export const button = style({
background: 'blue',
padding: '12px 24px',
'@media': {
'(max-width: 768px)': {
padding: '8px 16px'
}
}
});
Tailwind CSS provou que é possível ter produtividade com CSS puro como saída. Seu sistema utility-first gera arquivos CSS estáticos e otimizados, sem qualquer JavaScript em runtime.
5. Análise crítica: quando cada abordagem faz sentido
A escolha não é binária. Cada abordagem tem seu contexto ideal:
Projetos pequenos (landing pages, MVPs): CSS puro ou Tailwind são suficientes. A simplicidade reduz custos de manutenção.
Grandes sistemas de design: CSS Modules com tokens de design oferecem controle granular. Para times experientes, vanilla-extract ou Linaria fornecem tipagem sem overhead.
SPAs com interação pesada: CSS-in-JS com runtime ainda pode fazer sentido para estilos dinâmicos complexos, desde que otimizado com lazy loading e code splitting.
Aplicações SSR/SSG: CSS puro ou zero-runtime são obrigatórios. Next.js, por exemplo, recomenda oficialmente CSS Modules e Tailwind para Server Components.
Times em crescimento: CSS puro reduz a barreira de entrada. Novos desenvolvedores já conhecem CSS; ensinar Styled Components é um custo adicional.
6. O estado atual dos frameworks e bibliotecas
O ecossistema reflete claramente essa transição. Next.js 14+ recomenda CSS Modules e Tailwind como padrão, relegando CSS-in-JS a casos específicos. Styled Components e Emotion estão se adaptando ao React 19 e Server Components, mas com limitações — estilos dinâmicos em componentes servidores ainda são problemáticos.
Novos players estão surgindo com filosofias diferentes:
- Panda CSS: Gera estilos em tempo de build, combinando a sintaxe familiar do CSS-in-JS com saída estática
- StyleX (Meta): Focado em performance e previsibilidade, usado internamente no Facebook
- Open Props: Design tokens customizáveis que funcionam com CSS puro
7. Conclusão: CSS-in-JS está morto ou apenas evoluindo?
CSS-in-JS como o conhecemos — runtime pesado, injeção dinâmica de estilos, patterns inseguros — está morrendo, e isso é saudável para o ecossistema. O que permanece são os princípios que tornaram a abordagem valiosa: escopo verdadeiro, co-localização de estilos e eliminação de CSS morto.
Para 2025, a tendência é clara:
- CSS puro como padrão para a maioria dos projetos
- Ferramentas de compilação (zero-runtime) como complemento para casos que exigem tipagem e escopo avançados
- Tailwind e CSS Modules como as abordagens dominantes no ecossistema React
O CSS-in-JS não está morto — está evoluindo para formas mais maduras. O que morreu foi a ideia de que injetar estilos via JavaScript em runtime era a única solução para os problemas do CSS. Agora temos ferramentas melhores, e o CSS moderno finalmente está à altura dos desafios que enfrentamos.
Referências
- Styled Components Documentation — Documentação oficial da biblioteca que popularizou o CSS-in-JS no ecossistema React
- Next.js CSS Documentation — Guia oficial de estilização do Next.js, com recomendações atuais para CSS Modules e Tailwind
- Vanilla Extract Documentation — Documentação da biblioteca zero-runtime CSS-in-JS que combina tipagem TypeScript com saída estática
- MDN: @scope CSS Rule — Documentação oficial do recurso nativo de escopo CSS, parte da evolução moderna da linguagem
- Tailwind CSS Documentation — Documentação oficial do framework utility-first que provou a viabilidade do CSS puro em larga escala