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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stylelint-plugin-defensive-css",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "A Stylelint plugin to enforce defensive CSS best practices.",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -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
- /* GROUPING VENDOR PREFIXES */
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