diff --git a/docs/rules/html-element-name-kebab-casing.md b/docs/rules/html-element-name-kebab-casing.md
new file mode 100644
index 000000000..536bf4c4b
--- /dev/null
+++ b/docs/rules/html-element-name-kebab-casing.md
@@ -0,0 +1,34 @@
+# enforce the tag name of the Vue component and HTML element to be `kebab-case` (vue/html-element-name-kebab-casing)
+
+- :wrench: The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.
+
+This rule enforce the tag name of the Vue component, HTML element, and custom element to be `kebab-case`.
+It does not target elements of MathML or SVG.
+
+
+Do not use this rule if you prefer `PascalCase` to the Vue component name.
+
+## :book: Rule Details
+
+:+1: Examples of **correct** code:
+
+```html
+
+
+
+
+
+```
+
+:-1: Examples of **incorrect** code:
+
+```html
+
+
+
+
+
+
+
+
+```
diff --git a/lib/index.js b/lib/index.js
index 79769f2a3..551136c97 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -12,6 +12,7 @@ module.exports = {
'comment-directive': require('./rules/comment-directive'),
'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'),
'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'),
+ 'html-element-name-kebab-casing': require('./rules/html-element-name-kebab-casing'),
'html-end-tags': require('./rules/html-end-tags'),
'html-indent': require('./rules/html-indent'),
'html-quotes': require('./rules/html-quotes'),
diff --git a/lib/rules/html-element-name-kebab-casing.js b/lib/rules/html-element-name-kebab-casing.js
new file mode 100644
index 000000000..df5ce5901
--- /dev/null
+++ b/lib/rules/html-element-name-kebab-casing.js
@@ -0,0 +1,83 @@
+/**
+ * @author Yosuke Ota
+ * issue https://github.com/vuejs/eslint-plugin-vue/issues/499
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+const casing = require('../utils/casing')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+const kebabConverter = casing.getConverter('kebab-case')
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'enforce the tag name of the Vue component and HTML element to be `kebab-case`',
+ category: undefined,
+ url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.7.0/docs/rules/html-element-name-kebab-casing.md'
+ },
+ fixable: 'code',
+ schema: []
+ },
+
+ create (context) {
+ const tokens = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
+ const sourceCode = context.getSourceCode()
+
+ let hasInvalidEOF = false
+
+ return utils.defineTemplateBodyVisitor(context, {
+ 'VElement' (node) {
+ if (hasInvalidEOF) {
+ return
+ }
+
+ if (utils.isSvgElementNode(node) || utils.isMathMLElementNode(node)) {
+ return
+ }
+
+ const name = node.rawName
+ const casingName = kebabConverter(name)
+ if (casingName !== name) {
+ const startTag = node.startTag
+ const open = tokens.getFirstToken(startTag)
+
+ context.report({
+ node: open,
+ loc: open.loc,
+ message: 'Element name "{{name}}" is not kebab-case.',
+ data: {
+ name
+ },
+ fix: fixer => {
+ const endTag = node.endTag
+ if (!endTag) {
+ return fixer.replaceText(open, `<${casingName}`)
+ }
+ const endTagOpen = tokens.getFirstToken(endTag)
+ // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
+ // return [
+ // fixer.replaceText(open, `<${casingName}`),
+ // fixer.replaceText(endTagOpen, `${casingName}`)
+ // ]
+ const code = `<${casingName}${sourceCode.text.slice(open.range[1], endTagOpen.range[0])}${casingName}`
+ return fixer.replaceTextRange([open.range[0], endTagOpen.range[1]], code)
+ }
+ })
+ }
+ }
+ }, {
+ Program (node) {
+ hasInvalidEOF = utils.hasInvalidEOF(node)
+ }
+ })
+ }
+}
diff --git a/tests/lib/rules/html-element-name-kebab-casing.js b/tests/lib/rules/html-element-name-kebab-casing.js
new file mode 100644
index 000000000..1c3a54ce4
--- /dev/null
+++ b/tests/lib/rules/html-element-name-kebab-casing.js
@@ -0,0 +1,204 @@
+/**
+ * @author Yosuke Ota
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/html-element-name-kebab-casing')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const tester = new RuleTester({
+ parser: 'vue-eslint-parser'
+})
+
+tester.run('html-element-name-kebab-casing', rule, {
+ valid: [
+ '',
+ '
',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '
',
+ // Invalid EOF
+ '
+
+
+
+`,
+ output: `
+
+
+
+
+
+`,
+ errors: ['Element name "TheComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "TheComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "TheComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "TheComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "TheComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "theComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "The-component" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "Thecomponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "TheComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "TheComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: ['Element name "TheComponent" is not kebab-case.']
+ },
+ {
+ code: `
+
+
+
+`,
+ output: `
+
+
+
+`,
+ errors: [
+ 'Element name "Div" is not kebab-case.',
+ 'Element name "INPUT" is not kebab-case.'
+ ]
+ }
+ ]
+})