Componentes
50. Agrupamento de linhas
estável
seed-componentes.md v2.00 · §50seção 101 de 18450-agrupamento-de-linhas-estavel-f7-2-2026-08-15.md · MD5 12246b77Título completo no canon: Agrupamento de linhas — estável (F7.2, 2026-08-15) · AG1–AG8 · gabarito A2
Em uma frase: Agrupar linhas por um campo: cabeçalho de grupo em surface-subtle, colapso por grupo, contador e subtotal declarados.
(decisão registrada — ver Histórico da seção)
| # | Decisão | Porquê | Descartado |
|---|---|---|---|
| AG1 | Um <tbody> por grupo — rowgroup NATIVO. O cabeçalho de grupo é uma <tr> com um único <th colspan scope="colgroup"> |
A semântica de agrupamento já existe no HTML; ARIA nova aqui seria "bad ARIA" sobre estrutura que o browser já expõe | <div role="rowgroup"> (recria o que é nativo) · tabela dentro de tabela (JAWS 2024 não navega — #791) |
| AG2 | O disclosure é um <button aria-expanded> DENTRO do cabeçalho de grupo — nunca aria-expanded na <tr> |
aria-expanded na linha empurra a tabela para o padrão grid/treegrid (MDN), e o DT1 proíbe grid na v1. O botão é disclosure puro, padrão com suporte universal |
role="treegrid" (APG: não é para produção; suporte pobre) · linha inteira clicável (anti-padrão do DT1) |
| AG3 | Nome acessível do disclosure = rótulo + contagem ("Metalúrgica Andrade, 3 faturas"), e o estado vem do aria-expanded — nunca do texto |
Roselli: o leitor de tela não anuncia quantas linhas apareceram; a contagem no nome administra a expectativa antes do clique | Nome só com o rótulo (o usuário expande às cegas) · anunciar "expandido/recolhido" no texto (duplica o que a AT já diz) |
| AG4 | As linhas do grupo vêm DEPOIS do controle na ordem do código, e o par de visibilidade é display:table-row × display:none |
display:block numa <tr> destrói a semântica de linha (Roselli, Tables, CSS Display Properties, and ARIA); e linha revelada acima do controle deixa o usuário de AT sem saber onde ela apareceu |
visibility:hidden (ocupa espaço) · hidden no atributo (mesma coisa, mas some da guarda CSS) |
| AG5 | aria-controls é OPCIONAL e declarado como tal — presente aqui, com a nota de que JAWS retirou o anúncio em 2019 e sua ausência não cria barreira |
Manter a relação declarada custa nada e serve se o suporte voltar; prometer que ela funciona hoje seria falso | Depender de aria-controls para a navegação funcionar |
| AG6 | Contador junto ao rótulo (C26), com o ZERO EXPLÍCITO — 0 aparece, não some |
Zero é informação: "este grupo existe e está vazio" é diferente de "este grupo não existe". Medição do produto de referência: eles mostram NOVO CLIENTE 0 |
Esconder o contador no zero (o usuário passa a duvidar se o grupo sumiu) |
| AG7 | Grupo de contagem zero revela o vazio de escala mínima (Z3) — uma linha "Nenhum item neste grupo", nunca o nada |
Botão que não revela nada é botão morto — o mesmo defeito que o §25 já proíbe em preview, aqui em produção | Disclosure desabilitado (esconde a razão) · grupo zero oculto (contradiz o AG6) |
| AG8 | Um nível de agrupamento na v1. Subgrupo entra por demanda, com pesquisa própria | <tbody> não aninha, e a saída seria tabela dentro de tabela — que reprova em JAWS (#791). A profundidade que o SIGA pede hoje é 1 |
Agrupamento em árvore dentro da tabela (é outro arquétipo: treegrid, descartado no AG2) |
Seleção dentro do grupo: ver a extensão do §41 (SL6). Pares de cor: todos herdados —
cabeçalho de grupo em surface-subtle com text-primary (12,63 claro / 14,02 escuro),
contador em text-muted (5,61 / 6,80), subtotal em text-secondary (7,13 / 9,10).
(Instrumento: contraste-f72.py; comportamento: suite-container.mjs, guardas AG-01…AG-08.)
Também cita o §50: tela-lista.