Cómo crear un componente de acordeón con el truco Checkbox de CSS
Spanish (Español) translation by Carlos (you can also view the original English article)



En este breve tutorial, aprenderemos a crear un componente de acordeón flexible usando solo CSS y aprovechando «la técnica con el truco checkbox de CSS». Y quizá lo más importante, nuestro componente será completamente responsivo y su diseño cambiará entre horizontal y vertical según el tamaño de la ventana gráfica.
Durante el proceso, discutiremos cómo funciona el truco checkbox de CSS, y veremos esta técnica siendo utilizada por otros desarrolladores en CodePen para inspirarnos. ¿Suena interesante?
Nuestro acordeón responsivo con CSS
Esto es lo que crearemos durante este tutorial:
Nota: Este tutorial asume que tienes cierto conocimiento de flexbox. Si estás empezando, revisa este tutorial de flexbox para principiantes:
Espera, ¿cuál es el truco checkbox de CSS?
El truco checkbox de CSS te permite controlar ciertos estilos dependiendo si las casillas de verificación (o botones de radio) están marcadas o no. Usa el selector de pseudoclases :checked, que nos permite indicar «si una casilla de verificación (checkbox ) está marcada, aplicar estas reglas de estilo a su elemento hermano, etc.».
Los desarrolladores suelen ocultar la entrada en sí, controlando el valor marcado a mediante su etiqueta, de modo que los usuarios ni siquiera se enteran de que están activando una casilla de verificación.
Es una de mis favoritas; de hecho, he utilizado la misma técnica en varios tutoriales:


CSSConsejo rápido: Cómo crear un formulario de comentarios «fuera del lienzo» con CSS puroGeorge Martsoukos

Selectores CSSCómo crear un componente de filtrado en CSS puroGeorge Martsoukos
1. Empieza con el marcado en HTML
Para los fines de este ejercicio, tomaremos contenido de Wikipedia sobre: animales, plantas, espacio y ríos.
Luego, crearemos los botones de opción correspondientes que agruparemos bajo la palabra clave wiki:
1 |
<input type="radio" id="animal" name="wiki" value="Animal" checked> |
2 |
<input type="radio" id="plant" name="wiki" value="Plant"> |
3 |
<input type="radio" id="space" name="wiki" value="Space"> |
4 |
<input type="radio" id="river" name="wiki" value="River"> |
Crea una lista sin ordenar
A continuación, definiremos una lista no ordenada con cuatro elementos. Cada objeto de la lista representará un elemento del acordeón/panel y contendrá dos elementos:
- En primer lugar, una etiqueta que servirá como título del acordeón y se encargará de abrir el elemento objetivo. Su valor
fordeberá coincidir con el valoridde uno de los botones de radio mencionados. - En segundo lugar, un elemento
divque servirá como el área de contenido del acordeón.
De forma predeterminada un panel en nuestro acordeón debe estar abierto. Tomando eso en consideración, añadamos el atributo checked al primer botón de opción.
Teniendo todo junto, aquí está el marcado que necesitaremos:
1 |
<ul class="accordion"> |
2 |
<li>
|
3 |
<label for="animal" class="accordion-title"> |
4 |
<span>...</span> |
5 |
<span class="accordion-heading">...</span> |
6 |
</label>
|
7 |
<div class="accordion-content">...</div> |
8 |
</li>
|
9 |
<li>
|
10 |
<label for="plant" class="accordion-title"> |
11 |
<span>...</span> |
12 |
<span class="accordion-heading">...</span> |
13 |
</label>
|
14 |
<div class="accordion-content">...</div> |
15 |
</li>
|
16 |
<li>
|
17 |
<label for="space" class="accordion-title"> |
18 |
<span>...</span> |
19 |
<span class="accordion-heading">...</span> |
20 |
</label>
|
21 |
<div class="accordion-content">...</div> |
22 |
</li>
|
23 |
<li>
|
24 |
<label for="river" class="accordion-title"> |
25 |
<span>...</span> |
26 |
<span class="accordion-heading">...</span> |
27 |
</label>
|
28 |
<div class="accordion-content">...</div> |
29 |
</li>
|
30 |
</ul>
|
2. Define los estilos
Con el marcado listo (y en línea con el truco checkbox de CSS que describí anteriormente) primero ocultaremos visualmente los botones de opción moviéndolos fuera de la pantalla:
1 |
input[type="radio"] { |
2 |
position: absolute; |
3 |
left: -9999px; |
4 |
}
|
El acordeón tendrá un ancho máximo, una altura mínima y se comportará como un contenedor flexible:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.accordion { |
4 |
display: flex; |
5 |
width: calc(100% - 20px); |
6 |
max-width: 800px; |
7 |
min-height: 380px; |
8 |
margin: 0 auto; |
9 |
background: var(--accordion-color); |
10 |
color: var(--white); |
11 |
}
|
Además, cada elemento de la lista servirá como una envoltura flexible:
1 |
.accordion li { |
2 |
display: flex; |
3 |
}
|
Los elementos del acordeón deben estar separados, así que vamos a darles un borde:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.accordion li:not(:last-child) { |
4 |
border: 1px solid var(--separator-color); |
5 |
}
|
Cada título (etiqueta) dentro de un objeto será un contenedor flexible y sus elementos secundarios se distribuirán de forma vertical a través del eje principal. Además, todos los elementos tendrán un ancho de 70 px:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.accordion .accordion-title { |
4 |
display: flex; |
5 |
flex-direction: column; |
6 |
justify-content: space-between; |
7 |
width: 70px; |
8 |
font-size: 1.4rem; |
9 |
font-weight: bold; |
10 |
line-height: normal; |
11 |
padding: 20px 10px; |
12 |
background: var(--title-color); |
13 |
transition: color 0.1s; |
14 |
}
|
15 |
|
16 |
.accordion .accordion-title:hover { |
17 |
color: var(--active-color); |
18 |
}
|
El texto dentro de .accordion-heading se rotará verticalmente:
1 |
.accordion .accordion-heading { |
2 |
display: inline-block; |
3 |
white-space: nowrap; |
4 |
transform-origin: bottom; |
5 |
transform: rotate(-90deg) translate(50%, 50%); |
6 |
}
|
Inicialmente, aparte del primer panel, todos los demás paneles estarán ocultos:
1 |
.accordion .accordion-content { |
2 |
display: none; |
3 |
align-items: center; |
4 |
padding: 20px; |
5 |
}
|
3. El truco de checkbox: Alternar los paneles
Ahora para crear la magia. Cada vez que hagamos clic en una etiqueta, deberá aparecer su contenido asociado. Para hacer que esto suceda, aprovecharemos la pseudoclase :checked, el selector sibling subsecuente (~) y el combinador sibling adyacente (+). Si necesitas un repaso de lo que hacen estos selectores, revisa este tutorial:
Así que,cuando un elemento se vuelve visible, recibirá display: flex y no display: block. Esto se debe a que queremos centrar verticalmente su contenido aprovechando el valor de propiedad align-items: center. Asimismo, los colores del panel activo tienen que cambiar, de tal modo que un visitante pueda comprender claramente qué panel está abierto.
De forma opcional, cada vez que un botón de opción recibe focus, podemos añadir una descripción a su etiqueta asociada. Este pequeño detalle nos ayudará a mejorar la accesibilidad de nuestro componente.
Aquí están todo lo relacionado con CSS:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
[value="Animal"]:checked ~ .accordion [for="animal"] + .accordion-content, |
4 |
[value="Plant"]:checked ~ .accordion [for="plant"] + .accordion-content, |
5 |
[value="Space"]:checked ~ .accordion [for="space"] + .accordion-content, |
6 |
[value="River"]:checked ~ .accordion [for="river"] + .accordion-content { |
7 |
display: flex; |
8 |
}
|
9 |
|
10 |
[value="Animal"]:checked ~ .accordion [for="animal"], |
11 |
[value="Plant"]:checked ~ .accordion [for="plant"], |
12 |
[value="Space"]:checked ~ .accordion [for="space"], |
13 |
[value="River"]:checked ~ .accordion [for="river"] { |
14 |
color: var(--active-color); |
15 |
}
|
16 |
|
17 |
/*optional*/
|
18 |
[value="Animal"]:focus ~ .accordion [for="animal"], |
19 |
[value="Plant"]:focus ~ .accordion [for="plant"], |
20 |
[value="Space"]:focus ~ .accordion [for="space"], |
21 |
[value="River"]:focus ~ .accordion [for="river"] { |
22 |
outline: 1px solid var(--active-color); |
23 |
}
|
4. Volviéndolo responsivo
Como lo comentamos, en pantallas pequeñas los elementos dentro del acordeón deben apilarse, por lo que el acordeón tendrá un diseño vertical. Gracias a flexbox, podemos implementar este diseño sin tanto esfuerzo. De hecho, todo lo que tenemos que hacer es actualizar la dirección de las envolturas flexibles y restablecer el valor de la propiedad transform de .accordion-heading.
Veamos los estilos responsivos dentro de un «media query»:
1 |
@media screen and (max-width: 650px) { |
2 |
.accordion { |
3 |
min-height: 0; |
4 |
}
|
5 |
|
6 |
.accordion, |
7 |
.accordion li { |
8 |
flex-direction: column; |
9 |
}
|
10 |
|
11 |
.accordion .accordion-title { |
12 |
flex-direction: row; |
13 |
width: auto; |
14 |
}
|
15 |
|
16 |
.accordion .accordion-heading { |
17 |
transform: none; |
18 |
}
|
19 |
|
20 |
.accordion .accordion-title, |
21 |
.accordion .accordion-content { |
22 |
padding: 20px; |
23 |
}
|
24 |
}
|
5. Bono: contenido limitado
En nuestro caso, existe mucho contenido dentro de cada uno de los elementos del acordeón, por lo que todo se ve muy bien. Pero, asegurémonos de que el acordeón seguirá funcionando bien cuando no haya suficiente contenido dentro de los paneles (por ejemplo, un panel que contenga solo enlaces sociales).



Para satisfacer este escenario, tenemos que hacer dos cosas:
- Añadir
flex-grow: 1al elemento de la lista que contenga el panel activo. Para dirigirnos solo a ese elemento y no a todos, necesitaremos agregar un nuevo atributo personalizado (data-radio) a los elementos de la lista con el valor del valor de su etiqueta. - Agregar
flex-grow: 1a.accordion-content, para que se expanda y cubra todo el ancho principal. - Centrar horizontalmente el contenido de
.accordion-contentgracias ajustify-content: center
Teniendo en cuenta todo lo anterior, modificaremos nuestro HTML de la siguiente manera:
1 |
<ul class="accordion"> |
2 |
<li data-radio="animal">...</li> |
3 |
<li data-radio="plant">...</li> |
4 |
<li data-radio="space">...</li> |
5 |
<li data-radio="river">...</li> |
6 |
</ul>
|
Luego, en el CSS incluiremos estos estilos:
1 |
.accordion .accordion-content { |
2 |
justify-content: center; |
3 |
flex-grow: 1; |
4 |
}
|
5 |
|
6 |
[value="Animal"]:checked ~ .accordion [data-radio="animal"], |
7 |
[value="Plant"]:checked ~ .accordion [data-radio="plant"], |
8 |
[value="Space"]:checked ~ .accordion [data-radio="space"], |
9 |
[value="River"]:checked ~ .accordion [data-radio="river"] { |
10 |
flex-grow: 1; |
11 |
}
|
Conclusión
¡Eso es todo amigos! En este breve tutorial, logramos crear un acordeón usando solo CSS y aprovechando el «truco checkbox de CSS». Esperemos que hayas disfrutado de este ejercicio y que lo amplíes para tus propósitos específicos.
Aquí tienes un recordatorio de lo que hemos creado:
Proyecto extra
Para concluir, solo ten presente que con esta implementación únicamente se puede abrir un panel del acordeón a la vez. Eso es porque utilizamos botonesde radio en el marcado. En caso de que quieras mostrar varios elementos de acordeón al mismo tiempo, reemplaza los botones de opción con casillas de verificación y haz los cambios necesarios (oculta las casillas de verificación) en el CSS.
Y como siempre, ¡muchas gracias por leer!
Más inspiración con el truco de checkbox en CodePen
¿Qué mejor lugar para ver lo que otros han creado con el truco chekbox de CSS que CodePen? Aquí tienes algunos excelentes ejemplos para abrir el apetito:
Toggles responsivos con emojis por George W. Park
Contenido con pestañas con solo CSS por Stephen Greig
Modal con puro CSS - «El truco de checkbox» por BeardedBear
Navegación con el truco checkbox de CSS por JAD3
Lecturas adicionales
Revisa estos recursos para aprender más sobre técnicas CSS similares y otras maneras de crear componentes de acordeón (¿alguien dijo Bootstrap?):


Selectores CSSCómo crear un componente de filtrado en CSS puroGeorge Martsoukos

CSSConsejo rápido: Cómo crear un formulario de comentarios «fuera del lienzo» con CSS puroGeorge Martsoukos

Bootstrap 4Consejo rápido: Cómo personalizar el componente de acordeón de Bootstrap 4George Martsoukos





