postcss-sort-media-queries 6.3.3 → 6.4.4
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 +88 -490
- package/package.json +6 -5
- package/src/index.js +137 -137
package/README.md
CHANGED
|
@@ -14,494 +14,33 @@
|
|
|
14
14
|
|
|
15
15
|
🌏 **English** ▫ [**O'zbek**](README-UZ.md)
|
|
16
16
|
|
|
17
|
-
[PostCSS] plugin for sorting and combining CSS media queries
|
|
17
|
+
**PostcSS Sort Qedia Queries** is a powerful and flexible [PostCSS] plugin for sorting and combining CSS media queries using **mobile-first** or **desktop-first** methodologies. It helps maintain a clean, predictable stylesheet structure, improves readability, and prevents unexpected style overrides.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
A key advantage of this plugin is its **full support for nested media queries**, ensuring correct processing and ordering even in complex, deeply structured stylesheets. This makes it perfectly suited for modern CSS workflows that rely on nesting via PostCSS or preprocessor-like patterns.
|
|
20
|
+
|
|
21
|
+
In addition, the plugin fully supports **CSS Media Queries Level 4**, including modern **range syntax**.
|
|
22
|
+
|
|
23
|
+
Sorting works based on [OlehDutchenko/sort-css-media-queries](https://github.com/OlehDutchenko/sort-css-media-queries).
|
|
20
24
|
|
|
21
25
|
## Table of Contents
|
|
22
26
|
|
|
23
|
-
- [Online demo](#online-demo)
|
|
24
|
-
- [Examples](#examples)
|
|
25
|
-
- [Mobile first sorting](#mobile-first-sorting)
|
|
26
|
-
- [Desktop first sorting](#desktop-first-sorting)
|
|
27
|
-
- [Nested media queries sorting](#nested-media-queries-sorting)
|
|
28
27
|
- [Install](#install)
|
|
29
28
|
- [Usage](#usage)
|
|
30
29
|
- [Options](#options)
|
|
31
30
|
- [sort](#sort)
|
|
32
31
|
- [Custom sort function](#custom-sort-function)
|
|
33
32
|
- [Sort configuration](#sort-configuration)
|
|
33
|
+
- [Online demo](#online-demo)
|
|
34
|
+
- [Examples (with nested media queries)](#examples)
|
|
35
|
+
- [Sorting options](#mobile-first-sorting)
|
|
36
|
+
- [Desktop first sorting](#desktop-first-sorting)
|
|
34
37
|
- [Changelog](#changelog)
|
|
35
38
|
- [License](#license)
|
|
36
39
|
- [Other PostCSS plugins](#other-postcss-plugins)
|
|
37
40
|
- [Thanks 💪](#thanks)
|
|
38
41
|
|
|
39
|
-
## Online demo
|
|
40
|
-
|
|
41
|
-
And here is the [Online Demo]
|
|
42
|
-
|
|
43
|
-
## Examples
|
|
44
|
-
|
|
45
|
-
### Mobile first sorting
|
|
46
|
-
|
|
47
|
-
**Before**
|
|
48
|
-
|
|
49
|
-
```css
|
|
50
|
-
@media (min-width: 1400px) {}
|
|
51
|
-
@media (min-width: 1200px) {}
|
|
52
|
-
|
|
53
|
-
@layer reset {
|
|
54
|
-
|
|
55
|
-
@media (min-width: 1200px) {
|
|
56
|
-
@media (min-width: 992px) {}
|
|
57
|
-
@media (min-width: 768px) {}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
@media (min-width: 768px) {
|
|
61
|
-
@media (min-width: 640px) {}
|
|
62
|
-
@media (min-width: 320px) {}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**After**
|
|
68
|
-
|
|
69
|
-
```css
|
|
70
|
-
@layer reset {
|
|
71
|
-
|
|
72
|
-
@media (min-width: 768px) {
|
|
73
|
-
@media (min-width: 320px) {}
|
|
74
|
-
@media (min-width: 640px) {}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@media (min-width: 1200px) {
|
|
78
|
-
@media (min-width: 768px) {}
|
|
79
|
-
@media (min-width: 992px) {}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
@media (min-width: 1200px) {}
|
|
84
|
-
@media (min-width: 1400px) {}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Desktop first sorting
|
|
88
|
-
|
|
89
|
-
**Before**
|
|
90
|
-
```css
|
|
91
|
-
@media screen and (width < 640px) {
|
|
92
|
-
.header { color: #cdcdcd }
|
|
93
|
-
}
|
|
94
|
-
@media screen and (min-width: 760px) {
|
|
95
|
-
.desktop-first { color: #cdcdcd }
|
|
96
|
-
}
|
|
97
|
-
@media screen and (width < 640px) {
|
|
98
|
-
.main { color: #cdcdcd }
|
|
99
|
-
}
|
|
100
|
-
@media screen and (min-width: 1280px) {
|
|
101
|
-
.desktop-first { color: #cdcdcd }
|
|
102
|
-
}
|
|
103
|
-
@media screen and (max-width: 760px) {
|
|
104
|
-
.footer { color: #cdcdcd }
|
|
105
|
-
}
|
|
106
|
-
@media screen and (max-width: 640px) {
|
|
107
|
-
.footer { color: #cdcdcd }
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
**After**
|
|
112
|
-
|
|
113
|
-
```css
|
|
114
|
-
@media screen and (max-width: 760px) {
|
|
115
|
-
.footer { color: #cdcdcd }
|
|
116
|
-
}
|
|
117
|
-
@media screen and (width < 640px) {
|
|
118
|
-
/* combined */
|
|
119
|
-
.header { color: #cdcdcd }
|
|
120
|
-
.main { color: #cdcdcd }
|
|
121
|
-
.footer { color: #cdcdcd }
|
|
122
|
-
}
|
|
123
|
-
@media screen and (min-width: 760px) {
|
|
124
|
-
.desktop-first { color: #cdcdcd }
|
|
125
|
-
}
|
|
126
|
-
@media screen and (min-width: 1280px) {
|
|
127
|
-
.desktop-first { color: #cdcdcd }
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### Nested media queries sorting
|
|
132
|
-
|
|
133
|
-
**Before**
|
|
134
|
-
|
|
135
|
-
```css
|
|
136
|
-
@media (min-width: 710px) {
|
|
137
|
-
.print-only-global-2 {
|
|
138
|
-
display: block;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
@media (min-width: 1210px) {
|
|
143
|
-
.print-only-global-4 {
|
|
144
|
-
display: block;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.print-only-global-5 {
|
|
148
|
-
display: block;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.print-only-global-6 {
|
|
152
|
-
display: block;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
@media (min-width: 310px) {
|
|
157
|
-
.print-only-global-1 {
|
|
158
|
-
display: block;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
@media (min-width: 710px) {
|
|
163
|
-
.print-only-global-3 {
|
|
164
|
-
display: block;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
@media (min-width: 1210px) {
|
|
169
|
-
.print-only-global-7 {
|
|
170
|
-
display: block;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
@media print {
|
|
175
|
-
.print-only {
|
|
176
|
-
display: block;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.print-only-parent-1 {
|
|
180
|
-
display: block;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
@media (orientation: landscape) {
|
|
184
|
-
.print-only {
|
|
185
|
-
color: black;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
@media (min-width: 910px) {
|
|
189
|
-
.nested-landscape-3 {
|
|
190
|
-
color: black;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.nested-landscape-4 {
|
|
194
|
-
color: black;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.nested-landscape-5 {
|
|
198
|
-
color: black;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
@media (min-width: 810px) {
|
|
203
|
-
.nested-landscape-2 {
|
|
204
|
-
color: black;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
@media (min-width: 710px) {
|
|
209
|
-
.nested-landscape-1 {
|
|
210
|
-
color: black;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
.nested-landscape-0 {
|
|
215
|
-
color: black;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
@media (min-width: 710px) {
|
|
219
|
-
.nested-landscape-4 {
|
|
220
|
-
color: black;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.nested-landscape-5 {
|
|
224
|
-
color: black;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.nested-landscape-6 {
|
|
228
|
-
color: black;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
@media (min-width: 710px) {
|
|
233
|
-
.nested-landscape-8 {
|
|
234
|
-
color: black;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.nested-landscape-9 {
|
|
238
|
-
color: black;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
@media (min-width: 500px) {
|
|
244
|
-
.print-only {
|
|
245
|
-
color: black;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
.print-only-parent-2 {
|
|
250
|
-
display: block;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
@media (min-width: 500px) {
|
|
254
|
-
.print-only {
|
|
255
|
-
color: black;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
@media (orientation: portrait) {
|
|
260
|
-
.print-only {
|
|
261
|
-
color: gray;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.print-only-parent-3 {
|
|
266
|
-
display: block;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
@media (min-width: 320px) {
|
|
270
|
-
.print-only {
|
|
271
|
-
color: black;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
@media (orientation: landscape) {
|
|
276
|
-
.print-only-landscape-1-1 {
|
|
277
|
-
color: gray;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.print-only-landscape-1-2 {
|
|
281
|
-
color: gray;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.print-only-landscape-1-3 {
|
|
285
|
-
color: gray;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
@layer base {
|
|
291
|
-
|
|
292
|
-
@media (min-width: 1220px) {
|
|
293
|
-
.print-only-1200-1 {
|
|
294
|
-
color: black;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
@media (min-width: 320px) {
|
|
299
|
-
.print-only-320-1 {
|
|
300
|
-
color: black;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
@media (min-width: 1220px) {
|
|
305
|
-
.print-only-1200-2 {
|
|
306
|
-
color: black;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
@media (min-width: 620px) {
|
|
311
|
-
.print-only-640-1 {
|
|
312
|
-
color: black;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
@media (min-width: 320px) {
|
|
317
|
-
.print-only-320-2 {
|
|
318
|
-
color: black;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
@media (min-width: 320px) {
|
|
323
|
-
.print-only-320-3 {
|
|
324
|
-
color: black;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
@media (min-width: 620px) {
|
|
329
|
-
.print-only-640-2 {
|
|
330
|
-
color: black;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
**After**
|
|
337
|
-
|
|
338
|
-
```css
|
|
339
|
-
@layer base {
|
|
340
|
-
@media (min-width: 320px) {
|
|
341
|
-
.print-only-320-1 {
|
|
342
|
-
color: black;
|
|
343
|
-
}
|
|
344
|
-
.print-only-320-2 {
|
|
345
|
-
color: black;
|
|
346
|
-
}
|
|
347
|
-
.print-only-320-3 {
|
|
348
|
-
color: black;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
@media (min-width: 620px) {
|
|
352
|
-
.print-only-640-1 {
|
|
353
|
-
color: black;
|
|
354
|
-
}
|
|
355
|
-
.print-only-640-2 {
|
|
356
|
-
color: black;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
@media (min-width: 1220px) {
|
|
360
|
-
.print-only-1200-1 {
|
|
361
|
-
color: black;
|
|
362
|
-
}
|
|
363
|
-
.print-only-1200-2 {
|
|
364
|
-
color: black;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
@media (min-width: 310px) {
|
|
369
|
-
.print-only-global-1 {
|
|
370
|
-
display: block;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
@media (min-width: 710px) {
|
|
374
|
-
.print-only-global-2 {
|
|
375
|
-
display: block;
|
|
376
|
-
}
|
|
377
|
-
.print-only-global-3 {
|
|
378
|
-
display: block;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
@media (min-width: 1210px) {
|
|
382
|
-
.print-only-global-4 {
|
|
383
|
-
display: block;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
.print-only-global-5 {
|
|
387
|
-
display: block;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
.print-only-global-6 {
|
|
391
|
-
display: block;
|
|
392
|
-
}
|
|
393
|
-
.print-only-global-7 {
|
|
394
|
-
display: block;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
@media print {
|
|
398
|
-
.print-only {
|
|
399
|
-
display: block;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
.print-only-parent-1 {
|
|
403
|
-
display: block;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
.print-only-parent-2 {
|
|
407
|
-
display: block;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
.print-only-parent-3 {
|
|
411
|
-
display: block;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
@media (min-width: 320px) {
|
|
415
|
-
.print-only {
|
|
416
|
-
color: black;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
@media (min-width: 500px) {
|
|
421
|
-
.print-only {
|
|
422
|
-
color: black;
|
|
423
|
-
}
|
|
424
|
-
.print-only {
|
|
425
|
-
color: black;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
@media (orientation: landscape) {
|
|
430
|
-
.print-only {
|
|
431
|
-
color: black;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
.nested-landscape-0 {
|
|
435
|
-
color: black;
|
|
436
|
-
}
|
|
437
|
-
.print-only-landscape-1-1 {
|
|
438
|
-
color: gray;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
.print-only-landscape-1-2 {
|
|
442
|
-
color: gray;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
.print-only-landscape-1-3 {
|
|
446
|
-
color: gray;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
@media (min-width: 710px) {
|
|
450
|
-
.nested-landscape-1 {
|
|
451
|
-
color: black;
|
|
452
|
-
}
|
|
453
|
-
.nested-landscape-4 {
|
|
454
|
-
color: black;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
.nested-landscape-5 {
|
|
458
|
-
color: black;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
.nested-landscape-6 {
|
|
462
|
-
color: black;
|
|
463
|
-
}
|
|
464
|
-
.nested-landscape-8 {
|
|
465
|
-
color: black;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
.nested-landscape-9 {
|
|
469
|
-
color: black;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
@media (min-width: 810px) {
|
|
474
|
-
.nested-landscape-2 {
|
|
475
|
-
color: black;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
@media (min-width: 910px) {
|
|
480
|
-
.nested-landscape-3 {
|
|
481
|
-
color: black;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
.nested-landscape-4 {
|
|
485
|
-
color: black;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
.nested-landscape-5 {
|
|
489
|
-
color: black;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
@media (orientation: portrait) {
|
|
495
|
-
.print-only {
|
|
496
|
-
color: gray;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
```
|
|
501
|
-
|
|
502
42
|
## Install
|
|
503
43
|
|
|
504
|
-
First thing's, install the module:
|
|
505
44
|
|
|
506
45
|
```
|
|
507
46
|
npm install postcss postcss-sort-media-queries --save-dev
|
|
@@ -528,9 +67,8 @@ If you already use PostCSS, add the plugin to plugins list:
|
|
|
528
67
|
module.exports = {
|
|
529
68
|
plugins: [
|
|
530
69
|
+ require('postcss-sort-media-queries')({
|
|
531
|
-
+ sort: 'mobile-first'
|
|
70
|
+
+ sort: 'mobile-first' | 'desktop-first' | function // default ('mobile-first')
|
|
532
71
|
+ }),
|
|
533
|
-
require('autoprefixer')
|
|
534
72
|
]
|
|
535
73
|
}
|
|
536
74
|
```
|
|
@@ -544,7 +82,6 @@ module.exports = {
|
|
|
544
82
|
+ // custom sorting function
|
|
545
83
|
+ }
|
|
546
84
|
+ }),
|
|
547
|
-
require('autoprefixer')
|
|
548
85
|
]
|
|
549
86
|
}
|
|
550
87
|
```
|
|
@@ -554,9 +91,9 @@ and set this plugin in settings.
|
|
|
554
91
|
|
|
555
92
|
## Options
|
|
556
93
|
|
|
557
|
-
> Sorting works based on [OlehDutchenko/sort-css-media-queries](https://github.com/OlehDutchenko/sort-css-media-queries)
|
|
94
|
+
> Sorting works based on [OlehDutchenko/sort-css-media-queries](https://github.com/OlehDutchenko/sort-css-media-queries)
|
|
558
95
|
|
|
559
|
-
###
|
|
96
|
+
### Sort
|
|
560
97
|
|
|
561
98
|
This option support **string** or **function** values.
|
|
562
99
|
|
|
@@ -564,26 +101,15 @@ This option support **string** or **function** values.
|
|
|
564
101
|
- `{string}` `'desktop-first'` - desktop first sorting
|
|
565
102
|
- `{function}` your own sorting function
|
|
566
103
|
|
|
567
|
-
#### `'mobile-first'`
|
|
568
|
-
|
|
569
|
-
```js
|
|
570
|
-
postcss([
|
|
571
|
-
sortMediaQueries({
|
|
572
|
-
sort: 'mobile-first' // default
|
|
573
|
-
})
|
|
574
|
-
]).process(css);
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
#### `'desktop-first'`
|
|
104
|
+
#### `'mobile-first'` or `'desktop-first'`
|
|
578
105
|
|
|
579
106
|
```js
|
|
580
107
|
postcss([
|
|
581
108
|
sortMediaQueries({
|
|
582
|
-
sort: 'desktop-first'
|
|
109
|
+
sort: 'mobile-first' | 'desktop-first' // default (mobile-first)
|
|
583
110
|
})
|
|
584
111
|
]).process(css);
|
|
585
112
|
```
|
|
586
|
-
|
|
587
113
|
### Custom sort function
|
|
588
114
|
```js
|
|
589
115
|
postcss([
|
|
@@ -615,6 +141,78 @@ postcss([
|
|
|
615
141
|
|
|
616
142
|
Or alternatively create a `sort-css-mq.config.json` file in the root of your project. Or add property `sortCssMQ: {}` in your `package.json`.
|
|
617
143
|
|
|
144
|
+
## Online demo
|
|
145
|
+
|
|
146
|
+
And here is the [Online Demo]
|
|
147
|
+
|
|
148
|
+
## Examples
|
|
149
|
+
|
|
150
|
+
### Mobile first sorting (with nested media queries)
|
|
151
|
+
|
|
152
|
+
#### Before
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
root
|
|
156
|
+
│
|
|
157
|
+
├── (min-width: 1400px)
|
|
158
|
+
├── (min-width: 1200px)
|
|
159
|
+
│
|
|
160
|
+
└── @layer reset
|
|
161
|
+
│
|
|
162
|
+
├── (min-width: 1200px)
|
|
163
|
+
│ ├── (min-width: 992px)
|
|
164
|
+
│ └── (min-width: 768px)
|
|
165
|
+
│
|
|
166
|
+
└── (min-width: 768px)
|
|
167
|
+
├── (min-width: 640px)
|
|
168
|
+
└── (min-width: 320px)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### After
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
root
|
|
175
|
+
│
|
|
176
|
+
├── @layer reset
|
|
177
|
+
│ │
|
|
178
|
+
│ ├── (min-width: 768px)
|
|
179
|
+
│ │ ├── (min-width: 320px)
|
|
180
|
+
│ │ └── (min-width: 640px)
|
|
181
|
+
│ │
|
|
182
|
+
│ └── (min-width: 1200px)
|
|
183
|
+
│ ├── (min-width: 768px)
|
|
184
|
+
│ └── (min-width: 992px)
|
|
185
|
+
│
|
|
186
|
+
├── (min-width: 1200px)
|
|
187
|
+
└── (min-width: 1400px)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Desktop first sorting
|
|
191
|
+
|
|
192
|
+
**Before**
|
|
193
|
+
```css
|
|
194
|
+
root
|
|
195
|
+
│
|
|
196
|
+
├── (width < 640px)
|
|
197
|
+
├── (min-width: 760px)
|
|
198
|
+
├── (width < 640px)
|
|
199
|
+
├── (min-width: 1280px)
|
|
200
|
+
├── (max-width: 760px)
|
|
201
|
+
└── (max-width: 640px)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**After**
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
root
|
|
208
|
+
│
|
|
209
|
+
├── (min-width: 760px)
|
|
210
|
+
├── (max-width: 640px)
|
|
211
|
+
├── (width < 640px)
|
|
212
|
+
├── (max-width: 760px)
|
|
213
|
+
└── (min-width: 1280px)
|
|
214
|
+
```
|
|
215
|
+
|
|
618
216
|
---
|
|
619
217
|
|
|
620
218
|
## Changelog
|
|
@@ -627,7 +225,7 @@ See [Releases history]
|
|
|
627
225
|
|
|
628
226
|
## Other PostCSS plugins
|
|
629
227
|
|
|
630
|
-
- [`postcss-momentum-scrolling`](https://github.com/solversgroup/postcss-momentum-scrolling) - plugin for adding **momentum** style scrolling behavior (`-webkit-overflow-scrolling:touch`) for elements with overflow (scroll, auto) on iOS
|
|
228
|
+
- [`postcss-momentum-scrolling`](https://github.com/solversgroup/postcss-momentum-scrolling) - plugin for adding **momentum** style scrolling behavior (`-webkit-overflow-scrolling:touch`) for elements with overflow (scroll, auto) on iOS (**deprecated for modern Safari**)
|
|
631
229
|
|
|
632
230
|
## Thanks
|
|
633
231
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-sort-media-queries",
|
|
3
3
|
"description": "PostCSS plugin for sorting and combining CSS media queries with mobile first / **desktop first methodologies",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.4.4",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0"
|
|
7
7
|
},
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"module": "src/index.js",
|
|
38
38
|
"exports": {
|
|
39
39
|
".": {
|
|
40
|
+
"types": "./src/index.d.ts",
|
|
40
41
|
"require": "./build/wrapper.cjs",
|
|
41
42
|
"import": "./src/index.js",
|
|
42
43
|
"default": "./src/index.js"
|
|
@@ -56,13 +57,13 @@
|
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@eslint/js": "^10.0.1",
|
|
59
|
-
"@vitest/coverage-v8": "^4.1.
|
|
60
|
-
"esbuild": "^0.
|
|
61
|
-
"eslint": "^10.
|
|
60
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
61
|
+
"esbuild": "^0.28.0",
|
|
62
|
+
"eslint": "^10.2.0",
|
|
62
63
|
"globals": "^17.4.0",
|
|
63
64
|
"husky": "^9.1.7",
|
|
64
65
|
"prettier": "3.8.1",
|
|
65
|
-
"vitest": "^4.1.
|
|
66
|
+
"vitest": "^4.1.2"
|
|
66
67
|
},
|
|
67
68
|
"peerDependencies": {
|
|
68
69
|
"nanoid": "^5.1.6",
|
package/src/index.js
CHANGED
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
import createSort from 'sort-css-media-queries/create-sort';
|
|
2
|
-
import { nanoid } from 'nanoid';
|
|
3
|
-
|
|
4
|
-
// PostCSS plugin to sort CSS @media rules according to a configurable order.
|
|
5
|
-
// The plugin groups top-level and nested media at-rules, merges rules
|
|
6
|
-
// with identical queries, and re-inserts them in the desired order.
|
|
7
|
-
|
|
8
|
-
// Helper that ensures `options.sort` is a function and sorts queries.
|
|
9
|
-
function sortAtRules(queries, options, sortCSSmq) {
|
|
10
|
-
if (typeof options.sort !== 'function') {
|
|
11
|
-
options.sort = options.sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return queries.sort(options.sort);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getDepth(node) {
|
|
18
|
-
let depth = 0;
|
|
19
|
-
|
|
20
|
-
for (let p = node.parent; p; p = p.parent) {
|
|
21
|
-
depth++;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return depth;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function plugin(options = {}) {
|
|
28
|
-
|
|
29
|
-
// Set default options and allow user overrides
|
|
30
|
-
options = Object.assign(
|
|
31
|
-
{
|
|
32
|
-
sort: 'mobile-first',
|
|
33
|
-
configuration: false,
|
|
34
|
-
},
|
|
35
|
-
options
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// Create a sorter based on configuration (from sort-css-media-queries)
|
|
39
|
-
const sortCSSmq = createSort(options.configuration);
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
postcssPlugin: 'postcss-sort-media-queries',
|
|
43
|
-
|
|
44
|
-
// Execute once after the entire tree has been parsed
|
|
45
|
-
OnceExit(root, { AtRule }) {
|
|
46
|
-
// Collect parent nodes that contain media at-rules. We separate
|
|
47
|
-
// top-level (`root`) parents from nested parents so ordering
|
|
48
|
-
// semantics can be preserved independently.
|
|
49
|
-
let parents = [];
|
|
50
|
-
|
|
51
|
-
// Walk all @media at-rules and group their parents
|
|
52
|
-
root.walkAtRules('media', (atRule) => {
|
|
53
|
-
if (!atRule.parent.groupId) {
|
|
54
|
-
let groupId = nanoid();
|
|
55
|
-
|
|
56
|
-
atRule.parent.groupId = groupId;
|
|
57
|
-
|
|
58
|
-
parents[groupId] = {
|
|
59
|
-
parent: atRule.parent,
|
|
60
|
-
depth: getDepth(atRule.parent),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return;
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
if (!parents) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
parents = Object.fromEntries(
|
|
72
|
-
Object.entries(parents).sort(([, a], [, b]) => {
|
|
73
|
-
return b.depth - a.depth;
|
|
74
|
-
})
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
Object.keys(parents).forEach((groupId) => {
|
|
78
|
-
let { parent } = parents[groupId];
|
|
79
|
-
|
|
80
|
-
// Filter only @media nodes from the parent's children
|
|
81
|
-
let medias = parent.nodes.filter(
|
|
82
|
-
(node) => node.type === 'atrule' && node.name === 'media'
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
if (!medias) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
let atRules = [];
|
|
90
|
-
|
|
91
|
-
medias.forEach((atRule) => {
|
|
92
|
-
if (!atRules[atRule.params]) {
|
|
93
|
-
atRules[atRule.params] = new AtRule({
|
|
94
|
-
name: atRule.name,
|
|
95
|
-
params: atRule.params,
|
|
96
|
-
source: atRule.source,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
[...atRule.nodes].forEach((node) => {
|
|
101
|
-
atRules[atRule.params].append(node);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Remove the original at-rule since its contents have been
|
|
105
|
-
// merged into `atRules[atRule.params]`.
|
|
106
|
-
atRule.remove();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Sort query keys and append merged at-rules back to the parent
|
|
110
|
-
if (atRules) {
|
|
111
|
-
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
112
|
-
parent.append(atRules[query]);
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
root.walkAtRules('media', (parent) => {
|
|
118
|
-
// Filter only @media nodes from the parent's children
|
|
119
|
-
let medias = parent.nodes.filter(
|
|
120
|
-
(node) => node.type === 'atrule' && node.name === 'media'
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
if (!medias) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
medias.forEach((atRule) => {
|
|
128
|
-
parent.append(atRule);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
plugin.postcss = true;
|
|
136
|
-
|
|
137
|
-
export default plugin;
|
|
1
|
+
import createSort from 'sort-css-media-queries/create-sort';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
3
|
+
|
|
4
|
+
// PostCSS plugin to sort CSS @media rules according to a configurable order.
|
|
5
|
+
// The plugin groups top-level and nested media at-rules, merges rules
|
|
6
|
+
// with identical queries, and re-inserts them in the desired order.
|
|
7
|
+
|
|
8
|
+
// Helper that ensures `options.sort` is a function and sorts queries.
|
|
9
|
+
function sortAtRules(queries, options, sortCSSmq) {
|
|
10
|
+
if (typeof options.sort !== 'function') {
|
|
11
|
+
options.sort = options.sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return queries.sort(options.sort);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getDepth(node) {
|
|
18
|
+
let depth = 0;
|
|
19
|
+
|
|
20
|
+
for (let p = node.parent; p; p = p.parent) {
|
|
21
|
+
depth++;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return depth;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function plugin(options = {}) {
|
|
28
|
+
|
|
29
|
+
// Set default options and allow user overrides
|
|
30
|
+
options = Object.assign(
|
|
31
|
+
{
|
|
32
|
+
sort: 'mobile-first',
|
|
33
|
+
configuration: false,
|
|
34
|
+
},
|
|
35
|
+
options
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Create a sorter based on configuration (from sort-css-media-queries)
|
|
39
|
+
const sortCSSmq = createSort(options.configuration);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
postcssPlugin: 'postcss-sort-media-queries',
|
|
43
|
+
|
|
44
|
+
// Execute once after the entire tree has been parsed
|
|
45
|
+
OnceExit(root, { AtRule }) {
|
|
46
|
+
// Collect parent nodes that contain media at-rules. We separate
|
|
47
|
+
// top-level (`root`) parents from nested parents so ordering
|
|
48
|
+
// semantics can be preserved independently.
|
|
49
|
+
let parents = [];
|
|
50
|
+
|
|
51
|
+
// Walk all @media at-rules and group their parents
|
|
52
|
+
root.walkAtRules('media', (atRule) => {
|
|
53
|
+
if (!atRule.parent.groupId) {
|
|
54
|
+
let groupId = nanoid();
|
|
55
|
+
|
|
56
|
+
atRule.parent.groupId = groupId;
|
|
57
|
+
|
|
58
|
+
parents[groupId] = {
|
|
59
|
+
parent: atRule.parent,
|
|
60
|
+
depth: getDepth(atRule.parent),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!parents) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
parents = Object.fromEntries(
|
|
72
|
+
Object.entries(parents).sort(([, a], [, b]) => {
|
|
73
|
+
return b.depth - a.depth;
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
Object.keys(parents).forEach((groupId) => {
|
|
78
|
+
let { parent } = parents[groupId];
|
|
79
|
+
|
|
80
|
+
// Filter only @media nodes from the parent's children
|
|
81
|
+
let medias = parent.nodes.filter(
|
|
82
|
+
(node) => node.type === 'atrule' && node.name === 'media'
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (!medias) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let atRules = [];
|
|
90
|
+
|
|
91
|
+
medias.forEach((atRule) => {
|
|
92
|
+
if (!atRules[atRule.params]) {
|
|
93
|
+
atRules[atRule.params] = new AtRule({
|
|
94
|
+
name: atRule.name,
|
|
95
|
+
params: atRule.params,
|
|
96
|
+
source: atRule.source,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
[...atRule.nodes].forEach((node) => {
|
|
101
|
+
atRules[atRule.params].append(node);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Remove the original at-rule since its contents have been
|
|
105
|
+
// merged into `atRules[atRule.params]`.
|
|
106
|
+
atRule.remove();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Sort query keys and append merged at-rules back to the parent
|
|
110
|
+
if (atRules) {
|
|
111
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
112
|
+
parent.append(atRules[query]);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
root.walkAtRules('media', (parent) => {
|
|
118
|
+
// Filter only @media nodes from the parent's children
|
|
119
|
+
let medias = parent.nodes.filter(
|
|
120
|
+
(node) => node.type === 'atrule' && node.name === 'media'
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!medias) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
medias.forEach((atRule) => {
|
|
128
|
+
parent.append(atRule);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
plugin.postcss = true;
|
|
136
|
+
|
|
137
|
+
export default plugin;
|