@double-great/stylelint-a11y 2.0.0
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/.babelrc +3 -0
- package/.eslintignore +2 -0
- package/.eslintrc.js +27 -0
- package/.github/dependabot.yml +10 -0
- package/.github/workflows/test.yml +21 -0
- package/.husky/pre-commit +4 -0
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/index.js +18 -0
- package/dist/rules/content-property-no-static-value/__tests__/index.js +37 -0
- package/dist/rules/content-property-no-static-value/index.js +90 -0
- package/dist/rules/font-size-is-readable/__tests__/index.js +35 -0
- package/dist/rules/font-size-is-readable/index.js +77 -0
- package/dist/rules/index.js +48 -0
- package/dist/rules/line-height-is-vertical-rhythmed/__tests__/index.js +59 -0
- package/dist/rules/line-height-is-vertical-rhythmed/index.js +84 -0
- package/dist/rules/media-prefers-color-scheme/__tests__/index.js +47 -0
- package/dist/rules/media-prefers-color-scheme/index.js +121 -0
- package/dist/rules/media-prefers-reduced-motion/__tests__/index.js +41 -0
- package/dist/rules/media-prefers-reduced-motion/index.js +175 -0
- package/dist/rules/no-display-none/__tests__/index.js +17 -0
- package/dist/rules/no-display-none/index.js +76 -0
- package/dist/rules/no-obsolete-attribute/__tests__/index.js +27 -0
- package/dist/rules/no-obsolete-attribute/index.js +78 -0
- package/dist/rules/no-obsolete-attribute/obsoleteAttributes.js +8 -0
- package/dist/rules/no-obsolete-element/__tests__/index.js +27 -0
- package/dist/rules/no-obsolete-element/index.js +78 -0
- package/dist/rules/no-obsolete-element/obsoleteElements.js +8 -0
- package/dist/rules/no-outline-none/__tests__/index.js +40 -0
- package/dist/rules/no-outline-none/index.js +88 -0
- package/dist/rules/no-spread-text/__tests__/index.js +32 -0
- package/dist/rules/no-spread-text/index.js +75 -0
- package/dist/rules/no-text-align-justify/__tests__/index.js +32 -0
- package/dist/rules/no-text-align-justify/index.js +76 -0
- package/dist/rules/selector-pseudo-class-focus/__tests__/index.js +46 -0
- package/dist/rules/selector-pseudo-class-focus/index.js +121 -0
- package/jest.config.js +20 -0
- package/jest.setup.js +5 -0
- package/package.json +63 -0
- package/recommended.js +8 -0
- package/src/index.js +8 -0
- package/src/rules/content-property-no-static-value/README.md +34 -0
- package/src/rules/content-property-no-static-value/__tests__/index.js +48 -0
- package/src/rules/content-property-no-static-value/index.js +72 -0
- package/src/rules/font-size-is-readable/README.md +34 -0
- package/src/rules/font-size-is-readable/__tests__/index.js +45 -0
- package/src/rules/font-size-is-readable/index.js +57 -0
- package/src/rules/index.js +27 -0
- package/src/rules/line-height-is-vertical-rhythmed/README.md +51 -0
- package/src/rules/line-height-is-vertical-rhythmed/__tests__/index.js +75 -0
- package/src/rules/line-height-is-vertical-rhythmed/index.js +61 -0
- package/src/rules/media-prefers-color-scheme/README.md +72 -0
- package/src/rules/media-prefers-color-scheme/__tests__/index.js +60 -0
- package/src/rules/media-prefers-color-scheme/index.js +115 -0
- package/src/rules/media-prefers-reduced-motion/README.md +60 -0
- package/src/rules/media-prefers-reduced-motion/__tests__/index.js +55 -0
- package/src/rules/media-prefers-reduced-motion/index.js +164 -0
- package/src/rules/no-display-none/README.md +27 -0
- package/src/rules/no-display-none/__tests__/index.js +21 -0
- package/src/rules/no-display-none/index.js +58 -0
- package/src/rules/no-obsolete-attribute/README.md +35 -0
- package/src/rules/no-obsolete-attribute/__tests__/index.js +33 -0
- package/src/rules/no-obsolete-attribute/index.js +58 -0
- package/src/rules/no-obsolete-attribute/obsoleteAttributes.js +204 -0
- package/src/rules/no-obsolete-element/README.md +20 -0
- package/src/rules/no-obsolete-element/__tests__/index.js +33 -0
- package/src/rules/no-obsolete-element/index.js +57 -0
- package/src/rules/no-obsolete-element/obsoleteElements.js +32 -0
- package/src/rules/no-outline-none/README.md +84 -0
- package/src/rules/no-outline-none/__tests__/index.js +51 -0
- package/src/rules/no-outline-none/index.js +75 -0
- package/src/rules/no-spread-text/README.md +50 -0
- package/src/rules/no-spread-text/__tests__/index.js +42 -0
- package/src/rules/no-spread-text/index.js +72 -0
- package/src/rules/no-text-align-justify/README.md +52 -0
- package/src/rules/no-text-align-justify/__tests__/index.js +42 -0
- package/src/rules/no-text-align-justify/index.js +60 -0
- package/src/rules/selector-pseudo-class-focus/README.md +43 -0
- package/src/rules/selector-pseudo-class-focus/__tests__/index.js +51 -0
- package/src/rules/selector-pseudo-class-focus/index.js +85 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { utils } from 'stylelint';
|
|
2
|
+
import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
|
|
3
|
+
import isStandardSyntaxSelector from 'stylelint/lib/utils/isStandardSyntaxSelector';
|
|
4
|
+
import isStandardSyntaxAtRule from 'stylelint/lib/utils/isStandardSyntaxAtRule';
|
|
5
|
+
import isCustomSelector from 'stylelint/lib/utils/isCustomSelector';
|
|
6
|
+
|
|
7
|
+
export const ruleName = 'a11y/media-prefers-color-scheme';
|
|
8
|
+
|
|
9
|
+
export const messages = utils.ruleMessages(ruleName, {
|
|
10
|
+
expected: (selector) => `Expected ${selector} is used with @media (prefers-color-scheme)`,
|
|
11
|
+
});
|
|
12
|
+
const targetProperties = ['background-color', 'color'];
|
|
13
|
+
|
|
14
|
+
function check(selector, node) {
|
|
15
|
+
const declarations = node.nodes;
|
|
16
|
+
const params = node.parent.params;
|
|
17
|
+
const parentNodes = node.parent.nodes;
|
|
18
|
+
|
|
19
|
+
if (!declarations) return true;
|
|
20
|
+
|
|
21
|
+
if (!isStandardSyntaxSelector(selector)) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (isCustomSelector(selector)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let currentSelector = null;
|
|
30
|
+
|
|
31
|
+
const declarationsIsMatched = declarations.some((declaration) => {
|
|
32
|
+
const noMatchedParams = !params || params.indexOf('prefers-color-scheme') === -1;
|
|
33
|
+
const index = targetProperties.indexOf(declaration.prop);
|
|
34
|
+
currentSelector = targetProperties[index];
|
|
35
|
+
|
|
36
|
+
return index >= 0 && noMatchedParams;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!declarationsIsMatched) return true;
|
|
40
|
+
|
|
41
|
+
if (declarationsIsMatched) {
|
|
42
|
+
const parentMatchedNode = parentNodes.some((parentNode) => {
|
|
43
|
+
if (!parentNode || !parentNode.nodes) return;
|
|
44
|
+
return parentNode.nodes.some((childrenNode) => {
|
|
45
|
+
const childrenNodes = childrenNode.nodes;
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
!parentNode.params ||
|
|
49
|
+
!Array.isArray(childrenNodes) ||
|
|
50
|
+
selector !== childrenNode.selector
|
|
51
|
+
)
|
|
52
|
+
return false;
|
|
53
|
+
|
|
54
|
+
const matchedChildrenNodes = childrenNodes.some((declaration) => {
|
|
55
|
+
const index = targetProperties.indexOf(declaration.prop);
|
|
56
|
+
if (currentSelector !== targetProperties[index]) return false;
|
|
57
|
+
|
|
58
|
+
return index >= 0 && parentNode.params.indexOf('prefers-color-scheme') >= 0;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return matchedChildrenNodes;
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!parentMatchedNode) return false;
|
|
66
|
+
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default function (actual) {
|
|
74
|
+
return (root, result) => {
|
|
75
|
+
const validOptions = utils.validateOptions(result, ruleName, { actual });
|
|
76
|
+
|
|
77
|
+
if (!validOptions || !actual) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
root.walk((node) => {
|
|
82
|
+
let selector = null;
|
|
83
|
+
|
|
84
|
+
if (node.type === 'rule') {
|
|
85
|
+
if (!isStandardSyntaxRule(node)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
selector = node.selector;
|
|
90
|
+
} else if (node.type === 'atrule' && node.name === 'page' && node.params) {
|
|
91
|
+
if (!isStandardSyntaxAtRule(node)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
selector = node.params;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!selector) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const isAccepted = check(selector, node);
|
|
103
|
+
|
|
104
|
+
if (!isAccepted) {
|
|
105
|
+
utils.report({
|
|
106
|
+
index: node.lastEach,
|
|
107
|
+
message: messages.expected(selector),
|
|
108
|
+
node,
|
|
109
|
+
ruleName,
|
|
110
|
+
result,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# media-prefers-reduced-motion
|
|
2
|
+
|
|
3
|
+
Require certain styles if the animation or transition in media features.
|
|
4
|
+
|
|
5
|
+
Safari 10.1 [introduced](https://webkit.org/blog/7551/responsive-design-for-motion/) the Reduced Motion Media Query. It is a non-vendor-prefixed declaration that allows developers to "create styles that avoid large areas of motion for users that specify a preference for reduced motion in System Preferences."
|
|
6
|
+
|
|
7
|
+
The `--fix` option on the command line can automatically fix all of the problems reported by this rule.
|
|
8
|
+
|
|
9
|
+
## Options
|
|
10
|
+
|
|
11
|
+
### true
|
|
12
|
+
|
|
13
|
+
The following pattern are considered violations:
|
|
14
|
+
|
|
15
|
+
```css
|
|
16
|
+
.foo {
|
|
17
|
+
animation: 1s ease-in;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```css
|
|
22
|
+
.bar {
|
|
23
|
+
animation-name: skew;
|
|
24
|
+
}
|
|
25
|
+
@media screen and (prefers-reduced-motion) {
|
|
26
|
+
.bar {
|
|
27
|
+
transition: none;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The following patterns are _not_ considered violations:
|
|
33
|
+
|
|
34
|
+
```css
|
|
35
|
+
div {
|
|
36
|
+
transition: none;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```css
|
|
41
|
+
.foo {
|
|
42
|
+
transition: none;
|
|
43
|
+
}
|
|
44
|
+
@media screen and (prefers-reduced-motion: reduce) {
|
|
45
|
+
.foo {
|
|
46
|
+
transition: none;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```css
|
|
52
|
+
.bar {
|
|
53
|
+
animation: none;
|
|
54
|
+
}
|
|
55
|
+
@media screen and (prefers-reduced-motion) {
|
|
56
|
+
.bar {
|
|
57
|
+
animation: none;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { messages, ruleName } from '../index';
|
|
2
|
+
|
|
3
|
+
testRule({
|
|
4
|
+
ruleName,
|
|
5
|
+
config: [true],
|
|
6
|
+
fix: true,
|
|
7
|
+
|
|
8
|
+
accept: [
|
|
9
|
+
{
|
|
10
|
+
code: 'a { }',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
code: 'div { transition: none; }',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
code: '.foo { transition: none } @media screen and (prefers-reduced-motion: reduce) { .foo { transition: none } }',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
code: '.bar { animation: none } @media screen and (prefers-reduced-motion) { .bar { animation: none } }',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
code: 'a { animation-name: skew; } @media screen and (prefers-reduced-motion) { a { animation: none } }',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
code: '.foo { transition: all; @media (prefers-reduced-motion: reduce) { transition: none; } }',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
|
|
29
|
+
reject: [
|
|
30
|
+
{
|
|
31
|
+
code: 'a { animation-name: skew; }',
|
|
32
|
+
fixed:
|
|
33
|
+
'@media screen and (prefers-reduced-motion: reduce) {\na { animation: none;\n}\n}\na { animation-name: skew; }',
|
|
34
|
+
message: messages.expected('a'),
|
|
35
|
+
line: 1,
|
|
36
|
+
column: 3,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
code: 'a { animation-name: skew; } @media screen and (prefers-reduced-motion) { a { transition: none; } }',
|
|
40
|
+
fixed:
|
|
41
|
+
'@media screen and (prefers-reduced-motion: reduce) {\na { animation: none;\n}\n} a { animation-name: skew; } @media screen and (prefers-reduced-motion) { a { transition: none; } }',
|
|
42
|
+
message: messages.expected('a'),
|
|
43
|
+
line: 1,
|
|
44
|
+
column: 3,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
code: '.foo { animation: 1s ease-in; } @media screen and (prefers-reduced-motion) { .foo { animation: 1s ease-in; } }',
|
|
48
|
+
fixed:
|
|
49
|
+
'@media screen and (prefers-reduced-motion: reduce) {\n.foo { animation: none;\n}\n} .foo { animation: 1s ease-in; } @media screen and (prefers-reduced-motion) { .foo { animation: 1s ease-in; } }',
|
|
50
|
+
message: messages.expected('.foo'),
|
|
51
|
+
line: 1,
|
|
52
|
+
column: 3,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { utils } from 'stylelint';
|
|
2
|
+
import { parse } from 'postcss';
|
|
3
|
+
import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
|
|
4
|
+
import isStandardSyntaxSelector from 'stylelint/lib/utils/isStandardSyntaxSelector';
|
|
5
|
+
import isStandardSyntaxAtRule from 'stylelint/lib/utils/isStandardSyntaxAtRule';
|
|
6
|
+
import isCustomSelector from 'stylelint/lib/utils/isCustomSelector';
|
|
7
|
+
|
|
8
|
+
export const ruleName = 'a11y/media-prefers-reduced-motion';
|
|
9
|
+
|
|
10
|
+
export const messages = utils.ruleMessages(ruleName, {
|
|
11
|
+
expected: (selector) => `Expected ${selector} is used with @media (prefers-reduced-motion)`,
|
|
12
|
+
});
|
|
13
|
+
const targetProperties = ['transition', 'animation', 'animation-name'];
|
|
14
|
+
|
|
15
|
+
function checkChildrenNodes(childrenNodes, currentSelector, parentNode) {
|
|
16
|
+
return childrenNodes.some((declaration) => {
|
|
17
|
+
const index = targetProperties.indexOf(declaration.prop);
|
|
18
|
+
if (currentSelector === 'animation-name' && targetProperties[index] === 'animation')
|
|
19
|
+
return true;
|
|
20
|
+
if (currentSelector !== targetProperties[index]) return false;
|
|
21
|
+
if (declaration.value !== 'none') return false;
|
|
22
|
+
|
|
23
|
+
return index >= 0 && parentNode.params.indexOf('prefers-reduced-motion') >= 0;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function check(selector, node) {
|
|
28
|
+
const declarations = node.nodes;
|
|
29
|
+
const params = node.parent.params;
|
|
30
|
+
const parentNodes = node.parent.nodes;
|
|
31
|
+
|
|
32
|
+
if (!declarations) return true;
|
|
33
|
+
|
|
34
|
+
if (!isStandardSyntaxSelector(selector)) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isCustomSelector(selector)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let currentSelector = null;
|
|
43
|
+
|
|
44
|
+
const declarationsIsMatched = declarations.some((declaration) => {
|
|
45
|
+
const noMatchedParams = !params || params.indexOf('prefers-reduced-motion') === -1;
|
|
46
|
+
const index = targetProperties.indexOf(declaration.prop);
|
|
47
|
+
currentSelector = targetProperties[index];
|
|
48
|
+
if (targetProperties.indexOf(declaration.prop) >= 0 && declaration.value === 'none') {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return index >= 0 && noMatchedParams;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (!declarationsIsMatched) return true;
|
|
56
|
+
|
|
57
|
+
if (declarationsIsMatched) {
|
|
58
|
+
const parentMatchedNode = parentNodes.some((parentNode) => {
|
|
59
|
+
if (!parentNode || !parentNode.nodes) return;
|
|
60
|
+
return parentNode.nodes.some((childrenNode) => {
|
|
61
|
+
const childrenNodes = childrenNode.nodes;
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
childrenNode.type === 'atrule' &&
|
|
65
|
+
childrenNode.params.indexOf('prefers-reduced-motion') >= 0
|
|
66
|
+
) {
|
|
67
|
+
return childrenNodes.some((declaration) => {
|
|
68
|
+
const index = targetProperties.indexOf(declaration.prop);
|
|
69
|
+
if (currentSelector === 'animation-name' && targetProperties[index] === 'animation')
|
|
70
|
+
return true;
|
|
71
|
+
if (currentSelector !== targetProperties[index]) return false;
|
|
72
|
+
if (declaration.value !== 'none') return false;
|
|
73
|
+
|
|
74
|
+
return index >= 0;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
!parentNode.params ||
|
|
80
|
+
!Array.isArray(childrenNodes) ||
|
|
81
|
+
selector !== childrenNode.selector
|
|
82
|
+
)
|
|
83
|
+
return false;
|
|
84
|
+
|
|
85
|
+
return checkChildrenNodes(childrenNodes, currentSelector, parentNode);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (!parentMatchedNode) return false;
|
|
90
|
+
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default function (actual, _, context) {
|
|
98
|
+
return (root, result) => {
|
|
99
|
+
const validOptions = utils.validateOptions(result, ruleName, { actual });
|
|
100
|
+
|
|
101
|
+
if (!validOptions || !actual) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
root.walk((node) => {
|
|
106
|
+
let selector = null;
|
|
107
|
+
|
|
108
|
+
if (node.type === 'rule') {
|
|
109
|
+
if (!isStandardSyntaxRule(node)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
selector = node.selector;
|
|
114
|
+
} else if (node.type === 'atrule' && node.name === 'page' && node.params) {
|
|
115
|
+
if (!isStandardSyntaxAtRule(node)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
selector = node.params;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!selector) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const isAccepted = check(selector, node);
|
|
127
|
+
|
|
128
|
+
if (context.fix && !isAccepted) {
|
|
129
|
+
const media = parse('@media screen and (prefers-reduced-motion: reduce) {}');
|
|
130
|
+
media.nodes.forEach((o) => {
|
|
131
|
+
o.raws.after = '\n';
|
|
132
|
+
});
|
|
133
|
+
const cloneRule = node.clone();
|
|
134
|
+
cloneRule.raws = {
|
|
135
|
+
...cloneRule.raws,
|
|
136
|
+
before: '\n',
|
|
137
|
+
after: '\n',
|
|
138
|
+
semicolon: true,
|
|
139
|
+
};
|
|
140
|
+
cloneRule.nodes.forEach((o) => {
|
|
141
|
+
if (o.prop === 'animation-name') {
|
|
142
|
+
o.prop = 'animation';
|
|
143
|
+
}
|
|
144
|
+
if (targetProperties.indexOf(o.prop) >= 0) {
|
|
145
|
+
o.value = 'none';
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
media.first.append(cloneRule);
|
|
149
|
+
node.before(media);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!isAccepted) {
|
|
154
|
+
utils.report({
|
|
155
|
+
index: node.lastEach,
|
|
156
|
+
message: messages.expected(selector),
|
|
157
|
+
node,
|
|
158
|
+
ruleName,
|
|
159
|
+
result,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# no-display-none
|
|
2
|
+
|
|
3
|
+
Sources that will help you do without `{ display: none; }` and hide the content:
|
|
4
|
+
|
|
5
|
+
- [CSS Tricks](https://css-tricks.com/places-its-tempting-to-use-display-none-but-dont/)
|
|
6
|
+
- [A11Y Project](https://a11yproject.com/posts/how-to-hide-content/)
|
|
7
|
+
- [WebAIM](https://webaim.org/techniques/css/invisiblecontent/)
|
|
8
|
+
|
|
9
|
+
## Options
|
|
10
|
+
|
|
11
|
+
### true
|
|
12
|
+
|
|
13
|
+
The following pattern are considered violations:
|
|
14
|
+
|
|
15
|
+
```css
|
|
16
|
+
.foo {
|
|
17
|
+
display: none;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The following patterns are _not_ considered violations:
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
.foo {
|
|
25
|
+
display: flex;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { messages, ruleName } from '../index';
|
|
2
|
+
|
|
3
|
+
testRule({
|
|
4
|
+
ruleName,
|
|
5
|
+
config: [true],
|
|
6
|
+
|
|
7
|
+
accept: [
|
|
8
|
+
{
|
|
9
|
+
code: '.foo { display: flex; }',
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
|
|
13
|
+
reject: [
|
|
14
|
+
{
|
|
15
|
+
code: '.foo { display: none; }',
|
|
16
|
+
message: messages.expected('.foo'),
|
|
17
|
+
line: 1,
|
|
18
|
+
column: 3,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { utils } from 'stylelint';
|
|
2
|
+
import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
|
|
3
|
+
|
|
4
|
+
export const ruleName = 'a11y/no-display-none';
|
|
5
|
+
|
|
6
|
+
export const messages = utils.ruleMessages(ruleName, {
|
|
7
|
+
expected: (selector) => `Unexpected using "{ display: none; }" in ${selector}`,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
function check(selector, node) {
|
|
11
|
+
if (node.type !== 'rule') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return !node.nodes.some(
|
|
16
|
+
(o) =>
|
|
17
|
+
o.type === 'decl' && o.prop.toLowerCase() === 'display' && o.value.toLowerCase() === 'none'
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function (actual) {
|
|
22
|
+
return (root, result) => {
|
|
23
|
+
const validOptions = utils.validateOptions(result, ruleName, { actual });
|
|
24
|
+
|
|
25
|
+
if (!validOptions || !actual) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
root.walk((node) => {
|
|
30
|
+
let selector = null;
|
|
31
|
+
|
|
32
|
+
if (node.type === 'rule') {
|
|
33
|
+
if (!isStandardSyntaxRule(node)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
selector = node.selector;
|
|
37
|
+
} else if (node.type === 'atrule' && node.name.toLowerCase() === 'page' && node.params) {
|
|
38
|
+
selector = node.params;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!selector) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isAccepted = check(selector, node);
|
|
46
|
+
|
|
47
|
+
if (!isAccepted) {
|
|
48
|
+
utils.report({
|
|
49
|
+
index: node.lastEach,
|
|
50
|
+
message: messages.expected(selector),
|
|
51
|
+
node,
|
|
52
|
+
ruleName,
|
|
53
|
+
result,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# no-obsolete-attribute
|
|
2
|
+
|
|
3
|
+
Disallow obsolete attribute using.
|
|
4
|
+
|
|
5
|
+
**Sources:**
|
|
6
|
+
|
|
7
|
+
- [W3G Obsolete features](https://www.w3.org/TR/html5/obsolete.html#obsolete)
|
|
8
|
+
- [W3C Non-conforming features](https://w3c.github.io/html/obsolete.html#non-conforming-features)
|
|
9
|
+
- [W3G Features removed](https://www.w3.org/TR/html52/changes.html#features-removed)
|
|
10
|
+
|
|
11
|
+
## Options
|
|
12
|
+
|
|
13
|
+
### true
|
|
14
|
+
|
|
15
|
+
The following pattern are considered violations:
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
body[link] {
|
|
19
|
+
background-color: pink;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
a,
|
|
25
|
+
img[datasrc] {
|
|
26
|
+
color: pink;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```css
|
|
31
|
+
img[align],
|
|
32
|
+
a[name] {
|
|
33
|
+
color: pink;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ruleName, messages } from '../index';
|
|
2
|
+
|
|
3
|
+
testRule({
|
|
4
|
+
ruleName,
|
|
5
|
+
config: [true],
|
|
6
|
+
|
|
7
|
+
accept: [
|
|
8
|
+
{
|
|
9
|
+
code: 'a { color: pink; }',
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
|
|
13
|
+
reject: [
|
|
14
|
+
{
|
|
15
|
+
code: 'body[link] { color: pink; }',
|
|
16
|
+
message: messages.expected('body[link]'),
|
|
17
|
+
line: 1,
|
|
18
|
+
column: 3,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: 'a, img[datasrc] { color: pink; }',
|
|
22
|
+
message: messages.expected('a, img[datasrc]'),
|
|
23
|
+
line: 1,
|
|
24
|
+
column: 3,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
code: 'img[align], a[name] { color: pink; }',
|
|
28
|
+
message: messages.expected('img[align], a[name]'),
|
|
29
|
+
line: 1,
|
|
30
|
+
column: 3,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { utils } from 'stylelint';
|
|
2
|
+
import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
|
|
3
|
+
import { obsoleteAttributes } from './obsoleteAttributes';
|
|
4
|
+
|
|
5
|
+
export const ruleName = 'a11y/no-obsolete-attribute';
|
|
6
|
+
|
|
7
|
+
export const messages = utils.ruleMessages(ruleName, {
|
|
8
|
+
expected: (selector) => `Unexpected using obsolete attribute "${selector}"`,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function check(selector, node) {
|
|
12
|
+
if (node.type !== 'rule') {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return !node.selectors.some((sel) => {
|
|
17
|
+
return obsoleteAttributes.has(sel);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function (actual) {
|
|
22
|
+
return (root, result) => {
|
|
23
|
+
const validOptions = utils.validateOptions(result, ruleName, { actual });
|
|
24
|
+
|
|
25
|
+
if (!validOptions || !actual) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
root.walk((node) => {
|
|
30
|
+
let selector = null;
|
|
31
|
+
|
|
32
|
+
if (node.type === 'rule') {
|
|
33
|
+
if (!isStandardSyntaxRule(node)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
selector = node.selector;
|
|
37
|
+
} else if (node.type === 'atrule' && node.name.toLowerCase() === 'page' && node.params) {
|
|
38
|
+
selector = node.params;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!selector) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isAccepted = check(selector, node);
|
|
46
|
+
|
|
47
|
+
if (!isAccepted) {
|
|
48
|
+
utils.report({
|
|
49
|
+
index: node.lastEach,
|
|
50
|
+
message: messages.expected(selector),
|
|
51
|
+
node,
|
|
52
|
+
ruleName,
|
|
53
|
+
result,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
}
|