z-index no funciona: 5 soluciones definitivas

“Le puse z-index: 9999 y sigue detrás” 😤 Si esto te suena familiar, este artículo es para ti.

El problema de z-index no suele ser el valor que asignas, sino el contexto de apilamiento que estás ignorando.


La causa raíz: stacking context

Cada elemento HTML crea su propio contexto de apilamiento. Los elementos hijos solo pueden estar dentro de ese contexto, nunca salir de él.

Esto significa:

  • Aunque tu hijo tenga z-index: 9999
  • Si su padre tiene z-index: 1
  • El hijo nunca podrá estar encima de elementos externos al padre

Las 5 soluciones

Solución 1: Añade position al elemento

z-index solo funciona con elementos posicionados.

/* ❌ NO funciona */
.boton-flotante {
  z-index: 100;
}

/* ✅ CORRECTO */
.boton-flotante {
  position: relative;
  z-index: 100;
}

Valores válidos de position: relative, absolute, fixed, sticky


Solución 2: Saca al hijo del contexto del padre

Si el padre está limitando a tu hijo, tienes opciones:

Opción A: Mueve el HTML

<!-- ❌ El dropdown está dentro de .header -->
<div class="header">
  <div class="dropdown">...</div>
</div>

<!-- ✅ El dropdown está fuera -->
<div class="header"></div>
<div class="dropdown">...</div>

Opción B: Usa Portal (React/Vue)

// React - Renderiza fuera del padre
import { createPortal } from 'react-dom';

return createPortal(
  <Dropdown />,
  document.body
);

Solución 3: Remueve propiedades que crean nuevo contexto

Estas propiedades crean stacking context automáticamente:

  • opacity menor a 1
  • transform (cualquier valor excepto none)
  • filter
  • isolation: isolate
  • position: fixed
/* ❌ Crea nuevo contexto */
.caja {
  opacity: 0.9;
  transform: scale(1.1);
}

/* ✅ Alternativas sin crear contexto */
.caja {
  background: rgba(255, 255, 255, 0.9);
  /* transform eliminado */
}

Solución 4: Usa position: fixed para overlays

Para modales y popups, position: fixed es más confiable:

.modal-overlay {
  position: fixed;
  z-index: 9999;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
}

.modal-contenido {
  position: relative;
  z-index: 10000; /* Mayor que el overlay */
}

Solución 5: Establece el z-index del padre

A veces, solo necesitas darle al padre el z-index correcto:

.header {
  position: relative;
  z-index: 100;
}

.dropdown {
  position: absolute;
  z-index: 200; /* Ahora funciona */
  top: 100%;
}

Ejemplo completo: Modal que no aparece

El problema

<div class="modal-container"> <!-- z-index: 1 -->
  <div class="modal"> <!-- z-index: 9999 - NO FUNCIONA -->
    Contenido
  </div>
</div>

La solución

<div class="modal-container">
  <!-- El modal se rendered en document.body via portal -->
</div>

<!-- O directamente: -->
<div class="modal">
  Contenido
</div>
.modal {
  position: fixed; /* Crea contexto independiente */
  z-index: 9999;
  /* Centrado */
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Tabla de referencias rápidas

ProblemaSolución
z-index no tiene efectoAñadir position: relative/absolute/fixed/sticky
Hijo detrás de elementos externosRemover el padre del flujo o usar portal
z-index no supera a otrosVerificar z-index del padre
Elementos se cortanRevisar overflow: hidden del padre
Modal detrás de headerUsar position: fixed en el modal

Artículos relacionados


: El número en z-index no es la respuesta. El contexto lo es.