isolation y el grupo de fusión
Contra qué se fusiona exactamente un elemento, qué es un grupo aislado, la lista de propiedades que aíslan sin que se lo pidas, y cómo acotar una fusión a lo que tú decidas.
La pregunta que mix-blend-mode deja abierta es contra qué se fusiona. La respuesta no es “contra lo que hay detrás” sino algo más acotado y más útil: contra el backdrop dentro de su grupo de aislamiento. Saber dónde están las fronteras de ese grupo, y que hay media docena de propiedades que las crean sin avisar, es la diferencia entre controlar una fusión y pelearse con ella.
- Definir grupo de aislamiento y su relación con el contexto de apilamiento.
- Acotar una fusión a un subárbol con
isolation: isolate. - Enumerar las propiedades que aíslan sin que las hayas puesto para eso.
- Diagnosticar los dos fallos clásicos de fusión.
Contra qué se fusiona un elemento
Un elemento con mix-blend-mode se fusiona con su backdrop, que es todo lo pintado por debajo de él dentro del contexto de apilamiento que lo contiene. Ese contexto actúa como un grupo aislado: el contenido del grupo se compone entre sí primero, y el resultado se compone después con lo de fuera sin fusionarse.
Dicho al revés: una fusión nunca atraviesa la frontera de un contexto de apilamiento. Un elemento con multiply dentro de un contenedor que forme contexto de apilamiento se multiplica con sus hermanos y con el fondo de ese contenedor, y no con el fondo de la página.
Ese es el mecanismo entero, y de él salen los dos problemas de siempre: la fusión llega demasiado lejos, o no llega a ninguna parte.
Acotar una fusión
isolation: isolate crea un contexto de apilamiento sin ningún otro efecto secundario. Es la herramienta para el primer problema.
<div class="tarjeta">
<img src="foto.avif" alt="">
<div class="tinte"></div>
</div>
.tarjeta {
position: relative;
isolation: isolate;
}
.tinte {
position: absolute;
inset: 0;
background: oklch(55% 0.15 264);
mix-blend-mode: color;
}
Sin isolation: isolate, el tinte se fusiona con todo lo que tenga debajo en el contexto de apilamiento vigente, que puede ser la página entera: al pasar por encima del borde de la tarjeta seguiría tiñendo lo que hubiera detrás. Con el aislamiento, la fusión se detiene en la tarjeta.
El valor por defecto es isolation: auto, que significa “aísla solo si alguna otra cosa ya lo hacía”.
Comparado con las alternativas para conseguir lo mismo, isolation es la única que no arrastra nada más. position: relative con z-index: 0 también crea contexto de apilamiento, y además cambia el posicionamiento. transform: translateZ(0) también, y además crea bloque contenedor y promociona una capa. opacity: 0.999 también, y además es una mentira que alguien borrará. isolation: isolate dice exactamente lo que hace.
Las propiedades que aíslan sin que lo pidas
El segundo problema —“he puesto multiply y no hace nada”— casi siempre es un contexto de apilamiento que alguien creó sin darse cuenta, y la lista de formas de crear uno es larga:
opacitymenor que 1transform,rotate,scale,translatedistintos denonefilter,backdrop-filtermix-blend-modedistinto denormalclip-path,maskperspective,transform-style: preserve-3dcontain: paint,contain: layoutycontent-visibilitywill-changecon cualquiera de las propiedades anterioresposition: fixed, yposition: stickyen la mayoría de los motoresposition: relativeoabsoluteconz-indexdistinto deautoisolation: isolate- Un elemento en la capa superior, como un
dialogabierto o un popover
Cualquiera de esas en un ancestro corta la fusión ahí. Las que más aparecen por accidente son transform, puesta para centrar algo o para animar, will-change, puesta por rendimiento, y contain, puesta al aislar un componente.
El diagnóstico es mecánico: sube por el árbol desde el elemento que se fusiona hasta el que quieres que sea el fondo, y comprueba en el inspector si algún ancestro intermedio tiene alguna de esas propiedades. La que encuentres es la culpable.
El caso completo
Los dos usos se combinan en el patrón que más se repite: una imagen con un tinte de marca dentro de una tarjeta con una superposición de texto que también se fusiona, pero con otro alcance.
.tarjeta {
position: relative;
isolation: isolate; /* frontera exterior de la fusion */
overflow: hidden;
border-radius: 16px;
}
.tarjeta > img {
inline-size: 100%;
block-size: 100%;
object-fit: cover;
}
.tarjeta > .tinte {
position: absolute;
inset: 0;
background: oklch(50% 0.14 264);
mix-blend-mode: color; /* se fusiona con la imagen, no con la pagina */
}
.tarjeta > .titulo {
position: relative; /* por encima del tinte, sin fusionarse */
z-index: 1;
color: #ffffff;
}
El título lleva z-index: 1 y position: relative, con lo que también crea contexto de apilamiento y por tanto queda fuera del alcance del tinte y no se tiñe. Ese detalle es el que hace que el patrón funcione, y es también la demostración de que la lista de arriba no es solo una fuente de problemas: se usa a propósito.
Poner isolation: isolate en la raíz de un componente es una buena costumbre aunque no haya ninguna fusión. Garantiza que ningún z-index de dentro pueda competir con el de fuera, con lo que el componente se puede colocar en cualquier sitio de la página sin que su ordenación interna se filtre. Cuesta una declaración y elimina una clase entera de bugs de apilamiento.
Lo que hace difícil este tema es que tres conceptos con nombres distintos —contexto de apilamiento, grupo de aislamiento y grupo de composición— resultan ser en la práctica el mismo árbol, y cada especificación lo llama por su nombre. El contexto de apilamiento viene de CSS 2 y responde a la pregunta de qué se pinta encima de qué. El grupo de aislamiento viene de la especificación de composición y responde a hasta dónde llega una fusión. El grupo de composición es el concepto del motor de renderizado y responde a qué se rasteriza en una textura aparte. Son tres preguntas independientes y sus respuestas coinciden por una razón profunda: todas se responden con la misma frontera, que es el punto donde el motor tiene que materializar un resultado intermedio. Cuando algo obliga a componer un subárbol aparte —una opacidad, un filtro, un transform en 3D—, ese resultado intermedio existe físicamente como un mapa de píxeles, y a partir de ahí las tres cosas se siguen: lo que hay dentro no puede colarse entre lo de fuera porque ya está aplanado; una fusión no puede ver más allá porque solo tiene ese mapa; y el compositor trata ese mapa como una unidad. De ahí sale la regla de diseño más útil de todo este nivel, y es una que rara vez se enuncia: cada frontera de aislamiento es también un punto de coste. Poner isolation: isolate en cien tarjetas de una lista no es gratis aunque no haya fusiones, porque cada una pasa a ser una unidad de composición. La decisión correcta no es “aislar por si acaso” sino aislar en los pocos sitios donde la frontera coincide con una frontera real del diseño: la raíz de un componente que se reutiliza, el contenedor de una superposición, el panel de un diálogo. En esos sitios la frontera ya existía conceptualmente y hacerla explícita no añade coste real, porque el motor probablemente ya iba a componer ahí. Aislar en medio de un subárbol homogéneo, en cambio, introduce una capa que no aporta ninguna semántica y sí un mapa de píxeles más.
- Tiñe una imagen con
mix-blend-mode: colorsin aislar y observa hasta dónde llega la fusión. - Añade
isolation: isolateal contenedor y comprueba que se detiene. - Pon un
transform: translateZ(0)en un ancestro y localiza dónde deja de funcionar la fusión. - Saca un título de una superposición usando solo
positionyz-index. - Recorre la lista de propiedades que aíslan y busca cuáles hay ya en tu proyecto por otras razones.