Pestañas CSS puras con detalles, cuadrículas y subcuadrículas
El uso de CSS para crear interfaces con pestañas es un tema interminable en el desarrollo web moderno. ¿Son posibles? Si es así, ¿son accesibles? Escribí cómo construirlos. primero hace nueve años y cómo Integrar prácticas de accesibilidad Ingreselos.
Aunque mi solución funciona posible Todavía relevante hoy, he encontrado un enfoque más moderno para las pestañas CSS, usando <details> combinación de elementos Cuadrícula CSS y subcuadrícula.
Primero, HTML
Comencemos configurando la estructura HTML. necesitaremos un conjunto <details> El elemento dentro del contenedor principal que llamaremos .grid. cada <details> será un .item Como puedes imaginar, cada pestaña es una pestaña dentro de la interfaz.
<div class="grid">
<!-- First tab: set to open -->
<details class="item" name="alpha" open>
<summary class="subitem">First item</summary>
<div><!-- etc. --></div>
</details>
<details class="item" name="alpha">
<summary class="subitem">Second item</summary>
<div><!-- etc. --></div>
</details>
<details class="item" name="alpha">
<summary class="subitem">Third item</summary>
<div><!-- etc. --></div>
</details>
</div>

¡Estas ni siquiera parecen etiquetas reales! Pero esta es la estructura correcta que queremos antes de entrar en CSS, donde jugaremos con cuadrículas y subcuadrículas de CSS.
Siguiente, CSS
Configuremos una cuadrícula para el elemento contenedor usando (lo adivinaste) CSS Grid. Básicamente lo que estamos haciendo es una cuadrícula de tres columnas, una columna por pestaña (o .item), con un pequeño espacio entre ellos.
También estableceremos dos líneas en .griduno cambia de tamaño según el contenido y el otro permanece proporcional al espacio disponible. La primera fila contendrá nuestras pestañas y la segunda fila está reservada para mostrar el panel de pestañas activas.
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(200px, 1fr));
grid-template-rows: auto 1fr;
column-gap: 1rem;
}
Ahora nos parecemos más a pestañas:

A continuación, necesitamos configurar la subcuadrícula para el elemento de pestaña. Queremos subredes porque nos permite utilizar las existentes. .grid filas sin necesidad de anidar una cuadrícula completamente nueva con nuevas filas. De esta manera todo se alinea muy bien.
Entonces configuraremos cada pestaña: <details> Elementos: como cuadrículas y establece sus columnas y filas para heredar del elemento principal. .gridde filas y subgrid.
details {
display: grid;
grid-template-columns: subgrid;
grid-template-rows: subgrid;
}
Además, queremos que cada elemento de pestaña llene todo el .gridasí que lo configuramos en <details> El elemento ocupa todo el espacio disponible horizontal y verticalmente. grid-column y grid-row característica:
details {
display: grid;
grid-template-columns: subgrid;
grid-template-rows: subgrid;
grid-column: 1 / -1;
grid-row: 1 / span 3;
}
Al principio parece un poco extraño porque las tres pestañas están apiladas una encima de la otra, pero cubren toda la superficie. .grid Esto es exactamente lo que queremos.

A continuación, colocamos el contenido del panel de pestañas en la segunda fila de la subcuadrícula y lo estiramos a las tres columnas. estamos usando ::details-content (Gran apoyo, pero Aún no en WebKit Al momento de escribir) apunta al contenido del panel, lo cual es bueno porque significa que no necesitamos tener otro contenedor en el marcado solo para ese propósito.
details::details-content {
grid-row: 2; /* position in the second row */
grid-column: 1 / -1; /* cover all three columns */
padding: 1rem;
border-bottom: 2px solid dodgerblue;
}
Lo que pasa con las interfaces con pestañas es que solo queremos mostrar un panel de pestañas abiertas a la vez. Afortunadamente, tenemos una opción. (open) estado <details> elemento y ocultar ::details-content de cualquier pestaña :not((open))Al usar habilitar selector:
details:not((open))::details-content {
display: none;
}
Todavía tenemos pestañas superpuestas, pero el único panel de pestañas que mostramos está abierto actualmente, lo que aclara un poco las cosas:

torneado <details> Ir a la pestaña
¡Ahora viene lo divertido! Ahora, todas nuestras pestañas están visualmente apiladas. Queremos difundirlos a lo largo y ancho .gridfila superior. cada <details> elemento contiene un <summary> Proporciona etiquetas de pestañas y botones para activar y desactivar cada pestaña.
coloquemos <summary> elemento en la primera fila de la subcuadrícula y agregue el estilo de luz aplicado <details> La pestaña se encuentra en (open) estado:
summary {
grid-row: 1; /* First subgrid row */
display: grid;
padding: 1rem; /* Some breathing room */
border-bottom: 2px solid dodgerblue;
cursor: pointer; /* Update the cursor when hovered */
}
/* Style the <summary> element when <details> is (open) */
details(open) summary {
font-weight: bold;
}
Nuestras pestañas todavía están apiladas juntas, pero ¿qué tal si aplicamos un estilo ligero cuando la pestaña está abierta?

¡Ya casi llegamos! Lo último es posicionarse. <summary> Subcuadrícula de los elementos en las columnas para que ya no se bloqueen entre sí. usaremos :nth-of-type Pseudoseleccione cada uno individualmente en orden en HTML:
/* First item in first column */
details:nth-of-type(1) summary {
grid-column: 1 / span 1;
}
/* Second item in second column */
details:nth-of-type(2) summary {
grid-column: 2 / span 1;
}
/* Third item in third column */
details:nth-of-type(3) summary {
grid-column: 3 / span 1;
}
¡Échale un vistazo! Las pestañas están espaciadas uniformemente a lo largo de la fila superior de la subcuadrícula:

Desafortunadamente, no podemos usar bucles en CSS (¡todavía!), pero podemos usar variables para mantener los estilos secos:
summary {
grid-column: var(--n) / span 1;
}
Ahora necesitamos configurar --n cada variable <details> elemento. Me gusta insertar variables directamente en HTML y usarlas como ganchos de estilo:
<div class="grid">
<details class="item" name="alpha" open style="--n: 1">
<summary class="subitem">First item</summary>
<div><!-- etc. --></div>
</details>
<details class="item" name="alpha" style="--n: 2">
<summary class="subitem">Second item</summary>
<div><!-- etc. --></div>
</details>
<details class="item" name="alpha" style="--n: 3">
<summary class="subitem">Third item</summary>
<div><!-- etc. --></div>
</details>
</div>
Nuevamente, dado que actualmente no hay bucles en CSS, tiendo a usar lenguajes de plantilla, específicamente líquidopara obtener alguna acción en bucle. De esta manera, no es necesario escribir HTML explícitamente para cada pestaña:
{% for item in itemList %}
<div class="grid">
<details class="item" name="alpha" style="--n: {{ forloop.index }}" {% if forloop.first %}open{% endif %}>
<!-- etc. -->
</details>
</div>
{% endfor %}
Por supuesto, puedes utilizar diferentes lenguajes de plantilla. Si prefieres que sea sencillo, ¡hay muchos disponibles!
toques finales
Está bien, mentí. tener una cosa más Qué deberíamos hacer. Ahora sólo puedes hacer clic en el último <summary> elemento, porque todos <details> Los bloques se apilan juntos con el último encima de la pila.
Tal vez lo hayas adivinado: tenemos que poner nuestro <summary> Ponga el elemento en la parte superior configurando z-index.
summary {
z-index: 1;
}
Aquí está la demostración funcional completa:
Accesibilidad
este <details> El elemento incluye funciones de accesibilidad integradas, como navegación con teclado y compatibilidad con lectores de pantalla para estados expandidos y contraídos. Estoy seguro de que podemos hacerlo mejor, pero probablemente ese sea un tema para otro artículo. Espero brindar algunos comentarios en los comentarios para ayudar a cubrir tantas bases como sea posible.
renovar: Nathan Knoller se hizo eco existir Hay algunos puntos maravillosos sobre Mastodon. Adrián Roselli interviene Hay más contexto en los comentarios.
Estamos en 2025 y podemos crear pestañas usando solo HTML y CSS sin ninguna modificación. No sé ustedes, pero este desarrollador está contento hoy, incluso si todavía necesitamos un poco de paciencia para que los navegadores admitan plenamente estas funciones.