
Element Code: HT-030
What this check means
Every element a user can tab to must expose a role: the machine-readable answer to "what is this thing?" Native interactive elements do this automatically. A <button> announces as a button, a link announces as a link, a checkbox announces as a checkbox, and each brings its expected keyboard behavior along for free.
The problem starts when developers build interactivity out of generic containers. A <div> with tabindex="0" and an onclick handler is focusable, looks clickable, and works fine with a mouse. But a div has no role. A screen reader lands on it and announces the text content with no hint that it is interactive, or worse, announces nothing meaningful at all. The user has no idea they can activate it, and even if they guess, Enter and Space do nothing because nobody wired up keydown handlers.
That is the exact failure this check flags: focusable elements whose semantics do not declare an interactive role. It maps to WCAG 2.1 success criterion 4.1.2, Name, Role, Value, which is Level A, meaning it is a baseline conformance failure, not a nice-to-have. The axe-core rules in this family also catch the inverse mess: focusable elements sitting inside aria-hidden="true" containers, where the element is reachable by keyboard but explicitly erased from the accessibility tree. Both variants strand real users.
Why an SEO tool cares about this
Fair question, and I will not pretend accessibility scores are a ranking factor, because they are not. Here is the actual overlap I see in audits:
- Fake links are invisible to crawlers. The same div-with-onclick pattern that fails screen readers also fails Googlebot. A div that navigates via JavaScript is not an
<a href>, so it passes no link signals and may leave target pages undiscovered. I have found entire product category trees hidden behind non-crawlable click handlers. Fixing semantics fixes discovery. - Semantic HTML is extraction-friendly HTML. Search engines and answer engines parse structure. Pages built from role-less divs give parsers less to work with than pages using native elements.
- Usability is downstream revenue. Keyboard users, switch users, and screen reader users bounce off broken widgets, and legal exposure under accessibility regulations is real for commercial sites.
The decision path
Native element vs DIY: what you get for free
| You want | Native (use this) | DIY equivalent you must fully rebuild |
|---|---|---|
| A button | <button> | div role="button" tabindex="0" + Enter and Space handlers + focus styling |
| A link that navigates | <a href="..."> | span role="link" tabindex="0" + Enter handler, and crawlers still cannot follow it |
| A checkbox | <input type="checkbox"> | div role="checkbox" + aria-checked state management + Space handler |
| Expand/collapse | <details>/<summary> | Button + aria-expanded + region wiring, all by hand |
| A slider | <input type="range"> | role="slider" + aria-valuenow/min/max + arrow key handlers |
Read that right column again. Every DIY row is a small pile of code you now own, test, and maintain forever, to poorly reimplement what the browser hands you for free. The native column is why "just use a button" is the most repeated sentence in accessibility reviews.
How to find these on your site
- axe DevTools (browser extension): run it on key templates; the rules covering focusable elements without interactive semantics and focusable content inside aria-hidden regions flag offenders with exact selectors.
- Lighthouse accessibility audit: bundled in Chrome DevTools, runs axe-core under the hood, good for one-off page checks.
- Screaming Frog: version 20 and later integrates axe-core, so you can crawl the whole site with JavaScript rendering enabled and export accessibility violations at scale. This is how you find out whether the problem is one widget or the entire design system.
- The keyboard test: unplug your mouse and tab through the page. Every stop should visibly show focus, announce sensibly in VoiceOver or NVDA, and activate with Enter or Space. Five minutes of this teaches you more than any report.
How to fix it, step by step
- Inventory the offenders. Export the axe or Screaming Frog violations and group by component, not by URL. Ten thousand flagged pages is usually three broken components in a shared template.
- Swap in native elements wherever possible. Change
div onclickto<button type="button">; change JavaScript navigation to a real<a href>. Restyle with CSS, since fear of default button styling is the usual reason for the div in the first place. - For genuine custom widgets, complete the contract: a role that matches the behavior,
tabindex="0", keydown handling for Enter and Space, an accessible name, and state attributes likearia-expandedoraria-checkedwhere relevant. Follow the ARIA Authoring Practices Guide patterns rather than inventing your own. - Never leave focusable elements inside aria-hidden containers. If a panel is hidden, also remove its contents from the tab order.
- Re-crawl and re-test with the same tools plus a manual keyboard pass, and add the axe rule to CI so the pattern does not creep back in with the next sprint.
DO and DON'T
- Reach for button, a href, input, and details before any ARIA
- Give every custom widget a role, tabindex, keyboard handlers, and a name
- Use real anchor tags for navigation so crawlers can follow links
- Test with a keyboard and a screen reader, not just an automated scan
- Fix at the component level so the repair propagates sitewide
- Ship div or span elements with click handlers and tabindex but no role
- Add role="button" without wiring Enter and Space keys
- Leave focusable elements inside aria-hidden="true" containers
- Slap ARIA roles on things a native element handles better
- Treat a passing automated scan as proof the widget actually works for people
What good looks like
Tab through the finished page and every focus stop is either a native interactive element or a custom widget with a truthful role, a visible focus ring, a sensible announced name, and working Enter or Space activation. Navigation is real links a crawler can follow. axe DevTools reports zero violations for the role and aria-hidden rule families, the Screaming Frog accessibility export is clean across templates, and a screen reader user can operate the page without guessing. That is the bar, and honestly, most of the distance is covered by one habit: use the element that already exists.
FAQ
Does fixing accessibility roles improve rankings?
Is tabindex="0" on a div always wrong?
Which WCAG criterion does this fail?
Why not just add role="button" everywhere the scanner complains?
Can I audit this at scale across a big site?
Broken semantics hide from every dashboard except a proper crawl.
The advanced audit combines a rendered crawl, accessibility checks, and link-graph analysis so you see exactly which components are costing you users and crawl paths.
Claude Vincent is a technical SEO consultant focused on crawlability, rendering, and AI-search visibility. He writes the field guides and case studies at SEO ProCheck, with a bias toward the durable, unglamorous work that decides whether search engines and AI answer engines can actually read and cite a site.
About SEO ProCheck
Technical SEO consulting and GEO strategy with 20 years of enterprise experience. Case studies, resources, and tools for search and AI visibility.
Work With Me
Technical SEO audits, GEO strategy, site migrations, and international SEO. Hourly consulting for teams who need hands-on support, not just reports.







