@govtechsg/oobee 0.10.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +22 -0
- package/.github/pull_request_template.md +11 -0
- package/.github/workflows/docker-test.yml +54 -0
- package/.github/workflows/image.yml +107 -0
- package/.github/workflows/publish.yml +18 -0
- package/.idea/modules.xml +8 -0
- package/.idea/purple-a11y.iml +9 -0
- package/.idea/vcs.xml +6 -0
- package/.prettierrc.json +12 -0
- package/.vscode/extensions.json +5 -0
- package/.vscode/settings.json +10 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/DETAILS.md +163 -0
- package/Dockerfile +60 -0
- package/INSTALLATION.md +146 -0
- package/INTEGRATION.md +785 -0
- package/LICENSE +22 -0
- package/README.md +587 -0
- package/SECURITY.md +5 -0
- package/__mocks__/mock-report.html +1431 -0
- package/__mocks__/mockFunctions.ts +32 -0
- package/__mocks__/mockIssues.ts +64 -0
- package/__mocks__/mock_all_issues/000000001.json +64 -0
- package/__mocks__/mock_all_issues/000000002.json +53 -0
- package/__mocks__/mock_all_issues/fake-file.txt +0 -0
- package/__tests__/logs.test.ts +25 -0
- package/__tests__/mergeAxeResults.test.ts +278 -0
- package/__tests__/utils.test.ts +118 -0
- package/a11y-scan-results.zip +0 -0
- package/eslint.config.js +53 -0
- package/exclusions.txt +2 -0
- package/gitlab-pipeline-template.yml +54 -0
- package/jest.config.js +1 -0
- package/package.json +96 -0
- package/scripts/copyFiles.js +44 -0
- package/scripts/install_oobee_dependencies.cmd +13 -0
- package/scripts/install_oobee_dependencies.command +101 -0
- package/scripts/install_oobee_dependencies.ps1 +110 -0
- package/scripts/oobee_shell.cmd +13 -0
- package/scripts/oobee_shell.command +11 -0
- package/scripts/oobee_shell.sh +55 -0
- package/scripts/oobee_shell_ps.ps1 +54 -0
- package/src/cli.ts +401 -0
- package/src/combine.ts +240 -0
- package/src/constants/__tests__/common.test.ts +44 -0
- package/src/constants/cliFunctions.ts +305 -0
- package/src/constants/common.ts +1840 -0
- package/src/constants/constants.ts +443 -0
- package/src/constants/errorMeta.json +319 -0
- package/src/constants/itemTypeDescription.ts +11 -0
- package/src/constants/oobeeAi.ts +141 -0
- package/src/constants/questions.ts +181 -0
- package/src/constants/sampleData.ts +187 -0
- package/src/crawlers/__tests__/commonCrawlerFunc.test.ts +51 -0
- package/src/crawlers/commonCrawlerFunc.ts +656 -0
- package/src/crawlers/crawlDomain.ts +877 -0
- package/src/crawlers/crawlIntelligentSitemap.ts +156 -0
- package/src/crawlers/crawlLocalFile.ts +193 -0
- package/src/crawlers/crawlSitemap.ts +356 -0
- package/src/crawlers/custom/extractAndGradeText.ts +57 -0
- package/src/crawlers/custom/flagUnlabelledClickableElements.ts +964 -0
- package/src/crawlers/custom/utils.ts +486 -0
- package/src/crawlers/customAxeFunctions.ts +82 -0
- package/src/crawlers/pdfScanFunc.ts +468 -0
- package/src/crawlers/runCustom.ts +117 -0
- package/src/index.ts +173 -0
- package/src/logs.ts +66 -0
- package/src/mergeAxeResults.ts +964 -0
- package/src/npmIndex.ts +284 -0
- package/src/screenshotFunc/htmlScreenshotFunc.ts +411 -0
- package/src/screenshotFunc/pdfScreenshotFunc.ts +762 -0
- package/src/static/ejs/partials/components/categorySelector.ejs +4 -0
- package/src/static/ejs/partials/components/categorySelectorDropdown.ejs +57 -0
- package/src/static/ejs/partials/components/pagesScannedModal.ejs +70 -0
- package/src/static/ejs/partials/components/reportSearch.ejs +47 -0
- package/src/static/ejs/partials/components/ruleOffcanvas.ejs +105 -0
- package/src/static/ejs/partials/components/scanAbout.ejs +263 -0
- package/src/static/ejs/partials/components/screenshotLightbox.ejs +13 -0
- package/src/static/ejs/partials/components/summaryScanAbout.ejs +141 -0
- package/src/static/ejs/partials/components/summaryScanResults.ejs +16 -0
- package/src/static/ejs/partials/components/summaryTable.ejs +20 -0
- package/src/static/ejs/partials/components/summaryWcagCompliance.ejs +94 -0
- package/src/static/ejs/partials/components/topFive.ejs +6 -0
- package/src/static/ejs/partials/components/wcagCompliance.ejs +70 -0
- package/src/static/ejs/partials/footer.ejs +21 -0
- package/src/static/ejs/partials/header.ejs +230 -0
- package/src/static/ejs/partials/main.ejs +40 -0
- package/src/static/ejs/partials/scripts/bootstrap.ejs +8 -0
- package/src/static/ejs/partials/scripts/categorySelectorDropdownScript.ejs +190 -0
- package/src/static/ejs/partials/scripts/categorySummary.ejs +141 -0
- package/src/static/ejs/partials/scripts/highlightjs.ejs +335 -0
- package/src/static/ejs/partials/scripts/popper.ejs +7 -0
- package/src/static/ejs/partials/scripts/reportSearch.ejs +248 -0
- package/src/static/ejs/partials/scripts/ruleOffcanvas.ejs +801 -0
- package/src/static/ejs/partials/scripts/screenshotLightbox.ejs +71 -0
- package/src/static/ejs/partials/scripts/summaryScanResults.ejs +14 -0
- package/src/static/ejs/partials/scripts/summaryTable.ejs +78 -0
- package/src/static/ejs/partials/scripts/utils.ejs +441 -0
- package/src/static/ejs/partials/styles/bootstrap.ejs +12375 -0
- package/src/static/ejs/partials/styles/highlightjs.ejs +54 -0
- package/src/static/ejs/partials/styles/styles.ejs +1843 -0
- package/src/static/ejs/partials/styles/summaryBootstrap.ejs +12458 -0
- package/src/static/ejs/partials/summaryHeader.ejs +70 -0
- package/src/static/ejs/partials/summaryMain.ejs +75 -0
- package/src/static/ejs/report.ejs +420 -0
- package/src/static/ejs/summary.ejs +47 -0
- package/src/static/mustache/.prettierrc +4 -0
- package/src/static/mustache/Attention Deficit.mustache +11 -0
- package/src/static/mustache/Blind.mustache +11 -0
- package/src/static/mustache/Cognitive.mustache +7 -0
- package/src/static/mustache/Colorblindness.mustache +20 -0
- package/src/static/mustache/Deaf.mustache +12 -0
- package/src/static/mustache/Deafblind.mustache +7 -0
- package/src/static/mustache/Dyslexia.mustache +14 -0
- package/src/static/mustache/Low Vision.mustache +7 -0
- package/src/static/mustache/Mobility.mustache +15 -0
- package/src/static/mustache/Sighted Keyboard Users.mustache +42 -0
- package/src/static/mustache/report.mustache +1709 -0
- package/src/types/print-message.d.ts +28 -0
- package/src/types/types.ts +46 -0
- package/src/types/xpath-to-css.d.ts +3 -0
- package/src/utils.ts +332 -0
- package/tsconfig.json +15 -0
@@ -0,0 +1,801 @@
|
|
1
|
+
<%# functions used to populate content in the rule offcanvas that appears when an item in the
|
2
|
+
category summary is clicked %>
|
3
|
+
|
4
|
+
<script>
|
5
|
+
let currentComboboxOptionIndex = 0;
|
6
|
+
let isListenerAttached = false;
|
7
|
+
let options;
|
8
|
+
let isOffCanvasComboboxOpen = false;
|
9
|
+
|
10
|
+
function generateWcagConformanceLinks(conformanceList) {
|
11
|
+
const wcagConformanceUrls = {
|
12
|
+
wcag111: 'https://www.w3.org/TR/WCAG22/#non-text-content',
|
13
|
+
wcag122: 'https://www.w3.org/TR/WCAG22/#captions-prerecorded',
|
14
|
+
wcag131: 'https://www.w3.org/TR/WCAG22/#info-and-relationships',
|
15
|
+
wcag134: 'https://www.w3.org/TR/WCAG22/#orientation',
|
16
|
+
wcag135: 'https://www.w3.org/TR/WCAG22/#identify-input-purpose',
|
17
|
+
wcag141: 'https://www.w3.org/TR/WCAG22/#use-of-color',
|
18
|
+
wcag142: 'https://www.w3.org/TR/WCAG22/#audio-control',
|
19
|
+
wcag143: 'https://www.w3.org/TR/WCAG22/#contrast-minimum',
|
20
|
+
wcag144: 'https://www.w3.org/TR/WCAG22/#resize-text',
|
21
|
+
wcag146: 'https://www.w3.org/TR/WCAG21/#contrast-enhanced',
|
22
|
+
wcag1410: 'https://www.w3.org/TR/WCAG22/#reflow',
|
23
|
+
wcag1412: 'https://www.w3.org/TR/WCAG22/#text-spacing',
|
24
|
+
wcag211: 'https://www.w3.org/TR/WCAG22/#keyboard',
|
25
|
+
wcag221: 'https://www.w3.org/TR/WCAG22/#timing-adjustable',
|
26
|
+
wcag222: 'https://www.w3.org/TR/WCAG22/#pause-stop-hide',
|
27
|
+
wcag224: 'https://www.w3.org/TR/WCAG21/#interruptions',
|
28
|
+
wcag241: 'https://www.w3.org/TR/WCAG22/#bypass-blocks',
|
29
|
+
wcag242: 'https://www.w3.org/TR/WCAG22/#page-titled',
|
30
|
+
wcag243: 'https://www.w3.org/TR/WCAG22/#focus-order',
|
31
|
+
wcag244: 'https://www.w3.org/TR/WCAG22/#link-purpose-in-context',
|
32
|
+
wcag249: 'https://www.w3.org/TR/WCAG21/#link-purpose-link-only',
|
33
|
+
wcag258: 'https://www.w3.org/TR/WCAG22/#target-size-minimum',
|
34
|
+
wcag311: 'https://www.w3.org/TR/WCAG22/#language-of-page',
|
35
|
+
wcag312: 'https://www.w3.org/TR/WCAG22/#language-of-parts',
|
36
|
+
wcag315: 'https://www.w3.org/TR/WCAG22/#reading-level',
|
37
|
+
wcag332: 'https://www.w3.org/TR/WCAG22/#labels-or-instructions',
|
38
|
+
wcag325: 'https://www.w3.org/TR/WCAG21/#change-on-request',
|
39
|
+
wcag411: 'https://www.w3.org/TR/WCAG22/#parsing',
|
40
|
+
wcag412: 'https://www.w3.org/TR/WCAG22/#name-role-value',
|
41
|
+
};
|
42
|
+
|
43
|
+
const links = [];
|
44
|
+
for (let i = 1; i < conformanceList.length; i++) {
|
45
|
+
const [wcagSection, subSection, ...sectionItem] = conformanceList[i].slice(4); // slice to get rid of 'wcag';
|
46
|
+
const formattedConformanceNumber = `${wcagSection}.${subSection}.${sectionItem.join('')}`;
|
47
|
+
links.push(
|
48
|
+
`<a href="${
|
49
|
+
wcagConformanceUrls[conformanceList[i]]
|
50
|
+
}" target="_blank">WCAG ${formattedConformanceNumber}</a>`,
|
51
|
+
);
|
52
|
+
}
|
53
|
+
return links.join('  ,   ');
|
54
|
+
}
|
55
|
+
|
56
|
+
function expandRule(selectedCategory, selectedRule) {
|
57
|
+
const conformanceLevels = {
|
58
|
+
wcag2a: 'A',
|
59
|
+
wcag2aa: 'AA',
|
60
|
+
wcag21aa: 'AA',
|
61
|
+
wcag22aa: 'AA',
|
62
|
+
wcag2aaa: 'AAA',
|
63
|
+
};
|
64
|
+
|
65
|
+
document.getElementById('expandedRuleName').innerHTML = htmlEscapeString(
|
66
|
+
selectedRule.description,
|
67
|
+
);
|
68
|
+
|
69
|
+
const expandedRuleDescription = document.getElementById('expandedRuleDescription');
|
70
|
+
if (whyItMatters[selectedRule.rule]) {
|
71
|
+
expandedRuleDescription.hidden = false;
|
72
|
+
expandedRuleDescription.innerHTML = whyItMatters[selectedRule.rule];
|
73
|
+
} else {
|
74
|
+
expandedRuleDescription.hidden = true;
|
75
|
+
}
|
76
|
+
|
77
|
+
const expandedRuleHelpUrl = document.getElementById('expandedRuleHelpUrl');
|
78
|
+
if (selectedRule.helpUrl) {
|
79
|
+
expandedRuleHelpUrl.hidden = false;
|
80
|
+
expandedRuleHelpUrl.href = selectedRule.helpUrl;
|
81
|
+
} else {
|
82
|
+
expandedRuleHelpUrl.hidden = true;
|
83
|
+
}
|
84
|
+
|
85
|
+
// will be read out by screen readers but invisible on the page
|
86
|
+
const spanForSr = `<span class="visually-hidden">Level </span>`;
|
87
|
+
const wcagLevel = selectedRule.conformance[0];
|
88
|
+
const aLevel = conformanceLevels[wcagLevel]; // the actual level (i.e. "A", "AA")
|
89
|
+
const bestPracIcon = `<img src="data:image/svg+xml,<svg width='11' height='15' viewBox='0 0 11 15' fill='none' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M4.27122 14.4973C3.32493 14.4973 2.55693 13.7293 2.55693 12.7831V9.35449H7.69979V12.7831C7.69979 13.7293 6.93179 14.4973 5.9855 14.4973H4.27122Z' fill='white'/><path fill-rule='evenodd' clip-rule='evenodd' d='M7.69712 12.0171H5.98283V10.6234C5.98626 10.4297 6.00169 10.2497 6.03598 10.0766C6.14912 9.48343 6.47312 9.03771 6.79369 8.62971L7.20855 8.12057C7.38512 7.90457 7.5634 7.69029 7.7314 7.46743C8.43598 6.54171 8.67769 5.55257 8.47026 4.44343C8.18055 2.89371 6.74912 1.72114 5.14112 1.71429C3.5074 1.72114 2.07598 2.89371 1.78626 4.44343C1.58055 5.55257 1.82226 6.54171 2.52512 7.46743C2.69655 7.692 2.87483 7.90971 3.05483 8.12743L3.46283 8.63143C3.83312 9.08743 4.17255 9.50914 4.28226 10.0783C4.31483 10.2514 4.33026 10.4331 4.33369 10.6114V12.0171H2.6194V10.6234C2.61769 10.5566 2.61255 10.4794 2.59883 10.4023C2.56969 10.2531 2.38626 10.0269 2.22683 9.828L1.7314 9.216C1.53769 8.98114 1.34569 8.748 1.16055 8.50629C0.161118 7.19143 -0.195453 5.71886 0.101118 4.128C0.545118 1.74514 2.65198 0.0102857 5.10855 0C7.60455 0.0102857 9.7114 1.74514 10.1554 4.128C10.452 5.71886 10.0954 7.19143 9.09598 8.50629C8.91426 8.74457 8.72398 8.97771 8.53198 9.20914L8.1394 9.69429C7.94055 9.94457 7.75883 10.1863 7.7194 10.4006C7.70398 10.4811 7.69883 10.56 7.69712 10.6389V12.0171Z' fill='white'/></svg>" alt="" />`
|
90
|
+
|
91
|
+
document.getElementById('expandedRuleConformance').replaceChildren(
|
92
|
+
createElementFromString(`
|
93
|
+
<div class="d-flex align-items-center">
|
94
|
+
<div ${aLevel ? "" : "aria-hidden=\"true\""} class="conformance-bubble ${wcagLevel}">
|
95
|
+
${aLevel ? spanForSr : ""}
|
96
|
+
${aLevel ? aLevel : bestPracIcon}
|
97
|
+
</div>
|
98
|
+
${
|
99
|
+
!aLevel
|
100
|
+
? `<span>Best practice</span>`
|
101
|
+
: generateWcagConformanceLinks(selectedRule.conformance)
|
102
|
+
}
|
103
|
+
</div>
|
104
|
+
`),
|
105
|
+
);
|
106
|
+
|
107
|
+
if (oobeeAiRules.includes(selectedRule.rule)) {
|
108
|
+
document.querySelector('#expandedRuleAiFeedback').style.display = 'block';
|
109
|
+
} else {
|
110
|
+
document.querySelector('#expandedRuleAiFeedback').style.display = 'none';
|
111
|
+
}
|
112
|
+
|
113
|
+
const availableFixCategories = [];
|
114
|
+
const categorySelectors = [];
|
115
|
+
const comboboxCategorySelectors = [];
|
116
|
+
|
117
|
+
Object.keys(filteredItems).forEach(category => {
|
118
|
+
const ruleInCategory = filteredItems[category].rules.find(r => r.rule === selectedRule.rule);
|
119
|
+
|
120
|
+
if (ruleInCategory !== undefined && category !== 'passed') {
|
121
|
+
if (category !== 'passed') {
|
122
|
+
availableFixCategories.push(category);
|
123
|
+
}
|
124
|
+
|
125
|
+
// Create button / combobox option for each category based on availableCategories
|
126
|
+
const element = createElementFromString(`
|
127
|
+
<button id="${category}OffCanvasButtonSelector" aria-label="${getFormattedCategoryTitle(category)}, ${
|
128
|
+
ruleInCategory.totalItems
|
129
|
+
} ${ruleInCategory.totalItems === 1 ? 'occurrence' : 'occurrences'}" class="${category} category-selector">
|
130
|
+
<span class="d-flex align-items-center category-name">${getFormattedCategoryTitle(
|
131
|
+
category,
|
132
|
+
)}</span>
|
133
|
+
<span class="category-information">${ruleInCategory.totalItems} ${ruleInCategory.totalItems === 1 ? 'occurrence' : 'occurrences'}</span>
|
134
|
+
</button>
|
135
|
+
`);
|
136
|
+
|
137
|
+
const ruleOffCanvasComboboxOption = createElementFromString(`
|
138
|
+
<li
|
139
|
+
tabindex="-1"
|
140
|
+
id="${category}ExpandedRuleDropdownSelector"
|
141
|
+
class="${category} category-selector d-flex flex-row align-items-center gap-2 position-relative"
|
142
|
+
role="option"
|
143
|
+
>
|
144
|
+
<span
|
145
|
+
id="${category}Title"
|
146
|
+
class="d-flex align-items-center category-name fw-bold d-inline mb-0"
|
147
|
+
>
|
148
|
+
${getFormattedCategoryTitle(category)}
|
149
|
+
</span>
|
150
|
+
<span id="${category}ItemsInformation" class="category-information">
|
151
|
+
|
152
|
+
(${ruleInCategory.totalItems} Occurrences)
|
153
|
+
|
154
|
+
</span>
|
155
|
+
</li>`
|
156
|
+
);
|
157
|
+
|
158
|
+
// Populate the array of button / combobox option
|
159
|
+
categorySelectors.push(element);
|
160
|
+
comboboxCategorySelectors.push(ruleOffCanvasComboboxOption);
|
161
|
+
|
162
|
+
// Dynamically update the button / combobox option for categorySelectors
|
163
|
+
document.getElementById('expandedRuleCategorySelectors').replaceChildren(...categorySelectors);
|
164
|
+
document.getElementById('expandedRuleIssueTypeListbox').replaceChildren(...comboboxCategorySelectors);
|
165
|
+
|
166
|
+
// Iterate through each button / combobox option and add event listeners
|
167
|
+
document.getElementById('expandedRuleCategorySelectors').querySelectorAll("button").forEach(button => {
|
168
|
+
button.addEventListener('click', event => {
|
169
|
+
|
170
|
+
// Stops event listener from being added multiple times
|
171
|
+
event.stopImmediatePropagation()
|
172
|
+
|
173
|
+
removeSelectedClass()
|
174
|
+
|
175
|
+
event.currentTarget.classList.add('selected')
|
176
|
+
|
177
|
+
document.getElementById(`${event.currentTarget.classList[0]}ExpandedRuleDropdownSelector`).classList.add('selected')
|
178
|
+
|
179
|
+
updateExpandedRuleDropdownToggleContainer(`(${event.currentTarget.children[1].innerText})`)
|
180
|
+
|
181
|
+
ruleInfoText()
|
182
|
+
|
183
|
+
buildExpandedRuleCategoryContent(category, ruleInCategory);
|
184
|
+
})
|
185
|
+
})
|
186
|
+
|
187
|
+
document.getElementById('expandedRuleIssueTypeListbox').querySelectorAll("li").forEach(list => {
|
188
|
+
list.addEventListener('click', event => {
|
189
|
+
|
190
|
+
// Stops event listener from being added multiple times
|
191
|
+
event.stopImmediatePropagation()
|
192
|
+
|
193
|
+
removeSelectedClass()
|
194
|
+
|
195
|
+
event.currentTarget.classList.add('selected')
|
196
|
+
|
197
|
+
document.getElementById(`${event.currentTarget.classList[0]}OffCanvasButtonSelector`).classList.add('selected')
|
198
|
+
|
199
|
+
updateExpandedRuleDropdownToggleContainer(event.currentTarget.children[1].innerText)
|
200
|
+
|
201
|
+
ruleInfoText()
|
202
|
+
|
203
|
+
buildExpandedRuleCategoryContent(category, ruleInCategory);
|
204
|
+
|
205
|
+
toggleOffCanvasCombobox();
|
206
|
+
});
|
207
|
+
});
|
208
|
+
|
209
|
+
// Automatically click corresponding button / combobox option category in ruleOffCanvas
|
210
|
+
if (category === selectedCategory) {
|
211
|
+
element.click();
|
212
|
+
|
213
|
+
document.getElementById('expandedRuleDropdownToggleContainer').classList.replace(expandedRuleDropdownToggleContainer.classList[0], category);
|
214
|
+
document.getElementById('expandedRuleDropdownToggleCategoryTitle').innerText = getFormattedCategoryTitle(category)
|
215
|
+
document.getElementById('expandedRuleDropdownToggleCategoryInfo').innerText = `(${ruleInCategory.totalItems} Occurrences)`
|
216
|
+
}
|
217
|
+
if (category === selectedCategory) {
|
218
|
+
ruleOffCanvasComboboxOption.click();
|
219
|
+
|
220
|
+
document.getElementById('expandedRuleDropdownToggleContainer').classList.replace(expandedRuleDropdownToggleContainer.classList[0], category);
|
221
|
+
document.getElementById('expandedRuleDropdownToggleCategoryTitle').innerText = getFormattedCategoryTitle(category)
|
222
|
+
document.getElementById('expandedRuleDropdownToggleCategoryInfo').innerText = `(${ruleInCategory.totalItems} Occurrences)`
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
function ruleInfoText() {
|
227
|
+
document.getElementById('expandedRuleInfoText');
|
228
|
+
if (category === 'mustFix' && availableFixCategories.includes('goodToFix')) {
|
229
|
+
ruleInfoText.innerHTML =
|
230
|
+
'<p class="mb-4">There are also occurrences of this issue that falls under "Good to Fix”.</p>';
|
231
|
+
} else if (category === 'goodToFix' && availableFixCategories.includes('mustFix')) {
|
232
|
+
ruleInfoText.innerHTML =
|
233
|
+
'<p class="mb-4">There are also occurrences of this issue that falls under "Must Fix”.</p>';
|
234
|
+
} else if (category === 'passed' && availableFixCategories.length > 0) {
|
235
|
+
ruleInfoText.innerHTML = `<p class="mb-4">There are also occurrences of this issue that falls under ${availableFixCategories
|
236
|
+
.map(c => `"${getFormattedCategoryTitle(c)}"`)
|
237
|
+
.join(' and ')}.</p>`;
|
238
|
+
} else {
|
239
|
+
ruleInfoText.innerHTML = '';
|
240
|
+
}
|
241
|
+
}
|
242
|
+
|
243
|
+
function updateExpandedRuleDropdownToggleContainer(dropdownOccurrencesString){
|
244
|
+
document.getElementById('expandedRuleDropdownToggleContainer').classList.replace(expandedRuleDropdownToggleContainer.classList[0], event.currentTarget.classList[0]);
|
245
|
+
document.getElementById('expandedRuleDropdownToggleCategoryTitle').innerText = event.currentTarget.children[0].innerText.replace(/\n/g, '')
|
246
|
+
document.getElementById('expandedRuleDropdownToggleCategoryInfo').innerText = dropdownOccurrencesString
|
247
|
+
}
|
248
|
+
});
|
249
|
+
// START Script expandedRuleDropdownCategorySelector
|
250
|
+
const offCanvasComboboxElements = {
|
251
|
+
button: document.getElementById('expandedRuleIssueTypeComboBox'),
|
252
|
+
wrapper: document.getElementById('expandedRuleIssueTypeComboBoxWrapper'),
|
253
|
+
dropdown: document.getElementById('expandedRuleIssueTypeListbox'),
|
254
|
+
};
|
255
|
+
|
256
|
+
function toggleOffCanvasCombobox() {
|
257
|
+
document.getElementById('expandedRuleIssueTypeListbox').classList.toggle('active');
|
258
|
+
isOffCanvasComboboxOpen = !isOffCanvasComboboxOpen;
|
259
|
+
document.getElementById('expandedRuleIssueTypeComboBox').setAttribute('aria-expanded', isOffCanvasComboboxOpen.toString());
|
260
|
+
|
261
|
+
if (isOffCanvasComboboxOpen) {
|
262
|
+
document.getElementById('expandedRuleIssueTypeComboBox').setAttribute('aria-activedescendant', 'expandedRuleIssueTypeListbox');
|
263
|
+
}
|
264
|
+
}
|
265
|
+
|
266
|
+
const handleKeyPressInOffCanvas = event => {
|
267
|
+
const { key } = event;
|
268
|
+
const openKeys = ['ArrowDown', 'ArrowUp', 'Enter', ' '];
|
269
|
+
|
270
|
+
if (!isOffCanvasComboboxOpen && openKeys.includes(key)) {
|
271
|
+
toggleOffCanvasCombobox();
|
272
|
+
focusCurrentOption();
|
273
|
+
} else if (isOffCanvasComboboxOpen) {
|
274
|
+
switch (key) {
|
275
|
+
case 'Escape':
|
276
|
+
event.preventDefault();
|
277
|
+
toggleOffCanvasCombobox();
|
278
|
+
break;
|
279
|
+
case 'ArrowDown':
|
280
|
+
moveFocusDown();
|
281
|
+
break;
|
282
|
+
case 'ArrowUp':
|
283
|
+
moveFocusUp();
|
284
|
+
break;
|
285
|
+
case 'Enter':
|
286
|
+
case ' ':
|
287
|
+
const selectedComboboxRule = focusCurrentOption();
|
288
|
+
document.getElementById(`${selectedComboboxRule}ExpandedRuleDropdownSelector`).click();
|
289
|
+
break;
|
290
|
+
case 'Tab':
|
291
|
+
toggleOffCanvasCombobox();
|
292
|
+
break;
|
293
|
+
default:
|
294
|
+
break;
|
295
|
+
}
|
296
|
+
}
|
297
|
+
};
|
298
|
+
|
299
|
+
const handleOffCanvasComboboxInteraction = event => {
|
300
|
+
const isClickInsideButton = offCanvasComboboxElements.button.contains(event.target);
|
301
|
+
const isClickInsideDropdown = offCanvasComboboxElements.dropdown.contains(event.target);
|
302
|
+
|
303
|
+
if (isClickInsideButton || (!isClickInsideDropdown && isOffCanvasComboboxOpen)) {
|
304
|
+
toggleOffCanvasCombobox();
|
305
|
+
}
|
306
|
+
|
307
|
+
const clickedOffCanvasComboboxElement = event.target.closest('[role="option"]');
|
308
|
+
|
309
|
+
let clickedOffCanvasComboboxOption = null;
|
310
|
+
if (clickedOffCanvasComboboxElement && offCanvasComboboxElements.dropdown.contains(clickedOffCanvasComboboxElement)) {
|
311
|
+
clickedOffCanvasComboboxOption = clickedOffCanvasComboboxElement;
|
312
|
+
}
|
313
|
+
|
314
|
+
if (clickedOffCanvasComboboxOption) {
|
315
|
+
toggleOffCanvasCombobox();
|
316
|
+
}
|
317
|
+
|
318
|
+
};
|
319
|
+
|
320
|
+
const moveFocusDown = () => {
|
321
|
+
if (currentComboboxOptionIndex < document.getElementById('expandedRuleIssueTypeListbox').querySelectorAll('[role="option"]').length - 1) {
|
322
|
+
currentComboboxOptionIndex++;
|
323
|
+
} else {
|
324
|
+
currentComboboxOptionIndex = 0;
|
325
|
+
}
|
326
|
+
focusCurrentOption();
|
327
|
+
};
|
328
|
+
|
329
|
+
const moveFocusUp = () => {
|
330
|
+
if (currentComboboxOptionIndex > 0) {
|
331
|
+
currentComboboxOptionIndex--;
|
332
|
+
} else {
|
333
|
+
currentComboboxOptionIndex = document.getElementById('expandedRuleIssueTypeListbox').querySelectorAll('[role="option"]').length - 1;
|
334
|
+
}
|
335
|
+
focusCurrentOption();
|
336
|
+
};
|
337
|
+
|
338
|
+
const focusCurrentOption = () => {
|
339
|
+
const currentComboboxOption = document.getElementById('expandedRuleIssueTypeListbox').querySelectorAll('[role="option"]')[currentComboboxOptionIndex];
|
340
|
+
|
341
|
+
currentComboboxOption.focus();
|
342
|
+
|
343
|
+
currentComboboxOption.scrollIntoView({
|
344
|
+
block: 'nearest',
|
345
|
+
});
|
346
|
+
return currentComboboxOption.classList[0];
|
347
|
+
};
|
348
|
+
|
349
|
+
if (!isListenerAttached) {
|
350
|
+
offCanvasComboboxElements.wrapper.addEventListener('keydown', handleKeyPressInOffCanvas);
|
351
|
+
document.addEventListener('click', handleOffCanvasComboboxInteraction);
|
352
|
+
isListenerAttached = true;
|
353
|
+
}
|
354
|
+
// END Script expandedRuleDropdownCategorySelector
|
355
|
+
}
|
356
|
+
|
357
|
+
function removeSelectedClass() {
|
358
|
+
document.getElementById('expandedRuleCategorySelectors').querySelectorAll("button").forEach(button => {
|
359
|
+
button.classList.remove('selected');
|
360
|
+
});
|
361
|
+
document.getElementById('expandedRuleIssueTypeListbox').querySelectorAll("li").forEach(list => {
|
362
|
+
list.classList.remove('selected');
|
363
|
+
});
|
364
|
+
}
|
365
|
+
|
366
|
+
function buildExpandedRuleCategoryContent(category, ruleInCategory) {
|
367
|
+
const contentContainer = document.getElementById('expandedRuleCategoryContent');
|
368
|
+
const isCustomFlow = <%- isCustomFlow -%>;
|
369
|
+
|
370
|
+
if (category === 'passed') {
|
371
|
+
contentContainer.innerHTML = `You may find the list of passed HTML elements in <a href='./passed_items.json' target='_blank'>passed_items.json.txt</a>.`;
|
372
|
+
return;
|
373
|
+
}
|
374
|
+
|
375
|
+
const contentTitle = createElementFromString(`
|
376
|
+
<h4 id="issue-page-count" class="mb-4">
|
377
|
+
Pages with this issue (${ruleInCategory.pagesAffected.length})
|
378
|
+
</h4>`);
|
379
|
+
|
380
|
+
const accordionsList = createElementFromString(`<ul class="unbulleted-list"></ul>`);
|
381
|
+
|
382
|
+
ruleInCategory.pagesAffected.forEach((page, index) => {
|
383
|
+
const accordionId = `${ruleInCategory.rule}-${category}-page-${index}`;
|
384
|
+
var accordionAIId = `${ruleInCategory.rule}-${category}-accordion-AI-${index}`;
|
385
|
+
var buttonAIId = `${ruleInCategory.rule}-${category}-button-AI-${index}`;
|
386
|
+
var errorAIId = `${ruleInCategory.rule}-${category}-error-AI-${index}`;
|
387
|
+
|
388
|
+
const accordion = createElementFromString(`
|
389
|
+
<li>
|
390
|
+
<div class="accordion mt-2 ${category}">
|
391
|
+
<div class="accordion-item">
|
392
|
+
<div class="accordion-header" id="${accordionId}-title">
|
393
|
+
<button
|
394
|
+
aria-label="Page ${index + 1}: ${page.pageTitle}, ${page.items.length} ${page.items.length === 1 ? 'occurrence' : 'occurrences'}" class="accordion-button collapsed"
|
395
|
+
type="button"
|
396
|
+
|
397
|
+
>
|
398
|
+
<span class="sr-only visually-hidden">${page.items.length} ${page.items.length === 1 ? 'occurrence' : 'occurrences'}</span>
|
399
|
+
<div class="me-3">${page.metadata ? page.metadata : page.pageTitle}</div>
|
400
|
+
<div class="ms-auto counter">${page.items.length}</div>
|
401
|
+
</button>
|
402
|
+
</div>
|
403
|
+
<div id="${accordionId}-content" class="accordion-collapse collapse" aria-labelledby="${accordionId}-title">
|
404
|
+
<div class="accordion-body p-3">
|
405
|
+
${ isCustomFlow
|
406
|
+
?
|
407
|
+
`
|
408
|
+
<div class="custom-flow-screenshot-container">
|
409
|
+
<img alt="Screenshot of ${page.url}" src="${page.pageImagePath}" class="custom-flow-screenshot"/>
|
410
|
+
<div><a href="${page.url}" target="_blank">${page.url}</a></div>
|
411
|
+
</div>
|
412
|
+
`
|
413
|
+
: `<a href="${page.url}" target="_blank">${page.url}</a>`
|
414
|
+
}
|
415
|
+
<div class="page-accordion-content-title">
|
416
|
+
<span>${getFormattedCategoryTitle(category)} elements</span>
|
417
|
+
<span class="page-items-count">${page.items.length}</span>
|
418
|
+
</div>
|
419
|
+
</div>
|
420
|
+
</div>
|
421
|
+
</div>
|
422
|
+
</div>
|
423
|
+
</li>
|
424
|
+
`);
|
425
|
+
|
426
|
+
accordion.querySelector('button').addEventListener('click', function(event) {
|
427
|
+
var accordionBody = accordion.querySelector(".accordion-body");
|
428
|
+
|
429
|
+
// So that It does not keep adding
|
430
|
+
if (!accordionBody.querySelector(".unbulleted-list"))
|
431
|
+
{
|
432
|
+
buildExpandedRuleCategoryContentAccordian(accordionId,category,ruleInCategory,page,index);
|
433
|
+
|
434
|
+
this.setAttribute('data-bs-target', '#' + accordionId+"-content");
|
435
|
+
|
436
|
+
// Remove the event listener temporarily
|
437
|
+
this.removeEventListener('click', arguments.callee);
|
438
|
+
|
439
|
+
// Programmatically trigger a click on the button to open the accordion
|
440
|
+
this.click();
|
441
|
+
}
|
442
|
+
});
|
443
|
+
|
444
|
+
accordion.querySelector('button').addEventListener('click', function(event) {
|
445
|
+
// Set data attributes
|
446
|
+
this.setAttribute('data-bs-toggle', 'collapse');
|
447
|
+
this.setAttribute('data-bs-target', '#' + accordionId + "-content");
|
448
|
+
this.setAttribute('aria-expanded', 'false');
|
449
|
+
this.setAttribute('aria-controls', accordionId + "-content");
|
450
|
+
|
451
|
+
// Initialize the Collapse plugin on the button element
|
452
|
+
var collapse = new bootstrap.Collapse(this, {
|
453
|
+
toggle: false // Set to true if you want to toggle the collapsed state on initialization
|
454
|
+
});
|
455
|
+
|
456
|
+
// Remove the event listener temporarily
|
457
|
+
this.removeEventListener('click', arguments.callee);
|
458
|
+
|
459
|
+
// Programmatically trigger a click on the button to open the accordion
|
460
|
+
setTimeout(() => { // Delaying to ensure the content is added before triggering the click
|
461
|
+
this.click();
|
462
|
+
}, 0);
|
463
|
+
|
464
|
+
})
|
465
|
+
|
466
|
+
if (isCustomFlow) {
|
467
|
+
const customScreenshotElem = accordion.getElementsByClassName(`custom-flow-screenshot`)[0];
|
468
|
+
customScreenshotElem.onerror = function(event) {
|
469
|
+
this.onerror = null;
|
470
|
+
this.remove();
|
471
|
+
}
|
472
|
+
customScreenshotElem.onclick = function(event) {
|
473
|
+
event.preventDefault();
|
474
|
+
openLightbox(this.src, page.pageTitle, page.url);
|
475
|
+
}
|
476
|
+
}
|
477
|
+
|
478
|
+
accordionsList.appendChild(accordion);
|
479
|
+
return accordion;
|
480
|
+
});
|
481
|
+
contentContainer.replaceChildren(contentTitle, accordionsList);
|
482
|
+
|
483
|
+
hljs.highlightAll();
|
484
|
+
}
|
485
|
+
|
486
|
+
function generateItemMessageElement(displayNeedsReview, rawMessage) {
|
487
|
+
if (rawMessage.includes('\n\nFix')) {
|
488
|
+
rawMessage = rawMessage.replace('\n\nFix', '\n Fix');
|
489
|
+
}
|
490
|
+
|
491
|
+
const htmlEscapedMessageArray = rawMessage.split('\n ').map(m => htmlEscapeString(m));
|
492
|
+
|
493
|
+
if (displayNeedsReview) {
|
494
|
+
if (htmlEscapedMessageArray.length === 1) {
|
495
|
+
return `<p class="mb-0">${htmlEscapedMessageArray[0]}</p>`;
|
496
|
+
} else {
|
497
|
+
return `<ul>${htmlEscapedMessageArray.map(m => `<li>${m}</li>`).join('')}</ul>`;
|
498
|
+
}
|
499
|
+
} else {
|
500
|
+
let i = 0;
|
501
|
+
const elements = [];
|
502
|
+
while (i < htmlEscapedMessageArray.length) {
|
503
|
+
if (htmlEscapedMessageArray[i].startsWith('Fix ')) {
|
504
|
+
elements.push(`<p class="mb-0">${htmlEscapedMessageArray[i]}</p>`);
|
505
|
+
i++;
|
506
|
+
} else {
|
507
|
+
const fixesList = [];
|
508
|
+
while (
|
509
|
+
i < htmlEscapedMessageArray.length &&
|
510
|
+
!htmlEscapedMessageArray[i].startsWith('Fix a')
|
511
|
+
) {
|
512
|
+
fixesList.push(`<li>${htmlEscapedMessageArray[i]}</li>`);
|
513
|
+
i++;
|
514
|
+
}
|
515
|
+
elements.push(`<ul>${fixesList.join('')}</ul>`);
|
516
|
+
}
|
517
|
+
}
|
518
|
+
|
519
|
+
return elements.join('');
|
520
|
+
}
|
521
|
+
}
|
522
|
+
|
523
|
+
function buildExpandedRuleCategoryContentAccordian(accordionId, ruleInCategory)
|
524
|
+
{
|
525
|
+
console.log('Accordion ID:', accordionId);
|
526
|
+
console.log('ruleInCategory',JSON.stringify(ruleInCategory))
|
527
|
+
}
|
528
|
+
|
529
|
+
function buildExpandedRuleCategoryContentAccordian(accordionId,category,ruleInCategory,page,index)
|
530
|
+
{
|
531
|
+
var accordionAIId = `${ruleInCategory.rule}-${category}-accordion-AI-${index}`;
|
532
|
+
var buttonAIId = `${ruleInCategory.rule}-${category}-button-AI-${index}`;
|
533
|
+
var errorAIId = `${ruleInCategory.rule}-${category}-error-AI-${index}`;
|
534
|
+
let accordion = document.getElementById(`${accordionId}-title`).parentElement.parentElement.parentElement
|
535
|
+
|
536
|
+
|
537
|
+
const accordionBody = accordion.getElementsByClassName('accordion-body')[0];
|
538
|
+
const elementCardsList = createElementFromString('<ul class="unbulleted-list"></ul>');
|
539
|
+
|
540
|
+
page.items.forEach(async (item, index) => {
|
541
|
+
const accordionDivToAppendAI = `${accordionAIId}-${index}`;
|
542
|
+
const buttonDivForAiFeedback = `${buttonAIId}-${index}`;
|
543
|
+
const aiErrorDiv = `${errorAIId}-${index}`;
|
544
|
+
let itemCard;
|
545
|
+
const isPurpleAiRule = oobeeAiRules.includes(ruleInCategory.rule);
|
546
|
+
let oobeeAiQueryLabel;
|
547
|
+
if (isPurpleAiRule) {
|
548
|
+
oobeeAiQueryLabel = await checkPurpleAiQueryLabel(ruleInCategory.rule, item.html);
|
549
|
+
}
|
550
|
+
|
551
|
+
itemCard = createElementFromString(`
|
552
|
+
<li>
|
553
|
+
<div class="card mt-3">
|
554
|
+
${item.displayNeedsReview ? `<div class="needsReview">This occurrence might be a false positive that needs to be verified by a human.</div>` : ``}
|
555
|
+
<div class="p-3">
|
556
|
+
${item.screenshotPath
|
557
|
+
?
|
558
|
+
`
|
559
|
+
<div class="hide-on-img-error">
|
560
|
+
<div class="d-flex justify-content-between g-one">
|
561
|
+
<div class="fw-bold">Screenshot</div>
|
562
|
+
<div class="page-item-card-section-content bg-grey-w-border">
|
563
|
+
<img
|
564
|
+
src=${item.screenshotPath}
|
565
|
+
onerror="this.onerror = null; this.closest('div.hide-on-img-error').remove();"
|
566
|
+
alt="Screenshot of affected element" />
|
567
|
+
</div>
|
568
|
+
</div>
|
569
|
+
<hr/>
|
570
|
+
</div>
|
571
|
+
`
|
572
|
+
: ``
|
573
|
+
}
|
574
|
+
${ item.xpath
|
575
|
+
?
|
576
|
+
`
|
577
|
+
<div class="d-flex justify-content-between g-one">
|
578
|
+
<div class="fw-bold">Path</div>
|
579
|
+
<div class="page-item-card-section-content">
|
580
|
+
<div class="g-one path-container">
|
581
|
+
${item.xpath}
|
582
|
+
<button
|
583
|
+
aria-label="Copy path to clipboard"
|
584
|
+
class="copy-button"
|
585
|
+
data-bs-toggle="tooltip"
|
586
|
+
data-bs-placement="top"
|
587
|
+
data-bs-original-title="Copy"
|
588
|
+
>
|
589
|
+
<svg class="copy-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
590
|
+
<path d="M14.2188 2.5H7.65625C7.15897 2.5 6.68206 2.69754 6.33042 3.04917C5.97879 3.40081 5.78125 3.87772 5.78125 4.375C5.28397 4.375 4.80706 4.57254 4.45542 4.92417C4.10379 5.27581 3.90625 5.75272 3.90625 6.25V15.625C3.90625 16.1223 4.10379 16.5992 4.45542 16.9508C4.80706 17.3025 5.28397 17.5 5.78125 17.5H12.3438C12.841 17.5 13.3179 17.3025 13.6696 16.9508C14.0212 16.5992 14.2188 16.1223 14.2188 15.625C14.716 15.625 15.1929 15.4275 15.5446 15.0758C15.8962 14.7242 16.0938 14.2473 16.0938 13.75V4.375C16.0938 3.87772 15.8962 3.40081 15.5446 3.04917C15.1929 2.69754 14.716 2.5 14.2188 2.5ZM14.2188 14.6875V6.25C14.2188 5.75272 14.0212 5.27581 13.6696 4.92417C13.3179 4.57254 12.841 4.375 12.3438 4.375H6.71875C6.71875 4.12636 6.81752 3.8879 6.99334 3.71209C7.16915 3.53627 7.40761 3.4375 7.65625 3.4375H14.2188C14.4674 3.4375 14.7058 3.53627 14.8817 3.71209C15.0575 3.8879 15.1562 4.12636 15.1562 4.375V13.75C15.1562 13.9986 15.0575 14.2371 14.8817 14.4129C14.7058 14.5887 14.4674 14.6875 14.2188 14.6875ZM4.84375 6.25C4.84375 6.00136 4.94252 5.7629 5.11834 5.58709C5.29415 5.41127 5.53261 5.3125 5.78125 5.3125H12.3438C12.5924 5.3125 12.8308 5.41127 13.0067 5.58709C13.1825 5.7629 13.2812 6.00136 13.2812 6.25V15.625C13.2812 15.8736 13.1825 16.1121 13.0067 16.2879C12.8308 16.4637 12.5924 16.5625 12.3438 16.5625H5.78125C5.53261 16.5625 5.29415 16.4637 5.11834 16.2879C4.94252 16.1121 4.84375 15.8736 4.84375 15.625V6.25Z" fill="#26241b"/>
|
591
|
+
</svg>
|
592
|
+
</button>
|
593
|
+
</div>
|
594
|
+
</div>
|
595
|
+
</div>
|
596
|
+
<hr/>
|
597
|
+
`
|
598
|
+
:``
|
599
|
+
}
|
600
|
+
<div class="d-flex justify-content-between g-one">
|
601
|
+
${
|
602
|
+
item.html
|
603
|
+
? `
|
604
|
+
<div class="fw-bold">HTML element</div>
|
605
|
+
<pre class="page-item-card-section-content">
|
606
|
+
<code class="language-html">
|
607
|
+
${htmlEscapeString(item.html)}
|
608
|
+
</code>
|
609
|
+
</pre>`
|
610
|
+
: `
|
611
|
+
<div class="fw-bold">Location</div>
|
612
|
+
<div class="page-item-card-section-content">
|
613
|
+
${item.page > 0 ? `Page ${item.page}` : 'Document'}
|
614
|
+
</div>`
|
615
|
+
}
|
616
|
+
</div>
|
617
|
+
<hr />
|
618
|
+
<div class="d-flex justify-content-between g-one">
|
619
|
+
<div class="fw-bold page-item-card-section-title">${
|
620
|
+
item.displayNeedsReview ? 'Details' : 'How to fix'
|
621
|
+
}</div>
|
622
|
+
<div class="page-item-card-section-content">
|
623
|
+
${generateItemMessageElement(item.displayNeedsReview, item.message)}
|
624
|
+
</div>
|
625
|
+
</div>
|
626
|
+
${isPurpleAiRule ? `
|
627
|
+
<hr />
|
628
|
+
<div class="d-flex g-one">
|
629
|
+
<div class="fw-bold page-item-card-section-title">AI suggestion</div>
|
630
|
+
<div id="${accordionDivToAppendAI}" class="page-item-card-section-content">
|
631
|
+
${oobeeAiQueryLabel.hasNetworkError ?
|
632
|
+
`<button
|
633
|
+
id=${buttonDivForAiFeedback}
|
634
|
+
class="aiGenerateResponseButton"
|
635
|
+
onClick="handleOfflinePurpleAi(
|
636
|
+
'${ruleInCategory.rule}',
|
637
|
+
'${accordionDivToAppendAI}',
|
638
|
+
'${escapeHtmlStringForArg(item.html)}',
|
639
|
+
'${buttonDivForAiFeedback}',
|
640
|
+
'${aiErrorDiv}')"
|
641
|
+
>
|
642
|
+
Generate response
|
643
|
+
</button>
|
644
|
+
<div id=${aiErrorDiv}></div>` :
|
645
|
+
(oobeeAiQueryLabel.label ?
|
646
|
+
`<button
|
647
|
+
id=${buttonDivForAiFeedback}
|
648
|
+
class="aiGenerateResponseButton"
|
649
|
+
onClick="getPurpleAiAnswer(
|
650
|
+
'${ruleInCategory.rule}',
|
651
|
+
'${accordionDivToAppendAI}',
|
652
|
+
'${oobeeAiQueryLabel.label}',
|
653
|
+
'${buttonDivForAiFeedback}',
|
654
|
+
'${aiErrorDiv}')"
|
655
|
+
>
|
656
|
+
Generate response
|
657
|
+
</button>
|
658
|
+
<div id=${aiErrorDiv}></div>` :
|
659
|
+
`<span class="processAI">
|
660
|
+
Processing AI suggestions, please check back later.
|
661
|
+
</span>`
|
662
|
+
)
|
663
|
+
}
|
664
|
+
</div>
|
665
|
+
</div>` : ``
|
666
|
+
}
|
667
|
+
</div>
|
668
|
+
</div>
|
669
|
+
<li>
|
670
|
+
`);
|
671
|
+
elementCardsList.appendChild(itemCard);
|
672
|
+
|
673
|
+
const copyButtonElem = itemCard.getElementsByClassName('copy-button')[0];
|
674
|
+
const copyTooltipItem = new bootstrap.Tooltip(copyButtonElem);
|
675
|
+
const textToCopy = createElementFromString(`<textarea>${item.xpath}</textarea>`)
|
676
|
+
copyButtonElem.onclick = event => {
|
677
|
+
textToCopy.select();
|
678
|
+
// Copy the text inside the text field
|
679
|
+
navigator.clipboard.writeText(textToCopy.value);
|
680
|
+
|
681
|
+
copyButtonElem.setAttribute('data-bs-original-title', 'Copied');
|
682
|
+
copyTooltipItem.update();
|
683
|
+
copyTooltipItem.show();
|
684
|
+
|
685
|
+
setTimeout(() => {
|
686
|
+
copyButtonElem.setAttribute('data-bs-original-title', 'Copy');
|
687
|
+
copyTooltipItem.update();
|
688
|
+
}, 1500)
|
689
|
+
};
|
690
|
+
copyButtonElem.onmousedown = event => {
|
691
|
+
event.preventDefault();
|
692
|
+
}
|
693
|
+
|
694
|
+
hljs.configure({
|
695
|
+
ignoreUnescapedHTML: true
|
696
|
+
});
|
697
|
+
hljs.highlightAll();
|
698
|
+
});
|
699
|
+
|
700
|
+
accordionBody.appendChild(elementCardsList);
|
701
|
+
|
702
|
+
}
|
703
|
+
|
704
|
+
const whyItMatters = {
|
705
|
+
'accesskeys': '<p>\n Specifying a <code>accesskey</code> attribute value for some part of a\n document allows users to quickly activate or move the focus to a specific\n element by pressing the specified key (usually in combination with the\n <code><kbd>alt</kbd></code> key). Duplicating <code>accesskey</code> values\n creates unexpected effects that ultimately make the page less accessible.\n</p>',
|
706
|
+
'area-alt': '<p>\n Screen readers have no way of translating images into words. It is important\n that all images, including image maps, have <code>alt</code> text values.\n</p>',
|
707
|
+
'aria-allowed-attr': '<p>\n Using ARIA attributes in roles where they are not allowed can interfere with\n the accessibility of the web page. Using an invalid role-attribute combination\n will, at best, result in no effect on the accessibility of the application\n and, at worst, may trigger behavior that disables accessibility for entire\n portions of an application.\n</p>',
|
708
|
+
'aria-command-name': '<p>\n Screen reader users are not able to discern the purpose of elements with\n <code>role="link"</code>, <code>role="button"</code>, or\n <code>role="menuitem"</code> that do not have an accessible name.\n</p>',
|
709
|
+
'aria-dialog-name': '<p>\n Screen reader users are not able to discern the purpose of elements with\n <code>role="dialog"</code> or <code>role="alertdialog"</code> that do not have\n an accessible name.\n</p>',
|
710
|
+
'aria-hidden-focus': '<p>\n Using the <code>aria-hidden="true"</code> attribute on an element removes the\n element and ALL of its child nodes from the accessibility API making it\n completely inaccessible to screen readers and other assistive technologies.\n Aria-hidden may be used with extreme caution to hide visibly rendered content\n from assistive technologies only if the act of hiding this content is intended\n to improve the experience for users of assistive technologies by removing\n redundant or extraneous content. If aria-hidden is used to hide visible\n content from screen readers, the identical or equivalent meaning and\n functionality must be exposed to assistive technologies.\n</p>',
|
711
|
+
'aria-input-field-name': '<p>\n This new rule ensures every ARIA input field has an accessible name.\n Accessible names must exist for the following input field roles:\n</p>\n<ul>\n <li>combobox</li>\n <li>listbox</li>\n <li>searchbox</li>\n <li>slider</li>\n <li>spinbutton</li>\n <li>textbox</li>\n</ul>',
|
712
|
+
'aria-meter-name': '<p>\n Screen reader users are not able to discern the purpose of elements with\n <code>role="meter"</code> that do not have an accessible name.\n</p>',
|
713
|
+
'aria-progressbar-name': '<p>\n Screen reader users are not able to discern the purpose of elements with\n <code>role="progressbar"</code> that do not have an accessible name.\n</p>',
|
714
|
+
'aria-required-children': '<p>\n For each role, WAI-ARIA explicitly defines which child and parent roles are\n allowable and/or required. ARIA <code>role</code>s missing required child\n <code>role</code>s will not be able to perform the accessibility functions\n intended by the developer.\n</p>',
|
715
|
+
'aria-required-parent': '<p>\n For each role, WAI-ARIA explicitly defines which child and parent roles are\n allowable and/or required. Elements containing ARIA <code>role</code> values\n missing required parent element <code>role</code> values will not enable\n assistive technology to function as intended by the developer.\n</p>',
|
716
|
+
'aria-roledescription': '<p>\n Inappropriate <code>aria-roledescription</code> attribute values that conflict\n with an element's implied or explicit <code>role</code> value can interfere\n with the accessibility of the web page. A conflicting\n <code>aria-roledescription</code> attribute value may result in no effect on\n the accessibility of the application and may trigger behavior that disables\n accessibility for entire portions of an application.\n</p>',
|
717
|
+
'aria-text': '<p>\n When a text node is split by markup (e.g.\n <code><h1>Hello <span>World</span></h1></code>)\n VoiceOver will treat it as two separate phrases instead of just one. Adding\n <code>role="text"</code> around the elements solves the problem. However, it\n also overrides the role of the element and all descendants and treats them all\n as text nodes. If one of the descendant elements is also focusable it would\n create an empty tab stop. That is, you could tab to the element but VoiceOver\n would not announce its name, role, or value.\n</p>',
|
718
|
+
'aria-toggle-field-name': '<p>\n Ensures every element with a semantic role also has an accessible name.\n Semantic roles include:\n</p>\n<ul>\n <li>checkbox</li>\n <li>menu</li>\n <li>menuitemcheckbox</li>\n <li>menuitemradio</li>\n <li>radio</li>\n <li>radiogroup</li>\n <li>switch</li>\n</ul>',
|
719
|
+
'aria-tooltip-name': '<p>\n Screen reader users are not able to discern the purpose of elements with\n <code>role="tooltip"</code> that do not have an accessible name.\n</p>',
|
720
|
+
'aria-treeitem-name': '<p>\n Screen reader users are not able to discern the purpose of elements with\n <code>role="treeitem"</code> that do not have an accessible name.\n</p>',
|
721
|
+
'aria-valid-attr-value': '<p>\n ARIA attributes (i.e. starting with <code>aria-</code>) must contain valid\n values. These values must be spelled correctly and correspond to values that\n make sense for a particular attribute to perform the intended accessibility\n function.\n</p>',
|
722
|
+
'aria-valid-attr': '<p>\n If the developer uses a non-existent or misspelled ARIA attribute, the\n attribute will not be able to perform the accessibility function intended by\n the developer.\n</p>',
|
723
|
+
'autocomplete-valid': '<p>\n Failure to provide autocomplete values in form fields results in inaccessible\n content. Screen readers do not read identified autocomplete form fields if the\n appropriate autocomplete attribute values are missing. Users cannot correctly\n navigate forms when screen readers cannot provide adequate information to the\n user regarding form field interaction requirements.\n</p>',
|
724
|
+
'avoid-inline-spacing': '<p>\n Many people with cognitive disabilities have trouble tracking lines of text\n when a block of text is single spaced. Providing spacing between 1.5 to 2\n allows them to start a new line more easily once they have finished the\n previous one.\n</p>',
|
725
|
+
'blink': '<p>\n As the name suggests, <code>blink</code> tags cause content to flash. Though\n you may like the effect, blinking text can be difficult to read, and blinking\n objects (links, buttons, etc.) can be difficult to activate, especially for\n users with imprecise or limited dexterity.\n</p>',
|
726
|
+
'definition-list': '<p>\n Screen readers have a specific way of announcing definition lists. When such\n lists are not properly marked up, this creates the opportunity for confusing\n or inaccurate screen reader output.\n</p>',
|
727
|
+
'dlitem': '<p>\n A definition list item must be wrapped in parent <code>dl</code> elements,\n otherwise it will be invalid.\n</p>',
|
728
|
+
'duplicate-id-active': '<p>\n The ID attribute uniquely identifies focusable elements on a page. It does not\n make sense to duplicate an active ID.\n</p>',
|
729
|
+
'empty-table-header': '<p>\n Table header elements should have visible text that describes the purpose of\n the row or column to both sighted users and screen reader users.\n</p>',
|
730
|
+
'frame-focusable-content': '<p>\n When a frame has a negative tabindex, the browser is prevented from\n redirecting the focus to the content inside that frame. This causes all its\n content from getting skipped in keyboard navigation, and if the frame is\n scrollable also prevents the focus from reaching any element from which the\n frame can be scrolled with the keyboard.\n</p>',
|
731
|
+
'frame-tested': '<p>\n Without the axe-core script, it is not possible for the tool to perform\n violation checking on multiple levels of nested iframes.\n</p>',
|
732
|
+
'frame-title-unique': '<p>\n Screen reader users rely on a frame title to describe the contents of the\n <code>frame</code>. Navigating through frames and iframes can quickly become\n difficult and confusing for users of this technology if the frames are not\n marked with a <code>title</code> attribute.\n</p>',
|
733
|
+
'frame-title': '<p>\n Screen reader users rely on a frame title to describe the contents of the\n <code>frame</code>. Navigating through <code>frame</code> and\n <code>iframe</code> elements quickly becomes difficult and confusing for users\n of this technology if the markup does not contain a\n <code>title</code> attribute.\n</p>',
|
734
|
+
'html-xml-lang-mismatch': '<p>\n When configuring a screen reader, users select a default language. If the\n language of a webpage is not specified, the screen reader assumes the default\n language set by the user. Multiple languages are an issue for users who speak\n and access websites in multiple languages. It is essential to specify a\n default language and ensure that it is valid for screen readers to function\n correctly.\n</p>',
|
735
|
+
'image-alt': '<p>\n Screen readers have no way of translating an image into words that gets read\n to the user, even if the image only consists of text. As a result, it's\n necessary for images to have short, descriptive <code>alt</code> text so\n screen reader users clearly understand the image's contents and purpose.\n</p>',
|
736
|
+
'image-redundant-alt': '<p>\n It is unnecessary and potentially confusing to have alternative text for a\n link or image to be repeated in text adjacent to the link or image since it\n would be read twice by a screen reader.\n</p>',
|
737
|
+
'input-button-name': '<p>\n Screen reader users are not able to discern the purpose of an\n <code>input type="button"</code> without an accessible name.\n</p>',
|
738
|
+
'input-image-alt': '<p>\n An <code><input type="image"></code> button must have\n alternate text, otherwise screen reader users will not know the button's\n purpose. Even if the image contains only text, it still requires alternate\n text, since a screen reader cannot translate images of words into output.\n</p>',
|
739
|
+
'landmark-banner-is-top-level': '<p>\n If the banner landmark is not the top-level landmark (and is contained within\n another landmark), it does not effectively designate the pre-defined header\n portion of the layout in the design and therefore prevents screen reader users\n from being able to easily find their way around the layout.\n</p>',
|
740
|
+
'landmark-complementary-is-top-level': '<p>\n Complementary content is ancillary content to the main theme of a document or\n page. Screen reader users have the option to skip over complementary content\n when it appears at the top level of the accessibility API. Embedding an\n <code><aside></code> element in another landmark may disable screen\n reader functionality allowing users to navigate through complementary content.\n</p>',
|
741
|
+
'landmark-contentinfo-is-top-level': '<p>\n The purpose of the <code>contentinfo</code> landmark can be defeated when\n placed within another landmark, as it can prevent blind screen reader users\n from being able to quickly find and navigate to the appropriate landmark.\n</p>',
|
742
|
+
'landmark-main-is-top-level': '<p>\n Navigating a web page is far simpler for screen reader users if the content\n splits between some high-level sections. Content outside of these sections is\n difficult to find, and its purpose may be unclear.\n</p>',
|
743
|
+
'landmark-no-duplicate-banner': '<p>\n Landmarks allow blind users to navigate and find content quickly. Missing\n landmarks require screen reader users to sort through too much extra\n information to find anything.\n</p>',
|
744
|
+
'landmark-no-duplicate-contentinfo': '<p>\n One of the main purposes of landmarks is to allow blind users to quickly find\n and navigate to the appropriate landmark, so you should keep the total number\n of landmarks relatively low. If you don't, screen reader users will have to\n sort through too much extra information to find what they're looking for.\n</p>',
|
745
|
+
'landmark-no-duplicate-main': '<p>\n Navigating a web page is far simpler for screen reader users if all of the\n content splits between one or more high-level sections. Content outside of\n these sections is difficult to find, and its purpose may be unclear.\n</p>',
|
746
|
+
'landmark-unique': '<p>\n <code>landmark-unique</code> is a new best practice rule ensures that\n landmarks have a unique role or accessible name (i.e. role, label, title)\n combination.\n</p>',
|
747
|
+
'list': '<p>\n Screen readers have a specific way of announcing lists. This feature makes\n lists clearer to understand, but will only work if lists are properly\n structured.\n</p>',
|
748
|
+
'listitem': '<p>\n For a list to be valid, it must have both parent and child elements. Parent\n elements can either be a set of <code>ul</code> tags or a set of\n <code>ol</code> tags. Child elements must be declared inside of these tags\n using the <code>li</code> tag.\n</p>',
|
749
|
+
'marquee': '<p>\n The <code>marquee</code> element creates scrolling text that is difficult to\n read and click on. Beyond that, it can be distracting to viewers, especially\n to those with low vision, cognitive disabilities, or attention deficits.\n</p>',
|
750
|
+
'meta-refresh': '<p>\n Since users do not expect a page to refresh automatically, such refreshing can\n be disorienting. Refreshing also moves the programmatic focus back to the top\n of the page, away from where the user had it. Such resetting is frustrating\n for users.\n</p>',
|
751
|
+
'object-alt': '<p>\n Screen readers have no way of translating non-text content into text announced\n to users. Instead, they read out alternative text. For screen reader users to\n obtain the information contained in embedded <code>object</code> elements\n which must contain short, descriptive alternative text.\n</p>',
|
752
|
+
'presentation-role-conflict': '<p>\n There are certain cases where the semantic role of an element with\n <code>role="none"</code> or <code>role="presentation"</code> does not resolve\n to none or presentation (respectively). When this happens, the element is not\n removed from the accessibility tree (as expected) and screen readers are able\n to interact with it.\n</p>',
|
753
|
+
'role-img-alt': '<p>\n Screen readers have no way of translating an image into words that gets read\n to the user, even if the image only consists of text. As a result, it's\n necessary for images to have short, descriptive and accessible alternative\n text so screen reader users clearly understand the image's contents and\n purpose.\n</p>',
|
754
|
+
'scope-attr-valid': '<p>\n The <code>scope</code> attribute makes table navigation much easier for screen\n reader users, provided that it is used correctly. Incorrectly used,\n <code>scope</code> can make table navigation much harder and less efficient.\n</p>',
|
755
|
+
'scrollable-region-focusable': '<p>\n Checks scrollable content for focusable elements enabling keyboard navigation.\n Keyboard navigation should not fail when focus moves to an element within a\n scrollable region.\n</p>',
|
756
|
+
'select-name': '<p>\n Effective form labels are required to make forms accessible. The purpose of\n form elements such as checkboxes, radio buttons, input fields, etcetera, is\n often apparent to sighted users, even if the form element is not\n programmatically labeled. Screen readers users require useful form labels to\n identify form fields. Adding a label to all form elements eliminates ambiguity\n and contributes to a more accessible product.\n</p>',
|
757
|
+
'server-side-image-map': '<p>\n Server-side image maps are not keyboard accessible; mouse clicks are required\n to access the links contained in the image, making the image inaccessible to\n people who only use keyboards for their navigation.\n</p>',
|
758
|
+
'skip-link': '<p>\n Screen readers announce content sequentially as it appears in the HTML file.\n What this means for users of assistive technology is that the content at the\n top of the page, typically including the entire navigation, is read out to the\n user before reaching any of the main content. Since content at the top of the\n page can often be very lengthy, it can be time-consuming to listen to or tab\n through all of it when the user is only interested in the main content.\n Including a skip link in an HTML page is beneficial to blind users, users with\n low vision, and mouse-only users.\n</p>',
|
759
|
+
'svg-img-alt': "<p>\n If you can't see, images are completely useless without a digital text alternative. The same is true in varying degrees for people with low vision or colour-blindness. \n</p>",
|
760
|
+
'tabindex': '<p>\n Using <code>tabindex</code> with a value greater than 0 can create as many\n problems as it solves. It creates an unexpected tab order, which makes the\n page less intuitive and can give the appearance of skipping certain elements\n entirely.\n</p>',
|
761
|
+
'table-duplicate-name': '<p>\n When tables have summary and caption text that is identical, screen reader\n users can be confused and find it difficult to know the name and purpose of\n the table.\n</p>',
|
762
|
+
'td-headers-attr': "<p>\n Sighted users can usually tell at a glance what the table's headers are and\n what their relationship to the data is. For non-sighted users this must be\n done in the markup.\n</p>",
|
763
|
+
'th-has-data-cells': '<p>\n When tables are not marked up semantically and do not have the correct header\n structure, screen reader users cannot correctly perceive the relationships\n between the cells and their contents visually.\n</p>',
|
764
|
+
'valid-lang': '<p>\n When configuring a screen reader, users select a default language. If the\n language of a webpage is not specified, the screen reader assumes it is the\n default language set by the user. Language selection becomes an issue for\n users who speak multiple languages and access the website in more than one\n language. It is essential to specify a language and ensure that it is valid so\n website text is pronounced correctly.\n</p>',
|
765
|
+
'video-caption': '<p>\n If a video has no caption, deaf users have limited or no access to the\n information contained in it. Even if a captions track is available, ensure\n that it contains all meaningful information in the video, not just dialogue.\n</p>',
|
766
|
+
'no-autoplay-audio': '<p>\n People who are blind or have low vision and use screen reading software can\n find it hard to hear the screen reader's speech output if there is other audio\n playing at the same time. If automatically playing audio lasts more than three\n seconds, an easily located, accessible mechanism must be provided to pause or\n stop the audio or control the audio volume. An audio control allows screen\n reader users to hear the screen reader without other sounds playing.\n</p>',
|
767
|
+
'aria-hidden-body': '<p>\n Screen readers do not read content marked with the <code>aria-hidden="true"</code> attribute value. Users can still tab to focusable elements in the hidden objects, but screen readers remain silent.\n</p>',
|
768
|
+
'aria-required-attr': '<p>\n ARIA widget roles require additional attributes that describe the state of the\n widget. The state of the widget is not communicated to screen reader users if\n a required attribute is omitted.\n</p>',
|
769
|
+
'bypass': '<p>\n Since web sites often display secondary, repeated content on multiple pages\n (such as navigation links, heading graphics, and advertising frames),\n keyboard-only users benefit from faster, more direct access to the primary\n content on a page. This reduces keystrokes and minimizes associated physical\n pain.\n</p>',
|
770
|
+
'color-contrast': '<p>\n Some people with low vision experience low contrast, meaning that there aren't\n very many bright or dark areas. Everything tends to appear about the same\n brightness, which makes it hard to distinguish outlines, borders, edges, and\n details. Text that is too close in luminance (brightness) to the background\n can be hard to read.\n</p>',
|
771
|
+
'document-title': '<p>\n Screen reader users use page titles to get an overview of the contents of the\n page. Navigating through pages can quickly become difficult and confusing for\n screen reader users if the pages are not marked with a title. The page\n <code>title</code> element is the first thing screen reader users hear when\n first loading a web page.\n</p>',
|
772
|
+
'duplicate-id-aria': '<p>\n Duplicate IDs are common validation errors that may break the accessibility of\n labels, e.g., ARIA elements, form fields, table header cells.\n</p>',
|
773
|
+
'duplicate-id': "<p>\n The ID attribute uniquely identifies elements on a page. It does not make\n sense to duplicate an ID.\n</p>\n\n<p>\n Duplicate ID's can break the accessibility of labels for forms, table header\n cells, etc., by the second instance being skipped by screen readers, or by\n client-side scripts. They are common markup validation errors that can\n eliminate possible sources of accessibility problems, when not breaking\n accessibility.\n</p>",
|
774
|
+
'empty-heading': '<p>\n Screen readers alert users to the presence of a heading tag. If the heading is\n empty or the text cannot be accessed, this could either confuse users or even\n prevent them from accessing information on the page's structure.\n</p>',
|
775
|
+
'form-field-multiple-labels': '<p>\n Assigning multiple labels to the same form field can cause problems for some\n combinations of screen readers and browsers, and the results are inconsistent\n from one combination to the next. Some combinations will read the first label.\n Some will read the last label. Others will read both labels.\n</p>',
|
776
|
+
'heading-order': '<p>\n The underlying purpose of headers is to convey the structure of the page. For\n sighted users, the same purpose is achieved using different sizes of text.\n Text size, however, is not helpful for users of screen readers, because a\n screen reader identifies a header only if it is properly marked-up. When\n heading elements are applied correctly, the page becomes much easier to\n navigate for screen reader users and sighted users alike.\n</p>',
|
777
|
+
'html-has-lang': '<p>\n When configuring a screen reader, users select a default language. If the\n language of a webpage is not specified, the screen reader assumes the default\n language set by the user. Language settings become an issue for users who\n speak multiple languages and access website in more than one language. It is\n essential to specify a language and ensure that it is valid so website text is\n pronounced correctly.\n</p>',
|
778
|
+
'html-lang-valid': '<p>\n When configuring a screen reader, users select a default language. If the\n language of a webpage is not specified, the screen reader assumes the default\n language set by the user. Language settings are an issue for users who speak\n multiple languages and access website in more than one language. It is\n essential to specify a language and ensure that it is valid so website text is\n pronounced correctly.\n</p>',
|
779
|
+
'label-title-only': '<p>\n The <code>title</code> and <code>aria-describedby</code> attributes are used\n to provide additional information such as a hint. Hints are exposed to\n accessibility APIs differently than labels and as such, this can cause\n problems with assistive technologies.\n</p>',
|
780
|
+
'link-in-text-block': '<p>\n Some people with low vision experience low contrast, meaning that there aren't\n very many bright or dark areas. Everything tends to appear about the same\n brightness, which makes it hard to distinguish outlines, borders, edges, and\n details. Text that is too close in luminance (brightness) to the background\n can be hard to read.\n</p>',
|
781
|
+
'link-name': '<p>\n Inaccessible link elements pose barriers to accessibility, as they are a fundamental component of a website.\n </p>',
|
782
|
+
'meta-viewport-large': '<p>\n The <code>user-scalable="no"</code> parameter inside the\n <code>content</code> attribute of\n <code><meta name="viewport"></code> element disables zooming on a page.\n The <code>maximum-scale</code> parameter limits the amount the user can zoom.\n This is problematic for people with low vision who rely on screen magnifiers\n to properly see the contents of a web page.\n</p>',
|
783
|
+
'meta-viewport': '<p>\n The <code>user-scalable="no"</code> parameter inside the\n <code>content</code> attribute of\n <code><meta name="viewport"></code> element disables zooming on a page.\n The <code>maximum-scale</code> parameter limits the amount the user can zoom.\n This is problematic for people with low vision who rely on screen magnifiers\n to properly see the contents of a web page.\n</p>',
|
784
|
+
'nested-interactive': '<p>\n Focusable elements with an interactive control ancestor (any element that\n accepts user input such as button or anchor elements) are not announced by\n screen readers and create an empty tab stop. That is, you could tab to the\n element but the screen reader will not announce its name, role, or state.\n</p>',
|
785
|
+
'page-has-heading-one': '<p>\n Screen reader users can use keyboard shortcuts to navigate directly to the\n first <code>h1</code>, which, in principle, should allow them to jump directly\n to the main content of the web page. If there is no <code>h1</code>, or if the\n <code>h1</code> appears somewhere other than at the start of the main content,\n screen reader users must listen to more of the web page to understand its\n structure, wasting valuable time.\n</p>',
|
786
|
+
'region': '<p>\n Navigating a web page is far simpler for screen reader users if the content\n splits between multiple high-level sections. Content outside of sections is\n difficult to find, and the content's purpose may be unclear.\n</p>',
|
787
|
+
'button-name': '<p>\n Screen reader users are not able to discern the purpose of elements with\n <code>role="link"</code>, <code>role="button"</code>, or\n <code>role="menuitem"</code> that do not have an accessible name.\n</p>',
|
788
|
+
'aria-allowed-role': '<p>\n Intended accessible technology behavior by a developer is not enabled when an\n assigned WAI-ARIA role value is invalid for the parent element.\n</p>',
|
789
|
+
'aria-roles': '<p>\n Elements assigned invalid ARIA role values are not interpreted by assistive\n technology as intended by the developer.\n</p>',
|
790
|
+
'label': '<p>\n Effective form labels are required to make forms accessible. The purpose of\n form elements such as checkboxes, radio buttons, input fields, etcetera, is\n often apparent to sighted users, even if the form element is not\n programmatically labeled. Screen readers users require useful form labels to\n identify form fields. Adding a label to all form elements eliminates ambiguity\n and contributes to a more accessible product.\n</p>',
|
791
|
+
'landmark-one-main': '<p>\n Navigating a web page is far simpler for screen reader users if all of the\n content splits between one or more high-level sections. Content outside of\n these sections is difficult to find, and its purpose may be unclear.\n</p>',
|
792
|
+
'aria-braille-equivalent': '<p class="rule-desc-text">\nARIA braille attributes were introduced to allow adjusting how labels and role descriptions are rendered on a braille display. They cannot be the only attribute providing a label, or a role description. When used without a corresponding label or role description ARIA says to ignore these attributes, although this may not happen consistently in screen readers and other assistive technologies.\n</p>',
|
793
|
+
'aria-conditional-attr': '<p class="rule-desc-text">\nUsing ARIA attributes on elements where they are not expected can result in unpredictable behavior for assistive technologies. This can lead to a poor user experience for people with disabilities who rely on these technologies. It is important to follow the ARIA specification to ensure that assistive technologies can properly interpret and communicate the intended meaning of the content.\n</p>',
|
794
|
+
'aria-deprecated-role': '<p class="rule-desc-text">\nUsing deprecated WAI-ARIA roles is bad for accessibility. They will not be recognized or correctly processed by screen readers and other assistive technologies. Using these means not everyone will be able to access essential information.\n</p>',
|
795
|
+
'aria-prohibited-attr': '<p class="rule-desc-text">\nUsing ARIA attributes in roles where they are prohibited can mean that important information is not communicated to users of assistive technologies. assistive technologies may also attempt to compensate for the issue, resulting in inconsistent and confusing behavior of these tools.\n</p>',
|
796
|
+
'target-size' : '<p>\n Touch targets must have sufficient size and spacing in order to "be easily activated without accidentally activating an adjacent target." When touch targets are too small or too close together, it becomes difficult for users to activate them.\n</p>',
|
797
|
+
'oobee-confusing-alt-text' : '<p>\n Alt text should be clear and concise. It should relay informative content / function of the image. If the alt text is too long or confusing, it can be difficult for screen reader users to understand the image.\n</p>',
|
798
|
+
'oobee-accessible-label' : '<p>\n Unlabelled clickable elements are inaccessible because it prevents screen readers from effectively conveying the purpose of the element. This causes challenges for screen reader users to navigate and understand the webpage. Clickable elements should also be made focusable by screen readers unless deliberately excluded (e.g. decorative or functionally redundant).\n</p>',
|
799
|
+
'oobee-grading-text-contents' : '<p>\n Text content should be easy to understand for individuals with education levels up to university graduates. If the text content is difficult to understand, provide supplemental content or a version that is easy to understand.\n</p>',
|
800
|
+
};
|
801
|
+
</script>
|