CSS checkbox y radios personalizados
No hay diseñador web que acabe agotado de personalizar los formularios. Son complejos y llenos de sutilezas que debes conocer. Y por si no fuera poco hay lugares imposibles de tocar como los input radio y los input checkbox... ¡Hasta ahora!
En el siguiente tutorial voy a enseñar como realizar un checkbox y un radio personalizado para un formulario en HTML. Usando la metodología BEM para organizar los estilos en un formato de componente fácil de construir o modificiar. Todo ello sin preocuparnos de las limitaciones propias del CSS logrando adaptar nuestros diseños, y sin sacrificar la accesibilidad: el input seguirá siendo utilizable con el teclado y visible para los lectores de pantalla. Además incluiré un resumen en Sass para que sea fácil de copiar. Compatible con todos los navegadores más utilizados: Chrome, Firefox, Edge, Safari...
DEMO
Pulsa en cada elemento para ver su funcionamiento.
Radio
Checkbox
Ocultar no es destruir
Toda la técnica se apoya en esconder el input real y pintar un sustituto. La tentación clásica es display: none, y es una trampa: elimina el input del orden de tabulación y del árbol de accesibilidad. El resultado es que nadie puede marcarlo con Tab y Espacio, y un lector de pantalla ni siquiera sabe que existe. Si el checkbox es el típico "Acepto los términos y condiciones", habrás bloqueado el envío del formulario a cualquier persona que no use ratón.
La alternativa es ocultarlo solo visualmente: reducirlo a un píxel recortado. El input sigue existiendo, sigue recibiendo foco y Espacio lo marca; el label que lo envuelve se encarga de los clics de ratón.
.custom-radio-checkbox > .custom-radio-checkbox__input {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
Solo falta un detalle: al navegar con teclado hay que ver dónde está el foco. Como el input es invisible, dibujamos el anillo sobre su sustituto.
.custom-radio-checkbox > .custom-radio-checkbox__input:focus-visible + .custom-radio-checkbox__show {
outline: 2px solid #000;
outline-offset: 2px;
}
Prueba la demo de arriba con el tabulador: puedes recorrer los controles, cambiar de radio con las flechas y marcar el checkbox con Espacio.
Radio
Estructuramos nuestro radio de forma natural. Pero debemos incluir un span que será el lugar donde mostraremos la imagen con el sustituto al radio.
<p>
<label class="custom-radio-checkbox">
<!-- Input oculto -->
<input class="custom-radio-checkbox__input" type="radio" name="genero" value="mujer" checked>
<!-- Imagen en sustitucion -->
<span class="custom-radio-checkbox__show custom-radio-checkbox__show--radio"></span>
<!-- Texto -->
<span class="custom-radio-checkbox__text">Mujer</span>
</label>
</p>
<p>
<label class="custom-radio-checkbox">
<!-- Input oculto -->
<input class="custom-radio-checkbox__input" type="radio" name="genero" value="hombre">
<!-- Imagen en sustitucion -->
<span class="custom-radio-checkbox__show custom-radio-checkbox__show--radio"></span>
<!-- Texto -->
<span class="custom-radio-checkbox__text">Hombre</span>
</label>
</p>
Ocultamos los radios con el truco accesible que acabamos de ver y en su lugar dejamos en los span una imagen de fondo que previamente hemos preparado.
/* Centra el input personalizado con el texto */
.custom-radio-checkbox {
display: inline-flex;
align-items: center;
cursor: pointer;
font-family: arial;
}
/* Modificador para dar la imagen de radio */
.custom-radio-checkbox__show--radio {
background-image: url(radio-uncheck.png);
}
/* Oculta el input original sin sacarlo del teclado ni del lector de pantalla */
.custom-radio-checkbox > .custom-radio-checkbox__input {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* Radio personalizado usando <span> */
.custom-radio-checkbox > .custom-radio-checkbox__show {
display: inline-block;
width: 15px;
height: 15px;
margin-right: .5rem;
background-size: cover;
}
/* Anillo de foco sobre el sustituto al navegar con teclado */
.custom-radio-checkbox > .custom-radio-checkbox__input:focus-visible + .custom-radio-checkbox__show {
outline: 2px solid #000;
outline-offset: 2px;
}
/* Cambia el radio personalizado cuando es pulsado */
.custom-radio-checkbox > .custom-radio-checkbox__input:checked + .custom-radio-checkbox__show--radio {
background-image: url(radio-check.png);
}
La imagen sin pulsar sería la siguiente.
:quality(85)/https://andros.dev/media/blog/2018/06/radio-uncheck.avif)
Y pulsado sería así.
:quality(85)/https://andros.dev/media/blog/2018/06/radio-check.avif)
Checkbox
Se trabaja de la misma forma. Primero declaramos nuestro input dentro de un label, sin olvidar un span vacío que será el futuro espacio para la imagen.
<label class="custom-radio-checkbox">
<!-- Input oculto -->
<input class="custom-radio-checkbox__input" type="checkbox" name="genero" value="hombre">
<!-- Imagen en sustitucion -->
<span class="custom-radio-checkbox__show custom-radio-checkbox__show--checkbox"></span>
<!-- Texto -->
<span class="custom-radio-checkbox__text">Acepto las condiciones de uso y privacidad</span>
</label>
Mostramos una imagen de fondo dentro del span y ocultamos el checkbox original, de nuevo sin display: none para que Espacio siga funcionando.
/* Centra el input personalizado con el texto */
.custom-radio-checkbox {
display: inline-flex;
align-items: center;
cursor: pointer;
font-family: arial;
}
/* Modificador para dar la imagen de checkbox */
.custom-radio-checkbox__show--checkbox {
background-image: url(checkbox-uncheck.png);
}
/* Oculta el input original sin sacarlo del teclado ni del lector de pantalla */
.custom-radio-checkbox > .custom-radio-checkbox__input {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* Radio personalizado usando <span> */
.custom-radio-checkbox > .custom-radio-checkbox__show {
display: inline-block;
width: 15px;
height: 15px;
margin-right: .5rem;
background-size: cover;
}
/* Anillo de foco sobre el sustituto al navegar con teclado */
.custom-radio-checkbox > .custom-radio-checkbox__input:focus-visible + .custom-radio-checkbox__show {
outline: 2px solid #000;
outline-offset: 2px;
}
/* Cambia el checkbox personalizado cuando es pulsado */
.custom-radio-checkbox > .custom-radio-checkbox__input:checked + .custom-radio-checkbox__show--checkbox {
background-image: url(checkbox-check.png);
}
La imagen sin pulsar sería la siguiente.
:quality(85)/https://andros.dev/media/blog/2018/06/checkbox-uncheck.avif)
Y pulsado sería así.
:quality(85)/https://andros.dev/media/blog/2018/06/checkbox-check.avif)
Todo cohesionado en un componente
HTML
<!-- Radios -->
<p>
<label class="custom-radio-checkbox">
<!-- Input oculto -->
<input class="custom-radio-checkbox__input" type="radio" name="genero" value="mujer" checked>
<!-- Imagen en sustitucion -->
<span class="custom-radio-checkbox__show custom-radio-checkbox__show--radio"></span>
<!-- Texto -->
<span class="custom-radio-checkbox__text">Mujer</span>
</label>
</p>
<p>
<label class="custom-radio-checkbox">
<!-- Input oculto -->
<input class="custom-radio-checkbox__input" type="radio" name="genero" value="hombre">
<!-- Imagen en sustitucion -->
<span class="custom-radio-checkbox__show custom-radio-checkbox__show--radio"></span>
<!-- Texto -->
<span class="custom-radio-checkbox__text">Hombre</span>
</label>
</p>
<!-- End Radios -->
<!-- Checkbox -->
<p>
<label class="custom-radio-checkbox">
<!-- Input oculto -->
<input class="custom-radio-checkbox__input" type="checkbox" name="genero" value="hombre">
<!-- Imagen en sustitucion -->
<span class="custom-radio-checkbox__show custom-radio-checkbox__show--checkbox"></span>
<!-- Texto -->
<span class="custom-radio-checkbox__text">Acepto las condiciones de uso y privacidad</span>
</label>
</p>
<!-- End Checkbox -->
CSS
/* Centra el input personalizado con el texto */
.custom-radio-checkbox {
display: inline-flex;
align-items: center;
cursor: pointer;
font-family: arial;
}
/* Modificador para dar la imagen de checkbox */
.custom-radio-checkbox__show--checkbox {
background-image: url(checkbox-uncheck.png);
}
/* Modificador para dar la imagen de radio */
.custom-radio-checkbox__show--radio {
background-image: url(radio-uncheck.png);
}
/* Oculta el input original sin sacarlo del teclado ni del lector de pantalla */
.custom-radio-checkbox > .custom-radio-checkbox__input {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* Radio personalizado usando <span> */
.custom-radio-checkbox > .custom-radio-checkbox__show {
display: inline-block;
width: 15px;
height: 15px;
margin-right: .5rem;
background-size: cover;
}
/* Anillo de foco sobre el sustituto al navegar con teclado */
.custom-radio-checkbox > .custom-radio-checkbox__input:focus-visible + .custom-radio-checkbox__show {
outline: 2px solid #000;
outline-offset: 2px;
}
/* Cambia el checkbox personalizado cuando es pulsado */
.custom-radio-checkbox > .custom-radio-checkbox__input:checked + .custom-radio-checkbox__show--checkbox {
background-image: url(checkbox-check.png);
}
/* Cambia el radio personalizado cuando es pulsado */
.custom-radio-checkbox > .custom-radio-checkbox__input:checked + .custom-radio-checkbox__show--radio {
background-image: url(radio-check.png);
}
Sass
.custom-radio-checkbox
display: inline-flex
align-items: center
cursor: pointer
font-family: arial
&__show--checkbox
background-image: url(checkbox-uncheck.png)
&__show--radio
background-image: url(radio-uncheck.png)
&__input
position: absolute
width: 1px
height: 1px
overflow: hidden
clip: rect(0 0 0 0)
white-space: nowrap
&__show
display: inline-block
width: 15px
height: 15px
margin-right: .5rem
background-size: cover
&__input:focus-visible + &__show
outline: 2px solid #000
outline-offset: 2px
&__input:checked + &__show--checkbox
background-image: url(checkbox-check.png)
&__input:checked + &__show--radio
background-image: url(radio-check.png)
This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.
Help me keep writing
Every coffee gives me a push toward the next article.
Sure, it's on me!
Comments
There are no comments yet.