z-index y contextos de apilamiento en CSS
En esta lección
- El eje Z
- El orden natural, sin z-index
- z-index necesita position (o casi)
- El contexto de apilamiento
- Qué crea un contexto nuevo
- isolation: isolate: crear un contexto a propósito
- La capa superior: modales sin z-index
- Cómo depurar un z-index que no funciona
- Una escala ordenada con variables
- Errores frecuentes
- Resumen
La página parece plana, pero los elementos se dibujan unos encima de otros, como hojas de papel sobre una mesa. z-index decide qué hoja queda arriba. Parece sencillo hasta que pones z-index: 9999 a un menú y sigue quedando debajo de un banner. En esta lección vas a entender por qué pasa: qué es un contexto de apilamiento, qué propiedades lo crean sin que te des cuenta, cómo usar isolation: isolate y cómo organizar los valores para no volver a pelearte con ellos.
En posicionamiento viste lo básico de z-index. Aquí lo vemos con calma.
El eje Z
Además del ancho (eje X) y el alto (eje Y), la página tiene un tercer eje imaginario, el eje Z, que sale de la pantalla hacia ti. z-index coloca un elemento en ese eje: cuanto mayor el número, más cerca de ti y más “arriba”.
.menu-desplegable { position: absolute; z-index: 10; }
.aviso-cookies { position: fixed; z-index: 20; }
Si dos elementos se solapan, el de mayor z-index tapa al otro. Admite números enteros, también negativos, y el valor auto (por defecto).
El orden natural, sin z-index
Antes de tocar nada, el navegador ya sigue un orden. Dentro de un mismo grupo, de abajo arriba:
- El fondo y los bordes del elemento que contiene el grupo.
- Los hijos con
z-indexnegativo. - Los elementos normales (no posicionados), en el orden del HTML.
- Los elementos flotantes (
float). - El texto y los elementos en línea.
- Los elementos posicionados (
relative,absolute,fixed,sticky) conz-index: autoo0, en el orden del HTML. - Los elementos con
z-indexpositivo, de menor a mayor.
Dos consecuencias prácticas:
- Un elemento posicionado queda por encima de uno normal aunque ninguno tenga
z-index. Por eso, a veces, ponerposition: relativea algo hace que “salte” por encima de sus vecinos. - Entre elementos del mismo nivel, gana el que aparece más tarde en el HTML.
z-index necesita position (o casi)
z-index no hace nada en un elemento con position: static, que es el valor por defecto:
.tarjeta { z-index: 5; } /* no tiene efecto */
.tarjeta { position: relative; z-index: 5; } /* ahora sí */
position: relative sin top ni left no mueve el elemento, así que es la forma habitual de “activar” el z-index sin cambiar nada más.
La excepción son los hijos de un contenedor flex o grid: en ellos z-index funciona aunque sean static. Si en una rejilla de fotos quieres que la que pasa a primer plano quede encima, no hace falta position:
.galeria { display: grid; }
.galeria img:hover { z-index: 1; scale: 1.2; }
El contexto de apilamiento
Aquí está la clave de todo. Un contexto de apilamiento es un grupo de elementos que se ordenan entre sí, y que hacia fuera se comporta como una sola capa.
Piensa en carpetas sobre una mesa. Dentro de cada carpeta hay hojas ordenadas del 1 al 9999. Pero si la carpeta A está debajo de la carpeta B, todas las hojas de A quedan debajo de B, aunque una hoja de A tenga el número 9999 y la carpeta B solo tenga un 2. El número de la hoja solo sirve dentro de su carpeta.
<header class="cabecera">
<ul class="menu">...</ul>
</header>
<div class="banner">...</div>
.cabecera { position: relative; z-index: 1; } /* carpeta con nivel 1 */
.menu { position: absolute; z-index: 9999; } /* hoja 9999, pero DENTRO de la carpeta 1 */
.banner { position: relative; z-index: 2; } /* carpeta con nivel 2 */
El menú se despliega sobre el banner… y queda debajo. Su 9999 solo compite con los otros hijos de .cabecera. Hacia fuera, lo que cuenta es el 1 de la cabecera contra el 2 del banner. Este es el famoso “z-index: 9999 no funciona”. Subirlo a 999999 tampoco funcionaría.
La solución es comparar al nivel correcto: subir la cabecera, no el menú.
.cabecera { position: relative; z-index: 3; } /* ahora toda la carpeta va encima */
En el ejemplo del editor tienes las dos versiones lado a lado.
Qué crea un contexto nuevo
Esta es la parte traicionera: muchas propiedades que no tienen nada que ver con capas crean un contexto nuevo sin avisar. Las más habituales:
| Propiedad | Crea contexto cuando… |
|---|---|
position: relative / absolute | Tiene un z-index distinto de auto |
position: fixed / sticky | Siempre |
| Hijo de flex o grid | Tiene un z-index distinto de auto |
opacity | Es menor que 1 |
transform, translate, rotate, scale | Distinto de none |
filter, backdrop-filter | Distinto de none |
mix-blend-mode | Distinto de normal |
clip-path, mask | Distinto de none |
isolation | Es isolate |
will-change | Nombra alguna de las anteriores |
contain | layout, paint, strict o content |
container-type | size o inline-size |
Y el propio <html> es el contexto raíz de toda la página.
Un caso real: añades a una tarjeta un efecto transform: translateY(-4px) al pasar el ratón. De repente, el menú de opciones que esa tarjeta tiene dentro deja de verse sobre la tarjeta de abajo. El transform ha convertido la tarjeta en una carpeta con nivel auto, y la siguiente tarjeta, que va después en el HTML, la tapa.
.tarjeta { position: relative; }
.tarjeta:hover { transform: translateY(-4px); z-index: 1; } /* súbela mientras está activa */
isolation: isolate: crear un contexto a propósito
A veces quieres justo lo contrario: encerrar un grupo de capas para que no interfieran con el resto. isolation: isolate crea un contexto nuevo sin efectos secundarios: no mueve nada, no cambia el aspecto y no necesita un número de z-index.
El uso más típico es un adorno detrás del texto con z-index: -1:
.etiqueta {
position: relative;
isolation: isolate; /* sin esto, el adorno se va detrás del fondo de la página */
}
.etiqueta::before {
content: "";
position: absolute;
inset: 0;
z-index: -1; /* detrás del texto de la etiqueta */
background: #fde68a;
border-radius: 999px;
}
Sin isolation, el z-index: -1 manda el adorno al fondo del contexto más cercano, que puede ser la página entera: quedaría escondido detrás del fondo de una sección. Con isolate, la etiqueta es su propio contexto y el -1 significa “detrás del texto, pero dentro de la etiqueta”.
También es útil en componentes: si cada tarjeta, modal o widget lleva isolation: isolate, los z-index de dentro nunca chocarán con los del resto de la página.
La capa superior: modales sin z-index
Hay una capa que está por encima de todo, sin importar los contextos: la capa superior (top layer). Llegan a ella:
- Un
<dialog>abierto conshowModal(). - Un elemento con el atributo
popovercuando está abierto. - Un elemento a pantalla completa.
<dialog id="confirmar">¿Seguro que quieres cancelar la reserva?</dialog>
Si usas <dialog> para tus modales (lo tienes en details y dialog), no necesitas ningún z-index para que queden encima: ningún contexto los atrapa. Es la mejor solución al problema de las capas.
Cómo depurar un z-index que no funciona
Cuando un elemento se empeña en quedarse debajo, sigue estos pasos:
- ¿Tiene
positiono es hijo de flex/grid? Si esstatic, elz-indexse ignora. En las DevTools de Firefox, la regla aparece en gris y, al pasar el ratón, explica por qué no tiene efecto. - ¿Con quién compite de verdad? Sube por sus antepasados en la pestaña Elementos y busca el primero que cree un contexto:
z-indexconposition,opacity,transform,filter… Revisa la pestaña Computed de cada uno. - Compara al mismo nivel. Encuentra el antepasado del elemento y el antepasado del que lo tapa que son hermanos dentro del mismo contexto. Esos son los que tienes que ordenar.
- Prueba en vivo. Cambia el
z-indexdel antepasado desde las DevTools hasta que funcione, y luego llévalo a tu CSS.
Chrome y Edge tienen además un panel Layers (en Edge, 3D View) que dibuja las capas de la página en tres dimensiones. Lo abres desde el menú de las DevTools, en “Más herramientas”.
Una escala ordenada con variables
Si cada vez que algo queda debajo le sumas un cero (10, 100, 9999, 99999…), en unos meses nadie sabrá qué va encima de qué. La solución es definir una escala en un solo sitio, con variables CSS:
:root {
--z-base: 1;
--z-desplegable: 100;
--z-cabecera-fija: 200;
--z-capa-oscura: 300;
--z-modal: 400;
--z-aviso: 500;
}
.menu-usuario { position: absolute; z-index: var(--z-desplegable); }
.cabecera { position: sticky; top: 0; z-index: var(--z-cabecera-fija); }
.toast { position: fixed; bottom: 16px; z-index: var(--z-aviso); }
Ventajas:
- Los nombres dicen qué es cada capa, no un número mágico.
- El orden de toda la web se ve de un vistazo.
- Los saltos de 100 en 100 dejan hueco para añadir capas intermedias.
- Dentro de un componente aislado con
isolation: isolate, basta con1,2,3: esos números no salen fuera.
Errores frecuentes
- Poner
z-indexa un elementostatic. No hace nada. Añadeposition: relative, salvo que sea hijo de flex o grid. - Subir el número del hijo. Si el problema es el contexto del padre, da igual poner 9999 o un millón. Ordena el antepasado que compite.
- No ver el contexto que crean
opacity,transformofilter. Un efecto de:hoverpuede cambiar las capas. Revisa los antepasados en las DevTools. z-index: -1que desaparece. Se ha ido detrás del fondo de un antepasado. Ponisolation: isolateen el padre directo.- Números al azar.
z-index: 999,9999,99999repartidos por el CSS acaban en una guerra. Usa una escala con variables. - Luchar contra las capas en los modales. Usa
<dialog>conshowModal(): va a la capa superior.
Resumen
| Concepto | Qué significa |
|---|---|
z-index | Posición en el eje Z: mayor número, más arriba |
| Requisito | position distinto de static, o ser hijo de flex/grid |
Sin z-index | Los posicionados van encima; a igualdad, el último del HTML |
| Contexto de apilamiento | Grupo que se ordena por dentro y fuera es una sola capa |
| Lo crean | z-index con position, fixed, sticky, opacity < 1, transform, filter… |
| “9999 no funciona” | El padre está en un contexto más bajo: ordena el antepasado |
isolation: isolate | Crea un contexto a propósito, sin efectos visuales |
| Capa superior | <dialog> modal y popover: encima de todo, sin z-index |
| Escala con variables | --z-desplegable, --z-modal… en :root |
Ya controlas las capas. Sigue con una de las preguntas más famosas de CSS: cómo centrar elementos.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.