diff --git a/src/rules/index.js b/src/rules/index.js
index c924ed29e..005f1dc94 100644
--- a/src/rules/index.js
+++ b/src/rules/index.js
@@ -16,6 +16,7 @@ export { default as idClassValue } from './id-class-value';
export { default as idUnique } from './id-unique';
export { default as inlineScriptDisabled } from './inline-script-disabled';
export { default as inlineStyleDisabled } from './inline-style-disabled';
+export { default as inputRequiresLabel } from './input-requires-label';
export { default as scriptDisabled } from './script-disabled';
export { default as spaceTabMixedDisabled } from './space-tab-mixed-disabled';
export { default as specCharEscape } from './spec-char-escape';
diff --git a/src/rules/input-requires-label.js b/src/rules/input-requires-label.js
new file mode 100644
index 000000000..aae039b7e
--- /dev/null
+++ b/src/rules/input-requires-label.js
@@ -0,0 +1,46 @@
+export default {
+ id: 'input-requires-label',
+ description: 'All [ input ] tags must have a corresponding [ label ] tag. ',
+ init: function(parser, reporter){
+ var self = this,
+ labelTags = [],
+ inputTags = [];
+
+ parser.addListener('tagstart', function(event) {
+ var tagName = event.tagName.toLowerCase(),
+ mapAttrs = parser.getMapAttrs(event.attrs),
+ col = event.col + tagName.length + 1;
+
+ if (tagName === 'input') {
+ inputTags.push({event: event, col: col, id: mapAttrs['id']});
+ }
+
+ if (tagName === 'label') {
+ if (('for' in mapAttrs) && mapAttrs['for'] !== '') {
+ labelTags.push({event: event, col: col, forValue: mapAttrs['for']});
+ }
+ }
+
+ });
+
+ parser.addListener('end', function() {
+ inputTags.forEach(function(inputTag) {
+ if (!hasMatchingLabelTag(inputTag)) {
+ reporter.warn('No matching [ label ] tag found.', inputTag.event.line, inputTag.col, self, inputTag.event.raw);
+ }
+ });
+ });
+
+
+ function hasMatchingLabelTag(inputTag) {
+ var found = false;
+ labelTags.forEach(function(labelTag){
+ if (inputTag.id && (inputTag.id === labelTag.forValue)) {
+ found = true;
+ }
+ });
+ return found;
+
+ }
+ }
+};
diff --git a/test/rules/input-requires-label.spec.js b/test/rules/input-requires-label.spec.js
new file mode 100644
index 000000000..700786683
--- /dev/null
+++ b/test/rules/input-requires-label.spec.js
@@ -0,0 +1,71 @@
+const expect = require("expect.js");
+
+const HTMLHint = require('../../dist/htmlhint.js').HTMLHint;
+
+const ruleId = 'input-requires-label';
+const ruleOptions = {};
+
+ruleOptions[ruleId] = true;
+
+describe(`Rules: ${ruleId}`, function(){
+
+ describe('Successful cases', function() {
+
+ it('Input tag with a matching label before should result in no error', function () {
+ var code = '';
+ var messages = HTMLHint.verify(code, ruleOptions);
+ expect(messages.length).to.be(0);
+ });
+
+ it('Input tag with a matching label after should result in no error', function () {
+ var code = ' ';
+ var messages = HTMLHint.verify(code, ruleOptions);
+ expect(messages.length).to.be(0);
+ });
+ });
+
+
+ describe('Error cases', function() {
+
+ it('Input tag with no matching label should result in an error', function () {
+ var code = '';
+ var messages = HTMLHint.verify(code, ruleOptions);
+ expect(messages.length).to.be(1);
+ expect(messages[0].rule.id).to.be(ruleId);
+ expect(messages[0].line).to.be(1);
+ expect(messages[0].col).to.be(7);
+ expect(messages[0].type).to.be('warning');
+ });
+
+ it('Input tag with label that doesn\'t match id should result in error', function () {
+ var code = ' ';
+ var messages = HTMLHint.verify(code, ruleOptions);
+ expect(messages.length).to.be(1);
+ expect(messages[0].rule.id).to.be(ruleId);
+ expect(messages[0].line).to.be(1);
+ expect(messages[0].col).to.be(7);
+ expect(messages[0].type).to.be('warning');
+ });
+
+ it('Input tag with blank label:for should result in error', function () {
+ var code = ' ';
+ var messages = HTMLHint.verify(code, ruleOptions);
+ expect(messages.length).to.be(1);
+ expect(messages[0].rule.id).to.be(ruleId);
+ expect(messages[0].line).to.be(1);
+ expect(messages[0].col).to.be(7);
+ expect(messages[0].type).to.be('warning');
+ });
+
+ it('Input tag with no id should result in error', function () {
+ var code = ' ';
+ var messages = HTMLHint.verify(code, ruleOptions);
+ expect(messages.length).to.be(1);
+ expect(messages[0].rule.id).to.be(ruleId);
+ expect(messages[0].line).to.be(1);
+ expect(messages[0].col).to.be(7);
+ expect(messages[0].type).to.be('warning');
+ });
+ });
+
+});