@double-great/stylelint-a11y 3.1.0 → 3.3.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +40 -12
  3. package/package.json +13 -2
  4. package/src/index.js +35 -1
  5. package/src/rules/content-property-no-static-value/README.md +20 -0
  6. package/src/rules/content-property-no-static-value/index.js +13 -7
  7. package/src/rules/font-size-is-readable/index.js +6 -0
  8. package/src/rules/index.js +1 -1
  9. package/src/rules/line-height-is-vertical-rhythmed/README.md +19 -0
  10. package/src/rules/line-height-is-vertical-rhythmed/index.js +15 -5
  11. package/src/rules/media-prefers-color-scheme/index.js +6 -0
  12. package/src/rules/media-prefers-reduced-motion/index.js +88 -19
  13. package/src/rules/no-display-none/index.js +7 -1
  14. package/src/rules/no-obsolete-attribute/index.js +9 -1
  15. package/src/rules/no-obsolete-element/index.js +9 -1
  16. package/src/rules/no-outline-none/index.js +7 -1
  17. package/src/rules/no-spread-text/README.md +19 -0
  18. package/src/rules/no-spread-text/index.js +12 -3
  19. package/src/rules/no-text-align-justify/index.js +7 -1
  20. package/src/rules/selector-pseudo-class-focus/index.js +6 -0
  21. package/test/e2e/README.md +37 -0
  22. package/test/e2e/cli.test.js +71 -0
  23. package/test/e2e/performance.test.js +98 -0
  24. package/test/e2e/projects/basic-css/.stylelintrc.json +17 -0
  25. package/test/e2e/projects/basic-css/styles.css +81 -0
  26. package/test/e2e/projects/large-codebase/.stylelintrc.json +17 -0
  27. package/test/e2e/projects/large-codebase/components.css +329 -0
  28. package/test/e2e/projects/large-codebase/layout.css +393 -0
  29. package/test/e2e/projects/scss-project/.stylelintrc.json +18 -0
  30. package/test/e2e/projects/scss-project/main.scss +163 -0
  31. package/test/e2e/projects.test.js +349 -0
  32. package/test/helpers/exec-stylelint.js +80 -0
  33. package/test/helpers/simple-test-utils.js +75 -0
  34. package/test/integration/integration.test.js +265 -0
  35. package/test/system/ci-integration.test.js +244 -0
  36. package/test/system/compatibility.test.js +249 -0
  37. package/test/system/performance-regression.test.js +277 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,68 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.3.0] - 2025-08-17
9
+
10
+ ### Added
11
+
12
+ - Advanced testing infrastructure with comprehensive CI/CD workflows
13
+ - System-level performance regression testing suite
14
+ - Cross-platform compatibility testing for Node.js environments
15
+ - Integration tests for real-world stylelint plugin functionality
16
+ - Enhanced rule configuration options with customizable parameters
17
+ - Meta objects for all rules with documentation URLs and fixable status
18
+ - Comprehensive rule documentation with examples and configuration options
19
+
20
+ ### Changed
21
+
22
+ - Enhanced stylelint standards compliance with proper meta objects across all rules
23
+ - Standardized rule message formatting for consistent error reporting
24
+ - Improved Jest configuration with proper Node.js module support
25
+ - Updated all rule implementations to follow modern stylelint plugin patterns
26
+ - Enhanced rule functionality with configurable options for better customization
27
+ - Improved error messages for better developer experience
28
+
29
+ ### Fixed
30
+
31
+ - Removed deprecated experimental VM modules flag from Jest scripts for Node.js compatibility
32
+ - Resolved CI performance test expectations for consistent build environments
33
+ - Cleaned up test infrastructure and removed redundant configuration files
34
+ - Fixed rule message clarity and consistency across all accessibility rules
35
+
36
+ ## [3.2.0] - 2025-08-16
37
+
38
+ ### Added
39
+
40
+ - Comprehensive E2E testing infrastructure with real-world projects
41
+ - Integration testing suite for plugin functionality
42
+ - Performance benchmarking with regression detection
43
+ - Test helper utilities for simplified testing
44
+ - Support for SCSS/Sass project testing
45
+ - Large codebase performance testing
46
+ - CLI integration tests with full stylelint compatibility
47
+ - Development documentation in README
48
+ - Pull request template for consistent PR descriptions
49
+
50
+ ### Changed
51
+
52
+ - Standardized import patterns across all rules for better consistency
53
+ - Fixed rule order in exports object to maintain alphabetical sorting
54
+ - Improved Jest configuration for multiple test types
55
+ - Enhanced ESLint configuration for E2E tests
56
+ - Updated actions/checkout from v4 to v5 (#77)
57
+
58
+ ### Fixed
59
+
60
+ - Fixed `media-prefers-reduced-motion` rule incorrectly adding duplicate media queries when `prefers-reduced-motion` is already nested inside another media query (#66)
61
+
62
+ ### Developer Experience
63
+
64
+ - Added npm scripts for granular test execution (`test:unit`, `test:integration`, `test:e2e`, `test:all`)
65
+ - Simplified test utilities for faster development
66
+ - Cross-platform compatibility testing
67
+ - Performance metrics and scaling tests
68
+ - Individual rule benchmarks included
69
+
8
70
  ## [3.1.0] - 2025-08-16
9
71
 
10
72
  ### Added
package/README.md CHANGED
@@ -52,19 +52,47 @@ This shareable config contains the following:
52
52
 
53
53
  Since it adds stylelint-a11y to `plugins`, you don't have to do this yourself when extending this config.
54
54
 
55
- ## Help out
55
+ ## Rule Configuration
56
56
 
57
- There work on the plugin's rules is still in progress, so if you feel like it, you're welcome to help out in any of these (the plugin follows stylelint guidelines so most part of this is based on its docs):
57
+ Many rules support additional configuration options for customization. For example:
58
58
 
59
- - Create, enhance, and debug rules (see stylelint's guide to "[Working on rules](https://github.com/stylelint/stylelint/blob/main/docs/developer-guide/rules.md)").
60
- - Improve documentation.
61
- - Chime in on any open issue or pull request.
62
- - Open new issues about your ideas on new rules, or for how to improve the existing ones, and pull requests to show us how your idea works.
63
- - Add new tests to absolutely anything.
64
- - Work on improving performance of rules.
65
- - Contribute to [stylelint](https://github.com/stylelint/stylelint)
66
- - Spread the word.
59
+ ```json
60
+ {
61
+ "rules": {
62
+ "a11y/font-size-is-readable": [true, { "thresholdInPixels": 16 }],
63
+ "a11y/no-spread-text": [true, { "minWidth": 30, "maxWidth": 60 }],
64
+ "a11y/line-height-is-vertical-rhythmed": [true, { "baselineGrid": 20 }]
65
+ }
66
+ }
67
+ ```
68
+
69
+ Refer to individual rule documentation for available options.
70
+
71
+ ## Development
72
+
73
+ ### Testing
74
+
75
+ Run tests with the following commands:
76
+
77
+ - `npm run test` - Run unit tests for all rules
78
+ - `npm run test:unit` - Run unit tests only
79
+ - `npm run test:integration` - Run integration tests
80
+ - `npm run test:e2e` - Run end-to-end tests with real projects
81
+ - `npm run test:performance` - Run performance benchmarks
82
+ - `npm run test:all` - Run complete test suite
83
+
84
+ ### Testing Infrastructure
85
+
86
+ This project includes testing at a few levels:
87
+
88
+ - **Unit tests** - Individual rule functionality
89
+ - **Integration tests** - Plugin integration with stylelint
90
+ - **E2E tests** - Real-world project testing with intentional violations
91
+ - **Performance tests** - Benchmark testing for large codebases
67
92
 
68
- We communicate via [issues](https://github.com/double-great/stylelint-a11y/issues) and [pull requests](https://github.com/double-great/stylelint-a11y/pulls).
93
+ ### Other Commands
69
94
 
70
- There is also [stackoverflow](https://stackoverflow.com/questions/tagged/stylelint), which would be the preferred QA forum.
95
+ - `npm run lint` - Run ESLint
96
+ - `npm run format:check` - Check code formatting
97
+ - `npm run format:fix` - Fix code formatting
98
+ - `npm run coverage` - Run tests with coverage report
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@double-great/stylelint-a11y",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "description": "Plugin for stylelint with a11y rules",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -29,7 +29,17 @@
29
29
  "format:check": "prettier --check .",
30
30
  "format:fix": "prettier --write .",
31
31
  "test": "jest",
32
- "coverage": "jest --coverage"
32
+ "test:unit": "jest src",
33
+ "test:integration": "jest test/integration",
34
+ "test:e2e": "jest test/e2e",
35
+ "test:performance": "jest test/e2e/performance.test.js",
36
+ "test:system": "jest test/system",
37
+ "test:regression": "jest test/system/performance-regression.test.js",
38
+ "test:compatibility": "jest test/system/compatibility.test.js",
39
+ "test:ci": "jest test/system/ci-integration.test.js",
40
+ "test:all": "jest",
41
+ "coverage": "jest --coverage",
42
+ "coverage:all": "jest --coverage"
33
43
  },
34
44
  "prettier": {
35
45
  "printWidth": 100,
@@ -48,6 +58,7 @@
48
58
  "jest": "^30.0.5",
49
59
  "jest-light-runner": "^0.7.9",
50
60
  "jest-preset-stylelint": "^8.0.0",
61
+ "postcss-scss": "^4.0.9",
51
62
  "prettier": "^3.6.2",
52
63
  "stylelint": "^16.23.1"
53
64
  },
package/src/index.js CHANGED
@@ -3,8 +3,42 @@ const { createPlugin } = stylelint;
3
3
 
4
4
  import rules from './rules/index.js';
5
5
 
6
+ // Import meta objects
7
+ import { meta as contentPropertyMeta } from './rules/content-property-no-static-value/index.js';
8
+ import { meta as fontSizeReadableMeta } from './rules/font-size-is-readable/index.js';
9
+ import { meta as lineHeightRhythmedMeta } from './rules/line-height-is-vertical-rhythmed/index.js';
10
+ import { meta as mediaPrefersColorSchemeMeta } from './rules/media-prefers-color-scheme/index.js';
11
+ import { meta as mediaPrefersReducedMotionMeta } from './rules/media-prefers-reduced-motion/index.js';
12
+ import { meta as noDisplayNoneMeta } from './rules/no-display-none/index.js';
13
+ import { meta as noObsoleteAttributeMeta } from './rules/no-obsolete-attribute/index.js';
14
+ import { meta as noObsoleteElementMeta } from './rules/no-obsolete-element/index.js';
15
+ import { meta as noOutlineNoneMeta } from './rules/no-outline-none/index.js';
16
+ import { meta as noSpreadTextMeta } from './rules/no-spread-text/index.js';
17
+ import { meta as noTextAlignJustifyMeta } from './rules/no-text-align-justify/index.js';
18
+ import { meta as selectorPseudoClassFocusMeta } from './rules/selector-pseudo-class-focus/index.js';
19
+
20
+ const ruleMetaMap = {
21
+ 'content-property-no-static-value': contentPropertyMeta,
22
+ 'font-size-is-readable': fontSizeReadableMeta,
23
+ 'line-height-is-vertical-rhythmed': lineHeightRhythmedMeta,
24
+ 'media-prefers-color-scheme': mediaPrefersColorSchemeMeta,
25
+ 'media-prefers-reduced-motion': mediaPrefersReducedMotionMeta,
26
+ 'no-display-none': noDisplayNoneMeta,
27
+ 'no-obsolete-attribute': noObsoleteAttributeMeta,
28
+ 'no-obsolete-element': noObsoleteElementMeta,
29
+ 'no-outline-none': noOutlineNoneMeta,
30
+ 'no-spread-text': noSpreadTextMeta,
31
+ 'no-text-align-justify': noTextAlignJustifyMeta,
32
+ 'selector-pseudo-class-focus': selectorPseudoClassFocusMeta,
33
+ };
34
+
6
35
  const rulesPlugins = Object.keys(rules).map((ruleName) => {
7
- return createPlugin(`a11y/${ruleName}`, rules[ruleName]);
36
+ const plugin = createPlugin(`a11y/${ruleName}`, rules[ruleName]);
37
+
38
+ // Add meta object to the plugin
39
+ plugin.meta = ruleMetaMap[ruleName];
40
+
41
+ return plugin;
8
42
  });
9
43
 
10
44
  export default rulesPlugins;
@@ -11,6 +11,26 @@ Disallow CSS generated content except aria-label attribute content and empty str
11
11
 
12
12
  ### true
13
13
 
14
+ The rule is enabled with default allowed values: `['""', "''", 'attr(aria-label)']`.
15
+
16
+ ### { allowedValues: string[] }
17
+
18
+ - `allowedValues` (default: `['""', "''", 'attr(aria-label)']`): Array of allowed content values
19
+
20
+ #### Example configuration
21
+
22
+ ```javascript
23
+ {
24
+ "a11y/content-property-no-static-value": [true, {
25
+ "allowedValues": ["''", '""', "attr(title)", "counter(section)"]
26
+ }]
27
+ }
28
+ ```
29
+
30
+ ### Examples
31
+
32
+ #### ✓ Default configuration (`true`)
33
+
14
34
  The following pattern is considered a violation:
15
35
 
16
36
  ```css
@@ -7,9 +7,15 @@ const {
7
7
  export const ruleName = 'a11y/content-property-no-static-value';
8
8
 
9
9
  export const messages = ruleMessages(ruleName, {
10
- expected: (selector) => `Unexpected using "content" property in ${selector}`,
10
+ expected: (selector) => `Expected "content" property to not be used in ${selector}`,
11
11
  });
12
12
 
13
+ export const meta = {
14
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/content-property-no-static-value/README.md',
15
+ fixable: false,
16
+ deprecated: false,
17
+ };
18
+
13
19
  const isContentPropertyUsedCorrectly = (selectors) =>
14
20
  selectors.every((selector) => {
15
21
  return /:before|:after/.test(selector);
@@ -18,24 +24,24 @@ const isContentPropertyUsedCorrectly = (selectors) =>
18
24
  const checkNodesForContentProperty = (node) =>
19
25
  node.nodes.filter((n) => n.prop).some((n) => n.prop.toLowerCase() === 'content');
20
26
 
21
- function check(node) {
27
+ function check(node, options = {}) {
22
28
  if (node.type !== 'rule' || !checkNodesForContentProperty(node) || !node.first) {
23
29
  return true;
24
30
  }
25
31
 
32
+ const allowedValues = options.allowedValues || ["''", '""', 'attr(aria-label)'];
33
+
26
34
  return node.nodes.some((o) => {
27
35
  return (
28
36
  o.type === 'decl' &&
29
37
  o.prop.toLowerCase() === 'content' &&
30
38
  isContentPropertyUsedCorrectly(o.parent.selectors) &&
31
- (o.value.toLowerCase() === "''" ||
32
- o.value.toLowerCase() === '""' ||
33
- o.value.toLowerCase() === 'attr(aria-label)')
39
+ allowedValues.some((allowed) => o.value.toLowerCase() === allowed.toLowerCase())
34
40
  );
35
41
  });
36
42
  }
37
43
 
38
- export default function contentPropertyNoStaticValue(actual) {
44
+ export default function contentPropertyNoStaticValue(actual, options) {
39
45
  return (root, result) => {
40
46
  const validOptions = validateOptions(result, ruleName, { actual });
41
47
 
@@ -60,7 +66,7 @@ export default function contentPropertyNoStaticValue(actual) {
60
66
  return;
61
67
  }
62
68
 
63
- const isAccepted = check(node);
69
+ const isAccepted = check(node, options);
64
70
 
65
71
  if (!isAccepted) {
66
72
  report({
@@ -10,6 +10,12 @@ export const messages = ruleMessages(ruleName, {
10
10
  expected: (selector) => `Expected a larger font-size in ${selector}`,
11
11
  });
12
12
 
13
+ export const meta = {
14
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/font-size-is-readable/README.md',
15
+ fixable: false,
16
+ deprecated: false,
17
+ };
18
+
13
19
  const pxToPt = (v) => 0.75 * v;
14
20
 
15
21
  const checkInPx = (value, THRESHOLD_IN_PX) =>
@@ -15,6 +15,7 @@ export default {
15
15
  'content-property-no-static-value': contentPropertyNoStaticValue,
16
16
  'font-size-is-readable': fontSizeIsReadable,
17
17
  'line-height-is-vertical-rhythmed': lineHeightIsVerticalRhythmed,
18
+ 'media-prefers-color-scheme': mediaPrefersColorScheme,
18
19
  'media-prefers-reduced-motion': mediaPrefersReducedMotion,
19
20
  'no-display-none': noDisplayNone,
20
21
  'no-obsolete-attribute': noObsoleteAttribute,
@@ -23,5 +24,4 @@ export default {
23
24
  'no-spread-text': noSpreadText,
24
25
  'no-text-align-justify': noTextAlignJustify,
25
26
  'selector-pseudo-class-focus': selectorPseudoClassFocus,
26
- 'media-prefers-color-scheme': mediaPrefersColorScheme,
27
27
  };
@@ -10,6 +10,25 @@ Disallow not vertical rhythmed line-height.
10
10
 
11
11
  ### true
12
12
 
13
+ The rule is enabled with default values (24px baseline grid, 1.5 minimum relative line-height).
14
+
15
+ ### { baselineGrid: number, minRelativeLineHeight: number }
16
+
17
+ - `baselineGrid` (default: `24`): Baseline grid value in pixels for pixel-based line-heights
18
+ - `minRelativeLineHeight` (default: `1.5`): Minimum allowed relative line-height value
19
+
20
+ #### Example configuration
21
+
22
+ ```javascript
23
+ {
24
+ "a11y/line-height-is-vertical-rhythmed": [true, { "baselineGrid": 20, "minRelativeLineHeight": 1.3 }]
25
+ }
26
+ ```
27
+
28
+ ### Examples
29
+
30
+ #### ✓ Default configuration (`true`)
31
+
13
32
  The following pattern is considered a violation:
14
33
 
15
34
  ```css
@@ -10,13 +10,23 @@ export const messages = ruleMessages(ruleName, {
10
10
  expected: (selector) => `Expected a vertical rhythmed line-height in ${selector}`,
11
11
  });
12
12
 
13
- function check(node) {
13
+ export const meta = {
14
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/line-height-is-vertical-rhythmed/README.md',
15
+ fixable: false,
16
+ deprecated: false,
17
+ };
18
+
19
+ function check(node, options = {}) {
14
20
  if (node.type !== 'rule') {
15
21
  return true;
16
22
  }
17
23
 
18
- const checkInPx = (o) => o.value.toLowerCase().endsWith('px') && parseInt(o.value) % 24 !== 0;
19
- const checkInRel = (o) => !isNaN(o.value) && parseFloat(o.value) < 1.5;
24
+ const baselineGrid = options.baselineGrid || 24;
25
+ const minRelativeLineHeight = options.minRelativeLineHeight || 1.5;
26
+
27
+ const checkInPx = (o) =>
28
+ o.value.toLowerCase().endsWith('px') && parseInt(o.value) % baselineGrid !== 0;
29
+ const checkInRel = (o) => !isNaN(o.value) && parseFloat(o.value) < minRelativeLineHeight;
20
30
 
21
31
  return !node.nodes.some(
22
32
  (o) =>
@@ -24,7 +34,7 @@ function check(node) {
24
34
  );
25
35
  }
26
36
 
27
- export default function lineHeightIsVerticalRhythmed(actual) {
37
+ export default function lineHeightIsVerticalRhythmed(actual, options) {
28
38
  return (root, result) => {
29
39
  const validOptions = validateOptions(result, ruleName, { actual });
30
40
 
@@ -49,7 +59,7 @@ export default function lineHeightIsVerticalRhythmed(actual) {
49
59
  return;
50
60
  }
51
61
 
52
- const isAccepted = check(node);
62
+ const isAccepted = check(node, options);
53
63
 
54
64
  if (!isAccepted) {
55
65
  report({
@@ -13,6 +13,12 @@ export const ruleName = 'a11y/media-prefers-color-scheme';
13
13
  export const messages = ruleMessages(ruleName, {
14
14
  expected: (selector) => `Expected ${selector} is used with @media (prefers-color-scheme)`,
15
15
  });
16
+
17
+ export const meta = {
18
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/media-prefers-color-scheme/README.md',
19
+ fixable: false,
20
+ deprecated: false,
21
+ };
16
22
  const targetProperties = ['background-color', 'color'];
17
23
 
18
24
  function check(selector, node) {
@@ -2,7 +2,9 @@ import isCustomSelector from 'stylelint/lib/utils/isCustomSelector.mjs';
2
2
  import isStandardSyntaxAtRule from 'stylelint/lib/utils/isStandardSyntaxAtRule.mjs';
3
3
  import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule.mjs';
4
4
  import isStandardSyntaxSelector from 'stylelint/lib/utils/isStandardSyntaxSelector.mjs';
5
+
5
6
  import { parse } from 'postcss';
7
+
6
8
  import stylelint from 'stylelint';
7
9
  const {
8
10
  utils: { report, ruleMessages, validateOptions },
@@ -13,6 +15,12 @@ export const ruleName = 'a11y/media-prefers-reduced-motion';
13
15
  export const messages = ruleMessages(ruleName, {
14
16
  expected: (selector) => `Expected ${selector} is used with @media (prefers-reduced-motion)`,
15
17
  });
18
+
19
+ export const meta = {
20
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/media-prefers-reduced-motion/README.md',
21
+ fixable: true,
22
+ deprecated: false,
23
+ };
16
24
  const targetProperties = ['transition', 'animation', 'animation-name'];
17
25
 
18
26
  function checkChildrenNodes(childrenNodes, currentSelector, parentNode) {
@@ -62,6 +70,30 @@ function check(selector, node) {
62
70
 
63
71
  if (!declarationsIsMatched) return true;
64
72
 
73
+ // Check if there's a nested media query with prefers-reduced-motion inside the current rule
74
+ const hasNestedPrefersReducedMotion = declarations.some((childNode) => {
75
+ if (childNode.type === 'atrule' && childNode.name === 'media') {
76
+ return childNode.params && childNode.params.indexOf('prefers-reduced-motion') >= 0;
77
+ }
78
+
79
+ return false;
80
+ });
81
+
82
+ if (hasNestedPrefersReducedMotion) return true;
83
+
84
+ // Check if there's a sibling media query with prefers-reduced-motion at the same level
85
+ if (node.parent && node.parent.type === 'atrule' && node.parent.name === 'media') {
86
+ const siblingHasPrefersReducedMotion = node.parent.nodes.some((siblingNode) => {
87
+ if (siblingNode.type === 'atrule' && siblingNode.name === 'media') {
88
+ return siblingNode.params && siblingNode.params.indexOf('prefers-reduced-motion') >= 0;
89
+ }
90
+
91
+ return false;
92
+ });
93
+
94
+ if (siblingHasPrefersReducedMotion) return true;
95
+ }
96
+
65
97
  if (declarationsIsMatched) {
66
98
  const parentMatchedNode = parentNodes.some((parentNode) => {
67
99
  if (!parentNode || !parentNode.nodes) return false;
@@ -143,25 +175,62 @@ export default function mediaPrefersReducedMotion(actual, _, context) {
143
175
  media.nodes.forEach((o) => {
144
176
  o.raws.after = '\n';
145
177
  });
146
- const cloneRule = node.clone();
147
-
148
- cloneRule.raws = {
149
- ...cloneRule.raws,
150
- before: '\n',
151
- after: '\n',
152
- semicolon: true,
153
- };
154
- cloneRule.nodes.forEach((o) => {
155
- if (o.prop === 'animation-name') {
156
- o.prop = 'animation';
157
- }
158
-
159
- if (targetProperties.indexOf(o.prop) >= 0) {
160
- o.value = 'none';
161
- }
162
- });
163
- media.first.append(cloneRule);
164
- node.before(media);
178
+
179
+ // Check if we're already inside a media query
180
+ if (node.parent && node.parent.type === 'atrule' && node.parent.name === 'media') {
181
+ // Create a clone with only the animation/transition properties set to none
182
+ const cloneRule = node.clone();
183
+
184
+ cloneRule.nodes = cloneRule.nodes.filter((o) => {
185
+ if (o.prop === 'animation-name') {
186
+ o.prop = 'animation';
187
+ o.value = 'none';
188
+
189
+ return true;
190
+ }
191
+
192
+ if (targetProperties.indexOf(o.prop) >= 0) {
193
+ o.value = 'none';
194
+
195
+ return true;
196
+ }
197
+
198
+ return false;
199
+ });
200
+
201
+ cloneRule.raws = {
202
+ ...cloneRule.raws,
203
+ before: '\n',
204
+ after: '\n',
205
+ semicolon: true,
206
+ };
207
+
208
+ media.first.append(cloneRule);
209
+ // Insert the nested media query inside the current rule
210
+ node.append(media.first);
211
+ } else {
212
+ // Original logic for non-nested case
213
+ const cloneRule = node.clone();
214
+
215
+ cloneRule.raws = {
216
+ ...cloneRule.raws,
217
+ before: '\n',
218
+ after: '\n',
219
+ semicolon: true,
220
+ };
221
+ cloneRule.nodes.forEach((o) => {
222
+ if (o.prop === 'animation-name') {
223
+ o.prop = 'animation';
224
+ }
225
+
226
+ if (targetProperties.indexOf(o.prop) >= 0) {
227
+ o.value = 'none';
228
+ }
229
+ });
230
+ media.first.append(cloneRule);
231
+ // Insert the media query before the current node
232
+ node.before(media);
233
+ }
165
234
 
166
235
  return;
167
236
  }
@@ -7,9 +7,15 @@ const {
7
7
  export const ruleName = 'a11y/no-display-none';
8
8
 
9
9
  export const messages = ruleMessages(ruleName, {
10
- expected: (selector) => `Unexpected using "{ display: none; }" in ${selector}`,
10
+ expected: (selector) => `Expected "display: none" to not be used in ${selector}`,
11
11
  });
12
12
 
13
+ export const meta = {
14
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/no-display-none/README.md',
15
+ fixable: false,
16
+ deprecated: false,
17
+ };
18
+
13
19
  function check(selector, node) {
14
20
  if (node.type !== 'rule') {
15
21
  return true;
@@ -1,5 +1,7 @@
1
1
  import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule.mjs';
2
+
2
3
  import { obsoleteAttributes } from './obsoleteAttributes.js';
4
+
3
5
  import stylelint from 'stylelint';
4
6
  const {
5
7
  utils: { report, ruleMessages, validateOptions },
@@ -8,9 +10,15 @@ const {
8
10
  export const ruleName = 'a11y/no-obsolete-attribute';
9
11
 
10
12
  export const messages = ruleMessages(ruleName, {
11
- expected: (selector) => `Unexpected using obsolete attribute "${selector}"`,
13
+ expected: (selector) => `Expected obsolete attribute "${selector}" to not be used`,
12
14
  });
13
15
 
16
+ export const meta = {
17
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/no-obsolete-attribute/README.md',
18
+ fixable: false,
19
+ deprecated: false,
20
+ };
21
+
14
22
  function check(selector, node) {
15
23
  if (node.type !== 'rule') {
16
24
  return true;
@@ -1,5 +1,7 @@
1
1
  import isStandardSyntaxRule from 'stylelint/lib/utils/isStandardSyntaxRule.mjs';
2
+
2
3
  import { obsoleteElements } from './obsoleteElements.js';
4
+
3
5
  import stylelint from 'stylelint';
4
6
  const {
5
7
  utils: { report, ruleMessages, validateOptions },
@@ -8,9 +10,15 @@ const {
8
10
  export const ruleName = 'a11y/no-obsolete-element';
9
11
 
10
12
  export const messages = ruleMessages(ruleName, {
11
- expected: (selector) => `Unexpected using obsolete selector "${selector}"`,
13
+ expected: (selector) => `Expected obsolete selector "${selector}" to not be used`,
12
14
  });
13
15
 
16
+ export const meta = {
17
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/no-obsolete-element/README.md',
18
+ fixable: false,
19
+ deprecated: false,
20
+ };
21
+
14
22
  function check(selector, node) {
15
23
  if (node.type !== 'rule') {
16
24
  return true;
@@ -7,9 +7,15 @@ const {
7
7
  export const ruleName = 'a11y/no-outline-none';
8
8
 
9
9
  export const messages = ruleMessages(ruleName, {
10
- expected: (selector) => `Unexpected using "outline" property in ${selector}`,
10
+ expected: (selector) => `Expected "outline" to not be removed without alternative in ${selector}`,
11
11
  });
12
12
 
13
+ export const meta = {
14
+ url: 'https://github.com/double-great/stylelint-a11y/blob/main/src/rules/no-outline-none/README.md',
15
+ fixable: false,
16
+ deprecated: false,
17
+ };
18
+
13
19
  function check(selector, node) {
14
20
  if (node.type !== 'rule') {
15
21
  return true;
@@ -13,6 +13,25 @@ Require width of text greater than 45 characters and less than 80 characters.
13
13
 
14
14
  ### true
15
15
 
16
+ The rule is enabled with default values (45ch minimum, 80ch maximum).
17
+
18
+ ### { minWidth: number, maxWidth: number }
19
+
20
+ - `minWidth` (default: `45`): Minimum allowed width in `ch` units
21
+ - `maxWidth` (default: `80`): Maximum allowed width in `ch` units
22
+
23
+ #### Example configuration
24
+
25
+ ```javascript
26
+ {
27
+ "a11y/no-spread-text": [true, { "minWidth": 30, "maxWidth": 60 }]
28
+ }
29
+ ```
30
+
31
+ ### Examples
32
+
33
+ #### ✓ Default configuration (`true`)
34
+
16
35
  The following pattern is considered a violation:
17
36
 
18
37
  ```css