civil-and-structural-engineering
Construindo um Picker de Data de Javascript Personalizado com Suporte de Acessibilidade
Table of Contents
O desafio da entrada de data na Web
A seleção de datas é uma das interações mais comuns e frustrantes na web. Os catadores nativos de datas de navegador variam de forma selvagem entre plataformas, e o padrão oferece controle de estilo limitado e comportamento de teclado inconsistente. Construir um seletor de datas personalizado lhe dá controle total sobre aparência, comportamento e acessibilidade. Quando feito corretamente, torna-se um componente inclusivo que serve aos usuários, independentemente de seu dispositivo, método de entrada ou tecnologia assistiva.
Um selecionador de datas acessível não é apenas sobre o cumprimento das diretrizes WCAG. Ele impacta diretamente os usuários reais: pessoas que navegam com teclados, aqueles que dependem de leitores de tela, indivíduos com deficiências motoras que usam dispositivos de switch, e qualquer um que se beneficia de pistas visuais claras. As técnicas aqui cobertas se aplicam a qualquer framework JavaScript, mas os exemplos usam JavaScript baunilha para que você possa adaptá-los ao React, Vue, ou qualquer outro ambiente.
Fundações de Acessibilidade para Catadores de Datas
Compreender os Critérios de Sucesso da WCAG
Vários critérios de sucesso WCAG 2.1 aplicam-se diretamente aos catadores de data. A acessibilidade do teclado (SC 2.1.1) requer que toda a funcionalidade seja operable através de uma interface de teclado. Focus visible (SC 2.4.7) exige um indicador de foco claro para que os usuários saibam qual data é selecionada. Nome, função, valor (SC 4.1.2) garante que as tecnologias assistivas podem identificar o componente e seu estado atual. O Guia de Práticas de Autoria ARIA fornece um padrão de dialog [ que serve como base para widgets de calendário.
HTML semântico como a espinha dorsal
Comece com elementos HTML semânticos em vez de genéricos ]. A grade de calendário deve usar um elemento porque representa semanticamente dados tabulares. Cada célula de data deve ser um elemento , que é inerentemente focalizável e ativable via teclado. Evite usar elementos como alvos interativos porque eles não têm suporte ao teclado nativo e requerem ARIA adicional para comunicar interatividade. Uma base semântica sólida reduz a quantidade de ARIA que você precisa adicionar e cria uma experiência mais robusta em tecnologias assistivas.
Arquitectar o componente de seleção de datas
Elementos de Interface de Utilizador Principal
Um coletor de datas completo precisa desses componentes visuais e funcionais:
- Um campo de entrada de texto que mostra a data selecionada e ativa o calendário
- Uma janela de calendário contendo um cabeçalho mês/ ano, botões de navegação para alterar meses e uma grade de dias
- Data de hoje realçada visualmente para orientação
- A data selecionada está marcada com um estilo distinto
- Um botão claro ou tecla Escape para descartar o calendário sem selecionar
Gestão do Estado
Internamente, o seletor de datas deve rastrear vários pedaços de estado: a data atualmente focada (que pode diferir da data selecionada), o mês e ano visíveis, o valor da data selecionado e se o calendário está aberto ou fechado. Mantenha-os como variáveis JavaScript e atualize os atributos DOM e ARIA em resposta às alterações de estado. Separar o estado da renderização torna o componente mais fácil de depurar e estender com recursos como intervalos de datas ou vários meses.
Construindo a Estrutura HTML para Acessibilidade
O campo de entrada
O campo de entrada é o ponto primário de interação do usuário. Ele deve comunicar seu propósito e relação com o calendário popup usando atributos ARIA:
Exemplo de entrada acessível:
<label for="date-picker-input">Departure date</label>
<input type="text"
id="date-picker-input"
role="combobox"
aria-haspopup="dialog"
aria-expanded="false"
aria-controls="datepicker-calendar"
aria-autocomplete="none"
readonly
placeholder="MM/DD/YYYY">
O padrão sinaliza para tecnologias assistivas que esta entrada controla um popup. O atributo reflete dinamicamente se o calendário é visível. Definir a entrada como evita a entrada de texto manual enquanto permite o foco do teclado e clica em eventos. Para implementações que permitem digitar datas diretas, adicione lógica de validação e gerenciamento de estado.
A Janela do Calendário
O calendário segue o padrão dialog da ARIA Authoring Practices. Deve ser renderizado como um irmão para o campo de entrada no DOM, tipicamente envolto em um recipiente que lida com o posicionamento:
<div id="datepicker-calendar"
role="dialog"
aria-modal="true"
aria-label="Choose a date"
hidden>
<div class="calendar-header">
<button class="prev-month" aria-label="Previous month">‹</button>
<h3 id="calendar-month-year" aria-live="polite">January 2025</h3>
<button class="next-month" aria-label="Next month">›</button>
</div>
<table class="calendar-grid" role="grid" aria-labelledby="calendar-month-year">
<thead>
<tr>
<th scope="col"><abbr title="Sunday">Sun</abbr></th>
<th scope="col"><abbr title="Monday">Mon</abbr></th>
<th scope="col"><abbr title="Tuesday">Tue</abbr></th>
<th scope="col"><abbr title="Wednesday">Wed</abbr></th>
<th scope="col"><abbr title="Thursday">Thu</abbr></th>
<th scope="col"><abbr title="Friday">Fri</abbr></th>
<th scope="col"><abbr title="Saturday">Sat</abbr></th>
</tr>
</thead>
<tbody>
<!-- Date rows rendered dynamically -->
</tbody>
</table>
<div class="calendar-footer">
<button class="today-button">Today</button>
</div>
</div>
O na tabela fornece navegação otimizada para leitores de tela dentro de layouts tabulares. Cada célula de data deve ser um elemento com o explicitamente aplicado se usar um elemento não-semântico. A região no cabeçalho mês/ano garante que os leitores de tela anunciam mudanças de mês sem interromper o usuário.
Implementação de Navegação de Teclado
Estratégia de Gestão de Foco
Quando o calendário for aberto, o foco será movido para a data selecionada atualmente, ou para a data atual, se não existir nenhuma seleção. Isto segue o padrão [[FLT: 0]] de foco automático[[ FLT: 1]]. O calendário deverá capturar o foco dentro de si mesmo enquanto estiver aberto, impedindo o usuário de tagar para elementos por trás do popup. Implemente uma armadilha de foco que ciclos entre o primeiro e último elemento focalizável dentro da janela.
Mapeamento de Eventos de Teclado
Os catadores de datas exigem uma interface de teclado consistente. O padrão de grade [[FLT: 0]] ARIA define mapeamentos de teclas padrão para navegar por conteúdo tabular:
- [[FLT: 0]] Teclas de arrow ] mover o foco um dia na direção correspondente. Quando o foco se move para além do mês atual, o calendário muda automaticamente para o mês adjacente.
- Home and End movem o foco para o primeiro ou último dia do mês atual.
- Page Up and Page Down navegue até o mês anterior ou próximo, preservando o dia do mês (clamado até o último dia do mês-alvo, se necessário).
- Enter or Space seleciona a data atualmente focada e fecha o calendário.
- Escape fecha o calendário e retorna o foco para o campo de entrada.
- Tab move o foco dentro dos componentes da janela (botões de navegação do mês, grade, botão de hoje).
Gerenciando o tabindex roving
Use o padrão ]roving tabindex dentro da grade de datas. Apenas um botão de data tem a qualquer momento, enquanto todos os outros botões de data têm . Isto garante que pressionar Tab uma vez entra na grade e pressionar Tab novamente se move para o próximo elemento focal fora da grade. Quando as teclas de seta moverem o foco, atualize os valores de acordo e chame na célula recém- ativa.
Atributos ARIA para o suporte ao leitor de tela
Gestão Dinâmica do Estado da ARIA
Os leitores de tela dependem dos atributos ARIA para comunicar alterações de estado. Atualizar esses atributos em tempo real, à medida que o usuário interage com o selecionador de datas:
- ]Aria-expandida na entrada: definida como quando o calendário se abre, quando fecha.
- aria-oculta] na janela do calendário: remover () quando visível, adicionar () quando oculto.
- aria-selecionado[] no botão de data selecionado: definido como na data escolhida, em todos os outros. Note que os botões suportam nativamente um estado pressionado, mas é o atributo correto para funções de quadrícula.
- ]Aria-activedescendente na entrada: aponte para o ID da célula de data atualmente focada.
- ]Rótulo de aria]em cada botão de data: incluir a data completa, como "15 de janeiro de 2025" em vez de apenas "15".
Anunciando as Alterações de Contexto
Quando o usuário navega para um novo mês, anuncie o mês e o ano usando uma região ao vivo. Muitas implementações atualizam o na janela ou usam uma região visualmente escondida . Quando uma data é selecionada, anuncie "15 de janeiro de 2025 selecionado" para confirmar a ação. Forneça uma região para confirmações críticas e para atualizações menos urgentes, como mudanças mensais.
Detalhes da Implementação do JavaScript
Inicialização e Configuração
Criar uma classe ou função de fábrica DatePicker que aceite opções de configuração: o elemento de entrada, o formato de data, as datas mínimas e máximas e as funções de retorno de chamadas para a selecção de datas. Inicializar configurando os ouvintes de eventos na entrada e renderizando o mês de calendário inicial:
class AccessibleDatePicker {
constructor(input, options = {}) {
this.input = input;
this.options = Object.assign({
format: 'MM/DD/YYYY',
minDate: null,
maxDate: null,
onDateSelect: () => {}
}, options);
this.selectedDate = null;
this.focusedDate = null;
this.currentMonth = new Date().getMonth();
this.currentYear = new Date().getFullYear();
this.isOpen = false;
this.calendar = this.buildCalendarElement();
this.setupEventListeners();
}
// ... methods
}
A renderizar a Grelha do Calendário
A função renderização calcula o primeiro dia do mês, o número de dias do mês e quaisquer dias seguintes do mês anterior e do mês seguinte para preencher a grade. Cada botão de data recebe os atributos de dados para os componentes de data e [FLT: 31]] com a data legível pelo homem:
renderMonth() {
const firstDay = new Date(this.currentYear, this.currentMonth, 1).getDay();
const daysInMonth = new Date(this.currentYear, this.currentMonth + 1, 0).getDate();
const daysInPrevMonth = new Date(this.currentYear, this.currentMonth, 0).getDate();
let cells = [];
// Add trailing days from previous month
for (let i = firstDay - 1; i >= 0; i--) {
cells.push(this.createDateButton(daysInPrevMonth - i, this.currentMonth - 1, this.currentYear, true));
}
// Add days of current month
for (let day = 1; day <= daysInMonth; day++) {
cells.push(this.createDateButton(day, this.currentMonth, this.currentYear));
}
// Add leading days of next month
const remaining = 42 - cells.length; // 6 rows × 7 columns
for (let day = 1; day <= remaining; day++) {
cells.push(this.createDateButton(day, this.currentMonth + 1, this.currentYear, true));
}
// Render into table body...
}
Delegação de eventos para conteúdo dinâmico
Em vez de anexar os ouvintes de eventos a cada botão de data individualmente, use a delegação de eventos no recipiente da grade. Ouça os eventos no elemento e determine qual data foi clicada usando . Esta abordagem lida com datas geradas dinamicamente sem religar os ouvintes e reduz o uso da memória. Para eventos de teclado, anexe um ] ao recipiente da grade que mapeia a tecla pressiona para as ações de navegação e seleção.
Fluxo de Seleção de Datas
Quando um usuário seleciona uma data, o componente formata a data de acordo com o formato configurado, atualiza o valor de entrada, armazena a data selecionada internamente e chama o retorno de chamada . Após a seleção, o calendário fecha e foca retorna à entrada. Atualize o atributo na célula selecionada anteriormente para e na célula selecionada recentemente para . Ativar um evento personalizado no elemento de entrada, para que outros scripts que escutam as alterações possam reagir.
Styling Visual para Acessibilidade
Independência de Contraste e Cor
A cor nunca deve ser o único indicador de estado. Use uma combinação de cor de fundo, cor de texto, borda e decoração de texto para comunicar estados selecionados, focados e desativados. Mantenha uma proporção de contraste mínima de 3:1 para conteúdo não- texto e de 4,5:1 para conteúdo de texto por WCAG SC 1.4.3 e 1.4.11. Providencie um modo de alto contraste que usa bordas arrojadas e sublinha em vez de alterações de cores sutis.
Desenho do indicador de foco
O indicador de foco deve ser visível contra todos os elementos circundantes. Use um ou com espessura e contraste suficientes. Evite remover o contorno padrão do navegador sem fornecer uma substituição. Um contorno sólido de 3 pixels com uma cor que contrasta tanto com o fundo padrão quanto com o fundo selecionado garante visibilidade. Teste o indicador de foco contra o estado de data selecionado, estado desativado e estado de pairar para confirmar que ele permanece distinguível.
Tamanho do Alvo de Toque
Cada botão de data deve ter um alvo de toque mínimo de 44x44 pixels CSS, conforme recomendado pelo WCAG SC 2.5.8. Isto aplica- se a dispositivos móveis, tablets e utilizadores com baixa destreza que possam usar o toque ou a entrada de estilo. Certifique- se de botões de navegação do mês e o botão de hoje também atendem a estas dimensões mínimas. Se as restrições de espaço exigirem alvos menores, forneça um revestimento visual ou aumente a área clicável usando pseudo- elementos ].
Testando seu selecionador de datas para acessibilidade
Teste manual do teclado
Testar o componente usando apenas um teclado. Aba para a entrada, abrir o calendário com Enter ou Space e navegar por todas as datas usando as teclas de setas. Verifique se Escape fecha o contexto e retorna o foco para a entrada. Confirme que o indicador de foco está visível em todos os momentos e que a ordem de foco segue a disposição visual. Teste com as teclas Page Up, Page Down, Home e End para garantir que eles executam o esperado navegação mensal e dia. Documente qualquer comportamento inesperado e corrija- o antes de considerar o componente completo.
Teste do Leitor de Tela
Teste com pelo menos dois leitores de tela. Uma combinação de NVDA (gratuito) com JAWS[ (comercial) com Chrome cobre a maioria dos usuários de leitores de tela. Verifique se o leitor de tela anuncia a função de diálogo, o mês e o ano, e a data como o usuário navega. Confirme que estado é anunciado e que a data selecionada é comunicada quando a janela se abre. Teste com um leitor de tela em um dispositivo móvel usando TalkBack (Android) ou VoiceOver (iOS) para garantir que a navegação toque funciona.
Ferramentas de Teste Automatizadas
Use ferramentas automatizadas como axe DevTools ou Lighthouse para captar problemas de acessibilidade comuns. Estas ferramentas podem identificar atributos ARIA em falta, contraste de cor insuficiente e rótulos em falta. No entanto, os testes automatizados não podem detectar todos os problemas. Os testes manuais continuam a ser essenciais para avaliar anúncios de leitores de tela, fluxo de teclado e usabilidade do mundo real. Integre testes de acessibilidade em seu pipeline de integração contínua usando ferramentas como axe-core para capturar regressões precocemente.
Considerações sobre Integração no Mundo Real
Internacionalização e Localização
Os catadores de datas servem um público global. Suporte várias localidades usando a API para formatar datas e nomes de mês. Permita a configuração do primeiro dia da semana (domingo vs. segunda- feira) com base na localidade. Rendere os cabeçalhos do dia da semana usando nomes abreviados que respeitam a localidade. Forneça traduções para as etiquetas ARIA como "Mês anterior" e "Escolha uma data". A documentação Intl.DateTimeFormat[] fornece orientações abrangentes sobre a formatação de datas locale- aware.
Otimização de Toque e Móvel
Nos dispositivos móveis, posicione a janela do calendário para evitar sobreposições com o teclado virtual. Considere usar um modo de ecrã completo para o calendário em ecrãs pequenos, com toda a janela dedicada à interface de selecção de datas. Implemente gestos de deslize para navegação mensal como um aumento progressivo. Teste em dispositivos móveis reais com entrada de leitor de ecrã e toque para confirmar que os alvos de toque são suficientemente grandes e que as acções de gesto têm recuos acessíveis.
Desempenho e Tamanho do Pacote
Um captador de datas personalizado não deverá inchar a sua aplicação. Mantenha o pacote JavaScript abaixo de 10KB minimizado e gzipped evitando dependências desnecessárias e escrevendo código eficiente. Use encerramentos e funções de fábrica para evitar a sobrecarga da herança de classes. Iniciem o calendário DOM apenas quando o usuário abrir o captador pela primeira vez, em vez de criá- lo na carga da página. Limpe os ouvintes de eventos e as referências DOM quando o componente for destruído para evitar vazamentos de memória em aplicações de uma página.
Manusear casos de borda
Datas Inabilitadas e Restritas
Implementar uma função que verifica as datas min/max, as restrições do dia- da- semana e as regras de exclusão personalizadas. As datas desactivadas devem receber e ser excluídas da navegação do teclado. Visualmente, as datas desactivadas devem usar opacidade reduzida e evitar efeitos de hover. Quando uma página do utilizador passa pela grelha, ignora as datas completamente desactivadas. Forneça uma dica ou uma dica explicando por que razão uma data não está disponível, acessível através de ].
Valores nulos e ações claras
Suporte para limpar a data selecionada, fornecendo um botão limpo dentro do rodapé do calendário ou permitindo que o usuário faça um novo clique na data selecionada atualmente. Quando nenhuma data estiver selecionada, o substituto de entrada deverá comunicar o formato esperado. O calendário deverá abrir mostrando o mês atual sem nenhuma data selecionada, mas com foco na data atual para orientação. Persista no último mês selecionado, de modo que, se o usuário reabrir o calendário, eles retornem ao mês em que estavam visualizando anteriormente.
Compatibilidade com o navegador e o framework
Teste o identificador de datas através do Chrome, Firefox, Safari e Edge. Preste atenção especial ao Safari, que historicamente teve inconsistente suporte indicador de foco]. Use para fornecer indicadores de foco apenas quando o usuário navega através do teclado, evitando contornos persistentes em interações de clique. Para frameworks como React, envolva o coletor de datas em um gancho personalizado que gerencia estado e ciclo de vida através de e . Para Vue, use e para reagir às mudanças de suporte e eventos de entrada.
Recompondo tudo
Um seletor de datas acessível é construído com base em HTML semântico, gestão cuidadosa de ARIA e gestão robusta de eventos de teclado. O campo de entrada usa a semântica combobox para comunicar a sua relação com a janela de calendário. A grade de datas segue o padrão de tabindex para navegação eficiente de teclado. Os atributos ARIA atualizam dinamicamente para refletir o estado atual, os leitores de tela recebem anúncios de contexto através de regiões ao vivo e o design visual mantém um contraste suficiente e indicadores de foco claros.
A medida real do sucesso não é quantos atributos ARIA você adiciona, mas como a experiência se sente sem problemas para os usuários de tecnologia assistiva. Um selecionador de datas bem construído deve se sentir tão natural para um usuário de teclado quanto para um usuário de mouse. Ao seguir os padrões descritos aqui e testar completamente com tecnologias assistivas reais, você cria um componente que serve a todos.
Acessibilidade não é uma opção de recurso ou uma etapa final de polimento. É parte integrante do processo de design e desenvolvimento. Quando você constrói com acessibilidade desde o início, você evita refatores caros e oferece uma experiência melhor para todos os usuários. O seletor de datas é apenas um componente, mas os princípios que você aplica aqui carregam em todos os elementos interativos que você constrói.