Uma visão geral básica de como criar um componente de navegação estruturada responsivo e acessível para que os usuários naveguem pelo seu site.
Nesta postagem, quero compartilhar ideias sobre uma maneira de criar componentes da navegação estrutural. Teste a demonstração.
Se preferir vídeos, confira a versão desta postagem no YouTube:
Visão geral
Um componente de localização atual mostra em que parte da hierarquia do site o usuário está. O nome vem de Hansel e Gretel, que deixaram pedaços de pão para trás em uma floresta escura e conseguiram encontrar o caminho de casa rastreando os pedaços de pão.
A navegação estrutural desta postagem não é padrão
navegação estrutural,
semelhantes a uma navegação estrutural. Elas oferecem mais funcionalidades ao colocar
páginas diretamente na navegação com um <select>
, tornando o acesso a várias camadas
sempre que possível.
UX de segundo plano
No vídeo de demonstração de componentes acima, as categorias de marcador de posição são gêneros de
videogames. Essa trilha é criada ao navegar pelo seguinte caminho: home »
rpg » indie » on sale
, conforme mostrado abaixo.
Esse componente de breadcrumbs precisa permitir que os usuários naveguem por essa hierarquia de informações, pulando ramificações e selecionando páginas com velocidade e precisão.
Arquitetura de informações
Acho útil pensar em termos de coleções e itens.
Coleções
Uma coleção é uma matriz de opções para você escolher. Na página inicial do protótipo de breadcrumb desta postagem, as coleções são FPS, RPG, brawler, dungeon crawler, esportes e quebra-cabeça.
Itens
Um videogame é um item, e uma coleção específica também pode ser um item se representar outra coleção. Por exemplo, RPG é um item e uma coleção válida. Quando é um item, o usuário está na página da coleção. Por exemplo: ele está na página RPG, que exibe uma lista de jogos de RPG, incluindo subcategorias adicionais AAA, Indie e Autopublicação.
Em termos de ciência da computação, esse componente de navegação estrutural representa uma multidimensional matriz:
const rawBreadcrumbData = {
"FPS": {...},
"RPG": {
"AAA": {...},
"indie": {
"new": {...},
"on sale": {...},
"under 5": {...},
},
"self published": {...},
},
"brawler": {...},
"dungeon crawler": {...},
"sports": {...},
"puzzle": {...},
}
Seu app ou site terá uma arquitetura de informação (IA) personalizada que cria uma matriz multidimensional diferente, mas espero que o conceito de páginas de destino de coleção e a travessia de hierarquia também possam ser incluídos nos seus breadcrumbs.
Layouts
Marcação
Bons componentes começam com o HTML apropriado. Nesta próxima seção, vou abordar minhas escolhas de markup e como elas afetam o componente geral.
Esquema escuro e claro
<meta name="color-scheme" content="dark light">
A metatag color-scheme
no exemplo acima
snippet informa ao navegador que esta página quer a versão clara e a escura
estilos. As localizações atuais do exemplo não incluem CSS para esses esquemas de cores,
Assim, a navegação estrutural usará as cores padrão fornecidas pelo navegador.
Elemento de navegação
<nav class="breadcrumbs" role="navigation"></nav>
É apropriado usar o elemento
<nav>
para a navegação do site, que tem um papel de
navegação ARIA implícito.
Nos testes, notei que o atributo role
mudou a maneira como um
Leitor de tela interagiu com o elemento. Ele foi anunciado como
navegação, e por isso decidi adicioná-lo.
Ícones
Quando um ícone é repetido em uma página, o elemento SVG
<use>
significa que você pode definir o path
uma vez e usá-lo em todas as instâncias do
ícone. Isso evita que as mesmas informações de caminho sejam repetidas, causando
documentos maiores e o potencial de inconsistência do caminho.
Para usar essa técnica, adicione um elemento SVG oculto à página e envolva os ícones.
em um elemento <symbol>
com um ID exclusivo:
<svg style="display: none;">
<symbol id="icon-home">
<title>A home icon</title>
<path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
</symbol>
<symbol id="icon-dropdown-arrow">
<title>A down arrow</title>
<path d="M19 9l-7 7-7-7"/>
</symbol>
</svg>
O navegador lê o HTML SVG, coloca as informações do ícone na memória e continua com o restante da página referenciando o ID para outros usos do ícone, como este:
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<use href="#icon-home" />
</svg>
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<use href="#icon-dropdown-arrow" />
</svg>
Defina uma vez e use quantas vezes quiser, com impacto mínimo no desempenho da página
e estilização flexível. Observe que aria-hidden="true"
foi adicionado ao elemento SVG.
Os ícones não são úteis para alguém que apenas ouve o conteúdo ao navegar, ocultando
e impede que adicionem ruído desnecessário.
Link dividido .crumb
É aqui que o caminho de navegação tradicional e os caminhos neste componente divergem.
Normalmente, esse seria apenas um link <a>
, mas adicionei a UX de travessia com um
seletor disfarçado. A classe .crumb
é responsável por dispor o link e
do ícone, enquanto .crumbicon
é responsável por empilhar o ícone e selecionar
elemento reunido. Chamei de link dividido porque as funções dele são muito
semelhantes a um botão dividido,
mas para navegação de página.
<span class="crumb">
<a href="#sub-collection-b">Category B</a>
<span class="crumbicon">
<svg>...</svg>
<select class="disguised-select" title="Navigate to another category">
<option>Category A</option>
<option selected>Category B</option>
<option>Category C</option>
</select>
</span>
</span>
Um link e algumas opções não são nada especiais, mas adicionam mais funcionalidade a um
breadcrumb simples. Adicionar um title
ao elemento <select>
é útil para a tela.
usuários do leitor, fornecendo informações sobre a ação do botão. No entanto,
também ajuda todos os outros, você vai perceber
que está em destaque
iPad. Um atributo fornece contexto de botão para muitos usuários.
Decorações do separador
<span class="crumb-separator" aria-hidden="true">→</span>
Os separadores são opcionais, mas adicionar apenas um também funciona (confira o terceiro exemplo no vídeo
acima). Depois, dou aria-hidden="true"
a cada um, já que eles são decorativos e não
algo que um leitor de tela precisa anunciar.
A propriedade gap
, abordada a seguir, simplifica o espaçamento entre esses elementos.
Estilos
Como a cor usa cores do sistema, ela tem principalmente lacunas e pilhas para estilos.
Direção e fluxo do layout
O elemento de navegação principal nav.breadcrumbs
define uma propriedade personalizada com escopo
para que os filhos usem e, caso contrário, estabelece um layout horizontal alinhado verticalmente. Isso garante que as trilhas, os divisores e os ícones fiquem alinhados.
.breadcrumbs {
--nav-gap: 2ch;
display: flex;
align-items: center;
gap: var(--nav-gap);
padding: calc(var(--nav-gap) / 2);
}
Cada .crumb
também estabelece um layout alinhado verticalmente com alguma
diferença, mas segmenta especialmente os links filhos e especifica o estilo
white-space: nowrap
. Isso é crucial para a navegação estrutural com várias palavras,
usar várias linhas. Mais adiante nesta postagem, vamos adicionar estilos para processar o
overflow horizontal causado por essa propriedade white-space
.
.crumb {
display: inline-flex;
align-items: center;
gap: calc(var(--nav-gap) / 4);
& > a {
white-space: nowrap;
&[aria-current="page"] {
font-weight: bold;
}
}
}
O aria-current="page"
é adicionado para ajudar o link da página atual a se destacar dos
demais. Os usuários de leitores de tela não apenas terão uma indicação clara de que o link é
para a página atual, mudamos visualmente o elemento para ajudar usuários com visão normal
ter uma experiência do usuário semelhante.
O componente .crumbicon
usa a grade para empilhar um ícone SVG com um elemento <select>
"quase
invisível".
.crumbicon {
--crumbicon-size: 3ch;
display: grid;
grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
place-items: center;
& > * {
grid-area: stack;
}
}
O elemento <select>
é o último no DOM, então ele está na parte de cima da pilha
e é interativo. Adicione um estilo de opacity: .01
para que o elemento ainda possa ser usado.
e o resultado é uma caixa de seleção que se encaixa perfeitamente no formato do ícone.
Essa é uma boa maneira de personalizar a aparência de um elemento <select>
enquanto
e manter a funcionalidade integrada.
.disguised-select {
inline-size: 100%;
block-size: 100%;
opacity: .01;
font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}
Menu flutuante
Os breadcrumbs precisam representar um caminho muito longo. Sou fã de permitir que as coisas saiam da tela horizontalmente, quando apropriado, e senti que esse componente de breadcrumbs se qualificou bem.
.breadcrumbs {
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
scroll-padding-inline: calc(var(--nav-gap) / 2);
& > .crumb:last-of-type {
scroll-snap-align: end;
}
@supports (-webkit-hyphens:none) { & {
scroll-snap-type: none;
}}
}
Os estilos de sobrecarga configuram a UX a seguir:
- Rolagem horizontal com contenção de rolagem excessiva.
- Padding de rolagem horizontal.
- Um ponto de ajuste na última trilha. Isso significa que, no carregamento da página, carregamentos de navegação no local e visíveis.
- Remove o ponto de ajuste do Safari, que tem problemas com as combinações de rolagem horizontal e efeito de ajuste.
Consultas de mídia
Um ajuste sutil para janelas de visualização menores é ocultar a página inicial o rótulo, deixando apenas o ícone:
@media (width <= 480px) {
.breadcrumbs .home-label {
display: none;
}
}
Acessibilidade
Movimento
Não há muito movimento nesse componente, mas, ao agrupar a
transição em uma verificação prefers-reduced-motion
, podemos evitar movimentos indesejados.
@media (prefers-reduced-motion: no-preference) {
.crumbicon {
transition: box-shadow .2s ease;
}
}
Nenhum dos outros estilos precisa mudar. Os efeitos de foco e de passar o cursor são ótimos
e significativos sem um transition
, mas, se o movimento estiver correto, vamos adicionar uma transição
sutil à interação.
JavaScript
Primeiro, independentemente do tipo de roteador usado no site ou aplicativo,
quando um usuário muda os breadcrumbs, o URL precisa ser atualizado e a página
adequada precisa ser mostrada ao usuário. Em segundo lugar, para normalizar
a experiência do usuário, verifique se
nenhuma navegação inesperada acontece quando os usuários estão apenas navegando pelo <select>
.
Duas medidas importantes de experiência do usuário que precisam ser tratadas pelo JavaScript: o elemento de seleção mudou
e a prevenção de disparo de evento de mudança <select>
ansioso.
A prevenção de eventos antecipados é necessária devido ao uso de um elemento
<select>
. No Windows Edge e em outros navegadores, selecione changed
.
é disparado à medida que o usuário navega pelas opções com o teclado. É por isso que eu
chamado de ansioso, já que o usuário apenas pseudoselecionou a opção, como passar o cursor
ou foco, mas ainda não tiver confirmado a escolha com enter
ou click
. O evento
ansioso torna esse recurso de mudança de categoria de componente inacessível, porque
abrir a caixa de seleção e simplesmente navegar por um item aciona o evento e
muda a página antes que o usuário esteja pronto.
Um evento <select>
alterado melhor
const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])
// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
let ignoreChange = false
nav.addEventListener('change', e => {
if (ignoreChange) return
// it's actually changed!
})
nav.addEventListener('keydown', ({ key }) => {
if (preventedKeys.has(key))
ignoreChange = true
else if (allowedKeys.has(key))
ignoreChange = false
})
})
A estratégia para isso é observar os eventos de pressionamento do teclado em cada <select>
.
e determinar se a tecla pressionada foi uma confirmação de navegação (Tab
ou
Enter
) ou a navegação espacial (ArrowUp
ou ArrowDown
). Com este
determinada, o componente pode decidir aguardar ou continuar, quando o evento do
O elemento <select>
é acionado.
Conclusão
Agora que você sabe como eu fiz isso, o que você faria‽ 🙂
Vamos diversificar nossas abordagens e aprender todas as maneiras de criar na Web. Crie uma demonstração, envie um tweet para mim e adicione o link acesse a seção "Remixes da comunidade" abaixo.
Remixes da comunidade
- Tux Solbakk como um componente da Web: demonstração e código