Aplicar estilos CSS a campos radio y checkbox

Un problema típico de los formularios es dar estilos personalizados a los elementos input de tipo radio y checkbox. Aunque se les pueden aplicar algunas propiedades de CSS como widthheight o box-shadow, lo cierto es que poco más se puede hacer con estos elementos.

Pero ojo, en este artículo no vamos a ver como dar estilos a los propios elementos input

Los estilos personalizados se los vamos a dar a los pseudo-elementos :after y :before del label asociado a cada input, dependiendo de si este tiene la propiedad checked (si está checkeado) o no.

¿Y por que no damos estilos directamente a los pseudo-elementos de los input…?
Sería una opción más limpia, pero algunos navegadores (Firefox & Edge) no reconocen los pseudo-elementos :after y :before en los input, así que, tenemos que usar los pseudo-elementos de los label asociados al input , de esta manera aprovecharemos la “zona de selección” del label como podremos ver más adelante.

Ver Demo

HTML

La estructura HTML es muy importante, ya que después con CSS podremos dar estilos a los elementos dependiendo de como estén colocados.

Si ponemos el input y a continuación el label podemos crear un estilo del tipo input:checked+label que solo se aplicará cuando el input esté checkeado.

<input type="checkbox" id="check1" ><label for="check1">Check me!</label>

Con los input de tipo radio igual, primero ponemos el input y después el label :

<input type="radio" id="radio1"><label for="radio1">Radio 1</label>
<input type="radio" id="radio2"><label for="radio2">Radio 2</label>

Manteniendo esa estructura podemos hacer lo que queramos: flotar elementos, rodearlos con un contenedor, meterlos en una lista, ocultarlos…

CSS

Teniendo en cuenta la anterior estructura HTML para los input y sus label podemos crear los siguientes estilos CSS:

input[type="checkbox"] + label {
  color: red;
}

Con esta regla de CSS decimos que cualquier label que esté inmediatamente detrás de un input de tipo checkbox tendrá el color del texto rojo.

Pseudo-clase :checked

Y con el selector de la pseudo-clase :checked podemos modificar los estilos del label cuando el input está checkeado. Solo hay que añadir el selector :checked al input para, por ejemplo, cambiar el color del texto del label :

input[type="checkbox"]:checked + label {
  color: blue;
}

Con esta regla de CSS decimos que cualquier label que esté inmediatamente detrás de un input de tipo checkbox que esté checkeado tendrá el color del texto azul.

En este punto podemos dar estilos a los label de los checkbox checkeados, a sus pseudo-elementos o a cualquier otro elemento que queramos poner dentro o a continuación de los label como pueden ser span , img , etc… mientras se mantengan detrás del input checkeado.

Hay que tener en cuenta que la idea de usar el label es por su propiedad de “zona de selección”  para el input. Al asociar los label a sus input mediante el atributo for hacemos que cualquier elemento dentro del label sea clickable y haga que se marque o se desmarque el checkbox o el radio input asociado a ese label.

Pseudo-elementos :before y :after

Con los selectores de los pseudo-elementos :before y :after del label asociado a cada input podemos crear nuestro checkbox personalizado. Olvidamos los estilos anteriores y empezamos de cero.

:before

Con el :before creamos el recuadro del checkbox, primero para cuando no esta checkeado y después añadimos un color de borde diferente para cuando si lo está:

input[type="checkbox"] + label:before {
	content: "";
	width: 26px;
	height: 26px;
	float: left;
	margin: 0.5em 0.5em 0 0
	border: 2px solid #ccc;
	background: #fff;
}
input[type="checkbox"]:checked + label:before {
	border-color: #0fbf12;
}

:after

Con el :after creamos la marca del checkbox, pero solo para cuando esté checkeado, lo haremos, como ya hemos visto, con el selector :checked :

input[type="checkbox"]:checked + label:after {
    content: "";
    width: 12px;
    height: 6px;
    border: 4px solid #0fbf12;
    float: left;
    margin-left: -1.95em;
    border-right: 0;
    border-top: 0;
    margin-top: 1em;
    transform: rotate(-55deg);
}

Ajustando la propiedad margin-left podemos tapar el input, pero también podemos ocultarlo y no influirá a la hora de checkearlo.

input[type="checkbox"] {
	display: none;
}

Solo queda ajustar el texto para que cuadre con el checkbox , modificar el cursor con la propiedad pointer y ya que estamos pues creamos dos estilos, sin checkear (gris) y checkeado (verde):

input[type="checkbox"] + label {
    font-weight: bold;
    line-height: 3em;
    color: #ccc;
    cursor: pointer;
}
input[type="checkbox"]:checked + label {
    color: #0fbf12;
}

Para los input de tipo radio sería exactamente igual, lo único que hay que tener en cuenta es que suelen ser redondos…
Esto se soluciona fácilmente añadiendo la propiedad border-radius al pseudoelemento :before :

input[type="radio"] + label:before {
    content: "";
    width: 26px;
    height: 26px;
    float: left;
    margin: 0.5em 0.5em 0 0
    border: 2px solid #ccc;
    background: #fff;
    border-radius: 100%;
}

Y la marca de checkeado también debería ser redonda, por lo que el pseudoelemento :after quedaría así:

input[type="radio"]:checked + label:after {
    content: "";
    width: 0;
    height: 0;
    border: 8px solid #0fbf12;
    float: left;
    margin-left: -1.8em;
    margin-top: 0.8em;
    border-radius: 100%;
}

Obviamente podemos añadir estilos para los estados :hover y :disabled aunque solo recomiendo el último ya que el :hover puede resultar confuso. Al checkearlo o des-checkearlo (como el puntero está sobre él) se activa el :hover y el usuario ya no sabe si está checkeado o no.

Demo

Dejo por aquí una demo donde se incluyen estilos para los input de tipo radio y checkbox. También hay estilos para el estado :disabled :

En Codepen podéis ver la demo a tamaño completo.