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 + } + } +]