Componentes
46. Shell de aplicação
seed-componentes.md v1.43 · §46seção 48 de 9846-shell-de-aplicacao-estavel-v0-50-sh1-sh15.md · MD5 5da3cf7cTítulo completo no canon: Shell de aplicação — estável (v0.50) · SH1–SH15 · pattern F6 · suíte suite-shell.mjs 68/68 ✓ · render 18/0 · contraste 32/0 · gate visual aprovado 2026-08-11 — FECHA O BLOCO 6A
Natureza: pattern de COMPOSIÇÃO do F6, não componente novo — consome menu/dropdown (§35), tabs (§34), breadcrumb (§36), command palette ⌘K (§39), drawer (§31), avatar (§32), badge (§21), lista (§28), central de notificações (§33) e o botão (§1). Nenhum componente nasce aqui. É a mesma natureza do §33, o primeiro pattern F6 do sistema, e a fronteira que o Bloco 5 declarou desde 2026-08-06: "sidebar/topbar é shell do F6, consome este bloco".
PRINCÍPIO-MÃE, decidido com o Rafael em 2026-08-11: CONTRATO POR REGIÃO, COMPOSIÇÃO LIVRE. As cinco regiões são especificadas até o último detalhe; qual delas o produto liga, e o que coloca dentro, é decisão do projeto do ERP — não do Design System. Racional dele, adotado: engessar a composição limita em vez de ajudar, e não gera problema de padronização — o que carrega a identidade é cor, tipografia, traço, densidade e comportamento do item ativo, não o arranjo das caixas. O que o DS não abre mão: cada região se comporta igual em toda composição (SH14) e acessibilidade não é opcional em arranjo nenhum (SH2, SH5, SH7, SH8).
Escopo do produto, decidido em briefing antes da pesquisa: o ERP é web, responsivo, desktop-prioritário, usado pela equipe — escritório como uso principal, campo como uso real. O menu muda por perfil. A navegação é por ÁREA DO NEGÓCIO (processo), não por ferramenta. O app nativo do cliente é fronteira declarada: quando existir, o DS entrega tokens e componentes, nunca layout de navegação, porque brigar com a convenção do iOS e do Android é perder. O site institucional é o bloco 6B, e nasce mobile-first.
Base (3 rodadas encadeadas, 2026-08-11): R1 canon interno — o §33 como molde de pattern F6; o Bloco 5 inteiro como insumo (o shell não inventa, arruma); MN1 (as quatro saídas semânticas) e MN6 (comando indisponível fica visível), que o SH6 precisa distinguir; DW1 (os dois regimes do drawer) e CN6 (o precedente de standard); a escala de sobreposição 60<65<70<75<80; o §40 (densidade compacta medida em 33px). R2 mercado e stack — componente Sidebar oficial do shadcn/ui, que é o consumidor real do Lovable, com
SidebarProvidere os três modosoffcanvas/icon/none; blocos de sidebar colapsável comCollapsibledo Radix e threading porborder-l; prática consolidada de SaaS enterprise (topo para contexto global, lateral para navegação primária, porque as duas camadas fazem trabalhos diferentes e param de disputar espaço); SAP Fiori launchpad shell bar (logo, back, título, busca corporativa, notificações, Me Area). R3 normas e fora-do-circuito — ARIA11/W3C sobre landmark repetido exigindo nome único; MDN sobrerole="navigation";aria-current="page"como critério AAA de baixo custo; o corpus de acessibilidade de SPA (foco preso na navegação após troca de rota é o defeito nº 1, e o alvo do foco não pode ser landmark, link nem botão); Material Design navigation rail (3–7 destinos, posicionado abaixo da top bar). Consolidado aprovado pelo Rafael em 2026-08-11.REFERÊNCIA DE ARQUITETURA, e o que dela foi herdado. O Rafael apresentou um layout de referência (
preview-clientes-v2.html, 58.741 B, md569f00537763e14ef907d2f8130c26a92) no padrão de um produto de mercado, com instrução explícita: "apenas ideia para entender o layout de construção". Regra que sai daqui e vale para todo bloco futuro: de um layout de referência herda-se ESTRUTURA e COMPORTAMENTO; cor, tipografia e forma vêm do DS, sempre. Herdado: o rail como camada acima da sidebar · a sidebar ser contextual ao rail · árvore de quatro níveis com fio de conexão · busca fixa e centralizada com o atalho impresso dentro · seletor de contexto no topo à esquerda · contadores no fim da linha · ações que aparecem no hover · densidade compacta. Descartado por não ser do sistema: rail preto puro#000000(a superfície mais escura do DS é#0B1419), verde de sucesso#2DBA7C(o do DS é turquesa), gradiente terminando em#D94B7D, badge rosa, e neutros deslocados (#9AA8B0onde o DS tem#90A6B3;#CBD3D8onde tem#ACBECA) — a mesma deriva de hex digitado que custou as erratas E4 e E16 na Fase 5.
46.1 Decisões SH1–SH15#
O contrato de montagem de tela (ex-SH16) é LEI DO SISTEMA e vive no
seed-composicao.md§1 (CP1) — canvassurface-subtle, peçasurface-raised+radius-lg+ par sombra+anel, calhasp-3, nada encosta em nada. A exceção nomeada do rail foi SUPERSEDIDA no gate de 2026-08-15 ("um bloco montado como os outros, todos independentes, igual o ClickUp"): o rail é peça sobre o canvas como as demais — radius-lg, calha nos quatro lados, sem borda (a própria cor é a fronteira: 5,82/8,62 claro · 2,70/1,82 escuro contra o canvas). Registro completo no CP1 doseed-composicao.mdv1.4. O shell é a primeira instância do contrato, não a dona dele (supersede SUP-1, 2026-08-14; fecha a errata E-CF-03 — o cabeçalho desta seção dizia "SH1–SH14" com a tabela terminando em SH15).
| # | Decisão | Porquê | Descartado |
|---|---|---|---|
| SH1 | Cinco regiões nomeadas: rail · topbar · sidebar contextual · conteúdo · painel lateral. Obrigatórias: conteúdo + ao menos uma região de navegação. As demais o produto liga conforme a tarefa | Composição é escolha de tarefa, não de identidade. O §33 já opera assim: não obriga ninguém a ter central de notificações — diz que, se tiver, é assim | Composição fixa em N arranjos canônicos (engessa arquitetura de produto de dentro do DS) · nenhuma região obrigatória (tela sem navegação nem landmark é beco sem saída) |
| SH2 | Toda região de navegação é landmark com NOME ACESSÍVEL ÚNICO, e o nome não contém a palavra "navegação" — o leitor de tela já anuncia o papel. Nomes canônicos: "Áreas" (rail) · "Seções" (sidebar) · "Trilha" (breadcrumb) · "Conta" (menu do avatar) | ARIA11/W3C: landmark repetido exige nome único e significativo. O shell tem três a quatro nav simultâneos — sem nome, o usuário de leitor de tela recebe uma lista de navegações idênticas e não tem critério para escolher |
nav anônimo · nome contendo "navegação" (redundância verbosa) · nomes iguais em regiões diferentes |
| SH3 | RAIL — contrato: destinos de topo · 3 a 12 · largura 80px · ícone do set §45 + rótulo sempre visível, ≤9 caracteres por palavra · um ativo por vez · ordem estável · altura total, à esquerda da topbar | O Material limita a 3–7, mas calibrado para tablet, com o rail substituindo a barra inferior; num ERP de desktop o rail é a espinha do produto. Teto de 12 declarado como calibrado aqui, NÃO derivado de norma. Largura e limite de caracteres são MEDIDOS (medir-rail.mjs): Montserrat 9px caixa-alta mede 7,01px por caractere; um rail de 64px tem 52px úteis e comporta 7 caracteres — três dos seis rótulos de exemplo transbordavam. 80px dão 68px úteis = 9 caracteres. O rail cedeu de 64 para 80px porque "Comercial" (9) é nome legítimo de área de ERP — quem tem de caber é o negócio. Rótulo truncado não é rótulo, logo o limite recai sobre a palavra. Altura total porque o rail é o nível MAIS ALTO de navegação — ficar dentro da moldura da topbar contradiz a hierarquia que ele representa |
Rail só com ícone (rótulo em tooltip não é descoberto; o §45 mede glifos a 24px, não a 24px sem contexto) · rail abaixo da topbar (a posição que o Material recomenda, semanticamente errada aqui) · teto de 7 (herdaria calibração de outro contexto — o erro da errata E7) |
| SH4 | SIDEBAR — contrato: árvore de até 4 níveis, contextual à região acima. Quatro travas: (a) um ramo aberto por vez · (b) o ramo do item ativo abre sozinho ao carregar · (c) em modo estreito, níveis 2+ abrem em flyout · (d) quinto nível vira tabs §34 dentro da tela | O teto de 2 níveis foi proposto e derrubado pelo Rafael com contraexemplo (Financeiro → Faturamento → Faturas emitidas), e a referência confirma 4 na prática. As travas existem porque profundidade sem elas vira parede de 60 linhas. O flyout é disclosure de links, nunca role="menu" — distinção que o MN1 pagou caro para estabelecer |
Árvore livre sem travas · nível 3 como navegação secundária dentro da página (solução de Fiori e Carbon; cai porque explorar é ver a estrutura sem entrar nela, e navegação secundária só aparece depois de já ter chegado) |
| SH5 | Item ativo: aria-current="page" + marca que SOBREVIVE AO GRAYSCALE — barra lateral de 3px mais peso tipográfico, nunca só fundo colorido |
Indicar o item atual exige as duas coisas: destaque visual e aria-current no link. E o DF4 faz do grayscale gate estrutural do sistema — fundo colorido sozinho morre em cinza e em mono impresso |
Só fundo colorido · só aria-current (invisível para quem enxerga) · negrito sozinho (some em densidade compacta) |
| SH6 | Navegação por perfil OMITE o item; ação indisponível MOSTRA com motivo | O MN6 decidiu que comando indisponível fica visível, com motivo, porque "menu é inventário de comandos". Navegação é outra coisa: mostrar "Faturamento" a quem não pode entrar não ajuda ninguém e revela a estrutura interna da empresa. A distinção preserva o MN6 no domínio dele | Item bloqueado na sidebar (ruído permanente + vazamento organizacional) · omitir também na ação (o usuário não entende por que a operação falhou) |
| SH7 | Troca de rota move o foco para o CONTEÚDO (tabindex="-1", nunca o landmark em si), com live region persistente anunciando a tela |
É o defeito nº 1 de aplicação de página única: o foco fica preso na navegação, o conteúdo troca e o usuário de leitor de tela não sabe. O alvo do foco não pode ser landmark, link nem botão — as três quebram por evidência documentada. O DS já tem o protocolo de live region no §1.13 | Não fazer nada (o comportamento padrão, e é o defeito) · focar o main como landmark (lê o conteúdo inteiro) · focar o primeiro link (some do fluxo de tabulação ao receber tabindex="-1") |
| SH8 | Skip link como PRIMEIRO elemento focável, visível ao receber foco | WCAG 2.4.1. Num ERP com dezenas de itens de menu, tabular por toda a navegação a cada tela é inviável — e o SH4 permite quatro níveis | Skip link permanentemente oculto (existe e não funciona) |
| SH9 | BUSCA fixa e centralizada na topbar, com o ATALHO IMPRESSO DENTRO DO CAMPO. Ela abre a command palette §39 — é a mesma busca, com dois caminhos de entrada. Escopo: navega, não consulta dado | Supersede da decisão anterior deste consolidado, que tirava o campo e deixava só o atalho. O campo torna o atalho descoberto por quem lê, em vez de decorado por quem foi treinado — e a referência do Rafael mostrou isso antes de a spec ser escrita. Busca de registro depende de índice no ERP: é produto, não DS | Só o atalho (aposta em treinamento) · campo que não abre o §39 (dois mecanismos de busca, duas fontes de verdade) · busca de registro no shell (fronteira de produto) |
| SH10 | TOPBAR — contrato de três zonas: identidade e contexto à esquerda · busca ao centro · ações globais e conta à direita. Slots nomeados; o produto escolhe o que entra. O título da tela vive no CONTEÚDO, não na topbar | Prática consolidada de produto enterprise: topo para contexto global, lateral para navegação primária — assim as duas camadas param de disputar o mesmo espaço. O Fiori põe o título na shell bar e paga com uma barra que faz duas coisas | Título na topbar (duplica o h1 do conteúdo e cria duas fontes de verdade sobre onde o usuário está) · topbar com conteúdo livre (vira depósito) |
| SH11 | Seletor de contexto existe, no topo à esquerda | Estava reservado e desligado por falta de decisão de produto; a referência do Rafael decide que existe | Seletor dentro da sidebar (some quando a sidebar some) · nascer sem ele (redesenho depois) |
| SH12 | Em tela estreita, rail e sidebar colapsam num ÚNICO drawer §31 em regime MODAL | O CN6 escolheu standard porque o caso é consultar sem sair da tela. Navegação é o oposto: você escolhe e sai. Em tela estreita o drawer cobre o conteúdo — página interativa atrás seria mentira semântica, e o APG só admite aria-modal quando o fundo está de fato bloqueado |
Drawer standard (incoerente com a tarefa) · rail persistente no celular (come 64 dos 360px) · sidebar espremida (não vira app, vira ERP ruim no celular) |
| SH13 | PAINEL LATERAL de detalhe: drawer §31 em regime STANDARD — inspecionar um registro sem sair da lista | Aqui o caso é consulta lado a lado: ler → navegar à fonte → comparar. É o caso central que criou o regime standard no DW1 e que o CN6 já exerceu | Modal (perde o contexto da lista, que é justamente o que motivou o painel) · navegação para tela cheia (perde a posição de rolagem e a seleção) |
| SH15 | CHROME NÃO SEGUE O TEMA. A superfície do rail e a tinta sobre ela são fixas nos dois temas — desde a CP-P3 (2026-08-14) a superfície é o gradiente --seed-rail-background (turquesa-700 → turquesa-800, CP20 adotado no gate G2) com tinta branca, e o item ativo é a pílula clara turquesa-100 com tinta turquesa-900 (SH15-b) |
Achado da medição, não do desenho. O rail consumia surface-inverse, que significa "o oposto do tema": no escuro ele virava uma barra clara (#F2F6F9) dentro de uma interface escura, e o rótulo sobre o item ativo media 1,48. O rail não quer ser o oposto do tema — quer ser estruturalmente escuro nos dois, como o chrome de aplicação que ele é. É o simétrico do DG12 da Fase 5 (tinta sobre dado não segue o tema, porque o objeto contrastado é claro nos dois modos). Medido no estado atual: branco sobre os dois extremos do gradiente 6,32/9,37 · pílula 5,31/7,87 de fronteira · tinta da pílula 11,37. (Errata E-CF-05 fechada na v0.66: esta linha ainda descrevia o valor da era cinza-900 — "e o item ativo usa #006C62 fixo" — duas gerações atrás do artefato: a E-CF-04 consolidou o parágrafo do §46.2 e esqueceu esta linha. A REGRA nunca mudou; o VALOR mudou duas vezes: cinza-900 → turquesa-700 (v6.3) → gradiente (CP-P3).) |
surface-inverse (inverte por definição — era o defeito) · rail claro no claro e escuro no escuro (perde a âncora visual que faz o chrome ser reconhecível entre temas) |
| SH14 | Uma região NUNCA faz o trabalho de outra. Se rail e sidebar coexistem, o rail é destino de topo e a sidebar é contexto | Duas navegações com o mesmo papel deixam o usuário de leitor de tela sem critério de escolha — quebra o SH2 por dentro. É a trava que permite a composição ser livre sem virar deriva | Sidebar como segundo rail · rail com itens de contexto |
46.2 Anatomia, tokens e fronteiras#
Rail: largura 80px (SH3 — medido: 68px úteis = 9 caracteres; "Comercial" cabe); superfície
fixa nos dois temas (SH15 — o rail NÃO consome surface-inverse, que foi o defeito medido a
1,48), hoje o gradiente da rampa da marca via semântico próprio --seed-rail-background
(turquesa-700 #006C62 → turquesa-800 #005048; CP20 do seed-composicao.md, adotado no gate
G2 de 2026-08-14 e executado na CP-P3 — extremos medidos 6,32/9,37, tinta passa nos dois, nunca
na média); hover e pressionado do item consomem os overlays de chrome do SUP-6
(--seed-chrome-overlay-hover/-active, medidos em composição: 4,88/6,82 e 4,68/6,50);
ícone 24px do §45 + rótulo em micro-caps; item ativo pela regra do SH5; alvo de toque ≥44px.
(Errata E-CF-04 fechada na v0.65: este parágrafo dizia 64px, surface-inverse e implicava
cinza-900. Atualizado de novo na v0.66 pela CP-P3: a redação "gradiente admitido — adoção
pendente de gate visual" ficou falsa após o gate.)
Topbar: altura 60px; surface-raised com borda inferior border-subtle; z-index 60 (a
mesma faixa de chrome da escala 60<65<70<75<80 fixada no Bloco 4). Sidebar: largura 280px,
surface-page; indentação progressiva com fio de conexão em border-subtle; contador opcional
no fim da linha em mono; linha na densidade compacta do §40 (33px medidos), com alvo real de
toque garantido pela linha inteira, como no §28. Conteúdo: main com tabindex="-1" para o
SH7. Painel lateral: §31 integral em standard, z-index 65.
Nenhum par de cor novo — mas compor pares medidos separadamente não garante que a
combinação passe, e o contraste-shell.py provou isso: a primeira execução deu 22 PASS ·
8 FAIL. Correções, todas por medição: o rail deixou de consumir surface-inverse (SH15) · o
contador e o placeholder saíram de text-muted (3,38 e 3,11 no claro) para text-secondary
(6,55 e 6,03) · o item ativo da sidebar deixou de ser texto de marca (4,23) e passou a texto
primário com barra em text-brand — a cor não carrega o estado, quem carrega é a barra e o
peso (SH5) · a sigla do seletor de contexto saiu de branco sobre surface-brand (2,71) para
branco sobre surface-brand-deep (6,32). Placar final: 32 PASS · 0 FAIL.
PENDÊNCIA SH-P1 — buraco na camada semântica, achado por este bloco. Nenhum token semântico de borda passa 3:1 contra
surface-raisedno tema claro:border-defaultmede 1,49 eborder-strong2,53. O campo de busca precisa de contorno que o identifique como componente (1.4.11), e a única saída medida foi consumir o primitivocinza-500(3,38 no claro · 4,50 no escuro). Isso é empréstimo declarado, não regra nova — e viola a lei "componente consome semântico". A correção certa é um token semântico novo na camada 2, e ela não pertence a este bloco: mexer nos três gêmeos por causa do shell seria decidir a camada de tokens dentro de um pattern. Fecha quando a camada 2 for editada por outro motivo, pela regra GI2.✅ FECHADA em 2026-08-14, pela via prevista (gatilho GI2 + decisão do Rafael: "vamos resolver logo"): a camada 2 foi editada pela CP-P3 (gêmeos v1.11) e, na mesma janela, os gêmeos v1.12 promovem o empréstimo a semântico —
--seed-border-interactive=cinza-500#788F9Dnos dois temas, medido contra as seis superfícies onde borda de componente aparece (3,38/3,38/3,11 claro · 4,50/5,51/5,05 escuro, todos ≥3,0), slot dark declarado (coincidência medida, não invariância). O campo de busca do shell consome o semântico desde o preview v0.3 — nenhum primitivo é mais consumido por exceção no §46. Alternativa descartada com número:cinza-600também passa (3,21–4,74), mas mudaria a cor RENDERIZADA que o gate visual já aprovou, sem motivo — a pendência pedia promoção de expressão, não de valor. A metade "separação de peça" já havia fechado no CP17 (par sombra+anel).
Fora do shell, declarado: que menus existem e o que há dentro deles (projeto do ERP) · qual perfil vê o quê (regra de negócio) · persistência do estado da sidebar (produto — Lovable/Supabase, mesma fronteira do CN4) · busca de registro (produto) · dashboard e composição de painel (bloco 6C) · site institucional (bloco 6B) · app nativo do cliente (fora do DS: tokens e componentes sim, layout de navegação não) · o que o slot de IA da topbar abre (sub-bloco próprio da F6 — o slot existe, o destino não é especificado aqui).
Consequência declarada da navegação por PROCESSO: como o mesmo objeto é alcançável por mais de um caminho, o breadcrumb §36 deixa de ser decoração e passa a responder por qual caminho eu cheguei — é ele que desambigua, não a sidebar.
46.3 Os 7 testes#
- Nenhum par de cor novo; todos herdados e medidos.
- Item ativo nunca depende só de cor — barra + peso +
aria-current(SH5). - Grayscale: rail, sidebar e ativo permanecem legíveis (DF4 como gate estrutural).
- Regra do um: um item ativo por região; uma região por papel (SH14).
- Landmarks: três a quatro
navsimultâneos, todos com nome único, nenhum contendo a palavra "navegação" (SH2). - Troca de rota: foco no conteúdo e anúncio na live region, sem foco em landmark, link ou botão (SH7); skip link é o primeiro focável (SH8).
- 360px: rail e sidebar colapsam em drawer modal único; nada transborda; alvos ≥44px (SH12).
(Estado: estável desde 2026-08-11, por "aprovo" explícito do Rafael após o gate visual
sobre o preview v0.1. Quatro camadas cumpridas: suite-shell.mjs 68 PASS · 0 FAIL —
prova de que as guardas são reais: contra um preview com 5 defeitos deliberados, reprova em
8; render-shell.mjs em Chrome real 18 PASS · 0 FAIL; contraste-shell.py 32 PASS ·
0 FAIL após as correções que a própria medição exigiu; e o gate visual humano. Pendência que
o bloco levou consigo: SH-P1, que é da camada de tokens, não do shell — fechada em
2026-08-14 pelos gêmeos v1.12 (border-interactive; fechamento completo na própria
pendência, acima).)