diff --git a/.circleci/config.yml b/.circleci/config.yml index 9b27ebbca4..f2f20d6f9d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,7 @@ unix_nightly_box: &unix_nightly_box orbs: puppeteer: threetreeslight/puppeteer@0.1.2 - browser-tools: circleci/browser-tools@1.4.0 + browser-tools: circleci/browser-tools@1.4.4 set_npm_auth: &set_npm_auth run: npm config set "//registry.npmjs.org/:_authToken" $NPM_AUTH @@ -30,6 +30,13 @@ restore_build: &restore_build keys: - v9-cache-build-<< pipeline.git.revision >> +commands: + browser-tools-job: + steps: + - browser-tools/install-browser-tools: + # TODO: remove when chromedriver downloads are fixed + chrome-version: 116.0.5845.96 + jobs: # Fetch and cache dependencies. dependencies_unix: @@ -47,7 +54,7 @@ jobs: else echo "node_modules does not exist" fi - - browser-tools/install-browser-tools + - browser-tools-job - <<: *set_npm_auth - run: npm ci - run: npx browser-driver-manager install chromedriver --verbose @@ -85,7 +92,7 @@ jobs: <<: *unix_box steps: - checkout - - browser-tools/install-browser-tools + - browser-tools-job - <<: *restore_dependency_cache_unix - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build @@ -97,7 +104,7 @@ jobs: <<: *unix_box steps: - checkout - - browser-tools/install-browser-tools + - browser-tools-job - <<: *restore_dependency_cache_unix - <<: *restore_build - run: npm run test -- --browsers Firefox @@ -109,7 +116,7 @@ jobs: <<: *unix_box steps: - checkout - - browser-tools/install-browser-tools + - browser-tools-job - <<: *restore_dependency_cache_unix - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build @@ -121,7 +128,7 @@ jobs: <<: *unix_box steps: - checkout - - browser-tools/install-browser-tools + - browser-tools-job - <<: *restore_dependency_cache_unix - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build @@ -133,7 +140,7 @@ jobs: <<: *unix_box steps: - checkout - - browser-tools/install-browser-tools + - browser-tools-job - <<: *restore_dependency_cache_unix - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build @@ -145,7 +152,7 @@ jobs: <<: *unix_box steps: - checkout - - browser-tools/install-browser-tools + - browser-tools-job - <<: *restore_dependency_cache_unix - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build @@ -157,7 +164,7 @@ jobs: <<: *unix_box steps: - checkout - - browser-tools/install-browser-tools + - browser-tools-job - <<: *restore_dependency_cache_unix - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build @@ -192,7 +199,7 @@ jobs: steps: - checkout - <<: *restore_dependency_cache_unix - - browser-tools/install-browser-tools + - browser-tools-job # install ACT rules # install first as for some reason installing a single package # also re-installs all repo dependencies as well @@ -208,7 +215,7 @@ jobs: steps: - checkout - <<: *restore_dependency_cache_unix - - browser-tools/install-browser-tools + - browser-tools-job # install ARIA practices # install first as for some reason installing a single package # also re-installs all repo dependencies as well diff --git a/.eslintrc.js b/.eslintrc.js index 855c45f454..0dd3a87d43 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -52,6 +52,7 @@ module.exports = { 'dot-notation': 2, 'no-new-func': 0, 'no-new-wrappers': 0, + 'no-shadow': 2, 'no-restricted-syntax': [ 'error', { diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b3f81f34..5cecc5de4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,51 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [4.8.0](https://github.com/dequelabs/axe-core/compare/v4.7.2...v4.8.0) (2023-09-06) + +### Features + +- add EN.301.549 tags to rules ([#4063](https://github.com/dequelabs/axe-core/issues/4063)) ([de3da89](https://github.com/dequelabs/axe-core/commit/de3da897e56179d94ef8a0dc1a667b5663c489d1)) +- add rule aria-conditional-attr ([#4094](https://github.com/dequelabs/axe-core/issues/4094)) ([d417630](https://github.com/dequelabs/axe-core/commit/d417630e89a41603426c2bb545b49057f03ed8e5)) +- add rule aria-deprecated-role ([#4074](https://github.com/dequelabs/axe-core/issues/4074)) ([03f2771](https://github.com/dequelabs/axe-core/commit/03f2771ab43bd877b7919c29b4f5e737b5a69544)) +- **aria-input/toggle-field-name:** set impact always to serious ([#4095](https://github.com/dequelabs/axe-core/issues/4095)) ([e031d68](https://github.com/dequelabs/axe-core/commit/e031d68652229a80ba6ff7d02d29a50a846bfa5b)) +- **aria-prohibited-attr:** add rule aria-prohibited-attr ([#4088](https://github.com/dequelabs/axe-core/issues/4088)) ([7b115d3](https://github.com/dequelabs/axe-core/commit/7b115d3a9e7256ae2c0a1d7d0f9ba791a06c8599)) +- **checks:** enable help-same-as-label, but remove from rules ([#4096](https://github.com/dequelabs/axe-core/issues/4096)) ([034038a](https://github.com/dequelabs/axe-core/commit/034038a625b390ed25b30fccc96e3fc1f384dbc1)) +- **d.ts:** improve axe.d.ts types ([#4081](https://github.com/dequelabs/axe-core/issues/4081)) ([7c5f991](https://github.com/dequelabs/axe-core/commit/7c5f99143a1d97e294d21e14917f4963013fc6f8)), closes [#3966](https://github.com/dequelabs/axe-core/issues/3966) +- deprecate & disable duplicate-id / duplicate-id-active ([#4071](https://github.com/dequelabs/axe-core/issues/4071)) ([733c45e](https://github.com/dequelabs/axe-core/commit/733c45e6a40a9f8ff6e75f7db864edff0b404ca2)) +- Deprecate impact on checks, use rules instead ([#4114](https://github.com/dequelabs/axe-core/issues/4114)) ([2cc5547](https://github.com/dequelabs/axe-core/commit/2cc5547634ee783701675631ee3978129707e6f0)) +- **duplicate-id-aria:** set to review on fail and tag as wcag412 ([#4075](https://github.com/dequelabs/axe-core/issues/4075)) ([9f1a3e3](https://github.com/dequelabs/axe-core/commit/9f1a3e3cbffbe09eaf90fa254c6421fd4264cf4a)) +- **impact:** aria-roles / aria-valid-attr-value is always "critical" ([#4112](https://github.com/dequelabs/axe-core/issues/4112)) ([5cc8041](https://github.com/dequelabs/axe-core/commit/5cc8041f74a6f015dcbca36ee7414767528277c2)) +- **impact:** scope-attr-valid is always "moderate" ([#4113](https://github.com/dequelabs/axe-core/issues/4113)) ([131f552](https://github.com/dequelabs/axe-core/commit/131f5524e8c8022ace047ac6d69d779460c85fe6)) +- **new-rule:** aria-braille-equivalent finds incorrect uses of aria-braille attributes ([#4107](https://github.com/dequelabs/axe-core/issues/4107)) ([6260a2f](https://github.com/dequelabs/axe-core/commit/6260a2f25781b465960aec0b1e7781be5496c9bd)) +- **page-no-duplicate-banner/contentinfo:** deprecate options.nativeScopeFilter, take into ancestors with sectioning roles ([#4105](https://github.com/dequelabs/axe-core/issues/4105)) ([c6e07be](https://github.com/dequelabs/axe-core/commit/c6e07bec43ef1935f2afb9429e9f12a937c38f14)) + +### Bug Fixes + +- **access-name:** get name from header elements ([#4097](https://github.com/dequelabs/axe-core/issues/4097)) ([fbe99bf](https://github.com/dequelabs/axe-core/commit/fbe99bf87a3ebd7d6bc4b4eca7a58bbff28a5b23)) +- add element semantics ([#4115](https://github.com/dequelabs/axe-core/issues/4115)) ([637bf6c](https://github.com/dequelabs/axe-core/commit/637bf6c58c3e62877511687d8a6046f8aee63f03)) +- **aria-allowed-attr:** pass aria-expanded on checkbox & switch ([#4110](https://github.com/dequelabs/axe-core/issues/4110)) ([fcf76e0](https://github.com/dequelabs/axe-core/commit/fcf76e04d8534dfed75caf1f2c4a74ef4faa29ae)), closes [#3339](https://github.com/dequelabs/axe-core/issues/3339) +- **aria-allowed-role:** Add doc-glossary to allowed roles for aside element ([#4083](https://github.com/dequelabs/axe-core/issues/4083)) ([6ca38f6](https://github.com/dequelabs/axe-core/commit/6ca38f65c28e9df0c429df1018b519394e22507e)) +- **aria-allowed-role:** add meter to allowed roles for named img ([#4055](https://github.com/dequelabs/axe-core/issues/4055)) ([173f29d](https://github.com/dequelabs/axe-core/commit/173f29da9558a1fd0510609aacc9e4deebdf74b4)), closes [#4054](https://github.com/dequelabs/axe-core/issues/4054) +- **aria-required-childen:** test visibility of grandchildren ([#4091](https://github.com/dequelabs/axe-core/issues/4091)) ([a202b69](https://github.com/dequelabs/axe-core/commit/a202b69b955b45fc10abe06059925013bede07eb)) +- **aria-text:** typo in rule description ([#4131](https://github.com/dequelabs/axe-core/issues/4131)) ([85a0e9c](https://github.com/dequelabs/axe-core/commit/85a0e9c358ae78b4ceb2093dc9891d523eaf25b2)) +- **aria-valid-attr-value:** allow empty value on aria-braille\* & aria-valuetext ([#4109](https://github.com/dequelabs/axe-core/issues/4109)) ([c4c3e65](https://github.com/dequelabs/axe-core/commit/c4c3e658408d89b5ccd747d5fad9031c5d3a0de0)) +- avoid memory issues by doing better cleanup ([#4059](https://github.com/dequelabs/axe-core/issues/4059)) ([16c5cfa](https://github.com/dequelabs/axe-core/commit/16c5cfa66615537b2131a5a381fbed9a5336d853)) +- avoid problems from element IDs that exist on object prototype ([#4060](https://github.com/dequelabs/axe-core/issues/4060)) ([8d135dd](https://github.com/dequelabs/axe-core/commit/8d135dd58ccd72393b981464f66a01e770d9cf95)) +- **color-contrast:** correctly handle flex and position ([#4086](https://github.com/dequelabs/axe-core/issues/4086)) ([9d5f496](https://github.com/dequelabs/axe-core/commit/9d5f496c4ee7e95d113cdceab284fb6ca7be98e3)) +- **color-contrast:** get text stoke from offset shadows ([#4079](https://github.com/dequelabs/axe-core/issues/4079)) ([13acffe](https://github.com/dequelabs/axe-core/commit/13acffe540f834f5321f9c5c124b565cec92ce06)) +- **color-contrast:** ignore format unicode characters ([#4102](https://github.com/dequelabs/axe-core/issues/4102)) ([049522e](https://github.com/dequelabs/axe-core/commit/049522e3ef0676b198763e39e8c8a300c8eeb195)) +- **color-contrast:** ignore zero width characters ([#4103](https://github.com/dequelabs/axe-core/issues/4103)) ([4deb0a0](https://github.com/dequelabs/axe-core/commit/4deb0a0876d574c3d7d586b27ae07d4f5be586db)) +- **color-contrast:** process non-rgb color functions ([#4092](https://github.com/dequelabs/axe-core/issues/4092)) ([9634282](https://github.com/dequelabs/axe-core/commit/963428256d7a119c7b6188868eb9d4a4651a8949)) +- **commons/dom/createGrid:** only add the visible, non-overflow areas of an element to the grid ([#4101](https://github.com/dequelabs/axe-core/issues/4101)) ([d77f47b](https://github.com/dequelabs/axe-core/commit/d77f47b8dd346e205b6cddb4f6ce544ef5f699e4)) +- ensure reporter errors can propagate ([#4111](https://github.com/dequelabs/axe-core/issues/4111)) ([080cc1b](https://github.com/dequelabs/axe-core/commit/080cc1b5f5ed048ab435c312dec291d1b4eb4393)) +- ignore stylesheets fetching style tag in jsdom ([#4138](https://github.com/dequelabs/axe-core/issues/4138)) ([d7c16a4](https://github.com/dequelabs/axe-core/commit/d7c16a481d5a5f68c1e970040e01f125b2025378)) +- **jsdom:** allow axe.setup() without a global window ([#4116](https://github.com/dequelabs/axe-core/issues/4116)) ([33b0314](https://github.com/dequelabs/axe-core/commit/33b0314922762c0e562b613219b5cc96e3ce31f5)) +- **target-size:** correctly calculate bounding box ([#4125](https://github.com/dequelabs/axe-core/issues/4125)) ([1494b4c](https://github.com/dequelabs/axe-core/commit/1494b4c2159fbae2a937cc7c3dc1d269915ef4d4)) +- **target-size:** update to match new spacing requirements ([#4117](https://github.com/dequelabs/axe-core/issues/4117)) ([49eaa0e](https://github.com/dequelabs/axe-core/commit/49eaa0e1663724f70b2571cc7393e306bf0c7321)) +- Use correct copyright year ([#4098](https://github.com/dequelabs/axe-core/issues/4098)) ([cab6a2b](https://github.com/dequelabs/axe-core/commit/cab6a2b2f012f5963d0f4294217578c790508fcc)) +- **utils/clone:** don't try to clone elements from different window context ([#4072](https://github.com/dequelabs/axe-core/issues/4072)) ([55000d0](https://github.com/dequelabs/axe-core/commit/55000d066f018e4c3f2b9ec4eabf23eb1781dfbb)) + ### [4.7.2](https://github.com/dequelabs/axe-core/compare/v4.7.1...v4.7.2) (2023-05-25) ### Bug Fixes diff --git a/axe.d.ts b/axe.d.ts index c174b24c0f..403e504873 100644 --- a/axe.d.ts +++ b/axe.d.ts @@ -164,9 +164,9 @@ declare namespace axe { interface NodeResult { html: string; impact?: ImpactValue; - target: string[]; + target: UnlabelledFrameSelector; xpath?: string[]; - ancestry?: string[]; + ancestry?: UnlabelledFrameSelector; any: CheckResult[]; all: CheckResult[]; none: CheckResult[]; @@ -181,8 +181,11 @@ declare namespace axe { relatedNodes?: RelatedNode[]; } interface RelatedNode { - target: string[]; html: string; + target: UnlabelledFrameSelector; + xpath?: string[]; + ancestry?: UnlabelledFrameSelector; + element?: HTMLElement; } interface RuleLocale { [key: string]: { @@ -193,7 +196,7 @@ declare namespace axe { interface CheckMessages { pass: string | { [key: string]: string }; fail: string | { [key: string]: string }; - incomplete: string | { [key: string]: string }; + incomplete?: string | { [key: string]: string }; } interface CheckLocale { [key: string]: CheckMessages; @@ -257,10 +260,31 @@ declare namespace axe { brand?: string; application?: string; } + interface CheckHelper { + async: () => (result: boolean | undefined | Error) => void; + data: (data: unknown) => void; + relatedNodes: (nodes: Element[]) => void; + } + interface AfterResult { + id: string; + data?: unknown; + relatedNodes: SerialDqElement[]; + result: boolean | undefined; + node: SerialDqElement; + } interface Check { id: string; - evaluate?: Function | string; - after?: Function | string; + evaluate?: + | string + | (( + this: CheckHelper, + node: Element, + options: unknown, + virtualNode: VirtualNode + ) => boolean | undefined | void); + after?: + | string + | ((results: AfterResult[], options: unknown) => AfterResult[]); options?: any; matches?: string; enabled?: boolean; @@ -280,9 +304,10 @@ declare namespace axe { all?: string[]; none?: string[]; tags?: string[]; - matches?: string; + matches?: string | ((node: Element, virtualNode: VirtualNode) => boolean); reviewOnFail?: boolean; - metadata?: Omit; + actIds?: string[]; + metadata?: Omit; } interface AxePlugin { id: string; @@ -346,7 +371,8 @@ declare namespace axe { type AxeReporter = ( rawResults: RawResult[], option: RunOptions, - callback: (report: T) => void + resolve: (report: T) => void, + reject: (error: Error) => void ) => void; interface VirtualNode { @@ -367,6 +393,42 @@ declare namespace axe { shadowSelect: (selector: CrossTreeSelector) => Element | null; shadowSelectAll: (selector: CrossTreeSelector) => Element[]; getStandards(): Required; + DqElement: new ( + elm: Element, + options?: { absolutePaths?: boolean } + ) => SerialDqElement; + uuid: ( + options?: { random?: Uint8Array | Array }, + buf?: Uint8Array | Array, + offset?: number + ) => string | Uint8Array | Array; + } + + interface Aria { + getRoleType: (role: string | Element | VirtualNode | null) => string | null; + } + + interface Dom { + isFocusable: (node: Element | VirtualNode) => boolean; + isNativelyFocusable: (node: Element | VirtualNode) => boolean; + } + + type AccessibleTextOptions = { + inControlContext?: boolean; + inLabelledByContext?: boolean; + }; + + interface Text { + accessibleText: ( + element: Element, + options?: AccessibleTextOptions + ) => string; + } + + interface Commons { + aria: Aria; + dom: Dom; + text: Text; } interface EnvironmentData { @@ -380,6 +442,7 @@ declare namespace axe { let version: string; let plugins: any; let utils: Utils; + let commons: Commons; /** * Source string to use as an injected script in Selenium diff --git a/bower.json b/bower.json index 09e0dfb056..298c37a578 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "axe-core", - "version": "4.7.2", + "version": "4.8.0", "deprecated": true, "contributors": [ { diff --git a/build/configure.js b/build/configure.js index 79ff1b5880..d4998985e5 100644 --- a/build/configure.js +++ b/build/configure.js @@ -41,7 +41,7 @@ function buildRules(grunt, options, commons, callback) { var axeImpact = Object.freeze(['minor', 'moderate', 'serious', 'critical']); // TODO: require('../axe') does not work if grunt configure is moved after uglify, npm test breaks with undefined. Complicated grunt concurrency issue. var locale = getLocale(grunt, options); options.getFiles = false; - buildManual(grunt, options, commons, function (result) { + buildManual(grunt, options, commons, function (build) { var metadata = { rules: {}, checks: {} @@ -96,8 +96,8 @@ function buildRules(grunt, options, commons, callback) { .join('\n'); var tags = options.tags ? options.tags.split(/\s*,\s*/) : []; - var rules = result.rules; - var checks = result.checks; + var rules = build.rules; + var checks = build.checks; // Translate checks before parsing them so that translations // get applied to the metadata object @@ -113,9 +113,9 @@ function buildRules(grunt, options, commons, callback) { function parseMetaData(source, propType) { var data = source.metadata; - var key = source.id || source.type; - if (key && locale && locale[propType] && propType !== 'checks') { - data = locale[propType][key] || data; + var id = source.id || source.type; + if (id && locale && locale[propType] && propType !== 'checks') { + data = locale[propType][id] || data; } var result = clone(data) || {}; @@ -151,8 +151,8 @@ function buildRules(grunt, options, commons, callback) { } function getIncompleteMsg(summaries) { - var summary = summaries.find(function (summary) { - return typeof summary.incompleteFallbackMessage === 'string'; + var summary = summaries.find(function (element) { + return typeof element.incompleteFallbackMessage === 'string'; }); return summary ? summary.incompleteFallbackMessage : ''; } @@ -184,8 +184,8 @@ function buildRules(grunt, options, commons, callback) { }); } - function findCheck(checks, id) { - return checks.filter(function (check) { + function findCheck(checkCollection, id) { + return checkCollection.filter(function (check) { if (check.id === id) { return true; } @@ -336,21 +336,21 @@ function buildRules(grunt, options, commons, callback) { metadata.rules[rule.id] = parseMetaData(rule, 'rules'); // Translate rules } - var rules; + var result; if (rule.tags.includes('deprecated')) { - rules = descriptions.deprecated.rules; + result = descriptions.deprecated.rules; } else if (rule.tags.includes('experimental')) { - rules = descriptions.experimental.rules; + result = descriptions.experimental.rules; } else if (rule.tags.find(tag => tag.includes('aaa'))) { - rules = descriptions.wcag2aaa.rules; + result = descriptions.wcag2aaa.rules; } else if (rule.tags.includes('best-practice')) { - rules = descriptions.bestPractice.rules; + result = descriptions.bestPractice.rules; } else if (rule.tags.find(tag => tag.startsWith('wcag2a'))) { - rules = descriptions.wcag20.rules; + result = descriptions.wcag20.rules; } else if (rule.tags.find(tag => tag.startsWith('wcag21a'))) { - rules = descriptions.wcag21.rules; + result = descriptions.wcag21.rules; } else { - rules = descriptions.wcag22.rules; + result = descriptions.wcag22.rules; } var issueType = []; @@ -363,7 +363,7 @@ function buildRules(grunt, options, commons, callback) { var actLinks = createActLinksForRule(rule); - rules.push([ + result.push([ `[${rule.id}](https://dequeuniversity.com/rules/axe/${axeVersion}/${rule.id}?application=RuleDescription)`, entities.encode(rule.metadata.description), impact, @@ -408,8 +408,8 @@ ${TOC} ${ruleTables}`; // Translate failureSummaries - metadata.failureSummaries = createFailureSummaryObject(result.misc); - metadata.incompleteFallbackMessage = getIncompleteMsg(result.misc); + metadata.failureSummaries = createFailureSummaryObject(build.misc); + metadata.incompleteFallbackMessage = getIncompleteMsg(build.misc); callback({ auto: replaceFunctions( diff --git a/build/rule-generator.js b/build/rule-generator.js index 208a488f05..ee2c8eca75 100644 --- a/build/rule-generator.js +++ b/build/rule-generator.js @@ -49,7 +49,7 @@ async function run() { const result = await Promise.all( files.map(async meta => { const path = `${meta.dir}/${meta.name}`; - const content = meta.content; + const content = meta.content + '\n'; await createFile(path, content); return path; }) diff --git a/build/rule-generator/get-files-metadata.js b/build/rule-generator/get-files-metadata.js index cc396ad1f3..50abce3091 100644 --- a/build/rule-generator/get-files-metadata.js +++ b/build/rule-generator/get-files-metadata.js @@ -1,4 +1,5 @@ const directories = require('./directories'); +const outdent = require('outdent'); /** * Helper to convert a given string to camel case (split by hyphens if any) @@ -23,8 +24,9 @@ const getRuleSpecFileMeta = (ruleName, ruleHasMatches, ruleChecks) => { name: `${ruleName}.json`, content: JSON.stringify( { - id: `${ruleName}`, - selector: '', + id: ruleName, + impact: '', + selector: '*', ...(ruleHasMatches && { matches: `${ruleName}-matches` }), @@ -70,15 +72,13 @@ const getRuleMatchesFileMeta = ( const fnName = `${camelCase(ruleName)}Matches`; const ruleMatchesJs = { name: `${ruleName}-matches.js`, - content: ` - // TODO: Filter node(s) - - function ${fnName}(node, virtualNode) { - return node - } + content: outdent` + // TODO: Filter node(s) - export default ${fnName} - `, + export default function ${fnName}(node, virtualNode) { + return true; + } + `, dir: directories.rules }; files.push(ruleMatchesJs); @@ -87,12 +87,18 @@ const getRuleMatchesFileMeta = ( if (ruleHasUnitTestAssets) { const ruleMatchesTestJs = { name: `${ruleName}-matches.js`, - content: ` - describe('${ruleName}-matches', function() { - 'use strict'; - // TODO: Write tests - }) - `, + content: outdent` + describe('${ruleName}-matches', () => { + const rule = axe.utils.getRule('${ruleName}'); + const { queryFixture } = axe.testUtils; + + // TODO: Replace with real tests + it('returns false for paragraphs', () => { + const vNode = queryFixture('

Hello world

'); + assert.isFalse(rule.matches(vNode.actualNode, vNode)); + }); + }); + `, dir: directories.testRuleMatches }; files.push(ruleMatchesTestJs); @@ -116,7 +122,6 @@ const getCheckSpecFileMeta = (name, dir) => { id: `${name}`, evaluate: `${name}-evaluate`, metadata: { - impact: '', messages: { pass: '', fail: '', @@ -142,13 +147,12 @@ const getCheckJsFileMeta = (name, dir) => { const fnName = `${camelCase(name)}Evaluate`; return { name: `${name}-evaluate.js`, - content: ` - // TODO: Logic for check - function ${fnName}(node, options, virtualNode) { - return true - } - export default ${fnName}; - `, + content: outdent` + // TODO: Logic for check + export default function ${fnName}(node, options, virtualNode) { + return true + } + `, dir }; }; @@ -163,12 +167,24 @@ const getCheckJsFileMeta = (name, dir) => { const getCheckTestJsFileMeta = (name, dir) => { return { name: `${name}.js`, - content: ` - describe('${name} tests', function() { - 'use strict'; - // TODO: Write tests - }) - `, + content: outdent` + describe('${name} tests', () => { + const { checkSetup, getCheckEvaluate } = axe.testUtils; + const checkContext = axe.testUtils.MockCheckContext(); + const checkEvaluate = getCheckEvaluate('${name}'); + + afterEach(() => { + checkContext.reset(); + }); + + // TODO: Replace this with real tests for this check + it('returns false when img has no alt', () => { + const params = checkSetup(''); + assert.isFalse(checkEvaluate.apply(checkContext, params)); + assert.deepEqual(checkContext._data, { messageKey: 'missing-alt' }); + }); + }); + `, dir }; }; diff --git a/build/tasks/add-locale.js b/build/tasks/add-locale.js index f3845d5a19..35e15e75df 100644 --- a/build/tasks/add-locale.js +++ b/build/tasks/add-locale.js @@ -45,7 +45,7 @@ module.exports = function (grunt) { var commons = file.src[0]; buildManual(grunt, options, commons, function (result) { - var out = { + var locale = { lang: options.lang, rules: result.rules.reduce(function (out, rule) { out[rule.id] = rule.metadata; @@ -74,10 +74,10 @@ module.exports = function (grunt) { var oldMessages = grunt.file.readJSON(localeFile); // mergeMessages mutates out - mergeMessages(out, oldMessages); + mergeMessages(locale, oldMessages); } - grunt.file.write(file.dest, JSON.stringify(out, null, ' ')); + grunt.file.write(file.dest, JSON.stringify(locale, null, ' ')); console.log('created file at', file.dest); }); }); diff --git a/build/tasks/esbuild.js b/build/tasks/esbuild.js index 6a5479dff9..30dbeb4ef6 100644 --- a/build/tasks/esbuild.js +++ b/build/tasks/esbuild.js @@ -26,7 +26,10 @@ module.exports = function (grunt) { bundle: true }) .then(done) - .catch(done); + .catch(e => { + grunt.fail.fatal(e); + done(); + }); }); }); } diff --git a/build/tasks/validate.js b/build/tasks/validate.js index c36dfddb53..9af5edb28a 100644 --- a/build/tasks/validate.js +++ b/build/tasks/validate.js @@ -112,8 +112,8 @@ function createSchemas() { conform: 'Must have at least two valid messages' } }, + // @deprecated: Use impact on rules instead impact: { - required: true, type: 'string', enum: ['minor', 'moderate', 'serious', 'critical'] } @@ -134,6 +134,7 @@ function createSchemas() { type: 'string' }, impact: { + required: true, type: 'string', enum: ['minor', 'moderate', 'serious', 'critical'] }, @@ -198,12 +199,6 @@ function createSchemas() { type: 'array', items: { type: 'string' - }, - conform: function hasCategoryTag(tags) { - return tags.some(tag => tag.includes('cat.')); - }, - messages: { - conform: 'must include a category tag' } }, actIds: { @@ -307,5 +302,168 @@ function validateRule({ tags, metadata }) { if (help.toLowerCase().includes(prohibitedWord)) { issues.push(`metadata.help can not contain the word '${prohibitedWord}'.`); } + + issues.push(...findTagIssues(tags)); + return issues; +} + +const miscTags = ['ACT', 'experimental', 'review-item', 'deprecated']; + +const categories = [ + 'aria', + 'color', + 'forms', + 'keyboard', + 'language', + 'name-role-value', + 'parsing', + 'semantics', + 'sensory-and-visual-cues', + 'structure', + 'tables', + 'text-alternatives', + 'time-and-media' +]; + +const standardsTags = [ + { + // Has to be first, as others rely on the WCAG level getting picked up first + name: 'WCAG', + standardRegex: /^wcag2(1|2)?a{1,3}(-obsolete)?$/, + criterionRegex: /^wcag\d{3,4}$/ + }, + { + name: 'Section 508', + standardRegex: /^section508$/, + criterionRegex: /^section508\.\d{1,2}\.[a-z]$/, + wcagLevelRegex: /^wcag2aa?$/ + }, + { + name: 'Trusted Tester', + standardRegex: /^TTv5$/, + criterionRegex: /^TT\d{1,3}\.[a-z]$/, + wcagLevelRegex: /^wcag2aa?$/ + }, + { + name: 'EN 301 549', + standardRegex: /^EN-301-549$/, + criterionRegex: /^EN-9\.[1-4]\.[1-9]\.\d{1,2}$/, + wcagLevelRegex: /^wcag21?aa?$/ + } +]; + +function findTagIssues(tags) { + const issues = []; + const catTags = tags.filter(tag => tag.startsWith('cat.')); + const bestPracticeTags = tags.filter(tag => tag === 'best-practice'); + + // Category + if (catTags.length !== 1) { + issues.push(`Must have exactly one cat. tag, got ${catTags.length}`); + } + if (catTags.length && !categories.includes(catTags[0].slice(4))) { + issues.push(`Invalid category tag: ${catTags[0]}`); + } + if (!startsWith(tags, catTags)) { + issues.push(`Tag ${catTags[0]} must be before ${tags[0]}`); + } + tags = removeTags(tags, catTags); + + // Best practice + if (bestPracticeTags.length > 1) { + issues.push( + `Only one best-practice tag is allowed, got ${bestPracticeTags.length}` + ); + } + if (!startsWith(tags, bestPracticeTags)) { + issues.push(`Tag ${bestPracticeTags[0]} must be before ${tags[0]}`); + } + tags = removeTags(tags, bestPracticeTags); + + const standards = {}; + // WCAG, Section 508, Trusted Tester, EN 301 549 + for (const { + name, + standardRegex, + criterionRegex, + wcagLevelRegex + } of standardsTags) { + const standardTags = tags.filter(tag => tag.match(standardRegex)); + const criterionTags = tags.filter(tag => tag.match(criterionRegex)); + if (!standardTags.length && !criterionTags.length) { + continue; + } + + standards[name] = { + name, + standardTag: standardTags[0] ?? null, + criterionTags + }; + if (bestPracticeTags.length !== 0) { + issues.push(`${name} tags cannot be used along side best-practice tag`); + } + if (standardTags.length === 0) { + issues.push(`Expected one ${name} tag, got 0`); + } else if (standardTags.length > 1) { + issues.push(`Expected one ${name} tag, got: ${standardTags.join(', ')}`); + } + if (criterionTags.length === 0) { + issues.push(`Expected at least one ${name} criterion tag, got 0`); + } + + if (wcagLevelRegex) { + const wcagLevel = standards.WCAG.standardTag; + if (!wcagLevel.match(wcagLevelRegex)) { + issues.push(`${name} rules not allowed on ${wcagLevel}`); + } + } + + // Must have the same criteria listed + if (name === 'EN 301 549') { + const wcagCriteria = standards.WCAG.criterionTags.map(tag => + tag.slice(4) + ); + const enCriteria = criterionTags.map(tag => + tag.slice(5).replaceAll('.', '') + ); + if ( + wcagCriteria.length !== enCriteria.length || + !startsWith(wcagCriteria, enCriteria) + ) { + issues.push( + `Expect WCAG and EN criteria numbers to match: ${wcagCriteria.join( + ', ' + )} vs ${enCriteria.join(', ')}}` + ); + } + } + tags = removeTags(tags, [...standardTags, ...criterionTags]); + } + + // Other tags + const usedMiscTags = miscTags.filter(tag => tags.includes(tag)); + const unknownTags = removeTags(tags, usedMiscTags); + if (unknownTags.length) { + issues.push(`Invalid tags: ${unknownTags.join(', ')}`); + } + + // At this point only misc tags are left: + tags = removeTags(tags, unknownTags); + if (!startsWith(tags, usedMiscTags)) { + issues.push( + `Tags [${tags.join(', ')}] should be sorted like [${usedMiscTags.join( + ', ' + )}]` + ); + } + return issues; } + +function startsWith(arr1, arr2) { + return arr2.every((item, i) => item === arr1[i]); +} + +function removeTags(tags, tagsToRemove) { + return tags.filter(tag => !tagsToRemove.includes(tag)); +} diff --git a/doc/API.md b/doc/API.md index 9355005820..5f916713b4 100644 --- a/doc/API.md +++ b/doc/API.md @@ -76,23 +76,26 @@ Each rule in axe-core has a number of tags. These provide metadata about the rul The `experimental`, `ACT`, `TT`, and `section508` tags are only added to some rules. Each rule with a `section508` tag also has a tag to indicate what requirement in old Section 508 the rule is required by. For example `section508.22.a`. -| Tag Name | Accessibility Standard / Purpose | -| ---------------- | ---------------------------------------------------- | -| `wcag2a` | WCAG 2.0 Level A | -| `wcag2aa` | WCAG 2.0 Level AA | -| `wcag2aaa` | WCAG 2.0 Level AAA | -| `wcag21a` | WCAG 2.1 Level A | -| `wcag21aa` | WCAG 2.1 Level AA | -| `wcag22aa` | WCAG 2.2 Level AA | -| `best-practice` | Common accessibility best practices | -| `wcag***` | WCAG success criterion e.g. wcag111 maps to SC 1.1.1 | -| `ACT` | W3C approved Accessibility Conformance Testing rules | -| `section508` | Old Section 508 rules | -| `section508.*.*` | Requirement in old Section 508 | -| `TTv5` | Trusted Tester v5 rules | -| `TT*.*` | Test ID in Trusted Tester | -| `experimental` | Cutting-edge rules, disabled by default | -| `cat.*` | Category mappings used by Deque (see below) | +| Tag Name | Accessibility Standard / Purpose | +| ----------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `wcag2a` | WCAG 2.0 Level A | +| `wcag2aa` | WCAG 2.0 Level AA | +| `wcag2aaa` | WCAG 2.0 Level AAA | +| `wcag21a` | WCAG 2.1 Level A | +| `wcag21aa` | WCAG 2.1 Level AA | +| `wcag22aa` | WCAG 2.2 Level AA | +| `best-practice` | Common accessibility best practices | +| `wcag2a-obsolete` | WCAG 2.0 Level A, no longer required for conformance | +| `wcag***` | WCAG success criterion e.g. wcag111 maps to SC 1.1.1 | +| `ACT` | W3C approved Accessibility Conformance Testing rules | +| `section508` | Old Section 508 rules | +| `section508.*.*` | Requirement in old Section 508 | +| `TTv5` | Trusted Tester v5 rules | +| `TT*.*` | Test ID in Trusted Tester | +| `EN-301-549` | Rule required under [EN 301 549](https://www.etsi.org/deliver/etsi_en/301500_301599/301549/03.02.01_60/en_301549v030201p.pdf) | +| `EN-9.*` | Section in EN 301 549 listing the requirement | +| `experimental` | Cutting-edge rules, disabled by default | +| `cat.*` | Category mappings used by Deque (see below) | All rules have a `cat.*` tag, which indicates what type of content it is part of. The following `cat.*` tags exist in axe-core: @@ -225,7 +228,7 @@ axe.configure({ - The rules attribute is an Array of rule objects - each rule object can contain the following attributes - `id` - string(required). This uniquely identifies the rule. If the rule already exists, it will be overridden with any of the attributes supplied. The attributes below that are marked required, are only required for new rules. - - `impact` - string(optional). Override the impact defined by checks + - `impact` - string(required). Sets the impact of that rule's results - `reviewOnFail` - boolean(option, default `false`). Override the result of a rule to return "Needs Review" rather than "Violation" if the rule fails. - `selector` - string(optional, default `*`). A [CSS selector](./developer-guide.md#supported-css-selectors) used to identify the elements that are passed into the rule for evaluation. - `excludeHidden` - boolean(optional, default `true`). This indicates whether elements that are hidden from all users are to be passed into the rule for evaluation. @@ -618,6 +621,8 @@ The `assets` attribute expects an array of preload(able) constraints to be fetch The `timeout` attribute in the object configuration is `optional` and has a fallback default value (10000ms). The `timeout` is essential for any network dependent assets that are preloaded, where-in if a given request takes longer than the specified/ default value, the operation is aborted. +Preloading is not applicable to all rules. Even if the `preload` option is enabled, preloading steps may be skipped if no enabled rules require preloading. + ##### Callback Parameter The callback parameter is a function that will be called when the asynchronous `axe.run` function completes. The callback function is passed two parameters. The first parameter will be an error thrown inside of axe if axe.run could not complete. If axe completed correctly the first parameter will be null, and the second parameter will be the results object. diff --git a/doc/developer-guide.md b/doc/developer-guide.md index 1bbd83e4c3..07abffb216 100644 --- a/doc/developer-guide.md +++ b/doc/developer-guide.md @@ -115,12 +115,12 @@ After execution, a Check will return `true`, `false`, or `undefined` depending o Rules are defined by JSON files in the [lib/rules directory](../lib/rules). The JSON object is used to seed the [Rule object](../lib/core/base/rule.js#L30). A valid Rule JSON consists of the following: - `id` - `String` A unique name of the Rule. +- `impact` - `String` (one of `minor`, `moderate`, `serious`, or `critical`). Sets the impact of the results of this rule - `selector` - **optional** `String` which is a [CSS selector](#supported-css-selectors) that specifies the elements of the page on which the Rule runs. axe-core will look inside of the light DOM and _open_ [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) trees for elements matching the provided selector. If omitted, the rule will run against every node. - `excludeHidden` - **optional** `Boolean` Whether the rule should exclude hidden elements. Defaults to `true`. - `enabled` - **optional** `Boolean` Whether the rule is enabled by default. Defaults to `true`. - `pageLevel` - **optional** `Boolean` Whether the rule is page level. Page level rules will only run if given an entire `document` as context. - `matches` - **optional** `String` The ID of the filtering function that will exclude elements that match the `selector` property. See the [`metadata-function-map`](../lib/core/base/metadata-function-map.js) file for all defined IDs. -- `impact` - **optional** `String` (one of `minor`, `moderate`, `serious`, or `critical`). Override the impact defined by checks. - `tags` - **optional** `Array` Strings of the accessibility guidelines of which the Rule applies. - `metadata` - `Object` Consisting of: - `description` - `String` Text string that describes what the rule does. @@ -155,7 +155,7 @@ Similar to Rules, Checks are defined by JSON files in the [lib/checks directory] - `after` - **optional** `String` The ID of the function that gets called for checks that operate on a page-level basis, to process the results from the iframes. - `options` - **optional** `Object` Any information the Check needs that you might need to customize and/or is locale specific. Options can be overridden at runtime (with the options parameter) or config-time. For example, the [valid-lang](../lib/checks/language/valid-lang.json) Check defines what ISO 639-1 language codes it should accept as valid. Options do not need to follow any specific format or type; it is up to the author of a Check to determine the most appropriate format. - `metadata` - `Object` Consisting of: - - `impact` - `String` (one of `minor`, `moderate`, `serious`, or `critical`) + - `impact` - **Deprecated** `String` (one of `minor`, `moderate`, `serious`, or `critical`) - `messages` - `Object` These messages are displayed when the Check passes or fails - `pass` - `String` [doT.js](http://olado.github.io/doT/) template string displayed when the Check passes - `fail` - `String` [doT.js](http://olado.github.io/doT/) template string displayed when the Check fails diff --git a/doc/examples/chrome-debugging-protocol/axe-cdp.js b/doc/examples/chrome-debugging-protocol/axe-cdp.js index bf56073467..5ecae05a75 100644 --- a/doc/examples/chrome-debugging-protocol/axe-cdp.js +++ b/doc/examples/chrome-debugging-protocol/axe-cdp.js @@ -39,7 +39,7 @@ const example = async url => { // its value (`results.result.value` is undefined). By // `JSON.stringify()`ing it, we can `JSON.parse()` it later on // and return a valid results set. - .then(results => JSON.stringify(results)) + .then(res => JSON.stringify(res)) .then(resolve) .catch(reject); }); diff --git a/doc/examples/jsdom/test/a11y.js b/doc/examples/jsdom/test/a11y.js index bd946cef0c..0bc935c2e5 100644 --- a/doc/examples/jsdom/test/a11y.js +++ b/doc/examples/jsdom/test/a11y.js @@ -1,10 +1,11 @@ /* global describe, it */ +const axe = require('axe-core'); const jsdom = require('jsdom'); const { JSDOM } = jsdom; const assert = require('assert'); describe('axe', () => { - const { window } = new JSDOM(` + const { document } = new JSDOM(` JSDOM Example @@ -18,30 +19,30 @@ describe('axe', () => {

Not a label

- `); - - const axe = require('axe-core'); + `).window; const config = { rules: { 'color-contrast': { enabled: false } } }; - it('should report that good HTML is good', function (done) { - var n = window.document.getElementById('working'); - axe.run(n, config, function (err, result) { - assert.equal(err, null, 'Error is not null'); - assert.equal(result.violations.length, 0, 'Violations is not empty'); - done(); - }); + it('reports that good HTML is good', async () => { + const node = document.getElementById('working'); + const result = await axe.run(node, config); + assert.equal(result.violations.length, 0, 'Violations is not empty'); + }); + + it('reports that bad HTML is bad', async () => { + const node = document.getElementById('broken'); + const results = await axe.run(node, config); + assert.equal(results.violations.length, 1, 'Violations.length is not 1'); }); - it('should report that bad HTML is bad', function (done) { - var n = window.document.getElementById('broken'); - axe.run(n, config, function (err, result) { - assert.equal(err, null, 'Error is not null'); - assert.equal(result.violations.length, 1, 'Violations.length is not 1'); - done(); - }); + it('allows commons after axe.setup() is called', () => { + axe.setup(document); + const input = document.querySelector('input'); + const role = axe.commons.aria.getRole(input); + assert.equal(role, 'textbox'); + axe.teardown(); }); }); diff --git a/doc/examples/puppeteer/axe-puppeteer.js b/doc/examples/puppeteer/axe-puppeteer.js index 2b54d826a1..64f657b33c 100644 --- a/doc/examples/puppeteer/axe-puppeteer.js +++ b/doc/examples/puppeteer/axe-puppeteer.js @@ -9,11 +9,11 @@ const isValidURL = input => { return u.protocol && u.host; }; -// node axe-puppeteer.js -const url = process.argv[2]; -assert(isValidURL(url), 'Invalid URL'); +(async () => { + // node axe-puppeteer.js + const url = process.argv[2]; + assert(isValidURL(url), 'Invalid URL'); -const main = async url => { let browser; let results; try { @@ -34,6 +34,8 @@ const main = async url => { // Get the results from `axe.run()`. results = await handle.jsonValue(); + console.log(results); + // Destroy the handle & return axe results. await handle.dispose(); } catch (err) { @@ -42,19 +44,9 @@ const main = async url => { await browser.close(); } - // Re-throw - throw err; + console.error('Error running axe-core:', err.message); + process.exit(1); } await browser.close(); - return results; -}; - -main(url) - .then(results => { - console.log(results); - }) - .catch(err => { - console.error('Error running axe-core:', err.message); - process.exit(1); - }); +})(); diff --git a/doc/rule-descriptions.md b/doc/rule-descriptions.md index 684fa0bfe6..bf03843516 100644 --- a/doc/rule-descriptions.md +++ b/doc/rule-descriptions.md @@ -12,71 +12,73 @@ ## WCAG 2.0 Level A & AA Rules -| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | -| :------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------- | :--------------------------------------------------------------------------------------------- | :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [area-alt](https://dequeuniversity.com/rules/axe/4.7/area-alt?application=RuleDescription) | Ensures <area> elements of image maps have alternate text | Critical | cat.text-alternatives, wcag2a, wcag244, wcag412, section508, section508.22.a, ACT, TTv5, TT6.a | failure, needs review | [c487ae](https://act-rules.github.io/rules/c487ae) | -| [aria-allowed-attr](https://dequeuniversity.com/rules/axe/4.7/aria-allowed-attr?application=RuleDescription) | Ensures ARIA attributes are allowed for an element's role | Serious, Critical | cat.aria, wcag2a, wcag412 | failure, needs review | [5c01ea](https://act-rules.github.io/rules/5c01ea) | -| [aria-command-name](https://dequeuniversity.com/rules/axe/4.7/aria-command-name?application=RuleDescription) | Ensures every ARIA button, link and menuitem has an accessible name | Serious | cat.aria, wcag2a, wcag412, ACT, TTv5, TT6.a | failure, needs review | [97a4e1](https://act-rules.github.io/rules/97a4e1) | -| [aria-hidden-body](https://dequeuniversity.com/rules/axe/4.7/aria-hidden-body?application=RuleDescription) | Ensures aria-hidden='true' is not present on the document body. | Critical | cat.aria, wcag2a, wcag412 | failure | | -| [aria-hidden-focus](https://dequeuniversity.com/rules/axe/4.7/aria-hidden-focus?application=RuleDescription) | Ensures aria-hidden elements are not focusable nor contain focusable elements | Serious | cat.name-role-value, wcag2a, wcag412, TTv5, TT6.a | failure, needs review | [6cfa84](https://act-rules.github.io/rules/6cfa84) | -| [aria-input-field-name](https://dequeuniversity.com/rules/axe/4.7/aria-input-field-name?application=RuleDescription) | Ensures every ARIA input field has an accessible name | Moderate, Serious | cat.aria, wcag2a, wcag412, ACT, TTv5, TT5.c | failure, needs review | [e086e5](https://act-rules.github.io/rules/e086e5) | -| [aria-meter-name](https://dequeuniversity.com/rules/axe/4.7/aria-meter-name?application=RuleDescription) | Ensures every ARIA meter node has an accessible name | Serious | cat.aria, wcag2a, wcag111 | failure, needs review | | -| [aria-progressbar-name](https://dequeuniversity.com/rules/axe/4.7/aria-progressbar-name?application=RuleDescription) | Ensures every ARIA progressbar node has an accessible name | Serious | cat.aria, wcag2a, wcag111 | failure, needs review | | -| [aria-required-attr](https://dequeuniversity.com/rules/axe/4.7/aria-required-attr?application=RuleDescription) | Ensures elements with ARIA roles have all required ARIA attributes | Critical | cat.aria, wcag2a, wcag412 | failure | [4e8ab6](https://act-rules.github.io/rules/4e8ab6) | -| [aria-required-children](https://dequeuniversity.com/rules/axe/4.7/aria-required-children?application=RuleDescription) | Ensures elements with an ARIA role that require child roles contain them | Critical | cat.aria, wcag2a, wcag131 | failure, needs review | [bc4a75](https://act-rules.github.io/rules/bc4a75), [ff89c9](https://act-rules.github.io/rules/ff89c9) | -| [aria-required-parent](https://dequeuniversity.com/rules/axe/4.7/aria-required-parent?application=RuleDescription) | Ensures elements with an ARIA role that require parent roles are contained by them | Critical | cat.aria, wcag2a, wcag131 | failure | [ff89c9](https://act-rules.github.io/rules/ff89c9) | -| [aria-roles](https://dequeuniversity.com/rules/axe/4.7/aria-roles?application=RuleDescription) | Ensures all elements with a role attribute use a valid value | Minor, Serious, Critical | cat.aria, wcag2a, wcag412 | failure | [674b10](https://act-rules.github.io/rules/674b10) | -| [aria-toggle-field-name](https://dequeuniversity.com/rules/axe/4.7/aria-toggle-field-name?application=RuleDescription) | Ensures every ARIA toggle field has an accessible name | Moderate, Serious | cat.aria, wcag2a, wcag412, ACT, TTv5, TT5.c | failure, needs review | [e086e5](https://act-rules.github.io/rules/e086e5) | -| [aria-tooltip-name](https://dequeuniversity.com/rules/axe/4.7/aria-tooltip-name?application=RuleDescription) | Ensures every ARIA tooltip node has an accessible name | Serious | cat.aria, wcag2a, wcag412 | failure, needs review | | -| [aria-valid-attr-value](https://dequeuniversity.com/rules/axe/4.7/aria-valid-attr-value?application=RuleDescription) | Ensures all ARIA attributes have valid values | Serious, Critical | cat.aria, wcag2a, wcag412 | failure, needs review | [6a7281](https://act-rules.github.io/rules/6a7281) | -| [aria-valid-attr](https://dequeuniversity.com/rules/axe/4.7/aria-valid-attr?application=RuleDescription) | Ensures attributes that begin with aria- are valid ARIA attributes | Critical | cat.aria, wcag2a, wcag412 | failure | [5f99a7](https://act-rules.github.io/rules/5f99a7) | -| [blink](https://dequeuniversity.com/rules/axe/4.7/blink?application=RuleDescription) | Ensures <blink> elements are not used | Serious | cat.time-and-media, wcag2a, wcag222, section508, section508.22.j, TTv5, TT2.b | failure | | -| [button-name](https://dequeuniversity.com/rules/axe/4.7/button-name?application=RuleDescription) | Ensures buttons have discernible text | Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a, ACT, TTv5, TT6.a | failure, needs review | [97a4e1](https://act-rules.github.io/rules/97a4e1), [m6b1q3](https://act-rules.github.io/rules/m6b1q3) | -| [bypass](https://dequeuniversity.com/rules/axe/4.7/bypass?application=RuleDescription) | Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content | Serious | cat.keyboard, wcag2a, wcag241, section508, section508.22.o, TTv5, TT9.a | needs review | [cf77f2](https://act-rules.github.io/rules/cf77f2), [047fe0](https://act-rules.github.io/rules/047fe0), [b40fd1](https://act-rules.github.io/rules/b40fd1), [3e12e1](https://act-rules.github.io/rules/3e12e1), [ye5d6e](https://act-rules.github.io/rules/ye5d6e) | -| [color-contrast](https://dequeuniversity.com/rules/axe/4.7/color-contrast?application=RuleDescription) | Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds | Serious | cat.color, wcag2aa, wcag143, ACT, TTv5, TT13.c | failure, needs review | [afw4f7](https://act-rules.github.io/rules/afw4f7), [09o5cg](https://act-rules.github.io/rules/09o5cg) | -| [definition-list](https://dequeuniversity.com/rules/axe/4.7/definition-list?application=RuleDescription) | Ensures <dl> elements are structured correctly | Serious | cat.structure, wcag2a, wcag131 | failure | | -| [dlitem](https://dequeuniversity.com/rules/axe/4.7/dlitem?application=RuleDescription) | Ensures <dt> and <dd> elements are contained by a <dl> | Serious | cat.structure, wcag2a, wcag131 | failure | | -| [document-title](https://dequeuniversity.com/rules/axe/4.7/document-title?application=RuleDescription) | Ensures each HTML document contains a non-empty <title> element | Serious | cat.text-alternatives, wcag2a, wcag242, ACT, TTv5, TT12.a | failure | [2779a5](https://act-rules.github.io/rules/2779a5) | -| [duplicate-id-active](https://dequeuniversity.com/rules/axe/4.7/duplicate-id-active?application=RuleDescription) | Ensures every id attribute value of active elements is unique | Serious | cat.parsing, wcag2a, wcag411 | failure | [3ea0c8](https://act-rules.github.io/rules/3ea0c8) | -| [duplicate-id-aria](https://dequeuniversity.com/rules/axe/4.7/duplicate-id-aria?application=RuleDescription) | Ensures every id attribute value used in ARIA and in labels is unique | Critical | cat.parsing, wcag2a, wcag411 | failure | [3ea0c8](https://act-rules.github.io/rules/3ea0c8) | -| [duplicate-id](https://dequeuniversity.com/rules/axe/4.7/duplicate-id?application=RuleDescription) | Ensures every id attribute value is unique | Minor | cat.parsing, wcag2a, wcag411 | failure | [3ea0c8](https://act-rules.github.io/rules/3ea0c8) | -| [form-field-multiple-labels](https://dequeuniversity.com/rules/axe/4.7/form-field-multiple-labels?application=RuleDescription) | Ensures form field does not have multiple label elements | Moderate | cat.forms, wcag2a, wcag332, TTv5, TT5.c | needs review | | -| [frame-focusable-content](https://dequeuniversity.com/rules/axe/4.7/frame-focusable-content?application=RuleDescription) | Ensures <frame> and <iframe> elements with focusable content do not have tabindex=-1 | Serious | cat.keyboard, wcag2a, wcag211, TTv5, TT4.a | failure, needs review | [akn7bn](https://act-rules.github.io/rules/akn7bn) | -| [frame-title-unique](https://dequeuniversity.com/rules/axe/4.7/frame-title-unique?application=RuleDescription) | Ensures <iframe> and <frame> elements contain a unique title attribute | Serious | cat.text-alternatives, wcag412, wcag2a, TTv5, TT12.d | needs review | [4b1c6c](https://act-rules.github.io/rules/4b1c6c) | -| [frame-title](https://dequeuniversity.com/rules/axe/4.7/frame-title?application=RuleDescription) | Ensures <iframe> and <frame> elements have an accessible name | Serious | cat.text-alternatives, wcag2a, wcag412, section508, section508.22.i, TTv5, TT12.d | failure, needs review | [cae760](https://act-rules.github.io/rules/cae760) | -| [html-has-lang](https://dequeuniversity.com/rules/axe/4.7/html-has-lang?application=RuleDescription) | Ensures every HTML document has a lang attribute | Serious | cat.language, wcag2a, wcag311, ACT, TTv5, TT11.a | failure | [b5c3f8](https://act-rules.github.io/rules/b5c3f8) | -| [html-lang-valid](https://dequeuniversity.com/rules/axe/4.7/html-lang-valid?application=RuleDescription) | Ensures the lang attribute of the <html> element has a valid value | Serious | cat.language, wcag2a, wcag311, ACT, TTv5, TT11.a | failure | [bf051a](https://act-rules.github.io/rules/bf051a) | -| [html-xml-lang-mismatch](https://dequeuniversity.com/rules/axe/4.7/html-xml-lang-mismatch?application=RuleDescription) | Ensure that HTML elements with both valid lang and xml:lang attributes agree on the base language of the page | Moderate | cat.language, wcag2a, wcag311, ACT | failure | [5b7ae0](https://act-rules.github.io/rules/5b7ae0) | -| [image-alt](https://dequeuniversity.com/rules/axe/4.7/image-alt?application=RuleDescription) | Ensures <img> elements have alternate text or a role of none or presentation | Critical | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, ACT, TTv5, TT7.a, TT7.b | failure, needs review | [23a2a8](https://act-rules.github.io/rules/23a2a8) | -| [input-button-name](https://dequeuniversity.com/rules/axe/4.7/input-button-name?application=RuleDescription) | Ensures input buttons have discernible text | Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a, ACT, TTv5, TT5.c | failure, needs review | [97a4e1](https://act-rules.github.io/rules/97a4e1) | -| [input-image-alt](https://dequeuniversity.com/rules/axe/4.7/input-image-alt?application=RuleDescription) | Ensures <input type="image"> elements have alternate text | Critical | cat.text-alternatives, wcag2a, wcag111, wcag412, section508, section508.22.a, ACT, TTv5, TT7.a | failure, needs review | [59796f](https://act-rules.github.io/rules/59796f) | -| [label](https://dequeuniversity.com/rules/axe/4.7/label?application=RuleDescription) | Ensures every form element has a label | Minor, Critical | cat.forms, wcag2a, wcag412, section508, section508.22.n, ACT, TTv5, TT5.c | failure, needs review | [e086e5](https://act-rules.github.io/rules/e086e5) | -| [link-in-text-block](https://dequeuniversity.com/rules/axe/4.7/link-in-text-block?application=RuleDescription) | Ensure links are distinguished from surrounding text in a way that does not rely on color | Serious | cat.color, wcag2a, wcag141, TTv5, TT13.a | failure, needs review | | -| [link-name](https://dequeuniversity.com/rules/axe/4.7/link-name?application=RuleDescription) | Ensures links have discernible text | Serious | cat.name-role-value, wcag2a, wcag412, wcag244, section508, section508.22.a, ACT, TTv5, TT6.a | failure, needs review | [c487ae](https://act-rules.github.io/rules/c487ae) | -| [list](https://dequeuniversity.com/rules/axe/4.7/list?application=RuleDescription) | Ensures that lists are structured correctly | Serious | cat.structure, wcag2a, wcag131 | failure | | -| [listitem](https://dequeuniversity.com/rules/axe/4.7/listitem?application=RuleDescription) | Ensures <li> elements are used semantically | Serious | cat.structure, wcag2a, wcag131 | failure | | -| [marquee](https://dequeuniversity.com/rules/axe/4.7/marquee?application=RuleDescription) | Ensures <marquee> elements are not used | Serious | cat.parsing, wcag2a, wcag222, TTv5, TT2.b | failure | | -| [meta-refresh](https://dequeuniversity.com/rules/axe/4.7/meta-refresh?application=RuleDescription) | Ensures <meta http-equiv="refresh"> is not used for delayed refresh | Critical | cat.time-and-media, wcag2a, wcag221, TTv5, TT8.a | failure | [bc659a](https://act-rules.github.io/rules/bc659a), [bisz58](https://act-rules.github.io/rules/bisz58) | -| [meta-viewport](https://dequeuniversity.com/rules/axe/4.7/meta-viewport?application=RuleDescription) | Ensures <meta name="viewport"> does not disable text scaling and zooming | Critical | cat.sensory-and-visual-cues, wcag2aa, wcag144, ACT | failure | [b4f0c3](https://act-rules.github.io/rules/b4f0c3) | -| [nested-interactive](https://dequeuniversity.com/rules/axe/4.7/nested-interactive?application=RuleDescription) | Ensures interactive controls are not nested as they are not always announced by screen readers or can cause focus problems for assistive technologies | Serious | cat.keyboard, wcag2a, wcag412, TTv5, TT6.a | failure, needs review | [307n5z](https://act-rules.github.io/rules/307n5z) | -| [no-autoplay-audio](https://dequeuniversity.com/rules/axe/4.7/no-autoplay-audio?application=RuleDescription) | Ensures <video> or <audio> elements do not autoplay audio for more than 3 seconds without a control mechanism to stop or mute the audio | Moderate | cat.time-and-media, wcag2a, wcag142, ACT, TTv5, TT2.a | needs review | [80f0bf](https://act-rules.github.io/rules/80f0bf) | -| [object-alt](https://dequeuniversity.com/rules/axe/4.7/object-alt?application=RuleDescription) | Ensures <object> elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | failure, needs review | [8fc3b6](https://act-rules.github.io/rules/8fc3b6) | -| [role-img-alt](https://dequeuniversity.com/rules/axe/4.7/role-img-alt?application=RuleDescription) | Ensures [role='img'] elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, ACT, TTv5, TT7.a | failure, needs review | [23a2a8](https://act-rules.github.io/rules/23a2a8) | -| [scrollable-region-focusable](https://dequeuniversity.com/rules/axe/4.7/scrollable-region-focusable?application=RuleDescription) | Ensure elements that have scrollable content are accessible by keyboard | Serious | cat.keyboard, wcag2a, wcag211, TTv5, TT4.a | failure | [0ssw9k](https://act-rules.github.io/rules/0ssw9k) | -| [select-name](https://dequeuniversity.com/rules/axe/4.7/select-name?application=RuleDescription) | Ensures select element has an accessible name | Minor, Critical | cat.forms, wcag2a, wcag412, section508, section508.22.n, ACT, TTv5, TT5.c | failure, needs review | [e086e5](https://act-rules.github.io/rules/e086e5) | -| [server-side-image-map](https://dequeuniversity.com/rules/axe/4.7/server-side-image-map?application=RuleDescription) | Ensures that server-side image maps are not used | Minor | cat.text-alternatives, wcag2a, wcag211, section508, section508.22.f, TTv5, TT4.a | needs review | | -| [svg-img-alt](https://dequeuniversity.com/rules/axe/4.7/svg-img-alt?application=RuleDescription) | Ensures <svg> elements with an img, graphics-document or graphics-symbol role have an accessible text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, ACT, TTv5, TT7.a | failure, needs review | [7d6734](https://act-rules.github.io/rules/7d6734) | -| [td-headers-attr](https://dequeuniversity.com/rules/axe/4.7/td-headers-attr?application=RuleDescription) | Ensure that each cell in a table that uses the headers attribute refers only to other cells in that table | Serious | cat.tables, wcag2a, wcag131, section508, section508.22.g, TTv5, TT14.b | failure, needs review | [a25f45](https://act-rules.github.io/rules/a25f45) | -| [th-has-data-cells](https://dequeuniversity.com/rules/axe/4.7/th-has-data-cells?application=RuleDescription) | Ensure that <th> elements and elements with role=columnheader/rowheader have data cells they describe | Serious | cat.tables, wcag2a, wcag131, section508, section508.22.g, TTv5, 14.b | failure, needs review | [d0f69e](https://act-rules.github.io/rules/d0f69e) | -| [valid-lang](https://dequeuniversity.com/rules/axe/4.7/valid-lang?application=RuleDescription) | Ensures lang attributes have valid values | Serious | cat.language, wcag2aa, wcag312, ACT, TTv5, TT11.b | failure | [de46e4](https://act-rules.github.io/rules/de46e4) | -| [video-caption](https://dequeuniversity.com/rules/axe/4.7/video-caption?application=RuleDescription) | Ensures <video> elements have captions | Critical | cat.text-alternatives, wcag2a, wcag122, section508, section508.22.a, TTv5, TT17.a | needs review | [eac66b](https://act-rules.github.io/rules/eac66b) | +| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | +| :------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------- | :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [area-alt](https://dequeuniversity.com/rules/axe/4.8/area-alt?application=RuleDescription) | Ensures <area> elements of image maps have alternate text | Critical | cat.text-alternatives, wcag2a, wcag244, wcag412, section508, section508.22.a, TTv5, TT6.a, EN-301-549, EN-9.2.4.4, EN-9.4.1.2, ACT | failure, needs review | [c487ae](https://act-rules.github.io/rules/c487ae) | +| [aria-allowed-attr](https://dequeuniversity.com/rules/axe/4.8/aria-allowed-attr?application=RuleDescription) | Ensures an element's role supports its ARIA attributes | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure, needs review | [5c01ea](https://act-rules.github.io/rules/5c01ea) | +| [aria-braille-equivalent](https://dequeuniversity.com/rules/axe/4.8/aria-braille-equivalent?application=RuleDescription) | Ensure aria-braillelabel and aria-brailleroledescription have a non-braille equivalent | Serious | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | needs review | | +| [aria-command-name](https://dequeuniversity.com/rules/axe/4.8/aria-command-name?application=RuleDescription) | Ensures every ARIA button, link and menuitem has an accessible name | Serious | cat.aria, wcag2a, wcag412, TTv5, TT6.a, EN-301-549, EN-9.4.1.2, ACT | failure, needs review | [97a4e1](https://act-rules.github.io/rules/97a4e1) | +| [aria-conditional-attr](https://dequeuniversity.com/rules/axe/4.8/aria-conditional-attr?application=RuleDescription) | Ensures ARIA attributes are used as described in the specification of the element's role | Serious | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure | [5c01ea](https://act-rules.github.io/rules/5c01ea) | +| [aria-deprecated-role](https://dequeuniversity.com/rules/axe/4.8/aria-deprecated-role?application=RuleDescription) | Ensures elements do not use deprecated roles | Minor | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure | [674b10](https://act-rules.github.io/rules/674b10) | +| [aria-hidden-body](https://dequeuniversity.com/rules/axe/4.8/aria-hidden-body?application=RuleDescription) | Ensures aria-hidden="true" is not present on the document body. | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure | | +| [aria-hidden-focus](https://dequeuniversity.com/rules/axe/4.8/aria-hidden-focus?application=RuleDescription) | Ensures aria-hidden elements are not focusable nor contain focusable elements | Serious | cat.name-role-value, wcag2a, wcag412, TTv5, TT6.a, EN-301-549, EN-9.4.1.2 | failure, needs review | [6cfa84](https://act-rules.github.io/rules/6cfa84) | +| [aria-input-field-name](https://dequeuniversity.com/rules/axe/4.8/aria-input-field-name?application=RuleDescription) | Ensures every ARIA input field has an accessible name | Serious | cat.aria, wcag2a, wcag412, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT | failure, needs review | [e086e5](https://act-rules.github.io/rules/e086e5) | +| [aria-meter-name](https://dequeuniversity.com/rules/axe/4.8/aria-meter-name?application=RuleDescription) | Ensures every ARIA meter node has an accessible name | Serious | cat.aria, wcag2a, wcag111, EN-301-549, EN-9.1.1.1 | failure, needs review | | +| [aria-progressbar-name](https://dequeuniversity.com/rules/axe/4.8/aria-progressbar-name?application=RuleDescription) | Ensures every ARIA progressbar node has an accessible name | Serious | cat.aria, wcag2a, wcag111, EN-301-549, EN-9.1.1.1 | failure, needs review | | +| [aria-prohibited-attr](https://dequeuniversity.com/rules/axe/4.8/aria-prohibited-attr?application=RuleDescription) | Ensures ARIA attributes are not prohibited for an element's role | Serious | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure, needs review | [5c01ea](https://act-rules.github.io/rules/5c01ea) | +| [aria-required-attr](https://dequeuniversity.com/rules/axe/4.8/aria-required-attr?application=RuleDescription) | Ensures elements with ARIA roles have all required ARIA attributes | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure | [4e8ab6](https://act-rules.github.io/rules/4e8ab6) | +| [aria-required-children](https://dequeuniversity.com/rules/axe/4.8/aria-required-children?application=RuleDescription) | Ensures elements with an ARIA role that require child roles contain them | Critical | cat.aria, wcag2a, wcag131, EN-301-549, EN-9.1.3.1 | failure, needs review | [bc4a75](https://act-rules.github.io/rules/bc4a75), [ff89c9](https://act-rules.github.io/rules/ff89c9) | +| [aria-required-parent](https://dequeuniversity.com/rules/axe/4.8/aria-required-parent?application=RuleDescription) | Ensures elements with an ARIA role that require parent roles are contained by them | Critical | cat.aria, wcag2a, wcag131, EN-301-549, EN-9.1.3.1 | failure | [ff89c9](https://act-rules.github.io/rules/ff89c9) | +| [aria-roles](https://dequeuniversity.com/rules/axe/4.8/aria-roles?application=RuleDescription) | Ensures all elements with a role attribute use a valid value | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure | [674b10](https://act-rules.github.io/rules/674b10) | +| [aria-toggle-field-name](https://dequeuniversity.com/rules/axe/4.8/aria-toggle-field-name?application=RuleDescription) | Ensures every ARIA toggle field has an accessible name | Serious | cat.aria, wcag2a, wcag412, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT | failure, needs review | [e086e5](https://act-rules.github.io/rules/e086e5) | +| [aria-tooltip-name](https://dequeuniversity.com/rules/axe/4.8/aria-tooltip-name?application=RuleDescription) | Ensures every ARIA tooltip node has an accessible name | Serious | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure, needs review | | +| [aria-valid-attr-value](https://dequeuniversity.com/rules/axe/4.8/aria-valid-attr-value?application=RuleDescription) | Ensures all ARIA attributes have valid values | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure, needs review | [6a7281](https://act-rules.github.io/rules/6a7281) | +| [aria-valid-attr](https://dequeuniversity.com/rules/axe/4.8/aria-valid-attr?application=RuleDescription) | Ensures attributes that begin with aria- are valid ARIA attributes | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | failure | [5f99a7](https://act-rules.github.io/rules/5f99a7) | +| [blink](https://dequeuniversity.com/rules/axe/4.8/blink?application=RuleDescription) | Ensures <blink> elements are not used | Serious | cat.time-and-media, wcag2a, wcag222, section508, section508.22.j, TTv5, TT2.b, EN-301-549, EN-9.2.2.2 | failure | | +| [button-name](https://dequeuniversity.com/rules/axe/4.8/button-name?application=RuleDescription) | Ensures buttons have discernible text | Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a, TTv5, TT6.a, EN-301-549, EN-9.4.1.2, ACT | failure, needs review | [97a4e1](https://act-rules.github.io/rules/97a4e1), [m6b1q3](https://act-rules.github.io/rules/m6b1q3) | +| [bypass](https://dequeuniversity.com/rules/axe/4.8/bypass?application=RuleDescription) | Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content | Serious | cat.keyboard, wcag2a, wcag241, section508, section508.22.o, TTv5, TT9.a, EN-301-549, EN-9.2.4.1 | needs review | [cf77f2](https://act-rules.github.io/rules/cf77f2), [047fe0](https://act-rules.github.io/rules/047fe0), [b40fd1](https://act-rules.github.io/rules/b40fd1), [3e12e1](https://act-rules.github.io/rules/3e12e1), [ye5d6e](https://act-rules.github.io/rules/ye5d6e) | +| [color-contrast](https://dequeuniversity.com/rules/axe/4.8/color-contrast?application=RuleDescription) | Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds | Serious | cat.color, wcag2aa, wcag143, TTv5, TT13.c, EN-301-549, EN-9.1.4.3, ACT | failure, needs review | [afw4f7](https://act-rules.github.io/rules/afw4f7), [09o5cg](https://act-rules.github.io/rules/09o5cg) | +| [definition-list](https://dequeuniversity.com/rules/axe/4.8/definition-list?application=RuleDescription) | Ensures <dl> elements are structured correctly | Serious | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1 | failure | | +| [dlitem](https://dequeuniversity.com/rules/axe/4.8/dlitem?application=RuleDescription) | Ensures <dt> and <dd> elements are contained by a <dl> | Serious | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1 | failure | | +| [document-title](https://dequeuniversity.com/rules/axe/4.8/document-title?application=RuleDescription) | Ensures each HTML document contains a non-empty <title> element | Serious | cat.text-alternatives, wcag2a, wcag242, TTv5, TT12.a, EN-301-549, EN-9.2.4.2, ACT | failure | [2779a5](https://act-rules.github.io/rules/2779a5) | +| [duplicate-id-aria](https://dequeuniversity.com/rules/axe/4.8/duplicate-id-aria?application=RuleDescription) | Ensures every id attribute value used in ARIA and in labels is unique | Critical | cat.parsing, wcag2a, wcag412, EN-301-549, EN-9.4.1.2 | needs review | [3ea0c8](https://act-rules.github.io/rules/3ea0c8) | +| [form-field-multiple-labels](https://dequeuniversity.com/rules/axe/4.8/form-field-multiple-labels?application=RuleDescription) | Ensures form field does not have multiple label elements | Moderate | cat.forms, wcag2a, wcag332, TTv5, TT5.c, EN-301-549, EN-9.3.3.2 | needs review | | +| [frame-focusable-content](https://dequeuniversity.com/rules/axe/4.8/frame-focusable-content?application=RuleDescription) | Ensures <frame> and <iframe> elements with focusable content do not have tabindex=-1 | Serious | cat.keyboard, wcag2a, wcag211, TTv5, TT4.a, EN-301-549, EN-9.2.1.1 | failure, needs review | [akn7bn](https://act-rules.github.io/rules/akn7bn) | +| [frame-title-unique](https://dequeuniversity.com/rules/axe/4.8/frame-title-unique?application=RuleDescription) | Ensures <iframe> and <frame> elements contain a unique title attribute | Serious | cat.text-alternatives, wcag2a, wcag412, TTv5, TT12.d, EN-301-549, EN-9.4.1.2 | needs review | [4b1c6c](https://act-rules.github.io/rules/4b1c6c) | +| [frame-title](https://dequeuniversity.com/rules/axe/4.8/frame-title?application=RuleDescription) | Ensures <iframe> and <frame> elements have an accessible name | Serious | cat.text-alternatives, wcag2a, wcag412, section508, section508.22.i, TTv5, TT12.d, EN-301-549, EN-9.4.1.2 | failure, needs review | [cae760](https://act-rules.github.io/rules/cae760) | +| [html-has-lang](https://dequeuniversity.com/rules/axe/4.8/html-has-lang?application=RuleDescription) | Ensures every HTML document has a lang attribute | Serious | cat.language, wcag2a, wcag311, TTv5, TT11.a, EN-301-549, EN-9.3.1.1, ACT | failure | [b5c3f8](https://act-rules.github.io/rules/b5c3f8) | +| [html-lang-valid](https://dequeuniversity.com/rules/axe/4.8/html-lang-valid?application=RuleDescription) | Ensures the lang attribute of the <html> element has a valid value | Serious | cat.language, wcag2a, wcag311, TTv5, TT11.a, EN-301-549, EN-9.3.1.1, ACT | failure | [bf051a](https://act-rules.github.io/rules/bf051a) | +| [html-xml-lang-mismatch](https://dequeuniversity.com/rules/axe/4.8/html-xml-lang-mismatch?application=RuleDescription) | Ensure that HTML elements with both valid lang and xml:lang attributes agree on the base language of the page | Moderate | cat.language, wcag2a, wcag311, EN-301-549, EN-9.3.1.1, ACT | failure | [5b7ae0](https://act-rules.github.io/rules/5b7ae0) | +| [image-alt](https://dequeuniversity.com/rules/axe/4.8/image-alt?application=RuleDescription) | Ensures <img> elements have alternate text or a role of none or presentation | Critical | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, TTv5, TT7.a, TT7.b, EN-301-549, EN-9.1.1.1, ACT | failure, needs review | [23a2a8](https://act-rules.github.io/rules/23a2a8) | +| [input-button-name](https://dequeuniversity.com/rules/axe/4.8/input-button-name?application=RuleDescription) | Ensures input buttons have discernible text | Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT | failure, needs review | [97a4e1](https://act-rules.github.io/rules/97a4e1) | +| [input-image-alt](https://dequeuniversity.com/rules/axe/4.8/input-image-alt?application=RuleDescription) | Ensures <input type="image"> elements have alternate text | Critical | cat.text-alternatives, wcag2a, wcag111, wcag412, section508, section508.22.a, TTv5, TT7.a, EN-301-549, EN-9.1.1.1, EN-9.4.1.2, ACT | failure, needs review | [59796f](https://act-rules.github.io/rules/59796f) | +| [label](https://dequeuniversity.com/rules/axe/4.8/label?application=RuleDescription) | Ensures every form element has a label | Critical | cat.forms, wcag2a, wcag412, section508, section508.22.n, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT | failure, needs review | [e086e5](https://act-rules.github.io/rules/e086e5) | +| [link-in-text-block](https://dequeuniversity.com/rules/axe/4.8/link-in-text-block?application=RuleDescription) | Ensure links are distinguished from surrounding text in a way that does not rely on color | Serious | cat.color, wcag2a, wcag141, TTv5, TT13.a, EN-301-549, EN-9.1.4.1 | failure, needs review | | +| [link-name](https://dequeuniversity.com/rules/axe/4.8/link-name?application=RuleDescription) | Ensures links have discernible text | Serious | cat.name-role-value, wcag2a, wcag244, wcag412, section508, section508.22.a, TTv5, TT6.a, EN-301-549, EN-9.2.4.4, EN-9.4.1.2, ACT | failure, needs review | [c487ae](https://act-rules.github.io/rules/c487ae) | +| [list](https://dequeuniversity.com/rules/axe/4.8/list?application=RuleDescription) | Ensures that lists are structured correctly | Serious | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1 | failure | | +| [listitem](https://dequeuniversity.com/rules/axe/4.8/listitem?application=RuleDescription) | Ensures <li> elements are used semantically | Serious | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1 | failure | | +| [marquee](https://dequeuniversity.com/rules/axe/4.8/marquee?application=RuleDescription) | Ensures <marquee> elements are not used | Serious | cat.parsing, wcag2a, wcag222, TTv5, TT2.b, EN-301-549, EN-9.2.2.2 | failure | | +| [meta-refresh](https://dequeuniversity.com/rules/axe/4.8/meta-refresh?application=RuleDescription) | Ensures <meta http-equiv="refresh"> is not used for delayed refresh | Critical | cat.time-and-media, wcag2a, wcag221, TTv5, TT8.a, EN-301-549, EN-9.2.2.1 | failure | [bc659a](https://act-rules.github.io/rules/bc659a), [bisz58](https://act-rules.github.io/rules/bisz58) | +| [meta-viewport](https://dequeuniversity.com/rules/axe/4.8/meta-viewport?application=RuleDescription) | Ensures <meta name="viewport"> does not disable text scaling and zooming | Critical | cat.sensory-and-visual-cues, wcag2aa, wcag144, EN-301-549, EN-9.1.4.4, ACT | failure | [b4f0c3](https://act-rules.github.io/rules/b4f0c3) | +| [nested-interactive](https://dequeuniversity.com/rules/axe/4.8/nested-interactive?application=RuleDescription) | Ensures interactive controls are not nested as they are not always announced by screen readers or can cause focus problems for assistive technologies | Serious | cat.keyboard, wcag2a, wcag412, TTv5, TT6.a, EN-301-549, EN-9.4.1.2 | failure, needs review | [307n5z](https://act-rules.github.io/rules/307n5z) | +| [no-autoplay-audio](https://dequeuniversity.com/rules/axe/4.8/no-autoplay-audio?application=RuleDescription) | Ensures <video> or <audio> elements do not autoplay audio for more than 3 seconds without a control mechanism to stop or mute the audio | Moderate | cat.time-and-media, wcag2a, wcag142, TTv5, TT2.a, EN-301-549, EN-9.1.4.2, ACT | needs review | [80f0bf](https://act-rules.github.io/rules/80f0bf) | +| [object-alt](https://dequeuniversity.com/rules/axe/4.8/object-alt?application=RuleDescription) | Ensures <object> elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, EN-301-549, EN-9.1.1.1 | failure, needs review | [8fc3b6](https://act-rules.github.io/rules/8fc3b6) | +| [role-img-alt](https://dequeuniversity.com/rules/axe/4.8/role-img-alt?application=RuleDescription) | Ensures [role="img"] elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, TTv5, TT7.a, EN-301-549, EN-9.1.1.1, ACT | failure, needs review | [23a2a8](https://act-rules.github.io/rules/23a2a8) | +| [scrollable-region-focusable](https://dequeuniversity.com/rules/axe/4.8/scrollable-region-focusable?application=RuleDescription) | Ensure elements that have scrollable content are accessible by keyboard | Serious | cat.keyboard, wcag2a, wcag211, TTv5, TT4.a, EN-301-549, EN-9.2.1.1 | failure | [0ssw9k](https://act-rules.github.io/rules/0ssw9k) | +| [select-name](https://dequeuniversity.com/rules/axe/4.8/select-name?application=RuleDescription) | Ensures select element has an accessible name | Critical | cat.forms, wcag2a, wcag412, section508, section508.22.n, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT | failure, needs review | [e086e5](https://act-rules.github.io/rules/e086e5) | +| [server-side-image-map](https://dequeuniversity.com/rules/axe/4.8/server-side-image-map?application=RuleDescription) | Ensures that server-side image maps are not used | Minor | cat.text-alternatives, wcag2a, wcag211, section508, section508.22.f, TTv5, TT4.a, EN-301-549, EN-9.2.1.1 | needs review | | +| [svg-img-alt](https://dequeuniversity.com/rules/axe/4.8/svg-img-alt?application=RuleDescription) | Ensures <svg> elements with an img, graphics-document or graphics-symbol role have an accessible text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, TTv5, TT7.a, EN-301-549, EN-9.1.1.1, ACT | failure, needs review | [7d6734](https://act-rules.github.io/rules/7d6734) | +| [td-headers-attr](https://dequeuniversity.com/rules/axe/4.8/td-headers-attr?application=RuleDescription) | Ensure that each cell in a table that uses the headers attribute refers only to other cells in that table | Serious | cat.tables, wcag2a, wcag131, section508, section508.22.g, TTv5, TT14.b, EN-301-549, EN-9.1.3.1 | failure, needs review | [a25f45](https://act-rules.github.io/rules/a25f45) | +| [th-has-data-cells](https://dequeuniversity.com/rules/axe/4.8/th-has-data-cells?application=RuleDescription) | Ensure that <th> elements and elements with role=columnheader/rowheader have data cells they describe | Serious | cat.tables, wcag2a, wcag131, section508, section508.22.g, TTv5, TT14.b, EN-301-549, EN-9.1.3.1 | failure, needs review | [d0f69e](https://act-rules.github.io/rules/d0f69e) | +| [valid-lang](https://dequeuniversity.com/rules/axe/4.8/valid-lang?application=RuleDescription) | Ensures lang attributes have valid values | Serious | cat.language, wcag2aa, wcag312, TTv5, TT11.b, EN-301-549, EN-9.3.1.2, ACT | failure | [de46e4](https://act-rules.github.io/rules/de46e4) | +| [video-caption](https://dequeuniversity.com/rules/axe/4.8/video-caption?application=RuleDescription) | Ensures <video> elements have captions | Critical | cat.text-alternatives, wcag2a, wcag122, section508, section508.22.a, TTv5, TT17.a, EN-301-549, EN-9.1.2.2 | needs review | [eac66b](https://act-rules.github.io/rules/eac66b) | ## WCAG 2.1 Level A & AA Rules -| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | -| :----------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :------ | :------------------------------------- | :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [autocomplete-valid](https://dequeuniversity.com/rules/axe/4.7/autocomplete-valid?application=RuleDescription) | Ensure the autocomplete attribute is correct and suitable for the form field | Serious | cat.forms, wcag21aa, wcag135, ACT | failure | [73f2c2](https://act-rules.github.io/rules/73f2c2) | -| [avoid-inline-spacing](https://dequeuniversity.com/rules/axe/4.7/avoid-inline-spacing?application=RuleDescription) | Ensure that text spacing set through style attributes can be adjusted with custom stylesheets | Serious | cat.structure, wcag21aa, wcag1412, ACT | failure | [24afc2](https://act-rules.github.io/rules/24afc2), [9e45ec](https://act-rules.github.io/rules/9e45ec), [78fd32](https://act-rules.github.io/rules/78fd32) | +| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | +| :----------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | :------ | :-------------------------------------------------------------- | :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [autocomplete-valid](https://dequeuniversity.com/rules/axe/4.8/autocomplete-valid?application=RuleDescription) | Ensure the autocomplete attribute is correct and suitable for the form field | Serious | cat.forms, wcag21aa, wcag135, EN-301-549, EN-9.1.3.5, ACT | failure | [73f2c2](https://act-rules.github.io/rules/73f2c2) | +| [avoid-inline-spacing](https://dequeuniversity.com/rules/axe/4.8/avoid-inline-spacing?application=RuleDescription) | Ensure that text spacing set through style attributes can be adjusted with custom stylesheets | Serious | cat.structure, wcag21aa, wcag1412, EN-301-549, EN-9.1.4.12, ACT | failure | [24afc2](https://act-rules.github.io/rules/24afc2), [9e45ec](https://act-rules.github.io/rules/9e45ec), [78fd32](https://act-rules.github.io/rules/78fd32) | ## WCAG 2.2 Level A & AA Rules @@ -84,42 +86,42 @@ These rules are disabled by default, until WCAG 2.2 is more widely adopted and r | Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | | :----------------------------------------------------------------------------------------------- | :------------------------------------------------- | :------ | :--------------------------------------------- | :------------------------- | :-------- | -| [target-size](https://dequeuniversity.com/rules/axe/4.7/target-size?application=RuleDescription) | Ensure touch target have sufficient size and space | Serious | wcag22aa, wcag258, cat.sensory-and-visual-cues | failure, needs review | | +| [target-size](https://dequeuniversity.com/rules/axe/4.8/target-size?application=RuleDescription) | Ensure touch target have sufficient size and space | Serious | cat.sensory-and-visual-cues, wcag22aa, wcag258 | failure, needs review | | ## Best Practices Rules Rules that do not necessarily conform to WCAG success criterion but are industry accepted practices that improve the user experience. -| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | -| :----------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :----------------- | :----------------------------------------- | :------------------------- | :------------------------------------------------- | -| [accesskeys](https://dequeuniversity.com/rules/axe/4.7/accesskeys?application=RuleDescription) | Ensures every accesskey attribute value is unique | Serious | cat.keyboard, best-practice | failure | | -| [aria-allowed-role](https://dequeuniversity.com/rules/axe/4.7/aria-allowed-role?application=RuleDescription) | Ensures role attribute has an appropriate value for the element | Minor | cat.aria, best-practice | failure, needs review | | -| [aria-dialog-name](https://dequeuniversity.com/rules/axe/4.7/aria-dialog-name?application=RuleDescription) | Ensures every ARIA dialog and alertdialog node has an accessible name | Serious | cat.aria, best-practice | failure, needs review | | -| [aria-text](https://dequeuniversity.com/rules/axe/4.7/aria-text?application=RuleDescription) | Ensures "role=text" is used on elements with no focusable descendants | Serious | cat.aria, best-practice | failure, needs review | | -| [aria-treeitem-name](https://dequeuniversity.com/rules/axe/4.7/aria-treeitem-name?application=RuleDescription) | Ensures every ARIA treeitem node has an accessible name | Serious | cat.aria, best-practice | failure, needs review | | -| [empty-heading](https://dequeuniversity.com/rules/axe/4.7/empty-heading?application=RuleDescription) | Ensures headings have discernible text | Minor | cat.name-role-value, best-practice | failure, needs review | [ffd0e9](https://act-rules.github.io/rules/ffd0e9) | -| [empty-table-header](https://dequeuniversity.com/rules/axe/4.7/empty-table-header?application=RuleDescription) | Ensures table headers have discernible text | Minor | cat.name-role-value, best-practice | failure, needs review | | -| [frame-tested](https://dequeuniversity.com/rules/axe/4.7/frame-tested?application=RuleDescription) | Ensures <iframe> and <frame> elements contain the axe-core script | Critical | cat.structure, review-item, best-practice | failure, needs review | | -| [heading-order](https://dequeuniversity.com/rules/axe/4.7/heading-order?application=RuleDescription) | Ensures the order of headings is semantically correct | Moderate | cat.semantics, best-practice | failure, needs review | | -| [image-redundant-alt](https://dequeuniversity.com/rules/axe/4.7/image-redundant-alt?application=RuleDescription) | Ensure image alternative is not repeated as text | Minor | cat.text-alternatives, best-practice | failure | | -| [label-title-only](https://dequeuniversity.com/rules/axe/4.7/label-title-only?application=RuleDescription) | Ensures that every form element has a visible label and is not solely labeled using hidden labels, or the title or aria-describedby attributes | Serious | cat.forms, best-practice | failure | | -| [landmark-banner-is-top-level](https://dequeuniversity.com/rules/axe/4.7/landmark-banner-is-top-level?application=RuleDescription) | Ensures the banner landmark is at top level | Moderate | cat.semantics, best-practice | failure | | -| [landmark-complementary-is-top-level](https://dequeuniversity.com/rules/axe/4.7/landmark-complementary-is-top-level?application=RuleDescription) | Ensures the complementary landmark or aside is at top level | Moderate | cat.semantics, best-practice | failure | | -| [landmark-contentinfo-is-top-level](https://dequeuniversity.com/rules/axe/4.7/landmark-contentinfo-is-top-level?application=RuleDescription) | Ensures the contentinfo landmark is at top level | Moderate | cat.semantics, best-practice | failure | | -| [landmark-main-is-top-level](https://dequeuniversity.com/rules/axe/4.7/landmark-main-is-top-level?application=RuleDescription) | Ensures the main landmark is at top level | Moderate | cat.semantics, best-practice | failure | | -| [landmark-no-duplicate-banner](https://dequeuniversity.com/rules/axe/4.7/landmark-no-duplicate-banner?application=RuleDescription) | Ensures the document has at most one banner landmark | Moderate | cat.semantics, best-practice | failure | | -| [landmark-no-duplicate-contentinfo](https://dequeuniversity.com/rules/axe/4.7/landmark-no-duplicate-contentinfo?application=RuleDescription) | Ensures the document has at most one contentinfo landmark | Moderate | cat.semantics, best-practice | failure | | -| [landmark-no-duplicate-main](https://dequeuniversity.com/rules/axe/4.7/landmark-no-duplicate-main?application=RuleDescription) | Ensures the document has at most one main landmark | Moderate | cat.semantics, best-practice | failure | | -| [landmark-one-main](https://dequeuniversity.com/rules/axe/4.7/landmark-one-main?application=RuleDescription) | Ensures the document has a main landmark | Moderate | cat.semantics, best-practice | failure | | -| [landmark-unique](https://dequeuniversity.com/rules/axe/4.7/landmark-unique?application=RuleDescription) | Landmarks should have a unique role or role/label/title (i.e. accessible name) combination | Moderate | cat.semantics, best-practice | failure | | -| [meta-viewport-large](https://dequeuniversity.com/rules/axe/4.7/meta-viewport-large?application=RuleDescription) | Ensures <meta name="viewport"> can scale a significant amount | Minor | cat.sensory-and-visual-cues, best-practice | failure | | -| [page-has-heading-one](https://dequeuniversity.com/rules/axe/4.7/page-has-heading-one?application=RuleDescription) | Ensure that the page, or at least one of its frames contains a level-one heading | Moderate | cat.semantics, best-practice | failure | | -| [presentation-role-conflict](https://dequeuniversity.com/rules/axe/4.7/presentation-role-conflict?application=RuleDescription) | Elements marked as presentational should not have global ARIA or tabindex to ensure all screen readers ignore them | Minor | cat.aria, best-practice, ACT | failure | [46ca7f](https://act-rules.github.io/rules/46ca7f) | -| [region](https://dequeuniversity.com/rules/axe/4.7/region?application=RuleDescription) | Ensures all page content is contained by landmarks | Moderate | cat.keyboard, best-practice | failure | | -| [scope-attr-valid](https://dequeuniversity.com/rules/axe/4.7/scope-attr-valid?application=RuleDescription) | Ensures the scope attribute is used correctly on tables | Moderate, Critical | cat.tables, best-practice | failure | | -| [skip-link](https://dequeuniversity.com/rules/axe/4.7/skip-link?application=RuleDescription) | Ensure all skip links have a focusable target | Moderate | cat.keyboard, best-practice | failure, needs review | | -| [tabindex](https://dequeuniversity.com/rules/axe/4.7/tabindex?application=RuleDescription) | Ensures tabindex attribute values are not greater than 0 | Serious | cat.keyboard, best-practice | failure | | -| [table-duplicate-name](https://dequeuniversity.com/rules/axe/4.7/table-duplicate-name?application=RuleDescription) | Ensure the <caption> element does not contain the same text as the summary attribute | Minor | cat.tables, best-practice | failure, needs review | | +| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | +| :----------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :----------------------------------------- | :------------------------- | :------------------------------------------------- | +| [accesskeys](https://dequeuniversity.com/rules/axe/4.8/accesskeys?application=RuleDescription) | Ensures every accesskey attribute value is unique | Serious | cat.keyboard, best-practice | failure | | +| [aria-allowed-role](https://dequeuniversity.com/rules/axe/4.8/aria-allowed-role?application=RuleDescription) | Ensures role attribute has an appropriate value for the element | Minor | cat.aria, best-practice | failure, needs review | | +| [aria-dialog-name](https://dequeuniversity.com/rules/axe/4.8/aria-dialog-name?application=RuleDescription) | Ensures every ARIA dialog and alertdialog node has an accessible name | Serious | cat.aria, best-practice | failure, needs review | | +| [aria-text](https://dequeuniversity.com/rules/axe/4.8/aria-text?application=RuleDescription) | Ensures role="text" is used on elements with no focusable descendants | Serious | cat.aria, best-practice | failure, needs review | | +| [aria-treeitem-name](https://dequeuniversity.com/rules/axe/4.8/aria-treeitem-name?application=RuleDescription) | Ensures every ARIA treeitem node has an accessible name | Serious | cat.aria, best-practice | failure, needs review | | +| [empty-heading](https://dequeuniversity.com/rules/axe/4.8/empty-heading?application=RuleDescription) | Ensures headings have discernible text | Minor | cat.name-role-value, best-practice | failure, needs review | [ffd0e9](https://act-rules.github.io/rules/ffd0e9) | +| [empty-table-header](https://dequeuniversity.com/rules/axe/4.8/empty-table-header?application=RuleDescription) | Ensures table headers have discernible text | Minor | cat.name-role-value, best-practice | failure, needs review | | +| [frame-tested](https://dequeuniversity.com/rules/axe/4.8/frame-tested?application=RuleDescription) | Ensures <iframe> and <frame> elements contain the axe-core script | Critical | cat.structure, best-practice, review-item | failure, needs review | | +| [heading-order](https://dequeuniversity.com/rules/axe/4.8/heading-order?application=RuleDescription) | Ensures the order of headings is semantically correct | Moderate | cat.semantics, best-practice | failure, needs review | | +| [image-redundant-alt](https://dequeuniversity.com/rules/axe/4.8/image-redundant-alt?application=RuleDescription) | Ensure image alternative is not repeated as text | Minor | cat.text-alternatives, best-practice | failure | | +| [label-title-only](https://dequeuniversity.com/rules/axe/4.8/label-title-only?application=RuleDescription) | Ensures that every form element has a visible label and is not solely labeled using hidden labels, or the title or aria-describedby attributes | Serious | cat.forms, best-practice | failure | | +| [landmark-banner-is-top-level](https://dequeuniversity.com/rules/axe/4.8/landmark-banner-is-top-level?application=RuleDescription) | Ensures the banner landmark is at top level | Moderate | cat.semantics, best-practice | failure | | +| [landmark-complementary-is-top-level](https://dequeuniversity.com/rules/axe/4.8/landmark-complementary-is-top-level?application=RuleDescription) | Ensures the complementary landmark or aside is at top level | Moderate | cat.semantics, best-practice | failure | | +| [landmark-contentinfo-is-top-level](https://dequeuniversity.com/rules/axe/4.8/landmark-contentinfo-is-top-level?application=RuleDescription) | Ensures the contentinfo landmark is at top level | Moderate | cat.semantics, best-practice | failure | | +| [landmark-main-is-top-level](https://dequeuniversity.com/rules/axe/4.8/landmark-main-is-top-level?application=RuleDescription) | Ensures the main landmark is at top level | Moderate | cat.semantics, best-practice | failure | | +| [landmark-no-duplicate-banner](https://dequeuniversity.com/rules/axe/4.8/landmark-no-duplicate-banner?application=RuleDescription) | Ensures the document has at most one banner landmark | Moderate | cat.semantics, best-practice | failure | | +| [landmark-no-duplicate-contentinfo](https://dequeuniversity.com/rules/axe/4.8/landmark-no-duplicate-contentinfo?application=RuleDescription) | Ensures the document has at most one contentinfo landmark | Moderate | cat.semantics, best-practice | failure | | +| [landmark-no-duplicate-main](https://dequeuniversity.com/rules/axe/4.8/landmark-no-duplicate-main?application=RuleDescription) | Ensures the document has at most one main landmark | Moderate | cat.semantics, best-practice | failure | | +| [landmark-one-main](https://dequeuniversity.com/rules/axe/4.8/landmark-one-main?application=RuleDescription) | Ensures the document has a main landmark | Moderate | cat.semantics, best-practice | failure | | +| [landmark-unique](https://dequeuniversity.com/rules/axe/4.8/landmark-unique?application=RuleDescription) | Landmarks should have a unique role or role/label/title (i.e. accessible name) combination | Moderate | cat.semantics, best-practice | failure | | +| [meta-viewport-large](https://dequeuniversity.com/rules/axe/4.8/meta-viewport-large?application=RuleDescription) | Ensures <meta name="viewport"> can scale a significant amount | Minor | cat.sensory-and-visual-cues, best-practice | failure | | +| [page-has-heading-one](https://dequeuniversity.com/rules/axe/4.8/page-has-heading-one?application=RuleDescription) | Ensure that the page, or at least one of its frames contains a level-one heading | Moderate | cat.semantics, best-practice | failure | | +| [presentation-role-conflict](https://dequeuniversity.com/rules/axe/4.8/presentation-role-conflict?application=RuleDescription) | Elements marked as presentational should not have global ARIA or tabindex to ensure all screen readers ignore them | Minor | cat.aria, best-practice, ACT | failure | [46ca7f](https://act-rules.github.io/rules/46ca7f) | +| [region](https://dequeuniversity.com/rules/axe/4.8/region?application=RuleDescription) | Ensures all page content is contained by landmarks | Moderate | cat.keyboard, best-practice | failure | | +| [scope-attr-valid](https://dequeuniversity.com/rules/axe/4.8/scope-attr-valid?application=RuleDescription) | Ensures the scope attribute is used correctly on tables | Moderate | cat.tables, best-practice | failure | | +| [skip-link](https://dequeuniversity.com/rules/axe/4.8/skip-link?application=RuleDescription) | Ensure all skip links have a focusable target | Moderate | cat.keyboard, best-practice | failure, needs review | | +| [tabindex](https://dequeuniversity.com/rules/axe/4.8/tabindex?application=RuleDescription) | Ensures tabindex attribute values are not greater than 0 | Serious | cat.keyboard, best-practice | failure | | +| [table-duplicate-name](https://dequeuniversity.com/rules/axe/4.8/table-duplicate-name?application=RuleDescription) | Ensure the <caption> element does not contain the same text as the summary attribute | Minor | cat.tables, best-practice | failure, needs review | | ## WCAG 2.x level AAA rules @@ -127,29 +129,31 @@ Rules that check for conformance to WCAG AAA success criteria that can be fully | Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | | :--------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | :------ | :--------------------------------------------- | :------------------------- | :------------------------------------------------- | -| [color-contrast-enhanced](https://dequeuniversity.com/rules/axe/4.7/color-contrast-enhanced?application=RuleDescription) | Ensures the contrast between foreground and background colors meets WCAG 2 AAA enhanced contrast ratio thresholds | Serious | cat.color, wcag2aaa, wcag146, ACT | failure, needs review | [09o5cg](https://act-rules.github.io/rules/09o5cg) | -| [identical-links-same-purpose](https://dequeuniversity.com/rules/axe/4.7/identical-links-same-purpose?application=RuleDescription) | Ensure that links with the same accessible name serve a similar purpose | Minor | cat.semantics, wcag2aaa, wcag249 | needs review | [b20e66](https://act-rules.github.io/rules/b20e66) | -| [meta-refresh-no-exceptions](https://dequeuniversity.com/rules/axe/4.7/meta-refresh-no-exceptions?application=RuleDescription) | Ensures <meta http-equiv="refresh"> is not used for delayed refresh | Minor | cat.time-and-media, wcag2aaa, wcag224, wcag325 | failure | [bisz58](https://act-rules.github.io/rules/bisz58) | +| [color-contrast-enhanced](https://dequeuniversity.com/rules/axe/4.8/color-contrast-enhanced?application=RuleDescription) | Ensures the contrast between foreground and background colors meets WCAG 2 AAA enhanced contrast ratio thresholds | Serious | cat.color, wcag2aaa, wcag146, ACT | failure, needs review | [09o5cg](https://act-rules.github.io/rules/09o5cg) | +| [identical-links-same-purpose](https://dequeuniversity.com/rules/axe/4.8/identical-links-same-purpose?application=RuleDescription) | Ensure that links with the same accessible name serve a similar purpose | Minor | cat.semantics, wcag2aaa, wcag249 | needs review | [b20e66](https://act-rules.github.io/rules/b20e66) | +| [meta-refresh-no-exceptions](https://dequeuniversity.com/rules/axe/4.8/meta-refresh-no-exceptions?application=RuleDescription) | Ensures <meta http-equiv="refresh"> is not used for delayed refresh | Minor | cat.time-and-media, wcag2aaa, wcag224, wcag325 | failure | [bisz58](https://act-rules.github.io/rules/bisz58) | ## Experimental Rules Rules we are still testing and developing. They are disabled by default in axe-core, but are enabled for the axe browser extensions. -| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | -| :------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | :------- | :----------------------------------------------------------------------------------- | :------------------------- | :------------------------------------------------- | -| [css-orientation-lock](https://dequeuniversity.com/rules/axe/4.7/css-orientation-lock?application=RuleDescription) | Ensures content is not locked to any specific display orientation, and the content is operable in all display orientations | Serious | cat.structure, wcag134, wcag21aa, experimental | failure, needs review | [b33eff](https://act-rules.github.io/rules/b33eff) | -| [focus-order-semantics](https://dequeuniversity.com/rules/axe/4.7/focus-order-semantics?application=RuleDescription) | Ensures elements in the focus order have a role appropriate for interactive content | Minor | cat.keyboard, best-practice, experimental | failure | | -| [hidden-content](https://dequeuniversity.com/rules/axe/4.7/hidden-content?application=RuleDescription) | Informs users about hidden content. | Minor | cat.structure, experimental, review-item, best-practice | failure, needs review | | -| [label-content-name-mismatch](https://dequeuniversity.com/rules/axe/4.7/label-content-name-mismatch?application=RuleDescription) | Ensures that elements labelled through their content must have their visible text as part of their accessible name | Serious | cat.semantics, wcag21a, wcag253, experimental | failure | [2ee8b8](https://act-rules.github.io/rules/2ee8b8) | -| [p-as-heading](https://dequeuniversity.com/rules/axe/4.7/p-as-heading?application=RuleDescription) | Ensure bold, italic text and font-size is not used to style <p> elements as a heading | Serious | cat.semantics, wcag2a, wcag131, experimental | failure, needs review | | -| [table-fake-caption](https://dequeuniversity.com/rules/axe/4.7/table-fake-caption?application=RuleDescription) | Ensure that tables with a caption use the <caption> element. | Serious | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g | failure | | -| [td-has-header](https://dequeuniversity.com/rules/axe/4.7/td-has-header?application=RuleDescription) | Ensure that each non-empty data cell in a <table> larger than 3 by 3 has one or more table headers | Critical | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g, TTv5, TT14.b | failure | | +| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | +| :------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | :------- | :----------------------------------------------------------------------------------------------------------- | :------------------------- | :------------------------------------------------- | +| [css-orientation-lock](https://dequeuniversity.com/rules/axe/4.8/css-orientation-lock?application=RuleDescription) | Ensures content is not locked to any specific display orientation, and the content is operable in all display orientations | Serious | cat.structure, wcag134, wcag21aa, EN-301-549, EN-9.1.3.4, experimental | failure, needs review | [b33eff](https://act-rules.github.io/rules/b33eff) | +| [focus-order-semantics](https://dequeuniversity.com/rules/axe/4.8/focus-order-semantics?application=RuleDescription) | Ensures elements in the focus order have a role appropriate for interactive content | Minor | cat.keyboard, best-practice, experimental | failure | | +| [hidden-content](https://dequeuniversity.com/rules/axe/4.8/hidden-content?application=RuleDescription) | Informs users about hidden content. | Minor | cat.structure, best-practice, experimental, review-item | failure, needs review | | +| [label-content-name-mismatch](https://dequeuniversity.com/rules/axe/4.8/label-content-name-mismatch?application=RuleDescription) | Ensures that elements labelled through their content must have their visible text as part of their accessible name | Serious | cat.semantics, wcag21a, wcag253, EN-301-549, EN-9.2.5.3, experimental | failure | [2ee8b8](https://act-rules.github.io/rules/2ee8b8) | +| [p-as-heading](https://dequeuniversity.com/rules/axe/4.8/p-as-heading?application=RuleDescription) | Ensure bold, italic text and font-size is not used to style <p> elements as a heading | Serious | cat.semantics, wcag2a, wcag131, EN-301-549, EN-9.1.3.1, experimental | failure, needs review | | +| [table-fake-caption](https://dequeuniversity.com/rules/axe/4.8/table-fake-caption?application=RuleDescription) | Ensure that tables with a caption use the <caption> element. | Serious | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g, EN-301-549, EN-9.1.3.1 | failure | | +| [td-has-header](https://dequeuniversity.com/rules/axe/4.8/td-has-header?application=RuleDescription) | Ensure that each non-empty data cell in a <table> larger than 3 by 3 has one or more table headers | Critical | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g, TTv5, TT14.b, EN-301-549, EN-9.1.3.1 | failure | | ## Deprecated Rules Deprecated rules are disabled by default and will be removed in the next major release. -| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | -| :----------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :------- | :--------------------------------------------------------------------------- | :------------------------- | :----------------------------------------------------------------------------------------------------- | -| [aria-roledescription](https://dequeuniversity.com/rules/axe/4.7/aria-roledescription?application=RuleDescription) | Ensure aria-roledescription is only used on elements with an implicit or explicit role | Serious | cat.aria, wcag2a, wcag412, deprecated | failure, needs review | | -| [audio-caption](https://dequeuniversity.com/rules/axe/4.7/audio-caption?application=RuleDescription) | Ensures <audio> elements have captions | Critical | cat.time-and-media, wcag2a, wcag121, section508, section508.22.a, deprecated | needs review | [2eb176](https://act-rules.github.io/rules/2eb176), [afb423](https://act-rules.github.io/rules/afb423) | +| Rule ID | Description | Impact | Tags | Issue Type | ACT Rules | +| :----------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :------- | :--------------------------------------------------------------------------------------------------- | :------------------------- | :----------------------------------------------------------------------------------------------------- | +| [aria-roledescription](https://dequeuniversity.com/rules/axe/4.8/aria-roledescription?application=RuleDescription) | Ensure aria-roledescription is only used on elements with an implicit or explicit role | Serious | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, deprecated | failure, needs review | | +| [audio-caption](https://dequeuniversity.com/rules/axe/4.8/audio-caption?application=RuleDescription) | Ensures <audio> elements have captions | Critical | cat.time-and-media, wcag2a, wcag121, EN-301-549, EN-9.1.2.1, section508, section508.22.a, deprecated | needs review | [2eb176](https://act-rules.github.io/rules/2eb176), [afb423](https://act-rules.github.io/rules/afb423) | +| [duplicate-id-active](https://dequeuniversity.com/rules/axe/4.8/duplicate-id-active?application=RuleDescription) | Ensures every id attribute value of active elements is unique | Serious | cat.parsing, wcag2a-obsolete, wcag411, deprecated | failure | [3ea0c8](https://act-rules.github.io/rules/3ea0c8) | +| [duplicate-id](https://dequeuniversity.com/rules/axe/4.8/duplicate-id?application=RuleDescription) | Ensures every id attribute value is unique | Minor | cat.parsing, wcag2a-obsolete, wcag411, deprecated | failure | [3ea0c8](https://act-rules.github.io/rules/3ea0c8) | diff --git a/doc/run-partial.md b/doc/run-partial.md index ae7114d8ea..a2570d2a92 100644 --- a/doc/run-partial.md +++ b/doc/run-partial.md @@ -13,6 +13,8 @@ const axeResults = await axe.finishRun(partialResults, options); **note**: The code in this page uses native DOM methods. This will only work on frames with the same origin. Scripts do not have access to `contentWindow` of cross-origin frames. Use of `runPartial` and `finishRun` in browser drivers like Selenium and Puppeteer works in the same way. +**note**: Because `axe.runPartial()` is designed to be serialized, it will not return element references even if the `elementRef` option is set. + ## axe.runPartial(context, options): Promise When using `axe.runPartial()` it is important to keep in mind that the `context` may be different for different frames. For example, `context` can be done in such a way that in frame A, `main` is excluded, and in frame B `footer` is. The `axe.utils.getFrameContexts` method will provide a list of frames that must be tested, and what context to test it with. diff --git a/lib/checks/aria/aria-errormessage-evaluate.js b/lib/checks/aria/aria-errormessage-evaluate.js index ed10261f64..b7b971d4e2 100644 --- a/lib/checks/aria/aria-errormessage-evaluate.js +++ b/lib/checks/aria/aria-errormessage-evaluate.js @@ -24,10 +24,10 @@ import { isVisibleToScreenReaders } from '../../commons/dom'; * @memberof checks * @return {Mixed} True if aria-errormessage references an existing element that uses a supported technique. Undefined if it does not reference an existing element. False otherwise. */ -function ariaErrormessageEvaluate(node, options, virtualNode) { +export default function ariaErrormessageEvaluate(node, options, virtualNode) { options = Array.isArray(options) ? options : []; - const attr = virtualNode.attr('aria-errormessage'); + const errorMessageAttr = virtualNode.attr('aria-errormessage'); const hasAttr = virtualNode.hasAttr('aria-errormessage'); const invaid = virtualNode.attr('aria-invalid'); const hasInvallid = virtualNode.hasAttr('aria-invalid'); @@ -74,12 +74,10 @@ function ariaErrormessageEvaluate(node, options, virtualNode) { } // limit results to elements that actually have this attribute - if (options.indexOf(attr) === -1 && hasAttr) { - this.data(tokenList(attr)); - return validateAttrValue.call(this, attr); + if (options.indexOf(errorMessageAttr) === -1 && hasAttr) { + this.data(tokenList(errorMessageAttr)); + return validateAttrValue.call(this, errorMessageAttr); } return true; } - -export default ariaErrormessageEvaluate; diff --git a/lib/checks/aria/aria-required-children-evaluate.js b/lib/checks/aria/aria-required-children-evaluate.js index beef5289af..226bd54703 100644 --- a/lib/checks/aria/aria-required-children-evaluate.js +++ b/lib/checks/aria/aria-required-children-evaluate.js @@ -28,43 +28,37 @@ export default function ariaRequiredChildrenEvaluate( ) { const reviewEmpty = options && Array.isArray(options.reviewEmpty) ? options.reviewEmpty : []; - const role = getExplicitRole(virtualNode, { dpub: true }); - const required = requiredOwned(role); + const explicitRole = getExplicitRole(virtualNode, { dpub: true }); + const required = requiredOwned(explicitRole); if (required === null) { return true; } - const { ownedRoles, ownedElements } = getOwnedRoles(virtualNode, required); - const unallowed = ownedRoles.filter(({ role }) => !required.includes(role)); + const ownedRoles = getOwnedRoles(virtualNode, required); + const unallowed = ownedRoles.filter( + ({ role, vNode }) => vNode.props.nodeType === 1 && !required.includes(role) + ); if (unallowed.length) { - this.relatedNodes(unallowed.map(({ ownedElement }) => ownedElement)); + this.relatedNodes(unallowed.map(({ vNode }) => vNode)); this.data({ messageKey: 'unallowed', values: unallowed - .map(({ ownedElement, attr }) => - getUnallowedSelector(ownedElement, attr) - ) + .map(({ vNode, attr }) => getUnallowedSelector(vNode, attr)) .filter((selector, index, array) => array.indexOf(selector) === index) .join(', ') }); return false; } - const missing = missingRequiredChildren( - virtualNode, - role, - required, - ownedRoles - ); - if (!missing) { + if (hasRequiredChildren(required, ownedRoles)) { return true; } - this.data(missing); + this.data(required); // Only review empty nodes when a node is both empty and does not have an aria-owns relationship - if (reviewEmpty.includes(role) && !ownedElements.some(isContent)) { + if (reviewEmpty.includes(explicitRole) && !ownedRoles.some(isContent)) { return undefined; } @@ -75,22 +69,20 @@ export default function ariaRequiredChildrenEvaluate( * Get all owned roles of an element */ function getOwnedRoles(virtualNode, required) { + let vNode; const ownedRoles = []; - const ownedElements = getOwnedVirtual(virtualNode).filter(vNode => { - return vNode.props.nodeType !== 1 || isVisibleToScreenReaders(vNode); - }); - - for (let i = 0; i < ownedElements.length; i++) { - const ownedElement = ownedElements[i]; - if (ownedElement.props.nodeType !== 1) { + const ownedVirtual = getOwnedVirtual(virtualNode); + while ((vNode = ownedVirtual.shift())) { + if (vNode.props.nodeType === 3) { + ownedRoles.push({ vNode, role: null }); + } + if (vNode.props.nodeType !== 1 || !isVisibleToScreenReaders(vNode)) { continue; } - const role = getRole(ownedElement, { noPresentational: true }); - - const globalAriaAttr = getGlobalAriaAttr(ownedElement); - const hasGlobalAriaOrFocusable = - !!globalAriaAttr || isFocusable(ownedElement); + const role = getRole(vNode, { noPresentational: true }); + const globalAriaAttr = getGlobalAriaAttr(vNode); + const hasGlobalAriaOrFocusable = !!globalAriaAttr || isFocusable(vNode); // if owned node has no role or is presentational, or if role // allows group or rowgroup, we keep parsing the descendant tree. @@ -101,37 +93,21 @@ function getOwnedRoles(virtualNode, required) { (['group', 'rowgroup'].includes(role) && required.some(requiredRole => requiredRole === role)) ) { - ownedElements.push(...ownedElement.children); + ownedVirtual.push(...vNode.children); } else if (role || hasGlobalAriaOrFocusable) { - ownedRoles.push({ - role, - attr: globalAriaAttr || 'tabindex', - ownedElement - }); + const attr = globalAriaAttr || 'tabindex'; + ownedRoles.push({ role, attr, vNode }); } } - return { ownedRoles, ownedElements }; + return ownedRoles; } /** - * Get missing children roles + * See if any required roles are in the ownedRoles array */ -function missingRequiredChildren(virtualNode, role, required, ownedRoles) { - for (let i = 0; i < ownedRoles.length; i++) { - const { role } = ownedRoles[i]; - - if (required.includes(role)) { - required = required.filter(requiredRole => requiredRole !== role); - return null; - } - } - - if (required.length) { - return required; - } - - return null; +function hasRequiredChildren(required, ownedRoles) { + return ownedRoles.some(({ role }) => role && required.includes(role)); } /** @@ -151,7 +127,6 @@ function getGlobalAriaAttr(vNode) { */ function getUnallowedSelector(vNode, attr) { const { nodeName, nodeType } = vNode.props; - if (nodeType === 3) { return `#text`; } @@ -160,20 +135,19 @@ function getUnallowedSelector(vNode, attr) { if (role) { return `[role=${role}]`; } - if (attr) { return nodeName + `[${attr}]`; } - return nodeName; } /** * Check if the node has content, or is itself content - * @param {VirtualNode} vNode + * @Object {Object} OwnedRole + * @property {VirtualNode} vNode * @returns {Boolean} */ -function isContent(vNode) { +function isContent({ vNode }) { if (vNode.props.nodeType === 3) { return vNode.props.nodeValue.trim().length > 0; } diff --git a/lib/checks/aria/braille-label-equivalent-evaluate.js b/lib/checks/aria/braille-label-equivalent-evaluate.js new file mode 100644 index 0000000000..ce301f684b --- /dev/null +++ b/lib/checks/aria/braille-label-equivalent-evaluate.js @@ -0,0 +1,22 @@ +import { sanitize, accessibleTextVirtual } from '../../commons/text'; + +/** + * Check that if aria-braillelabel is not empty, the element has an accessible text + * @memberof checks + * @return {Boolean} + */ +export default function brailleLabelEquivalentEvaluate( + node, + options, + virtualNode +) { + const brailleLabel = virtualNode.attr('aria-braillelabel') ?? ''; + if (!brailleLabel.trim()) { + return true; + } + try { + return sanitize(accessibleTextVirtual(virtualNode)) !== ''; + } catch { + return undefined; + } +} diff --git a/lib/checks/aria/braille-label-equivalent.json b/lib/checks/aria/braille-label-equivalent.json new file mode 100644 index 0000000000..a4d4dd0c1d --- /dev/null +++ b/lib/checks/aria/braille-label-equivalent.json @@ -0,0 +1,12 @@ +{ + "id": "braille-label-equivalent", + "evaluate": "braille-label-equivalent-evaluate", + "metadata": { + "impact": "serious", + "messages": { + "pass": "aria-braillelabel is used on an element with accessible text", + "fail": "aria-braillelabel is used on an element with no accessible text", + "incomplete": "Unable to compute accessible text" + } + } +} diff --git a/lib/checks/aria/braille-roledescription-equivalent-evaluate.js b/lib/checks/aria/braille-roledescription-equivalent-evaluate.js new file mode 100644 index 0000000000..5e03af7fac --- /dev/null +++ b/lib/checks/aria/braille-roledescription-equivalent-evaluate.js @@ -0,0 +1,29 @@ +import { sanitize } from '../../commons/text'; + +/** + * Check that if aria-brailleroledescription is not empty, + * the element has a non-empty aria-roledescription + * @memberof checks + * @return {Boolean} + */ +export default function brailleRoleDescriptionEquivalentEvaluate( + node, + options, + virtualNode +) { + const brailleRoleDesc = virtualNode.attr('aria-brailleroledescription') ?? ''; + if (sanitize(brailleRoleDesc) === '') { + return true; + } + const roleDesc = virtualNode.attr('aria-roledescription'); + if (typeof roleDesc !== 'string') { + this.data({ messageKey: 'noRoleDescription' }); + return false; + } + + if (sanitize(roleDesc) === '') { + this.data({ messageKey: 'emptyRoleDescription' }); + return false; + } + return true; +} diff --git a/lib/checks/aria/braille-roledescription-equivalent.json b/lib/checks/aria/braille-roledescription-equivalent.json new file mode 100644 index 0000000000..4005868c9c --- /dev/null +++ b/lib/checks/aria/braille-roledescription-equivalent.json @@ -0,0 +1,14 @@ +{ + "id": "braille-roledescription-equivalent", + "evaluate": "braille-roledescription-equivalent-evaluate", + "metadata": { + "impact": "serious", + "messages": { + "pass": "aria-brailleroledescription is used on an element with aria-roledescription", + "fail": { + "noRoleDescription": "aria-brailleroledescription is used on an element with no aria-roledescription", + "emptyRoleDescription": "aria-brailleroledescription is used on an element with an empty aria-roledescription" + } + } + } +} diff --git a/lib/checks/aria/no-implicit-explicit-label.json b/lib/checks/aria/no-implicit-explicit-label.json index b269bb642e..d5ed5a0b22 100644 --- a/lib/checks/aria/no-implicit-explicit-label.json +++ b/lib/checks/aria/no-implicit-explicit-label.json @@ -2,7 +2,7 @@ "id": "no-implicit-explicit-label", "evaluate": "no-implicit-explicit-label-evaluate", "metadata": { - "impact": "moderate", + "impact": "serious", "messages": { "pass": "There is no mismatch between a