-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy patharia-hidden-valid-usage.js
More file actions
105 lines (92 loc) · 2.89 KB
/
Copy patharia-hidden-valid-usage.js
File metadata and controls
105 lines (92 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* Check for valid use of aria-hidden="true"
*
* @param {Node} node The node to evaluate.
* @return {boolean} True if the aria-hidden usage is valid, false otherwise.
*/
// Common screen reader text classes
const srClasses = [
'screen-reader-text', 'sr-only', 'show-for-sr', 'visuallyhidden',
'visually-hidden', 'hidden-visually', 'invisible',
'accessibly-hidden', 'hide', 'hidden',
];
export default {
id: 'aria_hidden_valid_usage',
evaluate: ( node, options = {} ) => {
// Check if element is hidden with CSS
const computedStyle = window.getComputedStyle( node );
if ( computedStyle.display === 'none' || computedStyle.visibility === 'hidden' ) {
return true;
}
// Check against configured exclusion selectors.
const exclusions = options?.exclusions || [];
for ( const selector of exclusions ) {
if ( node.matches( selector ) ) {
return true;
}
}
const role = node.getAttribute( 'role' );
if ( role?.split( /\s+/ ).includes( 'presentation' ) ) {
return true;
}
const parentNode = node.parentElement;
if ( ! parentNode ) {
return false;
}
// Check if parent is hidden with CSS
const parentStyle = window.getComputedStyle( parentNode );
if ( parentStyle.display === 'none' || parentStyle.visibility === 'hidden' ) {
return true;
}
// Check if parent is button/anchor with accessible content
if ( [ 'button', 'a' ].includes( parentNode.tagName.toLowerCase() ) ) {
// Parent has non-empty aria-label
if (
(
parentNode.hasAttribute( 'aria-label' ) &&
parentNode.getAttribute( 'aria-label' ).trim()
) ||
(
parentNode.hasAttribute( 'aria-labelledby' ) &&
document.getElementById( parentNode.getAttribute( 'aria-labelledby' ) )
)
) {
return true;
}
// Check for visible text (excluding the aria-hidden element)
for ( const child of parentNode.childNodes ) {
if ( child === node ) {
continue;
}
// Direct text node
if ( child.nodeType === Node.TEXT_NODE &&
child.textContent.trim() ) {
return true;
}
// Text within an element node
if ( child.nodeType === Node.ELEMENT_NODE &&
! child.hasAttribute( 'aria-hidden' ) ) {
const style = window.getComputedStyle( child );
if ( style.display !== 'none' &&
style.visibility !== 'hidden' &&
child.textContent.trim() ) {
return true;
}
}
}
}
// Check siblings for screen reader text classes
const siblings = Array.from( parentNode.children );
for ( const sibling of siblings ) {
if ( sibling !== node ) {
for ( const srClass of srClasses ) {
if ( sibling.classList.contains( srClass ) ||
sibling.className.toLowerCase().includes( srClass ) ) {
return true;
}
}
}
}
return false;
},
};