Today I went to check out the Svelte source code, familiarizing myself with its architecture while also looking for any simple issues to practice on. And then I came across this - emits warning.
Although I think, based on the description of this issue, an a
tag without an href
should be considered invalid (at least not compliant with a11y). Later, I looked it up and found that there are some inconsistencies in the current specifications mentioned on StackOverflow.
However, in essence, href
can be empty, but in this case, it is recommended to use a button or similar tags instead of a
for better screen reader compatibility.
Later, I checked eslint-plugin-jsx-a11y and found that this eslint plugin helps to determine whether the href
is a valid value. The following are considered invalid values:
<a onClick={foo} /> // can be replaced with button
<a href="#" /> // only # without an id
<a href={"#"} /> // literal value
<a href={`#`} /> // literal value
<a href="javascript:void(0)" /> // should not use javascript:void(0)
<a href={"javascript:void(0)"} /> // literal value
<a href={`javascript:void(0)`} /> // literal value
There are also cases where href
is empty:
<a />
<a href={undefined} />
<a href={null} />
Svelte checks basic a11y
issues for you, such as missing href
or invalid href
values, because Svelte itself goes through a compilation phase, making these checks during the compilation stage:
// compiler/compile/nodes/Element.ts
// L425
if (this.name === 'a') {
const attribute = attribute_map.get('href') || attribute_map.get('xlink:href');
if (attribute) {
const value = attribute.get_static_value();
if (value === '' || value === '#') {
component.warn(attribute, {
code: `a11y-invalid-attribute`,
message: `A11y: '${value}' is not a valid ${attribute.name} attribute`
});
}
} else {
component.warn(this, {
code: `a11y-missing-attribute`,
message: `A11y: <a> element should have an href attribute`
});
}
}
At this stage, Svelte converts the syntax into an abstract syntax tree (AST), making the checks convenient.
However, upon closer inspection, huh? The case of javascript:void(0)
seems to have been missed. Currently, it only handles cases where the value is empty or #
. So I added a check: if (value === '' || value === '#' || /^\\W*javascript:/.test(value))
and submitted a Pull Request.
Another thing worth noting is that Svelte currently only checks for values that are is_static
, so for cases like:
<a href={undefined} />
<a href={null} />
<a href={`#`} />
<a href={"javascript:void(0)"} />
<a href={`javascript:void(0)`} />
Because the content inside {}
is an expression, Svelte cannot detect it (it marks is_static
as false). To check these cases, additional handling is required for these expressions. I will fix them all when I have time (the Golden Week is coming up).
It doesn't seem like a very useful feature, to be honest. Since it is done during the compilation phase and doesn't affect the bundle size, I'm starting to feel that the compiler approach is feasible. You can write code in whatever way you prefer, and as long as it compiles to JavaScript, it's all good.
Although this is not a new idea, similar concepts can be found in languages like LiveScript or Elm, to some extent. However, I think the reason why Svelte has gained attention (or is more easily adopted) is that it retains most of the JavaScript syntax, making the template learning curve almost zero (if you know React or Vue). It feels like writing HTML, CSS, and JavaScript together, just like Vue.
Another advantage is Svelte's reactive mechanism and animation control, which are often important considerations when implementing front-end interfaces and are not easily provided by other compilers. Everyone wants functional programming, but in reality, if it doesn't solve the developers' real problems, it remains a pursuit of a few enthusiasts.