HOWTO · CSS

Escolha de Estilo Dropdown no CSS

Este tutorial apresenta como estilizar o menu suspenso em CSS.

Nesta página

Você pode estilizar um menu suspenso nativo de HTML select alterando sua caixa e substituindo a seta padrão por uma seta desenhada em CSS. O controle continua sendo um select real, portanto os usuários mantêm o comportamento de seleção integrado e a interação pelo teclado. Duas abordagens comuns são remover diretamente a aparência nativa ou ocultar a seta nativa com um contêiner que a recorta.

Estilize um menu suspenso nativo de select mantendo seu comportamento

Um select contém elementos option, e o navegador renderiza o controle conforme as convenções da plataforma e do próprio navegador. O CSS pode alterar a caixa visível, mas a lista aberta e alguns detalhes ainda variam entre navegadores e sistemas operacionais. Mantenha o elemento nativo em vez de recriar um menu suspenso com elementos genéricos quando quiser preservar os comportamentos integrados de formulário e teclado.

Os exemplos usam as mesmas quatro opções. O CSS deixa espaço à direita do controle para uma seta e mostra um contorno de foco visível para quem navega pelo teclado. Ajuste as cores e dimensões para combinarem com sua interface.

Oculte a seta nativa com appearance: none e adicione uma seta personalizada

Defina appearance: none no select para remover a seta nativa do navegador onde houver suporte e, em seguida, desenhe um chevron com dois gradientes CSS. As camadas de background posicionam os dois traços perto da borda direita; o preenchimento à direita mantém o texto das opções afastado deles. As declarações -webkit-appearance e -moz-appearance são formas prefixadas da propriedade, usadas por mecanismos que reconhecem esses prefixos; elas não correspondem a famílias distintas de navegadores WebKit/Blink. A propriedade padrão é a declaração principal, então mantenha os prefixos apenas se os requisitos de compatibilidade dos seus navegadores justificarem isso.

Esta página completa é um exemplo estático para navegador. Salve-a como arquivo HTML e abra-a em um navegador para ver o controle; o pacote não inclui evidências de renderização em navegador que permitam afirmar um resultado visual observado.

O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Styled select</title>
  <style>
    .vehicle-select {
      box-sizing: border-box;
      width: 220px;
      height: 40px;
      padding: 0 32px 0 10px;
      border: 1px solid #777;
      border-radius: 4px;
      color: #222;
      background-color: #fff;
      background-image:
        linear-gradient(45deg, transparent 50%, #333 50%),
        linear-gradient(135deg, #333 50%, transparent 50%);
      background-position:
        calc(100% - 14px) 50%,
        calc(100% - 9px) 50%;
      background-size: 5px 5px, 5px 5px;
      background-repeat: no-repeat;
      appearance: none;
    }

    .vehicle-select:focus-visible {
      outline: 3px solid #1769aa;
      outline-offset: 2px;
    }
  </style>
</head>
<body>
  <label for="vehicle">Choose a vehicle</label>
  <select class="vehicle-select" id="vehicle" name="vehicle">
    <option>RE Himalayan</option>
    <option selected>CRF Rally</option>
    <option>GS 310</option>
    <option>KTM ADV</option>
  </select>
</body>
</html>

Oculte a seta nativa com um contêiner que a recorta

Um contêiner oferece outra forma de ocultar a seta nativa. Defina no select uma largura um pouco maior que a do contêiner e configure overflow do contêiner como hidden; assim, a seta nativa fica fora da área visível. Desenhe uma seta substituta no contêiner usando um pseudo-elemento. Como o select continua dentro dele, suas opções e interação nativa permanecem disponíveis.

Deixe o contêiner largo o bastante para o texto do controle e faça o select apenas um pouco mais largo. Se a diferença for grande demais, a borda direita do controle ou outro conteúdo também poderá ser recortado. O pseudo-elemento usa pointer-events: none para não interceptar os cliques no select.

No exemplo com contêiner, o select ultrapassa o contêiner em 20 pixels. Esta pequena verificação em JavaScript deixa explícita essa relação entre as larguras:

O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.

const wrapperWidth = 200;
const selectWidth = 220;
if (selectWidth <= wrapperWidth) {
  process.exitCode = 1;
}

A verificação é concluída com sucesso quando o select é mais largo que o contêiner; caso contrário, ela define um código de saída de falha.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Select with clipped arrow</title>
  <style>
    .select-wrap {
      position: relative;
      box-sizing: border-box;
      width: 200px;
      height: 42px;
      overflow: hidden;
      border: 1px solid #777;
      border-radius: 4px;
      background: #fff;
    }

    .select-wrap select {
      box-sizing: border-box;
      width: 220px;
      height: 40px;
      padding: 0 10px;
      border: 0;
      color: #222;
      background: transparent;
      font: inherit;
    }

    .select-wrap::after {
      position: absolute;
      top: 18px;
      right: 12px;
      width: 7px;
      height: 7px;
      border-right: 2px solid #333;
      border-bottom: 2px solid #333;
      content: "";
      pointer-events: none;
      transform: rotate(45deg);
    }

    .select-wrap:focus-within {
      outline: 3px solid #1769aa;
      outline-offset: 2px;
    }
  </style>
</head>
<body>
  <label for="vehicle">Choose a vehicle</label>
  <div class="select-wrap">
    <select id="vehicle" name="vehicle">
      <option>RE Himalayan</option>
      <option selected>CRF Rally</option>
      <option>GS 310</option>
      <option>KTM ADV</option>
    </select>
  </div>
</body>
</html>

Escolha uma abordagem e verifique seus limites

Use appearance: none quando quiser que o próprio select determine sua largura e borda e os navegadores de destino oferecerem suporte à estilização necessária. Use um contêiner que recorta a seta quando precisar posicioná-la no próprio contêiner ou quando a estilização direta da aparência não for adequada. Em ambos os casos, teste a página nos navegadores e dispositivos compatíveis com seu projeto: a renderização de elementos select nativos e suas listas abertas não é totalmente uniforme.

Confira se o valor selecionado aparece por inteiro, se a seta personalizada está alinhada e se o foco pelo teclado é fácil de identificar. Na versão com contêiner, compare as larguras do contêiner e do select se a borda ou o texto parecerem cortados. Não remova o indicador de foco apenas para deixar o controle visualmente mais limpo; se personalizá-lo, substitua-o por um estilo igualmente claro. Essas técnicas alteram a apresentação, mas deixam o select nativo responsável pela seleção e pelo comportamento do formulário.