Alternar tema

Boas práticas de Next.js com Tailwind CSS: da configuração ao modo escuro (2025)

Easton editorial illustration: hydration gauge console

Olhe para o className daquele componente de botão no VS Code: são 23 classes. De bg-blue-500 a dark:hover:bg-blue-800, tudo espremido em uma linha que cria uma barra de rolagem horizontal enorme. Um colega passou pela minha mesa, olhou para a tela e perguntou: “O que você escreveu aí?”

Na hora, eu nem soube responder. Usava Tailwind havia quase dois anos e ele realmente acelerava o trabalho, mas o código parecia cada vez mais indecifrável. Copiar e colar era rápido; alterar depois era um tormento. Para trocar o arredondamento de todos os botões, era preciso procurar rounded-lg em cada arquivo e editar um por um.

Esse incômodo não era só meu. Em 2025, o Tailwind CSS chegou ao v4 e o Next.js ao 15; configuração, modo escuro e otimização de desempenho mudaram bastante. No início, também fiquei perdido: onde estava o arquivo de configuração? Para onde foi darkMode: 'class'? Depois de enfrentar vários problemas, consegui entender o novo caminho.

Aqui estão as práticas que funcionaram em projetos reais: como controlar o excesso de classes, implementar o modo escuro sem remendos e reduzir o CSS de 500 KB para 50 KB. Se as classes do Tailwind também incomodam você ou se ainda existe dúvida sobre a atualização para o v4, os pontos abaixo ajudam a decidir.

O que mudou em 2025: Tailwind CSS v4 e Next.js 15

A maior mudança do v4 é que o arquivo de configuração deixou de ser obrigatório.

O conhecido tailwind.config.js passou a ser opcional. Quando vi isso pela primeira vez, achei que fosse brincadeira. Abri um projeto novo com Next.js 15.3 e o arquivo realmente não estava lá. A equipe do Tailwind chama a proposta de “configuração zero”: os arquivos do projeto são detectados automaticamente e tudo funciona de imediato.

Isso não impede personalizações. Pelo contrário: o v4 as leva para um lugar mais direto, o global.css. Cores do tema, espaçamento e fontes agora podem ser definidos com variáveis CSS:

@theme {
  --color-primary: #3b82f6;
  --color-secondary: #8b5cf6;
  --font-sans: 'Inter', sans-serif;
}

No começo, pensei que fosse um retrocesso. Depois de dois dias de uso, percebi que as alterações ficaram mais rápidas. Antes, mudar uma cor do tema exigia reiniciar o servidor e esperar a compilação; agora, a variável CSS é atualizada imediatamente. Designers também entendem a configuração sem precisar perguntar qual tonalidade representa blue-500.

Outro ganho concreto é a velocidade. O mecanismo do v4 foi reescrito em Rust. A equipe afirma que ele ficou cinco vezes mais rápido; em meu teste, a inicialização a frio caiu de 8 para menos de 2 segundos. Não é apenas uma pontuação de benchmark: ao iniciar o servidor várias vezes por dia, a diferença se acumula.

500 KB→50 KB
Otimização do tamanho do CSS
Redução de 90%, de 500 KB para 50 KB
5 vezes
Aumento na velocidade de compilação
O v4 foi reescrito em Rust e ficou cinco vezes mais rápido
8 s→2 s
Tempo de inicialização a frio
De 8 segundos para menos de 2 segundos

No Next.js 15, o App Router já era a configuração padrão. Com Tailwind, o isolamento de estilos em Server Components funciona bem e reduz a preocupação com conflitos. Só é preciso lembrar de adicionar 'use client' aos Client Components usados na troca de tema; sem isso, o modo escuro apresenta problemas, como veremos adiante.

Há também uma mudança pequena que passa despercebida: no v4, o valor padrão de border-color virou currentColor. Uma borda sem cor explícita passa a acompanhar a cor do texto. Ao migrar diretamente do v3, algumas bordas podem parecer ter desaparecido, quando na verdade assumiram a mesma cor do texto. Eu perdi bastante tempo até descobrir isso.

As mudanças do v4 são grandes, mas apontam para uma configuração mais rápida, simples e intuitiva. Depois do período inicial de adaptação, é difícil voltar ao modelo anterior.

Como resolver classes extensas com componentes

Voltando ao problema inicial: o que fazer com um botão que reúne mais de 20 classes?

A primeira reação de muita gente é usar @apply: mover as classes do Tailwind para um arquivo CSS e dar ao conjunto um nome como .btn-primary. Eu também fazia isso, até ler uma observação de Adam Wathan, criador do Tailwind, no Twitter: o uso intenso de @apply costuma indicar uma compreensão equivocada da proposta do Tailwind.

Parece uma crítica dura, mas faz sentido. @apply compila as classes utilitárias antecipadamente no CSS e reduz a vantagem da geração sob demanda. A suposta “abstração” pode apenas aumentar o pacote manualmente. Em um dos meus projetos, o uso excessivo de @apply fez o CSS de produção subir de 30 KB para 120 KB.

A abordagem mais adequada é encapsular os estilos em componentes.

As combinações recorrentes ficam em componentes React. A quantidade de classes continua igual, mas você as escreve apenas uma vez:

// ❌ Antes: repetido em todos os lugares
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg shadow-md transition duration-200">
  Enviar
</button>

// ✅ Agora: encapsulado em um componente
<Button variant="primary">Enviar</Button>

O componente Button ainda contém as classes, mas os demais arquivos ficam limpos. Para alterar todos os botões, basta editar um único lugar.

Ainda existe a questão dos estados: principal, secundário, ação destrutiva e assim por diante. Criar um componente para cada um não é necessário. É aí que entra o cva (class-variance-authority).

Essa biblioteca gerencia variantes de componentes e funciona muito bem com Tailwind:

import { cva, type VariantProps } from 'class-variance-authority'

const buttonStyles = cva(
  // Estilos básicos
  'font-bold rounded-lg transition duration-200',
  {
    variants: {
      variant: {
        primary: 'bg-blue-500 hover:bg-blue-700 text-white',
        secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-800',
        danger: 'bg-red-500 hover:bg-red-700 text-white'
      },
      size: {
        sm: 'py-1 px-3 text-sm',
        md: 'py-2 px-4',
        lg: 'py-3 px-6 text-lg'
      }
    },
    defaultVariants: {
      variant: 'primary',
      size: 'md'
    }
  }
)

export function Button({
  variant,
  size,
  children,
  ...props
}: VariantProps<typeof buttonStyles> & React.ButtonHTMLAttributes<HTMLButtonElement>) {
  return (
    <button className={buttonStyles({ variant, size })} {...props}>
      {children}
    </button>
  )
}

O uso fica bem mais simples:

<Button variant="primary">Salvar</Button>
<Button variant="danger" size="lg">Excluir</Button>
<Button variant="secondary" size="sm">Cancelar</Button>

O TypeScript ainda valida os valores: um nome de variante incorreto gera erro imediatamente. O shadcn/ui adota essa abordagem, e isso ajuda a manter o código de seus componentes organizado.

@apply não é completamente proibido. Ao sobrescrever estilos de uma biblioteca de terceiros, talvez não exista um componente que possa ser encapsulado; nesse caso, usá-lo pontualmente não é um problema. Para componentes próprios, prefira a composição em React.

Tema personalizado: construindo um design system

Depois de encapsular os componentes, surge outro problema: como manter o design consistente em todo o projeto?

Eu já trabalhei em projetos com cinco ou seis tons de azul: blue-400, blue-500, #3B82F6, rgb(59, 130, 246) e outros. A pergunta inevitável do designer era qual padrão estava sendo seguido. Um design system resolve isso ao definir cores, tipografia e espaçamento em um único lugar.

No v4, o @theme simplifica esse trabalho:

/* app/globals.css */
@import 'tailwindcss';

@theme {
  /* Cores da marca */
  --color-brand-primary: #3b82f6;
  --color-brand-secondary: #8b5cf6;

  /* Cores semânticas */
  --color-success: #10b981;
  --color-warning: #f59e0b;
  --color-error: #ef4444;

  /* Cores neutras, da mais clara à mais escura */
  --color-neutral-50: #f9fafb;
  --color-neutral-100: #f3f4f6;
  --color-neutral-500: #6b7280;
  --color-neutral-900: #111827;

  /* Famílias de fontes */
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-mono: 'Fira Code', monospace;

  /* Espaçamento: o layout usa uma grade de 8 px */
  --spacing-unit: 0.5rem; /* 8px */

  /* Raios */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 1rem;
}

Depois, use as variáveis diretamente nas classes do Tailwind:

<div className="bg-brand-primary text-neutral-50 rounded-md">
  Fundo com a cor do tema
</div>

Observe que usei bg-brand-primary, não bg-blue-500. Para mudar a cor da marca, altere uma variável e todo o site acompanha, sem procurar centenas de ocorrências.

Se você ainda quiser manter o arquivo de configuração do v3, também pode criar tailwind.config.ts:

import type { Config } from 'tailwindcss'

export default {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      // Estenda o tema padrão, que é a opção recomendada
      colors: {
        brand: {
          primary: '#3b82f6',
          secondary: '#8b5cf6',
        },
      },
      fontFamily: {
        sans: ['Inter', 'sans-serif'],
      },
    },
  },
} satisfies Config

extend é importante. Ao escrever diretamente em theme.colors, você substitui todas as cores padrão do Tailwind e classes como bg-red-500 deixam de existir. extend acrescenta valores em vez de substituir.

Outra técnica é combinar variáveis CSS com a configuração do Tailwind para permitir a troca de tema em tempo de execução:

:root {
  --color-primary: #3b82f6;
}

[data-theme='purple'] {
  --color-primary: #8b5cf6;
}
// tailwind.config.ts
colors: {
  primary: 'var(--color-primary)',
}

Assim, a troca de tema exige apenas a alteração de um atributo no DOM, sem recompilar o CSS. Isso é especialmente útil em produtos SaaS que permitem ao usuário escolher a cor do tema.

Com o design system pronto, a colaboração também melhora. Uma pessoa nova na equipe olha para globals.css e entende quais cores deve usar, sem escolhas arbitrárias.

Modo escuro sem flashes

O modo escuro foi a parte que mais me deu trabalho.

Na primeira implementação, segui um tutorial do v3. Ao clicar no botão, a página piscava em branco antes de escurecer. Usuários reclamaram do clarão. Era um flash of unstyled content causado pela diferença entre a renderização do servidor e a do cliente no Next.js.

No v4, darkMode: 'class' não é mais necessário: a estratégia por classe já é o padrão. O flash, porém, ainda precisa ser tratado, e a biblioteca next-themes ajuda nisso.

Instale-a:

npm install next-themes

Em seguida, envolva o layout raiz com ThemeProvider:

// app/layout.tsx
import { ThemeProvider } from 'next-themes'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="pt-BR" suppressHydrationWarning>
      <body>
        <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}

suppressHydrationWarning é indispensável. O next-themes adiciona class="dark" ao <html> no cliente, criando uma diferença em relação ao conteúdo renderizado pelo servidor. Esse atributo evita o aviso do React.

O botão de troca deve ficar em um Client Component:

'use client'

import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'

export function ThemeToggle() {
  const [mounted, setMounted] = useState(false)
  const { theme, setTheme } = useTheme()

  useEffect(() => setMounted(true), [])

  if (!mounted) return null // Evita diferenças na renderização do servidor

  return (
    <button
      onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
      className="rounded-lg p-2 hover:bg-neutral-100 dark:hover:bg-neutral-800"
    >
      {theme === 'dark' ? '🌞' : '🌙'}
    </button>
  )
}

As variáveis CSS também precisam refletir o modo escuro:

@theme {
  --color-bg-primary: #ffffff;
  --color-text-primary: #111827;
}

.dark {
  --color-bg-primary: #111827;
  --color-text-primary: #f9fafb;
}

Outra opção é usar o prefixo dark: nas classes:

<div className="bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-50">
  Conteúdo adaptado ao modo escuro
</div>

No design das cores, o modo escuro não é apenas uma inversão de preto e branco. Preto puro (#000000) é agressivo; prefira cinza-escuro (#111827 ou #1a1a1a). Branco puro também fica intenso demais, então #f9fafb costuma ser mais confortável. As sombras precisam de tratamento próprio porque perdem profundidade no fundo escuro:

// Modo claro: sombra projetada para baixo
<div className="shadow-lg dark:shadow-none dark:ring-1 dark:ring-neutral-800">

No modo escuro, um ring costuma separar os elementos melhor que uma sombra.

Imagens também podem parecer claras demais. Um filtro reduz discretamente o brilho:

.dark img {
  filter: brightness(0.9);
}

Esses detalhes tornam o modo escuro realmente confortável, em vez de apenas funcional.

Desempenho: deixando o CSS menor e mais rápido

A redução de 500 KB para 50 KB mencionada no início veio de um projeto real.

No v4, o modo JIT já fica ativo por padrão e gera estilos sob demanda. Ainda há espaço para otimização, principalmente no escaneamento de conteúdo.

Muita gente usa uma configuração ampla demais:

// ❌ Área de escaneamento muito ampla
content: [
  './**/*.{js,ts,jsx,tsx}',
]

Isso varre o projeto inteiro, incluindo node_modules e arquivos gerados em .next, desperdiçando tempo. Seja mais preciso:

// ✅ Escaneie somente os diretórios necessários
content: [
  './app/**/*.{js,ts,jsx,tsx,mdx}',
  './components/**/*.{js,ts,jsx,tsx}',
  './lib/**/*.{js,ts}',
]

Em meu teste, essa mudança deixou a inicialização do servidor de desenvolvimento 40% mais rápida.

Outro problema comum são classes dinâmicas:

// ❌ Estas classes serão removidas
const colors = ['red', 'blue', 'green']
<div className={`bg-${colors[0]}-500`}>

O Tailwind não encontra o texto completo bg-red-500; na build de produção, a classe é removida e o estilo desaparece. Escreva os nomes completos:

// ✅ Use nomes de classe completos
const colorMap = {
  red: 'bg-red-500',
  blue: 'bg-blue-500',
  green: 'bg-green-500',
}
<div className={colorMap[color]}>

Ou preserve as classes com uma safelist:

// tailwind.config.ts
safelist: [
  {
    pattern: /bg-(red|blue|green)-500/,
  },
]

Não exagere na safelist, pois muitas entradas voltam a aumentar o CSS. Prefira nomes completos no código e recorra à lista apenas quando necessário.

O v4 inclui minificação de CSS na build de produção. Para avançar na otimização, também é possível configurar cssnano:

// postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
  },
}

Também vale monitorar o tamanho dos pacotes. Eu uso @next/bundle-analyzer periodicamente:

npm install --save-dev @next/bundle-analyzer
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

module.exports = withBundleAnalyzer({
  // Outras configurações
})

Execute ANALYZE=true npm run build para gerar um relatório visual e identificar rapidamente os pacotes maiores.

O caso da Netflix é interessante: o CSS da página Top 10 tinha apenas 6,5 KB. O princípio era reduzir ao essencial e manter somente os estilos realmente usados. Não precisamos chegar ao mesmo extremo, mas a ideia é útil: incluir apenas o necessário.

Em uma revisão de código, encontrei uma importação de todo o @heroicons/react para usar apenas dois ícones. A importação sob demanda reduziu o pacote em 200 KB. É uma mudança pequena, mas várias mudanças assim produzem um ganho relevante.

Otimização não é uma tarefa feita uma única vez. Execute a análise de bundle antes das versões e transforme isso em hábito para evitar que o CSS cresça sem controle.

Migrando do v3 para o v4 com segurança

Se você ainda usa o v3, o momento certo para atualizar depende do projeto.

Em projetos novos, começar pelo v4 faz sentido. Em projetos existentes, avalie o custo das mudanças. O v4 inclui alterações incompatíveis e a migração não termina com um simples npm install.

A principal mudança está na configuração. O conteúdo de tailwind.config.js no v3 precisa migrar para global.css:

/* Antes, em tailwind.config.js */
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: '#3b82f6',
      },
    },
  },
}

/* Agora, em globals.css */
@theme {
  --color-primary: #3b82f6;
}

As utilidades personalizadas também mudaram. Antes, usava-se @layer utilities; agora, use @utility:

/* v3 */
@layer utilities {
  .text-balance {
    text-wrap: balance;
  }
}

/* v4 */
@utility text-balance {
  text-wrap: balance;
}

Há ainda uma armadilha discreta: classes de componente deixam de aceitar variantes. No v3, era possível fazer isto:

/* Aceito no v3 */
@layer components {
  .btn {
    @apply px-4 py-2 rounded;
  }
}

/* Depois, eram usadas variantes como hover:btn e dark:btn */

No v4, hover:btn não funciona. É preciso transformar a regra em uma utility ou encapsulá-la em um componente React.

Também existe a mudança na cor das bordas. Como border-color passa a ser currentColor, algumas bordas parecem sumir. Procure por border e defina uma cor quando necessário:

// v3: a borda era cinza automaticamente
<div className="border"></div>

// v4: a borda acompanha a cor do texto; defina uma cor explícita
<div className="border border-neutral-300"></div>

Minha sugestão é migrar em etapas:

  1. Primeiro: instale o v4, execute o ambiente de desenvolvimento e procure problemas visuais evidentes.
  2. Segundo: procure @layer e converta as regras para @utility ou @theme.
  3. Terceiro: procure border e acrescente cores onde estiverem ausentes.
  4. Quarto: mova gradualmente o tema do arquivo de configuração para o CSS e teste a cada etapa.
  5. Quinto: se classes dinâmicas forem removidas, use uma safelist como último recurso.

O processo leva de meio dia a um dia, dependendo do tamanho do projeto. Não tente alterar tudo de uma vez; faça lotes pequenos para facilitar a reversão se algo der errado.

Se o projeto usa shadcn/ui ou outra biblioteca de componentes, confirme antes se ela oferece suporte ao v4. Eu já atualizei o Tailwind antes da biblioteca e vi vários componentes perderem os estilos.

O v4 é uma boa evolução, mas não existe obrigação de atualizar imediatamente. Se o v3 atende bem ao projeto, escolha um momento adequado para pagar esse custo de migração.

Conclusão

Foram quase dois anos desde aquele botão com 23 classes até chegar a <Button variant="primary">.

Tailwind e Next.js formam uma combinação eficiente, mas exigem algumas decisões para funcionar bem. As mudanças do v4 parecem assustadoras no início, porém seguem uma direção coerente: configuração mais simples, melhor desempenho e uma experiência de desenvolvimento mais fluida.

As práticas discutidas aqui — componentes, tema, modo escuro e desempenho — não precisam ser adotadas de uma vez. Escolha primeiro o que resolve o problema atual. Uma refatoração completa em uma única etapa é cansativa e aumenta o risco.

Minha recomendação é começar pelos componentes. Reserve uma tarde para encapsular Button, Card e Input e gerenciar suas variantes com cva. É uma mudança de baixo risco e retorno imediato. Depois, avalie modo escuro e personalização do tema.

Quanto à atualização para o v4, não tenha pressa. Verifique a maturidade do ecossistema, a compatibilidade das bibliotecas usadas e o tempo disponível. A tecnologia mais nova não é necessariamente a melhor; a melhor é a que se encaixa no projeto.

Se você encontrou problemas semelhantes com Tailwind ou conhece uma abordagem melhor, compartilhe sua experiência nos comentários. Talvez sua solução seja ainda mais elegante.

Os exemplos de código mencionados no artigo estão no GitHub, com o link ao final, e podem ser usados diretamente. Se houver algum problema, abra uma issue.

Não fique apenas na leitura: teste o código. Sem executar, é difícil aprender de verdade.

Configuração completa de Next.js com Tailwind CSS v4

Do início sem configuração ao encapsulamento de componentes, à otimização e ao modo escuro

⏱️ Estimated time: 3 hr

  1. 1

    Step 1: Configuração básica do Tailwind v4

    Mudanças no v4:
    • O arquivo de configuração deixa de ser obrigatório
    • As personalizações passam para variáveis CSS no global.css
    • O mecanismo reescrito em Rust é cinco vezes mais rápido

    Configure o global.css:
    ```css
    @theme {
    --color-primary: #3b82f6;
    --color-secondary: #8b5cf6;
    --font-sans: 'Inter', sans-serif;
    }
    ```

    Vantagens:
    • Alterações nas variáveis CSS aparecem imediatamente com hot reload
    • A configuração também é compreensível para designers
    • Não é preciso reiniciar o servidor de desenvolvimento

    Ponto principal: a filosofia do v4 é não exigir configuração; os arquivos do projeto são detectados automaticamente e tudo funciona de imediato.
  2. 2

    Step 2: Resolver o excesso de classes

    Problema: uma única linha fica ocupada por 23 classes.

    Solução: encapsular em componentes.

    Use cva para gerenciar variantes:
    ```tsx
    import { cva, type VariantProps } from 'class-variance-authority'
    import { cn } from '@/lib/utils'

    const buttonVariants = cva(
    'inline-flex items-center justify-center rounded-md',
    {
    variants: {
    variant: {
    default: 'bg-primary text-primary-foreground',
    destructive: 'bg-destructive text-destructive-foreground',
    },
    size: {
    default: 'h-10 px-4 py-2',
    sm: 'h-9 px-3',
    lg: 'h-11 px-8',
    },
    },
    }
    )

    export function Button({ variant, size, className, ...props }) {
    return (
    <button
    className={cn(buttonVariants({ variant, size }), className)}
    {...props}
    />
    )
    }
    ```

    Resultado: 23 classes viram três propriedades: variant, size e className.

    Ponto principal: reserve uma tarde para encapsular Button, Card e Input e use cva para gerenciar as variantes.
  3. 3

    Step 3: Otimizar o desempenho de 500 KB para 50 KB

    Métodos de otimização:

    1. Configure o escaneamento de conteúdo:
    ```js
    // tailwind.config.js (v3)
    module.exports = {
    content: ['./app/**/*.{js,ts,jsx,tsx}'],
    // Inclua somente as classes realmente usadas
    }
    ```

    2. Importe sob demanda:
    ```tsx
    // Não importe a biblioteca inteira
    import { Button } from '@/components/ui/button'
    ```

    3. Evite classes dinâmicas:
    ```tsx
    // ❌ Errado: a classe dinâmica não é detectada
    const color = `bg-${theme}-500`

    // ✅ Correto: use nomes de classe completos
    const color = theme === 'blue' ? 'bg-blue-500' : 'bg-red-500'
    ```

    4. Use o modo JIT no v3:
    ```js
    module.exports = {
    mode: 'jit', // Geração sob demanda
    }
    ```

    Resultado: o CSS caiu de 500 KB para 50 KB, uma redução de 90%.
  4. 4

    Step 4: Configurar o modo escuro

    O v4 não precisa mais da configuração darkMode. Use next-themes:

    Instale o next-themes:
    ```bash
    npm install next-themes
    ```

    Configure o ThemeProvider:
    ```tsx
    'use client'
    import { ThemeProvider } from 'next-themes'

    export function Providers({ children }) {
    return (
    <ThemeProvider attribute="class" defaultTheme="system">
    {children}
    </ThemeProvider>
    )
    }
    ```

    Use o prefixo dark::
    ```tsx
    <div className="bg-white dark:bg-gray-900 text-black dark:text-white">
    Conteúdo
    </div>
    ```

    Pontos principais:
    • O v4 oferece suporte automático ao prefixo dark:
    • Use next-themes para gerenciar o tema
    • Não é necessário configurar darkMode

FAQ

O que mudou no Tailwind v4?
Principais mudanças:

1. O arquivo de configuração deixa de ser obrigatório
• tailwind.config.js passa a ser opcional
• Os arquivos do projeto são detectados automaticamente

2. As personalizações passam para o global.css
• Cores, espaçamentos e fontes são definidos com variáveis CSS
• Alterações nas variáveis aparecem imediatamente com hot reload

3. O mecanismo foi reescrito em Rust
• A velocidade aumentou cinco vezes
• A inicialização a frio caiu de 8 para menos de 2 segundos

4. O modo escuro foi simplificado
• A configuração darkMode não é mais necessária
• O prefixo dark: funciona automaticamente

Vantagens: configuração mais simples, compilação mais rápida e hot reload mais ágil.

Observação: o v4 ainda estava em beta no contexto original do artigo; para produção, a recomendação era aguardar a versão estável.
Como resolver o excesso de classes?
Problema: 23 classes ficam espremidas em uma única linha.

Solução: encapsular em componentes e usar cva para gerenciar variantes:
```tsx
import { cva } from 'class-variance-authority'

const buttonVariants = cva(
'inline-flex items-center justify-center',
{
variants: {
variant: {
default: 'bg-primary',
destructive: 'bg-destructive',
},
size: {
default: 'h-10 px-4',
sm: 'h-9 px-3',
},
},
}
)

export function Button({ variant, size, className, ...props }) {
return (
<button
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
)
}
```

Resultado: 23 classes viram três propriedades, as variantes ficam centralizadas e as alterações são mais simples.

Recomendação: reserve uma tarde para encapsular Button, Card e Input e gerencie as variantes com cva.
Como otimizar o Tailwind de 500 KB para 50 KB?
1. Configure o escaneamento de conteúdo:
```js
module.exports = {
content: ['./app/**/*.{js,ts,jsx,tsx}'],
}
```

2. Importe sob demanda:
```tsx
import { Button } from '@/components/ui/button'
```

3. Evite classes dinâmicas:
```tsx
// ❌ Errado
const color = `bg-${theme}-500`

// ✅ Correto
const color = theme === 'blue' ? 'bg-blue-500' : 'bg-red-500'
```

4. Use o modo JIT no v3:
```js
module.exports = {
mode: 'jit',
}
```

Resultado: o CSS caiu de 500 KB para 50 KB, uma redução de 90%. O ponto principal é incluir somente as classes utilizadas e evitar nomes dinâmicos.
Como configurar o modo escuro no Tailwind v4?
O v4 não precisa mais da configuração darkMode. Use next-themes:

Instalação:
```bash
npm install next-themes
```

Configuração:
```tsx
'use client'
import { ThemeProvider } from 'next-themes'

export function Providers({ children }) {
return (
<ThemeProvider attribute="class" defaultTheme="system">
{children}
</ThemeProvider>
)
}
```

Uso:
```tsx
<div className="bg-white dark:bg-gray-900">
Conteúdo
</div>
```

O v4 oferece suporte automático ao prefixo dark:, next-themes gerencia o tema e darkMode não precisa ser configurado. Adicione suppressHydrationWarning à tag html.
Vale a pena atualizar para o Tailwind v4?
Vantagens:
• Velocidade cinco vezes maior
• Configuração mais simples
• Hot reload mais rápido

Desvantagens no contexto original do artigo:
• A versão ainda estava em beta
• O ecossistema podia não estar maduro
• A migração exigia tempo

Recomendações:
• Projeto novo: experimente o v4
• Projeto existente: espere a versão estável antes de atualizar
• Em caso de dúvida: continue no v3 até o v4 estabilizar

A tecnologia mais nova nem sempre é a melhor escolha. Verifique a maturidade do ecossistema, a compatibilidade da sua biblioteca de componentes e o tempo disponível para a migração.
Como gerenciar temas no Tailwind?
No v4, use variáveis CSS no global.css:
```css
@theme {
--color-primary: #3b82f6;
--color-secondary: #8b5cf6;
--font-sans: 'Inter', sans-serif;
}
```

Uso:
```tsx
<div className="bg-primary text-primary-foreground">
Conteúdo
</div>
```

Vantagens:
• Alterações nas variáveis aparecem imediatamente com hot reload
• A configuração também é compreensível para designers
• Não é preciso reiniciar o servidor de desenvolvimento

Comece encapsulando Button, Card e Input, gerencie variantes com cva e só depois avance para a personalização do tema.

14 min de leitura · Publicado em: 20 dez 2025 · Atualizado em: 8 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog