Hero image for The Hover Blind Spot: What Web Agents Cannot Perceive by Default

The Hover Blind Spot: What Web Agents Cannot Perceive by Default

Hypothesis

Don Norman draws a fundamental distinction in interaction design. An affordance is what an object can do; a signifier is the perceivable indicator of where and how that action should occur. In his 2008 essay, Norman defines a signifier as “some sort of indicator, some signal in the physical or social world that can be interpreted meaningfully.” He adds that signifiers “can be accidental, and are often useful, but of mixed reliability.” This reliability is precisely what is at stake for autonomous agents navigating the web.

Our hypothesis is that the dominant signifier channel for pointer interactivity on the modern web is the CSS :hover pseudo-class. This state, however, contributes nothing to the representations a web agent receives at rest. When a page is fetched statically, the agent receives the Document Object Model (DOM), the accessibility tree, and the default computed styles. A hover rule, by definition, only applies when a pointer is over an element. Therefore, the richest stream of “this element is clickable” signals is invisible to an agent by default, creating a critical perceptual gap.

Method

To quantify this gap, we performed a live static audit on 2026-09-22. We did not render, interact with, or run JavaScript on the target pages. For each of nine production sites, we performed a static fetch of the HTML source, then parsed all CSS served by that page—both linked stylesheets and inline <style> blocks.

The pages audited were: github.com, developer.mozilla.org, astro.build, tailwindcss.com, stripe.com, vercel.com, nngroup.com/articles, smashingmagazine.com, and linear.app. The scope was limited to interactive elements identifiable by their tag and attributes: <a href>, <button>, <input> (excluding type="hidden"), <select>, <textarea>, and <summary>.

We counted the total number of these elements across the nine pages. Then, we scanned the served CSS for rules targeting :hover, :focus, :focus-visible, and declarations of cursor: pointer. Rules were counted per occurrence in the CSS source. This method captures the styling vocabulary a page serves but cannot apply in a static context. It is a static-fetch-only audit; we did not evaluate live rendering, JavaScript injection, or state changes.

Results

Across the nine audited pages, we identified 3,074 interactive elements. The CSS served by these pages contained 1,515 rules targeting the :hover pseudo-class. This translates to 49.3 hover rules per 100 interactive elements.

In contrast, we counted 759 rules targeting :focus or :focus-visible. Of those, 435 were specifically for :focus-visible, representing 57.3% of all focus-related rules. The ratio of :hover to :focus rules is approximately 2.0 : 1. We also found 149 cursor: pointer declarations, equating to 4.8 per 100 interactive elements. The analysis reveals that roughly twice as much CSS is dedicated to the pointer (hover) signifier channel as to the keyboard (focus) channel, and the explicit cursor: pointer cue is written far less often than the hover effect itself.

The per-site spread is significant:

Site :hover Rules
vercel.com 617
tailwindcss.com 353
nngroup.com/articles 240
stripe.com 108
developer.mozilla.org 98
astro.build 82
smashingmagazine.com 17
github.com 0
linear.app 0

Two rows report zero hover rules. For github.com and linear.app, this is a methodological artifact and a finding. Both sites inject their primary stylesheets at runtime from JavaScript bundles. A static fetch therefore sees none of their hover vocabulary. On these pages, the hover channel is not merely unread by a fetch-based agent—it is entirely absent from the initial data payload.

Computable rule: SSC and HOSR

To help agents and developers assess this gap, we propose two computable gates. These metrics should be evaluated after forcing hover states via the Chrome DevTools Protocol (CDP). The CDP method CSS.forcePseudoState “ensures that the given node will have specified pseudo-classes whenever its style is computed by the browser.” By pairing it with CSS.getComputedStyleForNode, one can diff the forced state against the default and make hidden signifiers visible.

However, a significant tooling gap exists. This protocol is not exposed by default in high-level browser automation frameworks. It is a long-standing feature request in Playwright (issue #3347) and is absent from its Locator API, which builds selectors on the static accessibility tree.

The two computable rules are:

  1. SSC (Signifier State Coverage): The proportion of interactive elements that possess at least one perceivable signifier in the default state. This includes an ARIA role or accessible name, a computed non-default cursor, or a visual differentiator (like a distinct border or background) independent of pointer presence. The formula is: SSC = (Elements with ≥1 default signifier) / (Total interactive elements). The gate is SSC ≥ 0.80.

  2. HOSR (Hover-Only Signal Ratio): The proportion of interactive elements whose only interactivity cue is revealed inside a :hover rule. For an element to count here, it must have no default-state signifier meeting the SSC criteria. The formula is: HOSR = (Elements whose sole cue is hover) / (Total interactive elements). The gate is HOSR ≤ 0.20.

Conclusion

The audit quantifies a stark representation gap. Web agents consuming a page at rest are denied access to the most prevalent signifier system for pointer interaction. This contrasts sharply with the :focus state. :focus is a real, machine-readable state that exists in the accessibility tree. Its perceived importance is codified in standards like WCAG 2.2 SC 2.4.13 Focus Appearance (Level AAA), which mandates a visible focus indicator of sufficient size and contrast.

Geometry and sizing are also graded by WCAG. SC 2.5.8 Target Size (Minimum) (Level AA) requires pointer targets to be at least 24×24 CSS pixels. But it says nothing about how a page must communicate that an element is interactive in its default state. The agent’s primary vocabulary remains the ARIA role and accessible name, as consumed by tools like Playwright’s Locator API. Even emerging DOM downsampling research, such as “Beyond Pixels: Exploring DOM Downsampling for LLM-Based Web Agents,” must contend with this lossy representation. Tools like browser-use serialize a pruned DOM; hover styling is not in it.

This is not a claim that agents can never perceive hover. The CDP CSS.forcePseudoState method provides a standards-track mechanism to make the state visible. The remediation rule is clear: before acting, an agent must force, diff, and enrich its representation with these hidden signifiers. The SSC and HOSR gates provide a computable framework for auditing this requirement. This review is based on official documentation, standards documents, and a live static audit of the pages named above — we did not run these sites hands-on.