Skip to content

Commit 042050d

Browse files
jeddy3sidverma32ybiquitous
authored
Add media-feature-range-notation (#6497)
Co-authored-by: sidverma32 <sid.verma32@gmail.com> Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com>
1 parent 1916118 commit 042050d

File tree

7 files changed

+359
-8
lines changed

7 files changed

+359
-8
lines changed

.changeset/healthy-suns-yawn.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"stylelint": minor
3+
---
4+
5+
Added: `media-feature-range-notation` rule

docs/user-guide/rules.md

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ Enforce one representation of things that have multiple with these `notation` (s
217217
- [`hue-degree-notation`](../../lib/rules/hue-degree-notation/README.md): Specify number or angle notation for degree hues (Autofixable).
218218
- [`import-notation`](../../lib/rules/import-notation/README.md): Specify string or URL notation for `@import` rules (Autofixable).
219219
- [`keyframe-selector-notation`](../../lib/rules/keyframe-selector-notation/README.md): Specify keyword or percentage notation for keyframe selectors (Autofixable).
220+
- [`media-feature-range-notation`](../../lib/rules/media-feature-range-notation/README.md): Specify context or prefix notation for media feature ranges.
220221
- [`selector-not-notation`](../../lib/rules/selector-not-notation/README.md): Specify simple or complex notation for `:not()` pseudo-class selectors (Autofixable).
221222
- [`selector-pseudo-element-colon-notation`](../../lib/rules/selector-pseudo-element-colon-notation/README.md): Specify single or double colon notation for applicable pseudo-element selectors (Autofixable).
222223

lib/reference/mediaFeatures.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,24 @@ const deprecatedMediaFeatureNames = new Set([
1414
'min-device-width',
1515
]);
1616

17-
const mediaFeatureNames = uniteSets(deprecatedMediaFeatureNames, [
18-
'any-hover',
19-
'any-pointer',
17+
const rangeTypeMediaFeatureNames = new Set([
2018
'aspect-ratio',
19+
'color-index',
2120
'color',
21+
'height',
22+
'monochrome',
23+
'resolution',
24+
'width',
25+
]);
26+
27+
const mediaFeatureNames = uniteSets(deprecatedMediaFeatureNames, rangeTypeMediaFeatureNames, [
28+
'any-hover',
29+
'any-pointer',
2230
'color-gamut',
23-
'color-index',
2431
'display-mode',
2532
'dynamic-range',
2633
'forced-colors',
2734
'grid',
28-
'height',
2935
'hover',
3036
'inverted-colors',
3137
'light-level',
@@ -43,7 +49,6 @@ const mediaFeatureNames = uniteSets(deprecatedMediaFeatureNames, [
4349
'min-monochrome',
4450
'min-resolution',
4551
'min-width',
46-
'monochrome',
4752
'orientation',
4853
'overflow-block',
4954
'overflow-inline',
@@ -52,14 +57,13 @@ const mediaFeatureNames = uniteSets(deprecatedMediaFeatureNames, [
5257
'prefers-contrast',
5358
'prefers-reduced-motion',
5459
'prefers-reduced-transparency',
55-
'resolution',
5660
'scan',
5761
'scripting',
5862
'update',
5963
'video-dynamic-range',
60-
'width',
6164
]);
6265

6366
module.exports = {
67+
rangeTypeMediaFeatureNames,
6468
mediaFeatureNames,
6569
};

lib/rules/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ const rules = {
197197
'media-feature-parentheses-space-inside': importLazy(() =>
198198
require('./media-feature-parentheses-space-inside'),
199199
)(),
200+
'media-feature-range-notation': importLazy(() => require('./media-feature-range-notation'))(),
200201
'media-feature-range-operator-space-after': importLazy(() =>
201202
require('./media-feature-range-operator-space-after'),
202203
)(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# media-feature-range-notation
2+
3+
Specify context or prefix notation for media feature ranges.
4+
5+
<!-- prettier-ignore -->
6+
```css
7+
@media (width >= 600px) and (min-width: 600px) {}
8+
/** ↑ ↑
9+
* These media feature notations */
10+
```
11+
12+
Media features of the range type can be written using prefixes or the more modern context notation.
13+
14+
Because `min-` and `max-` both equate to range comparisons that include the value, they may be [limiting in certain situations](https://drafts.csswg.org/mediaqueries/#mq-min-max).
15+
16+
## Options
17+
18+
`string`: `"context"|"prefix"`
19+
20+
### `"context"`
21+
22+
Media feature ranges _must always_ use context notation.
23+
24+
The following patterns are considered problems:
25+
26+
<!-- prettier-ignore -->
27+
```css
28+
@media (min-width: 1px) {}
29+
```
30+
31+
<!-- prettier-ignore -->
32+
```css
33+
@media (min-width: 1px) and (max-width: 2px) {}
34+
```
35+
36+
The following patterns are _not_ considered problems:
37+
38+
<!-- prettier-ignore -->
39+
```css
40+
@media (width >= 1px) {}
41+
```
42+
43+
<!-- prettier-ignore -->
44+
```css
45+
@media (1px <= width <= 2px) {}
46+
```
47+
48+
### `"prefix"`
49+
50+
Media feature ranges _must always_ use prefix notation.
51+
52+
The following patterns are considered problems:
53+
54+
<!-- prettier-ignore -->
55+
```css
56+
@media (width >= 1px) {}
57+
```
58+
59+
<!-- prettier-ignore -->
60+
```css
61+
@media (1px <= width <= 2px) {}
62+
```
63+
64+
The following patterns are _not_ considered problems:
65+
66+
<!-- prettier-ignore -->
67+
```css
68+
@media (min-width: 1px) {}
69+
```
70+
71+
<!-- prettier-ignore -->
72+
```css
73+
@media (min-width: 1px) and (max-width: 2px) {}
74+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
'use strict';
2+
3+
const { stripIndent } = require('common-tags');
4+
5+
const { messages, ruleName } = require('..');
6+
7+
testRule({
8+
ruleName,
9+
config: ['context'],
10+
11+
accept: [
12+
{
13+
code: '@media {}',
14+
description: 'empty media query',
15+
},
16+
{
17+
code: '@media () {}',
18+
description: 'empty media feature',
19+
},
20+
{
21+
code: '@media screen {}',
22+
description: 'keyword',
23+
},
24+
{
25+
code: '@media (color) {}',
26+
description: 'range type media feature in boolean context',
27+
},
28+
{
29+
code: '@media (color > 0) {}',
30+
description: 'range type media feature in non-boolean context',
31+
},
32+
{
33+
code: '@media (pointer: fine) {}',
34+
description: 'discrete type media feature',
35+
},
36+
{
37+
code: '@media (width >= 1px) {}',
38+
description: 'range type media feature in context notation',
39+
},
40+
{
41+
code: '@media screen and (width >= 1px) {}',
42+
description: 'range type media feature in context notation with keyword',
43+
},
44+
{
45+
code: '@media not print, (width >= 1px) {}',
46+
description: 'range type media feature in context notation in media query list',
47+
},
48+
{
49+
code: '@media (1px <= width <= 2px) {}',
50+
description: 'range type media feature in context notation with two values',
51+
},
52+
],
53+
54+
reject: [
55+
{
56+
code: '@media (min-width: 1px) {}',
57+
description: 'range type media feature in prefix notation',
58+
message: messages.expected('context'),
59+
line: 1,
60+
column: 8,
61+
endLine: 1,
62+
endColumn: 24,
63+
},
64+
{
65+
code: '@media screen and (min-width: 1px) {}',
66+
description: 'range type media feature in prefix notation with keyword',
67+
message: messages.expected('context'),
68+
line: 1,
69+
column: 19,
70+
endLine: 1,
71+
endColumn: 35,
72+
},
73+
{
74+
code: '@media not print, (min-width: 1px) {}',
75+
description: 'range type media feature in prefix notation in media query list',
76+
message: messages.expected('context'),
77+
line: 1,
78+
column: 19,
79+
endLine: 1,
80+
endColumn: 35,
81+
},
82+
{
83+
code: stripIndent`
84+
@media (min-width: 1px)
85+
and (max-width: 2px) {}
86+
`,
87+
description: 'two range type media features in prefix notation',
88+
warnings: [
89+
{ message: messages.expected('context'), line: 1, column: 8, endLine: 1, endColumn: 24 },
90+
{ message: messages.expected('context'), line: 2, column: 7, endLine: 2, endColumn: 23 },
91+
],
92+
},
93+
],
94+
});
95+
96+
testRule({
97+
ruleName,
98+
config: ['prefix'],
99+
100+
accept: [
101+
{
102+
code: '@media {}',
103+
description: 'empty media query',
104+
},
105+
{
106+
code: '@media () {}',
107+
description: 'empty media feature',
108+
},
109+
{
110+
code: '@media screen {}',
111+
description: 'keyword',
112+
},
113+
{
114+
code: '@media (color) {}',
115+
description: 'range type media feature in boolean context',
116+
},
117+
{
118+
code: '@media (min-color: 1) {}',
119+
description: 'range type media feature in non-boolean context',
120+
},
121+
{
122+
code: '@media (pointer: fine) {}',
123+
description: 'discrete type media query',
124+
},
125+
{
126+
code: '@media (min-width: 1px) {}',
127+
description: 'range type media feature in prefix notation',
128+
},
129+
{
130+
code: '@media screen and (min-width: 1px) {}',
131+
description: 'range type media feature in prefix notation with keyword',
132+
},
133+
{
134+
code: '@media not print, (min-width: 1px) {}',
135+
description: 'range type media feature in prefix notation in media query list',
136+
},
137+
],
138+
139+
reject: [
140+
{
141+
code: '@media (width >= 1px) {}',
142+
description: 'range type media feature in context notation',
143+
message: messages.expected('prefix'),
144+
line: 1,
145+
column: 8,
146+
endLine: 1,
147+
endColumn: 22,
148+
},
149+
{
150+
code: '@media screen and (width >= 1px) {}',
151+
description: 'range type media feature in context notation with keyword',
152+
message: messages.expected('prefix'),
153+
line: 1,
154+
column: 19,
155+
endLine: 1,
156+
endColumn: 33,
157+
},
158+
{
159+
code: '@media not print, (width >= 1px) {}',
160+
description: 'range type media feature in context notation in media query list',
161+
message: messages.expected('prefix'),
162+
line: 1,
163+
column: 19,
164+
endLine: 1,
165+
endColumn: 33,
166+
},
167+
{
168+
code: '@media (1px < width <= 2px) {}',
169+
description: 'range type media feature in context notation with two values',
170+
message: messages.expected('prefix'),
171+
line: 1,
172+
column: 8,
173+
endLine: 1,
174+
endColumn: 28,
175+
},
176+
],
177+
});

0 commit comments

Comments
 (0)