diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js
new file mode 100644
index 0000000000..f9cda2b0ff
--- /dev/null
+++ b/lib/standards/html-elms.js
@@ -0,0 +1,919 @@
+// Source: https://www.w3.org/TR/html-aria/#allowed-aria-roles-states-and-properties
+// Source: https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
+// Source https://html.spec.whatwg.org/multipage/dom.html#content-models
+// Source https://dom.spec.whatwg.org/#dom-element-attachshadow
+const htmlElms = {
+ a: {
+ // Note: variants work by matching the node against the
+ // `matches` attribute. if the variant matches AND has the
+ // desired property (contentTypes, etc.) then we use it,
+ // otherwise we move on to the next matching variant
+ variant: {
+ href: {
+ matches: '[href]',
+ contentTypes: ['interactive', 'phrasing', 'flow'],
+ allowedRoles: [
+ 'button',
+ 'checkbox',
+ 'menuitem',
+ 'menuitemcheckbox',
+ 'menuitemradio',
+ 'option',
+ 'radio',
+ 'switch',
+ 'tab',
+ 'treeitem',
+ 'doc-backlink',
+ 'doc-biblioref',
+ 'doc-glossref',
+ 'doc-noteref'
+ ]
+ },
+ // Note: the default variant is a special variant and is
+ // used as the last match if none of the other variants
+ // match or have the desired attribute
+ default: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ }
+ }
+ },
+ abbr: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ addres: {
+ contentTypes: ['flow'],
+ allowedRoles: true
+ },
+ area: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false
+ },
+ article: {
+ contentTypes: ['sectioning', 'flow'],
+ allowedRoles: [
+ 'feed',
+ 'presentation',
+ 'none',
+ 'document',
+ 'application',
+ 'main',
+ 'region'
+ ],
+ shadowRoot: true
+ },
+ aside: {
+ contentTypes: ['sectioning', 'flow'],
+ allowedRoles: [
+ 'feed',
+ 'note',
+ 'presentation',
+ 'none',
+ 'region',
+ 'search',
+ 'doc-dedication',
+ 'doc-example',
+ 'doc-footnote',
+ 'doc-pullquote',
+ 'doc-tip'
+ ]
+ },
+ audio: {
+ variant: {
+ controls: {
+ matches: '[controls]',
+ contentTypes: ['interactive', 'embedded', 'phrasing', 'flow']
+ },
+ default: {
+ contentTypes: ['embedded', 'phrasing', 'flow']
+ }
+ },
+ // Note: if the property applies regardless of variants it is
+ // placed at the top level instead of the default variant
+ allowedRoles: ['application']
+ },
+ b: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false
+ },
+ base: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ bdi: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ bdo: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ blockquote: {
+ contentTypes: ['flow'],
+ allowedRoles: true,
+ shadowRoot: true
+ },
+ body: {
+ allowedRoles: false,
+ shadowRoot: true
+ },
+ br: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: ['presentation', 'none'],
+ namingMethods: ['titleText', 'singleSpace']
+ },
+ button: {
+ contentTypes: ['interactive', 'phrasing', 'flow'],
+ allowedRoles: [
+ 'checkbox',
+ 'link',
+ 'menuitem',
+ 'menuitemcheckbox',
+ 'menuitemradio',
+ 'option',
+ 'radio',
+ 'switch',
+ 'tab'
+ ],
+ // 5.4 button Element
+ namingMethods: 'subtreeText'
+ },
+ canvas: {
+ allowedRoles: true,
+ contentTypes: ['embedded', 'phrasing', 'flow']
+ },
+ caption: {
+ allowedRoles: false
+ },
+ cite: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ code: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ col: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ colgroup: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ data: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ datalist: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ implicitAttrs: {
+ // Note: even though the value of aria-multiselectable is based
+ // on the attributes, we don't currently need to know the
+ // precise value. however, this allows us to make the attribute
+ // future proof in case we ever do need to know it
+ 'aria-multiselectable': 'false'
+ }
+ },
+ dd: {
+ allowedRoles: false
+ },
+ del: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ dfn: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ details: {
+ contentTypes: ['interactive', 'flow'],
+ allowedRoles: false
+ },
+ dialog: {
+ contentTypes: ['flow'],
+ allowedRoles: ['alertdialog']
+ },
+ div: {
+ contentTypes: ['flow'],
+ allowedRoles: true,
+ shadowRoot: true
+ },
+ dl: {
+ contentTypes: ['flow'],
+ allowedRoles: ['group', 'list', 'presentation', 'none']
+ },
+ dt: {
+ allowedRoles: ['listitem']
+ },
+ em: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ embed: {
+ contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'],
+ allowedRoles: ['application', 'document', 'img', 'presentation', 'none']
+ },
+ fieldset: {
+ contentTypes: ['flow'],
+ allowedRoles: ['none', 'presentation', 'radiogroup'],
+ // 5.5 fieldset and legend Elements
+ namingMethods: 'fieldsetLegendText'
+ },
+ figcaption: {
+ allowedRoles: ['group', 'none', 'presentation']
+ },
+ figure: {
+ contentTypes: ['flow'],
+ // Note: technically you're allowed no role when a figcaption
+ // descendant, but we can't match that so we'll go with any role
+ allowedRoles: true,
+ // 5.9 figure and figcaption Elements
+ namingMethods: ['figureText', 'titleText']
+ },
+ footer: {
+ contentTypes: ['flow'],
+ allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'],
+ shadowRoot: true
+ },
+ form: {
+ contentTypes: ['flow'],
+ allowedRoles: ['search', 'none', 'presentation']
+ },
+ h1: {
+ contentTypes: ['heading', 'flow'],
+ allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],
+ shadowRoot: true,
+ implicitAttrs: {
+ 'aria-level': '1'
+ }
+ },
+ h2: {
+ contentTypes: ['heading', 'flow'],
+ allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],
+ shadowRoot: true,
+ implicitAttrs: {
+ 'aria-level': '2'
+ }
+ },
+ h3: {
+ contentTypes: ['heading', 'flow'],
+ allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],
+ shadowRoot: true,
+ implicitAttrs: {
+ 'aria-level': '3'
+ }
+ },
+ h4: {
+ contentTypes: ['heading', 'flow'],
+ allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],
+ shadowRoot: true,
+ implicitAttrs: {
+ 'aria-level': '4'
+ }
+ },
+ h5: {
+ contentTypes: ['heading', 'flow'],
+ allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],
+ shadowRoot: true,
+ implicitAttrs: {
+ 'aria-level': '5'
+ }
+ },
+ h6: {
+ contentTypes: ['heading', 'flow'],
+ allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],
+ shadowRoot: true,
+ implicitAttrs: {
+ 'aria-level': '6'
+ }
+ },
+ head: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ header: {
+ contentTypes: ['flow'],
+ allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'],
+ shadowRoot: true
+ },
+ hgroup: {
+ contentTypes: ['heading', 'flow'],
+ allowedRoles: true
+ },
+ hr: {
+ contentTypes: ['flow'],
+ allowedRoles: ['none', 'presentation', 'doc-pagebreak'],
+ namingMethods: ['titleText', 'singleSpace']
+ },
+ html: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ i: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ iframe: {
+ contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'],
+ allowedRoles: ['application', 'document', 'img', 'none', 'presentation']
+ },
+ img: {
+ variant: {
+ nonEmptyAlt: {
+ matches: {
+ attributes: {
+ alt: '/.+/'
+ }
+ },
+ allowedRoles: [
+ 'button',
+ 'checkbox',
+ 'link',
+ 'menuitem',
+ 'menuitemcheckbox',
+ 'menuitemradio',
+ 'option',
+ 'progressbar',
+ 'scrollbar',
+ 'separator',
+ 'slider',
+ 'switch',
+ 'tab',
+ 'treeitem',
+ 'doc-cover'
+ ]
+ },
+ emptyAlt: {
+ matches: '[alt=""]',
+ allowedRoles: ['presentation', 'none']
+ },
+ noAlt: {
+ matches: {
+ attributes: {
+ alt: null
+ }
+ },
+ // Note: if img has accessible name then uses alt="some text"
+ // but we can't match that so we'll go with false
+ allowedRoles: false
+ },
+ usemap: {
+ matches: '[usemap]',
+ contentTypes: ['interactive', 'embedded', 'phrasing', 'flow']
+ },
+ default: {
+ contentTypes: ['embedded', 'phrasing', 'flow']
+ }
+ },
+ // 5.10 img Element
+ namingMethods: 'altText'
+ },
+ input: {
+ variant: {
+ button: {
+ matches: {
+ properties: {
+ type: 'button'
+ }
+ },
+ allowedRoles: [
+ 'link',
+ 'menuitem',
+ 'menuitemcheckbox',
+ 'menuitemradio',
+ 'option',
+ 'radio',
+ 'switch',
+ 'tab'
+ ]
+ },
+ // 5.2 input type="button", input type="submit" and input type="reset"
+ buttonType: {
+ matches: {
+ properties: {
+ type: ['button', 'submit', 'reset']
+ }
+ },
+ namingMethods: ['valueText', 'titleText', 'buttonDefaultText']
+ },
+ checkboxPressed: {
+ matches: {
+ properties: {
+ type: 'checkbox'
+ },
+ attributes: {
+ 'aria-pressed': '/.*/'
+ }
+ },
+ allowedRoles: ['button', 'menuitemcheckbox', 'option', 'switch'],
+ implicitAttrs: {
+ 'aria-checked': 'false'
+ }
+ },
+ checkbox: {
+ matches: {
+ properties: {
+ type: 'checkbox'
+ },
+ attributes: {
+ 'aria-pressed': null
+ }
+ },
+ allowedRoles: ['menuitemcheckbox', 'option', 'switch'],
+ implicitAttrs: {
+ 'aria-checked': 'false'
+ }
+ },
+ noRoles: {
+ matches: {
+ properties: {
+ type: [
+ 'color',
+ 'date',
+ 'datetime-local',
+ 'email',
+ 'file',
+ 'month',
+ 'number',
+ 'password',
+ 'range',
+ 'reset',
+ 'search',
+ 'submit',
+ 'tel',
+ 'time',
+ 'url',
+ 'week'
+ ]
+ }
+ },
+ allowedRoles: false
+ },
+ hidden: {
+ matches: {
+ properties: {
+ type: 'hidden'
+ }
+ },
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ image: {
+ matches: {
+ properties: {
+ type: 'image'
+ }
+ },
+ allowedRoles: [
+ 'link',
+ 'menuitem',
+ 'menuitemcheckbox',
+ 'menuitemradio',
+ 'radio',
+ 'switch'
+ ],
+ // 5.3 input type="image"
+ namingMethods: [
+ 'altText',
+ 'valueText',
+ 'labelText',
+ 'titleText',
+ 'buttonDefaultText'
+ ]
+ },
+ radio: {
+ matches: {
+ properties: {
+ type: 'radio'
+ }
+ },
+ allowedRoles: ['menuitemradio'],
+ implicitAttrs: {
+ 'aria-checked': 'false'
+ }
+ },
+ textWithList: {
+ matches: {
+ properties: {
+ type: 'text'
+ },
+ attributes: {
+ list: '/.*/'
+ }
+ },
+ allowedRoles: false
+ },
+ default: {
+ contentTypes: ['interactive', 'phrasing', 'flow'],
+ allowedRoles: ['combobox', 'searchbox', 'spinbutton'],
+ // 5.1 input type="text", input type="password", input type="search", input type="tel", input type="url" and textarea Element
+ // 5.7 Other Form Elements
+ namingMethods: 'labelText'
+ }
+ }
+ },
+ ins: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ kdb: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ label: {
+ contentTypes: ['interactive', 'phrasing', 'flow'],
+ allowedRoles: false
+ },
+ legend: {
+ allowedRoles: false
+ },
+ li: {
+ allowedRoles: [
+ 'menuitem',
+ 'menuitemcheckbox',
+ 'menuitemradio',
+ 'option',
+ 'none',
+ 'presentation',
+ 'radio',
+ 'separator',
+ 'tab',
+ 'treeitem',
+ 'doc-biblioentry',
+ 'doc-endnote'
+ ],
+ implicitAttrs: {
+ 'aria-setsize': '1',
+ 'aria-posinset': '1'
+ }
+ },
+ link: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ main: {
+ contentTypes: ['flow'],
+ allowedRoles: false,
+ shadowRoot: true
+ },
+ map: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ math: {
+ contentTypes: ['embedded', 'phrasing', 'flow'],
+ allowedRoles: false
+ },
+ mark: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ menu: {
+ contentTypes: ['flow'],
+ allowedRoles: [
+ 'directory',
+ 'group',
+ 'listbox',
+ 'menu',
+ 'menubar',
+ 'none',
+ 'presentation',
+ 'radiogroup',
+ 'tablist',
+ 'toolbar',
+ 'tree'
+ ]
+ },
+ meta: {
+ variant: {
+ itemprop: {
+ matches: '[itemprop]',
+ contentTypes: ['phrasing', 'flow']
+ }
+ },
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ meter: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false
+ },
+ nav: {
+ contentTypes: ['sectioning', 'flow'],
+ allowedRoles: ['doc-index', 'doc-pagelist', 'doc-toc'],
+ shadowRoot: true
+ },
+ noscript: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ object: {
+ variant: {
+ usemap: {
+ matches: '[usemap]',
+ contentTypes: ['interactive', 'embedded', 'phrasing', 'flow']
+ },
+ default: {
+ contentTypes: ['embedded', 'phrasing', 'flow']
+ }
+ },
+ allowedRoles: ['application', 'document', 'img']
+ },
+ ol: {
+ contentTypes: ['flow'],
+ allowedRoles: [
+ 'directory',
+ 'group',
+ 'listbox',
+ 'menu',
+ 'menubar',
+ 'none',
+ 'presentation',
+ 'radiogroup',
+ 'tablist',
+ 'toolbar',
+ 'tree'
+ ]
+ },
+ optgroup: {
+ allowedRoles: false
+ },
+ option: {
+ allowedRoles: false,
+ implicitAttrs: {
+ 'aria-selected': 'false'
+ }
+ },
+ output: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true,
+ // 5.6 output Element
+ namingMethods: 'subtreeText'
+ },
+ p: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true,
+ shadowRoot: true
+ },
+ param: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ picture: {
+ contentTypes: ['embedded', 'phrasing', 'flow'],
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ pre: {
+ contentTypes: ['flow'],
+ allowedRoles: true
+ },
+ progress: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true,
+ implicitAttrs: {
+ 'aria-valuemax': '100',
+ 'aria-valuemin': '0',
+ 'aria-valuenow': '0'
+ }
+ },
+ q: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ rp: {
+ allowedRoles: true
+ },
+ rt: {
+ allowedRoles: true
+ },
+ ruby: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ s: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ samp: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ script: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ section: {
+ contentTypes: ['sectioning', 'flow'],
+ allowedRoles: [
+ 'alert',
+ 'alertdialog',
+ 'application',
+ 'banner',
+ 'complementary',
+ 'contentinfo',
+ 'dialog',
+ 'document',
+ 'feed',
+ 'log',
+ 'main',
+ 'marquee',
+ 'navigation',
+ 'none',
+ 'note',
+ 'presentation',
+ 'search',
+ 'status',
+ 'tabpanel',
+ 'doc-abstract',
+ 'doc-acknowledgments',
+ 'doc-afterword',
+ 'doc-appendix',
+ 'doc-bibliography',
+ 'doc-chapter',
+ 'doc-colophon',
+ 'doc-conclusion',
+ 'doc-credit',
+ 'doc-credits',
+ 'doc-dedication',
+ 'doc-endnotes',
+ 'doc-epigraph',
+ 'doc-epilogue',
+ 'doc-errata',
+ 'doc-example',
+ 'doc-foreword',
+ 'doc-glossary',
+ 'doc-index',
+ 'doc-introduction',
+ 'doc-notice',
+ 'doc-pagelist',
+ 'doc-part',
+ 'doc-preface',
+ 'doc-prologue',
+ 'doc-pullquote',
+ 'doc-qna',
+ 'doc-toc'
+ ],
+ shadowRoot: true
+ },
+ select: {
+ variant: {
+ combobox: {
+ matches: {
+ attributes: {
+ multiple: null,
+ size: [null, '1']
+ }
+ },
+ allowedRoles: ['menu']
+ },
+ default: {
+ allowedRoles: false
+ }
+ },
+ contentTypes: ['interactive', 'phrasing', 'flow']
+ },
+ slot: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ small: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ source: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ span: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true,
+ shadowRoot: true
+ },
+ strong: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ style: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ svg: {
+ contentTypes: ['embedded', 'phrasing', 'flow'],
+ allowedRoles: ['application', 'document', 'img']
+ },
+ sub: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ summary: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ // 5.8 summary Element
+ namingMethods: 'subtreeText'
+ },
+ sup: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ table: {
+ contentTypes: ['flow'],
+ allowedRoles: true,
+ // 5.11 table Element
+ namingMethods: ['tableCaptionText', 'tableSummaryText']
+ },
+ tbody: {
+ allowedRoles: true
+ },
+ template: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ textarea: {
+ contentTypes: ['interactive', 'phrasing', 'flow'],
+ allowedRoles: false,
+ implicitAttrs: {
+ 'aria-multiline': 'true'
+ },
+ namingMethods: 'labelText'
+ },
+ tfoot: {
+ allowedRoles: true
+ },
+ thead: {
+ allowedRoles: true
+ },
+ time: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ title: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ td: {
+ allowedRoles: true
+ },
+ th: {
+ allowedRoles: true
+ },
+ tr: {
+ allowedRoles: true
+ },
+ track: {
+ allowedRoles: false,
+ noAriaAttrs: true
+ },
+ u: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ ul: {
+ contentTypes: ['flow'],
+ allowedRoles: [
+ 'directory',
+ 'group',
+ 'listbox',
+ 'menu',
+ 'menubar',
+ 'none',
+ 'presentation',
+ 'radiogroup',
+ 'tablist',
+ 'toolbar',
+ 'tree'
+ ]
+ },
+ var: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ },
+ video: {
+ variant: {
+ controls: {
+ matches: '[controls]',
+ contentTypes: ['interactive', 'embedded', 'phrasing', 'flow']
+ },
+ default: {
+ contentTypes: ['embedded', 'phrasing', 'flow']
+ }
+ },
+ allowedRoles: ['application']
+ },
+ wbr: {
+ contentTypes: ['phrasing', 'flow'],
+ allowedRoles: true
+ }
+};
+
+export default htmlElms;