1658557920
As guias de interface são um componente comum do site, mas os desenvolvedores geralmente enfrentam desafios para tornar esse padrão acessível. Os usuários de leitores de tela podem encontrar alguns problemas ao ler o conteúdo, e os usuários que dependem de um teclado para navegar em um site podem precisar de assistência para facilitar a navegação.
Este artigo cobrirá tudo o que você precisa saber para criar uma interface de guia acessível, incluindo a construção da estrutura HTML, a adição do estilo CSS e a adição de aprimoramentos da funcionalidade JavaScript.
Aqui está a interface com guias que construiremos neste tutorial:
Uma interface com guias listando diferentes países. À esquerda, as abas são exibidas verticalmente e à direita, há um texto fictício e uma imagem para o país selecionado (neste caso, Mongólia).
Vamos começar!
A estrutura HTML é muito importante porque fornece aos usuários de leitores de tela o contexto necessário para navegar na interface.
Existem duas maneiras de usar a estrutura HTML para ajudar a definir quais elementos podem ser focados com um teclado. Podemos usar uma abordagem baseada em link com o <a>
elemento como nosso gatilho de guia ou podemos usar o <button>
elemento.
Ambas as abordagens levam em consideração a navegação pelo teclado por serem nativamente focáveis pelo teclado, mas cada uma tem vantagens e desvantagens. Abordaremos ambas as abordagens neste artigo.
<a>
elemento como a guiaA principal vantagem dessa abordagem é que podemos usar a <a>
funcionalidade do elemento para criar o efeito de navegação da guia usando apenas HTML e CSS. O <a>
elemento traz suporte ao teclado cuidando muito do gerenciamento de foco e navegação para usuários de leitores de tela para nós!
Essa abordagem também é útil para usuários que têm o JavaScript desabilitado. Usaremos JavaScript apenas no final deste tutorial — para aprimorar algumas das <a>
funcionalidades do elemento para fins de acessibilidade.
No entanto, uma pequena desvantagem de usar o <a>
elemento como guia é que ele é contado no histórico de navegação do usuário. Em outras palavras, se o usuário visitar todas as abas e depois quiser retornar ao site anterior, terá que pressionar o botão voltar várias vezes.
Ainda assim, essa abordagem tem aprimoramento progressivo em mente, pois oferece a maior funcionalidade de interface de guia com a menor tecnologia possível. Essa abordagem funcionará mesmo que o JavaScript não carregue e mesmo que o CSS também falhe, então vale a pena considerar.
<button>
elemento como a guiaUsar o <button>
elemento como guia tem a vantagem de não afetar o histórico de navegação, facilitando o retorno do usuário ao site anterior.
No entanto, essa abordagem requer algumas considerações extras com HTML para ocultar todo o conteúdo de guias não ativas. Também requer algumas decisões adicionais em relação ao JavaScript, como determinar como exibir conteúdo, estratégias de gerenciamento de foco e alguns aprimoramentos de acessibilidade.
Se você usar essa abordagem, precisará considerar o que acontecerá se o JavaScript não carregar em seu site. Sem um fallback, os usuários não poderão navegar em sua interface com guias. Existem várias razões pelas quais o JavaScript pode falhar ao carregar. Por exemplo, os usuários podem ter o JavaScript desabilitado devido a questões de rastreamento e privacidade, ou o JavaScript pode falhar ao carregar devido a fatores externos.
Depois de decidir qual abordagem você usará, é hora de começar a criar a estrutura HTML!
<a>
abordagem do elementoVamos começar com as guias individuais. Aqui, usaremos a <a>
abordagem de elemento para a estrutura HTML:
<!-- Tab semantics -->
<ul role="tablist">
<li role="presentation">
<a role="tab" href="#panel1" id="tab1">Tab one</a>
</li>
<li role="presentation">
<a role="tab" href="#panel2" id="tab2">Tab two</a>
</li>
<li role="presentation">
<a role="tab" href="#panel3" id="tab3">Tab three</a>
</li>
<li role="presentation">
<a role="tab" href="#panel4" id="tab4">Tab four</a>
</li>
</ul>
<!-- Tab content semantics -->
<div class="tabpanel-container">
<section role="tabpanel" id="panel1" aria-labelledby="tab1" tabindex="0"></section>
<section role="tabpanel" id="panel2" aria-labelledby="tab2" tabindex="0"></section>
<section role="tabpanel" id="panel3" aria-labelledby="tab3" tabindex="0"></section>
<section role="tabpanel" id="panel4" aria-labelledby="tab4" tabindex="0"></section>
</div>
Agora, vamos examinar a estrutura mostrada no código acima:
<a>
ou <button>
) dentro da lista itens dentro de um <ul>
elementotablist
função ao nosso <ul>
para fornecer mais contexto para usuários de leitores de tela; essa função é usada para marcar um contêiner que envolve um conjunto de guias. Como o tablist
papel não fornece a semântica de tabulação ao elemento filho, adicionamos o tab
papel ao nosso <a>
elementotablist
e tab
, o HTML ainda será lido como uma lista de links ou botões, o que é um substituto aceitávelpresentation
papel aos <li>
elementos apenas para remover a semântica; isso ajudará a evitar interações estranhas, mantendo nosso fallback caso um leitor de tela específico não ofereça suporte a funções ARIA<section>
elemento com o tabpanel
papel. Espera-se que esse elemento use o mesmo nome da guia como um nome acessível. É por isso que adicionamos o id
atributo às nossas guias e o estamos usando como um rótulo em nossos painéis de guias com o aria-labelledby
atributotabindex="0"
aos nossos painéis de guias para permitir que itens dentro da guia (como campos de formulário, links ou botões) recebam o foco do teclado. Isso tornará mais fácil para os usuários de teclado acessar o conteúdo<button>
abordagem do elementoSe usarmos a <button>
abordagem baseada em -, precisaremos incluir uma etapa extra. Precisaremos adicionar o hidden
atributo a todos os painéis de guias, exceto a primeira guia. Este atributo ocultará os elementos para usuários com visão e usuários de leitores de tela. Para garantir que a primeira guia permaneça visível, ela deve ter o aria-selected="true"
atributo.
Nossa marcação para a <button>
abordagem deve ser algo assim:
<!-- Tab semantics -->
<ul role="tablist">
<li role="presentation">
<button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
</li>
<li role="presentation">
<button role="tab" href="#panel2" id="tab2">Tab two</button>
</li>
<li role="presentation">
<button role="tab" href="#panel3" id="tab3">Tab three</button>
</li>
<li role="presentation">
<button role="tab" href="#panel4" id="tab4">Tab four</button>
</li>
</ul>
<!-- Tab content semantics -->
<div class="tabpanel-container">
<section role="tabpanel" id="panel1" aria-labelledby="tab1" tabindex="0"></section>
<section role="tabpanel" id="panel2" aria-labelledby="tab2" tabindex="0" hidden></section>
<section role="tabpanel" id="panel3" aria-labelledby="tab3" tabindex="0" hidden></section>
<section role="tabpanel" id="panel4" aria-labelledby="tab4" tabindex="0" hidden></section>
</div>
Até agora, cobrimos as considerações iniciais para nossa marcação, mas há fatores adicionais que precisamos ter em mente para determinados casos de uso. Alguns fatores serão controlados de forma mais dinâmica, mas falaremos sobre eles um pouco mais adiante na seção JavaScript deste artigo.
Podemos relacionar as guias com o painel de guias para usuários de leitores de tela usando o atributo aria-controls
. No entanto, essa abordagem funcionará apenas no leitor de tela JAWS e seu uso pode parecer bastante detalhado, conforme explicado no artigo de Heydon Pickering . Em vez disso, usaremos algumas estratégias de gerenciamento de foco que também ajudarão os usuários de teclado.
Para exibir uma lista de guias com orientação vertical, você precisará adicionar o atributo [aria-orientation="vertical"]
ao contêiner. Isso indicará aos usuários de leitores de tela que as guias estão empilhadas verticalmente.
Isso será importante quando começarmos a discutir estratégias de navegação para nosso exemplo de interface com guias.
Aqui está um exemplo mostrando como o [aria-orientation="vertical"]
atributo seria usado em um snippet de código:
<ul role="tablist" aria-orientation="vertical">
<li role="presentation">
<button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
</li>
<li role="presentation">
<button role="tab" href="#panel2" id="tab2">Tab two</button>
</li>
<li role="presentation">
<button role="tab" href="#panel3" id="tab3">Tab three</button>
</li>
<li role="presentation">
<button role="tab" href="#panel4" id="tab4">Tab four</button>
</li>
</ul>
Quando um usuário clicar em uma guia, precisaremos de uma maneira de indicar que ela foi selecionada.
Usaremos o atributo [aria-selected="true"]
para indicar a seleção de guias para usuários de leitores de tela. Em seguida, vamos estilizá-lo com CSS para ajudar a indicar a seleção para usuários com visão. O [aria-selected="true"]
atributo mudará dinamicamente com JavaScript, portanto, não será adicionado à nossa marcação inicial.
<li role="presentation">
<button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
</li>
Agora que temos uma estrutura HTML sólida, é hora de adicionar algum estilo com CSS !
Para nosso exemplo de interface com guias, estamos usando uma abordagem baseada em link com o <a>
elemento como nosso gatilho de guia. Para estilizar a estrutura de guias HTML, precisaremos adicionar a navegação de guias, criar o indicador de foco e identificar o estilo do estado selecionado.
Para adicionar a navegação da guia, vamos adicionar uma regra CSS usando o :target
seletor assim:
.tabpanel:not(:target):not(.visible) {
display: none;
}
:target
é uma pseudo-classe que verifica se um elemento com um id
corresponde ao fragmento da URL.
Como estamos usando o <a>
elemento aqui para adicionar funcionalidade, podemos usar esse seletor para ocultar quaisquer painéis de guias não ativos. Estamos usando a visible
classe como uma exceção em nosso exemplo para que possamos adicionar alguns aprimoramentos com JavaScript posteriormente .
Outra coisa que precisamos ter em mente é a navegação pelo teclado, e uma das coisas mais importantes a fazer nesse sentido é criar um indicador de foco adequado.
Você provavelmente notou um indicador de foco ao navegar em um site usando a tecla tab . Dependendo do seu navegador, aparecerá como um contorno azul ou preto destacando um elemento selecionado.
Os navegadores oferecem estilos de foco padrão, mas não são suficientes para atender aos critérios WCAG 2.2 . Portanto, é necessário usar um estilo adicional. Para obter informações mais detalhadas sobre como criar um bom indicador de foco, leia o artigo de Sara Souiedan sobre este tópico.
Para nosso exemplo, usaremos os seguintes estilos de foco:
.tab:focus-visible {
background-color: royalblue;
color: whitesmoke;
outline: 0.2em solid transparent;
}
Decidi usar :focus-visible
em vez da :focus
pseudo-classe para que o estilo de foco seja ativado apenas com a navegação do teclado. A :focus
pseudo-classe também será ativada quando o elemento for clicado, o que pode ser confuso para os usuários.
:focus-visible
é muito bem suportado em navegadores modernos, por isso não deve criar nenhum tipo de conflito. Para obter mais informações sobre essa propriedade, consulte a documentação do MDN .
A opção “Mongólia” selecionada abaixo mostra como ficaria o estado de foco da nossa guia:
A primeira guia na lista de guias do projeto tem um fundo azul, fornecendo uma dica visual para a seleção de guias para usuários de teclado.
Para este projeto, decidi usar um contorno transparente. Isso é importante para o modo de alto contraste do Windows . Nesse modo, todas as cores do texto e do plano de fundo são substituídas pelas escolhidas pelo sistema operacional, portanto, não podemos depender da cor do plano de fundo para indicar um estado de foco. O contorno é a única maneira confiável de indicar o estado do foco.
Agora, vamos voltar ao que mencionei anteriormente sobre o uso do aria-selected="true"
atributo para ajudar os usuários de leitores de tela a identificar mais facilmente a guia selecionada. Podemos usar esse seletor para fornecer uma dica visual para a guia selecionada também!
.tab[aria-selected="true"] {
background-color: var(--component-bg);
}
Essa abordagem cria um pequeno problema devido ao funcionamento da especificidade do CSS. Ambos os seletores .tab:focus-visible
e .tab[aria-selected="true"]
, têm o mesmo nível de especificidade e ambos alteram a background-color
propriedade, portanto, a ordem da regra do seletor é crítica.
Queremos que o estado de foco background-color
sobrescreva qualquer outro background-color
, incluindo o estado selecionado, então adicionaremos a .tab:focus-visible
regra seletora após a .tab[aria-selected="true"]
regra seletora.
Aqui está o nosso resultado; observe como os dois estilos interagem entre si sem nenhum problema de especificidade:
A primeira guia na lista de guias do projeto está ativada e tem um fundo cinza, enquanto a terceira guia está focada no teclado e tem um fundo azul.
Se você estiver usando o <a>
elemento como sua guia, você já criou uma interface de guia funcional com a menor tecnologia possível!
Se você usou a <button>
abordagem baseada em -, não se preocupe! Em seguida, adicionaremos algumas funcionalidades com JavaScript, que serão cruciais para tornar a interface com abas mais acessível.
Há muito para descompactar nesta seção; vamos começar verificando o que o ARIA Authoring Practices Guide (APG) tem a dizer sobre esse padrão de componente. Aqui está o que o guia de padrões do ARIA APG para a interface de guia considera importante para a tecla tabtabindex
, os elementos in tabpanel
e as teclas de seta .
Em relação ao uso da tecla tab , o ARIA APG sugere o seguinte :
Quando o foco se move para a lista de guias, coloque o foco no
tab
elemento ativo.
Nossa primeira tarefa é remover a navegação do teclado com a tecla tab para a guia não selecionada para que, quando a tecla for pressionada, o foco vá diretamente para o painel de guias ativo. Então, quando shift + tab é pressionado no painel ativo, o foco volta para sua respectiva aba. Além disso, teremos que indicar a seleção de guias para usuários de leitores de tela e usuários com visão.
Aqui está o código que usei para resolver essas tarefas:
const TABLIST = document.querySelector("#tablist");
const TABS = [...TABLIST.querySelectorAll(".tab")];
const setSelectedTab = (element) => {
const selectedId = element.id;
TABS.forEach((e) => {
const id = e.getAttribute("id");
if (id === selectedId) {
e.removeAttribute("tabindex");
e.setAttribute("aria-selected", "true");
} else {
e.setAttribute("tabindex", "-1");
e.setAttribute("aria-selected", "false");
}
});
};
Aqui, selecionamos todas as abas e armazenamos aquela que está sendo clicada. Em seguida, analisamos o array com todas as nossas guias e comparamos cada id
atributo para verificar se é a guia que foi selecionada.
Se a guia não for a que desejamos selecionar, adicionamos os atributos tabindex="-1"
e . aria-selected="false"
No entanto, se a guia for a que desejamos selecionar, removemos o tabindex
atributo e adicionamos o aria-selected="true"
atributo. Os elementos <a>
e <button>
são focados no teclado por padrão, portanto, não há necessidade de adicionar um arquivo tabindex="0"
.
O tabindex="-1"
atributo tornará um elemento não focalizável com a tecla tab . Quando a tecla é pressionada, ela move o foco diretamente para o nosso painel de guias ativo, permitindo pular todas as nossas guias. Isso também é importante porque também nos permitirá gerenciar o foco das guias em outra que demonstrarei um pouco mais adiante neste artigo .
Agora tudo o que precisamos fazer é adicionar ouvintes de eventos às nossas guias para executar esta função! Para uma abordagem baseada em link, precisaremos adicionar um ouvinte de evento adicional a esses elementos.
Com uma <button>
abordagem baseada, qualquer evento de clique será ativado pelas teclas enter e espaço . No entanto, um <a>
elemento só adicionará esses eventos com a tecla enter , portanto, precisaremos adicionar um ouvinte de evento keydown para verificar quando a tecla de espaço é pressionada.
const handleClick = () => {
TABS.forEach((element) => {
element.addEventListener("click", function () {
setSelectedTab(element);
});
});
// Activates tab with Space. Only necessary if you use the <a> element as a tab
TABS.forEach((element) => {
element.addEventListener("keydown", function (e) {
if ((e.keyCode || e.which) === 32) {
setSelectedTab(element);
element.click();
}
});
});
};
Aqui está o nosso resultado!
Projeto de amostra com navegação de guia vertical. Quando a tecla tab é pressionada, ela move o foco do teclado para o painel de guias; quando shift+tab é pressionado, ele move o foco para a guia selecionada.
tabindex
intabpanel
O ARIA APG recomenda o seguinte para a interação do teclado da lista de guias :
Quando a lista de guias contém o foco, [tab] move o foco para o próximo elemento na sequência de guias da página fora da lista de guias, que é o painel de guias, a menos que o primeiro elemento contendo conteúdo significativo dentro do painel de guias seja focalizável.
Com base nessa recomendação, nossa próxima tarefa é verificar se cada painel de guia contém um elemento focalizável.
Para painéis de guias que não contêm um elemento focalizável, manteremos o tabindex="0"
atributo que adicionamos anteriormente. Caso contrário, atualizaremos o atributo para tabindex="-1"
, portanto, quando a tecla tab for pressionada, o foco será movido para o primeiro elemento focalizável dentro do painel de guias.
Usaremos este snippet de código para verificar se cada painel de guia contém elementos focalizáveis e alterar o tabindex
atributo conforme necessário:
const TABPANELS = [...document.querySelectorAll(".tabpanel")];
const determineTabindex = () => {
TABPANELS.forEach((element) => {
const focusableElements = element.querySelectorAll(
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled]), details:not([disabled]), summary:not(:disabled)'
).length;
focusableElements
? element.setAttribute("tabindex", "-1")
: element.setAttribute("tabindex", "0");
});
};
Aqui está o nosso resultado!
Interface com guias com navegação por teclado. Quando um painel de guias tem um elemento focalizável e navegamos usando a tecla tab, o foco é movido para o primeiro elemento focalizável. Caso contrário, o foco estará no próprio painel de guias.
Neste exemplo, quando tentamos usar nosso teclado para navegar na interface, o elemento focado varia dependendo se o painel de guias contém um elemento focado no teclado.
O ARIA APG oferece recomendações específicas de navegação pelo teclado, dependendo da orientação das guias da interface.
Aqui estão as recomendações quando o foco está em um elemento de guia em uma lista de guias horizontais :
seta para a esquerda : Move o foco para a guia anterior. Se o foco estiver na primeira guia, mova o foco para a última guia. Opcionalmente, ativa a guia recém-focada.
seta para a direita : Move o foco para a próxima guia. Se o foco estiver no último elemento da guia, mova o foco para a primeira guia. Opcionalmente, ativa a guia recém-focada.
Se você estiver construindo uma interface com abas exibidas verticalmente, além de usar o atributo aria-orientation="vertical"
, você terá que usar as teclas de seta para cima e seta para baixo em vez das teclas de seta para a esquerda e seta para a direita .
Aqui estão as recomendações quando uma lista de guias tem orientação de ária definida como vertical
:
Quando uma lista de guias tem sua orientação de ária definida como
vertical
:
a seta para baixo funciona como a seta para a direita descrita acima.
seta para cima funciona como a seta para a esquerda é descrita acima.
Minha intenção para este projeto era construir uma interface completamente flexível, mas com essa abordagem em algum momento a tela acabará sendo uma lista de abas horizontais ou uma lista de abas verticais. Como abordamos isso?
Vamos começar determinando quais teclas de seta devemos usar. Neste caso específico, temos duas alternativas:
aria-orientation="vertical"
e dê suporte apenas à navegação de seta para cima e seta para baixo. Caso contrário, adicione o atributo e dê suporte apenas à navegação de seta para a esquerda e seta para a direita .aria-orientation="horizontal"
Eu geralmente prefiro a solução mais simples, então escolho a segunda opção. No entanto, cada projeto é diferente. Se sua lista de guias estiver sempre na posição vertical, é melhor usar aria-orientation="vertical"
e oferecer suporte apenas à navegação com seta para cima e seta para baixo .
Aqui está o código que usei, com base na segunda opção:
const createArrowNavigation = () => {
const firstTab = TABS[0];
const lastTab = TABS[TABS.length - 1];
TABS.forEach((element) => {
element.addEventListener("keydown", function (e) {
if ((e.keyCode || e.which) === 38 || (e.keyCode || e.which) === 37) {
if (element == firstTab) {
e.preventDefault();
lastTab.focus();
} else {
e.preventDefault();
const focusableElement = TABS.indexOf(element) - 1;
TABS[focusableElement].focus();
}
} else if (
(e.keyCode || e.which) === 40 ||
(e.keyCode || e.which) === 39
) {
if (element == lastTab) {
e.preventDefault();
firstTab.focus();
} else {
e.preventDefault();
const focusableElement = TABS.indexOf(element) + 1;
TABS[focusableElement].focus();
}
}
});
});
};
Anteriormente, criamos uma variável que armazena todas as nossas guias. Podemos usá-lo para determinar qual é a primeira e a última guia de navegação. Isso é importante porque essas guias têm um comportamento especial.
Em seguida, podemos verificar quais códigos de tecla estão relacionados a cada tecla de seta. Para isso, podemos usar o site keycode.info para conferir. Para facilitar o processo, aqui está a lista dos códigos-chave:
Chave | Código chave |
---|---|
Seta esquerda | 37 |
Seta para cima | 38 |
Seta direita | 39 |
Seta para baixo | 40 |
Agora, ouvimos cada tecla para verificar a posição da guia focada no array e focamos no próximo elemento (a seta para a direita e a seta para baixo ) ou o elemento anterior (a seta para a esquerda e a seta para cima ) no array com o focus()
método . Como esses elementos têm o tabindex="-1"
atributo, poderemos focalizá-los com esse método.
Tenha em mente que se o elemento for o primeiro no array, ele moverá o foco para o último elemento e vice-versa. Outro ponto a ser considerado é que as teclas de seta também têm a função de mover a barra de rolagem; podemos evitar esse comportamento com o e.preventDefault()
método.
Aqui está nossa interface, mostrando o uso das teclas de seta para mover entre as abas:
Interface com guias com navegação pelo teclado, usando as teclas de seta para mover entre as guias.
Além de gerenciar o estado de foco de nosso componente, precisamos determinar o tempo para ativar a guia recém-focada. Em outras palavras, quando o painel de guias deve ser exibido? Deve ser mostrado quando a guia é clicada ou quando o foco é colocado na guia selecionada?
A resposta a este tópico é surpreendentemente matizada, e o W3C tem uma seção inteira sobre este tópico . Para resumir o W3C, alterar o conteúdo exibido no momento de focar um elemento (chamado de follow focus) pode ser benéfico para certos componentes, mas cria erros de acessibilidade para outros.
A exibição de conteúdo no momento do foco pode facilitar a navegação pelo teclado para usuários com visão, mas os usuários de leitores de tela podem não estar cientes de que um novo conteúdo foi adicionado à página. Além disso, precisamos considerar a quantidade de conteúdo que será exibida, pois isso pode afetar o desempenho.
Sugiro exibir o conteúdo quando a guia for clicada. Essa opção pode exigir um pressionamento extra de teclas para usuários de teclado, mas fornecerá uma experiência mais inclusiva para usuários de leitores de tela.
Quando o <a>
elemento é clicado, o foco é trazido para o painel de guias selecionado, que abrangerá a navegação para usuários de leitores de tela.
Se você decidir exibir o conteúdo com base no foco, certifique-se de usar a <button>
abordagem, em vez da <a>
abordagem do elemento.
Com um <button>
elemento, você precisará lidar com o gerenciamento de foco com JavaScript. Para isso, voltaremos à handleClick
função que criamos anteriormente na seção CSS deste artigo e adicionaremos alguns ajustes.
Primeiro, criaremos uma função que adiciona ou remove o atributo hidden
dependendo se o painel de guias está sendo direcionado. Se o painel de guias for o item de destino, também moveremos o foco do teclado para ele. Aqui está a minha abordagem para esta questão:
const showActivePanel = (element) => {
const selectedId = element.id;
TABPANELS.forEach((e) => {
e.hidden = "true";
});
const activePanel = document.querySelector(
`[aria-labelledby="${selectedId}"]`
);
activePanel.removeAttribute("hidden");
activePanel.focus()
};
O que estamos fazendo aqui é ocultar todos os painéis de guias adicionando a eles o hidden
atributo. Em seguida, selecionaremos nosso painel de guias de destino usando o aria-labelledby
valor do atributo. Lembre-se, cada button
um tem um id
que estamos usando para rotular o painel de guias usando este atributo; isso nos ajudará a selecionar o painel de guias correto.
Agora, apenas removemos o hidden
atributo e, em seguida, trazemos o foco do teclado para ele com o focus()
método. Por fim, adicionamos a showActivePanel
função em nossa handleClick
função para ser executada no clique.
Agora é hora de ver como o componente funciona como um todo!
Interface com abas mostrando o conteúdo no clique; as teclas de seta são usadas para navegação do teclado entre as guias.
<a>
estado inicial do elementoSe optarmos por usar um <a>
elemento para nossas guias, precisaremos lidar com o estado inicial do elemento se o JavaScript estiver ativo.
Aqui estão as ações que queremos realizar quando o documento for carregado:
aria-selected="true"
e ser focável pelo teclado quando a tecla tab for pressionada; as outras abas devem ter aria-selected="false"
e tabindex="-1"
atributosaria-selected="true"
eno tabindex
Vamos criar as funções para ambos os cenários.
const activateFirstPanel = () => {
TABS[0].setAttribute("tabindex", "0");
TABS[0].setAttribute("aria-selected", "true");
TABPANELS[0].classList.add("visible");
};
Esta função cobrirá nosso primeiro caso. Na verdade é bem simples. Já temos as listas de nós TABS
e TABPANELS
nós, podemos selecionar o primeiro elemento de cada lista e adicionar as propriedades necessárias.
Na seção CSS anterior, mencionei que estávamos usando a visible
classe como uma exceção e adicionaríamos alguns aprimoramentos com JavaScript posteriormente. Bem, agora é o momento para os aprimoramentos do JavaScript!
Adicionamos essa classe ao primeiro painel de guias para torná-la visível. Assim que o usuário começar a interagir com a guia, precisaremos remover essa classe.
const checkInitialSelectedTab = () => {
const targetedTabPanel = document
.querySelector(".tabpanel:target")
.getAttribute("aria-labelledby");
const selectedTab = document.querySelector(`#${targetedTabPanel}`);
selectedTab.setAttribute("aria-selected", "true");
selectedTab.removeAttribute("tabindex");
};
Nosso segundo cenário é um pouco mais complicado. Como este painel está sendo direcionado, ele será exibido na tela por padrão - isso se deve ao seletor.tabpanel:not(:target):not(.visible)
que usamos anteriormente. Mas, precisamos procurar a aba que ativa o painel.
Para isso, selecionaremos o painel de guias direcionado com JavaScript usando o .tabpanel:target
seletor no querySelector()
método. Assim que tivermos esse nó, obteremos o aria-labelledby
atributo. A guia id
é a mesma que estamos usando no aria-labelledby
atributo deste painel, então usaremos isso id
para procurar a guia e adicionaremos os atributos necessários.
Agora, só precisamos executar essas funções dependendo do que a URL contém, então usaremos outra função para lidar com isso:
const handleInitialState = () => {
TABS.forEach((e) => {
e.setAttribute("tabindex", "-1");
e.setAttribute("aria-selected", "false");
});
window.location.href.indexOf("#panel") === -1
? activateFirstPanel()
: checkInitialSelectedTab();
determineTabindex();
};
Bem, eu menti. Há apenas um pouco mais de trabalho que precisamos fazer nesta função.
Para começar, vamos definir o aria-selected
atributo de todas as guias para false
e o tabindex
atributo para -1
; então vamos corrigir o correto com as funções criadas.
Em seguida, precisamos verificar se um painel de guias está sendo segmentado na URL do site. Podemos usar o window.location.href
método para obter a URL e, em seguida, usar o indexOf
método array para verificar se existe um painel direcionado a essa URL.
Quando você usa uma função array em uma string, como nossa URL, ela vai criar uma array com cada caractere da string como um elemento dessa array, se o mundo inteiro estiver nessa URL, ela vai retornar a posição inicial de a string, se não, retornará -1. Isso é o que usaremos para determinar qual função executar.
Agora, vamos executar a determineTabindex()
função que criamos anteriormente. Removeremos a no-js
classe que adicionamos em nosso HTML e usamos para criar um fallback caso o JavaScript não carregue.
Estamos quase terminando!
Criamos anteriormente uma função para verificar se os painéis de guias possuem elementos focalizáveis: determineTabIndex()
; essa função precisa ser adicionada lá.
Você se lembra da nossa setSelectedTab()
função? Precisamos adicionar uma pequena linha para remover a visible
classe caso ela tenha sido usada. Para fazer isso, adicionaremos o código TABPANELS[0].classList.remove("visible");
antes de começarmos a verificar cada guia.
Por fim, vamos usar o seguinte comando para garantir que nossa função handleInitialState()
seja executada quando o site for carregado:
window.onload = handleInitialState;
Lembre-se, esta seção só se aplica se você usou o <a>
elemento para manipular a navegação das guias.
Abaixo está nossa interface com guias acessível completa! Depois de considerar várias opções, usei a abordagem baseada em link com o <a>
elemento para esta demonstração. A interface do usuário com guias foi projetada com um layout totalmente flexível, sem necessidade de consultas de mídia.
https://codepen.io/ItsCrisDiaz/pen/LYQjGpW
Este componente aborda os principais problemas de acessibilidade que os desenvolvedores encontram ao construir uma interface com guias: garantir que funcione corretamente para usuários de teclado e que as informações sejam mostradas corretamente para usuários de leitores de tela.
Construir um componente acessível pode ser um desafio devido às várias nuances dos requisitos de acessibilidade, mas é importante garantir que seu site seja utilizável para todos.
Fonte: https://blog.logrocket.com/build-accessible-user-interface-tabs-javascript/
1658557920
As guias de interface são um componente comum do site, mas os desenvolvedores geralmente enfrentam desafios para tornar esse padrão acessível. Os usuários de leitores de tela podem encontrar alguns problemas ao ler o conteúdo, e os usuários que dependem de um teclado para navegar em um site podem precisar de assistência para facilitar a navegação.
Este artigo cobrirá tudo o que você precisa saber para criar uma interface de guia acessível, incluindo a construção da estrutura HTML, a adição do estilo CSS e a adição de aprimoramentos da funcionalidade JavaScript.
Aqui está a interface com guias que construiremos neste tutorial:
Uma interface com guias listando diferentes países. À esquerda, as abas são exibidas verticalmente e à direita, há um texto fictício e uma imagem para o país selecionado (neste caso, Mongólia).
Vamos começar!
A estrutura HTML é muito importante porque fornece aos usuários de leitores de tela o contexto necessário para navegar na interface.
Existem duas maneiras de usar a estrutura HTML para ajudar a definir quais elementos podem ser focados com um teclado. Podemos usar uma abordagem baseada em link com o <a>
elemento como nosso gatilho de guia ou podemos usar o <button>
elemento.
Ambas as abordagens levam em consideração a navegação pelo teclado por serem nativamente focáveis pelo teclado, mas cada uma tem vantagens e desvantagens. Abordaremos ambas as abordagens neste artigo.
<a>
elemento como a guiaA principal vantagem dessa abordagem é que podemos usar a <a>
funcionalidade do elemento para criar o efeito de navegação da guia usando apenas HTML e CSS. O <a>
elemento traz suporte ao teclado cuidando muito do gerenciamento de foco e navegação para usuários de leitores de tela para nós!
Essa abordagem também é útil para usuários que têm o JavaScript desabilitado. Usaremos JavaScript apenas no final deste tutorial — para aprimorar algumas das <a>
funcionalidades do elemento para fins de acessibilidade.
No entanto, uma pequena desvantagem de usar o <a>
elemento como guia é que ele é contado no histórico de navegação do usuário. Em outras palavras, se o usuário visitar todas as abas e depois quiser retornar ao site anterior, terá que pressionar o botão voltar várias vezes.
Ainda assim, essa abordagem tem aprimoramento progressivo em mente, pois oferece a maior funcionalidade de interface de guia com a menor tecnologia possível. Essa abordagem funcionará mesmo que o JavaScript não carregue e mesmo que o CSS também falhe, então vale a pena considerar.
<button>
elemento como a guiaUsar o <button>
elemento como guia tem a vantagem de não afetar o histórico de navegação, facilitando o retorno do usuário ao site anterior.
No entanto, essa abordagem requer algumas considerações extras com HTML para ocultar todo o conteúdo de guias não ativas. Também requer algumas decisões adicionais em relação ao JavaScript, como determinar como exibir conteúdo, estratégias de gerenciamento de foco e alguns aprimoramentos de acessibilidade.
Se você usar essa abordagem, precisará considerar o que acontecerá se o JavaScript não carregar em seu site. Sem um fallback, os usuários não poderão navegar em sua interface com guias. Existem várias razões pelas quais o JavaScript pode falhar ao carregar. Por exemplo, os usuários podem ter o JavaScript desabilitado devido a questões de rastreamento e privacidade, ou o JavaScript pode falhar ao carregar devido a fatores externos.
Depois de decidir qual abordagem você usará, é hora de começar a criar a estrutura HTML!
<a>
abordagem do elementoVamos começar com as guias individuais. Aqui, usaremos a <a>
abordagem de elemento para a estrutura HTML:
<!-- Tab semantics -->
<ul role="tablist">
<li role="presentation">
<a role="tab" href="#panel1" id="tab1">Tab one</a>
</li>
<li role="presentation">
<a role="tab" href="#panel2" id="tab2">Tab two</a>
</li>
<li role="presentation">
<a role="tab" href="#panel3" id="tab3">Tab three</a>
</li>
<li role="presentation">
<a role="tab" href="#panel4" id="tab4">Tab four</a>
</li>
</ul>
<!-- Tab content semantics -->
<div class="tabpanel-container">
<section role="tabpanel" id="panel1" aria-labelledby="tab1" tabindex="0"></section>
<section role="tabpanel" id="panel2" aria-labelledby="tab2" tabindex="0"></section>
<section role="tabpanel" id="panel3" aria-labelledby="tab3" tabindex="0"></section>
<section role="tabpanel" id="panel4" aria-labelledby="tab4" tabindex="0"></section>
</div>
Agora, vamos examinar a estrutura mostrada no código acima:
<a>
ou <button>
) dentro da lista itens dentro de um <ul>
elementotablist
função ao nosso <ul>
para fornecer mais contexto para usuários de leitores de tela; essa função é usada para marcar um contêiner que envolve um conjunto de guias. Como o tablist
papel não fornece a semântica de tabulação ao elemento filho, adicionamos o tab
papel ao nosso <a>
elementotablist
e tab
, o HTML ainda será lido como uma lista de links ou botões, o que é um substituto aceitávelpresentation
papel aos <li>
elementos apenas para remover a semântica; isso ajudará a evitar interações estranhas, mantendo nosso fallback caso um leitor de tela específico não ofereça suporte a funções ARIA<section>
elemento com o tabpanel
papel. Espera-se que esse elemento use o mesmo nome da guia como um nome acessível. É por isso que adicionamos o id
atributo às nossas guias e o estamos usando como um rótulo em nossos painéis de guias com o aria-labelledby
atributotabindex="0"
aos nossos painéis de guias para permitir que itens dentro da guia (como campos de formulário, links ou botões) recebam o foco do teclado. Isso tornará mais fácil para os usuários de teclado acessar o conteúdo<button>
abordagem do elementoSe usarmos a <button>
abordagem baseada em -, precisaremos incluir uma etapa extra. Precisaremos adicionar o hidden
atributo a todos os painéis de guias, exceto a primeira guia. Este atributo ocultará os elementos para usuários com visão e usuários de leitores de tela. Para garantir que a primeira guia permaneça visível, ela deve ter o aria-selected="true"
atributo.
Nossa marcação para a <button>
abordagem deve ser algo assim:
<!-- Tab semantics -->
<ul role="tablist">
<li role="presentation">
<button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
</li>
<li role="presentation">
<button role="tab" href="#panel2" id="tab2">Tab two</button>
</li>
<li role="presentation">
<button role="tab" href="#panel3" id="tab3">Tab three</button>
</li>
<li role="presentation">
<button role="tab" href="#panel4" id="tab4">Tab four</button>
</li>
</ul>
<!-- Tab content semantics -->
<div class="tabpanel-container">
<section role="tabpanel" id="panel1" aria-labelledby="tab1" tabindex="0"></section>
<section role="tabpanel" id="panel2" aria-labelledby="tab2" tabindex="0" hidden></section>
<section role="tabpanel" id="panel3" aria-labelledby="tab3" tabindex="0" hidden></section>
<section role="tabpanel" id="panel4" aria-labelledby="tab4" tabindex="0" hidden></section>
</div>
Até agora, cobrimos as considerações iniciais para nossa marcação, mas há fatores adicionais que precisamos ter em mente para determinados casos de uso. Alguns fatores serão controlados de forma mais dinâmica, mas falaremos sobre eles um pouco mais adiante na seção JavaScript deste artigo.
Podemos relacionar as guias com o painel de guias para usuários de leitores de tela usando o atributo aria-controls
. No entanto, essa abordagem funcionará apenas no leitor de tela JAWS e seu uso pode parecer bastante detalhado, conforme explicado no artigo de Heydon Pickering . Em vez disso, usaremos algumas estratégias de gerenciamento de foco que também ajudarão os usuários de teclado.
Para exibir uma lista de guias com orientação vertical, você precisará adicionar o atributo [aria-orientation="vertical"]
ao contêiner. Isso indicará aos usuários de leitores de tela que as guias estão empilhadas verticalmente.
Isso será importante quando começarmos a discutir estratégias de navegação para nosso exemplo de interface com guias.
Aqui está um exemplo mostrando como o [aria-orientation="vertical"]
atributo seria usado em um snippet de código:
<ul role="tablist" aria-orientation="vertical">
<li role="presentation">
<button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
</li>
<li role="presentation">
<button role="tab" href="#panel2" id="tab2">Tab two</button>
</li>
<li role="presentation">
<button role="tab" href="#panel3" id="tab3">Tab three</button>
</li>
<li role="presentation">
<button role="tab" href="#panel4" id="tab4">Tab four</button>
</li>
</ul>
Quando um usuário clicar em uma guia, precisaremos de uma maneira de indicar que ela foi selecionada.
Usaremos o atributo [aria-selected="true"]
para indicar a seleção de guias para usuários de leitores de tela. Em seguida, vamos estilizá-lo com CSS para ajudar a indicar a seleção para usuários com visão. O [aria-selected="true"]
atributo mudará dinamicamente com JavaScript, portanto, não será adicionado à nossa marcação inicial.
<li role="presentation">
<button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
</li>
Agora que temos uma estrutura HTML sólida, é hora de adicionar algum estilo com CSS !
Para nosso exemplo de interface com guias, estamos usando uma abordagem baseada em link com o <a>
elemento como nosso gatilho de guia. Para estilizar a estrutura de guias HTML, precisaremos adicionar a navegação de guias, criar o indicador de foco e identificar o estilo do estado selecionado.
Para adicionar a navegação da guia, vamos adicionar uma regra CSS usando o :target
seletor assim:
.tabpanel:not(:target):not(.visible) {
display: none;
}
:target
é uma pseudo-classe que verifica se um elemento com um id
corresponde ao fragmento da URL.
Como estamos usando o <a>
elemento aqui para adicionar funcionalidade, podemos usar esse seletor para ocultar quaisquer painéis de guias não ativos. Estamos usando a visible
classe como uma exceção em nosso exemplo para que possamos adicionar alguns aprimoramentos com JavaScript posteriormente .
Outra coisa que precisamos ter em mente é a navegação pelo teclado, e uma das coisas mais importantes a fazer nesse sentido é criar um indicador de foco adequado.
Você provavelmente notou um indicador de foco ao navegar em um site usando a tecla tab . Dependendo do seu navegador, aparecerá como um contorno azul ou preto destacando um elemento selecionado.
Os navegadores oferecem estilos de foco padrão, mas não são suficientes para atender aos critérios WCAG 2.2 . Portanto, é necessário usar um estilo adicional. Para obter informações mais detalhadas sobre como criar um bom indicador de foco, leia o artigo de Sara Souiedan sobre este tópico.
Para nosso exemplo, usaremos os seguintes estilos de foco:
.tab:focus-visible {
background-color: royalblue;
color: whitesmoke;
outline: 0.2em solid transparent;
}
Decidi usar :focus-visible
em vez da :focus
pseudo-classe para que o estilo de foco seja ativado apenas com a navegação do teclado. A :focus
pseudo-classe também será ativada quando o elemento for clicado, o que pode ser confuso para os usuários.
:focus-visible
é muito bem suportado em navegadores modernos, por isso não deve criar nenhum tipo de conflito. Para obter mais informações sobre essa propriedade, consulte a documentação do MDN .
A opção “Mongólia” selecionada abaixo mostra como ficaria o estado de foco da nossa guia:
A primeira guia na lista de guias do projeto tem um fundo azul, fornecendo uma dica visual para a seleção de guias para usuários de teclado.
Para este projeto, decidi usar um contorno transparente. Isso é importante para o modo de alto contraste do Windows . Nesse modo, todas as cores do texto e do plano de fundo são substituídas pelas escolhidas pelo sistema operacional, portanto, não podemos depender da cor do plano de fundo para indicar um estado de foco. O contorno é a única maneira confiável de indicar o estado do foco.
Agora, vamos voltar ao que mencionei anteriormente sobre o uso do aria-selected="true"
atributo para ajudar os usuários de leitores de tela a identificar mais facilmente a guia selecionada. Podemos usar esse seletor para fornecer uma dica visual para a guia selecionada também!
.tab[aria-selected="true"] {
background-color: var(--component-bg);
}
Essa abordagem cria um pequeno problema devido ao funcionamento da especificidade do CSS. Ambos os seletores .tab:focus-visible
e .tab[aria-selected="true"]
, têm o mesmo nível de especificidade e ambos alteram a background-color
propriedade, portanto, a ordem da regra do seletor é crítica.
Queremos que o estado de foco background-color
sobrescreva qualquer outro background-color
, incluindo o estado selecionado, então adicionaremos a .tab:focus-visible
regra seletora após a .tab[aria-selected="true"]
regra seletora.
Aqui está o nosso resultado; observe como os dois estilos interagem entre si sem nenhum problema de especificidade:
A primeira guia na lista de guias do projeto está ativada e tem um fundo cinza, enquanto a terceira guia está focada no teclado e tem um fundo azul.
Se você estiver usando o <a>
elemento como sua guia, você já criou uma interface de guia funcional com a menor tecnologia possível!
Se você usou a <button>
abordagem baseada em -, não se preocupe! Em seguida, adicionaremos algumas funcionalidades com JavaScript, que serão cruciais para tornar a interface com abas mais acessível.
Há muito para descompactar nesta seção; vamos começar verificando o que o ARIA Authoring Practices Guide (APG) tem a dizer sobre esse padrão de componente. Aqui está o que o guia de padrões do ARIA APG para a interface de guia considera importante para a tecla tabtabindex
, os elementos in tabpanel
e as teclas de seta .
Em relação ao uso da tecla tab , o ARIA APG sugere o seguinte :
Quando o foco se move para a lista de guias, coloque o foco no
tab
elemento ativo.
Nossa primeira tarefa é remover a navegação do teclado com a tecla tab para a guia não selecionada para que, quando a tecla for pressionada, o foco vá diretamente para o painel de guias ativo. Então, quando shift + tab é pressionado no painel ativo, o foco volta para sua respectiva aba. Além disso, teremos que indicar a seleção de guias para usuários de leitores de tela e usuários com visão.
Aqui está o código que usei para resolver essas tarefas:
const TABLIST = document.querySelector("#tablist");
const TABS = [...TABLIST.querySelectorAll(".tab")];
const setSelectedTab = (element) => {
const selectedId = element.id;
TABS.forEach((e) => {
const id = e.getAttribute("id");
if (id === selectedId) {
e.removeAttribute("tabindex");
e.setAttribute("aria-selected", "true");
} else {
e.setAttribute("tabindex", "-1");
e.setAttribute("aria-selected", "false");
}
});
};
Aqui, selecionamos todas as abas e armazenamos aquela que está sendo clicada. Em seguida, analisamos o array com todas as nossas guias e comparamos cada id
atributo para verificar se é a guia que foi selecionada.
Se a guia não for a que desejamos selecionar, adicionamos os atributos tabindex="-1"
e . aria-selected="false"
No entanto, se a guia for a que desejamos selecionar, removemos o tabindex
atributo e adicionamos o aria-selected="true"
atributo. Os elementos <a>
e <button>
são focados no teclado por padrão, portanto, não há necessidade de adicionar um arquivo tabindex="0"
.
O tabindex="-1"
atributo tornará um elemento não focalizável com a tecla tab . Quando a tecla é pressionada, ela move o foco diretamente para o nosso painel de guias ativo, permitindo pular todas as nossas guias. Isso também é importante porque também nos permitirá gerenciar o foco das guias em outra que demonstrarei um pouco mais adiante neste artigo .
Agora tudo o que precisamos fazer é adicionar ouvintes de eventos às nossas guias para executar esta função! Para uma abordagem baseada em link, precisaremos adicionar um ouvinte de evento adicional a esses elementos.
Com uma <button>
abordagem baseada, qualquer evento de clique será ativado pelas teclas enter e espaço . No entanto, um <a>
elemento só adicionará esses eventos com a tecla enter , portanto, precisaremos adicionar um ouvinte de evento keydown para verificar quando a tecla de espaço é pressionada.
const handleClick = () => {
TABS.forEach((element) => {
element.addEventListener("click", function () {
setSelectedTab(element);
});
});
// Activates tab with Space. Only necessary if you use the <a> element as a tab
TABS.forEach((element) => {
element.addEventListener("keydown", function (e) {
if ((e.keyCode || e.which) === 32) {
setSelectedTab(element);
element.click();
}
});
});
};
Aqui está o nosso resultado!
Projeto de amostra com navegação de guia vertical. Quando a tecla tab é pressionada, ela move o foco do teclado para o painel de guias; quando shift+tab é pressionado, ele move o foco para a guia selecionada.
tabindex
intabpanel
O ARIA APG recomenda o seguinte para a interação do teclado da lista de guias :
Quando a lista de guias contém o foco, [tab] move o foco para o próximo elemento na sequência de guias da página fora da lista de guias, que é o painel de guias, a menos que o primeiro elemento contendo conteúdo significativo dentro do painel de guias seja focalizável.
Com base nessa recomendação, nossa próxima tarefa é verificar se cada painel de guia contém um elemento focalizável.
Para painéis de guias que não contêm um elemento focalizável, manteremos o tabindex="0"
atributo que adicionamos anteriormente. Caso contrário, atualizaremos o atributo para tabindex="-1"
, portanto, quando a tecla tab for pressionada, o foco será movido para o primeiro elemento focalizável dentro do painel de guias.
Usaremos este snippet de código para verificar se cada painel de guia contém elementos focalizáveis e alterar o tabindex
atributo conforme necessário:
const TABPANELS = [...document.querySelectorAll(".tabpanel")];
const determineTabindex = () => {
TABPANELS.forEach((element) => {
const focusableElements = element.querySelectorAll(
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled]), details:not([disabled]), summary:not(:disabled)'
).length;
focusableElements
? element.setAttribute("tabindex", "-1")
: element.setAttribute("tabindex", "0");
});
};
Aqui está o nosso resultado!
Interface com guias com navegação por teclado. Quando um painel de guias tem um elemento focalizável e navegamos usando a tecla tab, o foco é movido para o primeiro elemento focalizável. Caso contrário, o foco estará no próprio painel de guias.
Neste exemplo, quando tentamos usar nosso teclado para navegar na interface, o elemento focado varia dependendo se o painel de guias contém um elemento focado no teclado.
O ARIA APG oferece recomendações específicas de navegação pelo teclado, dependendo da orientação das guias da interface.
Aqui estão as recomendações quando o foco está em um elemento de guia em uma lista de guias horizontais :
seta para a esquerda : Move o foco para a guia anterior. Se o foco estiver na primeira guia, mova o foco para a última guia. Opcionalmente, ativa a guia recém-focada.
seta para a direita : Move o foco para a próxima guia. Se o foco estiver no último elemento da guia, mova o foco para a primeira guia. Opcionalmente, ativa a guia recém-focada.
Se você estiver construindo uma interface com abas exibidas verticalmente, além de usar o atributo aria-orientation="vertical"
, você terá que usar as teclas de seta para cima e seta para baixo em vez das teclas de seta para a esquerda e seta para a direita .
Aqui estão as recomendações quando uma lista de guias tem orientação de ária definida como vertical
:
Quando uma lista de guias tem sua orientação de ária definida como
vertical
:
a seta para baixo funciona como a seta para a direita descrita acima.
seta para cima funciona como a seta para a esquerda é descrita acima.
Minha intenção para este projeto era construir uma interface completamente flexível, mas com essa abordagem em algum momento a tela acabará sendo uma lista de abas horizontais ou uma lista de abas verticais. Como abordamos isso?
Vamos começar determinando quais teclas de seta devemos usar. Neste caso específico, temos duas alternativas:
aria-orientation="vertical"
e dê suporte apenas à navegação de seta para cima e seta para baixo. Caso contrário, adicione o atributo e dê suporte apenas à navegação de seta para a esquerda e seta para a direita .aria-orientation="horizontal"
Eu geralmente prefiro a solução mais simples, então escolho a segunda opção. No entanto, cada projeto é diferente. Se sua lista de guias estiver sempre na posição vertical, é melhor usar aria-orientation="vertical"
e oferecer suporte apenas à navegação com seta para cima e seta para baixo .
Aqui está o código que usei, com base na segunda opção:
const createArrowNavigation = () => {
const firstTab = TABS[0];
const lastTab = TABS[TABS.length - 1];
TABS.forEach((element) => {
element.addEventListener("keydown", function (e) {
if ((e.keyCode || e.which) === 38 || (e.keyCode || e.which) === 37) {
if (element == firstTab) {
e.preventDefault();
lastTab.focus();
} else {
e.preventDefault();
const focusableElement = TABS.indexOf(element) - 1;
TABS[focusableElement].focus();
}
} else if (
(e.keyCode || e.which) === 40 ||
(e.keyCode || e.which) === 39
) {
if (element == lastTab) {
e.preventDefault();
firstTab.focus();
} else {
e.preventDefault();
const focusableElement = TABS.indexOf(element) + 1;
TABS[focusableElement].focus();
}
}
});
});
};
Anteriormente, criamos uma variável que armazena todas as nossas guias. Podemos usá-lo para determinar qual é a primeira e a última guia de navegação. Isso é importante porque essas guias têm um comportamento especial.
Em seguida, podemos verificar quais códigos de tecla estão relacionados a cada tecla de seta. Para isso, podemos usar o site keycode.info para conferir. Para facilitar o processo, aqui está a lista dos códigos-chave:
Chave | Código chave |
---|---|
Seta esquerda | 37 |
Seta para cima | 38 |
Seta direita | 39 |
Seta para baixo | 40 |
Agora, ouvimos cada tecla para verificar a posição da guia focada no array e focamos no próximo elemento (a seta para a direita e a seta para baixo ) ou o elemento anterior (a seta para a esquerda e a seta para cima ) no array com o focus()
método . Como esses elementos têm o tabindex="-1"
atributo, poderemos focalizá-los com esse método.
Tenha em mente que se o elemento for o primeiro no array, ele moverá o foco para o último elemento e vice-versa. Outro ponto a ser considerado é que as teclas de seta também têm a função de mover a barra de rolagem; podemos evitar esse comportamento com o e.preventDefault()
método.
Aqui está nossa interface, mostrando o uso das teclas de seta para mover entre as abas:
Interface com guias com navegação pelo teclado, usando as teclas de seta para mover entre as guias.
Além de gerenciar o estado de foco de nosso componente, precisamos determinar o tempo para ativar a guia recém-focada. Em outras palavras, quando o painel de guias deve ser exibido? Deve ser mostrado quando a guia é clicada ou quando o foco é colocado na guia selecionada?
A resposta a este tópico é surpreendentemente matizada, e o W3C tem uma seção inteira sobre este tópico . Para resumir o W3C, alterar o conteúdo exibido no momento de focar um elemento (chamado de follow focus) pode ser benéfico para certos componentes, mas cria erros de acessibilidade para outros.
A exibição de conteúdo no momento do foco pode facilitar a navegação pelo teclado para usuários com visão, mas os usuários de leitores de tela podem não estar cientes de que um novo conteúdo foi adicionado à página. Além disso, precisamos considerar a quantidade de conteúdo que será exibida, pois isso pode afetar o desempenho.
Sugiro exibir o conteúdo quando a guia for clicada. Essa opção pode exigir um pressionamento extra de teclas para usuários de teclado, mas fornecerá uma experiência mais inclusiva para usuários de leitores de tela.
Quando o <a>
elemento é clicado, o foco é trazido para o painel de guias selecionado, que abrangerá a navegação para usuários de leitores de tela.
Se você decidir exibir o conteúdo com base no foco, certifique-se de usar a <button>
abordagem, em vez da <a>
abordagem do elemento.
Com um <button>
elemento, você precisará lidar com o gerenciamento de foco com JavaScript. Para isso, voltaremos à handleClick
função que criamos anteriormente na seção CSS deste artigo e adicionaremos alguns ajustes.
Primeiro, criaremos uma função que adiciona ou remove o atributo hidden
dependendo se o painel de guias está sendo direcionado. Se o painel de guias for o item de destino, também moveremos o foco do teclado para ele. Aqui está a minha abordagem para esta questão:
const showActivePanel = (element) => {
const selectedId = element.id;
TABPANELS.forEach((e) => {
e.hidden = "true";
});
const activePanel = document.querySelector(
`[aria-labelledby="${selectedId}"]`
);
activePanel.removeAttribute("hidden");
activePanel.focus()
};
O que estamos fazendo aqui é ocultar todos os painéis de guias adicionando a eles o hidden
atributo. Em seguida, selecionaremos nosso painel de guias de destino usando o aria-labelledby
valor do atributo. Lembre-se, cada button
um tem um id
que estamos usando para rotular o painel de guias usando este atributo; isso nos ajudará a selecionar o painel de guias correto.
Agora, apenas removemos o hidden
atributo e, em seguida, trazemos o foco do teclado para ele com o focus()
método. Por fim, adicionamos a showActivePanel
função em nossa handleClick
função para ser executada no clique.
Agora é hora de ver como o componente funciona como um todo!
Interface com abas mostrando o conteúdo no clique; as teclas de seta são usadas para navegação do teclado entre as guias.
<a>
estado inicial do elementoSe optarmos por usar um <a>
elemento para nossas guias, precisaremos lidar com o estado inicial do elemento se o JavaScript estiver ativo.
Aqui estão as ações que queremos realizar quando o documento for carregado:
aria-selected="true"
e ser focável pelo teclado quando a tecla tab for pressionada; as outras abas devem ter aria-selected="false"
e tabindex="-1"
atributosaria-selected="true"
eno tabindex
Vamos criar as funções para ambos os cenários.
const activateFirstPanel = () => {
TABS[0].setAttribute("tabindex", "0");
TABS[0].setAttribute("aria-selected", "true");
TABPANELS[0].classList.add("visible");
};
Esta função cobrirá nosso primeiro caso. Na verdade é bem simples. Já temos as listas de nós TABS
e TABPANELS
nós, podemos selecionar o primeiro elemento de cada lista e adicionar as propriedades necessárias.
Na seção CSS anterior, mencionei que estávamos usando a visible
classe como uma exceção e adicionaríamos alguns aprimoramentos com JavaScript posteriormente. Bem, agora é o momento para os aprimoramentos do JavaScript!
Adicionamos essa classe ao primeiro painel de guias para torná-la visível. Assim que o usuário começar a interagir com a guia, precisaremos remover essa classe.
const checkInitialSelectedTab = () => {
const targetedTabPanel = document
.querySelector(".tabpanel:target")
.getAttribute("aria-labelledby");
const selectedTab = document.querySelector(`#${targetedTabPanel}`);
selectedTab.setAttribute("aria-selected", "true");
selectedTab.removeAttribute("tabindex");
};
Nosso segundo cenário é um pouco mais complicado. Como este painel está sendo direcionado, ele será exibido na tela por padrão - isso se deve ao seletor.tabpanel:not(:target):not(.visible)
que usamos anteriormente. Mas, precisamos procurar a aba que ativa o painel.
Para isso, selecionaremos o painel de guias direcionado com JavaScript usando o .tabpanel:target
seletor no querySelector()
método. Assim que tivermos esse nó, obteremos o aria-labelledby
atributo. A guia id
é a mesma que estamos usando no aria-labelledby
atributo deste painel, então usaremos isso id
para procurar a guia e adicionaremos os atributos necessários.
Agora, só precisamos executar essas funções dependendo do que a URL contém, então usaremos outra função para lidar com isso:
const handleInitialState = () => {
TABS.forEach((e) => {
e.setAttribute("tabindex", "-1");
e.setAttribute("aria-selected", "false");
});
window.location.href.indexOf("#panel") === -1
? activateFirstPanel()
: checkInitialSelectedTab();
determineTabindex();
};
Bem, eu menti. Há apenas um pouco mais de trabalho que precisamos fazer nesta função.
Para começar, vamos definir o aria-selected
atributo de todas as guias para false
e o tabindex
atributo para -1
; então vamos corrigir o correto com as funções criadas.
Em seguida, precisamos verificar se um painel de guias está sendo segmentado na URL do site. Podemos usar o window.location.href
método para obter a URL e, em seguida, usar o indexOf
método array para verificar se existe um painel direcionado a essa URL.
Quando você usa uma função array em uma string, como nossa URL, ela vai criar uma array com cada caractere da string como um elemento dessa array, se o mundo inteiro estiver nessa URL, ela vai retornar a posição inicial de a string, se não, retornará -1. Isso é o que usaremos para determinar qual função executar.
Agora, vamos executar a determineTabindex()
função que criamos anteriormente. Removeremos a no-js
classe que adicionamos em nosso HTML e usamos para criar um fallback caso o JavaScript não carregue.
Estamos quase terminando!
Criamos anteriormente uma função para verificar se os painéis de guias possuem elementos focalizáveis: determineTabIndex()
; essa função precisa ser adicionada lá.
Você se lembra da nossa setSelectedTab()
função? Precisamos adicionar uma pequena linha para remover a visible
classe caso ela tenha sido usada. Para fazer isso, adicionaremos o código TABPANELS[0].classList.remove("visible");
antes de começarmos a verificar cada guia.
Por fim, vamos usar o seguinte comando para garantir que nossa função handleInitialState()
seja executada quando o site for carregado:
window.onload = handleInitialState;
Lembre-se, esta seção só se aplica se você usou o <a>
elemento para manipular a navegação das guias.
Abaixo está nossa interface com guias acessível completa! Depois de considerar várias opções, usei a abordagem baseada em link com o <a>
elemento para esta demonstração. A interface do usuário com guias foi projetada com um layout totalmente flexível, sem necessidade de consultas de mídia.
https://codepen.io/ItsCrisDiaz/pen/LYQjGpW
Este componente aborda os principais problemas de acessibilidade que os desenvolvedores encontram ao construir uma interface com guias: garantir que funcione corretamente para usuários de teclado e que as informações sejam mostradas corretamente para usuários de leitores de tela.
Construir um componente acessível pode ser um desafio devido às várias nuances dos requisitos de acessibilidade, mas é importante garantir que seu site seja utilizável para todos.
Fonte: https://blog.logrocket.com/build-accessible-user-interface-tabs-javascript/
1622207074
Who invented JavaScript, how it works, as we have given information about Programming language in our previous article ( What is PHP ), but today we will talk about what is JavaScript, why JavaScript is used The Answers to all such questions and much other information about JavaScript, you are going to get here today. Hope this information will work for you.
JavaScript language was invented by Brendan Eich in 1995. JavaScript is inspired by Java Programming Language. The first name of JavaScript was Mocha which was named by Marc Andreessen, Marc Andreessen is the founder of Netscape and in the same year Mocha was renamed LiveScript, and later in December 1995, it was renamed JavaScript which is still in trend.
JavaScript is a client-side scripting language used with HTML (Hypertext Markup Language). JavaScript is an Interpreted / Oriented language called JS in programming language JavaScript code can be run on any normal web browser. To run the code of JavaScript, we have to enable JavaScript of Web Browser. But some web browsers already have JavaScript enabled.
Today almost all websites are using it as web technology, mind is that there is maximum scope in JavaScript in the coming time, so if you want to become a programmer, then you can be very beneficial to learn JavaScript.
In JavaScript, ‘document.write‘ is used to represent a string on a browser.
<script type="text/javascript">
document.write("Hello World!");
</script>
<script type="text/javascript">
//single line comment
/* document.write("Hello"); */
</script>
#javascript #javascript code #javascript hello world #what is javascript #who invented javascript
1616670795
It is said that a digital resource a business has must be interactive in nature, so the website or the business app should be interactive. How do you make the app interactive? With the use of JavaScript.
Does your business need an interactive website or app?
Hire Dedicated JavaScript Developer from WebClues Infotech as the developer we offer is highly skilled and expert in what they do. Our developers are collaborative in nature and work with complete transparency with the customers.
The technology used to develop the overall app by the developers from WebClues Infotech is at par with the latest available technology.
Get your business app with JavaScript
For more inquiry click here https://bit.ly/31eZyDZ
Book Free Interview: https://bit.ly/3dDShFg
#hire dedicated javascript developers #hire javascript developers #top javascript developers for hire #hire javascript developer #hire a freelancer for javascript developer #hire the best javascript developers
1589255577
As a JavaScript developer of any level, you need to understand its foundational concepts and some of the new ideas that help us developing code. In this article, we are going to review 16 basic concepts. So without further ado, let’s get to it.
#javascript-interview #javascript-development #javascript-fundamental #javascript #javascript-tips
1626321063
PixelCrayons: Our JavaScript web development service offers you a feature-packed & dynamic web application that effectively caters to your business challenges and provide you the best RoI. Our JavaScript web development company works on all major frameworks & libraries like Angular, React, Nodejs, Vue.js, to name a few.
With 15+ years of domain expertise, we have successfully delivered 13800+ projects and have successfully garnered 6800+ happy customers with 97%+ client retention rate.
Looking for professional JavaScript web app development services? We provide custom JavaScript development services applying latest version frameworks and libraries to propel businesses to the next level. Our well-defined and manageable JS development processes are balanced between cost, time and quality along with clear communication.
Our JavaScript development companies offers you strict NDA, 100% money back guarantee and agile/DevOps approach.
#javascript development company #javascript development services #javascript web development #javascript development #javascript web development services #javascript web development company