diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts
index 07f17390182e..5b474f4fb9dd 100644
--- a/src/compiler/compile/nodes/Element.ts
+++ b/src/compiler/compile/nodes/Element.ts
@@ -98,6 +98,14 @@ const react_attributes = new Map([
['htmlFor', 'for']
]);
+const interactive_elements = new Set([
+ 'a', 'button', 'input', 'select', 'textarea'
+]);
+
+const noninteractive_roles = new Set([
+ 'article', 'banner', 'complementary', 'img', 'listitem', 'main', 'region', 'tooltip'
+]);
+
function get_namespace(parent: Element, element: Element, explicit_namespace: string) {
const parent_element = parent.find_nearest(/^Element/);
@@ -596,6 +604,18 @@ export default class Element extends Node {
});
}
}
+
+ if (interactive_elements.has(this.name)) {
+ if (attribute_map.has('role')) {
+ const roleValue = this.attributes.find(a => a.name === 'role').get_static_value().toString();
+ if (noninteractive_roles.has(roleValue)) {
+ component.warn(this, {
+ code: 'a11y-no-interactive-element-to-noninteractive-role',
+ message: `A11y: <${this.name}> cannot have role ${roleValue}`
+ });
+ }
+ }
+ }
}
validate_bindings_foreign() {
diff --git a/test/validator/samples/a11y-no-interactive-element-to-noninteractive-role/input.svelte b/test/validator/samples/a11y-no-interactive-element-to-noninteractive-role/input.svelte
new file mode 100644
index 000000000000..ad12bcfd069d
--- /dev/null
+++ b/test/validator/samples/a11y-no-interactive-element-to-noninteractive-role/input.svelte
@@ -0,0 +1,54 @@
+
+link
+link
+link
+link
+link
+link
+link
+link
+link
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/validator/samples/a11y-no-interactive-element-to-noninteractive-role/warnings.json b/test/validator/samples/a11y-no-interactive-element-to-noninteractive-role/warnings.json
new file mode 100644
index 000000000000..686a93940392
--- /dev/null
+++ b/test/validator/samples/a11y-no-interactive-element-to-noninteractive-role/warnings.json
@@ -0,0 +1,602 @@
+[
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 49,
+ "column": 38,
+ "line": 2
+ },
+ "message": "A11y: cannot have role article",
+ "pos": 11,
+ "start": {
+ "character": 11,
+ "column": 0,
+ "line": 2
+ }
+ },
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 87,
+ "column": 37,
+ "line": 3
+ },
+ "message": "A11y: cannot have role banner",
+ "pos": 50,
+ "start": {
+ "character": 50,
+ "column": 0,
+ "line": 3
+ }
+ },
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 132,
+ "column": 44,
+ "line": 4
+ },
+ "message": "A11y: cannot have role complementary",
+ "pos": 88,
+ "start": {
+ "character": 88,
+ "column": 0,
+ "line": 4
+ }
+ },
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 167,
+ "column": 34,
+ "line": 5
+ },
+ "message": "A11y: cannot have role img",
+ "pos": 133,
+ "start": {
+ "character": 133,
+ "column": 0,
+ "line": 5
+ }
+ },
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 207,
+ "column": 39,
+ "line": 6
+ },
+ "message": "A11y: cannot have role listitem",
+ "pos": 168,
+ "start": {
+ "character": 168,
+ "column": 0,
+ "line": 6
+ }
+ },
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 243,
+ "column": 35,
+ "line": 7
+ },
+ "message": "A11y: cannot have role main",
+ "pos": 208,
+ "start": {
+ "character": 208,
+ "column": 0,
+ "line": 7
+ }
+ },
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 281,
+ "column": 37,
+ "line": 8
+ },
+ "message": "A11y: cannot have role region",
+ "pos": 244,
+ "start": {
+ "character": 244,
+ "column": 0,
+ "line": 8
+ }
+ },
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 320,
+ "column": 38,
+ "line": 9
+ },
+ "message": "A11y: cannot have role tooltip",
+ "pos": 282,
+ "start": {
+ "character": 282,
+ "column": 0,
+ "line": 9
+ }
+ },
+ {
+ "code": "a11y-no-interactive-element-to-noninteractive-role",
+ "end": {
+ "character": 414,
+ "column": 38,
+ "line": 13
+ },
+ "message": "A11y: