Componentes
13. Date picker
seed-componentes.md v1.43 · §13seção 15 de 9813-date-picker-estavel-validado-pelo-rafael-em.md · MD5 a2a6780cTítulo completo no canon: Date picker — estável · validado pelo Rafael em 2026-08-03 (preview v0.13 + suite de 63 testes)
Base (3 rodadas): R1 — GOV.UK/NHS/NSW date input (memoráveis digitadas; NUNCA auto-advance; ano 4 dígitos), CMS (single-input flexível formata no blur), Scottish (calendar = progressive enhancement). R2 — shadcn Calendar/react-day-picker (locale pt-BR, range 2 meses, presets — ReUI), GitLab (digitar OU escolher, sempre). R3 — NN/g date input (picker frustra data distante: "165 cliques até 1990"), Baymard touch keyboards, icapps (picker inadequado p/ nascimento), Smashing birthday. M1–M6 conferidas no consolidado.
13.1 A régua por natureza da data (M1 — resolve o conflito 3-campos × máscara × picker)#
| Natureza | Interface | Porquê |
|---|---|---|
| Memorável/conhecida (nascimento, emissão de documento) | Campo ÚNICO mascarado dd/mm/aaaa (registry B5), entrada flexível ("1/8/26" → "01/08/2026" no blur — herda E6) |
A pessoa JÁ SABE a data: digitar vence qualquer navegação (NN/g/icapps); CMS valida o single-input |
| A escolher (agendar visita técnica; futuro <1 ano; dia da semana importa) | Input digitável + botão calendário (progressive enhancement) | Scottish/GitLab: os DOIS caminhos sempre; o calendário responde "que dia cai?" |
| Faixa (relatórios ERP "de/até") | Range picker 2 meses + presets ("Últimos 30 dias", "Este mês") + campos digitáveis | ReUI/NN/g Expedia; presets matam a navegação repetitiva |
Alternativa descartada com contexto (M2): os 3 campos separados do GOV.UK resolvem a ambiguidade dd/mm × mm/dd do mundo anglófono — o dd/mm/aaaa universal brasileiro não tem essa ambiguidade na mesma escala, e a máscara B5 já testou. Plano B documentado: se pesquisa com usuários SEED mostrar erro de formato, os 3 campos entram (com type=text inputmode=numeric — E1 vale lá também).
13.2 Decisões M3–M6#
| # | Decisão | Porquê | Descartado |
|---|---|---|---|
| M3 | Digitar NUNCA deixa de funcionar; o calendário jamais é o único caminho; grid com teclado APG completo (setas dia/semana, PageUp mês, Shift+PageUp ano) | GitLab/NHS/Scottish/APG dialog | Picker-only (prende quem digita rápido) |
| M4 | Calendário: react-day-picker (shadcn Calendar) locale pt-BR, semana começando domingo (BR), min/max, datas desabilitadas COM explicação no describedby | A11y de fábrica + a stack Lovable | Calendário próprio |
| M5 | Mobile: bottom-sheet (herda D7/H3); site simples pode usar type="date" nativo (princípio G2 — o picker do OS é excelente no toque) |
GitLab GlFormDate | Grid desktop espremido no toque |
| M6 | Defaults: agendamento abre no mês ATUAL; nascimento NUNCA tem default; autocomplete="bday" quando aplicável (WCAG 1.3.5) |
NN/g | "Hoje" pré-preenchido em nascimento |
13.2b Regra da máscara flexível (bug pego pelo Rafael na validação do preview, 2026-08-02)#
A máscara de data ACEITA o separador digitado pelo usuário ("/", e tolerante a "." e "-") como delimitador de segmento — é isso que torna "1/8/26" possível: a barra declara "dia de um dígito, próximo segmento". Máscara que só aceita dígitos corridos e injeta barras por posição fixa QUEBRA a promessa da entrada flexível (o usuário fica preso a 01082026). Regras: segmentos limitados a 2/2/4; barra automática só entra ao DIGITAR (nunca ao apagar — senão o backspace briga com a máscara); completude (zeros e século) acontece no blur, nunca durante a digitação. Esta regra vale para TODA máscara segmentada do registry B5 (data, telefone com DDD digitado com espaço, etc.): separador digitado = intenção do usuário, sempre respeitado.
13.3 Microcopy#
Ajuda com exemplo REAL: "Por exemplo: 15/03/2026" (NHS; nunca "dd/mm/aaaa" cru como placeholder — some ao digitar). Erros específicos: inexistente ("31/02 não existe — confira o dia"), fora da faixa ("Escolha uma data a partir de hoje"), formato ("Digite a data como 15/03/2026").
13.4 Os 7 testes#
- Grid: dia selecionado fundo turquesa-600 4.54:1; hoje = anel, não cor sozinha. 2. Selecionado = fundo + aria-selected; desabilitado = riscado + aria-disabled com motivo. 3. Grayscale: hoje (anel) × selecionado (fundo). 4. Par hoje × selecionado a 1s. 5. Um campo = uma data (range = dois valores NOMEADOS de/até). 6. Valor no input sempre. 7. Bottom-sheet, células 44px. ✅
Também cita o §13: banco-credencial, banco-edicao-linha, banco-texto-rico, tela-chat.