ENTENDIENDO POR QUÉ Z-INDEX EN CSS NO FUNCIONA Y CÓMO SOLUCIONARLO
Introducción al problema de z-index en CSS
La propiedad z-index en CSS representa una de las herramientas más útiles para controlar el orden de superposición de elementos en la tercera dimensión de una página web. Sin embargo, muchos desarrolladores se encuentran con situaciones en las que esta propiedad parece no tener ningún efecto, a pesar de asignarle valores aparentemente correctos. Este comportamiento inesperado suele deberse a conceptos fundamentales del posicionamiento y del orden de apilamiento que no siempre son intuitivos a primera vista.
En este tutorial profundizaremos en las causas principales por las que z-index no funciona como se espera, exploraremos el rol del posicionamiento, el orden de apilamiento predeterminado del navegador y los contextos de apilamiento que afectan a elementos hijos. Todo ello acompañado de ejemplos prácticos que permitirán comprender y resolver estos problemas de manera efectiva. Al final, dispondrás de las herramientas necesarias para manejar el orden de capas en tus proyectos web con precisión y sin recurrir a soluciones improvisadas.
¿Qué es exactamente la propiedad z-index?
La propiedad z-index forma parte del modelo de coordenadas tridimensionales que utiliza CSS para representar elementos en una página. Las dimensiones x e y corresponden al ancho y alto habituales, mientras que la dimensión z representa la profundidad o la posición a lo largo del eje perpendicular a la pantalla.
Al aumentar el valor de z-index de un elemento, este se posiciona más al frente respecto a otros elementos que compartan el mismo espacio en las dimensiones x e y. Los valores pueden ser números enteros positivos o negativos, siendo cero el valor por defecto. Un valor mayor significa que el elemento aparecerá por encima de aquellos con valores menores.
Aquí un ejemplo básico de cómo funciona cuando todo está configurado correctamente:
<div class="caja caja-roja"></div>
<div class="caja caja-azul"></div>
.caja {
width: 200px;
height: 200px;
position: absolute;
top: 50px;
left: 50px;
}
.caja-roja {
background-color: red;
z-index: 1;
}
.caja-azul {
background-color: blue;
z-index: 10;
}
En este caso, la caja azul aparecerá por encima de la roja gracias a su mayor valor de z-index.
z-index no funciona sin una posición definida diferente de static
Una de las razones más frecuentes por las que z-index parece ignorado es que el elemento afectado mantiene la posición predeterminada del navegador, es decir, position: static.
Por defecto, todos los elementos en HTML tienen position: static, lo que significa que no participan en el control de profundidad mediante z-index. La propiedad z-index solo tiene efecto cuando el elemento tiene una posición explícita como relative, absolute, fixed o sticky.
Veamos un ejemplo donde z-index no produce ningún cambio:
.caja {
width: 150px;
height: 150px;
}
.caja-roja {
background-color: red;
z-index: 10;
}
.caja-azul {
background-color: blue;
margin-left: 50px;
margin-top: -100px;
z-index: 1;
}
En esta configuración, aunque la caja roja tenga un z-index mayor, no se superpondrá correctamente porque ninguno de los elementos tiene posición definida.
La solución consiste en aplicar una posición explícita:
.caja {
width: 150px;
height: 150px;
position: relative; /* o absolute, fixed, etc. */
}
Ahora sí, los valores de z-index serán respetados y la caja roja aparecerá por encima de la azul si su valor es mayor.
Este requisito es fundamental y a menudo pasa desapercibido, especialmente cuando se trabaja con bibliotecas o frameworks que aplican estilos base sin modificar la posición.
El orden de apilamiento predeterminado del navegador
Incluso cuando no se define ningún z-index, los navegadores aplican un orden de apilamiento natural que determina qué elementos aparecen por encima de otros. Este orden sigue reglas específicas establecidas por la especificación CSS.
El orden básico es el siguiente:
- Fondo y bordes del elemento raíz (html).
- Elementos en bloque no posicionados, en el orden de aparición en el HTML.
- Elementos posicionados, en el orden de aparición en el HTML.
Por ejemplo, si tenemos dos elementos posicionados sin z-index explícito:
<div class="caja caja-roja"></div>
<div class="caja caja-azul"></div>
.caja {
width: 150px;
height: 150px;
position: relative;
}
.caja-roja {
background-color: red;
}
.caja-azul {
background-color: blue;
margin-left: 50px;
margin-top: -100px;
}
La caja azul aparecerá por encima de la roja simplemente porque aparece después en el código HTML y ambos son elementos posicionados.
Si agregamos un tercer elemento sin posición:
<div class="caja caja-roja">Posicionado</div>
<div class="caja caja-azul">Posicionado</div>
<div class="caja caja-amarilla">No posicionado</div>
.caja-amarilla {
background-color: yellow;
position: static;
margin-left: 100px;
margin-top: -200px;
}
El elemento amarillo quedará detrás de los otros dos, independientemente de su orden en el HTML, porque los elementos posicionados siempre se apilan por encima de los no posicionados en el mismo nivel.
Este comportamiento predeterminado permite un control básico sin necesidad de z-index, pero se vuelve crítico cuando se combinan diferentes tipos de posicionamiento.
Los contextos de apilamiento y su impacto en elementos hijos
Uno de los conceptos más complejos y a la vez más importantes relacionados con z-index es el contexto de apilamiento o stacking context. Un contexto de apilamiento se crea cuando un elemento tiene ciertas propiedades que lo convierten en un contenedor independiente para el orden de sus descendientes.
Entre las propiedades que generan un nuevo contexto de apilamiento se encuentran:
- position diferente de static con z-index diferente de auto.
- opacity menor que 1.
- transform diferente de none.
- filter diferente de none.
- perspective diferente de none.
- isolation: isolate.
Cuando un elemento padre crea un contexto de apilamiento, todos sus hijos quedan restringidos dentro de ese contexto. El z-index de los hijos se evalúa únicamente dentro del contexto del padre, y nunca podrá superar al z-index del padre en el contexto superior.
Veamos un ejemplo clásico donde z-index de hijo no supera al elemento externo:
<div class="contenedor-rojo">
<div class="caja caja-amarilla"></div>
</div>
<div class="caja caja-azul"></div>
.contenedor-rojo {
position: relative;
width: 200px;
height: 200px;
background-color: red;
z-index: 5;
}
.caja {
width: 100px;
height: 100px;
position: absolute;
}
.caja-amarilla {
background-color: yellow;
top: 20px;
left: 20px;
z-index: 100;
}
.caja-azul {
background-color: blue;
top: 50px;
left: 80px;
z-index: 10;
}
A pesar de que la caja amarilla tiene z-index: 100, aparecerá detrás de la caja azul porque su padre (el contenedor rojo) tiene z-index: 5, inferior al 10 de la caja azul. El contexto creado por el contenedor rojo limita el alcance del z-index de sus hijos.
Para resolver este problema, existen varias opciones:
-
Eliminar el z-index o la posición que crea el contexto en el padre.
-
Reestructurar el HTML para sacar el elemento hijo del contenedor restrictivo.
-
Aplicar un z-index mayor al padre si es posible.
Ejemplo de reestructuración:
<div class="caja caja-roja"></div>
<div class="caja caja-amarilla"></div>
<div class="caja caja-azul"></div>
.caja-roja {
background-color: red;
z-index: 5;
}
.caja-amarilla {
background-color: yellow;
z-index: 100;
}
.caja-azul {
background-color: blue;
z-index: 10;
}
Ahora la caja amarilla aparecerá por encima de ambas gracias a su alto z-index y al no estar limitada por un contexto padre.
Comprender los contextos de apilamiento es esencial en proyectos complejos como modales, menús desplegables, tooltips o interfaces con múltiples capas.
Evitar valores excesivamente altos en z-index
Una práctica común pero desaconsejada es asignar valores muy altos a z-index, como 999 o 9999, con la intención de asegurar que un elemento siempre esté al frente.
.elemento-modal {
z-index: 9999;
}
Aunque esta técnica funciona a corto plazo, genera problemas de mantenibilidad a largo plazo. En proyectos grandes, múltiples desarrolladores pueden terminar en una guerra de z-index, incrementando valores progresivamente hasta números astronómicos, lo que complica el debugging y el entendimiento del flujo de capas.
La recomendación actual, alineada con las mejores prácticas de 2026, es utilizar valores pequeños y coherentes:
- Valores entre 1 y 10 para elementos básicos.
- 10-50 para overlays y tooltips.
- 50-100 para menús y dropdowns.
- 100-500 para modales.
- Reservar valores superiores solo para casos excepcionales como loaders globales.
De esta forma, se mantiene un sistema ordenado y predecible.
Además, herramientas modernas como CSS Custom Properties permiten definir escalas de z-index centralizadas:
:root {
--z-tooltip: 10;
--z-dropdown: 20;
--z-modal: 100;
--z-loader: 500;
}
.modal {
z-index: var(--z-modal);
}
Esta aproximación facilita cambios globales y mejora la legibilidad del código.
Casos prácticos avanzados con múltiples contextos
En aplicaciones reales, es común encontrarse con anidamiento de contextos de apilamiento. Por ejemplo, un modal que contiene un dropdown interno.
<div class="modal">
<div class="dropdown">
<ul class="opciones"></ul>
</div>
</div>
Si el modal tiene z-index: 100 y crea un contexto (por ejemplo, mediante opacity: 0.95), el dropdown interno, aunque tenga z-index: 1000, quedará restringido dentro del contexto del modal.
Para que el dropdown aparezca por encima de otros elementos externos al modal, se debe evitar crear contexto innecesario en el padre o aplicar isolation: isolate en el dropdown cuando sea necesario.
Otro caso frecuente ocurre con elementos fixed dentro de contenedores transformados. Las transformaciones crean contextos de apilamiento, lo que puede hacer que un header fixed quede detrás de contenido posterior.
La solución suele ser mover el elemento fixed fuera del contenedor transformado o eliminar la transformación cuando no sea estrictamente necesaria.
Interacción con otras propiedades modernas
En los últimos años, propiedades como backdrop-filter y clip-path también han empezado a crear contextos de apilamiento en los navegadores modernos. Esto significa que un simple filtro de fondo puede alterar inesperadamente el comportamiento de z-index en elementos hijos.
Es importante revisar la compatibilidad y el impacto de estas propiedades al diseñar componentes complejos.
Asimismo, el uso de grid y flexbox no crea contextos de apilamiento por sí solos, pero cuando se combinan con position o transform, el resultado puede ser impredecible si no se planifica adecuadamente.
Depuración de problemas de z-index
Cuando z-index no funciona como esperas, sigue estos pasos sistemáticos:
- Verifica que el elemento tenga position diferente de static.
- Inspecciona si algún ancestro crea un contexto de apilamiento.
- Comprueba el orden de aparición en el HTML para elementos sin z-index.
- Usa las herramientas de desarrollador del navegador para visualizar el orden de apilamiento (en Chrome/Firefox, la pestaña “Computed” muestra el stacking context).
- Evita valores extremos y opta por una escala coherente.
Estas prácticas ahorran tiempo y evitan frustraciones innecesarias.
Conclusiones
La propiedad z-index es poderosa pero requiere un entendimiento profundo del posicionamiento CSS, el orden de apilamiento predeterminado y especialmente los contextos de apilamiento. Las causas más comunes de que z-index no responda son la ausencia de posición definida, la influencia de contextos padres y el mal uso de valores excesivos.
Al aplicar posición adecuada, estructurar el HTML de forma lógica, evitar contextos innecesarios y mantener una escala de z-index ordenada, podrás controlar con precisión el orden de capas en cualquier proyecto web. Estas técnicas, combinadas con las herramientas de depuración modernas, te permitirán crear interfaces robustas y mantenibles.
Dominar estos conceptos no solo resuelve problemas inmediatos, sino que eleva la calidad general de tu código CSS, haciendo que tus desarrollos sean más predecibles y profesionales en el panorama actual del desarrollo frontend en 2026.