From b444c77d5460e6c3d6d0cd12b1f311a6cef51a11 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Mon, 29 Jun 2020 17:20:11 -0600 Subject: [PATCH 1/9] feat(standards): add html-aria spec --- lib/standards/html-aria.js | 715 +++++++++++++++++++++++++++++++++++++ 1 file changed, 715 insertions(+) create mode 100644 lib/standards/html-aria.js diff --git a/lib/standards/html-aria.js b/lib/standards/html-aria.js new file mode 100644 index 0000000000..85b3d1e91c --- /dev/null +++ b/lib/standards/html-aria.js @@ -0,0 +1,715 @@ +// 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 +const htmlElms = { + a: { + variant: { + href: { + matches: '[href]', + allowedRoles: [ + 'button', + 'checkbox', + 'menuitem', + 'menuitemcheckbox', + 'menuitemradio', + 'option', + 'radio', + 'switch', + 'tab', + 'treeitem', + 'doc-backlink', + 'doc-biblioref', + 'doc-glossref', + 'doc-noteref' + ] + }, + noHref: { + matches: { + attributes: { + href: null + } + }, + allowedRoles: true + } + } + }, + abbr: { + allowedRoles: true + }, + addres: { + allowedRoles: true + }, + area: { + allowedRoles: false + }, + article: { + allowedRoles: [ + 'feed', + 'presentation', + 'none', + 'document', + 'application', + 'main', + 'region' + ] + }, + aside: { + allowedRoles: [ + 'feed', + 'note', + 'presentation', + 'none', + 'region', + 'search', + 'doc-dedication', + 'doc-example', + 'doc-footnote', + 'doc-pullquote', + 'doc-tip' + ] + }, + audio: { + variant: { + controls: { + matches: '[controls]', + allowedRoles: ['application'] + }, + noControls: { + allowedRoles: ['application'] + } + } + }, + b: { + allowedRoles: false + }, + base: { + allowedRoles: false, + noAriaAttrs: true + }, + bdi: { + allowedRoles: true + }, + bdo: { + allowedRoles: true + }, + blockquote: { + allowedRoles: true + }, + body: { + allowedRoles: false + }, + br: { + allowedRoles: ['presentation', 'none'] + }, + button: { + allowedRoles: [ + 'checkbox', + 'link', + 'menuitem', + 'menuitemcheckbox', + 'menuitemradio', + 'option', + 'radio', + 'switch', + 'tab' + ] + }, + canvas: { + allowedRoles: true + }, + caption: { + allowedRoles: false + }, + cite: { + allowedRoles: true + }, + code: { + allowedRoles: true + }, + col: { + allowedRoles: false, + noAriaAttrs: true + }, + colgroup: { + allowedRoles: false, + noAriaAttrs: true + }, + data: { + allowedRoles: true + }, + datalist: { + 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: { + allowedRoles: true + }, + dfn: { + allowedRoles: true + }, + details: { + allowedRoles: false + }, + dialog: { + allowedRoles: ['alertdialog'] + }, + div: { + allowedRoles: true + }, + dl: { + allowedRoles: ['group', 'list', 'presentation', 'none'] + }, + dt: { + allowedRoles: ['listitem'] + }, + em: { + allowedRoles: true + }, + embed: { + allowedRoles: ['application', 'document', 'img', 'presentation', 'none'] + }, + fieldset: { + allowedRoles: ['none', 'presentation', 'radiogroup'] + }, + figcaption: { + allowedRoles: ['group', 'none', 'presentation'] + }, + figure: { + // 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 + }, + footer: { + allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'] + }, + form: { + allowedRoles: ['search', 'none', 'presentation'] + }, + h1: { + allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'], + implicitAttrs: { + 'aria-level': '1' + } + }, + h2: { + allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'], + implicitAttrs: { + 'aria-level': '2' + } + }, + h3: { + allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'], + implicitAttrs: { + 'aria-level': '3' + } + }, + h4: { + allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'], + implicitAttrs: { + 'aria-level': '4' + } + }, + h5: { + allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'], + implicitAttrs: { + 'aria-level': '5' + } + }, + h6: { + allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'], + implicitAttrs: { + 'aria-level': '6' + } + }, + head: { + allowedRoles: false, + noAriaAttrs: true + }, + header: { + allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'] + }, + hgroup: { + allowedRoles: true + }, + hr: { + allowedRoles: ['none', 'presentation', 'doc-pagebreak'] + }, + html: { + allowedRoles: false, + noAriaAttrs: true + }, + i: { + allowedRoles: true + }, + iframe: { + allowedRoles: ['application', 'document', 'img', 'none', 'presentation'] + }, + img: { + variant: { + alt: { + 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 + } + } + }, + input: { + variant: { + button: { + matches: '[type="button"]', + allowedRoles: [ + 'link', + 'menuitem', + 'menuitemcheckbox', + 'menuitemradio', + 'option', + 'radio', + 'switch', + 'tab' + ] + }, + checkboxPressed: { + matches: '[type="checkbox"][aria-pressed]', + allowedRoles: ['button', 'menuitemcheckbox', 'option', 'switch'], + implicitAttrs: { + 'aria-checked': 'false' + } + }, + checkbox: { + matches: '[type="checkbox"]', + allowedRoles: ['menuitemcheckbox', 'option', 'switch'], + implicitAttrs: { + 'aria-checked': 'false' + } + }, + noRoles: { + matches: { + attributes: { + type: [ + 'color', + 'date', + 'datetime-local', + 'email', + 'file', + 'month', + 'number', + 'password', + 'range', + 'reset', + 'search', + 'submit', + 'tel', + 'time', + 'url', + 'week' + ] + } + }, + allowedRoles: false + }, + hidden: { + matches: '[type="hidden"]', + allowedRoles: false, + noAriaAttrs: true + }, + image: { + matches: { + attributes: '[type="image"]' + }, + allowedRoles: [ + 'link', + 'menuitem', + 'menuitemcheckbox', + 'menuitemradio', + 'radio', + 'switch' + ] + }, + radio: { + matches: '[type="radio"]', + allowedRoles: ['menuitemradio'], + implicitAttrs: { + 'aria-checked': 'false' + } + }, + textWithList: { + matches: '[type="text"][list]', + allowedRoles: false + }, + // Note: this covers type=text and any other input type + default: { + matches: { + attributes: { + list: null + } + }, + allowedRoles: ['combobox', 'searchbox', 'spinbutton'] + } + } + }, + ins: { + allowedRoles: true + }, + kdb: { + allowedRoles: true + }, + label: { + 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: { + allowedRoles: false, + noAriaAttrs: true + }, + main: { + allowedRoles: false + }, + map: { + allowedRoles: false, + noAriaAttrs: true + }, + math: { + allowedRoles: false + }, + mark: { + allowedRoles: true + }, + menu: { + allowedRoles: [ + 'directory', + 'group', + 'listbox', + 'menu', + 'menubar', + 'none', + 'presentation', + 'radiogroup', + 'tablist', + 'toolbar', + 'tree' + ] + }, + meta: { + allowedRoles: false, + noAriaAttrs: true + }, + meter: { + allowedRoles: false + }, + nav: { + allowedRoles: ['doc-index', 'doc-pagelist', 'doc-toc'] + }, + noscript: { + allowedRoles: false, + noAriaAttrs: true + }, + object: { + allowedRoles: ['application', 'document', 'img'] + }, + ol: { + allowedRoles: [ + 'directory', + 'group', + 'listbox', + 'menu', + 'menubar', + 'none', + 'presentation', + 'radiogroup', + 'tablist', + 'toolbar', + 'tree' + ] + }, + optgroup: { + allowedRoles: false + }, + option: { + allowedRoles: false, + implicitAttrs: { + 'aria-selected': 'false' + } + }, + output: { + allowedRoles: true + }, + p: { + allowedRoles: true + }, + param: { + allowedRoles: false, + noAriaAttrs: true + }, + picture: { + allowedRoles: false, + noAriaAttrs: true + }, + pre: { + allowedRoles: true + }, + progress: { + allowedRoles: true, + implicitAttrs: { + 'aria-valuemax': '100', + 'aria-valuemin': '0', + 'aria-valuenow': '0' + } + }, + q: { + allowedRoles: true + }, + rp: { + allowedRoles: true + }, + rt: { + allowedRoles: true + }, + ruby: { + allowedRoles: true + }, + s: { + allowedRoles: true + }, + samp: { + allowedRoles: true + }, + script: { + allowedRoles: false, + noAriaAttrs: true + }, + section: { + 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' + ] + }, + select: { + variant: { + combobox: { + matches: { + attributes: { + multiple: null, + size: [null, '1'] + } + }, + allowedRoles: ['menu'] + }, + listbox: { + matches: '[multiple], select[size]:not([size="1"])', + allowedRoles: false + } + } + }, + slot: { + allowedRoles: false, + noAriaAttrs: true + }, + small: { + allowedRoles: true + }, + source: { + allowedRoles: false, + noAriaAttrs: true + }, + span: { + allowedRoles: true + }, + strong: { + allowedRoles: true + }, + style: { + allowedRoles: false, + noAriaAttrs: true + }, + svg: { + allowedRoles: ['application', 'document', 'img'] + }, + sub: { + allowedRoles: true + }, + summary: { + allowedRoles: false + }, + sup: { + allowedRoles: true + }, + table: { + allowedRoles: true + }, + tbody: { + allowedRoles: true + }, + template: { + allowedRoles: false, + noAriaAttrs: true + }, + textarea: { + allowedRoles: false, + implicitAttrs: { + 'aria-multiline': 'true' + } + }, + tfoot: { + allowedRoles: true + }, + thead: { + allowedRoles: true + }, + time: { + allowedRoles: true + }, + title: { + allowedRoles: false, + noAriaAttrs: true + }, + td: { + allowedRoles: true + }, + th: { + allowedRoles: true + }, + tr: { + allowedRoles: true + }, + track: { + allowedRoles: false, + noAriaAttrs: true + }, + u: { + allowedRoles: true + }, + ul: { + allowedRoles: [ + 'directory', + 'group', + 'listbox', + 'menu', + 'menubar', + 'none', + 'presentation', + 'radiogroup', + 'tablist', + 'toolbar', + 'tree' + ] + }, + var: { + allowedRoles: true + }, + video: { + allowedRoles: ['application'] + }, + wbr: { + allowedRoles: true + } +}; + +export default htmlElms; From 13e599b02679947d15281a034ce8e0c16a6eb79d Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 30 Jun 2020 08:16:14 -0600 Subject: [PATCH 2/9] fixes --- lib/standards/html-aria.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/standards/html-aria.js b/lib/standards/html-aria.js index 85b3d1e91c..e164bd24b7 100644 --- a/lib/standards/html-aria.js +++ b/lib/standards/html-aria.js @@ -68,15 +68,7 @@ const htmlElms = { ] }, audio: { - variant: { - controls: { - matches: '[controls]', - allowedRoles: ['application'] - }, - noControls: { - allowedRoles: ['application'] - } - } + allowedRoles: ['application'] }, b: { allowedRoles: false @@ -279,7 +271,7 @@ const htmlElms = { ] }, emptyAlt: { - matches: '[alt]', + matches: '[alt=""]', allowedRoles: ['presentation', 'none'] }, noAlt: { @@ -317,7 +309,7 @@ const htmlElms = { } }, checkbox: { - matches: '[type="checkbox"]', + matches: '[type="checkbox"]:not([aria-pressed])', allowedRoles: ['menuitemcheckbox', 'option', 'switch'], implicitAttrs: { 'aria-checked': 'false' From ed26f52f5257dad4f9e7fb6ae15947fb66f728d8 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 30 Jun 2020 11:17:18 -0600 Subject: [PATCH 3/9] rename obj --- lib/standards/html-aria.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/standards/html-aria.js b/lib/standards/html-aria.js index e164bd24b7..fad14e7026 100644 --- a/lib/standards/html-aria.js +++ b/lib/standards/html-aria.js @@ -1,6 +1,6 @@ // 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 -const htmlElms = { +const htmlAria = { a: { variant: { href: { @@ -704,4 +704,4 @@ const htmlElms = { } }; -export default htmlElms; +export default htmlAria; From 8d0a9a49b493e72c113fde158c5bafb75bcabf8b Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 7 Jul 2020 16:06:24 -0600 Subject: [PATCH 4/9] combine specs --- lib/standards/{html-aria.js => html-elms.js} | 262 ++++++++++++++++--- 1 file changed, 219 insertions(+), 43 deletions(-) rename lib/standards/{html-aria.js => html-elms.js} (60%) diff --git a/lib/standards/html-aria.js b/lib/standards/html-elms.js similarity index 60% rename from lib/standards/html-aria.js rename to lib/standards/html-elms.js index fad14e7026..cd615e15a3 100644 --- a/lib/standards/html-aria.js +++ b/lib/standards/html-elms.js @@ -1,10 +1,17 @@ // 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 -const htmlAria = { +// 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', @@ -22,26 +29,29 @@ const htmlAria = { 'doc-noteref' ] }, - noHref: { - matches: { - attributes: { - href: null - } - }, + // 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', @@ -50,9 +60,11 @@ const htmlAria = { 'application', 'main', 'region' - ] + ], + shadowRoot: true }, aside: { + contentTypes: ['sectioning', 'flow'], allowedRoles: [ 'feed', 'note', @@ -68,9 +80,19 @@ const htmlAria = { ] }, audio: { - allowedRoles: ['application'] + variant: { + controls: { + matches: '[controls]', + contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'] + }, + default: { + contentTypes: ['embedded', 'phrasing', 'flow'], + allowedRoles: ['application'] + } + } }, b: { + contentTypes: ['phrasing', 'flow'], allowedRoles: false }, base: { @@ -78,21 +100,29 @@ const htmlAria = { noAriaAttrs: true }, bdi: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, bdo: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, blockquote: { - allowedRoles: true + contentTypes: ['flow'], + allowedRoles: true, + shadowRoot: true }, body: { - allowedRoles: false + allowedRoles: false, + shadowRoot: true }, br: { - allowedRoles: ['presentation', 'none'] + contentTypes: ['phrasing', 'flow'], + allowedRoles: ['presentation', 'none'], + namingMethods: ['titleText', 'singleSpace'] }, button: { + contentTypes: ['interactive', 'phrasing', 'flow'], allowedRoles: [ 'checkbox', 'link', @@ -103,18 +133,23 @@ const htmlAria = { 'radio', 'switch', 'tab' - ] + ], + // 5.4 button Element + namingMethods: 'subtreeText' }, canvas: { - allowedRoles: true + allowedRoles: true, + contentTypes: ['embedded', 'phrasing', 'flow'] }, caption: { allowedRoles: false }, cite: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, code: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, col: { @@ -126,9 +161,11 @@ const htmlAria = { 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 @@ -142,81 +179,111 @@ const htmlAria = { 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: { - allowedRoles: true + 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: { - allowedRoles: ['none', 'presentation', 'radiogroup'] + 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 + allowedRoles: true, + // 5.9 figure and figcaption Elements + namingMethods: ['figureText', 'titleText'] }, footer: { - allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'] + 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' } @@ -226,22 +293,29 @@ const htmlAria = { noAriaAttrs: true }, header: { - allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'] + contentTypes: ['flow'], + allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'], + shadowRoot: true }, hgroup: { + contentTypes: ['heading', 'flow'], allowedRoles: true }, hr: { - allowedRoles: ['none', 'presentation', 'doc-pagebreak'] + 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: { @@ -283,6 +357,14 @@ const htmlAria = { // 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'], + namingMethods: 'altText' } } }, @@ -301,6 +383,15 @@ const htmlAria = { 'tab' ] }, + // 5.2 input type="button", input type="submit" and input type="reset" + buttonType: { + matches: { + attributes: { + type: ['button', 'submit', 'reset'] + } + }, + namingMethods: ['valueText', 'titleText', 'buttonDefaultText'] + }, checkboxPressed: { matches: '[type="checkbox"][aria-pressed]', allowedRoles: ['button', 'menuitemcheckbox', 'option', 'switch'], @@ -342,6 +433,7 @@ const htmlAria = { }, hidden: { matches: '[type="hidden"]', + contentTypes: ['phrasing', 'flow'], allowedRoles: false, noAriaAttrs: true }, @@ -356,6 +448,14 @@ const htmlAria = { 'menuitemradio', 'radio', 'switch' + ], + // 5.3 input type="image" + namingMethods: [ + 'altText', + 'valueText', + 'labelText', + 'titleText', + 'buttonDefaultText' ] }, radio: { @@ -369,24 +469,25 @@ const htmlAria = { matches: '[type="text"][list]', allowedRoles: false }, - // Note: this covers type=text and any other input type default: { - matches: { - attributes: { - list: null - } - }, - allowedRoles: ['combobox', 'searchbox', 'spinbutton'] + 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: { @@ -413,23 +514,30 @@ const htmlAria = { } }, link: { + contentTypes: ['phrasing', 'flow'], allowedRoles: false, noAriaAttrs: true }, main: { - allowedRoles: false + 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', @@ -445,23 +553,45 @@ const htmlAria = { ] }, meta: { - allowedRoles: false, - noAriaAttrs: true + variant: { + itemprop: { + matches: '[itemprop]', + contentTypes: ['phrasing', 'flow'] + }, + default: { + allowedRoles: false, + noAriaAttrs: true + } + } }, meter: { + contentTypes: ['phrasing', 'flow'], allowedRoles: false }, nav: { - allowedRoles: ['doc-index', 'doc-pagelist', 'doc-toc'] + contentTypes: ['sectioning', 'flow'], + allowedRoles: ['doc-index', 'doc-pagelist', 'doc-toc'], + shadowRoot: true }, noscript: { + contentTypes: ['phrasing', 'flow'], allowedRoles: false, noAriaAttrs: true }, object: { - allowedRoles: ['application', 'document', 'img'] + variant: { + usemap: { + matches: '[usemap]', + contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'] + }, + default: { + contentTypes: ['embedded', 'phrasing', 'flow'], + allowedRoles: ['application', 'document', 'img'] + } + } }, ol: { + contentTypes: ['flow'], allowedRoles: [ 'directory', 'group', @@ -486,23 +616,31 @@ const htmlAria = { } }, output: { - allowedRoles: true + contentTypes: ['phrasing', 'flow'], + allowedRoles: true, + // 5.6 output Element + namingMethods: 'subtreeText' }, p: { - allowedRoles: true + 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', @@ -511,6 +649,7 @@ const htmlAria = { } }, q: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, rp: { @@ -520,19 +659,24 @@ const htmlAria = { 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', @@ -581,7 +725,8 @@ const htmlAria = { 'doc-pullquote', 'doc-qna', 'doc-toc' - ] + ], + shadowRoot: true }, select: { variant: { @@ -594,17 +739,19 @@ const htmlAria = { }, allowedRoles: ['menu'] }, - listbox: { - matches: '[multiple], select[size]:not([size="1"])', + default: { + contentTypes: ['interactive', 'phrasing', 'flow'], allowedRoles: false } } }, slot: { + contentTypes: ['phrasing', 'flow'], allowedRoles: false, noAriaAttrs: true }, small: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, source: { @@ -612,9 +759,12 @@ const htmlAria = { noAriaAttrs: true }, span: { - allowedRoles: true + contentTypes: ['phrasing', 'flow'], + allowedRoles: true, + shadowRoot: true }, strong: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, style: { @@ -622,32 +772,44 @@ const htmlAria = { noAriaAttrs: true }, svg: { + contentTypes: ['embedded', 'phrasing', 'flow'], allowedRoles: ['application', 'document', 'img'] }, sub: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, summary: { - allowedRoles: false + contentTypes: ['phrasing', 'flow'], + allowedRoles: false, + // 5.8 summary Element + namingMethods: 'subtreeText' }, sup: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, table: { - allowedRoles: true + 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 @@ -656,6 +818,7 @@ const htmlAria = { allowedRoles: true }, time: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, title: { @@ -676,9 +839,11 @@ const htmlAria = { noAriaAttrs: true }, u: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, ul: { + contentTypes: ['flow'], allowedRoles: [ 'directory', 'group', @@ -694,14 +859,25 @@ const htmlAria = { ] }, var: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true }, video: { - allowedRoles: ['application'] + variant: { + controls: { + matches: '[controls]', + contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'] + }, + default: { + contentTypes: ['embedded', 'phrasing', 'flow'], + allowedRoles: ['application'] + } + } }, wbr: { + contentTypes: ['phrasing', 'flow'], allowedRoles: true } }; -export default htmlAria; +export default htmlElms; From 7ec32934fb793a79ed867a8b1d5dd355250794ce Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 7 Jul 2020 16:11:29 -0600 Subject: [PATCH 5/9] comment --- lib/standards/html-elms.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js index cd615e15a3..ac8e3c9286 100644 --- a/lib/standards/html-elms.js +++ b/lib/standards/html-elms.js @@ -364,6 +364,7 @@ const htmlElms = { }, default: { contentTypes: ['embedded', 'phrasing', 'flow'], + // 5.10 img Element namingMethods: 'altText' } } From c3b2ede30f0bae4b150fff0f210104cfaa708efd Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Wed, 8 Jul 2020 10:15:22 -0600 Subject: [PATCH 6/9] Update lib/standards/html-elms.js Co-authored-by: Wilco Fiers --- lib/standards/html-elms.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js index ac8e3c9286..b0ae68ac59 100644 --- a/lib/standards/html-elms.js +++ b/lib/standards/html-elms.js @@ -86,10 +86,10 @@ const htmlElms = { contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'] }, default: { - contentTypes: ['embedded', 'phrasing', 'flow'], - allowedRoles: ['application'] + contentTypes: ['embedded', 'phrasing', 'flow'] } - } + }, + allowedRoles: ['application'] }, b: { contentTypes: ['phrasing', 'flow'], From c06d75364804e9ae17a3e5881feabb41e512772b Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Wed, 8 Jul 2020 10:15:30 -0600 Subject: [PATCH 7/9] Update lib/standards/html-elms.js Co-authored-by: Wilco Fiers --- lib/standards/html-elms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js index b0ae68ac59..0b140c796b 100644 --- a/lib/standards/html-elms.js +++ b/lib/standards/html-elms.js @@ -320,7 +320,7 @@ const htmlElms = { }, img: { variant: { - alt: { + nonEmptyAlt: { matches: { attributes: { alt: '/.+/' From 3cd547c91a7f6988af98ffdb67b900e05f22e379 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Wed, 8 Jul 2020 10:15:39 -0600 Subject: [PATCH 8/9] Update lib/standards/html-elms.js Co-authored-by: Wilco Fiers --- lib/standards/html-elms.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js index 0b140c796b..c2ce09a5ff 100644 --- a/lib/standards/html-elms.js +++ b/lib/standards/html-elms.js @@ -363,11 +363,11 @@ const htmlElms = { contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'] }, default: { - contentTypes: ['embedded', 'phrasing', 'flow'], - // 5.10 img Element - namingMethods: 'altText' + contentTypes: ['embedded', 'phrasing', 'flow'] } - } + }, + // 5.10 img Element + namingMethods: 'altText' }, input: { variant: { From 0b2e531452328b06017fe98a1e6555b995dd1b12 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Wed, 8 Jul 2020 10:28:36 -0600 Subject: [PATCH 9/9] fixes, use properties for inputs instead of attributes --- lib/standards/html-elms.js | 79 +++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/lib/standards/html-elms.js b/lib/standards/html-elms.js index c2ce09a5ff..f9cda2b0ff 100644 --- a/lib/standards/html-elms.js +++ b/lib/standards/html-elms.js @@ -89,6 +89,8 @@ const htmlElms = { 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: { @@ -372,7 +374,11 @@ const htmlElms = { input: { variant: { button: { - matches: '[type="button"]', + matches: { + properties: { + type: 'button' + } + }, allowedRoles: [ 'link', 'menuitem', @@ -387,21 +393,35 @@ const htmlElms = { // 5.2 input type="button", input type="submit" and input type="reset" buttonType: { matches: { - attributes: { + properties: { type: ['button', 'submit', 'reset'] } }, namingMethods: ['valueText', 'titleText', 'buttonDefaultText'] }, checkboxPressed: { - matches: '[type="checkbox"][aria-pressed]', + matches: { + properties: { + type: 'checkbox' + }, + attributes: { + 'aria-pressed': '/.*/' + } + }, allowedRoles: ['button', 'menuitemcheckbox', 'option', 'switch'], implicitAttrs: { 'aria-checked': 'false' } }, checkbox: { - matches: '[type="checkbox"]:not([aria-pressed])', + matches: { + properties: { + type: 'checkbox' + }, + attributes: { + 'aria-pressed': null + } + }, allowedRoles: ['menuitemcheckbox', 'option', 'switch'], implicitAttrs: { 'aria-checked': 'false' @@ -409,7 +429,7 @@ const htmlElms = { }, noRoles: { matches: { - attributes: { + properties: { type: [ 'color', 'date', @@ -433,14 +453,20 @@ const htmlElms = { allowedRoles: false }, hidden: { - matches: '[type="hidden"]', + matches: { + properties: { + type: 'hidden' + } + }, contentTypes: ['phrasing', 'flow'], allowedRoles: false, noAriaAttrs: true }, image: { matches: { - attributes: '[type="image"]' + properties: { + type: 'image' + } }, allowedRoles: [ 'link', @@ -460,14 +486,25 @@ const htmlElms = { ] }, radio: { - matches: '[type="radio"]', + matches: { + properties: { + type: 'radio' + } + }, allowedRoles: ['menuitemradio'], implicitAttrs: { 'aria-checked': 'false' } }, textWithList: { - matches: '[type="text"][list]', + matches: { + properties: { + type: 'text' + }, + attributes: { + list: '/.*/' + } + }, allowedRoles: false }, default: { @@ -558,12 +595,10 @@ const htmlElms = { itemprop: { matches: '[itemprop]', contentTypes: ['phrasing', 'flow'] - }, - default: { - allowedRoles: false, - noAriaAttrs: true } - } + }, + allowedRoles: false, + noAriaAttrs: true }, meter: { contentTypes: ['phrasing', 'flow'], @@ -586,10 +621,10 @@ const htmlElms = { contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'] }, default: { - contentTypes: ['embedded', 'phrasing', 'flow'], - allowedRoles: ['application', 'document', 'img'] + contentTypes: ['embedded', 'phrasing', 'flow'] } - } + }, + allowedRoles: ['application', 'document', 'img'] }, ol: { contentTypes: ['flow'], @@ -741,10 +776,10 @@ const htmlElms = { allowedRoles: ['menu'] }, default: { - contentTypes: ['interactive', 'phrasing', 'flow'], allowedRoles: false } - } + }, + contentTypes: ['interactive', 'phrasing', 'flow'] }, slot: { contentTypes: ['phrasing', 'flow'], @@ -870,10 +905,10 @@ const htmlElms = { contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'] }, default: { - contentTypes: ['embedded', 'phrasing', 'flow'], - allowedRoles: ['application'] + contentTypes: ['embedded', 'phrasing', 'flow'] } - } + }, + allowedRoles: ['application'] }, wbr: { contentTypes: ['phrasing', 'flow'],