Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

Tailwind responsivo: sm, md, lg e o jeito mobile-first

Por que md:flex significa a partir de 768px, como usar max-lg: para o caminho inverso e quando a container query resolve melhor que o breakpoint.

Rodolfo Mori4 min de leitura

No Tailwind, uma classe sem prefixo vale em todas as larguras; md:* sobrescreve ou acrescenta estilo a partir de 48rem. Mobile-first não quer dizer desenhar só para celular. Quer dizer começar pela restrição comum e adicionar capacidade quando existe espaço.

O componente desta lição é o resumo de consulta. Ele começa empilhado e vira duas colunas. As duas estratégias foram compiladas no mesmo diretório para medir o custo concreto.

Sem prefixo é a base, não “desktop”

Este cartão é coluna no menor espaço e linha a partir de md:

html
<article class="flex flex-col gap-4 p-4 md:flex-row md:gap-6 md:p-8">
  <div>Dados do paciente</div><div>Resumo financeiro</div>
</article>

O compilador agrupa as substituições numa media query de largura mínima:

css
@media (width >= 48rem) {
  .md\:flex-row { flex-direction: row; }
  .md\:gap-6 { gap: calc(var(--spacing) * 6); }
  .md\:p-8 { padding: calc(var(--spacing) * 8); }
}

sm: não significa “tela pequena”. Significa “a partir do breakpoint sm”, 40rem no tema padrão. Para o menor tamanho, escreva a classe sem prefixo.

Cinco breakpoints, nenhum modelo de aparelho

Os padrões são sm 40rem, md 48rem, lg 64rem, xl 80rem e 2xl 96rem. Eles descrevem faixas de layout, não marcas de telefone ou tablet.

html
<section class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
  ...
</section>

Escolha o ponto em que o conteúdo deixa de caber. Se o nome de um botão quebra antes de 48rem, não espere md por tradição. Ajuste a estrutura ou crie um breakpoint de tema baseado naquele conteúdo.

max-* aplica um teto

Às vezes você precisa corrigir um componente legado escrito desktop-first. max-md:* vale abaixo de md.

html
<article class="flex flex-row gap-6 p-8 max-md:flex-col max-md:gap-4 max-md:p-4">
  ...
</article>

No teste controlado, a versão mobile-first compilou para 4.456 bytes e a versão desktop-first para 4.480 bytes. As duas produziram uma media query. A diferença de 24 bytes é pequena; a vantagem principal de mobile-first é raciocínio e cascata, não uma promessa de performance.

bash
wc -c mobile.out.css desktop.out.css
4456 mobile.out.css 4480 desktop.out.css 8936 total

Um intervalo combina mínimo e máximo

Empilhe md:max-lg: para atingir uma faixa. A ordem dos prefixos descreve as condições que envolvem a utilidade.

html
<aside class="hidden md:max-lg:block">Resumo compacto</aside>

O CSS medido colocou o máximo dentro do mínimo:

css
@media (width >= 48rem) {
  @media (width < 64rem) {
    .md\:max-lg\:block { display: block; }
  }
}

Não escreva md:lg:flex; dois mínimos empilhados só dizem que o maior vence e escondem intenção. Use lg:flex ou um intervalo explícito.

Breakpoint é token de tema

Você pode criar ou sobrescrever uma fronteira no @theme:

css
@theme {
  --breakpoint-xs: 30rem;
  --breakpoint-3xl: 120rem;
}
html
<div class="grid grid-cols-1 xs:grid-cols-2 3xl:grid-cols-6">...</div>

Mantenha as unidades consistentes. Misturar px e rem pode produzir ordenação inesperada das variantes. Rem também acompanha melhor preferências de tamanho de texto no navegador.

Container query responde ao lugar onde o componente vive

Um cartão pode aparecer numa sidebar estreita dentro de uma tela enorme. Nesse caso, a viewport é o sinal errado. Marque um ancestral com @container e use variantes @md:*.

html
<aside class="@container">
  <article class="flex flex-col @md:grid @md:grid-cols-2">
    <div>Paciente</div><div>Horário</div>
  </article>
</aside>
css
@container (width >= 28rem) {
  .\@md\:grid { display: grid; }
  .\@md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

Use query de container para comportamento interno reutilizável e breakpoint de viewport para a composição da página. A lição de Grid mostra um auto-fit que resolve outro pedaço do mesmo problema sem prefixos.

Estado e largura podem trabalhar juntos

Variantes empilham: md:hover:* significa hover apenas a partir de md. Pergunte se a condição faz sentido em dispositivos que não oferecem hover.

html
<a class="underline md:no-underline md:hover:underline" href="#agenda">
  Abrir agenda
</a>

Para foco, não restrinja o sinal à largura. focus-visible:* deve funcionar em qualquer viewport. Estados completos estão em hover e focus no Tailwind.

Erro reproduzido: breakpoint que não foi declarado

O prefixo desktop: não nasce por convenção. Sem um breakpoint com esse nome, o CLI 4.3.3 rejeitou a variante:

css
@import "tailwindcss";
.qa { @apply desktop:grid; }
bash
printf '@import "tailwindcss"; .qa { @apply desktop:grid; }\n' | npx @tailwindcss/cli@4.3.3 -i - -o /dev/null
Error: Cannot apply utility class `desktop:grid` because the `desktop` variant does not exist.

O processo encerrou com código 1. Use um breakpoint existente, como lg:, ou declare --breakpoint-desktop no tema antes de usar o prefixo.

Teste conteúdo, não só três números

Eu uso 320px para pressão extrema, 768px para transição e 1440px para verificar limites máximos, mas também arrasto lentamente entre eles. O bug costuma morar em 873px, onde ninguém tirou screenshot. Aumente o texto para 200%, use um nome longo e abra o teclado virtual.

Se você escolhe breakpoints pelo conteúdo, a interface suporta aparelhos que ainda nem existem. A base de media queries em CSS ajuda a entender o mecanismo que as variantes geram.

Missão com duas implementações

Escreva o resumo da consulta primeiro mobile-first e depois desktop-first com max-md. Compile os dois em fontes isoladas, compare tamanho e media queries e escolha qual leitura deixa o estado base mais óbvio. Em seguida, coloque o mesmo cartão numa sidebar e troque viewport por @container. A missão termina quando o componente responde ao espaço que realmente recebe.

Registre comportamento, não nomes de aparelho

Uma documentação útil diz “a partir de 48rem, o preço cabe ao lado da descrição sem quebrar a ação”. Dizer “layout de tablet” não informa qual conteúdo motivou a mudança e envelhece quando o componente vai para uma sidebar. Registre estado antes, condição e estado depois.

Faça uma matriz curta com viewport estreita e larga, container estreito e largo, texto normal e ampliado. Um componente com query própria deve reagir ao container mesmo quando a viewport permanece igual. Se a mudança depende das duas dimensões, documente a prioridade em vez de acumular prefixos.

Teste orientação e teclado virtual. A altura disponível cai sem que a largura necessariamente atravesse um breakpoint. Menus fixos, diálogos e formulários precisam lidar com esse espaço sem cortar botões. Use rolagem no conteúdo apropriado e preserve a ação alcançável.

Por fim, compile e leia as media queries na ordem final. Uma variável de breakpoint com unidade inconsistente pode ordenar condições de modo inesperado. O guia da trilha posiciona essa revisão antes de estados e tema porque toda interação precisa funcionar nas mesmas faixas.

Salve a matriz junto do componente e atualize-a quando o conteúdo ou os tokens mudarem. Breakpoint correto hoje pode ficar cedo ou tarde depois de uma troca de fonte.

  • tailwind
  • responsivo
  • breakpoints
  • mobile-first
  • container queries

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em @tailwindcss/cli 4.3.3 no Node 26.3.0, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. Tailwind CSS — Responsive design — tailwindcss.com
  2. MDN — CSS container queries — developer.mozilla.org

Continue por aqui