Lo que nadie te contó de la propiedad z-index

  • Por
Analizamos la propiedad CSS z-index para intentar descifrar su correcto uso más allá del habitual conocimiento que tenemos de ella, normalmente consistente en saber que las capas con mayor valor de z-index se colocan delante de las de valor menor.

El problema con z-index es que muy poca gente comprende cómo funciona realmente. No es complicado, pero si nunca te has tomado la molestia de leer su especificación, hay ciertamente algunos aspectos cruciales que has pasado por alto.

¿No me crees? Bien, veamos si puedes solventar este problema:

El problema

En el siguiente HTML tienes tres elementos <div>, y cada <div> contiene un único elemento <span>. Cada <span> tiene un color de fondo — rojo, verde y azul respectivamente. Cada <span> está también posicionado absolutamente cerca de la posición superior izquierda del documento, escasamente solapando a los otros elementos <span> así que puedes ver cuáles están apilados en frente de cada cual. El primer <span> tiene un valor z-index de 1, mientras que los otros dos no tienen ningún valor z-index fijado.

Aquí tenemos el HTML y CSS básicos para entenderlo. También he incluido una demo visual (vía Codepen) con el código que se muestra a continuación:

<div>
<span class="rojo">Rojo</span>
</div>
<div>
<span class="verde">Verde</span>
</div>
<div>
<span class="azul">Azul</span>
</div>
.rojo, .verde, .azul {
position: absolute;
}
.rojo {
background: red;
z-index: 1;
}
.verde {
background: green;
}
.azul {
background: blue;
}

Puede verse el ejemplo completo aquí.

Aquí está el desafío: intenta ver si puedes hacer que el <span> rojo se apile detrás del azul y del verde sin romper ninguna de las siguiente reglas:

  • No alteres el HTML de ninguna manera.
  • No añadas o cambies la propiedad z-index de ningún elemento.
  • No añadas o cambies la propiedad de posicionamiento de ningún elemento.
  • Para que puedas hacerte una idea clara, cliquea en "edit" en el enlace de Codepen que hay arriba y juega un momento con el código. Si tienes éxito, debería quedarte algo como lo que sigue. Prueba aquí.
Advertencia: No cliquees en la pestaña de CSS o te dará inmediatamente la respuesta.

La solución

La solución es añadir un valor de opacidad menor que 1 en el primer <div> (el padre del <span> rojo). Aquí está el CSS que se añade al Codepen:

div:first-child {
opacity: .99;
}

Si te estás rascando la cabeza ahora mismo en estado de shock y te muestras estupefacto por el hecho de que esa opacidad haya causado efecto en cómo los elementos se apilan los unos respecto a los otros, bienvenido al club. Me pasó lo mismo cuando tropecé la primera vez con el mismo problema.

Tranquilo, el resto del artículo dejará las cosas un poco más claras.

Ordenar el apilamiento

Z-index parece sencillo: elementos con un z-index mayor son apilados delante de los elementos con un menor z-index, ¿no? Bien, de hecho, no. Esto es parte del problema del z-index. Parece sencillo, así que los desarrolladores no dedican tiempo a leer sus reglas.

Cada elemento en un documento HTML puede estar delante o detrás de otro elemento en el documento. Esto se conoce como el orden de apilamiento. Las reglas que determinan este orden están bonita y claramente definidas en la especificación, pero como he constatado, no son completamente entendidas por la mayoría de desarrolladores.

Cuando el z-index y las propiedades de posición no están declaradas, las reglas son muy simples: básicamente, el orden de apilamiento es el mismo que el orden de aparición en el HTML. (DE ACUERDO, es de hecho un poco más complicado que eso, pero si no estás usando márgenes negativos para solapar elementos en la misma línea de posición, probablemente no te encontrarás con elementos problemáticos).

Cuando introduces la propiedad de posición en la mezcla, cualesquiera elementos posicionados (y sus hijos) se muestran delante de elementos no posicionados (decimos que un elemento está “posicionado” cuando tiene un valor de posición distinto de “static”, como “relative”, “absolute”, etc.)

Finalmente, cuando el z-index se involucra también, las cosas se complican un poco. Al principio es natural asumir que los elementos con valores z-index mayores están delante de los elementos con valores z-index menores, y que cualquier elemento con z-index está en frente de los elementos que no tienen z-index, pero no es tan simple. Lo primero de todo, z-index solo funciona con elementos posicionados. Si intentas asignarle un z-index a un elemento sin posición especificada, no hará nada. En segundo lugar, los valores z-index pueden crear contextos de apilamiento, y ahora de pronto lo que parecía sencillo es bastante más complicado.

Contextos de apilamiento

Grupos de elementos con un padre común que se mueven hacia adelante o hacia atrás juntos en el orden de apilamiento, se integran en lo que es conocido como contexto de apilamiento. Una completa comprensión de los contextos de apilamiento es la llave para realmente captar cómo funciona el z-index y el orden de apilamiento.

Cada contexto de apilamiento tiene un elemento HTML como su elemento raíz. Cuando un nuevo contexto de apilamiento se forma en un elemento, ése contexto confina todos sus elementos hijos en un lugar particular del orden. Eso significa que si un elemento es contenido en un contexto de apilamiento en lo más bajo del orden, no hay manera de que aparezca delante de otro elemento en un contexto de apilamiento que está en un nivel superior en el orden, ¡incluso con un z-index de mil millones!

Los nuevos contextos de apilamiento pueden ser formados en un elemento de tres maneras:

  • Cuando un elemento es el elemento raíz de un documento (el elemento <html>).
  • Cuando un elemento tiene un valor de posición que no sea “static” y valor de z-index distinto a “auto”.
  • Cuando un elemento tiene un valor de opacidad menor que uno.

La primera y la segunda manera de formar contextos de apilamiento tienen mucho sentido y son generalmente bien comprendidos por los desarrolladores web (incluso si no conocen el propio concepto de contexto de apilamiento).

La tercera manera (opacidad) es apenas mencionada fuera de los documentos de especificación de la W3C.

Determinando una posición de elemento en el orden de apilamiento

Actualmente, determinar el orden de apilamiento global para todos los elementos de una página (incluyendo bordes, fondos, capas de texto, etc.) es extremadamente complicado y se aleja del propósito de este artículo (de nuevo, hago referencia a la especificación). Pero para nuestro propósito, una comprensión básica del orden puede ayudar a desarrollar un CSS predecible. Así que vamos a dividir el orden en individuales contextos de apilamiento:

Orden de apilamiento dentro del mismo contexto de apilamiento

Aquí están las reglas básicas de orden de apilamiento dentro de un contexto simple (desde el fondo hacia delante):

  1. El contexto de apilamiento del elemento raíz.
  2. Elementos posicionados (y sus hijos) con valores z-index negativos (los valores más altos son apilados delante de valores menores; elementos con el mismo valor son apilados de acuerdo a su aparición en el HTML).
  3. Elementos no posicionados (ordenados por aparición en el HTML).
  4. Elementos posicionados (y sus hijos) con un valor z-index de auto (ordenados por aparición en el HTML).
  5. Elementos posicionados (y sus hijos) con valores z-index positivos (los valores mayores son apilados delante de valores menores; elementos con el mismo valor son apilados de acuerdo a su aparición en el HTML.
Nota: elementos posicionados con z-indexes negativos se ordenan primero dentro de un contexto de apilamiento, lo cual significa que aparecen detrás de otros elementos. A causa de ésto, llega a ser posible para un elemento aparecer detrás de su propio padre, lo cual normalmente no es posible. Esto funcionará solamente si el elemento parental está en el mismo contexto de apilamiento y no es el elemento raíz de ese contexto de apilamiento. Un gran ejemplo de ésto son los drop-shadows con CSS y sin imágenes de Nicolas Gallagher.

Orden de apilamiento global

Con una firme comprensión de cómo funcionan los contextos de apilamiento, y de cómo funciona el orden de apilamiento dentro de un contexto de apilamiento, imaginar dónde un elemento particular podrá aparecer en el orden de apilamiento global se convierte en una tarea más sencilla.

La clave es evitar la confusión generada cuando se forman nuevos contextos de apilamiento. Si pones un z-index con un valor de mil millones en un elemento y no se mueve hacia delante en el orden de apilamiento, echa un vistazo a su ancestro y mira si alguno de sus padres está formando contextos de apilamiento. Si lo hacen, tu z-index es inútil y no hace nada bueno.

En conclusión

Volvamos al problema original, donde recreé la estructura HTML añadiendo comentarios dentro de cada etiqueta indicado su lugar en el orden de apilamiento.

<div><!-- 1 -->
<span class="rojo"><!-- 6 --></span>
</div>
<div><!-- 2 -->
<span class="verde"><!-- 4 --><span>
</div>
<div><!-- 3 -->
<span class="azul"><!-- 5 --></span>
</div>

Cuando añadimos la regla de opacidad al primer <div>, el orden de apilamiento cambia así:

<div><!-- 1 -->
<span class="rojo"><!-- 1.1 --></span>
</div>
<div><!-- 2 -->
<span class="verde"><!-- 4 --><span>
</div>
<div><!-- 3 -->
<span class="azul"><!-- 5 --></span>
</div>

"span.rojo" solía ser 6, pero cambió a 1.1. Se muestra que un nuevo contexto de apilamiento ha sido creado, y "span.rojo" es ahora el primer elemento dentro de ese nuevo contexto.

Espero que ahora esté un poco más claro por qué la caja roja se movió detrás de las otras cajas. El ejemplo original contenía solo dos contextos de apilamiento, el raíz y el formado por "span.rojo" Cuando hemos añadido opacidad al elemento padre de "span.rojo", hemos formado un tercer contexto de apilamiento, y, como resultado, el valor z-index de "span.rojo" solo se aplicaba dentro de ese nuevo contexto. Como el primer <div> (el único al que le aplicamos opacidad) y sus elementos hermanos no tienen asignados ni posición ni valor de z-index, su orden de apilamiento es determinado por su orden de aparición en el HTML, lo cual significa que el primer <div>, y todos los elementos contenidos dentro de su contexto de apilamiento son renderizados detrás del segundo y el tercer <div>.

Recursos adicionales (en inglés)

Philip Walton

Autor

OldMith

Desarrollador Web. jQuery. Responsive Design. Wordpress. Friki por naturaleza.

Compartir

Comentarios

Eduardo

13/5/2013
Solución errónea
La solución CSS de la opacidad que dais creo que está mal. Escribís lo siguiente:

div:first-child {
opacity: .99;
}

Con esta solución se aplica la regla al primer elemento hijo de cada elemento div.
La solución sería por ejemplo:

<div id="primero">
<span class="red">Red</span>
</div>

div#primero {
opacity: .99;
}

Javier

13/5/2013
Está bien!!
Eduardo, la solución está bien. Lo que tu mencionas, solo sucedería si la regla fuese así:

div :first-child{}

Como te das cuenta con el espacio entre div y :first-child, ahí si se aplicaría. De lo contrario, tal como está, se aplica al 1er hijo de tipo div que esté dentro del body. En este caso específico al div que contiene al span con clase red.

Eduardo

14/5/2013
Es cierto, está bien
Llevás toda la razón Javier. El código del artículo está bien. Fallo mío.

Ale Prieto

03/6/2013
Zarpado!
¡genial!<br />
<br />
entendí por qué hacía lo que quería una parte de mi código, jaja<br />
<br />
¡gracias!

MrCotelandia

09/6/2013
O.o
No entendí :/

Izarkc2013

26/6/2013
:O
:O, genial muchas gracias por la info.

Antonio

08/8/2013
Muy buen articulo
Wow me costó trabajo comprenderlo pero al final lo he captado. buen articulo.

jamal_dt

29/8/2013
no funciona!!
al agregar el opacity no cambio la representación del cuadro rojo!!!

jamal_dt

29/8/2013
estaba equivocado
la solucion esta.. yo tenia en el html antes de la declaracion de los div un <h1>

Some Guy

13/10/2014
Contenido copiado
Si usas contenido de otra persona, al menos, referéncialo...
http://philipwalton.com/articles/what-no-one-told-you-about-z-index/

JOSE LUIS BARRERA

17/10/2014
UNA PREGUNTA
SE PUEDE PONER CON ESTE CODIGO UNA IMAGEN .PNG SOBRE UNA ANIMACION .SWF?

Maica

20/7/2015
Muy buena traducción
Muchísimas gracias por la perfecta trraducción al castellano