HOWTO · CSS

Menú desplegable de selección de estilo en CSS

Este tutorial presenta cómo aplicar estilo al menú desplegable en CSS.

En esta página

Puedes dar estilo a un menú desplegable HTML nativo select modificando su cuadro y sustituyendo la flecha predeterminada por otra dibujada con CSS. El control sigue siendo un select real, por lo que conserva su comportamiento de selección integrado y la interacción con el teclado. Dos enfoques habituales consisten en quitar directamente la apariencia nativa o recortar la flecha nativa con un contenedor.

Dar estilo a un menú desplegable nativo select sin perder su comportamiento

Un select contiene elementos option, y el navegador representa el control según las convenciones de su plataforma y su navegador. CSS puede modificar el cuadro visible, pero la lista emergente y algunos detalles siguen variando entre navegadores y sistemas operativos. Si quieres conservar el comportamiento integrado de los formularios y del teclado, mantén el elemento nativo en lugar de recrear el menú desplegable con elementos genéricos.

Los ejemplos usan las mismas cuatro opciones. Las etiquetas de las opciones en los bloques de código se mantienen en inglés porque esos bloques no se modifican. El CSS deja espacio a la derecha del control para la flecha y muestra un contorno de foco visible para quienes usan el teclado. Ajusta los colores y las dimensiones para que coincidan con tu interfaz.

Ocultar la flecha nativa con appearance: none y añadir una flecha personalizada

Establece appearance: none en el select para quitar la flecha nativa del navegador cuando sea compatible y, a continuación, dibuja un chevrón con dos degradados CSS. Las capas de background colocan los dos trazos cerca del borde derecho; el relleno derecho mantiene el texto de las opciones alejado de ellos. Las declaraciones -webkit-appearance y -moz-appearance son variantes con prefijo de proveedor para los motores que usan esos prefijos; no corresponden a familias de navegadores WebKit y Blink distintas. La propiedad estándar es la declaración principal, así que conserva los prefijos solo si los requisitos de compatibilidad de tus navegadores lo justifican.

Esta página completa es un ejemplo estático para el navegador. Guárdala como archivo HTML y ábrela en un navegador para ver el control; el paquete no aporta pruebas de representación en navegador que respalden un resultado visual afirmado.

El código y las etiquetas o salidas visibles de esta sección se mantienen en 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>

Ocultar la flecha nativa con un contenedor recortado

Un contenedor ofrece otra forma de ocultar la flecha nativa. Asigna al select un ancho ligeramente mayor que el del contenedor y establece overflow en hidden para el contenedor; así, la flecha nativa queda fuera de la zona visible. Dibuja una flecha de reemplazo sobre el contenedor con un pseudoelemento. Como el select permanece dentro del contenedor, sus opciones y su interacción nativa siguen disponibles.

El contenedor debe tener suficiente ancho para el texto del control, y el select solo debe sobresalir un poco. Si la diferencia es demasiado grande, también podrían quedar recortados el borde derecho del control u otros contenidos. El pseudoelemento usa pointer-events: none para no interceptar los clics en el select.

En el ejemplo con contenedor, el select sobresale 20 píxeles. Esta pequeña comprobación de JavaScript hace explícita esa relación entre los anchos:

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

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

La comprobación termina correctamente cuando el select es más ancho que el contenedor; de lo contrario, establece un código de salida de error.

<!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>

Elige un enfoque y comprueba sus límites

Usa appearance: none cuando quieras que el propio select defina su ancho y su borde, y los navegadores de destino admitan los estilos que necesitas. Usa un contenedor recortado cuando necesites una flecha situada en el contenedor o cuando no resulte adecuado aplicar estilos de apariencia directamente. En ambos casos, prueba la página en los navegadores y dispositivos compatibles: la representación nativa del select y sus menús emergentes no es totalmente uniforme.

Comprueba que el valor seleccionado se vea completo, que la flecha personalizada esté alineada y que el foco del teclado sea fácil de distinguir. En la versión con contenedor, compara los anchos del contenedor y del select si el borde o el texto parecen cortados. No elimines el indicador de foco solo para que el control se vea más limpio; si lo personalizas, sustitúyelo por un estilo igual de claro. Estas técnicas cambian la presentación, pero dejan que el select nativo se encargue de la selección y del comportamiento del formulario.