@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,204 @@
|
|
|
1
|
+
export const obsoleteAttributes = new Set([
|
|
2
|
+
'[dropzone]',
|
|
3
|
+
'a[charset]',
|
|
4
|
+
'link[charset]',
|
|
5
|
+
'a[coords]',
|
|
6
|
+
'a[shape]',
|
|
7
|
+
'a[methods]',
|
|
8
|
+
'link[methods]',
|
|
9
|
+
'a[name]',
|
|
10
|
+
'option[name]',
|
|
11
|
+
'embed[name]',
|
|
12
|
+
'img[name]',
|
|
13
|
+
'a[rev]',
|
|
14
|
+
'link[rev]',
|
|
15
|
+
'a[urn]',
|
|
16
|
+
'link[urn]',
|
|
17
|
+
'form[accept]',
|
|
18
|
+
'head[profile]',
|
|
19
|
+
'html[version]',
|
|
20
|
+
'link[target]',
|
|
21
|
+
'param[type]',
|
|
22
|
+
'param[valuetype]',
|
|
23
|
+
'script[language]',
|
|
24
|
+
'script[event]',
|
|
25
|
+
'script[for]',
|
|
26
|
+
'table[datapagesize]',
|
|
27
|
+
'table[summary]',
|
|
28
|
+
'td[axis]',
|
|
29
|
+
'th[axis]',
|
|
30
|
+
'td[scope]',
|
|
31
|
+
'td[abbr]',
|
|
32
|
+
'a[datasrc]',
|
|
33
|
+
'applet[datasrc]',
|
|
34
|
+
'button[datasrc]',
|
|
35
|
+
'div[datasrc]',
|
|
36
|
+
'frame[datasrc]',
|
|
37
|
+
'label[datasrc]',
|
|
38
|
+
'legend[datasrc]',
|
|
39
|
+
'marquee[datasrc]',
|
|
40
|
+
'option[datasrc]',
|
|
41
|
+
'span[datasrc]',
|
|
42
|
+
'table[datasrc]',
|
|
43
|
+
'a[datafld]',
|
|
44
|
+
'applet[datafld]',
|
|
45
|
+
'button[datafld]',
|
|
46
|
+
'div[datafld]',
|
|
47
|
+
'fieldset[datafld]',
|
|
48
|
+
'frame[datafld]',
|
|
49
|
+
'label[datafld]',
|
|
50
|
+
'legend[datafld]',
|
|
51
|
+
'marquee[datafld]',
|
|
52
|
+
'param[datafld]',
|
|
53
|
+
'span[datafld]',
|
|
54
|
+
'button[dataformatas]',
|
|
55
|
+
'div[dataformatas]',
|
|
56
|
+
'label[dataformatas]',
|
|
57
|
+
'legend[dataformatas]',
|
|
58
|
+
'marquee[dataformatas]',
|
|
59
|
+
'option[dataformatas]',
|
|
60
|
+
'span[dataformatas]',
|
|
61
|
+
'table[dataformatas]',
|
|
62
|
+
'body[alink]',
|
|
63
|
+
'body[bgcolor]',
|
|
64
|
+
'table[bgcolor]',
|
|
65
|
+
'td[bgcolor]',
|
|
66
|
+
'th[bgcolor]',
|
|
67
|
+
'tr[bgcolor]',
|
|
68
|
+
'body[link]',
|
|
69
|
+
'body[marginbottom]',
|
|
70
|
+
'body[marginheight]',
|
|
71
|
+
'body[marginleft]',
|
|
72
|
+
'body[marginright]',
|
|
73
|
+
'body[margintop]',
|
|
74
|
+
'body[marginwidth]',
|
|
75
|
+
'body[text]',
|
|
76
|
+
'body[vlink]',
|
|
77
|
+
'col[char]',
|
|
78
|
+
'tbody[char]',
|
|
79
|
+
'thead[char]',
|
|
80
|
+
'tfoot[char]',
|
|
81
|
+
'td[char]',
|
|
82
|
+
'th[char]',
|
|
83
|
+
'tr[char]',
|
|
84
|
+
'col[charoff]',
|
|
85
|
+
'tbody[charoff]',
|
|
86
|
+
'thead[charoff]',
|
|
87
|
+
'tfoot[charoff]',
|
|
88
|
+
'td[charoff]',
|
|
89
|
+
'th[charoff]',
|
|
90
|
+
'tr[charoff]',
|
|
91
|
+
'col[valign]',
|
|
92
|
+
'tbody[valign]',
|
|
93
|
+
'thead[valign]',
|
|
94
|
+
'tfoot[valign]',
|
|
95
|
+
'td[valign]',
|
|
96
|
+
'th[valign]',
|
|
97
|
+
'tr[valign]',
|
|
98
|
+
'col[width]',
|
|
99
|
+
'pre[width]',
|
|
100
|
+
'table[width]',
|
|
101
|
+
'td[width]',
|
|
102
|
+
'th[width]',
|
|
103
|
+
'dl[compact]',
|
|
104
|
+
'ol[compact]',
|
|
105
|
+
'ul[compact]',
|
|
106
|
+
'h1[align]',
|
|
107
|
+
'h2[align]',
|
|
108
|
+
'h3[align]',
|
|
109
|
+
'h4[align]',
|
|
110
|
+
'h5[align]',
|
|
111
|
+
'h6[align]',
|
|
112
|
+
'caption[align]',
|
|
113
|
+
'col[align]',
|
|
114
|
+
'div[align]',
|
|
115
|
+
'legend[align]',
|
|
116
|
+
'p[align]',
|
|
117
|
+
'table[align]',
|
|
118
|
+
'tbody[align]',
|
|
119
|
+
'thead[align]',
|
|
120
|
+
'tfoot[align]',
|
|
121
|
+
'td[align]',
|
|
122
|
+
'th[align]',
|
|
123
|
+
'tr[align]',
|
|
124
|
+
'li[type]',
|
|
125
|
+
'ul[type]',
|
|
126
|
+
'table[cellpadding]',
|
|
127
|
+
'table[cellspacing]',
|
|
128
|
+
'table[frame]',
|
|
129
|
+
'table[rules]',
|
|
130
|
+
'td[height]',
|
|
131
|
+
'th[height]',
|
|
132
|
+
'td[nowrap]',
|
|
133
|
+
'th[nowrap]',
|
|
134
|
+
'body[background]',
|
|
135
|
+
'table[background]',
|
|
136
|
+
'thead[background]',
|
|
137
|
+
'tbody[background]',
|
|
138
|
+
'tfoot[background]',
|
|
139
|
+
'tr[background]',
|
|
140
|
+
'td[background]',
|
|
141
|
+
'th[background]',
|
|
142
|
+
'embed[name]',
|
|
143
|
+
'img[name]',
|
|
144
|
+
'area[nohref]',
|
|
145
|
+
'area[type]',
|
|
146
|
+
'area[hreflang]',
|
|
147
|
+
'input[ismap]',
|
|
148
|
+
'input[usemap]',
|
|
149
|
+
'iframe[longdesc]',
|
|
150
|
+
'img[longdesc]',
|
|
151
|
+
'img[lowsrc]',
|
|
152
|
+
'object[archive]',
|
|
153
|
+
'object[classid]',
|
|
154
|
+
'object[code]',
|
|
155
|
+
'object[codebase]',
|
|
156
|
+
'object[codetype]',
|
|
157
|
+
'object[declare]',
|
|
158
|
+
'object[standby]',
|
|
159
|
+
'iframe[datasrc]',
|
|
160
|
+
'img[datasrc]',
|
|
161
|
+
'input[datasrc]',
|
|
162
|
+
'object[datasrc]',
|
|
163
|
+
'select[datasrc]',
|
|
164
|
+
'textarea[datasrc]',
|
|
165
|
+
'iframe[datafld]',
|
|
166
|
+
'img[datafld]',
|
|
167
|
+
'input[datafld]',
|
|
168
|
+
'object[datafld]',
|
|
169
|
+
'select[datafld]',
|
|
170
|
+
'textarea[datafld]',
|
|
171
|
+
'input[dataformatas]',
|
|
172
|
+
'object[dataformatas]',
|
|
173
|
+
'select[dataformatas]',
|
|
174
|
+
'iframe[marginheight]',
|
|
175
|
+
'iframe[marginwidth]',
|
|
176
|
+
'br[clear]',
|
|
177
|
+
'hr[width]',
|
|
178
|
+
'embed[hspace]',
|
|
179
|
+
'iframe[hspace]',
|
|
180
|
+
'input[hspace]',
|
|
181
|
+
'img[hspace]',
|
|
182
|
+
'object[hspace]',
|
|
183
|
+
'embed[vspace]',
|
|
184
|
+
'iframe[vspace]',
|
|
185
|
+
'input[vspace]',
|
|
186
|
+
'img[vspace]',
|
|
187
|
+
'object[vspace]',
|
|
188
|
+
'hr[color]',
|
|
189
|
+
'hr[noshade]',
|
|
190
|
+
'hr[size]',
|
|
191
|
+
'iframe[align]',
|
|
192
|
+
'embed[align]',
|
|
193
|
+
'hr[align]',
|
|
194
|
+
'input[align]',
|
|
195
|
+
'img[align]',
|
|
196
|
+
'object[align]',
|
|
197
|
+
'iframe[allowtransparency]',
|
|
198
|
+
'iframe[frameborder]',
|
|
199
|
+
'iframe[framespacing]',
|
|
200
|
+
'iframe[scrolling]',
|
|
201
|
+
'img[border]',
|
|
202
|
+
'object[border]',
|
|
203
|
+
'input[inputmode]',
|
|
204
|
+
]);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# no-obsolete-element
|
|
2
|
+
|
|
3
|
+
Disallow obsolete selectors using.
|
|
4
|
+
|
|
5
|
+
**Sources:**
|
|
6
|
+
|
|
7
|
+
- [W3G Obsolete features](https://www.w3.org/TR/html5/obsolete.html#obsolete)
|
|
8
|
+
- [W3G Features removed](https://www.w3.org/TR/html52/changes.html#features-removed)
|
|
9
|
+
|
|
10
|
+
## Options
|
|
11
|
+
|
|
12
|
+
### true
|
|
13
|
+
|
|
14
|
+
The following pattern are considered violations:
|
|
15
|
+
|
|
16
|
+
```css
|
|
17
|
+
blink {
|
|
18
|
+
color: pink;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ruleName, messages } from '../index';
|
|
2
|
+
|
|
3
|
+
testRule({
|
|
4
|
+
ruleName,
|
|
5
|
+
config: [true],
|
|
6
|
+
|
|
7
|
+
accept: [
|
|
8
|
+
{
|
|
9
|
+
code: '.foo { color: pink; }',
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
|
|
13
|
+
reject: [
|
|
14
|
+
{
|
|
15
|
+
code: 'blink { color: pink; }',
|
|
16
|
+
message: messages.expected('blink'),
|
|
17
|
+
line: 1,
|
|
18
|
+
column: 3,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: 'applet, a { color: pink; }',
|
|
22
|
+
message: messages.expected('applet, a'),
|
|
23
|
+
line: 1,
|
|
24
|
+
column: 3,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
code: 'applet, blink { color: pink; }',
|
|
28
|
+
message: messages.expected('applet, blink'),
|
|
29
|
+
line: 1,
|
|
30
|
+
column: 3,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { utils } from 'stylelint';
|
|
2
|
+
import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
|
|
3
|
+
import { obsoleteElements } from './obsoleteElements';
|
|
4
|
+
|
|
5
|
+
export const ruleName = 'a11y/no-obsolete-element';
|
|
6
|
+
|
|
7
|
+
export const messages = utils.ruleMessages(ruleName, {
|
|
8
|
+
expected: (selector) => `Unexpected using obsolete selector "${selector}"`,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function check(selector, node) {
|
|
12
|
+
if (node.type !== 'rule') {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return !node.selectors.some((sel) => {
|
|
16
|
+
return obsoleteElements.has(sel);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function (actual) {
|
|
21
|
+
return (root, result) => {
|
|
22
|
+
const validOptions = utils.validateOptions(result, ruleName, { actual });
|
|
23
|
+
|
|
24
|
+
if (!validOptions || !actual) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
root.walk((node) => {
|
|
29
|
+
let selector = null;
|
|
30
|
+
|
|
31
|
+
if (node.type === 'rule') {
|
|
32
|
+
if (!isStandardSyntaxRule(node)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
selector = node.selector;
|
|
36
|
+
} else if (node.type === 'atrule' && node.name.toLowerCase() === 'page' && node.params) {
|
|
37
|
+
selector = node.params;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!selector) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const isAccepted = check(selector, node);
|
|
45
|
+
|
|
46
|
+
if (!isAccepted) {
|
|
47
|
+
utils.report({
|
|
48
|
+
index: node.lastEach,
|
|
49
|
+
message: messages.expected(selector),
|
|
50
|
+
node,
|
|
51
|
+
ruleName,
|
|
52
|
+
result,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const obsoleteElements = new Set([
|
|
2
|
+
'applet',
|
|
3
|
+
'acronym',
|
|
4
|
+
'bgsound',
|
|
5
|
+
'dir',
|
|
6
|
+
'frame',
|
|
7
|
+
'frameset',
|
|
8
|
+
'noframes',
|
|
9
|
+
'hgroup',
|
|
10
|
+
'isindex',
|
|
11
|
+
'listing',
|
|
12
|
+
'nextid',
|
|
13
|
+
'noembed',
|
|
14
|
+
'plaintext',
|
|
15
|
+
'rb',
|
|
16
|
+
'rtc',
|
|
17
|
+
'strike',
|
|
18
|
+
'xmp',
|
|
19
|
+
'basefont',
|
|
20
|
+
'big',
|
|
21
|
+
'blink',
|
|
22
|
+
'center',
|
|
23
|
+
'font',
|
|
24
|
+
'marquee',
|
|
25
|
+
'multicol',
|
|
26
|
+
'nobr',
|
|
27
|
+
'spacer',
|
|
28
|
+
'tt',
|
|
29
|
+
'keygen',
|
|
30
|
+
'menu',
|
|
31
|
+
'menuitem',
|
|
32
|
+
]);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# no-outline-none
|
|
2
|
+
|
|
3
|
+
Disallow outline clearing.
|
|
4
|
+
|
|
5
|
+
Why? [Because](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#navigation-mechanisms-focus-visible)
|
|
6
|
+
|
|
7
|
+
**Sources:**
|
|
8
|
+
|
|
9
|
+
- [DON'T DO IT!](http://www.outlinenone.com/)
|
|
10
|
+
- [a11yproject](https://a11yproject.com/posts/never-remove-css-outlines/)
|
|
11
|
+
- [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/outline)
|
|
12
|
+
|
|
13
|
+
## Options
|
|
14
|
+
|
|
15
|
+
### true
|
|
16
|
+
|
|
17
|
+
The following pattern are considered violations:
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
.foo:focus {
|
|
21
|
+
outline: 0;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```css
|
|
26
|
+
.bar:focus {
|
|
27
|
+
outline: none;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
.baz:focus {
|
|
33
|
+
outline: none;
|
|
34
|
+
border: transparent;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```scss
|
|
39
|
+
.quux {
|
|
40
|
+
.quuux:focus {
|
|
41
|
+
outline: 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The following patterns are _not_ considered violations:
|
|
47
|
+
|
|
48
|
+
```css
|
|
49
|
+
.foo {
|
|
50
|
+
outline: 0;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```scss
|
|
55
|
+
$primary-color: #333;
|
|
56
|
+
.bar:focus {
|
|
57
|
+
outline: 1px solid $primary-color;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```css
|
|
62
|
+
.baz:focus {
|
|
63
|
+
outline: 1px solid #333;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```css
|
|
68
|
+
.quux:focus {
|
|
69
|
+
outline: 0;
|
|
70
|
+
border: 1px solid #000;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Note
|
|
75
|
+
|
|
76
|
+
[Similar rule](https://github.com/stylelint/stylelint/blob/master/lib/rules/declaration-property-value-blacklist/README.md) is in [Stylelint](https://github.com/stylelint/stylelint), but it triggers another error message and does not check for `:focus` selector and `border` property.
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"declaration-property-value-blacklist": {
|
|
81
|
+
"outline": ["none", "0"]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { messages, ruleName } from '../index';
|
|
2
|
+
|
|
3
|
+
testRule({
|
|
4
|
+
ruleName,
|
|
5
|
+
config: [true],
|
|
6
|
+
|
|
7
|
+
accept: [
|
|
8
|
+
{
|
|
9
|
+
code: '.foo { outline: 0; }',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
code: '$primary-color: #333; .bar:focus { outline: 1px solid $primary-color; }',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
code: '.baz:focus { outline: none; border-color: #333; }',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
code: '.quux:focus { outline: 0; border: 1px solid #000; }',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: '.quuux:focus { outline: none; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); }',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
|
|
25
|
+
reject: [
|
|
26
|
+
{
|
|
27
|
+
code: '.foo1:focus { outline: none; } .foo2:focus { outline: 1px solid red; }',
|
|
28
|
+
message: messages.expected('.foo1:focus'),
|
|
29
|
+
line: 1,
|
|
30
|
+
column: 3,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
code: '.bar:focus { outline: none; }',
|
|
34
|
+
message: messages.expected('.bar:focus'),
|
|
35
|
+
line: 1,
|
|
36
|
+
column: 3,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
code: '.baz:focus { outline: none; border: transparent; }',
|
|
40
|
+
message: messages.expected('.baz:focus'),
|
|
41
|
+
line: 1,
|
|
42
|
+
column: 3,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
code: '.quux { .quuux:focus { outline: 0; } }',
|
|
46
|
+
message: messages.expected('.quuux:focus'),
|
|
47
|
+
line: 1,
|
|
48
|
+
column: 11,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { utils } from 'stylelint';
|
|
2
|
+
import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule';
|
|
3
|
+
|
|
4
|
+
export const ruleName = 'a11y/no-outline-none';
|
|
5
|
+
|
|
6
|
+
export const messages = utils.ruleMessages(ruleName, {
|
|
7
|
+
expected: (selector) => `Unexpected using "outline" property in ${selector}`,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
function check(selector, node) {
|
|
11
|
+
if (node.type !== 'rule') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!selector.match(/:focus/gi)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const hasEmptyOutline = node.nodes.some(
|
|
20
|
+
(o) =>
|
|
21
|
+
o.type === 'decl' &&
|
|
22
|
+
o.prop.toLowerCase() === 'outline' &&
|
|
23
|
+
['0', 'none'].indexOf(o.value.toLowerCase()) >= 0
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
if (hasEmptyOutline) {
|
|
27
|
+
return node.nodes.some(
|
|
28
|
+
(o) =>
|
|
29
|
+
o.type === 'decl' &&
|
|
30
|
+
['border', 'border-color', 'box-shadow'].indexOf(o.prop.toLowerCase()) >= 0 &&
|
|
31
|
+
!o.value.toLowerCase().match(/transparent/gi)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default function (actual) {
|
|
39
|
+
return (root, result) => {
|
|
40
|
+
const validOptions = utils.validateOptions(result, ruleName, { actual });
|
|
41
|
+
|
|
42
|
+
if (!validOptions || !actual) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
root.walk((node) => {
|
|
47
|
+
let selector = null;
|
|
48
|
+
|
|
49
|
+
if (node.type === 'rule') {
|
|
50
|
+
if (!isStandardSyntaxRule(node)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
selector = node.selector;
|
|
54
|
+
} else if (node.type === 'atrule' && node.name.toLowerCase() === 'page' && node.params) {
|
|
55
|
+
selector = node.params;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!selector) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const isAccepted = check(selector, node);
|
|
63
|
+
|
|
64
|
+
if (!isAccepted) {
|
|
65
|
+
utils.report({
|
|
66
|
+
index: node.lastEach,
|
|
67
|
+
message: messages.expected(selector),
|
|
68
|
+
node,
|
|
69
|
+
ruleName,
|
|
70
|
+
result,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# no-spread-text
|
|
2
|
+
|
|
3
|
+
Require width of text greater than 45 characters and less than 80 characters.
|
|
4
|
+
|
|
5
|
+
**Sources:**
|
|
6
|
+
|
|
7
|
+
- [Ryan Mack](https://ryanmack.me/quick-measure)
|
|
8
|
+
- [Manuel Matuzovic](https://medium.com/@matuzo/writing-css-with-accessibility-in-mind-8514a0007939)
|
|
9
|
+
|
|
10
|
+
> Warning! This rule use some heuristics for define css node with styles for text. It may be unstable.
|
|
11
|
+
|
|
12
|
+
## Options
|
|
13
|
+
|
|
14
|
+
### true
|
|
15
|
+
|
|
16
|
+
The following pattern are considered violations:
|
|
17
|
+
|
|
18
|
+
```css
|
|
19
|
+
.foo {
|
|
20
|
+
text-transform: lowercase;
|
|
21
|
+
max-width: 40ch;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```css
|
|
26
|
+
.foo {
|
|
27
|
+
line-height: 1.8;
|
|
28
|
+
max-width: 82ch;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The following patterns are _not_ considered violations:
|
|
33
|
+
|
|
34
|
+
```css
|
|
35
|
+
.foo {
|
|
36
|
+
max-width: 65ch;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```css
|
|
41
|
+
.foo {
|
|
42
|
+
max-width: 82ch;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```css
|
|
47
|
+
.foo {
|
|
48
|
+
max-width: 100px;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { messages, ruleName } from '../index';
|
|
2
|
+
|
|
3
|
+
testRule({
|
|
4
|
+
ruleName,
|
|
5
|
+
config: [true],
|
|
6
|
+
|
|
7
|
+
accept: [
|
|
8
|
+
{
|
|
9
|
+
code: '.foo { }',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
code: '.foo { display: flex; max-width: 82ch; }',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
code: '.foo { height: 100%; max-width: 82ch; }',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
code: '.foo { text-transform: lowercase; max-width: 65ch; }',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
code: '.bar { word-spacing: -5px; max-width: 100px; }',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
code: '.baz { MAX-WIDTH: 63CH; }',
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
|
|
28
|
+
reject: [
|
|
29
|
+
{
|
|
30
|
+
code: '.foo { text-transform: lowercase; max-width: 40ch; }',
|
|
31
|
+
message: messages.expected('.foo'),
|
|
32
|
+
line: 1,
|
|
33
|
+
column: 3,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
code: '.bar { LINE-HEIGHT: 1.8; MAX-WIDTH: 81CH; }',
|
|
37
|
+
message: messages.expected('.bar'),
|
|
38
|
+
line: 1,
|
|
39
|
+
column: 3,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
});
|