Componentes
61. Data, intervalo e hora
seed-componentes.md v1.43 · §61seção 67 de 9861-data-intervalo-e-hora-estavel-f7-5-promovida-em.md · MD5 f5784b55Título completo no canon: Data, intervalo e hora — estável (F7.5 · promovida em 2026-08-17 por gate declarado, verbatim "aprovo. siga" · RE-GATE OBSERVADO em 2026-08-17: a BT-P1 e a DH-P6 FECHAM — insumo nomeado, ver §64.10.1) · DH1–DH18 · fecha C1 e C2 · bancada banco-data.html v0.4 (errata corrigida: este cabeçalho declarava v0.5, versão que nem o artefato, nem o gerador, nem o MANIFESTO, nem o mapa de cobertura têm — §64.10.3) · suite-data.mjs 31 · 0 · 1 [n/a] · BT7 consertado em 2026-08-17 (178px de excesso a 390px: o calendário empilha os atalhos abaixo de 470px, com exceção declarada ao piso de 44px na célula do dia)
Rodada zero: NÃO VISITADO — e a visita mudou a seção. O dispositivo existe no produto de referência (o §13 já tinha lido a data renderizada — C80, cor por estado), mas o seletor nunca tinha sido aberto. Navegação autorizada, modo somente leitura: o seletor foi aberto, lido e fechado; nenhuma data foi escolhida, alterada ou limpa. Oito dispositivos observados, C117–C124, no §15 do
estudo-clickup-completo.md.E a volta estreou um método que rendeu na primeira tentativa. Além da captura de tela, foi lida a árvore de acessibilidade da mesma região. As duas não coincidem — e o achado é o C122, negativo: cada dia é um
buttoncujo nome acessível é só o número (button "2"), semrole="grid", semgridcell, semcolumnheader, e semaria-selectedno dia escolhido. Na tela o dia 31 tem fundo sólido; na árvore ele não carrega estado nenhum.Copiar a forma teria copiado o defeito, porque o defeito não aparece na captura.
Base (3 rodadas, 2026-08-16), e o canon diverge em TRÊS. (a) ARIA APG — calendário é
role="grid"sobre<table>(row/columnheader/gridcellvêm implícitos detr/th/td); cabeçalho de coluna comabbrtrazendo o dia por extenso;aria-selected="true"só na célula escolhida; com campo vazio, o foco entra no dia de hoje; teclado ←/→ dia, ↑/↓ semana, Home/End na semana, PageUp/PageDown mês, Shift+PageUp/Down ano; e o campo de texto recebearia-describedbyapontando para a descrição do formato. (b) GOV.UK / NHS — não use calendário quando o usuário já sabe a data: use campos de texto, cominputmode="numeric"eautocomplete(bday-day/bday-month/bday-year) para o SC 1.3.5. O calendário é para quando a data não é sabida. (c) USWDS — faz os dois (campo + botão de calendário), e monta o intervalo como dois seletores independentes, comdata-min-date/data-max-date. Admite duas coisas: que não valida a coerência entre início e fim, e que o componente passa no SC 3.3.2 com ressalva — falta instrução visível de como usar o botão de calendário.R3 normas: SC 3.3.2 (o formato esperado tem de estar visível, não só no placeholder) · SC 1.3.5 (identificar o propósito do campo) · SC 3.3.7 Redundant Entry, nível A (o que o usuário já informou na sessão não se pede de novo).
61.1 A régua: pergunte se o usuário JÁ SABE a data#
A divergência não tem "jeito certo" — tem dois problemas diferentes que a mesma forma serve, e foi assim que o §60 se resolveu. Aqui a pergunta é outra:
| Se o usuário… | Então o caminho primário é… | E o outro… |
|---|---|---|
| já sabe a data (nota fiscal, nascimento, prazo em contrato) | digitar — teclado é mais rápido que caçar num calendário | o calendário fica disponível, e ninguém é obrigado a abri-lo |
| não sabe e precisa do contexto (que dia da semana? quantos dias faltam? cai no fim de semana?) | o calendário — a pergunta que ele responde não cabe num campo de texto | o campo continua lá, aceitando digitação |
Os dois existem SEMPRE, e nenhum é opcional. O GOV.UK está certo sobre a data sabida e o APG está certo sobre a data descoberta; escolher um dos dois seria acertar metade dos casos. No ERP da SEED os dois casos convivem na mesma tela: a data de emissão de uma nota o usuário sabe e digita; o agendamento de uma OS depende de que dia da semana cai e de quem está livre.
61.2 Os contratos#
| # | Contrato | O que ele descarta, e por quê |
|---|---|---|
| DH1 | O campo de TEXTO existe sempre e aceita digitação. O calendário é complemento, nunca substituto | Calendário sozinho. Para data sabida, obriga a navegar meses para chegar onde a digitação chegaria em 8 teclas |
| DH2 | O calendário existe sempre e é alcançável por teclado. Gatilho com nome próprio, não um ícone mudo | Campo de texto sozinho. Ele não responde "que dia da semana é" nem "quantos dias faltam" |
| DH3 | O formato esperado é VISÍVEL (SC 3.3.2) — em texto de apoio, não só no placeholder, que some ao digitar justamente quando o usuário mais precisa dele | Formato só no placeholder. É a ressalva que o próprio USWDS registrou contra si |
| DH4 | pt-BR: entrada em dd/mm/aaaa, inputmode="numeric", e o parser aceita separador /, . ou -. Exibição pode ser compacta; entrada não |
Aceitar mm/dd. Herda o VD5 — em pt-BR 1240.5 é doze mil quatrocentos e cinco, e a mesma armadilha de locale vale para data |
| DH5 | A grade é role="grid" sobre <table>, com <th> para os dias da semana e abbr com o nome por extenso |
Pilha de <button>. É exatamente o que o produto de referência faz (C122) |
| DH6 | O nome acessível de cada dia é COMPLETO — dia, mês, ano e dia da semana —, nunca só o número | button "2". Contraexemplo medido no C122: o leitor de tela anuncia "botão 2", sem mês, sem ano, sem saber o que já está escolhido |
| DH7 | aria-selected="true" só na célula escolhida; hoje se marca por aria-current="date" — são coisas diferentes e precisam de marcas diferentes |
Marcar hoje e o escolhido do mesmo jeito. Quem não vê perde a diferença entre "onde estou" e "o que escolhi" |
| DH8 | Teclado do APG, inteiro: ←/→ dia · ↑/↓ semana · Home/End na semana · PageUp/PageDown mês · Shift+PageUp/Down ano · Enter/Espaço escolhe · Esc fecha sem escolher | Só setas. Sem PageUp/Down, mudar de ano custa dezenas de teclas |
| DH9 | A grade tem UM tab stop (roving), como o SG1 | Um tab stop por dia. Quarenta e dois tab stops para atravessar um calendário |
| DH10 | Intervalo é UM controle: dois campos de texto e UM calendário compartilhado | Dois seletores independentes (USWDS). Separados, ninguém é dono da relação — e o próprio USWDS admite que não valida a coerência entre início e fim |
| DH11 | A coerência início ≤ fim é validada, e a mensagem diz QUAL dos dois corrigir e sugere a correção | "Intervalo inválido". Herda o VD11: erro que identifica o problema E sugere a saída |
| DH12 | O vão entre os extremos é DESENHADO na grade, e os dois extremos se distinguem do meio | Marcar só as pontas. Contrato decidido sem leitura — ver §61.4 |
| DH13 | A hora é campo SEPARADO e opt-in; não existir hora não é erro, e existir o campo não a torna obrigatória | Data e hora no mesmo campo. C118: no produto de referência a hora é um terceiro campo de texto, e a data não a carrega por padrão |
| DH14 | ISO 8601 na camada de dado; a string ISO NUNCA passa por new Date(string) |
Confiar no parser do agente de usuário. Regra já canônica do projeto — new Date("2026-08-16") desloca por UTC e devolve o dia anterior a oeste de Greenwich |
| DH15 | Atalho relativo mostra a data ABSOLUTA que resolve, ao lado do rótulo | "2 semanas" sozinho. C119: o relativo é ambíguo, e o produto responde mostrando o resultado antes do clique. Irmão do FC5 — o número é a prova de que o comando fez o que diz |
| DH16 | "Hoje" é o hoje do USUÁRIO, e o fuso fica declarado quando o dado é compartilhado | Hoje do servidor. Numa empresa em MG, ES e BA o fuso é o mesmo, mas o dado que sai em relatório não pode depender disso |
| DH17 | Recorrência NÃO é deste componente | Regra de recorrência dentro do seletor. C124: até o produto de referência a põe atrás de um passo a mais |
| DH18 | Data já informada na sessão não se pede de novo (SC 3.3.7, nível A): o segundo campo pré-carrega ou oferece o valor | Redigitar. É critério de nível A, o mais baixo — falhar nele é falhar no piso |
61.3 A fronteira: o que este componente NÃO decide#
- Regra de negócio de prazo — o que conta como atraso, se sábado é dia útil, qual feriado vale. O componente exibe e coleta; quem decide é o produto.
- Recorrência (DH17) · fuso como configuração · calendário de trabalho.
- Conversão de unidade de tempo — herdado do VD15: conversão é regra de engenharia.
61.4 O contrato que nasce SEM LEITURA, e isso está dito#
O DH12 — como o seletor desenha o vão entre início e fim — não pôde ser observado. A tarefa aberta tinha só a data de vencimento, e preencher a data inicial seria escrita, fora do modo autorizado. Não havia outra instância à mão com os dois extremos.
Achado negativo é achado. O DH12 fica marcado como decidido por canon e norma, sem leitura visual — e é o único dos dezoito nessa condição. Se uma volta futura achar uma instância com intervalo preenchido, ele é o primeiro a ser reconferido. Registrar de qual contrato não se tem leitura é o que separa uma spec incompleta de uma spec que não sabe onde está incompleta.
61.5 Números — a MEDIR na produção da bancada#
Método do §55: a seção nasce só com relações. Ficam a medir: altura e alvo da célula de dia ·
largura da grade · espaçamento entre semanas · o par de cor de hoje, do escolhido, do vão, do
hover e do foco, nos dois temas e em forced-colors (herdando o SG6-b) · alvo do gatilho do
calendário · e a distinção entre dia do mês corrente e dia vizinho.
Nenhuma medida é afirmada aqui.
61.6 A bancada — e as três decisões que ela obrigou#
banco-data.html · suite-data.mjs 25 PASS · 0 FAIL · 1 [n/a].
(a) O "hoje" da bancada é FIXO, e isso é decisão, não conveniência#
Um calendário que consulta o relógio muda de bytes todo dia. Isso quebraria a âncora
(bytes + MD5) que este projeto usa para provar que um arquivo é o que se pensa que ele é, e nenhuma
guarda poderia afirmar "o dia 16 está marcado como hoje". A data de referência entra pelo
gerador, declarada — nunca de Date.now().
É a mesma razão pela qual as suítes esperam por CONDIÇÃO e não por tempo: estado que depende do relógio não é verificável. O selo da bancada diz qual é o "hoje" dela, na primeira linha.
E o intervalo de exemplo é escolhido por asserção, não por gosto: o gerador aborta se hoje
não cair dentro do intervalo, porque é a única forma de provar, na mesma imagem, que "hoje" e
"escolhido" têm marcas diferentes (DH7).
(b) Dois alvos abaixo do piso, e o motivo do primeiro vale como regra#
O gatilho do calendário media 40px. A caixa declarava min-height: 44px — mas isso é a altura
externa: descontadas as bordas e o alinhamento, o botão ficava com 40. A célula de dia media
42px.
Regra: alvo se mede no retângulo do CONTROLE, não no contêiner que o abriga. Declarar 44 no pai é a versão sutil do defeito do
::beforeda SG-P7 — o número certo, no elemento errado.
(c) A ambiguidade que o DH15 existe para expor apareceu na própria bancada#
Dois atalhos de nomes diferentes — "Amanhã" e "Próxima segunda" — resolvem para a mesma data, porque o "hoje" fixado cai num domingo. A suíte registra isso como medida, não como falha: é exatamente a ambiguidade que torna a data resolvida necessária. Sem ela, o usuário clicaria em dois botões diferentes esperando resultados diferentes.
61.7 O gate achou um calendário inoperável — com 25 PASS · 0 FAIL#
Verbatim, 2026-08-16: "não consegui ter ação no calendário. não consegui fazer seleção de intervalo de data com o mouse, nem clicar em nenhuma data, quanto mais fazer o teste com tab".
A banco-data.html v0.1 tinha a grade estática, emitida em HTML pelo gerador. O clique num dia
movia o foco e escrevia numa live region — nada mudava na tela. Não dava para escolher um
intervalo, não dava para mudar de mês, e o gatilho do calendário não abria nada.
Uma grade estática não é um seletor de data — é a FOTO de um. E a foto passou em vinte e cinco guardas, porque todas elas mediam marcação: papel, nome, estado, alvo, teclado sob foco programático. Nenhuma perguntou se o usuário consegue usar.
E a guarda que devia pegar isso tinha sido escrita para não olhar ali#
A suite-data.mjs v0.1 tinha uma guarda BTN — "nenhum botão morto", regra do projeto desde
sempre. E ela excluía, por escrito:
!b.closest('table[role="grid"]') && !b.closest('.cal__nav') &&
!b.classList.contains('cal__hoje') && !b.classList.contains('cd__gatilho')
Os quatro lugares onde o defeito estava. Eu escrevi a exclusão porque aqueles botões "não são CTA comum" — e com isso removi do exame exatamente o que precisava ser examinado.
Regra nova: guarda escrita para NÃO OLHAR onde o defeito mora é pior que guarda nenhuma. Sem guarda, a pergunta continua aberta. Com uma guarda que desvia o olhar, o verde impede a pergunta. Toda exceção numa guarda precisa de motivo escrito — e o motivo não pode ser "esse caso é chato de medir".
E há exclusão legítima: a guarda nova exclui
[disabled], porque a plataforma define que botão desabilitado não responde. Excluir pelo que o contrato diz é método; excluir por onde o defeito mora é fuga.
As cinco guardas que nascem disso#
| # | O que mede, AGINDO |
|---|---|
| DH-OP1 | clicar num dia muda estado observável — não só a live region |
| DH-OP2 | o vão é pré-visualizado no ponteiro, antes do segundo clique |
| DH-OP3 | o segundo clique fecha o intervalo e escreve nos campos de texto |
| DH-OP4 | a navegação de mês muda o mês, e "Hoje" volta |
| DH-OP5 | o atalho relativo preenche, não só anuncia |
E a BTN passa a clicar em todo botão do componente, zerando a live region antes de cada clique.
Dois defeitos de instrumento a mais, e um de artefato achado pelo teste#
Nono (instrumento): a exclusão da BTN, acima. Décimo (instrumento): a primeira versão da
BTN nova media "o texto do anúncio MUDOU" — e dois campos de mesmo rótulo dizem legitimamente a
mesma frase. Ela acusou quatro botões corretos de mudos. Comparar com o valor anterior confunde
"não respondeu" com "respondeu igual". Agora a região é zerada antes de cada clique: o teste é
presença, não diferença.
E um defeito de artefato que só um teste de comportamento acha: a prévia do vão chamava
desenha() no mouseenter, e desenha() reescreve o innerHTML — o elemento sob o cursor era
destruído, o mouseenter disparava de novo no elemento novo, e o calendário entrava em laço,
tremendo. Redesenhar em resposta ao ponteiro destrói o alvo do próprio ponteiro. A prévia passou a
mutar atributos das células existentes, sem recriar nada.
E o calendário foi para o TOPO da página#
Ele vinha depois de sete campos de exemplo — cerca de trinta e três tab stops. Agora são quinze, e a grade é a primeira coisa substantiva do arquivo. O que a bancada existe para provar vem primeiro; o catálogo de estados vem depois.
61.8 Nível de prova, declarado#
Aplicando a régua da v10.2 — ao promover, declare o NÍVEL de prova de cada contrato:
| Nível | Contratos |
|---|---|
| 1 · Consumidor em produção | nenhum. Nenhum artefato do acervo tem seletor de data |
| 2 · Bancada | DH1–DH16 — provados pela banco-data.html, que nós construímos para prová-los |
| 3 · Ausência de violação | DH17 (recorrência não vive no componente) |
| Fora do componente | DH18 — SC 3.3.7 é contrato de TELA: o componente aceita valor inicial; quem o pré-carrega é a tela |
A seção segue
rascunho, e não é formalidade. Sem consumidor em produção e sem gate visual, o que existe é um componente que passa nos testes que ele mesmo trouxe. Placar verde não diz se o contrato sobreviveu ao mundo ou só ao caso que escrevemos para ele.
61.8 Pendências#
Aviso de supersede (2026-08-17): a linha DH-P6 desta tabela está SUPERSEDIDA pelo §64.10.4 — ela FECHOU com o re-gate observado da
banco-datav0.4. A tabela abaixo fica como histórico; o estado vigente é o do §64.10.4.
| # | Pendência | Por que fica aberta |
|---|---|---|
| DH-P1 | Bancada banco-data.html |
✅ FECHADA — 25 · 0 · 1 [n/a]. Nível de prova: 2 (bancada) |
| DH-P5 | Gate visual na banco-data.html v0.1 |
✅ FECHADA — e REPROVOU. "não consegui ter ação no calendário". Cinco guardas de operabilidade nasceram do achado |
| DH-P6 | RE-gate da banco-data.html — hoje v0.4 |
O calendário funciona e passa em 31 guardas — mas quem disse que a v0.1 estava quebrada foi o olho, não o placar. Além do conserto de operabilidade, a v0.4 traz barra fixa (BT1), seletor de largura (BT2) e o conserto do vão branco no tema escuro, que media 1,02 e passou a medir 10,52 / 7,98 (§64.2). Nada promove antes. Mesma pendência que a BT-P1 |
| DH-P2 | DH12 sem leitura visual | O vão do intervalo não pôde ser observado sem escrever. Reconferir na próxima volta que achar instância |
| DH-P3 | Calendário de trabalho e feriado | Parâmetro do produto, não do DS — irmão do VD-P2 (jornada). Fica nomeado para não voltar como surpresa |
| DH-P4 | Máscara durante a digitação | Mesma pendência do VD-P4: a máscara ao vivo não está implementada, e a decisão de fazê-la vale para os dois |
Também cita o §61: banco-edicao-linha.