Planteamiento y contexto aplicable
Una página de producto tiene un encabezado sticky y una tarjeta que contiene un botón de menú. El menú está posicionado de forma absoluta y tiene asignado un valor de apilamiento extremadamente grande. Cuando se abre cerca de la parte superior de la página, el encabezado aún se dibuja sobre él. Cuando se extiende más allá de la tarjeta, su parte inferior desaparece. Un compañero de equipo propone añadir otro cero al valor de apilamiento.
Explica por qué esa propuesta no puede solucionar ambos síntomas. Diagnostica el siguiente ejemplo, distingue el orden de dibujo (paint order) del recorte (clipping) y de la geometría de posicionamiento, y elige una reparación duradera para tres casos: un menú que puede permanecer dentro de su componente, un overlay que debe salir del componente y una interfaz de usuario cuya semántica requiere un cuadro de diálogo modal o popover.
<header class="site-header">Navigation</header>
<main class="page">
<section class="card">
<button type="button">Open menu</button>
<div class="menu">Menu items</div>
</section>
</main>.site-header {
position: sticky;
top: 0;
z-index: 4;
}
.page {
position: relative;
z-index: 1;
}
.card {
position: relative;
overflow: hidden;
transform: translateZ(0);
}
.menu {
position: absolute;
inset-block-start: 100%;
z-index: 999999;
}Asume navegadores evergreen actuales. La respuesta debe preservar el comportamiento deliberado de desplazamiento, contención y accesibilidad en lugar de limitarse a borrar CSS hasta que la captura de pantalla se vea correcta.
Qué evalúa el entrevistador
La primera señal es si el candidato conoce el límite de comparación. Un contexto de apilamiento se dibuja como una sola unidad atómica en su contexto padre. Los descendientes pueden reordenarse dentro de esa unidad, pero ningún valor de apilamiento descendiente puede elevar la unidad entera por encima de un hermano de su padre. En este caso, el valor grande del menú gana dentro de .page; .page como un todo sigue participando en el nivel 1, por debajo del encabezado en el nivel 4.
La segunda señal es la clasificación. “Detrás del encabezado” es un problema de orden de dibujo. “Cortado en el borde de la tarjeta” es un problema de recorte causado por overflow: hidden. Una tercera clase a menudo se ve similar: un ancestro con transform establece un bloque contenedor para descendientes con posicionamiento absoluto y fijo, por lo que las coordenadas o el comportamiento fijo pueden ser relativos a un ancestro inesperado. Un único valor de apilamiento no repara el recorte ni la geometría.
La tercera señal es el conocimiento de los creadores de contexto sin convertir la respuesta en una lista memorizada. Las causas comunes incluyen un elemento posicionado con un valor de apilamiento distinto de auto, posicionamiento fijo o sticky, opacidad inferior a uno, transformaciones, filtros, perspectiva, recorte o enmascaramiento, modos de mezcla (blending), isolation: isolate, contención de diseño o pintura, container queries e hijos flex o grid con un valor de apilamiento distinto de auto. La habilidad útil es encontrar el primer límite relevante en cada cadena de ancestros.
La cuarta señal es el criterio de reparación. Eliminar un contexto accidental, cambiar el nivel del ancestro correcto, cambiar el elemento padre (reparenting) de un overlay y usar la top layer del navegador resuelven contratos diferentes. Un portal cambia la ascendencia en el DOM y puede escapar del recorte del componente, pero no ingresa automáticamente a la top layer ni añade comportamiento modal, de foco, de descarte o de nombre accesible.
La señal final es la verificación. La inspección en DevTools debe respaldarse con una prueba observable de superposición, desplazamiento, comprobaciones de cambio de tamaño y zoom, uso del teclado y coexistencia con otros overlays. Una solución está incompleta si gana una captura de pantalla pero rompe un contenedor con scroll, se ancla al sistema de coordenadas incorrecto o permite que un menú ordinario cubra un modal real.
Preguntas para clarificar antes de responder
- ¿Qué píxeles son incorrectos? ¿El menú se dibuja debajo de otro elemento, se recorta en un límite, se posiciona en las coordenadas incorrectas o queda cubierto por un elemento de la top layer? Cada uno requiere soluciones distintas.
- ¿Qué ancestros crean contextos? Inspecciona tanto la cadena del menú como la cadena del elemento que lo cubre. La comparación decisiva ocurre donde las cadenas se convierten por primera vez en hermanas, no necesariamente en los dos elementos visibles.
- ¿El recorte es intencional? Una tarjeta puede recortar contenido multimedia redondeado y un contenedor de desplazamiento puede necesitar comportamiento de desbordamiento (overflow). Quitarlo globalmente puede arreglar el menú pero romper el diseño y el scroll.
- ¿A qué debe anclarse el overlay? Mover el elemento a una raíz de overlay compartida significa que sus coordenadas deben medirse en relación con esa raíz y actualizarse con el desplazamiento, cambio de tamaño, zoom y cambios de diseño.
- ¿La interfaz necesita semántica de top layer? Un cuadro de diálogo modal debe bloquear la interacción exterior y gestionar el foco. Un menú liviano puede requerir solo una raíz de overlay. Un popover adecuado puede usar el comportamiento de popover de la plataforma y su ubicación en la top layer.
- ¿Qué overlays pueden coexistir? Define si los menús, tooltips, navegación sticky, drawers y modales pueden superponerse. Un conjunto pequeño de capas con nombre es más fácil de razonar que valores arbitrarios de seis dígitos.
- ¿Qué contratos de navegador y de componentes aplican? Se pueden preferir las primitivas actuales de la plataforma; un webview más antiguo o una primitiva establecida de un sistema de diseño pueden cambiar la elección de implementación.
Estructura de respuesta en 30 segundos
“Clasificaría el fallo antes de cambiar valores. El 999999 del menú se compara solo dentro de su propio contexto de apilamiento. Su ancestro .page está en el nivel 1, por lo que todo el subárbol permanece por debajo del encabezado sticky en el nivel 4. Por separado, el desbordamiento oculto de la tarjeta recorta el menú, y su transform puede cambiar el bloque contenedor del menú. Inspeccionaría las cadenas de ancestros y el elemento real superior en el punto de superposición. Si el menú puede permanecer local, eliminaría un contexto accidental o elevaría el ancestro correcto y manejaría el recorte localmente. Si debe escapar, lo renderizaría en una raíz de overlay compartida y recalcularía la geometría de anclaje. Si la interacción es un modal o un popover adecuado, usaría la top layer del navegador e implementaría el contrato requerido de foco y descarte. Luego probaría el desplazamiento, cambio de tamaño, zoom, interacción con el teclado y coexistencia con otros overlays.”
Análisis detallado paso a paso
Comienza separando los cuatro mecanismos. Pueden fallar juntos, pero no son intercambiables:
PAINT ORDER
Which painted box is above another?
Controlled by the stacking-context tree and paint order inside each context.
CLIPPING
Which pixels are allowed to appear?
Controlled by overflow, clip paths, masks, and paint containment.
GEOMETRY
Which box supplies the coordinate system?
A transform can establish the containing block for absolute and fixed descendants.
TOP LAYER
Is the element outside normal document stacking?
Modal dialogs and shown popovers can be placed in the browser-managed top layer.Construye el árbol de contextos para el ejemplo en lugar de comparar los dos números visibles. El encabezado sticky crea un contexto en el nivel 4. El .page posicionado crea un contexto hermano en el nivel 1. El transform de la tarjeta crea otro contexto dentro de .page. El valor grande del menú solo lo ordena dentro de ese subárbol. Conceptualmente, la comparación es header: 4 contra page: 1; nunca es header: 4 contra menu: 999999.
A continuación, inspecciona el recorte de forma independiente. El menú es descendiente de .card, por lo que los píxeles fuera del padding box de la tarjeta son recortados por el desbordamiento oculto. El apilamiento cambia el orden de dibujo; no expande una región de recorte. Elevar .page, eliminar el transform o cambiar el valor del menú puede colocarlo por encima del encabezado y aun así dejar su parte inferior cortada.
Luego, inspecciona el bloque contenedor. El menú es absoluto, por lo que la tarjeta posicionada ya proporciona sus coordenadas. El transform es redundante para este menú en particular, pero se vuelve importante si una implementación futura cambia el menú a position: fixed: un ancestro transformado puede hacer que ese elemento “fijo” se comporte de forma relativa al ancestro en lugar del viewport. Por esto, cambiar de absoluto a fijo no es una vía de escape confiable.
Utiliza la inspección del navegador para confirmar la hipótesis en lugar de adivinar. Inspecciona los estilos computados en cada ancestro hasta encontrar el primer creador de contexto y el ancestro que recorta. Compara las cadenas de contexto del menú y del encabezado. En una coordenada donde se superpongan, consulta qué considera la prueba de impacto (hit testing) como el elemento superior:
function inspectOverlap(x, y) {
const stack = document.elementsFromPoint(x, y)
return stack.map((element) => ({
element,
position: getComputedStyle(element).position,
zIndex: getComputedStyle(element).zIndex,
overflow: getComputedStyle(element).overflow,
transform: getComputedStyle(element).transform,
}))
}elementsFromPoint() es conveniente para ver la pila de hit testing; elementFromPoint() devuelve solo el elemento elegible superior. El hit testing es evidencia de apoyo, no un depurador completo de contextos de apilamiento: los elementos con pointer-events: none pueden ser omitidos, y el recorte o la geometría fuera de pantalla aún requieren inspección de diseño.
Elige la reparación más pequeña que coincida con el límite de propiedad.
Para un menú local, elimina un contexto accidental si no tiene un efecto visual o de contención requerido. Si la página realmente necesita su contexto, compara y cambia el token a nivel de página porque ese es el hermano que se está ordenando contra el encabezado. Si el recorte de la tarjeta es solo para contenido multimedia, coloca ese recorte en un contenedor multimedia anidado en lugar de toda la tarjeta interactiva.
:root {
--layer-content: 0;
--layer-sticky: 20;
--layer-overlay: 40;
}
.page {
position: relative;
z-index: var(--layer-content);
}
.site-header {
z-index: var(--layer-sticky);
}
.card-media {
overflow: hidden;
border-radius: 1rem;
}
.menu {
z-index: var(--layer-overlay);
}Esto solo funciona cuando el contrato previsto permite que el subárbol de la página permanezca por debajo del encabezado y el menú no necesita cruzar ese límite. Si el menú debe cubrir el encabezado, la decisión de capas pertenece a un ancestro común o el overlay debe moverse a una raíz compartida.
Para un overlay reutilizable que deba escapar de contextos locales y recorte, renderízalo bajo una raíz de overlay compartida cerca de la raíz del documento. Conserva el disparador (trigger) como su propietario lógico, mantén las relaciones de accesibilidad y calcula el panel a partir del rectángulo del disparador. Recalcula cuando el desplazamiento, el cambio de tamaño, el zoom o los cambios de diseño alteren cualquiera de los rectángulos. El manejo de colisiones debe tener en cuenta los bordes del viewport y la dirección de escritura. Cambiar de padre sin gestionar la geometría cambia un error de recorte por un error de menú desalineado.
Para un cuadro de diálogo modal real, prefiere el elemento dialog nativo abierto de forma modal o una primitiva accesible consolidada que proporcione el mismo contrato. Para una interacción no modal adecuada, la API popover puede colocar el contenido mostrado en la top layer. Las entradas de la top layer se dibujan por encima de los contextos ordinarios del documento; las entradas posteriores quedan por encima de las anteriores, y cada una tiene su propio contexto raíz. El desbordamiento, la opacidad, las máscaras y las transformaciones de los ancestros no arrastran a un elemento de la top layer de vuelta al contexto de documento de su ancestro.
La top layer no es un “z-index más fuerte” universal. Es una capa ordenada administrada por el navegador con mecanismos semánticos de entrada y salida. Un modal aún necesita un nombre útil, foco inicial deliberado, interacción contenida, política de descarte y restauración de foco. Un menú aún necesita la semántica correcta de menú o divulgación y comportamiento de teclado. Elegir la capa nunca reemplaza la elección del modelo de interacción.
Valida la solución como una matriz:
- Ábrelo cerca de cada borde del viewport y dentro de contenedores de desplazamiento anidados.
- Desplaza la página y el contenedor más cercano; verifica que el panel permanezca anclado o se cierre según la política definida.
- Cambia el tamaño, aplica un zoom al 200% y prueba diseños estrechos y anchos.
- Navega con el teclado; verifica que el orden, el foco visible, la tecla Escape y el retorno del foco coincidan con el componente.
- Ábrelo teniendo ya presentes navegación sticky, un drawer, un tooltip y un modal real.
- Comprueba el hit testing en píxeles ocultos y confirma que ningún overlay invisible intercepte controles no relacionados.
- Prueba etiquetas traducidas largas y diseños de izquierda a derecha y de derecha a izquierda cuando sean compatibles.
- Confirma que el desbordamiento deliberado, la contención, la composición y el comportamiento de desplazamiento sigan funcionando.
Ejemplo de respuesta de alta calidad
“El valor grande se está comparando en el nivel incorrecto. El menú está dentro del contexto de apilamiento de .page, que participa en el nivel 1. El encabezado es un contexto hermano en el nivel 4. El navegador dibuja el subárbol de la página atómicamente por debajo del encabezado, por lo que un valor descendiente de 999999 no puede escapar y competir con el encabezado.
También hay un fallo de recorte independiente. El desbordamiento oculto de la tarjeta elimina los píxeles fuera de su límite; el z-index cambia el orden pero no puede agrandar ese recorte. El transform de la tarjeta crea otro contexto y puede establecer un bloque contenedor para descendientes absolutos y fijos, por lo que cambiar a posicionamiento fijo aún puede usar un sistema de coordenadas inesperado.
Inspeccionaría ambas cadenas de ancestros en DevTools, identificaría los primeros creadores de contexto y de recorte, y verificaría la pila de hit testing en una coordenada de superposición. Para un menú local, eliminaría solo un contexto accidental, movería el recorte a un contenedor de medios o ajustaría la capa nombrada en el ancestro común que realmente se está comparando. No seguiría aumentando un valor hoja.
Si por contrato el menú puede cruzar los límites del componente y del encabezado, lo renderizaría en una raíz de overlay compartida. Eso significa que el sistema de overlay gestiona la medición, el manejo de colisiones y las actualizaciones al desplazarse, cambiar de tamaño, hacer zoom y cambios de diseño. Un portal ayuda con la ascendencia y el recorte, pero no crea comportamiento de top layer ni de accesibilidad.
Si la interacción es un cuadro de diálogo modal, usaría el comportamiento de modal dialog nativo o una primitiva accesible probada. Si es un popover adecuado, consideraría el comportamiento popover de la plataforma. Ambos pueden usar la top layer del navegador, pero aún así implementaría el contrato correcto de nombre accesible, foco, teclado, descarte y restauración. Finalmente, verificaría los contenedores de scroll, bordes del viewport, zoom al 200%, uso del teclado, diseños traducidos, hit testing y coexistencia con otros overlays.”
Errores comunes
- Aumentar indefinidamente un valor descendiente → permanece atrapado en un contexto padre inferior → Compara los primeros contextos hermanos y cambia al propietario de esa decisión de capa.
- Tratar cada píxel oculto como un error de apilamiento → el overflow o la contención de pintura aún lo recortan → Inspecciona los ancestros que recortan independientemente del orden de dibujo.
- Cambiar el posicionamiento absoluto a fijo → un ancestro transformado aún puede definir el bloque contenedor → Rastrea el sistema de coordenadas antes de elegir un modo de posicionamiento.
- Eliminar todas las declaraciones de transform u overflow → se rompe el contenido multimedia redondeado, el desplazamiento, la contención o el comportamiento de renderizado → Elimina solo los desencadenantes accidentales o limítalos al elemento que los necesita.
- Usar portales para enviar cada overlay al body → problemas de anclaje, colisión, propiedad y foco reemplazan al error original → Usa un sistema de overlay compartido con contratos explícitos de geometría e interacción.
- Asumir que un portal es la top layer → los contextos ordinarios del documento aún pueden cubrirlo → Usa una primitiva de top layer de la plataforma cuando su semántica sea adecuada.
- Usar un cuadro de diálogo modal para cada panel flotante → el foco y la interacción exterior quedan innecesariamente bloqueados → Elige modal, popover, menú, tooltip o divulgación en línea basándote primero en el comportamiento.
- Crear valores globales arbitrarios → los componentes entran en una competencia escalonada de capas → Usa un conjunto pequeño de capas nombradas y mantén los valores locales como locales.
- Verificar solo una captura de pantalla → el desplazamiento, el zoom, el teclado u otro overlay reintroducen el fallo → Prueba la matriz de superposición e interacción.
Preguntas de seguimiento y respuestas
¿Un z-index mayor siempre coloca un elemento por encima de uno menor?
Solo cuando los valores participan en la misma comparación de contexto de apilamiento relevante. Los descendientes se ordenan dentro de su contexto, y ese contexto completo se ordena luego en su padre. Compara cadenas de contextos, no valores calculados aislados.
¿Qué propiedades de CSS crean habitualmente un contexto de apilamiento?
La raíz, el posicionamiento fijo o sticky, los elementos posicionados con un valor de apilamiento distinto de auto, opacidad menor que uno, transformaciones, filtros, perspectiva, recorte o enmascaramiento, modos de mezcla (blending), isolation, propiedades seleccionadas de contención y container queries, e hijos flex o grid con un valor de apilamiento distinto de auto son causas comunes. La lista exacta evoluciona, por lo que la inspección debe identificar el desencadenante computado en lugar de depender únicamente de la memoria.
¿Por qué un elemento puede estar por encima de su hermano y aun así verse cortado?
El orden de dibujo decide qué píxeles permitidos se pintan al final. Una región de recorte decide qué píxeles tienen permitido existir. Ganar el orden de dibujo no puede recuperar píxeles eliminados por el overflow, clip path, máscara o contención de pintura de un ancestro.
¿position: fixed siempre escapará de un ancestro con overflow-hidden?
No. El posicionamiento fijo cambia el comportamiento normal del bloque contenedor, pero las transformaciones y propiedades relacionadas en los ancestros pueden establecer el bloque contenedor. Otros mecanismos de recorte aún pueden aplicar. Rastrea la geometría y el recorte en lugar de usar el posicionamiento fijo como una solución universal.
¿Renderizar a través de un portal resuelve todo el problema?
Puede escapar de la ascendencia en el DOM local, los contextos y el recorte si la raíz de destino está fuera de ellos. También requiere una nueva gestión de mediciones, actualizaciones de scroll, manejo de colisiones, relaciones de eventos, foco y descarte. Permanece en el apilamiento ordinario del documento a menos que la propia primitiva ingrese a la top layer.
¿En qué se diferencia la top layer de un token de capa global muy alto?
Los elementos de la top layer se dibujan en una capa ordenada administrada por el navegador por encima del contenido ordinario del documento y crean contextos de apilamiento raíz. El orden de entrada determina el orden dentro de esa capa. Un token global alto aún participa en el árbol de contextos del documento y puede permanecer atrapado por un ancestro.
¿Debería un equipo prohibir todos los valores locales de z-index?
No. Los valores locales son útiles para ordenar partes dentro de un solo componente, como una insignia (badge) sobre una imagen. Usa una escala pequeña con nombres solo en los límites compartidos, como la navegación sticky, overlays reutilizables y drawers. El límite, no la magnitud del número, determina si un valor es global.
¿Cómo evitarías esta clase de regresión?
Documenta los pocos propietarios de capas compartidas, centraliza la ubicación de overlays reutilizables y añade historias de interacción que combinen contenido sticky, contenedores de desplazamiento, menús y modales. Verifica la ubicación en los bordes, el desplazamiento, el zoom, el comportamiento del teclado y otro overlay activo; una captura estática por sí sola no puede validar el contrato.