Los checkboxes son un tipo de control muy útil. Se trata de esos pequeños cuadraditos que nos permiten marcar opciones para decidir si las activamos o las desactivamos, y que llevan entre nosotros décadas sin apenas cambios:

Un control input de tipo checkbox

Su mayor problema es que son pequeños y no son especialmente bonitos. Y para acabar de denigrarlos, cuando Apple lanzó el iPhone hace ya más de una década cambió el aspecto de este tipo de selectores para que, en vez de ser una simple caja, se comportasen como una clavija, deslizándose de un lado a otro:

Control switch de iOS

Ahora la gente está acostumbrada a este y otros aspectos alternativos y es muy difícil volver atrás.

El caso es que en HTML no están disponibles controles como este switch de iOS, y nos tenemos que conformar con el humilde pero efectivo checkbox. Es también una pena que no exista una manera estandarizada de acceder al aspecto de estos controles para poder cambiarlos como queramos. Así que si queremos darles un aspecto diferente y más moderno tenemos que recurrir a la imaginación y una buena dosis de paciencia.

En este artículo te voy a explicar paso a paso y con detalle cómo conseguir el aspecto que desees. Nosotros haremos este:

Aspecto del checkbox personalizado que vamos a crear

pero podría ser cualquier otro aspecto alternativo. Todo depende de tu imaginación porque los pasos explicados valen para cualquier diseño.

Personalmente me gusta que se muestren el Sí y el No y porque muchos de estos diseños personalizados dejan mucho que desear en cuanto a usabilidad (muchas veces a propósito, como en muchos diálogos de permisos para cookies que podemos encontrar por ahí), y de este modo siempre se ve claramente si están activados o desactivados.

¿Cómo podemos cambiar el aspecto de un checkbox usando tan solo CSS?

Pues como digo, con un poco de habilidad y paciencia. Y conocer muy bien cómo funciona CSS.

El primer paso es ocultar totalmente el control checkbox original. Dado que queremos cambiar su aspecto y CSS no nos proporciona forma de hacerlo directamente, tenemos que ocultarlo para que no se vea:

input[type=checkbox] {
	visibility: hidden;
}

Pero claro, si ocultamos el control ¿cómo podemos pulsarlo para cambiar su valor?

El truco está en sacarle partido a la etiqueta <label> de HTML que está pensada precisamente para eso. Las etiquetas <label> tienen un atributo llamado for que si lo establecemos con el identificador de un control de formulario (en este caso de nuestro checkbox) pulsar sobre ellas es lo mismo que si pulsásemos sobre el propio control. Así, si tenemos esto:

<label for="miCheck">¿Opción activada?:</label>
<input type="checkbox" id="miCheck"/>

Lo que conseguiremos es que al pulsar sobre el texto "¿Opción activada?" de la <label>, cambiaremos el valor del checkbox.

Vamos a aprovechar este hecho para hacer que la etiqueta <label> se comporte como activador/desactivador de la opción.

Para darle el aspecto a nuestro nuevo control vamos a jugar con dos elementos básicos:

  • La etiqueta <label>, que será la que nos permita cambiar el estado del control subyacente, que estará siempre oculto en realidad.
  • Un envoltorio de ambas etiquetas, <label y <input type="checkbox">, que nos permitirá darle un aspecto a su fondo y a su recuadro, de modo que nos quede como necesitemos. Este envoltorio será una etiqueta <div> o similar que encierre a ambos controles. Necesitamos hacerlo así para las partes avanzadas, en concreto los textos a mostrar.

Es decir, en nuestro flamante control personalizado estos dos elementos son los que se observan en esta figura:

Esquema de nuestro control

El código HTML para nuestro "control" checkbox personalizado sería el siguiente:

<div class="checkbox-JASoft">
    <input type="checkbox" id="checkAvanzado" value="Valor"/>
    <label for="checkAvanzado">TEXTO QUE NO DEBERÍA VERSE</label>
</div>

Como vemos, el <div> contiene al checkbox (que estará oculto) y al <label> que sirve para activarlo y desactivarlo, ya que está asociado con el atributo for del mismo.

El contenedor/envoltorio, nos dará el fondo

Lo primero va a ser establecer el <div> contenedor para conseguir ese fondo gris oscuro con bordes redondeados que apreciamos en la figura anterior. Este sería el selector + propiedades CSS que vamos a utilizar:

.checkbox-JASoft {
	display: inline-block;
	position: relative;
    width: 70px;
	height: 30px;
	background: #555;
	border-radius: 15px;
	box-shadow: inset 0px 1px 1px rgba(0,0,0,0.6), 0px 1px 0px rgba(255,255,255,0.3);   
}

Como ves, establecemos su modo de visualización como inline-block para que se muestre en la misma línea que el texto que lo precede, que será su encabezado (y no el <label> que sería lo normal). Por otro lado le establecemos un modo de posicionamiento relativo simplemente para crear un marco de referencia en el que luego podamos posicionar a sus elementos "hijo", en este caso, como veremos enseguida, la etiqueta asociada al control. Por lo demás hay propiedades CSS muy simples para darle un tamaño, los bordes redondeados y una pequeña sombra que le de un aspecto más realista al "control" que estamos creando.

El label nos dará el control de deslizamiento

Bien. Lo siguiente que necesitamos es conseguir que la etiqueta <label> tome la forma de la figura, es decir, un pequeño botón rectangular de bordes redondeados, que será el que pulsemos para activar o desactivar el checkbox. Este sería el CSS para conseguirlo:

.checkbox-JASoft label {

    /* aspecto */
    display: block;
    width: 34px;
	height: 20px;
	border-radius: 17px;
	box-shadow: 0px 2px 5px 0px rgba(0,0,0,0.35);
	background: #fcfff4;
	background: linear-gradient(to top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%);
    cursor: pointer;
    
    /* Posicionamiento */
    position: absolute;
    top: 5px;
	left: 5px;
    z-index: 1;
    
	/* Comportamiento */
    transition: all .4s ease;
    
    /* ocultar el posible texto que tenga */
    overflow: hidden;
    text-indent: 35px;  
    transition: text-indent 0s;
}

Con estas propiedades perseguimos varios objetivos. En primer lugar darle el aspecto mencionado, para lo cual usamos las propiedades de ancho y alto, bordes, sombreado, el aspecto del cursor al pasarle por encima, y el color de fondo, que en este caso incluye también un degradado lineal de arriba a abajo, bastante sutil para darle un aspecto menos plano (pero para gustos...). Con esto toma el aspecto de "pastilla" con los bordes ovalados que se ve en la imagen de antes.

También necesitamos posicionarlo. Para ello lo colocamos de manera absoluta dentro del <div> (que como recordarás tenía un posicionamiento relativo precisamente para poder hacer esto). Como nuestro control checkbox por defecto no está seleccionado, vamos a hacer que en ese estado este "label" esté colocado en el lado izquierdo, así que lo posicionamos a 5 píxeles de la esquina superior izquierda con left y top. Evidentemente, si el tamaño que le diésemos al elemento de fondo fuese otro recalcularíamos esta posición, pero en nuestro caso, como el "padre" mide 30px de alto y a esta etiqueta le hemos puesto 20px de alto, dejando 5px desde arriba hace que quede perfectamente centrado, y los 5px desde la izquierda lo dejan encajadito en el borde redondeado de su contenedor.

También establezco explícitamente el índice en Z por un motivo que veremos en un momento.

Le establezco una transición para sus propiedades de forma que cambien paulatinamente (en algo menos de medio segundo) y no de golpe. Enseguida veremos por qué.

Finalmente, como puede que el <label> contenga algo de texto y en este caso no podemos permitir que se vea, lo quitamos de la vista indentándolo más allá del ancho (le podríamos poner text-indent:9999px o algo así "exagerado" para no tener que preocuparnos de esta medida si cambiásemos su ancho), lo que se salga de la parte visible no se verá (overflow:hidden) y para que no se vea la animación de este movimiento por culpa del transition anterior, le ponemos una transición inmediata al indentado del texto. Así logramos que aunque haya texto (de hecho le he dejado uno a propósito) que nunca llegue a verse.

De acuerdo, Con esto ya tenemos el botón que activa o desactiva el control checkbox subyacente (porque, recuerda, es un <label> conectado con éste y por tanto pulsándolo lo maneja) colocado en su posición "Off", o sea, no activado.

¿Cómo lo colocamos/definimos en su posición "On", o sea, activado?

Cuando está desactivado y pulsamos sobre la etiqueta, el checkbox cambia de estado aunque no lo veamos, y "se marca". Para detectar esta situación, CSS nos proporciona la pseudo-clase :checked, que toma el control checkbox automáticamente cuando está activado. Como además la <label> está situada justo a continuación del checkbox en el código de nuestro "control", podemos usar este selector + propiedades para cambiar lo que necesitemos, en este caso su posición:

.checkbox-JASoft input[type=checkbox]:checked + label {
	left: auto;
    right: 5px;
}

Fíjate en que lo que hace este selector es seleccionar las etiquetas (<label>) que estén situadas justo a continuación (+) de cualquier <input> de tipo checkbox (input[type=checkbox]) que esté seleccionado (:checked), el cual a su vez debe ser hijo (incluso podríamos poner directo con >) de un elemento con la clase de nuestro "control" aplicada (.checkbox-JASoft). Léelo con calma y verás que es sencillo. Bien, pues una vez seleccionadas lo único que hacemos es moverla al otro extremo del padre, anulando el valor de posición respecto a la izquierda y ahora alineándolo a la derecha, también 5px, claro. Al no cambiar ninguna otra propiedad, su aspecto y comportamiento quedan igual que en el restado desactivado de antes.

¡Estupendo! Ya tenemos el control funcionando y podemos pulsar sobre el elemento <label> para que se active y se desactive y que además se cambie de sitio:

El control personalizado con el aspecto actual, sin textos

Pero aún nos falta algo: los textos para indicar más claramente en qué estado se encuentra.

Cómo le añadimos los textos

Seguro que ya te lo imaginas: sacando partido a los pseudo-elementos :after y :before. En efecto. Es muy sencillo conseguir esos textos combinando éstos con un posicionamiento absoluto dentro de su "padre" y un poco de estilo para darle un aspecto agradable.

El código CSS sería el siguiente:

.checkbox-JASoft:after {
	content: 'NO';
	font: 12px/30px Arial, sans-serif;
	color: #AAA;
	position: absolute;
	right: 10px;
    z-index: 0;
	font-weight: bold;
	text-shadow: 1px 1px 0px rgba(255,255,255,.20);
}

.checkbox-JASoft:before {
	content: 'SÍ';
	font: 12px/30px Arial, sans-serif;
	color: lime;
	position: absolute;
	left: 10px;
	z-index: 0;
	font-weight: bold;
}

Como ves los añadimos dentro del elemento "padre" después y antes respectivamente de los demás elementos.

De este modo tenemos un elemento con el contenido NO con un tamaño de letra adecuado y un alto de línea igual a la altura del padre (30px), de color blanco, posicionado de manera absoluta par que quede un poco separado del borde derecho. También le damos una pequeña sombra para que se vea como "recortado" en el fondo. Hacemos lo mismo con el sólo que ahora lo colocamos en el otro lado, a la izquierda, y no le damos sombreado porque va a ir de color verde-lima que ya destaca lo suficiente.

Cuando el checkbox esté desactivado el <label> se queda pegado a la izquierda y tapa el texto , mientras que cuando está activado se mueve a la derecha y tapa al NO. Es por esto que antes le dimos a la etiqueta un z-index de 1, y a estos dos elementos le hemos dado un z-index de 0. Dado que forman parte del mismo contexto de apilamiento nos aseguramos de que la etiqueta siempre quede por encima de los textos tapando el que corresponda según el estado actual del checkbox.

¡Listo!

Tenemos un control mucho más atractivo y funcional que podemos personalizar fácilmente y que podemos reutilizar en donde lo necesitemos con tan solo replicar la estructura HTML del principio.

Es una pena que HTML/CSS no nos proporcionen una manera más directa y estándar para lograr lo mismo, pero lo cierto es que una vez que lo entiendes es muy sencillo de hacer.

Te dejo aquí (ZIP, 1,39KB) una descarga con el ejemplo para que veas el código completo y puedas jugar con ello.

Si quieres aprender bien y no con "recetas" a dominar HTML y CSS, no te olvides de mi curso sobre el tema, que es probablemente el más completo que vas a encontrar y me tendrás a mi para responder todas tus dudas.

¡Espero que te sea útil!

💪🏻 ¿Este post te ha ayudado?, ¿has aprendido algo nuevo?
Pues NO te pido que me invites a un café... Te pido algo más fácil y mucho mejor

Escrito por un humano, no por una IA