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