diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts
index 7a70e603a77f..ffcf0e51f26d 100644
--- a/src/compiler/compile/nodes/Element.ts
+++ b/src/compiler/compile/nodes/Element.ts
@@ -61,6 +61,17 @@ const a11y_no_onchange = new Set([
'option'
]);
+const a11y_labelable = new Set([
+ "button",
+ "input",
+ "keygen",
+ "meter",
+ "output",
+ "progress",
+ "select",
+ "textarea"
+]);
+
const invisible_elements = new Set(['meta', 'html', 'script', 'style']);
const valid_modifiers = new Set([
@@ -507,6 +518,16 @@ export default class Element extends Node {
}
}
+ if (this.name === 'label') {
+ const has_input_child = this.children.some(i => (i instanceof Element && a11y_labelable.has(i.name) ));
+ if (!attribute_map.has('for') && !has_input_child) {
+ component.warn(this, {
+ code: `a11y-label-has-associated-control`,
+ message: `A11y: A form label must be associated with a control.`
+ });
+ }
+ }
+
if (a11y_no_onchange.has(this.name)) {
if (handlers_map.has('change') && !handlers_map.has('blur')) {
component.warn(this, {
diff --git a/test/validator/samples/a11y-label-has-associated-control/input.svelte b/test/validator/samples/a11y-label-has-associated-control/input.svelte
new file mode 100644
index 000000000000..43304689dc29
--- /dev/null
+++ b/test/validator/samples/a11y-label-has-associated-control/input.svelte
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/test/validator/samples/a11y-label-has-associated-control/warnings.json b/test/validator/samples/a11y-label-has-associated-control/warnings.json
new file mode 100644
index 000000000000..b70a1a47de70
--- /dev/null
+++ b/test/validator/samples/a11y-label-has-associated-control/warnings.json
@@ -0,0 +1,32 @@
+[
+ {
+ "code": "a11y-label-has-associated-control",
+ "end": {
+ "character": 16,
+ "column": 16,
+ "line": 1
+ },
+ "message": "A11y: A form label must be associated with a control.",
+ "pos": 0,
+ "start": {
+ "character": 0,
+ "column": 0,
+ "line": 1
+ }
+ },
+ {
+ "code": "a11y-label-has-associated-control",
+ "end": {
+ "character": 149,
+ "column": 30,
+ "line": 6
+ },
+ "message": "A11y: A form label must be associated with a control.",
+ "pos": 119,
+ "start": {
+ "character": 119,
+ "column": 0,
+ "line": 6
+ }
+ }
+]