Fundação
Elevação & superfície
Cor e tipografia resolvem o plano. Este capítulo resolve a terceira dimensão: o que fica apoiado, o que fica flutuando, e quanto de sombra separa um do outro. São quatro sombras, seis raios e uma escada de camadas — e nada disso é decorativo. Sombra é a resposta visual para uma pergunta de hierarquia: isto está no papel ou está acima dele?
A regra que resume o capítulo: elevação é informação, não enfeite — só eleva o que pode ser tocado ou o que está por cima de outra coisa.
As quatro sombras
Vivem no :root de app/globals.css e chegam ao Tailwind como shadow-card, shadow-card-blue, shadow-fab e shadow-modal. Todas são COLORIDAS — nunca cinza puro nem preto duro. Os espécimes abaixo estão renderizados ao vivo, sobre o --bg real do produto.
card
.shadow-card
--shadow-card
O degrau de sempre. Quase imperceptível, e é isso mesmo.
Use em: Todo card, tabela, input e painel apoiado no fundo --bg. É a sombra padrão: na dúvida, é esta.
Cor de origem: --ink 15 23 42 a 4% · em uso em 37 arquivos
card-blue
.shadow-card-blue
--shadow-card-blue
A sombra que tem cor. Azul da marca vazando por baixo.
Use em: Só o que é AZUL e é ação: botão primário (variante primary de components/ui/button.tsx), card azul de destaque, hover de item selecionável.
Cor de origem: --blue 17 55 162 a 45% · em uso em 20 arquivos
fab
.shadow-fab
--shadow-fab
Amarelo derramado. A mais forte das quatro.
Use em: Só o botão flutuante e a variante accent do botão. Um por tela — dois FABs brilhando é briga, não hierarquia.
Cor de origem: --yellow 245 200 0 a 55% · em uso em 6 arquivos
modal
.shadow-modal
--shadow-modal
Descolou do plano. Grande, difusa, escura.
Use em: Tudo que flutua POR CIMA da página: dialog, bottom sheet, drawer, menu flutuante, popover. Sempre acompanhada de overlay.
Cor de origem: --ink 15 23 42 a 20% · em uso em 64 arquivos
| Token | Valor exato | Quando |
|---|---|---|
| --shadow-card | 0 4px 16px rgba(15, 23, 42, 0.04) | Todo card, tabela, input e painel apoiado no fundo --bg. É a sombra padrão: na dúvida, é esta. |
| --shadow-card-blue | 0 14px 30px -10px rgba(17, 55, 162, 0.45) | Só o que é AZUL e é ação: botão primário (variante primary de components/ui/button.tsx), card azul de destaque, hover de item selecionável. |
| --shadow-fab | 0 12px 28px -6px rgba(245, 200, 0, 0.55) | Só o botão flutuante e a variante accent do botão. Um por tela — dois FABs brilhando é briga, não hierarquia. |
| --shadow-modal | 0 20px 50px -15px rgba(15, 23, 42, 0.2) | Tudo que flutua POR CIMA da página: dialog, bottom sheet, drawer, menu flutuante, popover. Sempre acompanhada de overlay. |
Não existe uma quinta sombra. Se a peça pede um degrau que não está aqui, o problema é de hierarquia, não de sombra — provavelmente há elevação demais na tela. Utilitários genéricos do Tailwind (shadow-lg, shadow-2xl) e sombras escritas na mão em shadow-[...] não pertencem ao produto: são cinza neutro e brigam com a paleta.
No tema escuro a sombra troca de matéria
O admin tem um tema escuro por atributo — [data-tema="apol"] em app/globals.css. Sombra clara desaparece em fundo escuro, então dois dos quatro tokens são redefinidos lá.
| Token | Claro (:root) | Escuro ([data-tema="apol"]) |
|---|---|---|
| --shadow-card | 0 4px 16px rgba(15, 23, 42, 0.04) | 0 6px 18px rgba(0, 0, 0, 0.35) — preto real, porque relevo no escuro só vem de preto |
| --shadow-card-blue | 0 14px 30px -10px rgba(17, 55, 162, 0.45) | 0 14px 30px -10px rgba(245, 200, 0, 0.28) — vira glow amarelo; o azul sumia no fundo |
| --shadow-fab / --shadow-modal | Como acima | Não são redefinidos — herdam o valor do claro. |
Consequência prática: use sempre a classe (shadow-card), nunca o valor copiado. Quem escreve shadow-[0_4px_16px_rgba(15,23,42,0.04)] à mão ganha um card sem relevo nenhum no tema escuro — e o bug só aparece em quem usa o tema.
Escala de raio
tailwind.config.ts sobrescreve a escala do Tailwind: os números daqui não são os que você tem na memória. Cada amostra abaixo está desenhada no valor real.
- 10px
.rounded-sm
Chip, tag, badge, pastilha de status. O menor raio que a marca usa.
Padrão do Tailwind: 2px
- 16px
.rounded-md
O raio de trabalho: card, input, botão, linha de tabela, item de menu. É o mais usado do sistema.
Padrão do Tailwind: 6px
- 20px
.rounded-lg
Contêiner que agrupa outros cards, painel, bottom sheet, menu flutuante.
Padrão do Tailwind: 8px
- 26px
.rounded-xl
Bloco de destaque grande — card de meta, hero interno, tile do Brand Hub.
Padrão do Tailwind: 12px
- 36px
.rounded-2xl
Superfície de página inteira e cards do site. O maior raio antes do redondo.
Padrão do Tailwind: 16px
- 999px
.rounded-full
Avatar, FAB, pill de navegação, botão de ícone. Nunca em card retangular.
Padrão do Tailwind: 9999px (não sobrescrito)
A pegadinha que já mordeu: o bloco extend.borderRadius sobrescreve só sm · md · lg · xl · 2xl. O que ele não toca continua sendo Tailwind puro — e aí rounded-3xl (24px) fica menor que rounded-2xl (36px), e rounded sozinho é 4px, quase reto. Regra: use só as cinco sobrescritas mais rounded-full. rounded-3xl e rounded não fazem parte da escala da marca.
| O que é | Raio | Por quê |
|---|---|---|
| Chip, tag, badge | rounded-sm · 10px | Objeto pequeno com raio grande vira comprimido. |
| Card, input, botão, linha de tabela | rounded-md · 16px | O raio de trabalho — o mais usado do sistema. |
| Painel, sheet, menu flutuante | rounded-lg · 20px | Contêiner que guarda cards de 16px: o de fora tem que ser maior. |
| Bloco de destaque, tile | rounded-xl · 26px | O mesmo 26px que o site escreve como rounded-[26px]. |
| Card do site, superfície de página | rounded-2xl · 36px | O maior raio antes do redondo — respiro grande de peça institucional. |
| Avatar, FAB, pill, botão de ícone | rounded-full | Redondo é redondo: nunca aproxime com 2xl. |
| Raio aninhado | de fora sempre maior | Card de 16px dentro de painel de 20px. Igualar os dois faz o de dentro parecer torto. |
Os raios do site
O site do Alemão usa a mesma escala do produto (é o mesmo tailwind.config.ts), mas escreve dois raios fora dela em arbitrary value. Vale saber o que cada um é antes de copiar.
| Como está escrito no site | Valor | Situação |
|---|---|---|
| rounded-2xl | 36px | ✓ Na escala. Card de conteúdo, form de orçamento, bloco de serviço. |
| rounded-full | 9999px | ✓ Na escala. É o raio mais frequente do site — pill de navegação, selo, botão. |
| rounded-[26px] | 26px | ✓ Mesmo valor de rounded-xl — prefira a classe da escala. |
| rounded-3xl | 24px | ⚠️ Fora da escala (é o default do Tailwind). Balão de fala do hero — trocar por rounded-xl ao encostar no arquivo. |
| rounded-[22px] / rounded-[4px] | 22px / 4px | ⚠️ Fora da escala. Sem par na escada — a confirmar qual degrau assumem. |
O site não tem sombra nomeada. Um levantamento em components/site-alemao/ encontra dez sombras diferentes escritas à mão — de shadow-[0_10px_30px_rgba(16,32,63,0.06)] a shadow-2xl — contra os quatro tokens do produto. Pela lei do sistema unificado, quem manda é o produto: peça nova do site usa shadow-card e shadow-modal. A migração das dez existentes é a confirmar — está registrada em Pendências, não decidida aqui.
Hierarquia de superfície — a árvore de decisão
A pergunta que o time faz toda hora: fundo chapado, card sólido, vidro ou só sombra? Responda de cima pra baixo e pare na primeira que der Sim.
1. Isto flutua por cima da página?
Sim — dialog, sheet, drawer, menu, popovershadow-modal + overlay + z-[80]/[81]Superfície bg-superficie, raio rounded-lg (ou rounded-t-lg em bottom sheet), overlay bg-ink/50 backdrop-blur-sm. Sem borda: a sombra grande e o overlay já separam do fundo — borda em cima disso vira contorno duro.
2. O fundo atrás dele é rico? (azul, gradiente, foto ou padrão)
Sim — e o conteúdo é curtomaterial .vidro-* (sem sombra e sem borda)O vidro já traz a própria borda de luz. Escolha do material e cor de texto em Materiais de vidro. Texto corrido longo continua pedindo .vidro-fosco ou card sólido.
3. O fundo é claro? (branco ou --bg / --gelo)
Simcard sólido: bg-white + border border-line + shadow-cardEsta é a resposta padrão do sistema e a combinação mais repetida do produto — os três juntos, sempre. Sobre fundo claro, vidro está proibido: o blur não tem o que borrar.
4. Isto é a ação principal da tela?
Sim, e é azulshadow-card-blueVem de graça na variante primary de components/ui/button.tsx — não reescreva. Se for amarelo (FAB ou variante accent), a sombra é shadow-fab.
5. Sobrou algo?
É seção da página, não objetofundo chapado, zero sombraFaixa de seção, bloco de agrupamento, sticky header: bg-bg, bg-blue-soft ou bg-blue, separados por border-line. Sombra em coisa que não é objeto elevado só suja a tela.
Teste de bolso: se você não consegue dizer em uma frase por que aquele retângulo está acima do plano, ele não está — tire a sombra. Uma tela em que tudo flutua é uma tela sem hierarquia, e é assim que a sombra deixa de comunicar.
A regra do vidro, aplicada à superfície
Vidro é um material de superfície como qualquer outro — e o que decide se ele pode entrar é o que está ATRÁS dele, nunca o gosto de quem desenha.
Fundo rico
Vidro autorizado. Sem sombra e sem borda: o material já traz as duas.
Fundo claro
Card sólido. bg-white + border-line + shadow-card, os três juntos.
Vidro só sobre fundo rico. Sobre claro, card sólido — sem exceção, porque o blur precisa de algo pra borrar e sem isso o texto perde contraste (WCAG AA). A demonstração lado a lado do que acontece quando se ignora isso, mais os cinco materiais e a cor de texto de cada um, estão em Materiais de vidro.
| Superfície | Sombra | Borda | Raio |
|---|---|---|---|
| Card sólido sobre fundo claro | shadow-card | border border-line | rounded-md |
| Card azul de ação | shadow-card-blue | nenhuma — a sombra colorida já separa | rounded-md |
| Material .vidro-* sobre fundo rico | nenhuma | nenhuma — o material tem borda de luz | rounded-md ou rounded-lg |
| Dialog, sheet, drawer, menu | shadow-modal | nenhuma — overlay + sombra bastam | rounded-lg |
| Faixa / seção de página | nenhuma | border-line pra dividir | 0 — seção não tem raio |
| FAB | shadow-fab | border-4 border-white (o anel que o solta do fundo) | rounded-full |
Camadas — a escada real do sistema
Levantada com grep de z-index em app/ e components/. Isto é o que EXISTE hoje, não um ideal: a tabela serve pra você escolher um número que não colida, e pra ver onde o sistema já se contradiz.
| Camada | Quem ocupa | Onde vive |
|---|---|---|
| z-0 | Decoração de fundo | blobs do admin — components/admin/admin-layout-client.tsx |
| z-[1] | Régua dentro de gráfico | linha de meta — components/admin/shared/grafico-barras.tsx |
| z-10 | Conteúdo acima da decoração | wrapper do admin; cabeçalho sticky de lista |
| z-20 | Sticky dentro de um container que rola | thead do Caixa; faixa de aviso do admin |
| z-30 | Bottom nav do admin (mobile) | components/admin/admin-bottom-nav.tsx |
| z-40 | Header sticky do site · bottom nav do funcionário · WhatsApp flutuante | components/site-alemao/header.tsx · components/ponto/bottom-nav.tsx · components/site-alemao/whatsapp-flutuante.tsx |
| z-50 | FAB · menu mobile do site · takeover de celebração · dropdown do admin | components/ponto/fab.tsx · components/ponto/meta-batida-celebracao.tsx |
| z-[60] / z-[61] | Overlay / conteúdo — sheets de navegação e perfil · toast do Kanban · skip link do site | components/admin/menu-modulos-mobile.tsx · components/admin/shared/dialog-meu-perfil.tsx |
| z-[70] | Pilha de toasts | components/central/portal.tsx |
| z-[80] / z-[81] | Overlay / conteúdo — a escada de dialog mais usada (14 arquivos: OS, Caixa, Orçamentos, Tarefas, Compras) | components/admin/os/ficha-os-sheet.tsx e irmãos |
| z-[90] / z-[91] | Overlay / conteúdo — menu flutuante (abre POR CIMA de dialog) e dialogs de segundo nível | components/admin/shared/menu-flutuante.tsx · components/admin/os/receber-os-dialog.tsx |
| z-[95] | Confete de comemoração | components/caixa/confetti-festa.tsx |
| z-[100] | Abertura em tela cheia | components/intro/intro-variants.tsx |
| z-[200] | Barra de progresso de rota — tem que vencer tudo | components/admin/route-top-loader.tsx |
O padrão N / N+1 é a convenção que funciona: overlay em z-[80], conteúdo em z-[81]. Os dois têm que ser vizinhos — deixar o conteúdo em z-50 e o overlay em z-[80] é como o overlay engole o próprio dialog.
Três escadas concorrentes pra dialog. Hoje convivem 60/61 (sheets de navegação e perfil), 80/81 (a maioria — 14 arquivos entre OS, Caixa, Orçamentos, Tarefas e Compras) e 90/91 (menu flutuante e dialog de segundo nível). Os números não são arbitrários — o menu flutuante precisa abrir por cima de um dialog aberto, e o bottom nav em z-30 precisa ficar por baixo de todos eles. Mas a escada não está normalizada: a confirmar se 80/81 vira o padrão único de dialog e 90/91 fica só pro que abre por cima de dialog.
| Precisa de camada nova? | Use |
|---|---|
| Sticky dentro de um container que rola | z-20 — e nada acima disso |
| Barra de navegação fixa (mobile) | z-30 (admin) / z-40 (funcionário e site) |
| Botão flutuante que fica na tela | z-50 |
| Dialog, bottom sheet, drawer | z-[80] overlay + z-[81] conteúdo |
| Menu ou popover que abre por cima de um dialog | z-[90] + z-[91] |
| Toast | z-[70] — abaixo dos dialogs (80/81), de propósito: toast não deve tapar o que a pessoa está confirmando |
| Qualquer outra coisa | Nada. Reveja o empilhamento antes de inventar um número — número novo é dívida permanente. |
Superfície e o espaço que as camadas fixas reservam
Barra fixa não flutua sobre o conteúdo por acidente: o admin reserva a altura dela em CSS var. Quem escreve altura na mão reintroduz o bug.
| Token | Mobile | Desktop (md+) | Por quê |
|---|---|---|---|
| --admin-topo | calc(3.5rem + env(safe-area-inset-top, 0px)) | 2.5rem | 56px é exatamente o espaço do sino de notificações flutuante (top-4 + size-10). Reduzir faz o conteúdo começar embaixo do sino. |
| --admin-rodape | calc(3.75rem + env(safe-area-inset-bottom, 0px)) | 2.5rem | 60px de bottom nav + o home indicator do iPhone. No desktop não há nav — só a respiração. |
| --altura-tarefas | auto — no celular a página rola | calc(100dvh - --admin-topo - --admin-rodape) | Um lugar só. O valor já foi repetido à mão como calc(100dvh-104px) e mexer num sem o outro quebrava a altura do Kanban. |
Nunca use position:fixed pro CTA principal de uma tela que rola. Numa página que rola no nível do body, a barra fixa cobre ~110px do fim da viewport o tempo todo — inclusive na primeira visão. O padrão do sistema é h-dvh flex-col com a barra em shrink-0: o flexbox calcula o espaço, sem sobrepor nada.
Overlay — a camada que ninguém desenha e todo mundo vê
Toda superfície com shadow-modal vem acompanhada de um overlay. Ele não é fundo escuro genérico: é --ink com opacidade, mais um blur leve.
| Receita | Onde é usada | Quando |
|---|---|---|
| bg-ink/50 backdrop-blur-sm | 21 ocorrências — a mais comum | Dialog e sheet que exigem decisão: mais escuro, corta a distração. |
| bg-ink/40 backdrop-blur-sm | 17 ocorrências | Sheet de navegação e ficha lateral: a pessoa ainda precisa se localizar no que está atrás. |
| bg-ink/5 | 6 ocorrências | Não é overlay — é hover/fundo sutil de linha. Não confunda os dois usos. |
| bg-black/30 | 1 ocorrência (Exportar orçamento) | ⚠️ Fora do padrão: preto puro em vez de --ink. Corrigir ao encostar no arquivo. |
Escolha entre /40 e /50 pela tarefa, não pelo gosto: se fechar aquela camada sem decidir é aceitável, use /40; se a pessoa precisa decidir ali, use /50. E o overlay é sempre --ink — preto chapado não existe na marca.
Fica de olho
O que mantém a marca inteira — e o que a quebra.
Sempre
- Uma das quatro sombras: shadow-card, shadow-card-blue, shadow-fab, shadow-modal
- Card sólido = bg-white + border border-line + shadow-card, os três juntos
- Vidro só sobre fundo rico; sobre branco ou --bg, card sólido
- Raio de fora sempre maior que o de dentro (card 16px dentro de painel 20px)
- Overlay em --ink com backdrop-blur-sm, nunca preto puro
- Overlay e conteúdo em camadas vizinhas — z-[80] e z-[81]
- Reservar a altura de barra fixa em --admin-topo / --admin-rodape
- shadow-fab só no FAB, um por tela
Nunca
- shadow-lg, shadow-2xl ou shadow-[...] à mão — cinza neutro, briga com a paleta
- Copiar o valor da sombra em vez da classe (quebra o tema escuro em silêncio)
- rounded-3xl (24px) achando que é maior que rounded-2xl (36px)
- rounded sozinho (4px) — não faz parte da escala da marca
- Sombra em faixa, seção ou fundo chapado: aquilo não é objeto elevado
- Sombra e borda de vidro juntas — o material .vidro-* já traz as duas
- Inventar um z-index novo antes de conferir a escada deste capítulo
- position:fixed no CTA principal de uma tela que rola
Continua em
Elevação encosta em três capítulos. Nenhum deles repete o que está aqui.