Fundação

Espaçamento & grid

O espaçamento é a única parte da linguagem visual que o site e o produto já compartilham byte a byte — porque nenhuma das duas superfícies de token declara um valor de espaço. A escala é a do Tailwind 3.4.19, base de 4px, e o sistema usa 11.505 vezes em 662 arquivos. O que este capítulo faz é dizer quais degraus são de verdade.

A base é 4px

Um passo da escala = 0.25rem = 4px. Todo número desta página é múltiplo disso — inclusive as alturas de chrome do admin.

FatoOnde se prova
1 passo = 0.25rem = 4pxEscala padrão do Tailwind 3.4.19 (tailwindcss/stubs/config.full.js, chave spacing). O tailwind.config.ts do projeto estende fontSize, colors, borderRadius e boxShadow — e não toca em spacing.
Zero token de espaçamentocomponents/site-alemao/tokens.ts declara 11 variáveis, todas de cor. O :root de app/globals.css declara cor, sombra e as três alturas de chrome do admin. Nenhum dos dois declara espaço.
20 degraus cobrem 98,1%11.505 usos de gap/p/m/space com valor numérico; 11.288 caem nos 20 degraus documentados abaixo.
21 valores arbitrários no sistema inteirogap-[2px], mt-[7px], pb-[140px] e companhia: 21 ocorrências, 0,18% do total. A escala cobre 99,8% dos casos — valor arbitrário é exceção que se justifica no diff, não atalho.

Escala de componente — 2px a 32px

Onde 91,7% do espaçamento do sistema vive. A barra escura é o tamanho real, em 1:1 — meça na tela. A barra azul é o quanto o degrau é usado, proporcional ao campeão (passo 3, 2.165 usos).

  • 0.50.125rem · 2px

    Cola: rótulo e valor no mesmo bloco

    507
  • 10.25rem · 4px

    Ícone e texto lado a lado

    1.058
  • 1.50.375rem · 6px

    Padding de chip, badge e pílula

    1.161
  • 20.5rem · 8px

    Item de lista; o gap mais usado do sistema

    1.986
  • 2.50.625rem · 10px

    Meio degrau quando 2 aperta e 3 folga

    527
  • 30.75rem · 12px

    Padding de card; o passo nº 1 do sistema

    2.165
  • 3.50.875rem · 14px

    Ajuste fino de padding horizontal

    106
  • 41rem · 16px

    Gutter do container do site; padding de dialog

    1.317
  • 51.25rem · 20px

    Padding do container do admin e do app

    751
  • 61.5rem · 24px

    Gutter do site em sm+; gap entre cards do site

    641
  • 82rem · 32px

    Padding do admin em md+; bloco dentro da seção

    330

Os quatro degraus que decidem quase tudo: 3 (12px, 2.165 usos), 2 (8px, 1.986), 4 (16px, 1.317) e 1.5 (6px, 1.161). Juntos são 6.629 usos — 57,6% de todo o espaçamento do sistema. Na dúvida entre dois valores, o certo é quase sempre um destes quatro.

Escala de seção — 40px a 160px

6,4% dos usos, e praticamente só no site. Serve pra separar seção de seção, nunca pra respirar dentro de um card.

  • 102.5rem · 40px

    Gap entre colunas de uma seção do site

    134
  • 123rem · 48px

    Seção curta (py-12)

    130
  • 143.5rem · 56px

    Seção no mobile — o py-14 de py-14 md:py-20

    78
  • 164rem · 64px

    Seção média

    95
  • 205rem · 80px

    Seção no desktop — o md:py-20 do par

    117
  • 246rem · 96px

    Seção larga

    63
  • 287rem · 112px

    Bloco de respiro grande

    35
  • 328rem · 128px

    Topo de página interna

    53
  • 4010rem · 160px

    Só o hero — o maior espaço da marca

    34

Dois ritmos, um só sistema

A mesma escala, faixas diferentes. Isto não é inconsistência: é o que separa uma seção de landing de uma linha de tabela — e o código já respeita a separação sozinho.

SuperfícieFaixa que usaProva
Produto / admin0.5 a 5 (2px–20px)Os py mais usados em components/admin/ + app/[slug]/admin/ (351 arquivos): py-2 210×, py-3 127×, py-1.5 125×, py-0.5 107×. O admin inteiro tem um único py acima de 12: um py-14.
Marca / site12 a 20 pra seção (48px–80px)py-20 45×, py-14 33×, py-12 20×, py-16 16×. Seção do site nunca desce abaixo de py-12.
O par canônico de seçãopy-14 md:py-2025 ocorrências da dupla exata na mesma className (56px no celular → 80px no desktop). Quando a seção precisa de mais ar: py-20 md:py-24 ou py-20 md:py-28, 4 ocorrências cada.

A faixa do meio (6, 8, 10) é de bloco, não de seção. Se um py-8 apareceu separando duas seções do site, ou um py-20 apareceu dentro de um card do admin, alguém pegou o degrau da superfície errada. O número é legal nos dois lugares — o papel dele não.

Os três containers

Cada superfície tem um, e só um. Container novo não se inventa: reusa-se um destes três.

SuperfícieContainerProva no código
Site da marcamx-auto max-w-6xl px-4 sm:px-6Largura máxima 72rem (1152px), gutter 16px → 24px em sm. As 28 ocorrências de mx-auto … max-w-6xl em components/site-alemao/ respeitam esse gutter: 26 pelo px-4 sm:px-6 e 2 pela barra flutuante, que é absolute e faz o mesmo gutter com inset-x-4 sm:inset-x-6.
Admin / sistemamx-auto max-w-[1600px] px-5 md:px-8components/admin/admin-layout-client.tsx:608. O padding vertical não vem de classe: vem de --admin-topo / --admin-rodape em style inline — ver a seção seguinte.
App do funcionáriomx-auto max-w-[480px] px-5 md:max-w-[600px]app/[slug]/(funcionario)/layout.tsx:55, com pt-safe pro notch. Mobile-first literal: 480px é a largura de projeto, e o desktop só afrouxa pra 600px.
conteúdo
O container do site, ao vivo. A faixa azul é o gutter real: 16px agora, 24px a partir de 640px de viewport. Estreite a janela e veja o degrau acontecer.
conteúdo
O container do app do funcionário: gutter fixo de 20px, largura travada em 480px (600px em md+). Ele não centraliza texto num monitor de 27" — ele continua parecendo um app.

Larguras de leitura mais usadas: max-w-6xl 136×, max-w-2xl 110×, max-w-3xl 94×. O max-w-3xl é o do próprio Brand Hub: PageIntro e o cabeçalho de Sec travam nele pra a linha de texto não passar do confortável.

Breakpoints

Os cinco do Tailwind, sem customização — o tailwind.config.ts não declara screens. O que muda em cada um, porém, é decisão deste sistema.

PrefixoA partir deUsosO que muda de verdade
(base)0—Mobile-first é o default, não uma variante: uma coluna, gutter px-4/px-5, chrome do admin ligado (topo e rodapé reservados).
sm:640px729Gutter do site sobe: px-4 → sm:px-6 (79 usos). Grades abrem: sm:grid-cols-2 77×, sm:grid-cols-3 25×.
md:768px846O corte de verdade — o mais usado do sistema. Bottom nav do admin desaparece (md:hidden, 22 usos); --admin-topo cai de 56px pra 40px e --admin-rodape de 60px pra 40px (deixam de ser chrome e voltam a ser respiro); --altura-tarefas sai de auto e trava em 100dvh; container do admin px-5 → md:px-8; app do funcionário 480px → 600px; seção do site py-14 → md:py-20.
lg:1024px371Tabelas do admin revelam as colunas secundárias (hidden lg:block 12×, hidden lg:flex 6×) — abaixo daqui a mesma tabela vira lista de cards.
xl:1280px59Quarta e quinta coluna em grades densas (xl:grid-cols-4 7×, xl:grid-cols-3 10×).
2xl:1536px3Praticamente não existe neste sistema: 3 usos, todos grid-cols em listas do admin. Não conte com ele pra nada estrutural.

Alturas de chrome do admin

Três variáveis em app/globals.css que existem por um motivo específico: esses números eram mágicos e acoplados. O 104px de calc(100dvh-104px) do Kanban era 64+40 escrito à mão — mexer num sem o outro quebrava a altura das colunas.

VariávelMobile (base)md+ (≥768px)Quem consome
--admin-topocalc(3.5rem + env(safe-area-inset-top, 0px))2.5remadmin-layout-client.tsx:610 (paddingTop), tarefas-shell.tsx:631, .apolo-area
--admin-rodapecalc(3.75rem + env(safe-area-inset-bottom, 0px))2.5remidem, mais atualizar-flutuante.tsx:178, que se posiciona em calc(var(--admin-rodape) + 0.75rem) — sempre 12px acima da nav, em vez de um número fixo
--altura-tarefasautocalc(100dvh - var(--admin-topo) - var(--admin-rodape))tarefas-shell.tsx:632 (height). No celular o Kanban rola a página; só no desktop a altura trava pra as colunas rolarem por dentro.

Por que 3.5rem (56px) no topo

É exatamente o espaço do sino de notificações flutuante: fixed right-5 top-4 + size-10 = 16 + 40 = 56px (notificacoes-bell.tsx:136). A faixa de header saiu em 29/07/2026 e o número não mudou — reduzir agora começaria o conteúdo embaixo do sino.

Por que 3.75rem (60px) no rodapé

É a bottom nav do mobile, cujos itens têm min-h-[52px] mais a borda e o padding do invólucro (admin-bottom-nav.tsx). Em md+ a nav some e o número cai pra 40px de respiro.

O env(safe-area-inset-*) não é enfeite. O app instalado roda em standalone (appleWebApp.capable ligado), e aí o notch e o home indicator entram na viewport. Sem o env(), o topo do conteúdo fica atrás do notch e o último item da nav atrás da barra de gesto do iPhone. O fallback , 0px é obrigatório: navegador que não conhece a função precisa ver o calc() resolver em algo. Os utilitários pt-safe / pb-safe / px-safe em globals.css fazem o mesmo pra quem não usa as variáveis de chrome.

Regra dura: grid sempre tem coluna na base

Grade que só define colunas no md: precisa de grid-cols-1 na base. Isto não é estilo — é bug conhecido, medido em 31/07/2026 e espalhado por 13 arquivos.

✕ Coluna definida só no breakpoint

{/* Sem coluna na base: a implícita é dimensionada por
   min-content e NÃO encolhe. No celular a track foi a
   578px dentro de um container de 358px — 220px
   estourando pra fora da página. */}
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">

✓ Coluna na base, breakpoint só reconfigura

{/* grid-cols-1 = repeat(1, minmax(0, 1fr)).
   O piso é 0, então a track PODE encolher abaixo do
   min-content do conteúdo — e nada estoura. */}
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
PontoO que acontece
O mecanismogrid sem grid-template-columns cria uma coluna implícita, dimensionada por grid-auto-columns: auto. O mínimo de uma track auto é o min-content do conteúdo — ela cresce até caber o filho mais largo e não volta atrás.
Por que grid-cols-1 resolveNo Tailwind 3.4.19, grid-cols-1 é repeat(1, minmax(0, 1fr)) (tailwindcss/stubs/config.full.js:490). O minmax(0, …) baixa o piso da track pra zero. É o 0, não o 1fr, que conserta o bug.
Quando estouraSó quando algum filho tem min-content maior que o container: texto sem espaço pra quebrar, tabela, whitespace-nowrap, min-w-*. Por isso a grade passa despercebida com dado curto e quebra no dia em que o dado real chega.
Estado hoje180 grades já declaram grid-cols-1 na base junto de um sm:/md:/lg:grid-cols-*. Outras 99 ainda definem coluna só no breakpoint — nenhuma delas tem piso. Corrigir ao encostar no arquivo.
Não confundir com md:gridmd:grid md:grid-cols-2 está correto: no mobile o elemento nem é grid, então não existe track implícita pra estourar. A regra vale pra quem já é grid na base.

Mesma família de bug, outro eixo: barra vertical de gráfico com altura em % dentro de flex-col sem altura no pai — a barra simplesmente some (bug real de 29/07/2026, nos gráficos de /admin/pessoas/analise). A correção é a mesma ideia: calcular o pixel em vez de confiar num percentual sem referência. As barras desta página seguem a regra — width em px pro tamanho real, e % só onde há largura definida no pai.

Alvo de toque: 44px

Nada clicável abaixo de 44 × 44px. O sistema já escreve isso de três formas, e as três valem: min-h-[44px], h-11 e size-11 (2.75rem).

  • 40pxsize-10

    ✕ Reprova

    122 usos de 40px no sistema

    4px a menos parece nada e não é. O sino de notificações do admin é um caso confirmado: um <button> de size-10.

  • 44pxsize-11

    ✓ Mínimo

    183 usos (min-h-[44px] 49× · h-11 64× · size-11 58× · w-11 12×)

    O piso. A faixa de contato do header do site tem exatamente h-11 — e o sticky -top-11 rola exatamente essa faixa pra fora.

  • 48pxsize-12

    ✓ Confortável

    82 usos (h-12 45× · size-12 37×)

    O degrau de cima, pra ação primária. Acima dele, h-14 (56px) em 43 usos: FAB e CTA de tela cheia.

O quadrado azul acima tem o tamanho de verdade. Coloque o dedo nos dois e a diferença entre 40 e 44 deixa de ser abstrata. Quando o desenho não deixa o elemento crescer, o que cresce é a área de toque — padding no lugar de tamanho fixo, ou min-h-[44px] num invólucro maior que o ícone.

Divergências abertas

Duas, catalogadas em vez de escondidas — quem for mexer nelas merece saber que já são conhecidas.

ItemSituação
Rodapé do app do funcionário é número mágicoO admin reserva a nav com --admin-rodape. O app do funcionário reserva com pb-[140px] literal em app/[slug]/(funcionario)/layout.tsx:54 — um dos 21 valores arbitrários do sistema, e o maior deles. Mesma classe de acoplamento que as variáveis de chrome resolveram no admin. Virar token exige medir a nav flutuante (components/ponto/bottom-nav.tsx) mais o FAB: a confirmar.
Degraus fora do ritmo7 (28px) 89 usos, 9 (36px) 43, 3.5 (14px) 106, e um punhado de 11/4.5/5.5. São 141 usos, 1,2% do total. Não são erro por si — mas se um deles aparecer no seu diff, o valor vizinho da escala principal quase sempre serve.

Copiar rápido

Os quatro valores que mais se digita neste sistema.

  • Container do site
  • Container do admin
  • Seção do site
  • Grade responsiva

Fica de olho

O que mantém a marca inteira — e o que a quebra.

Sempre

  • Base 4px: todo espaço é múltiplo de 4 — inclusive as alturas de chrome
  • Dentro do componente, ficar entre os degraus 0.5 e 8 (2px–32px)
  • Seção do site em py-14 md:py-20 — o par canônico, 25 usos
  • Um dos três containers oficiais; container novo não se inventa
  • grid-cols-1 na base sempre que houver sm:/md:/lg:grid-cols-*
  • Reservar chrome do admin por --admin-topo / --admin-rodape, nunca por px na mão
  • env(safe-area-inset-*) com fallback 0px em qualquer borda de tela
  • Nada clicável abaixo de 44px: min-h-[44px], h-11 ou size-11

Nunca

  • Valor arbitrário tipo p-[13px] quando o degrau vizinho serve — são só 21 no sistema inteiro
  • grid gap-6 md:grid-cols-3 sem grid-cols-1: a track implícita não encolhe e estoura o mobile
  • py-20 dentro de um card do admin, ou py-8 separando seções do site — degrau da superfície errada
  • Recalcular 104px na mão em vez de somar as variáveis de chrome
  • Altura de barra de gráfico em % dentro de flex-col sem altura no pai — a barra some
  • Botão ou link de 40px (size-10) — 4px abaixo do alvo mínimo
  • Contar com 2xl: pra algo estrutural: são 3 usos no sistema inteiro