Performance · 2 min read · 19 October 2026

iOS Safari's compositor ignores your z-index

A sticky image bled through content that should cover it, on iPhone only. Raising z-index did nothing, because it isn't a stacking problem. It's paint order between GPU layers.

A cartoon phone where a picture card wrongly overlaps the header card above it
15–18
iOS versions where my notes record the bug
1
declaration fixes it: give the neighbour its own layer
0
times isolation: isolate helped

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-cover images: -webkit-transform: translateZ(0) on the container
  • carousels and rotate-plus-scale: will-change: transform
  • always set explicit image width and height
  • transition opacity and transform, never all
  • overflow: hidden with scaled children: will-change on 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.

Next storyThe favicon OpenNext corrupted