Ir ao conteúdo
SEED engenharia

Componentes

49. Detalhe de registro — painel e modal

estávelseed-componentes.md v2.00 · §49seção 99 de 18449-detalhe-de-registro-painel-e-modal-estavel-f7-3.md · MD5 d39903c8

Título completo no canon: Detalhe de registro — painel e modal — estável (F7.3, 2026-08-16) · PD1–PD19 · PD20–PD24 decididos em 2026-10-10 (leva 30) · gabarito A8 · fecha a pendência PN-P3

Em uma frase: Três regimes por VOLUME — modal (padrão), painel lateral e página inteira —, cabeçalho fixo, ausência de valor sempre em travessão, e URL própria.

O que esta seção resolve, e por que ela demorou. A PN-P3 está aberta desde a Fase 5: todo ERP é lista → detalhe, e o nosso caminho morria na lista. A seção não foi escrita antes por um motivo declarado — não havia LEITURA VISUAL do arquétipo, só extração de valores, e a F7.2 provou caro o que isso produz (números certos, composição errada). A leitura chegou em 2026-08-15, na §13.4 do estudo-clickup-completo.md, com sete dispositivos observados: C50 modal que navega entre irmãos sem fechar, com posição no conjunto em cabeçalho fixo · C51 propriedades em grade de duas colunas · C52 campos customizados em lista com ícone de tipo e rótulo truncado · C53 ações do campo reveladas no hover · C54 corpo truncado com "Expandir" · C55 rail de seções DENTRO do modal · C56 contadores de metadado como ícone+número.

(decisão registrada — ver Histórico da seção)

A LACUNA QUE A PESQUISA REVELOU, e é o achado da rodada: não existe canon para troca de conteúdo DENTRO de um diálogo já aberto. Todo o corpus trata abertura e fechamento. O anúncio do nome do diálogo acontece quando o foco entra nele; trocar o texto do título com o foco parado não produz anúncio nenhum. Sem instrumento próprio, navegar entre irmãos é silencioso para leitor de tela. O PD3 é decisão nossa, declarada como tal.

Gate de 2026-08-15 (Rafael, sobre amostra A/B navegável): "modal A por padrão, e opção do modelo B caso o usuário prefira, então vamos ter as duas opções. no modelo A, deixa opção de expandir a janela depois que ela for aberta." Daí saem o PD1 (dois regimes, modal como padrão), o PD17 (dois degraus da janela) e o PD18 (alternância a partir do próprio detalhe).

49.1 Os contratos#

# Contrato O que ele descarta, e por quê
PD1 Três regimes, escolhidos por VOLUME de conteúdo, com o MODAL como padrão. Modal centrado (registro que cabe sem rolagem longa) · painel lateral não-modal (o contexto da lista importa durante a leitura) · página inteira (objeto com muitas seções e edição global). O painel é preferência do usuário, persistida por usuário e não por sessão Copiar o modal do produto de referência como regime único. Carbon é explícito: campos suficientes para exigir rolagem já pedem painel ou página. E descarta manter dois desenhos do detalhe: o markup é UM só, movido de hospedeiro (ver PD16)
PD2 A paginação entre irmãos só existe com conjunto ≥ 2, exibe a posição (9/10) e nas pontas fica desabilitada Seta sempre presente e inerte: com um registro só, um controle que não faz nada é botão morto. Régua literal do Fiori
PD3 Trocar de irmão NÃO devolve o foco ao topo; o foco permanece no controle acionado e a troca é anunciada por live region polite no formato "Registro N de M — título" Reabrir o diálogo a cada irmão (perde rolagem e re-anuncia tudo) e confiar no aria-labelledby, que só é lido na abertura. Sem canon — decisão declarada
PD4 Registro excluído → seleciona o anterior; conjunto vazio → fecha Manter o detalhe aberto sobre um registro que não existe mais
PD5 Cabeçalho do detalhe é FIXO, e o contêiner de rolagem declara scroll-padding-top maior que a altura dele Sticky sem compensação: é a falha F110 do SC 2.4.11. Medido no gabarito: cabeçalho 68px, scroll-padding-top 76px — 64px reprovava por 4px, e "quase" não passa numa norma cujo teste é o foco sumir atrás da barra
PD6 Ausência de valor é SEMPRE travessão, na grade e na lista de campos A palavra "Vazio". O produto de referência usa os dois tratamentos no mesmo registro (C52) — divergência registrada e não copiada; o DT7 já escolheu o travessão
PD7 A grade de propriedades responde ao CONTÊINER (CP27): 1 coluna até 520px, 2 acima, 3 a partir de 900px — container query peca declarada na PEÇA (o nó do detalhe), não no corpo Breakpoint de viewport. Consequência que vale a lei: expandir a janela faz caber mais, nunca deixa o dado maior
PD8 Altura de linha do campo é FIXA; quem cede por falta de largura é o RÓTULO, com o texto completo em title e equivalente por foco (X2 do §23) Truncar o valor primeiro. O valor é o dado; o rótulo é a etiqueta dele, e etiqueta curta ainda identifica
PD9 Ações do campo reveladas no hover E no :focus-within, com opacity:0 — nunca visibility:hidden nem display:none — e FORA do fluxo (position:absolute) Os dois primeiros removem da árvore de acessibilidade (SC 2.1.1). E manter as ações no fluxo reserva largura permanente para o que está invisível: medido, 88px que faziam o valor truncar sempre no painel de 460px
PD10 O corte do corpo é disclosure de verdade: <button> com aria-expanded + aria-controls resolvendo em elemento real, rótulo que nomeia o quê ("Expandir descrição") e muda com o estado; corte por line-clamp Link, <details>/<summary> (perde o controle do clamp por número de linhas) e o rótulo "Expandir" solto, que fora do contexto visual não informa nada
PD11 O rail de seções é navegação por ÂNCORA, não abas: documento único rolável, aria-current em exatamente uma seção, nome acessível em cada botão Abas: fragmentariam o objeto e quebrariam a rolagem única que o PD10 pressupõe. Fiori admite âncora ou aba; âncora é o caso quando as seções são partes do mesmo objeto
PD12 Contador de metadado leva nome textual ("3 anexos"); o ícone não é rótulo O número nu: não diz de quê. Herda o §21
PD13 O detalhe tem URL própria e, ao fechar, o foco volta à linha de origem — se ela saiu do DOM, ao lugar mais próximo dela Estado só em memória: impede compartilhar o registro e devolve o foco ao topo do documento, fazendo perder o lugar na fila
PD14 Regime modal: showModal() nativo, aria-modal, fundo inerte, Esc fecha. Regime painel: NÃO-MODAL — sem trap de foco, com a lista viva ao lado Painel lateral com trap: contradiz a razão de ele existir. Para diálogo não-modal a norma pede atalho global que mova o foco entre o diálogo e a página
PD15 Campos editam por edição direta, herdando PN22–PN41 do §48 sob o SC 2.5.7 Modo de edição global na v1. Fiori: o conteúdo não muda de lugar entre exibição e edição
PD16 UM só markup de detalhe no documento, movido entre os dois hospedeiros Clonar o conteúdo por regime. Dois desenhos vivos é como um deles envelhece sozinho — e a troca de regime passaria a perder o estado do registro
PD17 A janela tem DOIS DEGRAUS DECLARADOS (padrão e expandido), operados por botão de dois estados com aria-pressed, rótulo e ícone acompanhando. O degrau é preferência do usuário e sobrevive à troca de registro. No degrau estreito o botão some Alça de arraste como caminho único: cai sob o SC 2.5.7 e exigiria alternativa sem arraste de qualquer forma — o mesmo raciocínio que transformou o resizer de coluna do §51 em slider. Fiori admite arraste com tamanhos predefinidos e encaixe; adotamos os tamanhos e dispensamos o arraste. Descarta também a janela que cresce até o conteúdo: com registro curto o botão ficaria inerte
PD19 A lista de origem só ACOMPANHA quando está viva. No regime painel o realce segue a navegação entre irmãos — a lista está visível ao lado e a sincronia é o serviço. No regime modal ela não acompanha; a marcação é aplicada no fechamento, junto com o foco, e aponta onde a leitura parou Sincronizar sempre. Achado do gate de 2026-08-16, verbatim: "quando navego com a seta pra cima ou para baixo, ele está mexendo as duas janelas… era pra mexer só a que abriu sobreposta". Sob o véu do modal o fundo está inerte: movimento ali não informa nada e compete com o conteúdo que tem o foco. Descarta também devolver o foco à linha que abriu a leitura — quem navegou do 3 ao 7 espera voltar ao 7
PD18 A alternância de regime vive DENTRO do cabeçalho do detalhe Um controle de preferência fora do detalhe. Com o diálogo modal aberto o fundo é inerte, e o controle ficaria inalcançável exatamente quando seria usado — medido nesta produção
PD20 (PD-P5 a) A zona de ações do REGISTRO é uma barra no TOPO DO CORPO, entre os contadores (PD12) e a grade (PD7), com um primário por vista (§1) — "Dar ganho", "Nova tarefa", "Solicitar contrato". A ação indisponível MOSTRA, com o motivo em texto visível logo abaixo (a regra do SH6), ligada à ação por aria-describedby; é aria-disabled, nunca disabled nativo (que sairia da ordem de foco), e acioná-la explica o motivo em vez de calar. Motivo 7,75 / 8,11 Primário no cabeçalho fixo + menu "⋯" (o cabeçalho já carrega navegação, status, título, tamanho e fechar; no painel estreito não cabe; o motivo ficaria escondido no menu) e barra fixa no pé (mais uma faixa fixa encolhe o corpo útil; o PD5 já calibrou o cabeçalho fixo contra o foco escondido, SC 2.4.11). Custo conhecido: a barra rola com o corpo e some da vista em registro longo
PD21 (PD-P6 b) O PD9 vale também para a GRADE de propriedades (PD7): a ação de uma propriedade ("Trocar responsável") aparece no sobrevoo e no :focus-within da linha, com opacity:0, FORA do fluxo (position:absolute) — não reserva largura (o PD9 mediu 88 px que faziam o valor truncar). No toque (hover: none) é sempre visível, como a HV-P13 fez na lista de campos. O marcador de estado ("trabalha agora") é ESTADO, não ação: fica visível e dentro do fluxo Ação sempre visível (a grade vira lista de botões e o valor trunca) e ação só no menu de ações (a troca fica longe do valor que ela muda). Custo conhecido: quem não passa o mouse não vê a ação até focar a linha
PD22 (PD-P7 a) O seletor de pessoa (§62) aberto dentro do detalhe é SOBREPOSIÇÃO presa ao campo — o padrão de combobox do WAI-ARIA: a lista flutua ancorada ao campo, na camada do popover (75), e não empurra o conteúdo. No detalhe modal a lista nasce dentro do próprio diálogo [inferência da folha]. As medidas (largura, altura máxima, deslocamento e camada da lista aberta) foram medidas no gabarito A8 em 2026-10-11 (leva 34, PD-P10) e estão no §62.4 Em fluxo, empurrando o resto (o conteúdo muda de lugar enquanto a pessoa escolhe — o que o mockup do ERP imprimiu) e diálogo modal (modal sobre modal quando o detalhe já é modal, §49 regime padrão). Exercido no gabarito desde a leva 34 (PD-P10): o "Trocar responsável" do A8 abre a lista de verdade (suite-detalhe, casos SOB-01…18)
PD23 (PD-P8 a) O campo exigido só para uma ação ("exigido para dar ganho") é dito UMA vez, na AJUDA da seção (uma linha sob o título), e o motivo da ação diz o que falta na hora (PD20). Sem marcador por campo Marcador em cada campo (o rótulo, que já é quem cede na largura, perde espaço, e vários marcadores repetem a mesma frase) e só na hora da ação (a pessoa só descobre ao tentar). Custo conhecido: a pessoa lê a lista longe do campo
PD24 (PD-P9 a) O rótulo truncado (PD8) tem equivalente por FOCO: a linha de rótulo cortado entra na ordem de foco (tabindex="0" só nela) e mostra o balão do §23 (role="tooltip", aria-describedby, o par surface-inverse / text-inverse que o rail já usa) com o texto completo, o mesmo do title. Esc fecha o balão sem fechar o diálogo e sem tirar o foco (WCAG 1.4.13, X3). Rótulo que cabe não ganha parada de Tab Rótulo em duas linhas (supersede do PD8: a altura de linha deixa de ser fixa e a grade perde o ritmo medido) e só o title (descumpre o próprio PD8 para quem usa teclado: o title o teclado não mostra). Custo conhecido: mais paradas de Tab, só nas linhas truncadas

49.2 Anatomia#

┌─ cabeçalho FIXO (PD5) ──────────────────────────────────────────────┐
│ [↑][↓]  9/10  «chip de status»  Título do registro …   [⤢][×]       │
├──────────────────────────────────────────────────┬──────────────────┤
│ contadores de metadado (PD12)                    │  rail de seções  │
│ AÇÕES DO REGISTRO — 1 primário (PD20)            │  (PD11, âncora)  │
│ grade de propriedades — 1 · 2 · 3 col (PD7)      │                  │
│ DESCRIÇÃO + [Expandir descrição] (PD10)          │   ▤              │
│ CAMPOS DO DOMÍNIO — lista, ícone de tipo,        │   ✉ ②            │
│   rótulo truncável, ações no hover/foco (PD8/PD9)│   ∿              │
│ COMENTÁRIOS                                       │                  │
└──────────────────────────────────────────────────┴──────────────────┘

Os dois hospedeiros, com a mesma anatomia:

Modal (padrão) Painel lateral
Elemento <dialog> com showModal() <aside> irmão da região de conteúdo
Fundo inerte vivo — a lista continua operável
Largura 860px · expandido 1360×940 largura de peça lateral
Grade de propriedades 2 col · 3 col no expandido 1 col
Botão de tamanho presente ausente (não há dois tamanhos a oferecer)

49.3 Acessibilidade#

  • O diálogo é nomeado pelo título do registro (aria-labelledby apontando para o <h2> que exibe o nome). O nome muda a cada irmão; o anúncio não vem daí, vem da live region do PD3.
  • Foco entra no detalhe ao abrir, volta ao gatilho ao fechar (PD13).
  • Nenhuma ação depende de hover (PD9): tudo que o mouse revela, o teclado revela por :focus-within, e o alvo tem 44px no eixo curto também — regra que este projeto já reprovou quatro vezes por medir só um dos eixos.
  • Cabeçalho fixo × foco: scroll-padding-top maior que o cabeçalho (PD5, SC 2.4.11).
  • Contraste: 28 pares medidos nos dois temas, 56 · 0 (contraste-f73.py). Nenhum par novo: tudo é consumo de token canônico. Um achado real da medição: a primeira versão usava border-default na fronteira dos botões, que mede 1,49 sobre branco e 2,32 no escuro — reprova o SC 1.4.11 nos dois temas. Trocado por border-interactive (3,38 e 4,50), que nasceu na v6.9 exatamente para este papel.

49.4 Estados#

Estado Tratamento
Valor ausente travessão, nos dois blocos (PD6)
Rótulo longo trunca com reticências, title com o texto completo, altura fixa (PD8)
Corpo longo line-clamp + disclosure nomeado (PD10)
Primeiro/último do conjunto seta da ponta desabilitada, nunca inerte sem sinal (PD2)
Conjunto vazio o detalhe fecha (PD4); a lista mostra o zero-resultados do §25, com saída
Degrau estreito o detalhe vira página: ocupa a tela, o botão de tamanho some (PD17)

49.5 Números MEDIDOS na produção do gabarito#

A spec nasce com relações; os números são medidos no artefato — método do §55.

Medida Valor
Largura do modal, degrau padrão 860px
Largura × altura, degrau expandido 1360 × 940px
Colunas da grade: painel · modal · expandido 1 · 2 · 3
Tipografia do rótulo e do valor nos dois degraus 12px e 14px — idênticas
Altura do cabeçalho fixo · scroll-padding-top 68px · 76px
Altura da linha de campo do domínio 36px
Alvo dos botões do cabeçalho e do rail de seções 44 × 44px
Alvo das ações de campo 44 × 36px (a linha inteira é o alvo de leitura)
Fronteira de controle × superfície (claro · escuro) 3,38 · 4,50
Piso do rótulo que cede (PD8) — o valor só trunca com o rótulo aqui 64px

49.6 Validação#

Camada Placar
suite-detalhe.mjs (render) — as guardas do §49 54 · 0 (47 na produção + 7 nascidas do gate de 2026-08-16); 91 · 0 em 2026-10-10 (eram 83 · 0; +8 da leva 30: ACR · GRD · AJU · BAL)
suite-composicao.mjs (render, 2 temas) 33 · 0
suite-container.mjs — blocos CQ e RF, com alvo explícito 15 · 0
contraste-f73.py — 28 pares × 2 temas 56 · 0
Fidelidade do gerador reprodução byte-perfeita conferida por MD5

Escopo declarado da suite-container.mjs: ela foi escrita para os gabaritos de dado A2/A3 e cobra árvore com role=tree (ARV-01…03), que é estrutura do A2 e não do A8 — o escopo do detalhe é lista plana, e dar hierarquia à sidebar só para satisfazer um teste seria inventar estrutura para agradar instrumento. Os blocos que se aplicam (CQ container query, RF reflow) rodam com o A8 passado como alvo explícito. Isto é decisão registrada, não placar vermelho tolerado.

49.7 Pendências desta seção#

# Pendência Por que fica aberta
PD-P1 O §56 comentários não é decidido aqui. A fronteira do C85 — comentário como cartão dentro do cartão do objeto — é peça em peça, que o CP2 proíbe no caso base Há um dispositivo observado, e ele é justamente o caso difícil. A rodada zero exige leitura visual do arquétipo, e uma leitura de um caso não é leitura do arquétipo. Navegar antes
PD-P2 O regime página inteira (3º degrau do PD1) não tem gabarito Nenhum registro do escopo atual exige. Nasce quando houver caso SEED que o peça, sob a mesma régua de volume
PD-P3 Persistência da preferência de regime e do degrau da janela É contrato de produto, não de DS: o gabarito demonstra o estado; onde ele mora é decisão do SIGA
PD-P4 Deep link do PD13 declarado, não exercitado no gabarito O gabarito é arquivo único sem rota; provar URL própria exige aplicação

49.8 Decisões da folha de 2026-10-10 (PD20–PD24) — o que cada uma fechou#

A folha de perguntas de 2026-10-10 (P12, "a, a, a" = "aplicar a recomendação da sessão nas três folhas v3") trouxe cinco perguntas do detalhe, todas respondidas pela opção recomendada. Fecham: PD-P5 (PD20) · PD-P6 (PD21) · PD-P7 (PD22, a forma; as medidas seguem a medir) · PD-P8 (PD23) · PD-P9 (PD24). O gabarito tela-detalhe v0.6 exerce o PD20, o PD21, o PD23 e o PD24 (gerador gen-tela-detalhe.py; seletores .acoes-reg, .prop .acoes, .marca-estado, .secao-ajuda, .rot--trunca + .balao); o PD22 fica só no canon.

49.9 O regime painel segue o §31.4 — SH-P62 e SH-P63 (leva 45, folha 11)#

O painel lateral do PD1 deixa de ter largura fixa (460 px; 400 px com contêiner ≤ 1100 — os números da §49.5 e do PD9 ficam como registro da época) e deixa de empurrar sem piso: segue a regra do painel lateral à direita do §31.4 (PL1–PL9). Medido em 2026-10-24 (Chrome, dois temas): a 1440 px a lista ia de 1062 a 590 px e agora vai a 640 (painel de 410, empurrando); a 1100 px ia a 310 e agora fica com 722 (o painel de 420 sobrepõe de cara); a 390 px o painel substitui a lista, como antes. O painel ganha a borda de arraste (PL5) e o botão Ampliar (PL6), que só aparece no regime painel — no modal seguem os dois degraus do PD17. Fecha a SH-P63.

TelaSEED Design System v2 · tela-detalhe (gabarito A8) — preview v0.8abrir em página própria ↗

Também cita o §49: tela-painel.

Esc