Elements overlapping on your website: what’s actually causing it

Two elements sitting on top of each other. You can see it. Your visitors can see it. And you’ve tried everything you can find.

This is one of the failures that doesn’t just make you question your CSS. It makes you question your ability to Google a CSS problem. That’s supposed to be the fallback, the thing that saves you when you don’t know the answer: search for it, find someone who figured it out, apply their fix. But with overlapping elements, the fixes you find are almost never written for your specific setup. What worked on someone else’s clean install, without 12 plugins, without a page builder generating its own CSS, without a theme and an app and a script all competing over the same z-index values, that fix doesn’t translate. And when the most fundamental troubleshooting skill you have stops working, the frustration is a different kind of stuck.

Here’s what’s actually happening, and why the obvious approach keeps not working.

The element you clicked isn’t always the one causing the overlap

Most people start by changing the z-index of the element on top. That doesn’t work, or it works on desktop and breaks on mobile, or it works on that element and something else shifts. The problem isn’t that you set the wrong number. The problem is that you’re changing a property on the wrong element, because you don’t know yet which element in the stack is actually responsible.

CSS has a concept called a stacking context. Every element on a page sits inside one, and stacking contexts nest inside each other in a hierarchy that doesn’t always match the visual structure you see. When two elements overlap, your instinct is to find the one on top and push it back, or find the one underneath and bring it forward. But z-index only works within the same stacking context. If those two elements are in different stacking contexts, changing the z-index of either one does nothing.

What creates a stacking context? A transform property. An opacity below 1. A filter. A position value combined with a z-index. Certain newer CSS properties like isolation and will-change. Any of these on a parent element creates a new stacking context for everything inside it, and that boundary is invisible unless you know to look for it.

So the element you’re trying to reorder might be perfectly positioned. The constraint is two or three levels up, on a parent you didn’t know to check, set by a rule that came from your theme, a plugin, or a script that injected it after the page loaded. You won’t find it by looking at the element. You’ll find it by reading the full ancestor chain.

The overlap you can see isn’t always what the browser recorded

There’s a second problem that makes this worse: some overlaps are created by JavaScript after the page loads. A sticky header, a chat widget, a cookie banner, a popup, a review app. These inject their own z-index values at runtime, after the initial CSS renders. By the time you open the inspector and click on the overlapping element, the DOM is in a different state than it was when the overlap first appeared. You see the result. You don’t necessarily see the rule that caused it, because that rule was set by a script that fired once, at a specific moment, under conditions you can’t easily reproduce.

This is why the advice “just open DevTools and look at the z-index” doesn’t always work. The inspector shows you the current state. The overlap you’re chasing might have been set by a script that’s since changed what it injected. And a stranger on a forum doesn’t know you have that script, any more than they know which other plugins are competing with it.

Where overlapping elements actually come from

There are four patterns that cause most overlapping element problems on sites managed by people who didn’t write the theme or the plugins.

A z-index set by an app or plugin. Review widgets, chat tools, sticky bars, and popup managers all inject CSS at runtime. They pick z-index values high enough to stay on top of your theme, but sometimes high enough to cover elements they weren’t supposed to. That z-index isn’t in your stylesheet. It’s not in your theme settings. It’s in a script you didn’t write.

A position conflict between a fixed or sticky element and the content beneath it. A fixed navigation header creates a blind zone at the top of every scroll position. If your content starts at the wrong offset, it slides under the header when the page scrolls. The fix isn’t on the content. It’s on the scroll offset, or on the header’s stacking context, or on the container that positions everything below it.

Absolute positioning without a positioned ancestor. An element set to position: absolute calculates its position relative to its nearest ancestor that has any position value other than static. If there isn’t one, it positions itself relative to the whole page. Move that element somewhere else in the DOM, or change a parent’s position value, and the absolutely positioned element jumps somewhere completely different, possibly onto something it was never supposed to touch.

A stacking context created by an animation or transform. Adding a CSS animation, a transition, a transform, or a filter to an element creates a new stacking context for everything inside it. If your theme or a plugin adds these for performance or visual effect, it can silently restructure the z-index relationships of everything nested inside that element, even if those nested elements didn’t change.

What you actually need to diagnose it

To find the real cause of an overlap, you need three things that DevTools doesn’t show you in one place.

You need to know every CSS rule applying to the overlapping element, including the ones that lost. The winning rule is visible. The losing rules, and the reason they lost, usually aren’t unless you know exactly where to look.

You need to know the full ancestor chain: which ancestors have a position value, which have a z-index set, and which are creating their own stacking contexts through transforms, opacity, filters, or other properties. The overlap is often caused by an ancestor you never clicked on.

You need to know where each rule came from: your theme, your page builder, an app, an inline style, or a JavaScript injection. “Your element has z-index: 9999” is not actionable. “Your element has z-index: 9999 set by an inline style injected by your chat widget, and the element underneath it has a stacking context boundary from a transform on its parent” is actionable. One tells you what to change. The other tells you to keep guessing.

If navigating all of that manually is the part where you stall, Loupely Lens captures it in one click. Click on either overlapping element, and Lens reads the full CSS picture: the winning rules, the losing rules, the origin of each one, the complete ancestor chain with every stacking context identified, and any competing elements detected automatically. The diagnosis shows up in the popup before anything downloads.

Elements overlapping on website: if you can see it, the evidence is there

An overlapping element isn’t a mystery. The browser knows exactly why it’s happening. Every rule, every ancestor constraint, every stacking context boundary is recorded and accessible. The problem isn’t that the information doesn’t exist. The problem is that DevTools was built for people who already know what they’re looking for, and when you don’t, even the most basic troubleshooting instinct, searching for an answer, stops feeling reliable.

The fix you found online probably wasn’t wrong. It just wasn’t written for your stack. The answer to your overlap is specific to your element, your ancestor chain, and which of your plugins or scripts is setting the z-index that’s winning. Open the inspector, click on the overlapping element, and look for any ancestor that has a transform, opacity, or filter set. That’s where the stacking context is. That’s almost always where the answer is.

Similar Posts