esm-styles 0.1.11 → 0.1.13

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.
@@ -0,0 +1,710 @@
1
+ # JS to CSS translations
2
+
3
+ JS object are translated to CSS this way:
4
+
5
+ ## T1
6
+
7
+ JS object keys that contain another object translate to CSS selectors, while keys pointing to an end value (like string or number), transalte to CSS properties:
8
+
9
+ `source.styles.js`
10
+
11
+ ```js
12
+ {
13
+ p: {
14
+ fontSize: '16px',
15
+ }
16
+ }
17
+ ```
18
+
19
+ `output.css`
20
+
21
+ ```css
22
+ p {
23
+ font-size: 16px;
24
+ }
25
+ ```
26
+
27
+ Key targeted to properties are converted automatically from `camelCase` to `kebab-case`.
28
+
29
+ ## T2
30
+
31
+ Nested js objects translate to flat CSS:
32
+
33
+ `source.styles.js`
34
+
35
+ ```js
36
+ {
37
+ p: {
38
+ fontSize: '16px',
39
+
40
+ a: {
41
+ color: 'red',
42
+ }
43
+ }
44
+ }
45
+
46
+ ```
47
+
48
+ `output.css`
49
+
50
+ ```css
51
+ p {
52
+ font-size: 16px;
53
+ }
54
+
55
+ p a {
56
+ color: red;
57
+ }
58
+ ```
59
+
60
+ `p` and `a` are recognized as standard HTML tag names.
61
+
62
+ Note: keys named as standard HTML tag that contain primitive values generate invalid CSS:
63
+
64
+ ```js
65
+ {
66
+ p: 'red',
67
+ }
68
+ ```
69
+
70
+ goes to
71
+
72
+ ```css
73
+ : {
74
+ p: red;
75
+ }
76
+ ```
77
+
78
+ ## T3
79
+
80
+ JS object keys with non-primitive values that were not recognized as standard HTML tag names convert to class selector:
81
+
82
+ `source.styles.js`
83
+
84
+ ```js
85
+ {
86
+ sticker: {
87
+ backgroundColor: 'yellow',
88
+ },
89
+
90
+ p: {
91
+ fontSize: '16px',
92
+
93
+ warning: {
94
+ color: 'red',
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ `output.css`
101
+
102
+ ```css
103
+ .sticker {
104
+ background-color: yellow;
105
+ }
106
+
107
+ p {
108
+ font-size: 16px;
109
+ }
110
+
111
+ p.warning {
112
+ color: red;
113
+ }
114
+ ```
115
+
116
+ ## T4
117
+
118
+ JS object keys can contain pseudo-classes, vendor prefixes, ids and attributes:
119
+
120
+ `source.styles.js`
121
+
122
+ ```js
123
+ {
124
+ a: {
125
+ color: 'blue',
126
+
127
+ ':hover': {
128
+ color: 'red',
129
+ }
130
+ },
131
+
132
+ ul: {
133
+ '-ms-overflow-style': 'none',
134
+ scrollbarWidth: 'none',
135
+ '::-webkit-scrollbar': {
136
+ display: 'none',
137
+ },
138
+ },
139
+
140
+ input: {
141
+ '#id1': { font: `normal 16px/1.5 'Helvetica Neue', sans-serif` },
142
+ '[type=text]': {
143
+ '::placeholder': {
144
+ color: 'red',
145
+ },
146
+ },
147
+ }
148
+ }
149
+ ```
150
+
151
+ `output.css`
152
+
153
+ ```css
154
+ a {
155
+ color: blue;
156
+ }
157
+
158
+ a:hover {
159
+ color: red;
160
+ }
161
+
162
+ ul {
163
+ -ms-overflow-style: none;
164
+ scrollbar-width: none;
165
+ }
166
+
167
+ ul::-webkit-scrollbar {
168
+ display: none;
169
+ }
170
+
171
+ input#id1 {
172
+ font: normal 16px/1.5 'Helvetica Neue', sans-serif;
173
+ }
174
+
175
+ input[type='text']::placeholder {
176
+ color: red;
177
+ }
178
+ ```
179
+
180
+ ## T5
181
+
182
+ Workaround when class name equals to tag name: put underscore before name.
183
+
184
+ `source.styles.js`
185
+
186
+ ```js
187
+ {
188
+ div: {
189
+ video: {
190
+ width: '100%',
191
+ height: '100%',
192
+ },
193
+ _video: {
194
+ padding: '10px',
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ `output.css`
201
+
202
+ ```css
203
+ div video {
204
+ width: 100%;
205
+ height: 100%;
206
+ }
207
+
208
+ div.video {
209
+ padding: 10px;
210
+ }
211
+ ```
212
+
213
+ (Generally, you should avoid using tag names as class names.)
214
+
215
+ ## T6
216
+
217
+ Double underscore is used as shorthand to target descendant of any level with class name:
218
+
219
+ `source.styles.js`
220
+
221
+ ```js
222
+ {
223
+ div: {
224
+ fontSize: '16px',
225
+
226
+ video: {
227
+ width: '100%',
228
+ height: '100%',
229
+ },
230
+
231
+ _video: {
232
+ borderRadius: '10px',
233
+ },
234
+
235
+ __video: {
236
+ fontStyle: 'italic',
237
+ }
238
+ }
239
+ }
240
+ ```
241
+
242
+ `output.css`
243
+
244
+ ```css
245
+ div {
246
+ font-size: 16px;
247
+ }
248
+
249
+ div video {
250
+ width: 100%;
251
+ height: 100%;
252
+ }
253
+
254
+ div.video {
255
+ border-radius: 10px;
256
+ }
257
+
258
+ div .video {
259
+ font-style: italic;
260
+ }
261
+ ```
262
+
263
+ Double underscore always suggests that the key is a class name, not a tag name.
264
+
265
+ ## T7
266
+
267
+ Key can be a complex selector:
268
+
269
+ `source.styles.js`
270
+
271
+ ```js
272
+ {
273
+ ul: {
274
+ '> li:not(:last-child) > img': {
275
+ filter: 'grayscale(100%)',
276
+ },
277
+ },
278
+ }
279
+ ```
280
+
281
+ `output.css`
282
+
283
+ ```css
284
+ ul > li:not(:last-child) > img {
285
+ filter: grayscale(100%);
286
+ }
287
+ ```
288
+
289
+ ## T8
290
+
291
+ To use style for multiple selectors, comma can be used:
292
+
293
+ `source.styles.js`
294
+
295
+ ```js
296
+ {
297
+ body: {
298
+ 'main, aside': {
299
+ 'input, textarea, button': {
300
+ outline: 'none',
301
+ },
302
+ },
303
+ }
304
+ }
305
+ ```
306
+
307
+ `output.css`
308
+
309
+ ```css
310
+ body main input,
311
+ body main textarea,
312
+ body main button,
313
+ body aside input,
314
+ body aside textarea,
315
+ body aside button {
316
+ outline: none;
317
+ }
318
+ ```
319
+
320
+ More complex and nested expressions are supported:
321
+
322
+ `source.styles.js`
323
+
324
+ ```js
325
+ {
326
+ body: {
327
+ 'main > p, aside > blockquote': {
328
+ fontWeight: 700,
329
+ ':hover': {
330
+ color: 'red',
331
+ },
332
+ },
333
+ },
334
+ }
335
+ ```
336
+
337
+ `output.css`
338
+
339
+ ```css
340
+ body main > p,
341
+ body aside > blockquote {
342
+ font-weight: 700;
343
+ }
344
+
345
+ body main > p:hover,
346
+ body aside > blockquote:hover {
347
+ color: red;
348
+ }
349
+ ```
350
+
351
+ ## T9
352
+
353
+ Content: if special character needed, use js unicode notation. It will be translated to CSS-compatible unicode (same goes for emoji):
354
+
355
+ `source.styles.js`
356
+
357
+ ```js
358
+ {
359
+ span: {
360
+ '::before': {
361
+ content: '👀',
362
+ },
363
+ '::after': {
364
+ content: '\u00a0', // non-breaking space
365
+ },
366
+ },
367
+ }
368
+ ```
369
+
370
+ `output.css`
371
+
372
+ ```css
373
+ span::before {
374
+ content: '\00d83d\00dc40';
375
+ }
376
+
377
+ span::after {
378
+ content: '\0000a0';
379
+ }
380
+ ```
381
+
382
+ ## T10
383
+
384
+ Media queries:
385
+
386
+ `source.styles.js`
387
+
388
+ ```js
389
+ {
390
+ p: {
391
+ fontSize: '1rem',
392
+
393
+ '@media screen and (max-width: 768px)': {
394
+ fontSize: `${14 / 16}rem`,
395
+ }
396
+ },
397
+
398
+ ul: {
399
+ marginBlock: '1em',
400
+
401
+ '> li': {
402
+ fontSize: '16px',
403
+
404
+ '@media screen and (max-width: 768px)': {
405
+ fontSize: '14px',
406
+ },
407
+ },
408
+ },
409
+ }
410
+ ```
411
+
412
+ `output.css`
413
+
414
+ ```css
415
+ p {
416
+ font-size: 1rem;
417
+ }
418
+
419
+ ul {
420
+ margin-block: 1em;
421
+ }
422
+
423
+ ul > li {
424
+ font-size: 16px;
425
+ }
426
+
427
+ @media screen and (max-width: 768px) {
428
+ p {
429
+ font-size: 0.875rem;
430
+ }
431
+ ul > li {
432
+ font-size: 14px;
433
+ }
434
+ }
435
+ ```
436
+
437
+ Media query parts are extracted from nested keys and collected in the end of the CSS file.
438
+
439
+ ## T11
440
+
441
+ Nested media queries also supported:
442
+
443
+ `source.styles.js`
444
+
445
+ ```js
446
+ {
447
+ p: {
448
+ fontSize: '1rem',
449
+
450
+ '@media screen and (max-width: 768px)': {
451
+ fontSize: `${14 / 16}rem`,
452
+
453
+ '@media screen and (orientation: portrait)': {
454
+ fontSize: `${12 / 16}rem`,
455
+ },
456
+ },
457
+ },
458
+ }
459
+ ```
460
+
461
+ `output.css`
462
+
463
+ ```css
464
+ @media screen and (max-width: 768px) {
465
+ p {
466
+ font-size: 0.875rem;
467
+ }
468
+ @media screen and (orientation: portrait) {
469
+ p {
470
+ font-size: 0.75rem;
471
+ }
472
+ }
473
+ }
474
+ ```
475
+
476
+ ## T12
477
+
478
+ Named media queries and media selectors.
479
+ Named media queries and selectors are described in `esm-styles.config.js` and should be used as `@media-name`:
480
+
481
+ `source.styles.js`
482
+
483
+ ```js
484
+ {
485
+ img: {
486
+ width: '100%',
487
+ '@dark': {
488
+ filter: 'brightness(0.8) saturate(0.8)',
489
+ },
490
+ },
491
+ div: {
492
+ fontFamily: 'var(--sans-serif)',
493
+ '@min-tablet': {
494
+ fontSize: '14px',
495
+ },
496
+ '@phone': {
497
+ fontSize: '12px',
498
+ },
499
+ },
500
+ }
501
+
502
+ ```
503
+
504
+ `output.css`
505
+
506
+ ```css
507
+ img {
508
+ width: 100%;
509
+ }
510
+
511
+ div {
512
+ font-family: var(--sans-serif);
513
+ }
514
+
515
+ @media (min-width: 500px) {
516
+ div {
517
+ font-size: 14px;
518
+ }
519
+ }
520
+
521
+ @media (max-width: 499px) {
522
+ div {
523
+ font-size: 12px;
524
+ }
525
+ }
526
+
527
+ :root.dark img {
528
+ filter: brightness(0.8) saturate(0.8);
529
+ }
530
+
531
+ @media screen and (prefers-color-scheme: dark) {
532
+ :root.auto img {
533
+ filter: brightness(0.8) saturate(0.8);
534
+ }
535
+ }
536
+ ```
537
+
538
+ Note: for the name `@dark` is also configured in `config.js` as "media prefix" to include same styles for both auto- and user-selected dark mode.
539
+
540
+ In the legacy code there is a bug which won't allow to use media prefixes inside named media queries.
541
+
542
+ This doesn't work:
543
+
544
+ `source.styles.js`
545
+
546
+ ```js
547
+ {
548
+ div: {
549
+ '@phone': {
550
+ fontWeight: 300,
551
+
552
+ '@dark': {
553
+ fontSize: '12px',
554
+ },
555
+ },
556
+ },
557
+ }
558
+ ```
559
+
560
+ `output.css`
561
+
562
+ ```css
563
+ @media (max-width: 499px) {
564
+ div {
565
+ font-weight: 300;
566
+ }
567
+ }
568
+
569
+ @media screen and (prefers-color-scheme: dark) {
570
+ }
571
+ ```
572
+
573
+ But this works as expected:
574
+
575
+ `source.styles.js`
576
+
577
+ ```js
578
+ {
579
+ div: {
580
+ '@dark': {
581
+ fontSize: '12px',
582
+
583
+ '@phone': {
584
+ fontWeight: 300,
585
+ },
586
+ },
587
+ },
588
+ }
589
+
590
+ ```
591
+
592
+ `output.css`
593
+
594
+ ```css
595
+ :root.dark div {
596
+ font-size: 12px;
597
+ }
598
+
599
+ @media (max-width: 499px) {
600
+ :root.dark div {
601
+ font-weight: 300;
602
+ }
603
+ }
604
+
605
+ @media screen and (prefers-color-scheme: dark) {
606
+ :root.auto div {
607
+ font-size: 12px;
608
+ }
609
+ @media (max-width: 499px) {
610
+ :root.auto div {
611
+ font-weight: 300;
612
+ }
613
+ }
614
+ }
615
+ ```
616
+
617
+ ## T13
618
+
619
+ Layers are supported. Layer keys' values are collected through the nested keys and grouped in the end of the file.
620
+
621
+ `source.styles.js`
622
+
623
+ ```js
624
+ {
625
+ div: {
626
+ thin: {
627
+ fontWeight: 200,
628
+ '@layer a': { fontWeight: 300 },
629
+ },
630
+ },
631
+ blockquote: {
632
+ color: 'red',
633
+ '@layer a': { fontWeight: 300 },
634
+ },
635
+ }
636
+ ```
637
+
638
+ `output.css`
639
+
640
+ ```css
641
+ div.thin {
642
+ font-weight: 200;
643
+ }
644
+
645
+ blockquote {
646
+ color: red;
647
+ }
648
+
649
+ @layer a {
650
+ div.thin {
651
+ font-weight: 300;
652
+ }
653
+
654
+ blockquote {
655
+ font-weight: 300;
656
+ }
657
+ }
658
+ ```
659
+
660
+ Also is possible to define layers order:
661
+
662
+ `source.styles.js`
663
+
664
+ ```js
665
+ {
666
+ '@layer a, b, c': '',
667
+ }
668
+ ```
669
+
670
+ `output.css`
671
+
672
+ ```css
673
+ @layer a, b, c;
674
+ ```
675
+
676
+ This key/value, if nested deeply, will be extracted and put in the beginning of the CSS file.
677
+
678
+ `source.styles.js`
679
+
680
+ ```js
681
+ '@layer x, y, z': '',
682
+
683
+ a: {
684
+ color: 'blue',
685
+
686
+ ':hover': {
687
+ color: 'red',
688
+ '@layer a, b, c': '',
689
+ },
690
+
691
+ '@layer c, b, a': '',
692
+ },
693
+
694
+ ```
695
+
696
+ `output.css`
697
+
698
+ ```css
699
+ @layer x, y, z;
700
+ @layer a, b, c;
701
+ @layer c, b, a;
702
+
703
+ a {
704
+ color: blue;
705
+ }
706
+
707
+ a:hover {
708
+ color: red;
709
+ }
710
+ ```