A recent proposal introduces `:near()`, a CSS pseudo-class designed to match an element when the user’s pointer is within a specified distance. The exact distance would be determined by a <length> argument. Thomas Walichiewicz, the proposer of `:near()`, illustrates its functionality as follows:
button:near(3rem) {
/* Pointer is within 3rem of the button */
}
The underlying mechanism for `:near()` would likely involve calculating the Euclidean distance between the pointer and an element, similar to how it can be done with JavaScript using the Pythagorean theorem. This article will explore several potential use cases, with accompanying demos that simulate `:near()`’s behavior, as it is not yet supported in web browsers.
Visual effects
:near() has the potential for a vast array of visual effects:
div {
/* Div is wow */
&:near(3rem) {
/* Div be wowzer */
}
&:near(1rem) {
/* Div be woahhhh */
}
}
Dim elements until :near()
To minimize visual clutter, components could be dimmed until a user’s pointer approaches them. This approach might offer an advantage over `:hover` in situations where elements have limited visibility, as `:near()` could trigger their appearance earlier, potentially improving interaction. However, maintaining accessible color contrast remains a crucial consideration, which might limit the utility of `:near()` in this specific context.
button:not(:near(3rem)) {
opacity: 70%; /* Or...something */
}
Hide elements until :near()
Beyond dimming, `:near()` could also be used to hide less critical components. This scenario appears to be a more suitable application for `:near()`, as it avoids concerns about color contrast, though it introduces a different set of accessibility considerations.
Consider the common pattern where a share button appears upon hovering over an image. This design choice aims to prevent the image from being obscured, keeping the button hidden initially. While not always ideal for UX, it is a familiar interaction, seen on platforms like Pinterest.
:near() could enhance this experience. Users often anticipate a button’s presence, perhaps in a general area like the bottom-right corner, but may not know its precise location, size, or offset. By making the button appear when the pointer is `:near()`, users would not need to hover with perfect accuracy to reveal it. This use case shares similarities with the dimming scenario, though the rationale for reduced visibility might differ.
For such a button to be accessible (meaning hoverable, focusable, and discoverable via “find in page”), certain CSS properties are unsuitable:
- display: hidden (prevents hovering, focusing, or finding in page)
- visibility: hidden (also prevents hovering, focusing, or finding in page)
- opacity: 0 (does not allow the element to be revealed after being found by “find in page”)
This leaves `content-visibility: hidden`. However, hiding content with `content-visibility: hidden` (or `display: none`) causes the element to disappear entirely, making it impossible for the pointer to be “near” something that doesn’t occupy space. Therefore, reserving space for the element, even if the exact dimensions are unknown, becomes necessary.
Since `:near()` is not yet supported, the accompanying demo simulates its behavior. A button is enclosed within a container featuring 3rem of padding. When this container is hovered, the button becomes visible. This technique expands the hoverable area (highlighted in red for clarity) rather than the button itself, effectively simulating `button:near(3rem)`.
The challenge then becomes how to hide an element while still reserving its space.
One method involves applying contain-intrinsic-size: auto none to the hidden element. This property ensures the element maintains a specific size even when its content is hidden. While a <length> can be specified for either value, `auto` here refers to the element’s previously rendered size. `none` serves as a required fallback, and in this context, no specific length is needed.
A challenge arises because if the button is `content-visibility: hidden`, its rendered size “was” effectively nothing. To address this, the element needs to be rendered, even if only for a brief moment, to capture its size. The following animation achieves this:
<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;
}
It is worth noting that if the button includes the hidden=until-found attribute, which makes it focusable and discoverable, `content-visibility: hidden` is not explicitly declared, as hidden=until-found handles this automatically. Regardless, the animation sets `content-visibility: visible` for 1ms, allowing `contain-intrinsic-size: auto none` to capture the element’s size and reserve its space, thus making it hoverable even when not visually present.
With an understanding of the mechanism, here is the complete simulated code (as `:near()` is not yet supported):
<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;
}
}
The border and background are unset because `content-visibility: hidden` conceals only the content, not the element itself. Padding is included as it influences the rendered size that needs to be remembered. Subsequently, these styles, along with `content-visibility: visible`, are applied to the button when its wrapper is `:hovered` or `:has(:focus-visible)`.
Here is the equivalent implementation using the proposed, but currently 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 essence, `:near()` would allow for the same functionality as the simulated technique but with reduced markup and simpler selectors. For accessibility requirements, the animation and `contain-intrinsic-size` trick provides a solution.
Prefetch/prerender when near
While `:near()` itself might not directly facilitate prefetching or prerendering, its underlying concept could be integrated into the Speculation Rules API. This API currently uses signals such as `mousedown`, `touchstart`, pointer direction and velocity, viewport presence, and scroll pauses to initiate prefetching or prerendering of linked resources. Incorporating a “near” signal could further enhance its capabilities.
The concept of “near” could extend beyond the `:near()` pseudo-class, especially given the high performance cost and complexity of custom hit-testing with `pointermove` events, as highlighted by Thomas. Another example illustrates this potential.
Improve interest invoker interactions
Hover-triggered overlays carry the risk of accidental pointer movement away from the trigger or target. The Interest Invoker API, designed for such interactions, employs `interest-show-delay` and `interest-hide-delay` CSS properties to prevent unintended activations and deactivations. However, from a user experience standpoint, interactions involving delays and time-sensitivity can be less intuitive.
Examples include:
- The pointer inadvertently moving into the space between an interest trigger (such as a link or button) and its target (like a popover).
- The pointer moving beyond the boundaries of the interest target when attempting to interact with elements close to its edge.
Consequently, the Interest Invoker API could utilize the “near” concept, either in conjunction with or as an alternative to show and hide delays, to prevent overlays from disappearing due to minor mis-interactions. This functionality could be configured via a CSS property, such as `near-radius: 3rem` or simply `near: 3rem`, which would trigger JavaScript events like `interest` and `loseinterest`, unlike the `:near()` pseudo-class.
Thomas’s proposal also suggests another use case: displaying a “drag to reorder” hint when the pointer is near a draggable element. This is a compelling application, as revealing tooltips even slightly earlier could significantly reduce task completion time.
Simulating these specific scenarios with valid HTML might prove challenging, primarily due to the content restrictions of `` and `
Downsides to :near()
One potential drawback of `:near()` is the risk of developers overusing it to hide elements, either as a substitute for superior UI design to reduce visual clutter, or conversely, to increase visual clutter with conditionally hidden, unnecessary icons.
Further potential abuses encompass heatmapping, fingerprinting, and aggressive advertising. There is also a risk of `:near()` being implemented in ways that degrade performance. Thomas’s proposal thoroughly addresses these potential misuses and outlines strategies for `:near()`’s implementation to mitigate them.
:near() accessibility concerns
It is crucial that `:near()` does not implicitly behave like `:hover` or `:focus/:focus-visible`. A key question to consider before using `:near()` is whether the interaction is “preemptive” or “presumptive.” While preemptive actions can be beneficial, presumptive ones are generally undesirable, as users should not perceive themselves as hovering or focusing on an interactive element when they are not. This principle aligns with various sections of the Web Content Accessibility Guidelines, particularly Success Criterion 2.4.7: Focus Visible (Level AA).
Additionally, Success Criterion 2.5.8: Target Size (Level AA) mandates that interactive elements smaller than 24x24px must include additional spacing, calculated as 24px minus the target’s width or height. The extent to which the `:near()` value would influence this requirement remains somewhat unclear.
In conclusion
This proposal presents many considerations, but its implementation as suggested by Thomas would be a welcome addition. However, robust WCAG guidance is essential before any development begins, particularly since similar effects can currently be achieved with more complex markup and CSS techniques.
Furthermore, the broader concept of “near” could be explored, with its underlying functionality potentially benefiting the Speculation Rules API and the Interest Invoker API (perhaps through a CSS property like `near-radius` for the latter).
This concludes the discussion.

