PostHole
Compose Login
You are browsing us.zone2 in read-only mode. Log in to participate.
rss-bridge 2026-02-20T15:36:55+00:00

Potentially Coming to a Browser :near() You

Danny has several ideas for how we could use :near(), a proposed pseudo-class that detects when the pointer is near an element.

Potentially Coming to a Browser :near() You originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


pseudo classes

Potentially Coming to a Browser :near() You

Daniel Schwarz

Feb 20, 2026

Just before we wrapped up 2025, I saw this proposal for :near(), a pseudo-class that would match if the pointer were to go near the element. By how much? Well, that would depend on the value of the <length> argument provided. Thomas Walichiewicz, who proposed :near(), suggests that it works like this:

button:near(3rem) {
/* Pointer is within 3rem of the button */

For those wondering, yes, we can use the Pythagorean theorem to measure the straight-line distance between two elements using JavaScript (“Euclidean distance” is the mathematical term), so I imagine that that’s what would be used behind the scenes here. I have some use cases to share with you, but the demos will only be simulating :near() since it’s obviously not supported in any web browser. Shall we dig in?

Visual effects

Without question, :near() could be used for a near-infinite (sorry) number of visual effects:

div {
/* Div is wow */

&:near(3rem) {
/* Div be wowzer */

&:near(1rem) {
/* Div be woahhhh */

Dim elements until :near()

To reduce visual clutter, you might want to dim certain components until users are near them. :near() could be more effective than :hover in this scenario because users could have trouble interacting with the components if they have limited visibility, and so being able to trigger them “earlier” could compensate for that to some degree. However, we have to ensure accessible color contrast, so I’m not sure how useful :near() can be in this situation.

button:not(:near(3rem)) {
opacity: 70%; /* Or...something */

Hide elements until :near()

In addition to dimming components, we could also hide components (as long as they’re not important, that is). This, I think, is a better use case for :near(), as we wouldn’t have to worry about color contrast, although it does come with a different accessibility challenge.

And here’s how :near() can enhance it. People know or suspect that the button’s there, right? Probably in the bottom-right corner? They know roughly where to click, but don’t know exactly where, as they don’t know the size or offset of the button. Well, showing the button when :near() means that they don’t have to hover so accurately to make the button appear. This scenario is pretty similar to the one above, perhaps with different reasons for the reduced visibility.

However, we need this button to be accessible (hoverable, focusable, and find-in-pageable). For that to happen, we can’t use:

  • (not hoverable, focusable, or find-in-pageable)
  • (also not hoverable, focusable, or find-in-page-able)
  • opacity: 0 (there’s no way to show it once it’s been found by find-in-page)

That leaves us with , but the problem with hiding content using (or elements with display: none) is that they literally disappear, and you can’t be near what simply isn’t there. This means that we need to reserve space for it, even if we don’t know how much space.

Now, :near() isn’t supported in any web browser, so in the demo below, I’ve wrapped the button in a container with 3rem of padding, and while that container is being :hovered, the button is shown. This increases the size of the hoverable region (which I’ve made red, so that you can see it) instead of the actual button. It essentially simulates button:near(3rem).

But how do we hide something while reserving the space?

First, we declare contain-intrinsic-size: auto none on the hidden target. This ensures that it remains a specific size even as something changes (in this case, even as its content is hidden). You can specify a <length> for either value, but in this case auto means whatever the rendered size was. none, which is a required fallback value, can also be a <length>, but we don’t need that at all, hence “none.”

The problem is, the rendered size “was” nothing, because the button is content-visibility: hidden, remember? That means we need to render it if only for a single millisecond, and that’s what this animation does:

<div id="image">
<div id="simulate-near">
<button hidden="until-found">Share</button>
</div>
</div>
@keyframes show-content {
from {
content-visibility: visible;

button {
/* Hide it by default */
&:not([hidden="until-found"]) {
content-visibility: hidden;

/* But make it visible for 1ms */
animation: 1ms show-content;

/* Save the size while visible */
contain-intrinsic-size: auto none;

Note that if the button has the hidden=until-found attribute-value, which is what makes it focusable and find-in-page-able, content-visibility: hidden isn’t declared because hidden=until-found does that automatically. Either way, the animation declares content-visibility: visible for 1ms while contain-intrinsic-size: auto none captures its size and reserves the space, enabling us to hover it even when it’s not visible.

Now that you understand how it works, here’s the full code (again, simulated, because :near() isn’t supported yet):

<div id="image">
<div id="simulate-near">
<button hidden="until-found">Share</button>
</div>
</div>
@keyframes show-content {
from {
content-visibility: visible;

#simulate-near {
/* Instead of :near(3rem) */
padding: 3rem;

button {
/* Unset any styles */
border: unset;
background: unset;

/* But include size-related styles */
padding: 1rem;

/* Hide it by default */
&:not([hidden="until-found"]) {
content-visibility: hidden;

/* But make it visible for 1ms */
animation: 1ms show-content;

/* Save the size while visible */
contain-intrinsic-size: auto none;

&:where(:hover, :has(:focus-visible)) button {
color: white;
background: black;
content-visibility: visible;

If you’re wondering why we’re unsetting border and background, it’s because content-visibility: hidden only hides the content, not the element itself, but we’ve included padding here because that affects the size that we’re trying to render n’ remember. After that we simply apply those styles as well as content-visibility: visible to the button when the the wrapper is :hovered or :has(:focus-visible).

And here’s the same thing but with the unsupported :near():

<div id="image">
<button hidden="until-found">Share</button>
</div>
@keyframes show-content {
from {
content-visibility: visible;

button {
/* Unset any styles */
border: unset;
background: unset;

/* But include size-related styles */
padding: 1rem;

/* Hide it by default */
&:not([hidden="until-found"]) {
content-visibility: hidden;

/* But make it visible for 1ms */
animation: 1ms show-content;

/* Save the size while visible */
contain-intrinsic-size: auto none;

&:where(:near(3rem), :hover, :focus-visible) {
color: white;
background: black;
content-visibility: visible;

In short, :near() enables us to do what the simulated technique does but without the extra markup and creative selectors, and if there are any accessibility needs, we have that animation/contain-intrinsic-size trick.

Prefetch/prerender when near

[...]


Original source

Reply