When the mouse hovers over an element on an HTML page, a :hover
pseudo-class is generated by the browser for that specific element as well as all its parent elements, regardless of whether the mouse is directly over any part of the parent element itself.
This behavior can be observed in the example below. In the "Two Nested Divs" scenario, hovering over the blue square will trigger a :hover
pseudo-class for both the blue and its parent red square.
Is there a CSS selector available that can respond to the mouse only when it's directly over the red square?
In order to achieve this, I have demonstrated a workaround using the Three Overlapping Divs illustration. Here, the red and blue squares are positioned as siblings with an invisible square div at their intersection. This hidden square intercepts the pointer-event causing the creation of a :hover
pseudo-class. This approach allows me to write CSS as if a :hover
pseudo-class exists for both visible squares without any inheritance issues.
However, with the Three Overlapping Divs scenario, the blue square is no longer considered a child of the red square. In my real-life scenario, maintaining this hierarchical relationship is crucial.
:root {
--line: 1px;
--side: 1vmin;
--size: max(min(100vh, 50vw), min(50vh, 100vw));
--font: calc(var(--size) / 20);
--rect: calc(var(--size) * 0.666 - var(--line));
--drop: calc(var(--rect) * 0.5);
--edge: calc(var(--drop) - var(--side));
--red: #9009;
--blue: #0099;
--bg: #040;
}
body {
margin: 0;
height: 100vh;
color: #ddd;
background-color: #222;
font-size: 0;
display: flex;
justify-content: center;
align-items: center;
}
@media (aspect-ratio < 1/1) {
main {
width: var(--size);
}
}
div {
position: relative;
box-sizing: border-box;
}
div[id] {
font-size: var(--font);
display: inline-block;
width: var(--size);
height: var(--size);
border: var(--line) solid #ddd;
p {
position: absolute;
pointer-events: none;
z-index: 1;
span {
display: block;
width: var(--rect);
margin-top: 0.5em;
font-size: 0.7em;
}
}
div.red,
div.blue,
div.overlap {
width: var(--rect);
height: var(--rect);
border: var(--side) solid red;
}
div.red:hover {
background-color: var(--red);
}
div.blue {
border-color: blue;
&:hover {
background-color: var(--blue);
}
}
&#two {
background-color: var(--bg);
.blue {
top: var(--edge);
left: var(--edge);
}
}
&#three {
.blue {
position: absolute;
top: var(--drop);
left: var(--drop);
}
.overlap {
position: absolute;
top: var(--drop);
left: var(--drop);
width: var(--drop);
height: var(--drop);
border-color: transparent;
}
}
}
#three:has(.overlap:hover) {
.red {
background-color: var(--red);
}
.blue {
background-color: var(--blue);
}
}
<div id="two">
<p>Two nested divs
<span>Hover over the red square to highlight it</span>
<span>Hovering over the blue square also highlights its parent red square, even without touching the intersecting area</span>
</p>
<div class="red">
<div class="blue"></div>
</div>
</div>
<div id="three">
<p>Three overlapping divs<span>Hover over either square to highlight it</span><span>Hover over the intersection to simultaneously highlight both squares</span></p>
<div class="red"></div>
<div class="blue"></div>
<div class="overlap"></div>
</div>