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.
| Fato | Onde se prova |
|---|---|
| 1 passo = 0.25rem = 4px | Escala 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çamento | components/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 inteiro | gap-[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ície | Faixa que usa | Prova |
|---|---|---|
| Produto / admin | 0.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 / site | 12 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ção | py-14 md:py-20 | 25 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ície | Container | Prova no código |
|---|---|---|
| Site da marca | mx-auto max-w-6xl px-4 sm:px-6 | Largura 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 / sistema | mx-auto max-w-[1600px] px-5 md:px-8 | components/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ário | mx-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. |
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.
| Prefixo | A partir de | Usos | O 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: | 640px | 729 | Gutter do site sobe: px-4 → sm:px-6 (79 usos). Grades abrem: sm:grid-cols-2 77×, sm:grid-cols-3 25×. |
| md: | 768px | 846 | O 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: | 1024px | 371 | Tabelas 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: | 1280px | 59 | Quarta e quinta coluna em grades densas (xl:grid-cols-4 7×, xl:grid-cols-3 10×). |
| 2xl: | 1536px | 3 | Praticamente 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ável | Mobile (base) | md+ (≥768px) | Quem consome |
|---|---|---|---|
| --admin-topo | calc(3.5rem + env(safe-area-inset-top, 0px)) | 2.5rem | admin-layout-client.tsx:610 (paddingTop), tarefas-shell.tsx:631, .apolo-area |
| --admin-rodape | calc(3.75rem + env(safe-area-inset-bottom, 0px)) | 2.5rem | idem, 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-tarefas | auto | calc(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">| Ponto | O que acontece |
|---|---|
| O mecanismo | grid 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 resolve | No 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 estoura | Só 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 hoje | 180 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:grid | md: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.
| Item | Situação |
|---|---|
| Rodapé do app do funcionário é número mágico | O 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 ritmo | 7 (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