Componentes
33. Central de Notificações
seed-componentes.md v1.43 · §33seção 35 de 9833-central-de-notificacoes-estavel-v0-34-cn5-cn12.md · MD5 10ce03fbTítulo completo no canon: Central de Notificações — estável (v0.34) · CN5–CN12 · pattern F6 · suite suite-cn.mjs 45/45 ✓ · regressão A–F 291 verdes · gate visual aprovado 2026-08-06
Natureza: pattern de COMPOSIÇÃO do F6, não componente novo — consome sino (icon-button §1) + badge dot (§21) + popover/tray (§24) + drawer (§31) + lista com os slots LS5 (§28) + empty esvaziado (§25) + toast (§17) + severidades T1–T5. As regras do marco v1.0 (CN1–CN4) permanecem intactas e NÃO foram re-pesquisadas: toasts de BACKGROUND gravam no centro, de primeiro plano não; régua editorial "só o que o usuário RESOLVE, nunca marketing"; persistência/tempo-real/preferências = produto (Lovable/Supabase, CN4).
Base (3 rodadas encadeadas, 2026-08-06, ~26 fontes): R1 — Carbon notifications pattern (íntegra: painel usado em conjunto com toasts; cronológica; nunca reenviar; WCAG 2.2.3/2.2.4 AAA; Carbon ainda não estabilizou o componente — sinal de pattern), PatternFly notification badge + drawer v3–v6 (a spec mais completa do canon: unread por peso bold; mark all read; clear all com ressalva "varia por produto"; ≤3–4 categorias; badge attention só p/ erro crítico), NN/g indicators/notifications, Smashing 2025 (fadiga), UX Mag (modelo âncora), ui-patterns, evidência de campo Atlassian community (2 dores documentadas: badge que zera na abertura faz o usuário esquecer pendências; mark-all-read que não persiste destrói a confiança). R2 — EUI Header (
EuiHeaderSectionItemButtoncom propnotificationdot/contador; lista de alerts em EuiPopover OU EuiFlyout — exatamente os 2 degraus da CN) + issue #4257 (flyout como MVP do notification center), Knock (modelo unseen→seen→read; badge default unseen como anti-ruído de feed; filtro All/Unread; reverse chronological), Novu (seen one-way por IntersectionObserver; #955: mark-all-read desabilitado sem itens; #4846 flicker de estado). R3 — SAP Fiori Notifications (cronológica por timestamp "como um inbox"; agrupamento por tipo é recurso de ALTO volume com bulk por grupo; maior prioridade do item define a do grupo; título 2 linhas + Show More; DISMISS ≠ PROCESSADO), Sara Soueidan live regions partes 1–2 (região nomeada dá contexto à contagem; live region não carrega rich text), ariaNotify/AOM (limitações estruturais — contexto), APG feed pattern (proposta SEM consenso do task force desde ARIA 1.1 — descartada), APG alertdialog (aria-modal só quando o conteúdo externo está de fato bloqueado), Harvard DAS (não mover foco em atualização não-crítica). Consolidado aprovado pelo Rafael em 2026-08-06.
33.1 Decisões CN5–CN12#
| # | Decisão | Porquê | Descartado |
|---|---|---|---|
| CN5 | Arquitetura em 2 superfícies: sino no chrome (z 60) abre o painel rápido = popover §24 (tray <640px por herança Y4) com as 5 mais recentes + header + footer "Ver todas (N)" → drawer §31 com a lista completa. O toast (CN2) segue sendo o anúncio de chegada; o painel nunca re-anuncia o que o toast já falou | EUI: popover OU flyout do mesmo botão de header; Carbon: painel em conjunto com toasts; 5 itens mantêm o popover sem scroll (degrau leve do §23.1) | Superfície única (popover com scroll infinito = drawer disfarçado); página dedicada como destino primário (perde o estado que motivou o drawer) |
| CN6 | Drawer da CN em regime STANDARD (show(), não-modal, página interativa) — resolve o ponto em aberto do DW1. Esc fecha; foco entra no X ao abrir e retorna ao SINO ao fechar (âncora estável — o trigger "Ver todas" morreu junto com o popover); z 65 (emenda v1.1); <640px largura total (DW2), regime inalterado |
O caso de uso É consulta lado-a-lado (ler → navegar à fonte → comparar) — o caso central que criou o standard no DW1; PatternFly: drawer de notificações = "ver a lista sem sair da tela atual"; APG: aria-modal só quando o fundo está de fato bloqueado — trap aqui seria mentira semântica | Modal-drawer (trap sem tarefa que o justifique); regime por prop do consumidor (regra do um: a CN tem UM comportamento) |
| CN7 | Badge do sino reflete NÃO-LIDA, nunca "vista": abrir o painel NÃO limpa o badge. Limpa por leitura item a item ou "Marcar todas como lidas". Dot default; contador e vermelho seguem CN1/V5. O conceito "vista/seen" NÃO existe no DS | Dor real (Atlassian community): badge que zera na abertura → usuário esquece pendências; PatternFly: unread no badge só quando existe marcar-como-lida E chegada é infrequente — a régua editorial CN garante ambas; Knock admite que unseen é otimização de feed social — o nosso contexto é ERP acionável | Modelo seen/unseen (Knock default) — certo p/ feed de marketing, errado p/ pendência de trabalho |
| CN8 | Semântica de lida: navegar à fonte (ação primária da linha) marca lida; ação secundária por item = toggle "Marcar como lida/não lida"; "Marcar todas como lidas" no header das 2 superfícies, desabilitado sem não-lidas. Sem "Limpar tudo" e sem dispensar destrutivo — item sai quando o produto o considera resolvido (CN4). Lida ≠ resolvida: itens lidos permanecem | PatternFly: clicar na linha = lida; Novu #955: mark-all desabilitado sem itens; Fiori: dismiss ≠ processado — e a régua CN é "só o que o usuário resolve", então sobrar até resolver é o correto; Atlassian: mark-all-read que não persiste é a quebra de confiança nº 1 (persistência é dura — CN4) | "Clear all" (risco de descartar pendência acionável); dispensar com undo (fila de undo sem demanda) |
| CN9 | Ordenação cronológica inversa, SEM agrupamento por categoria na v1. Severidade T1–T5 no leading já diferencia. Fronteira declarada: se o volume crescer, a 1ª evolução é agrupar por DATA (Hoje/Anteriores), nunca por tipo | Carbon: cronológica; Fiori: timestamp é o default ("como um inbox"), agrupamento é recurso de alto volume — a régua editorial mantém o volume baixo por construção; PatternFly limita a 3–4 categorias (o custo do agrupamento) | Agrupamento por tipo/prioridade na v1 (complexidade de launchpad corporativo sem demanda); ordenar por prioridade (esconde o recente) |
| CN10 | Item = lista §28 com os slots LS5, sem slot novo: ícone T1–T5 no leading · título ≤2 linhas com wrap · timestamp relativo em JetBrains Mono no trailing com absoluto acessível (<time datetime> + title) · não-lida por peso 700 + dot 8px vermelho-500 · live region única do grupo (1.13-b). Chegada com painel ABERTO: prepende + live region polite nomeando ("Nova notificação: …"), SEM toast; painel FECHADO: o toast é o anúncio (CN2) — nunca os dois |
LS5 nasceu para isso (zero re-spec); Fiori/Carbon: 2 linhas; Soueidan: live region nomeada dá contexto; anúncio duplicado = dupla fala (regra do um aplicada ao canal sonoro) | role="feed" do APG (proposta sem consenso do TF desde 2017, suporte AT irregular, e jogaria fora a lista §28 já validada); timestamp só relativo (inacessível p/ auditoria e AT) |
| CN11 | Composição dos estados (já decididos — amarração): vazio = empty §25 tipo esvaziado, "Tudo em dia" (EEny permitido, Z5), sem live region própria; erro de carga = slot role="alert" pré-existente com ação de recuperação (herda S5/Z via LS5); loading = Régua de Espera (carga inicial → skeleton imediato) |
Tudo herdado; a decisão é declarar a composição | — |
| CN12 | Acessibilidade do sino: icon-button §1 com nome acessível dinâmico — "Notificações" / "Notificações, N não lida(s)" — + aria-expanded + aria-haspopup="dialog". Badge dot/contador decorativo declarado (o número vive no nome). Mudança de contagem NÃO anuncia — a chegada já foi anunciada pelo toast (T5). Nomenclatura anti-colisão: as superfícies da CN chamam-se "Central de notificações" — a landmark "Notificações" pertence à região de toasts (§17) e nomes acessíveis duplicados confundiriam a navegação por landmarks |
Soueidan (o nome carrega o contexto); Harvard DAS (não interromper por atualização não-crítica); a colisão de nomes foi detectada na composição — registro para todo consumidor | Live region no badge (dupla fala com o toast); contagem só visual (AT sem o número); as duas superfícies chamadas "Notificações" (colisão com §17) |
33.2 Anatomia, tokens e fronteiras#
Painel rápido: popover §24 (surface-overlay + shadow-overlay), largura ~380px desktop, header com título + ghost "Marcar todas como lidas", máx. 5 linhas, footer "Ver todas (N)" full-width ≥44px. Drawer: §31 integral em standard (sm 400; <640px total; z 65; X 44px). Linha: §28 densidade default (≥44px). Empty/erro/skeleton: §25/S5/§19. Nenhum par de cor novo — todos os pares consumidos já foram medidos nos §17/§21/§24/§25/§26/§28/§30–§31 (primário 13.86/14.16 · secundário 6.55/8.92 · meta mono 4.74/7.95 · hover 12.75 · severidades T1/errata azul-800 · dot vermelho-500 decorativo-redundante ao peso). Fora do DS (reafirmado): preferências de notificação · push/e-mail · o que conta como "resolvido" · persistência/tempo-real (CN4) · agrupamento por data (fronteira de evolução, CN9) · tabela de notificações (B6).
33.3 Os 7 testes#
- Nenhum par novo; herdados medidos ✓. 2. Não-lida = peso+dot (nunca só cor); severidade = ícone+cor (T3). 3. Grayscale: peso e ícones carregam (provado no §28/§15). 4. Painel rápido × drawer a 1s (popover ancorado 5 itens × superfície lateral com lista completa). 5. Regra do um: uma live region nomeada por grupo; um canal de anúncio por chegada (toast OU live region). 6. Não-lidas visíveis sem interação (badge + peso + dot). 7. 360px: tray herdado do §24, drawer largura total, linhas ≥44px, X 44px, hitbox estendida no marcar-como-lida. (Suite
suite-cn.mjs45/45 ✓; regressão A–F byte-perfeita = 291 verdes acumulados; gate visual do Rafael aprovado em 2026-08-06 — empilhamento renderizado, geometria do popover,show()nativo, tray 360px, dark e grayscale.)
Também cita o §33: tela-atividade, tela-configuracoes.