Aguarde...

30 de maio de 2024

Elemento personalizado “Dots” (também conhecido como componente da Web)

Elemento personalizado “Dots” (também conhecido como componente da Web)

o protótipo

De qualquer forma, estou codificando um elemento personalizado “Dots” . Pontos são aqueles pequenos botões complicados usados ​​para navegar em carrosséis e coisas assim. Sim, eu sei que carrosséis são ruins e protesto, mas nem sempre a decisão é minha.

Veja este GIF:

interagindo com o componente “Pontos”

Obviamente, um monte de pontos são inúteis por si só. Pretendo combiná-los com um componente carrossel que criarei a seguir. Isso já não é um problema resolvido? Tenho pesquisado muitos carrosséis, todos implementados de maneira diferente. Meu objetivo é construir um conjunto de elementos combináveis ​​que possam formar um carrossel ou ser usados ​​em outro lugar. Onde mais os “Pontos” podem ser usados? Não sei, pare de fazer perguntas difíceis.

Estilizando

Este é o aspecto menos importante do componente. Adicionei CSS suficiente para a demonstração ficar bonita. A única coisa que vale a pena notar é o pseudoelemento oculto que estou usando para expandir a área de destino. Os pontos são 50% maiores do que parecem e, portanto, mais fáceis de cutucar. O efeito alongado é opcional e facilmente removido. Não tenho certeza se gosto, mas está na moda e é uma demonstração visual mais interessante.

Marcação acessível

Achei que seria um elemento personalizado simples de codificar, mas é surpreendentemente complicado. Qual é a marcação correta? Eu poderia usar uma lista de botões:

<dots-component>  <ol>    <li>      <button type="button" aria-selected="true">        <span>Item 1</span>      </button>    </li>    <li>      <button type="button">        <span>Item 2</span>      </button>    </li>  </ol></dots-component>

Muitos exemplos que pesquisei usam uma lista. A maioria gosta do uso do Splide<ul> , mas certamente <ol>o uso do Flickity está correto? Independentemente disso, optei por ficar sem:

<dots-component>  <button type="button" aria-selected="true">    <span>Item 1</span>  </button>  <button type="button">    <span>Item 2</span>  </button></dots-component>

Os leitores de tela podem anunciar os números da lista, o que parece redundante e confuso com os rótulos dos botões. Estou adicionando roleatributos posteriormente, o que também afeta isso.

Eu ponderei se <button>estava correto ou se deveria permitir <a href="#item-1">um aprimoramento mais progressivo. Como (ainda) não estou construindo o carrossel inteiro, não posso presumir que os links de fragmentos foram implementados corretamente.

O carrossel do Bootstrap renuncia a qualquer elemento interativo e espera que o usuário clique em uma altura de três pixels <li>– ah, querido. O carrossel Glide não está listado com <button>elementos que não possuem rótulo. Nenhum deles afirma ser acessível e o Bootstrap chega a dizer que “os carrosséis geralmente não atendem aos padrões de acessibilidade” . Pelo menos eles são honestos.

Para rótulos de botões, considerei usar aria-labelem vez do arquivo oculto span. O exemplo do W3C adota essa abordagem. No entanto, o W3C também diz que a primeira regra do ARIA é que vocênão fale, quero dizer, vamos apenas citar o documento:

Se você puder usar um elemento ou atributo HTML nativo com a semântica e o comportamento necessários já integrados, em vez de redirecionar um elemento e adicionar uma função, estado ou propriedade ARIA para torná-lo acessível, faça-o.

Resumindo: “Se você pode usar [HTML nativo], faça-o” . Então eu fiz isso. Chris Ferdinandi escreveu sobre outros problemas usando o aria-label, então estou evitando isso.

Padrões como o carrossel do Filament Group são usados role="tablist"​​na embalagem role="tab"dos botões. Adicionei essas funções porque é uma prática que parece incontestada. O VoiceOver da Apple anuncia o número de guias durante a navegação.

Usar aria-selectedo botão ativo é o atributo aceito para guias. Inicialmente pensei aria-currentmas isso é mais apropriado para paginação. Claro, eu poderia usar uma classe HTML para estilizar, seguindo a regra ARIA 1, mas esse atributo adiciona contexto e estilo [aria-selected="true"]é um bônus.

A marcação final fica assim:

<dots-component role="tablist">  <button type="button" aria-selected="true" tabindex="0" role="tab">    <span>Item 1</span>  </button>  <button type="button" aria-selected="false" tabindex="-1" role="tab">    <span>Item 2</span>  </button></dots-component>

O elemento personalizado JavaScript adiciona e atualiza os atributos conforme necessário (notas tabindexabaixo). O que você acha? Estou procurando feedback.

API interativa

Estou mais confiante na questão da interatividade. O JavaScript é bastante robusto e pode lidar com modificações no DOM se botões forem adicionados, removidos, etc. Os seletores de consulta não são armazenados em cache (otimização excessiva). Um único clickouvinte de evento é registrado para lidar com a navegação do ponteiro.

Eu implementei o padrão “The Looper”, como Adam Argyle o chama. Basicamente, assim como os grupos de entrada de rádio, apenas o elemento ativo está na sequência de guias do documento. Isso é feito usando tabindexatributos. Quando um botão está em foco, todo o grupo pode ser percorrido usando as teclas de seta. O fluxo de conteúdo da direita para a esquerda é contabilizado.

Atributos

Meu componente suporta um disabledatributo.

<dots-component disabled>  <!-- buttons --></dots-component>

Os atributos podem ser alternados com JavaScript de duas maneiras:

const dots = document.querySelector('dots-component');dots.disabled = true; // set directlydots.removeAttribute('disabled'); // use methods

disabledatributo é transmitido a todos <button>os filhos que sincronizam seu estado.

Jeremy Keith sugere usar odata-prefixo para atributos personalizados para evitar conflitos com possíveis atributos globais futuros. Recentemente critiquei o HTMX por usarhx-overdata-. Angular faz o mesmo comng-. Mas esses dois exemplos geralmente não usam elementos personalizados. Elementos personalizados permitem atributos não prefixados, o que me surpreende. Há muito mais discussão, mas parece que o cavalo fugiu dessa.

Acho que meu disabledatributo é seguro — digo hesitante — porque estou imitando um que já existe. Dito isto, não é idêntico porque a :disabledpseudoclasse CSS não funciona. Então talvez eu esteja cometendo o pior pecado…

Existe uma API ElementInternals mais recente que permite estado personalizado:

class DotsElement extends HTMLElement {  #internals;  connectedCallback() {    this.#internals = this.attachInternals();    this.#internals.states.add('disabled');  }}

O estado personalizado pode ser selecionado com CSS:

dots-component {  &:state(disabled) {    pointer-events: none;  }}

Este estado não se traduz diretamente em atributos HTML; eles devem ser conectados manualmente. Para o meu caso de uso, ainda preciso propagar o disabledatributo. No código completo estou usando métodos get/set junto com attributeChangedCallback.

A questão permanece: devo manter disabledou usar data-disabled<dots-component>elemento personalizado?

Eventos e Métodos

Meu elemento personalizado despacha um changeevento.

const dots = document.querySelector('dots-component');dots.addEventListener('change', (ev) => {  console.log(    `Index: ${ev.detail.activeIndex}`,    `Label: "${ev.target.activeElement.innerText}"`  );});

O elemento também possui alguns métodos:

dots.goto(2);dots.previous();dots.next();

Estes devem ser autoexplicativos.

Próxima Etapa

Conforme mencionado, pretendo construir a(s) outra(s) peça(s) de um padrão de carrossel. Já tentei isso antes com meu Carrossel responsivo principalmente a CSS. Essa versão não usa elementos personalizados e os “pontos” foram uma reflexão tardia.

Aliás, estou usando o nome <dots-component>por falta de ideia melhor. Sugestões são bem-vindas. Tenha um bom dia!

Postado em BlogTags:
Escreva um comentário