diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 7a70e603a7..915941d280 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -507,6 +507,16 @@ export default class Element extends Node { } } + if (this.name === 'label') { + const has_input_child = this.children.some(i => (i instanceof Element && i.name === 'input' )); + 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 0000000000..92458f4625 --- /dev/null +++ b/test/validator/samples/a11y-label-has-associated-control/input.svelte @@ -0,0 +1,5 @@ + + + + + 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 0000000000..5e9bb9c15a --- /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": 113, + "column": 30, + "line": 5 + }, + "message": "A11y: A form label must be associated with a control.", + "pos": 83, + "start": { + "character": 83, + "column": 0, + "line": 5 + } + } +]