A link is an a element with an href. Everything else that behaves like a link in a browser, a div with a click handler, a span that calls the router, an a with no href and an onClick, is a link only to the JavaScript that made it one. To anything that reads the HTML rather than executing it, it is text. Check C2 exists to count the difference, and this post is about the first two of its four points: whether the site's navigation is made of real anchors.
Why an agent needs the href
Most agents fetch a page with a plain HTTP request and parse what comes back. They do not run your JavaScript. The two fetches covers the consequences for content; the consequence for navigation is simpler and harder. An agent that has fetched your homepage and wants to reach your pricing page has exactly one way to find it: an a element whose href says where the pricing page is. It collects those, decides which to follow, and fetches the next URL.
Navigation built on click handlers gives it nothing to collect. The handler is a function that runs when a pointer event fires, and it may call a client-side router, push a history entry, or fetch a fragment. The URL it will navigate to exists only inside that function. There is no attribute to read, no URL in the markup, and the agent has no pointer to click with anyway. From its side of the fetch, the page has no way out.
The same thing is true of a search engine's crawler, which is why the pattern has been discouraged in SEO guidance for years. Google's own JavaScript SEO documentation is explicit that links should be a elements with an href, and that it does not follow links from other elements or from click events. A search crawler at least renders pages eventually. Most agents never do.
How C2 counts
C2 is worth 4 points and is rated Medium effort. The scanner evaluates the anchor clause on the homepage only, from the raw HTML, and it computes two numbers.
The first is the count of a[href] elements. An a with no href is not counted, which is correct: without an href it is not a hyperlink, it is a styling hook. The threshold is five. Fewer than five real anchors on a homepage means either the site is very small or its navigation is built out of something else.
The second is the count of elements that behave like buttons or links without being either: div and span elements with an onclick attribute, and div and span elements with role="button". The threshold is zero. One is enough to lose the two points, because one click-handled element in the primary navigation is one destination an agent cannot reach.
Both numbers appear in the evidence block as anchors and clickableNonAnchors. The other two points, one for the share of links with contextless text and one for breadcrumbs or a URL hierarchy, have their own posts; this one is about getting clickableNonAnchors to zero and anchors to a sensible number.
There is a limit to what the count can see, and it is worth being honest about it. A div whose click listener is attached with addEventListener in a script has no onclick attribute and no role, so it is invisible to the scanner as well as to the agent. It is not a false pass so much as a case the check cannot evidence, and it will still cost you every agent that fetched the page. The fix is the same.
The three common shapes
The first is the styled div with a handler, usually in a card grid or a menu.
<div class="nav-item" onclick="location.href='/pricing'">Pricing</div>
The destination is right there in the attribute, but it is inside a string inside a function call, and no parser is going to interpret it. It also cannot be opened in a new tab, has no focus state without more work, and is not in the tab order. Replace it with an anchor and the class can stay.
<a class="nav-item" href="/pricing">Pricing</a>
The second is the anchor with no href, a pattern from the days when href="#" caused unwanted scrolling and the fix was to remove it.
<a onclick="goTo('/pricing')">Pricing</a>
Without an href this is not a link to the parser, not focusable by default, and not counted. Put the href back. If the click handler still needs to run for the in-app transition, it can, but the href has to describe the real destination so that everything that does not run the handler has somewhere to go.
The third is the JavaScript-only router, where a framework component renders something and intercepts clicks on it. Here the answer depends on the framework and on where the rendering happens. The link components in most current frameworks render an a with an href and intercept the click for a client-side transition, which is the right shape: the anchor is in the server-rendered HTML for anything that reads it, and the handler is an enhancement for anything that runs it. The failure is when the component renders only on the client, so the server sends an empty shell and the anchors appear after hydration. The markup is correct, but it arrives too late to be in the first fetch. Rendering on the server fixes both A7 and C2 at once.
The menu in our header
Our own header has a mobile menu, and a mobile menu is where most sites lose this check, because it is the natural place for a JavaScript drawer with a hamburger button that toggles a class. Ours is a native details element.
<details>
<summary><span class="sr-only">Menu</span> …icon… </summary>
<nav aria-label="Main (mobile)">
<ul>
<li><a href="/scan">Scan</a></li>
<li><a href="/methodology">Methodology</a></li>
<li><a href="/search">Search</a></li>
<li><a href="/book">Book a call</a></li>
</ul>
</nav>
</details>
The comment in the component says the reason in one line: the mobile menu is a native details rather than a JS drawer, so the links stay in the raw HTML. The browser handles open and close with no script, the summary is a real focusable disclosure control, and every link is an a[href] on every fetch, whether or not the menu is drawn open. The desktop navigation is a nav full of the same anchors. There is no client-side code in the header at all, which also means the selected-item highlight that a client component would draw does not exist; we decided that a highlight was not worth a bundle.
The search field next to it is a GET form for the same reason. An agent can build a search URL from any page without driving anything, which is what check C6 tests, but the principle is identical: the destination is in the markup.
What to change
Search your templates for onclick, role="button" on a div or span, and <a without href. For each one, ask what a reader who cannot execute JavaScript should be able to do with it. If the answer is "go somewhere", it is an a with an href. If the answer is "do something on this page", it is a button, which is check C3's territory. If the answer is "nothing", it is not interactive and the handler should not be there.
Then look at the homepage in the raw fetch, not the browser, and count the anchors. curl -s https://yoursite.example | grep -c '<a ' is rough but good enough to notice that a navigation you thought had twelve items has none.
Check your own site
Run the free scan and read the C2 evidence on the report: anchors is how many real links the homepage carried in the raw HTML, and clickableNonAnchors is how many elements were pretending. The check is defined under actionability on the methodology page, and our own report at /site/agentfriendlyrank.com shows the header described above passing it.