Tags de Seções Comuns

Antigamente era comum utilizar apenas a tag div para separar o conteúdo, e atribuir a sua função através de IDs e classes. Entretanto, o HTML5 implementou tags novas que pretendem diminuir a utilização das divs, tornando-as quase obsoletas.

Abaixo, segue um exemplo do formato convencional em que os desenvolvedores organizam os elementos de uma página, através de tags div, IDs e classes:

<div id="header">
    <h1>Título</h1>
    <div id="nav">
    ...
    </div>
</div>
<div id="main">
    <div class="section-article">
        <div class="article-header">
        ...
        </div>
        <div class="article-content">
        ...
        </div>
        <div class="article-footer">
        ...
        </div>
    </div>

    <div class="section-article">
        <div class="article-header">
        ...
        </div>
        <div class="article-content">
        ...
        </div>
        <div class="article-footer">
        ...
        </div>
    </div>
</div>
<div id="sidebar">
...
</div>
<div id="footer">
...
</div>

No próximo exemplo, as diversas tags div são substítuidas por tags que indicam o tipo de elemento, sem precisar de IDs e classes para diferenciá-los.

<header>
    <h1>Título</h1>
    <nav>
    ...
    </nav>
</header>
<main>
    <section>
        <header>
        ...
        </header>
        <article>
        ...
        </article>
        <footer>
        ...
        </footer>
    </section>

    <section>
        <header>
        ...
        </header>
        <article>
        ...
        </article>
        <footer>
        ...
        </footer>
    </section>
</main>
<aside>
...
</aside>
<footer>
...
</footer>
    

Dessa forma, a organização do código é mais semântica, sendo que os recursos de Tecnologia Assistiva podem fazer uso dessas tags para melhorar a experiência de usuários com necessidades específicas. As principais marcações, para delimitação mais semântica de conteúdo, são:

Elemento Descrição
header Define o cabeçalho de uma página ou seção. Muitas vezes contém um logotipo, o título do site e uma menu de navegação do conteúdo.
section Define uma seção do Documento (permite também a utilização de mais de um cabeçalho de nível 1 numa página, um por section).
nav Define uma seção que contém apenas links e botões de navegação.
article Define um conteúdo que pode existir de forma independente do resto da página. Esta Tag poderia ser uma postagem em um fórum, um artigo de revista ou jornal, uma entrada de log da Web, um comentário enviado pelo usuário, ou qualquer outro item independente do conteúdo.
aside Define um elemento reservado do resto do conteúdo da página. Se for removido, o conteúdo restante ainda faz sentido. Geralmente são barras laterais (sidebars).
footer Define o rodapé de uma página ou seção. Muitas vezes contém um aviso de copyright, links para informações relevantes, comentários de uma seção ou endereços para dar feedback
main Define o conteúdo principal no documento, de maior importância. Existe apenas um elemento deste tipo em uma página.

É importante notar que a utilização desse recurso sozinho não melhora significativamente a experiência com tecnologias assistivas, fazendo necessário a utilização de outros recursos que serão abordados ao longo deste documento, como ARIA.

WAI-ARIA

O que é?

ARIA é uma especificação técnica, publicada pela W3C, que discursa sobre como melhorar a acessibilidade páginas web, em particular como providenciar informação semântica de conteúdos dinâmicos e componentes de interface de usuário.

Essa especificação providencia uma série de propriedades, estados e "papéis" (roles) desenhados para definir informações consisas sobre elementos de interface de usuário que podem aperfeiçoar a acessibilidade e interoperabilidade do conteúdo web, permitindo que o desenvolvedor transmita corretamente o comportamento e a informação estruturada para tecnologias assistivas que agem diretamente na marcação do documento.

Lembre-se dos padrões web!

ARIA é uma específicação muito rica na criação de aplicações web acessíveis, mas lembre-se sempre de organizar o código de forma semântica e seguindos os padrões web. Dessa forma, não é preciso utilizar muitos recursos como ARIA, pois a página será naturalmente mais acessível.

Nota sobre os exemplos

Muitas formas de implementação e atribuição de recursos do WAI-ARIA não são padronizadas e, portanto, são subjetivas e costumam causar divergência de opiniões. Neste documento, é abordado diferentes pontos de vista sobre essa implementação. Qualquer sugestão de mudança será discutida e bem aceita, através da Repositório do código fonte do Web para Todos no GitHub.

Atributos Role

Estes atributos permitem atribuir a um elemento um “papel”, uma função, dentro de uma página. Existem cerca de 73 “roles”, mas eles não podem ser atribuídos de qualquer forma. Ao criar um slidebar, por exemplo, utilizando um gráfico, o leitor de tela poderá ler algo como “Gráfico, botão”, ao invés de algo mais significativo como “Slider, valor de 42 por cento”. Utilizando os roles é possível atribuir esse comportamento à um elemento e adicionar significado a ele.

Estados e Propriedades

Atributos de Widget

Veja a seção Comportamento (DOM) para ver os exemplos de atributos para widgets de ARIA.

Atributos de regiões dinâmicas (live)

aria-live
aria-atomic
aria-busy
aria-relevant

Atributos de "Arrastar e Soltar"

Veja a seção Comportamento (DOM) para ver os exemplos de atributos para widgets do tipo arrastar e soltar.

Atributos de Relacionamentos

Estados e Propriedades Globais

Considerações sobre Redundância com ARIA

Antes de começar a implementar todos os atributos ARIA possíveis, algumas considerações devem ser feitas, pois existem recomendações e regras para seu uso. Se você puder utilizar um elemento HTML ou atributo com a semântica e comportamento que você necessita já nativos, ao invés de re-atribuir propósito a um elemento e adicionar um role ARIA, estado ou propriedade, utilize apenas o elemento nativo. Existem três exceções nessa regra, segundo a W3C:

  • Se o recurso está disponível no HTML mas não é implementado, ou é implementado mas não há suporte à acessibilidade.
  • Se as restrições design visual excluem o uso de um elemento nativo em particular, porque o elemento não pode ser estilizado como necessário.
  • Se o recurso não está disponível no HTML.

Também não modifique semânticas nativas, como adicionar uma tag p com um role button!

Para mais informações sobre a utilização de ARIA, visite o documento online da W3C Notes on Using ARIA in HTML (em inglês). Nova Página

Imagens

Por razões bastante óbvias, imagens não são um recurso completamente acessível. Pessoas com problemas de visão e até mesmo cegueira, por exemplo, tem dificuldades na interpretação de elementos que sejam muito visuais, e a presença de imagens como componentes essenciais no conteúdo de uma página podem dificultar o entendimento da mesma. Dessa forma, é necessário apresentar alternativas para que a informação contida na imagem possa ser compreendida.

Texto Alternativo

Através do atributo alt, o desenvolvedor pode criar um texto alternativo à imagem, cujo objetivo seja descrever os elementos da imagem.

<img src="exemplo.png" alt="pessoa cega utilizando um computador de mesa e fones de ouvido">

Atenção especial para imagens decorativas!

Uma prática muito comum no desenvolvimento web é a utilização de imagens como recurso decorativo. Essas imagens não são essenciais para a compreensão da página, portanto elas não necessitam de texto alternativo.

Mas é importante não confundir um usuário que utiliza leitor de tela, por exemplo, que pode entender que essa imagem é essencial para a compreensão da página. Dessa forma, uma prática recomendada é a inserção da imagem decorativa através de CSS. Assim, agentes de usuário (como o leitor de tela) não interpretam o elemento como imagem.

Conteúdo Independente de Fluxo

As tags Figure e Figcaption

A tag figure representa conteúdos independentes, frequentemente com uma legenda (com a tag figcaption), e referido como uma única unidade. Esse elemento é relacionado com o fluxo principal da página, mas a posição dele é independente deste fluxo. Geralmente representa imagens, ilustrações, diagramas, códigos ou outros esquemas, referenciados no conteúdo principal. As tags que compõe essa funcionalidade são:

Exemplos

Neste exemplo, há uma imagem, e para a mesma há uma legenda:

<figure>
    <img src=”imagem.png” alt=”bola de basquete num fundo branco”>
    <figcaption> Figura 1. Bola de basquete</figcaption>
</figure>
    

Neste outro, há um elemento criado com a tag canvas, e sua posição é independente do fluxo da página, e portanto melhor utilizado com a tag figure:

<figure>
    <canvas id="example" role="img"></canvas>
    <figcaption>Figura 2. Exemplo de uso da tag canvas</figcaption>
</figure>
    

Note aqui a utilização de um role de imagem no elemento canvas

Também é possível utilizar códigos, e outros tipos de elementos:

<figure>
    <figcaption>Exemplo de código PHP</figcaption>
    <pre>
        <code class="php">
        <?php
        echo ‘Olá Mundo!’;
        ?>
        </code>
    </pre>
</figure>
    

Áudio e Vídeo

Áudio

Uma das novidades que a quinta versão do HTML trouxe aos desenvolvedores foi a possibilidade de criar conteúdo multimídia de forma nativa. A vantagem aqui é que plugins pesados e ultrapassados, como Flash, se tornaram obsoletos. Os mesmos também continham diversos problemas relacionados à acessibilidade e performance.

A tag audio foi criada para aprimorar a incorporação de áudio em páginas web. Com ela, é possível adicionar múltiplas fontes, arquivos de legenda, etc. Veja os exemplos à seguir:

<audio src="audio-example.ogg">
    Seu navegador não tem suporte para reprodução nativa de áudio.
</audio>

O exemplo acima é a forma mais básica de reproduçao de áudio nativa no HTML5. Mas não é completamente acessível: navegadores que não tem suporte ao formato OGG de áudio não serão capazes de reproduzir.

<audio controls="controls">
    Seu navegador não tem suporte para reprodução nativa de áudio.

    <source src="audio-example.wav" type="audio/wav">
    <source src="audio-example.ogg" type="audio/ogg">
    <source src="audio-example.mp3" type="audio/mpeg">
</audio>

Este último exemplo, diferente do primeiro, possui suporte à diferentes formatos de áudio e, dessa forma, é mais capaz de ser executado sem maiores problemas na maioria dos navegadores modernos. Mas não devemos apenas nos preocuparmos com compatibilidade, ao falar de acessibilidade, mas também a disponibilização desse conteúdo em outros formatos de mídia, como texto. Vamos adiante:

<audio controls="controls">
    Seu navegador não tem suporte para reprodução nativa de áudio.

    <source src="audio-example.wav" type="audio/wav">
    <source src="audio-example.ogg" type="audio/ogg">
    <source src="audio-example.mp3" type="audio/mpeg">

    <track kind="captions" src="audio-example.en.vtt" srclang="en" label="English">
    <track kind="captions" src="audio-example.sv.vtt" srclang="pt" label="Português">
</audio>

Com a tag track ainda podemos definir tipos diferentes de conteúdo textual para o áudio. Veja na seção à seguir mais opções para este elemento: Conteúdo Textual Alternativo (tag track)

Conteúdo Textual Alternativo (tag track)

A tag track é um elemento que adiciona conteúdo textual alternativo à áudios e vídeos. O atributo kind desta tag define o tipo de conteúdo (ou seja, para que serve este arquivo textual dentro do escopo da mídia) e admite 5 tipos, sendo estes:

  • subtitles: conteúdo do tipo legendas. Providencia traduções de conteúdo que não pode ser interpretado por quem vê ou escuta a mídia, como tradução em português de uma mídia em inglês. Também pode conter informação adicional, como por exemplo textos que aparecem em um vídeo.
  • captions: outro tipo de conteúdo de legenda. O diferencial é que este tipo pretende transcrever o áudio, mesmo que este não seja diálogo, e possivelmente também uma tradução. Inclui informação não verbal de importância, como telefone tocando ou efeitos sonoros. Adequado à usuários que possuem deficiências auditivas ou para reproduções sem áudio de um vídeo.
  • descriptions: descrição textual da mídia. Adequado à usuários com problemas de visão, como cegueira, ou em situações em que o vídeo não pode ser exibido.
  • chapters: títulos de capítulos, para serem usados quando o usuário está navegando pela mídia.
  • metadata: faixas utilizadas por scripts. Não é visível pelo usuário.

Além do atributo kind, ainda há os atributos: src, apontando para o arquivo; label, apresentando um rótulo para o conteúdo textual; e srclang, indicando a língua em que está o conteúdo. Veja o exemplo à seguir, que mostra o uso de track em um vídeo:

<video controls poster="poster.gif">
    <source src="video.mp4" type="video/mp4">
    <source src="video.ogv" type="video/ogv">

    <track kind="captions" src="transcricoes.vtt" srclang="pt">

    <track kind="descriptions" src="descricoes.vtt" srclang="pt">

    <track kind="chapters" src="capitulos.vtt" srclang="pt">

    <track kind="subtitles" src="legendas_es.vtt" srclang="es">
    <track kind="subtitles" src="legendas_en.vtt" srclang="en">
    <track kind="subtitles" src="legendas_pt.vtt" srclang="pt">

    <track kind="metadata" src="keyStage1.vtt" srclang="pt" label="Key Stage 1">
    <track kind="metadata" src="keyStage2.vtt" srclang="pt" label="Key Stage 2">
    <track kind="metadata" src="keyStage3.vtt" srclang="pt" label="Key Stage 3">
</video>

Vídeo

Assim como acontecia com mídias de áudio, vídeos na web também dependiam de plugins como Flash e apresentavam problemas de acessibilidade, e o recurso nativo aprimorou a inserção destas mídias na web. Veja os exemplos:

<video src="filme.ogg" poster="posterimage.jpg">
    Seu navegador não possui suporte à videos.
</video>

Também podemos incrementar este vídeo com legendas, assim como no próximo exemplo:

<video src="filme.ogg" poster="posterimage.jpg">
    Seu navegador não possui suporte à videos.
    <track kind="subtitles" src="filme.pt.vtt" srclang="pt" label="Português">
    <track kind="subtitles" src="filme.en.vtt" srclang="en" label="English">
    <track kind="subtitles" src="filme.sv.vtt" srclang="sv" label="Svenska">
</video>

Assim como a tag áudio, podemos incrementar ainda mais o vídeo com múltiplas fontes do mesmo arquivo em formatos diferentes, da mesma forma. Também podemos especificar diferentes tipos de conteúdo textual, como transcrições e afins. Veja a seção à seguir para mais informações: Conteúdo Textual Alternativo (tag track)

Widgets Acessíveis com ARIA

Existe uma infinidade de necessidades que desenvolvedores devem solucionar através da construção de widgets: componentes de interface de usuário que realizam determinadas funções de forma dinâmica. Entretanto, a maioria destes não possui forma de implementação nativa, ou a mesma não pode ser utilizada. Para tal, cabe ao desenvolvedor saber utilizar recursos específicos de acessibilidade, como o ARIA, para construir estes widgets. Veja os exemplos a seguir:

Widget de alerta e diálogo de alerta:

Os exemplos à seguir não necessitam de muita configuração por sere mais uma questão de marcação adequada, talvez com exceção do diálogo de alerta.

        <p role="alert">Você deve ler e aceitar os termos de uso!</p>
    
<div role="alertdialog" aria-labelledby="titulo" aria-describedby="descricao">
    <div role="document" tabindex="0">
        <h2 id="titulo">Sua sessão de login está para expirar!</h2>
        <p id="descricao">
            Clique no botão abaixo para extender sua sessão
        </p>
        <button>OK</button>
    </div>
</div>
    

Exemplos

Alerta
Diálogo de Alerta

Widget de barra de progresso

Utilizando os roles e atributos de estado e propriedades adequados, uma barra de progresso que faria diferença apenas visual pode, agora, apresentar valores máximos, mínimos e ainda indicar o valor atual.

HTML

<div class="progress">
    <div class="progress-bar" id="progress-bar"
        role="progressbar"
        aria-valuenow="20"
        aria-valuemin="0"
        aria-valuemax="100"
    style="width:20%">
        20 %
    </div>
</div>
<button onclick="aumentar10('progress-bar');" >Aumentar 10%</button>

CSS

#progress {
    height: 30px;
    overflow: hidden;
    background-color: #f5f5f5;
    border: 1px solid black;
}
#progress-bar {
    float: left;
    font-size: 12px;
    line-height: 20px;
    color: #fff;
    text-align: center;
    background-color: blue;
    transition: width .6s ease;
}

JS

function aumentar10(progressbarID) {
    var barra = document.getElementById(progressbarID);
    valuenow = barra.getAttribute('aria-valuenow');
    if (valuenow != "100") {
        valuenow = Number(valuenow) + 10;
        barra.setAttribute('aria-valuenow', valuenow);
        barra.style.width = String(valuenow) + '%';
        barra.innerHTML = String(valuenow) + ' %';
    }
    return true;
}

Exemplo

Ao pressionar o botão, uma adição de 10% será dada na barra de progresso abaixo.

20 %

Widget de caixa de diálogo:

<div role="dialog" aria-labelledby="titulo" aria-describedby="descricao">
    <h2 id="titulo">Mensagem enviada com sucesso!</h2>
    <p id="descricao">
        Sua mensagem foi enviada e será respondida brevemente.
    </p>
    <button>Fechar</button>
</div>
    

Widget de Arrastar e Soltar

HTML

<div id="alert-area" aria-live="assertive"></div>
<div id="maindrag">
    <a href="#" class="indicator-anchor" style="display:none;">Início do componente de arrastar e soltar</a>
    <div id="draggable" tabindex="0" draggable="true" ondragstart="event.dataTransfer.setData( 'text/plain',null)" aria-grabbed="false">
        Lavar a louça
    </div>

    <div class="dropzone">
        <span id="drop1">Para Fazer</span>
    </div>

    <div class="dropzone">
        <span id="drop2">Fazendo</span>
    </div>

    <div class="dropzone">
        <span id="drop3">Feito</span>
    </div>
    <a href="#" class="indicator-anchor" style="display:none;">Fim do componente de arrastar e soltar</a>
</div>

CSS

.dropzone {
    color: white;
    background-color: gray;
    display: block;
    width: 350px;
    height: 200px;
    padding: 1em;
    margin: 0.5em;
}

#draggable {
    cursor: grab;
    color: white;
    font-weight: bold;
    background-color: black;
    width: 300px;
    padding: 0.5em;
}

#draggable:focus, #draggable:hover {
    border: 3px solid red;
}

[aria-dropeffect=move] {
    border: 5px solid black;
}

.drophover {
    border: 5px solid red !important;
}

.dropzone span {
    font-weight: bold;
}

.sronly, #alert-area, .indicator-anchor {
    position: absolute;
    left: -999em;
}

JS

var dragged;

var verify_dropELement = false;

var dnd_alert = document.getElementById('alert-area');

function dragStart(event) {
    // TODO: limitar a navegação por tab apenas entre as dropzones quando o elemento arrastável é pegos
    if (event.type == 'dragstart' || (event.type == 'keypress' & & event.keyCode == '13')) {

        // Ativa âncoras de começo e fim do widget
        var anchors = document.getElementsByClassName('indicator-anchor');
        for (var i = 0; i < anchors.length; i++) {
            anchors[i].removeAttribute('style');
        }

        // Anuncia que o elemento foi capturado
        var msg = document.createTextNode("O elemento foi capturado");
        dnd_alert.appendChild(msg);

        // Remove o tabindex do elemento arrastável
        dragged = event.target;
        dragged.removeAttribute('tabindex');
        var parent = dragged.parentNode;

        // modifica o atributo aria-grabbed como true no elemento
        event.target.setAttribute("aria-grabbed", true);
        var dropzones = document.getElementsByClassName('dropzone');
        for (var i = 0; i < dropzones.length; i++) {
            dropzones[i].setAttribute("aria-dropeffect", "move");
            dropzones[i].setAttribute('tabindex', '0');
        }
    }
}

function dragEnd(event) {
    // modifica o atributo aria-grabbed para false
    if (event.type == 'dragend' || (event.type == 'keypress' & & event.keyCode == '13' & & verify_dropELement)) {
        if (verify_dropELement) {

            var anchors = document.getElementsByClassName('indicator-anchor');
            for (var i = 0; i < anchors.length; i++) {
                anchors[i].setAttribute('style', 'display:none;')
            }

            var msg = document.createTextNode("O elemento foi largado");
            dnd_alert.appendChild(msg);
            event.target.setAttribute("aria-grabbed", false);
            var dropzones = document.getElementsByClassName('dropzone');
            for (var i = 0; i < dropzones.length; i++) {
                dropzones[i].removeAttribute("aria-dropeffect");
                dropzones[i].removeAttribute('tabindex');
                dropzones[i].className = "dropzone"
            }
        }
        verify_dropELement = false;
    }
}

function dragOver(event) {
    // previne o comportamento padrão para permitir que o elemento arrastado seja soltado dentro
    event.preventDefault();
}

function dragEnter(event) {
    var el = event.target;
    if (el.className == "dropzone" || el.className == "dropzone drophover") {
        // adiciona o atributo aria-dropeffect como move, indicando que o efeito realizado ao arrastar e soltar um elemento é movê-lo
        el.className = "dropzone drophover";
    }
}



function dragLeave(event) {
    var el = event.target;
    if (el.className == "dropzone" || el.className == "dropzone drophover") {
        // remove o atributo quando o elemento arrastado não está mais sobre este alvo
        el.className = "dropzone";
    }
}



function dropElement(event) {
    if (event.type == 'drop' || (event.type == 'keypress' & & event.keyCode == '13' & & !verify_dropELement)) {
        // previne a ação padrão (abre como link para alguns elementos)
        event.preventDefault();
        var el = event.target;
        // move o elemento arrastado para o alvo
        dragged.setAttribute('tabindex', 0);
        if (el.className == "dropzone" || el.className == "dropzone drophover") {
            el.appendChild(dragged);
        }
        verify_dropELement = true;
    }
}

var main = document.getElementById('maindrag');

var draggable = document.getElementById('draggable');

var dropzones = document.getElementsByClassName('dropzone');


draggable.addEventListener('keypress', dragStart, false);
main.addEventListener("dragstart", dragStart, false);

main.addEventListener("dragend", dragEnd, false);

main.addEventListener("dragover", dragOver, false);

main.addEventListener("dragenter", dragEnter, false);

main.addEventListener("dragleave", dragLeave, false);

main.addEventListener("drop", dropElement, false);

for (var i = 0; i < dropzones.length; i++) {
    dropzones[i].addEventListener('focus', dragEnter, false);
    dropzones[i].addEventListener('blur', dragLeave, false);
    dropzones[i].addEventListener('keypress', dropElement, true);
    dropzones[i].addEventListener('keypress', dragEnd, true);
}

Exemplo

Para utilizar o componente de arrastar e soltar deste exemplo, utilizando um teclado, as instruções são bastante simples. Confira a lista abaixo:

  • Selecione o elemento "Lavar a Louça" utilizando a tecla Enter.
  • Navegue pelas áreas em que o elemento pode ser largado utilizando a tecla tab.
  • Selecione a área desejada utilizando a tecla Enter.
  • Há âncoras para indicar o início e fim da seção em que existe áreas para largar o elemento.

Widget de link:

HTML

        <span role="link" id="link" tabindex="0">Site do Centro Tecnólógico de Acessibilidade</span>
    

CSS

[role=link] {
        color: #0064b4;
}

[role=link]:focus, [role=link]:hover {
    color: #23527c;
    text-decoration: underline;
    cursor: pointer;
}

JS

var link = document.getElementById('link');

function linkToCTA(e) {
    if (e.type == 'click' || (e.type == 'keydown' && e.keyCode == '13')) {
        window.location.href = 'http://cta.ifrs.edu.br';
    }
}

link.addEventListener('click', linkToCTA);
link.addEventListener('keypress', linkToCTA);

Exemplo

Site do Centro Tecnólógico de Acessibilidade

Não utilize este role em links nativos do HTML, pois é redundante. Âncora para: Considerações Sobre Redundância com ARIA

Também lembre-se que, se for extritamente necessário construir um link dessa forma, configurações adicionais são necessárias para dar ao elemento a função de um link.

Widget de log e marquee:

<div id="chat" role="log" aria-live="polite">
    ...
</div>

Utilizar role log implica que a região possui o valor polite para o a propriedade aria-live.

<div class="algumacoisa" role="marquee" aria-live="polite">
    ...
</div>

Utilizar role marquee implica que a região possui o valor off para o a propriedade aria-live.

Substitua o role log por marquee em casos em que a região live é atualizada com informação que não é relevante.

Exemplo do widgets de list, listbox e combobox

Os exemplos abaixo demonstram a utilização de listas e listas interativas, contendo opções ou funcionando de forma similar a elementos select. No primeiro, temos um exemplo de uma lista simples e não interativa.

<div class="lista" role="list">
    <div role="listitem">Ovos</div>
    <div role="listitem">Farinha</div>
    <div role="listitem">Leite</div>
    <div role="listitem">Pão</div>
</div>

Não utilize este role em listas nativas do HTML, pois é redundante. Âncora para: Considerações Sobre Redundância com ARIA

Para criar listas interativas, há roles diferentes de list, e um deles é o listbox. No exemplo abaixo, há uma lista de itens selecionáveis.

<div role="listbox" tabindex="0" id="listbox1" aria-activedescendant="listbox1-1">
    <div role="option" id="listbox1-1" class="selected">Verde</div>
    <div role="option" id="listbox1-2">Laranja</div>
    <div role="option" id="listbox1-3">Vermelho</div>
    <div role="option" id="listbox1-4">Azul</div>
    <div role="option" id="listbox1-5">Preto</div>
    <div role="option" id="listbox1-6">Violeta</div>
</div>

Outro role para listas interativas é o combobox, muito semelhante à textbox e utilizado em widgets que sejam como um elemento select, porém com uma entrada de usuário para busca ou adição de novo item. Note que ainda há uma lista utilizando listbox abaixo do elemento input com o role combobox.

<input type="text"
    aria-label="Tag"
    role="combobox"
    aria-expanded="true"
    aria-autocomplete="list"
    aria-owns="listbox"
aria-activedescendant="opcao_selecionada">
<ul role="listbox" id="listbox">
    <li role="option">Zebra</li>
    <li role="option" id="opcao_selecionada">Zoom</li>
</ul>

É imporante lembrar que nos casos dos roles listbox e combobox, precisa-se desenvolver todas operações através de JavaScript, como a seleção de opções e afins.

Widget de menu:

HTML

        <ul role="menu" id="vertical-menu" tabindex="0">
<li tabindex="0" role="menuitem">Item 1</li>
<li tabindex="0" role="menuitem">Item 2</li>
<div role="group" id="radiogroup">
<li tabindex="0" class="radio-item" role="menuitemradio" aria-checked="false">Opção 1</li>
<li tabindex="0" class="radio-item" role="menuitemradio" aria-checked="false">Opção 2</li>
<li tabindex="0" class="radio-item" role="menuitemradio" aria-checked="true">Opção 3</li>
</div>
</ul>

<div role="menubar" id="horizontal-menu" tabindex="0">
<span tabindex="0" role="menuitem">Novo Documento</span>
<span tabindex="0" role="menuitem">Abrir Documento</span>
<div role="group">
<span tabindex="0" class="checkbox-item" role="menuitemcheckbox" aria-checked="true">Salvar Automaticamente</span>
<span tabindex="0" class="checkbox-item" role="menuitemcheckbox" aria-checked="false">Salvar na Nuvem</span>
</div>
</div>
    

CSS

li[role=menuitem], li[role=menuitemradio], li[role=menuitemcheckbox] {
list-style: none;
display: block
}

ul[role=menu] li, div[role=menubar] span {
background-color: black;
padding: 1em;
color: white;
font-weight: bold;
}

ul[role=menu] li:hover, div[role=menubar] span:hover, ul[role=menu] li:focus, div[role=menubar] span:focus {
background-color: lightgrey;
color: black;
}

span[role=menuitem], span[role=menuitemradio], span[role=menuitemcheckbox] {
list-style: none;
margin-bottom: 0.5em;
margin-right: 0;
margin-left: 0;
display: inline-block
}

[aria-checked="true"] {
background-color: grey !important;
}

JS

function mudarEstado(e){
if (e.type == 'click' || (e.type == 'keydown' && e.keyCode == '13')) {
var obj = e.target;
if (obj) {
var checked = obj.getAttribute('aria-checked')
if (checked == 'true') {
obj.setAttribute('aria-checked', 'false');
}
else {
obj.setAttribute('aria-checked', 'true');
}
}
}
}

var elementos = document.getElementsByClassName("checkbox-item");

for (var i = 0; i < elementos.length; i++) {
elementos[i].addEventListener('click', mudarEstado);
elementos[i].addEventListener('keydown', mudarEstado);
}

var group = document.getElementById('radiogroup');
function mudarEstadoRadio(e) {
if (e.type == 'click' || (e.type == 'keydown' && e.keyCode == '13')) {
var group = document.getElementById('radiogroup');
for (var i = 0; i < group.children.length; i++) {
var checked = group.children[i].getAttribute('aria-checked')
if (checked == 'true') {
group.children[i].setAttribute('aria-checked', 'false');
}
}

var obj = e.target;

if (obj) {
var checked = obj.getAttribute('aria-checked')
if (checked == 'true') {
obj.setAttribute('aria-checked', 'false');
}
else {
obj.setAttribute('aria-checked', 'true');
}
}

}
}

var elements = document.getElementsByClassName("radio-item");
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', mudarEstadoRadio);
elements[i].addEventListener('keydown', mudarEstadoRadio);
}

Exemplos Ao Vivo

Menu Vertical com Seleções Únicas no Grupo de Opções
Menu Horizontal com Seleções Múltiplas no Grupo de Opções

Enquanto menu representa um menu de forma genérica, menubar necessariamente representa um menu horizontal.

Menus não precisam, necessariamente, serem atribuidos à listas (elemento ul).

Widget de status:

        <p role="status">Suas mudanças foram salvas automaticamente.</p>
    
  • gridcell:
  • radio:
  • scrollbar:
  • spinbutton:
  • tab:
  • tabpanel:
  • textbox:
  • timer:
  • tooltip:
  • treeitem:

Navegação por Teclado

Foco

Cores e Contraste

Responsividade