stylelint-plugin-defensive-css 0.3.0 → 0.4.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/README.md
CHANGED
|
@@ -191,6 +191,64 @@ div {
|
|
|
191
191
|
}
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
+
### Scroll Chaining
|
|
195
|
+
|
|
196
|
+
> [Read more about this pattern in Defensive CSS](https://defensivecss.dev/tip/scroll-chain/)
|
|
197
|
+
|
|
198
|
+
Have you ever opened a modal and started scrolling, and then when you reach the
|
|
199
|
+
end and keep scrolling, the content underneath the modal (the body element) will
|
|
200
|
+
scroll? This is called scroll chaining.
|
|
201
|
+
|
|
202
|
+
Enable this rule in order to require all scrollable overflow properties to have
|
|
203
|
+
an overscroll-behavior value.
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"rules": {
|
|
208
|
+
"plugin/use-defensive-css": [true, { "scroll-chaining": true }]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### ✅ Passing Examples
|
|
214
|
+
|
|
215
|
+
```css
|
|
216
|
+
div {
|
|
217
|
+
overflow-x: auto;
|
|
218
|
+
overscroll-behavior-x: contain;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
div {
|
|
222
|
+
overflow: hidden scroll;
|
|
223
|
+
overscroll-behavior: contain;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
div {
|
|
227
|
+
overflow: hidden; /* No overscroll-behavior is needed in the case of hidden */
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
div {
|
|
231
|
+
overflow-block: auto;
|
|
232
|
+
overscroll-behavior: none;
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### ❌ Failing Examples
|
|
237
|
+
|
|
238
|
+
```css
|
|
239
|
+
div {
|
|
240
|
+
overflow-x: auto;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
div {
|
|
244
|
+
overflow: hidden scroll;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
div {
|
|
248
|
+
overflow-block: auto;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
194
252
|
### Vendor Prefix Grouping
|
|
195
253
|
|
|
196
254
|
> [Read more about this pattern in Defensive CSS](https://defensivecss.dev/tip/grouping-selectors/)
|
package/package.json
CHANGED
|
@@ -14,6 +14,9 @@ const ruleMessages = stylelint.utils.ruleMessages(ruleName, {
|
|
|
14
14
|
flexWrapping() {
|
|
15
15
|
return 'Flex rows must have a `flex-wrap: wrap;` or `flex-wrap: wrap-reverse` declaration.';
|
|
16
16
|
},
|
|
17
|
+
scrollChaining() {
|
|
18
|
+
return `Containers with an auto or scroll 'overflow' must also have an 'overscroll-behavior' property defined.`;
|
|
19
|
+
},
|
|
17
20
|
vendorPrefixWGrouping() {
|
|
18
21
|
return `Separate different vendor prefixes into their own rules.`;
|
|
19
22
|
},
|
|
@@ -20,9 +20,15 @@ const defaultFlexWrappingProps = {
|
|
|
20
20
|
isMissingFlexWrap: true,
|
|
21
21
|
nodeToReport: undefined,
|
|
22
22
|
};
|
|
23
|
+
const defaultScrollChainingProps = {
|
|
24
|
+
hasOverflow: false,
|
|
25
|
+
hasOverscrollBehavior: false,
|
|
26
|
+
nodeToReport: undefined,
|
|
27
|
+
};
|
|
23
28
|
|
|
24
29
|
let backgroundRepeatProps = { ...defaultBackgroundRepeatProps };
|
|
25
30
|
let flexWrappingProps = { ...defaultFlexWrappingProps };
|
|
31
|
+
let scrollChainingProps = { ...defaultScrollChainingProps };
|
|
26
32
|
let isLastStyleDeclaration = false;
|
|
27
33
|
|
|
28
34
|
const ruleFunction = (_, options) => {
|
|
@@ -134,7 +140,45 @@ const ruleFunction = (_, options) => {
|
|
|
134
140
|
}
|
|
135
141
|
}
|
|
136
142
|
|
|
137
|
-
/*
|
|
143
|
+
/* SCROLL CHAINING */
|
|
144
|
+
if (options?.['scroll-chaining']) {
|
|
145
|
+
const overflowProperties = [
|
|
146
|
+
'overflow',
|
|
147
|
+
'overflow-x',
|
|
148
|
+
'overflow-y',
|
|
149
|
+
'overflow-inline',
|
|
150
|
+
'overflow-block',
|
|
151
|
+
];
|
|
152
|
+
if (
|
|
153
|
+
overflowProperties.includes(decl.prop) &&
|
|
154
|
+
(decl.value.includes('auto') || decl.value.includes('scroll'))
|
|
155
|
+
) {
|
|
156
|
+
scrollChainingProps.hasOverflow = true;
|
|
157
|
+
scrollChainingProps.nodeToReport = decl;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (decl.prop.includes('overscroll-behavior')) {
|
|
161
|
+
scrollChainingProps.hasOverscrollBehavior = true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (isLastStyleDeclaration) {
|
|
165
|
+
if (
|
|
166
|
+
scrollChainingProps.hasOverflow &&
|
|
167
|
+
!scrollChainingProps.hasOverscrollBehavior
|
|
168
|
+
) {
|
|
169
|
+
stylelint.utils.report({
|
|
170
|
+
message: ruleMessages.scrollChaining(),
|
|
171
|
+
node: scrollChainingProps.nodeToReport,
|
|
172
|
+
result,
|
|
173
|
+
ruleName,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
scrollChainingProps = { ...defaultScrollChainingProps };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* VENDOR PREFIX GROUPING */
|
|
138
182
|
if (options?.['vendor-prefix-grouping']) {
|
|
139
183
|
const hasMultiplePrefixes = findVendorPrefixes(decl.parent.selector);
|
|
140
184
|
|