Tradicionalmente, HTML incorpora una etiqueta HTML <select>
que permite crear sencillas listas desplegables para nuestras páginas. Sin embargo, históricamente siempre han sido muy limitadas en cuanto a personalización:
- 1️⃣ Sólo permiten cambiar estilos concretos y muchos dependen del sistema operativo.
- 2️⃣ No permiten incluir nada más que texto en las opciones.
- 3️⃣ El estilizado de opciones es muy limitado.
Recientemente, se está trabajando en permitir una personalización más profunda en estas etiquetas <select>
y conseguir alcanzar posibilidades que antes eran muy complicadas o había que implementar desde cero.
Personalizar la etiqueta <select>
Al momento de escribir este post, sólo Canary Chrome 135+ soporta esta característica.
Por defecto, las capacidades modernas de personalización en la etiqueta HTML <select>
están desactivadas, por lo que necesitaremos escribir lo siguiente en nuestro CSS para habilitarlas:
select,
::picker(select) {
appearance: base-select;
}
Esto significa que las etiquetas <select>
y el «picker» del <select>
(ventana para escoger opciones) se podrán personalizar desde un estilo básico y permitirá personalizaciones profundas, que por defecto están desactivadas.
El esquema general de un <select>
moderno y personalizado, sería el siguiente:
Veamoslo en un ejemplo en acción, comparándolo con el modelo clásico:
<div>
Clásico:
<select>
<option>CSS</option>
<option>Sass</option>
<option>PostCSS</option>
<option>Lightning</option>
<option>PandaCSS</option>
<option>TailwindCSS</option>
</select>
</div>
<div>
Moderno:
<select class="modern">
<button>
Elegiste: <selectedcontent></selectedcontent>
</button>
<option>CSS</option>
<option>Sass</option>
<option>PostCSS</option>
<option>Lightning</option>
<option>PandaCSS</option>
<option>TailwindCSS</option>
</select>
</div>
body {
display: flex;
gap: 2rem;
}
select.modern,
select.modern::picker(select) {
appearance: base-select;
}
select {
font-family: "Victor Mono";
font-size: 1rem;
background: #222;
color: #eee;
border-radius: 5px;
width: 275px;
& option {
background: #222;
color: #eee;
}
}
Ahora veamos poco a poco sus características por separado.
La opción elegida
Observa que en esta versión moderna de <select>
, al principio, hemos añadido un elemento <button>
. Este botón no es obligatorio añadirlo, pero si se añade, será la parte visible de nuestra lista desplegable, de modo que podemos personalizarlo, añadir contenido (de texto, por ejemplo) y utilizar la etiqueta HTML especial <selectedcontent>
, que el navegador reemplazará por la opción escogida por el usuario.
<select>
<button>
Elegiste: <selectedcontent></selectedcontent>
</button>
<!-- opciones -->
</select>
Obviamente, si no queremos contenido de texto adicional, simplemente lo omitiremos. El elemento <selectedcontent>
sólo funciona en el interior del elemento <button>
, dentro de un <select>
. No puede tener contenido, ya que el navegador clona en su interior la opción elegida cuando el usuario selecciona una.
Incluir imágenes en el <select>
Una de las grandes limitaciones clásicas de los elementos <select>
era la imposibilidad de añadir imágenes en las opciones. Con estas nuevas capacidades de personalización, podemos añadir elementos HTML como imágenes <img>
o <svg>
(por ejemplo) en las opciones de la lista:
<select>
<button>
Elegiste: <selectedcontent></selectedcontent>
</button>
<option><svg><use href="/assets/icons/logos.svg#css" /></svg> CSS</option>
<option><svg><use href="/assets/icons/logos.svg#sass" /></svg> Sass</option>
<option><svg><use href="/assets/icons/logos.svg#postcss" /></svg> PostCSS</option>
<option><svg><use href="/assets/icons/logos.svg#lightningcss" /></svg> Lightning</option>
<option><svg><use href="/assets/icons/logos.svg#panda-css" /></svg> PandaCSS</option>
<option><svg><use href="/assets/icons/logos.svg#tailwind" /></svg> TailwindCSS</option>
</select>
body {
min-height: 300px;
}
select, ::picker(select) {
appearance: base-select;
}
select {
font-family: "Victor Mono";
font-size: 1rem;
background: #222;
color: #eee;
border-radius: 5px;
width: 275px;
& button {
display: flex;
align-items: center;
}
& svg {
width: 32px;
aspect-ratio: 1;
}
& selectedcontent {
width: 100%;
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem;
}
& option {
padding: 5px 10px;
background: #222;
color: #fff;
&:hover {
background: indigo;
}
}
&::picker-icon {
display: flex;
align-items: center;
}
}
Con algo más de HTML y CSS, podemos incluso crear diseños un poco menos convencionales y conseguir una alta personalización. Por ejemplo, fijate en el siguiente código, donde hemos creado un <div>
con clase .container
para convertirlo en un grid de 4 celdas:
<select>
<button>
Elegiste: <selectedcontent></selectedcontent>
</button>
<div class="container">
<div>
<option><svg><use href="/assets/icons/logos.svg#css" /></svg> CSS</option>
<option><svg><use href="/assets/icons/logos.svg#sass" /></svg> Sass</option>
</div>
<div>
<option><svg><use href="/assets/icons/logos.svg#postcss" /></svg> PostCSS</option>
<option><svg><use href="/assets/icons/logos.svg#lightningcss" /></svg> Lightning</option>
</div>
<div>
<option><svg><use href="/assets/icons/logos.svg#panda-css" /></svg> PandaCSS</option>
<option><svg><use href="/assets/icons/logos.svg#tailwind" /></svg> TailwindCSS</option>
</div>
</div>
</select>
body {
min-height: 300px;
}
select, ::picker(select) {
appearance: base-select;
margin: 0.25rem 0;
width: 375px;
}
select {
font-family: "Victor Mono";
font-size: 1rem;
background: #222;
color: #eee;
border-radius: 0;
border: 0;
& .container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
background: #222;
}
& button {
display: flex;
align-items: center;
}
& svg {
width: 32px;
aspect-ratio: 1;
}
& selectedcontent {
width: 100%;
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem;
}
& option {
padding: 5px 10px;
color: #fff;
&:hover {
background: indigo;
}
}
&::picker-icon {
display: flex;
align-items: center;
}
}
Obviamente, corre de nuestra cuenta personalizar al máximo el diseño de la lista desplegable.
El elemento seleccionado
Habíamos comentado que con la etiqueta <button>
junto a la etiqueta <selectedcontent>
podemos personalizar el botón donde pulsamos para desplegar la lista, mientras que con el pseudoelemento ::picker(select)
podemos personalizar la ventana de opciones que aparece al pulsar en la lista desplegable.
Pero nos quedan algunos consejos más de personalización:
Elemento | Descripción |
---|---|
::picker(select) | Ventana desplegable al pulsar sobre un <select> personalizado. |
option:hover | Aplica estilos cuando se mueve el ratón por encima de una opción. |
::checkmark | Aplica estilos al check del elemento seleccionado. Con content se puede reemplazar. |
::picker-icon | Aplica estilos a la flecha del botón desplegable. |
Mientras que con el pseudoelemento ::picker-icon
podemos personalizar la flecha que aparece a la derecha del <select>
para desplegar la lista, con el pseudoelemento ::checkmark
podemos modificar el signo de verificado que aparece por defecto en los elementos seleccionados.
<select>
<button>
<selectedcontent></selectedcontent>
</button>
<option selected disabled>—Elige una opción—</option>
<option><svg><use href="/assets/icons/logos.svg#css" /></svg> CSS</option>
<option><svg><use href="/assets/icons/logos.svg#sass" /></svg> Sass</option>
<option><svg><use href="/assets/icons/logos.svg#postcss" /></svg> PostCSS</option>
<option><svg><use href="/assets/icons/logos.svg#lightningcss" /></svg> Lightning</option>
<option><svg><use href="/assets/icons/logos.svg#panda-css" /></svg> PandaCSS</option>
<option><svg><use href="/assets/icons/logos.svg#tailwind" /></svg> TailwindCSS</option>
</select>
body {
min-height: 375px;
}
select, ::picker(select) {
appearance: base-select;
}
select {
font-family: "Victor Mono";
font-size: 1rem;
background: #222;
color: #eee;
border-radius: 5px;
width: 275px;
& button {
display: flex;
align-items: center;
}
& svg {
width: 32px;
aspect-ratio: 1;
}
& selectedcontent {
width: 100%;
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem;
}
& option {
padding: 5px 10px;
background: #222;
color: #fff;
&:hover {
background: indigo;
}
}
& option:checked {
background: #000;
}
& ::checkmark {
content: "->";
color: lime;
font-weight: bold;
}
&::picker-icon {
display: flex;
justify-content: center;
align-items: center;
border-left: 1px solid #666;
padding-left: 0.4rem;
}
}
Las pseudoclases :open
y :closed
Por último, recuerda que puedes utilizar las pseudoclases :open
y :closed
para indicar cuando la lista está desplegada (open) o no (closed).
Veamos un ejemplo donde vamos a añadir una transición con un color de fondo en el botón del desplegable:
<select>
<button>
<selectedcontent></selectedcontent>
</button>
<option selected disabled>—Elige una opción—</option>
<option><svg><use href="/assets/icons/logos.svg#css" /></svg> CSS</option>
<option><svg><use href="/assets/icons/logos.svg#sass" /></svg> Sass</option>
<option><svg><use href="/assets/icons/logos.svg#postcss" /></svg> PostCSS</option>
<option><svg><use href="/assets/icons/logos.svg#lightningcss" /></svg> Lightning</option>
<option><svg><use href="/assets/icons/logos.svg#panda-css" /></svg> PandaCSS</option>
<option><svg><use href="/assets/icons/logos.svg#tailwind" /></svg> TailwindCSS</option>
</select>
body {
min-height: 375px;
}
select, ::picker(select) {
appearance: base-select;
}
select {
min-width: 375px;
transition: background 1s ease;
background: #222;
color: #fff;
& selectedcontent {
display: flex;
align-items: center;
gap: 1rem;
}
&::picker-icon {
display: flex;
align-items: center;
}
& button {
display: flex;
align-items: center;
}
& svg {
width: 32px;
aspect-ratio: 1;
}
&:open {
background: indigo;
}
}