Disallow link elements — <a> and <area> — whose href value is a commonly-misused placeholder (e.g. href="#", href="", href="javascript:..."). Both carry URL semantics per the HTML spec (the <a> element, the <area> element), so the same validity rules apply on each.
This rule is pragmatic accessibility/UX guidance, not spec enforcement. Values like href="#" and href="javascript:void(0)" are technically valid URLs per the HTML spec; the rule flags them because they are widely-recognized anti-patterns for faking a clickable anchor:
- Breaks expected keyboard behavior (anchors should navigate; buttons should act)
- The
javascript:pseudo-protocol is called out as an anti-pattern by MDN - Leaves assistive tech announcing a link that doesn't navigate
If a click handler is what you want, use a <button>. If you want a genuine fragment link, use href="#section-id".
Complements template-link-href-attributes, which handles the missing href case. This rule validates the href value.
This rule forbids the following:
<template>
<a href="#">Click</a>
<a href="#!">Click</a>
<a href="">Click</a>
<a href>Click</a>
<a href="javascript:void(0)">Click</a>
<a href="JavaScript:alert(1)">Execute</a>
</template>This rule allows the following:
<template>
<a href="/x">Link</a>
<a href="https://example.com">Link</a>
<a href="#section">Fragment link</a>
<a href="mailto:[email protected]">Email</a>
<a href={{this.url}}>Dynamic</a>
</template>Mustache hrefs whose value is a static literal (string, number, or boolean) are validated — the rule unwraps them to their static value via getStaticAttrValue. Only truly dynamic mustaches (PathExpressions, helpers with arguments, or concat statements that include a dynamic part) are skipped, because we can't statically determine what they will resolve to at runtime.