Programação Por Equipe DevBras

Capa do artigo: Triângulo e seta em CSS que crescem com a fonte e não deixam o botão mudo

Triângulo e seta em CSS que crescem com a fonte e não deixam o botão mudo

Faça a seta do menu em CSS com tamanho em em e cor do texto, escolha entre borda, clip-path e caractere, e garanta que o botão tenha nome e estado para leitor de tela.

O botão "Mais opções" tinha uma setinha num GIF de 8 por 5 pixels. A equipe trocou por CSS, ganhou uma requisição a menos e seguiu a vida. Meses depois, alguém aumentou a fonte do site e a seta continuou minúscula ao lado do texto grande; no modo de alto contraste do Windows ela virou um quadrado; e o leitor de tela anunciava só "botão", sem nome, porque no menu do celular a seta era o único conteúdo. A resposta curta: desenhe a seta com medidas em em e cor currentColor, para ela acompanhar o tamanho e a cor do texto; marque a seta como decorativa; e dê ao botão um nome em texto e o estado aria-expanded.

O triângulo em si é a parte fácil. O que quebra em produção é o tamanho fixo, a cor solta e o botão sem nome.

Por que o truque da borda funciona e onde ele falha

Um elemento com largura e altura zero e bordas grossas desenha só as bordas. Cada borda é um trapézio que encontra a vizinha numa diagonal. Se você pinta uma borda e deixa as duas laterais transparentes, sobra um triângulo apontando para o lado oposto à borda pintada. Borda de cima colorida, seta para baixo.

O truque é antigo e funciona em qualquer navegador. Os problemas vêm do jeito como costuma ser copiado. Com border-width: 5px, a seta não acompanha a fonte. Com border-top-color: #666, a cor não acompanha o texto, e no hover o texto muda de cor e a seta não. E como o desenho depende de bordas transparentes, modos de cor forçada, como o alto contraste do Windows, podem repintar essas bordas e transformar o triângulo num bloco. Vale testar no seu navegador alvo antes de espalhar.

Hoje há outras formas de desenhar a mesma seta, e nenhuma é obrigatória. A escolha depende do que você precisa: forma exata, facilidade de girar, comportamento no alto contraste.

Como desenhar a seta, em três versões

O exemplo é o botão de menu de uma loja, com o texto "Categorias" e uma seta que aponta para baixo quando fechado e para cima quando aberto. O HTML é o mesmo para as três versões:

<button type="button" class="menu-botao" aria-expanded="false" aria-controls="menu-categorias">
  Categorias
  <span class="seta" aria-hidden="true"></span>
</button>
<ul id="menu-categorias" hidden>
  <li><a href="/bicicletas">Bicicletas</a></li>
  <li><a href="/pecas">Peças</a></li>
</ul>

O aria-hidden="true" tira a seta da árvore de acessibilidade: ela é decoração, e o nome do botão vem do texto "Categorias".

Versão 1: borda, com medidas em em

.seta {
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: 0.4em;
  vertical-align: middle;
  border-left: 0.35em solid transparent;
  border-right: 0.35em solid transparent;
  border-top: 0.45em solid currentColor;
}

Com em, a seta cresce junto com a fonte do botão. Com currentColor, ela herda a cor do texto, inclusive no hover e no foco, sem regra extra.

Versão 2: clip-path, para forma livre

.seta {
  display: inline-block;
  width: 0.7em;
  height: 0.45em;
  margin-left: 0.4em;
  vertical-align: middle;
  background: currentColor;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}

Aqui o elemento tem tamanho de verdade e o clip-path recorta um triângulo com os pontos que você escolher. É mais fácil de ler e ajustar que a combinação de bordas. A ressalva é a mesma do alto contraste: a cor de fundo também pode ser forçada pelo sistema, então teste.

Versão 3: chevron de duas bordas girado

.seta {
  display: inline-block;
  width: 0.45em;
  height: 0.45em;
  margin-left: 0.5em;
  border-right: 0.12em solid currentColor;
  border-bottom: 0.12em solid currentColor;
  transform: translateY(-0.15em) rotate(45deg);
}

É a seta em forma de "V" aberto, comum em menus atuais. Como ela é feita de bordas visíveis com a cor do texto, costuma se comportar melhor em modos de cor forçada do que as versões baseadas em transparência ou fundo. Se você quer garantia total, um caractere de texto ou um SVG embutido com fill="currentColor" também seguem a cor do texto.

Como girar a seta pelo estado do botão

  1. Guarde o estado no próprio botão, em aria-expanded.
  2. No clique, inverta o valor e mostre ou esconda a lista.
  3. Gire a seta no CSS lendo o atributo, sem classe extra.
  4. Desligue a animação para quem pediu menos movimento.
.menu-botao[aria-expanded="true"] .seta {
  transform: rotate(180deg);
}

.seta {
  transition: transform 0.15s ease;
}

@media (prefers-reduced-motion: reduce) {
  .seta {
    transition: none;
  }
}
const botao = document.querySelector('.menu-botao');
const menu = document.getElementById('menu-categorias');

botao.addEventListener('click', () => {
  const aberto = botao.getAttribute('aria-expanded') === 'true';
  botao.setAttribute('aria-expanded', String(!aberto));
  menu.hidden = aberto;
});

Essa regra de giro serve para as versões 1 e 2. Na versão 3, que já usa transform para virar o "V", repita a translação e troque o ângulo para rotate(225deg) no estado aberto, senão uma regra sobrescreve a outra.

Ligar o visual ao aria-expanded tem um efeito bom: se alguém esquecer de atualizar o atributo, a seta não gira, e o erro aparece no teste visual antes de chegar ao usuário de leitor de tela.

Quando a seta é o único conteúdo do botão

No celular, o mesmo menu às vezes vira um botão só com a seta. Aí o botão precisa de nome acessível, senão o leitor de tela anuncia "botão" e mais nada. Use aria-label="Abrir categorias" no botão, ou um texto visualmente escondido dentro dele, e mantenha a seta com aria-hidden. O post sobre componente CSS que ainda precisa de nome aprofunda esse ponto. Garanta também uma área de toque confortável: a seta tem meio em, o botão não pode ter.

Erros comuns

  • Tamanho da seta em pixel. Com fonte aumentada, a seta fica minúscula e desalinhada do texto.
  • Cor da seta fixa e diferente da do texto. No hover, no foco ou no tema escuro, a seta some ou destoa; cinza sobre cinza não tem contraste.
  • Botão só com a seta e sem nome. O leitor de tela anuncia "botão" e a pessoa não sabe o que ele faz.
  • Seta indicando aberto ou fechado sem aria-expanded. Quem não vê a seta não sabe o estado do menu.
  • Não testar alto contraste. O triângulo feito de transparência pode virar quadrado ou sumir justamente para quem mais depende de contraste.
  • Caractere de seta sem aria-hidden. O leitor de tela lê o nome do símbolo no meio do rótulo do botão.

Checklist

  • As medidas da seta estão em em.
  • A cor da seta é currentColor.
  • A seta tem aria-hidden="true".
  • O botão tem nome em texto ou aria-label.
  • O botão tem aria-expanded atualizado no clique.
  • Com a fonte do navegador em 200%, a seta acompanha o texto.
  • No modo de alto contraste, a seta continua com forma de seta.

Se o alinhamento da seta com o texto está dando trabalho, o guia de centralizar sem número mágico ajuda, e o de cor com contraste cobre a escolha da cor do botão. Aumente agora a fonte do navegador duas vezes e olhe o seu menu: a seta cresceu junto, e sem ela o botão ainda diz o que faz?

Avalie este artigo

Referência: http://simplesideias.com.br/criando-triangulos-com-css