01 · The bug
Desktop was fine. Chrome was fine. The iPhone wasn't.
On a client site with a sticky side image, the content scrolling over it had a higher z-index and should have covered it. On iOS Safari the image bled through anyway.
02 · Why z-index can't fix it
Safari promotes sticky elements to their own GPU layer.
Between a composited element and a non-composited sibling, it doesn't honour z-index. So it's a compositor paint-order bug, not a stacking-context bug, which is why isolation: isolate does nothing. That fixes stacking contexts, and this isn't one.
03 · The fix
Put the neighbour in a layer too.
The content wrapper already had position: relative and a z-index. Adding a 3D transform composites it, and the two layers paint in the right order. will-change: transform also works, but less consistently on older Safari.
.content {
position: relative;
z-index: 2;
/* composite this layer too, so iOS Safari paints it above the sticky image */
transform: translate3d(0, 0, 0);
}04 · The minifier gotcha
Your build can undo the fix.
Some minifiers rewrite translate3d(0,0,0) to a 2D translate(0), which doesn't create a layer. In Tailwind, write it as an arbitrary property (transform:translate3d(0,0,0) inside square brackets) so it survives, and check the compiled CSS, not just the source.
05 · A different bug that looks similar
Flickering or smeared images are a separate class.
- sticky container with
object-coverimages:-webkit-transform: translateZ(0)on the container - carousels and rotate-plus-scale:
will-change: transform - always set explicit image width and height
- transition
opacityandtransform, neverall overflow: hiddenwith scaled children:will-changeon the parent
Work out which class you have before changing CSS.
06 · Verify on a real iPhone
Emulation won't show you this.
Test on real iOS Safari. My notes say later versions fixed it internally, so check on the devices your users actually have instead of assuming either way.
