Date Input
ProntoCampo de data formatado dd/mm/aaaa com máscara automática e validação. Mais rápido que Date Picker quando o usuário SABE a data.
Usar quando
Data de nascimento, data de fato (ex: data do contrato, data da audiência conhecida). Power-users que digitam mais rápido que clicam.
Não usar quando
Data desconhecida que precisa exploração visual (use Date Picker). Mobile (use input type=date nativo).
Variantes
Estados
Microinterações
| Microinteração | Disparada por | Comportamento | Timing |
|---|---|---|---|
| Auto-format | onChange | Insere "/" automaticamente após dd e mm | instant |
| Cursor advance | completou dd ou mm | Cursor pula para próxima parte (não obriga digitar /) | instant |
| Validação on-blur | blur | Verifica data válida (32/13/aaaa = error) | instant |
| Success morph | data válida on-blur | Ícone ✓ aparece com pop | 250ms |
| Error inline | validação falhou | Borda carmim + mensagem com ícone | 150ms |
| Sugestão "1985-05-15" | detectou formato ISO | Mostra botão sugerindo "15/05/1985" | instant |
Acessibilidade
Acessibilidade — checklist
Teclado
| Tab | Foco no input |
| 0-9 | Digita; cursor avança nos slashes |
| Backspace | Volta inclusive sobre slashes (auto-skip) |
| Enter | Submete form |
ARIA esperado
- inputMode="numeric" // teclado numérico em mobile
- pattern="\d{2}/\d{2}/\d{4}"
- autoComplete="bday" // ou "off" se não for nascimento
- aria-invalid="true" quando error
- aria-describedby="hint" para placeholder
Notas
- inputMode="numeric" mostra teclado numérico em mobile.
- Hint visível "dd/mm/aaaa" — não use só placeholder.
- Validação on-blur (não on-change — usuário ainda digitando).
- Anti-pattern: 3 inputs separados (dd, mm, yyyy) — pior UX.
Código
'use client';
import { useState } from 'react';
import { T, TYPE, SP, RADIUS } from '@/lib/tokens';
function maskDate(s: string): string {
const d = s.replace(/\D/g, '').slice(0, 8);
if (d.length <= 2) return d;
if (d.length <= 4) return d.slice(0, 2) + '/' + d.slice(2);
return d.slice(0, 2) + '/' + d.slice(2, 4) + '/' + d.slice(4, 8);
}
function validate(s: string): string | null {
if (s.length !== 10) return 'Use dd/mm/aaaa.';
const [dd, mm, yyyy] = s.split('/').map(Number);
if (mm < 1 || mm > 12) return 'Mês inválido.';
const last = new Date(yyyy, mm, 0).getDate();
if (dd < 1 || dd > last) return 'Dia inválido para o mês.';
return null;
}
export function DateInput({ ...props }) {
const [v, setV] = useState('');
const [error, setError] = useState<string | null>(null);
return (
<input
inputMode="numeric"
placeholder="dd/mm/aaaa"
value={v}
onChange={(e) => setV(maskDate(e.target.value))}
onBlur={() => setError(v.length > 0 ? validate(v) : null)}
// ... styles padrão de Input
/>
);
}Regras
Faça
- ✓Máscara automática dd/mm/aaaa enquanto digita.
- ✓inputMode="numeric" para teclado mobile correto.
- ✓Validação on-blur (não on-change).
- ✓Aceite tanto colado "1985-05-15" quanto "15/05/1985".
- ✓Hint visível "dd/mm/aaaa" sob o campo.
Não faça
- ✗Não use 3 inputs separados (dd, mm, yyyy).
- ✗Não bloqueie digitação não-numérica (usuário pode estar em mobile com layout diferente).
- ✗Não valide on-change (interrompe).
- ✗Não force colar com formato específico — aceite variações.
- ✗Não esqueça de ano bissexto / dias do mês.
Tokens usados
| Token | Valor | Papel |
|---|---|---|
T.surface | #FFFFFF | background |
T.border | #E3DFD2 | borda default |
T.borderInk | #000000 | borda focus |
T.carmim | #A32E2E | erro de validação |
T.verde | #2F6B3C | ícone success |
TYPE.base | 13 | fontSize |