Why is my z-index not working?
z-index only applies to positioned elements and flex/grid items. If it is set and still ignored, an ancestor has created a stacking context that the element cannot escape.
#Cause 1: the element is static
.tooltip {
z-index: 999; /* ignored */
}z-index needs position: relative, absolute, fixed or sticky — or the element must be a flex or grid item.
.tooltip {
position: relative;
z-index: 999; /* now it applies */
}#Cause 2: a stacking context is trapping it
This is the one that wastes an afternoon. A child can never escape its parent's stacking context, no matter how large its z-index is:
.card { position: relative; z-index: 1; }
.modal { position: fixed; z-index: 9999; } /* inside .card */The modal sits above everything inside .card, but .card as a whole is still at layer 1. Any sibling with z-index: 2 covers it.
#What creates a stacking context
Far more than people expect:
positionother than static, with az-indexthat is notautoopacityless than 1transform,filter,backdrop-filter,perspectivewill-changenaming any of the aboveisolation: isolatemix-blend-modeother than normalcontain: paint
An opacity: 0.99 added for a fade is enough to trap every descendant.
#How to debug it
In Chrome DevTools, the Layers panel shows the real stacking order. Or walk up the DOM from your element and check each ancestor for the properties above.
#How to fix it
Move the element out of the trapping ancestor — usually to a direct child of <body>. This is why modal libraries portal their markup to the end of the document.
Remove the context if it was accidental, such as an unnecessary opacity: 0.999.
Use <dialog> for modals. It renders in the browser's top layer, above every stacking context, and gives you focus trapping and Escape-to-close for free.