esm-styles 0.2.8 → 0.3.1
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 +26 -6
- package/dist/build.js +0 -0
- package/dist/lib/build.js +19 -4
- package/doc/ai-guide.md +29 -2
- package/doc/api-reference.md +30 -14
- package/doc/best-practices.md +793 -0
- package/doc/usage-guide.md +66 -15
- package/package.json +1 -1
- package/dist/lib/getCss.d.ts +0 -14
- package/dist/lib/getCss.js +0 -260
- package/dist/lib/utils/common.d.ts +0 -28
- package/dist/lib/utils/common.js +0 -47
- package/dist/lib/utils/endValue.d.ts +0 -2
- package/dist/lib/utils/endValue.js +0 -10
- package/dist/lib/utils/media.d.ts +0 -24
- package/dist/lib/utils/media.js +0 -93
- package/dist/lib/utils/obj2css.d.ts +0 -15
- package/dist/lib/utils/obj2css.js +0 -79
- package/dist/lib/utils/selectors.d.ts +0 -15
- package/dist/lib/utils/selectors.js +0 -87
- package/dist/lib/utils/tags.d.ts +0 -10
- package/dist/lib/utils/tags.js +0 -128
- package/dist/lib/utils/traversal.d.ts +0 -25
- package/dist/lib/utils/traversal.js +0 -78
- package/dist/watch.js +0 -37
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
# ESM Styles Best Practices
|
|
2
|
+
|
|
3
|
+
This document outlines recommended practices and common pitfalls when using ESM Styles to help you write maintainable and efficient CSS-in-JS code.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [File Organization](#file-organization)
|
|
8
|
+
- [Naming Conventions](#naming-conventions)
|
|
9
|
+
- [Configuration](#configuration)
|
|
10
|
+
- [Style Structure](#style-structure)
|
|
11
|
+
- [CSS Variables](#css-variables)
|
|
12
|
+
- [Media Queries](#media-queries)
|
|
13
|
+
- [Performance](#performance)
|
|
14
|
+
- [Common Pitfalls](#common-pitfalls)
|
|
15
|
+
|
|
16
|
+
## File Organization
|
|
17
|
+
|
|
18
|
+
### ✅ DO: Use a clear directory structure
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
src/styles/
|
|
22
|
+
├── source/
|
|
23
|
+
│ ├── $theme.mjs # Generated theme variables
|
|
24
|
+
│ ├── $device.mjs # Generated device variables
|
|
25
|
+
│ ├── global.styles.mjs # Global CSS variables
|
|
26
|
+
│ ├── light.styles.mjs # Light theme variables
|
|
27
|
+
│ ├── dark.styles.mjs # Dark theme variables
|
|
28
|
+
│ ├── twilight.styles.mjs # Twilight theme variables
|
|
29
|
+
│ ├── phone.styles.mjs # Phone device variables
|
|
30
|
+
│ ├── tablet.styles.mjs # Tablet device variables
|
|
31
|
+
│ ├── notebook.styles.mjs # Notebook device variables
|
|
32
|
+
│ ├── defaults.styles.mjs # Reset, base styles
|
|
33
|
+
│ ├── components.styles.mjs # Component styles
|
|
34
|
+
│ ├── layout.styles.mjs # Layout styles
|
|
35
|
+
│ └── components/
|
|
36
|
+
│ ├── button.styles.mjs
|
|
37
|
+
│ ├── card.styles.mjs
|
|
38
|
+
│ └── modal.styles.mjs
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### ✅ DO: Use consistent file naming
|
|
42
|
+
|
|
43
|
+
- Use kebab-case for module files: `button-group.styles.mjs`
|
|
44
|
+
- Use descriptive names: `navigation-menu.styles.mjs` not `nav.styles.mjs`
|
|
45
|
+
- Group related components in subdirectories
|
|
46
|
+
|
|
47
|
+
### ❌ DON'T: Mix different concerns in one file
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
// ❌ BAD: mixing layout and components
|
|
51
|
+
export default {
|
|
52
|
+
// Layout styles
|
|
53
|
+
container: { maxWidth: '1200px' },
|
|
54
|
+
|
|
55
|
+
// Component styles
|
|
56
|
+
button: { padding: '10px' },
|
|
57
|
+
|
|
58
|
+
// Theme variables
|
|
59
|
+
colors: { primary: '#blue' },
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Naming Conventions
|
|
64
|
+
|
|
65
|
+
### ✅ DO: Use semantic class names
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
// ✅ GOOD
|
|
69
|
+
export default {
|
|
70
|
+
navigationMenu: {
|
|
71
|
+
display: 'flex',
|
|
72
|
+
|
|
73
|
+
menuItem: {
|
|
74
|
+
padding: '8px 16px',
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
activeItem: {
|
|
78
|
+
fontWeight: 'bold',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### ❌ DON'T: Use presentation-based names
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
// ❌ BAD
|
|
88
|
+
export default {
|
|
89
|
+
blueBox: {
|
|
90
|
+
backgroundColor: 'blue', // What if you change to red?
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
bigText: {
|
|
94
|
+
fontSize: '24px', // What if you need different sizes?
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### ✅ DO: Use consistent naming patterns
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
// ✅ GOOD: consistent modifier patterns
|
|
103
|
+
export default {
|
|
104
|
+
button: {
|
|
105
|
+
padding: '10px 20px',
|
|
106
|
+
|
|
107
|
+
// State modifiers
|
|
108
|
+
isDisabled: { opacity: 0.5 },
|
|
109
|
+
isLoading: { cursor: 'wait' },
|
|
110
|
+
|
|
111
|
+
// Size variants
|
|
112
|
+
sizeSmall: { padding: '5px 10px' },
|
|
113
|
+
sizeLarge: { padding: '15px 30px' },
|
|
114
|
+
|
|
115
|
+
// Style variants
|
|
116
|
+
variantPrimary: { backgroundColor: 'blue' },
|
|
117
|
+
variantSecondary: { backgroundColor: 'gray' },
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### ❌ DON'T: Use ampersand (&) syntax
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
// ❌ BAD: ampersand is not supported
|
|
126
|
+
export default {
|
|
127
|
+
button: {
|
|
128
|
+
color: 'blue',
|
|
129
|
+
|
|
130
|
+
'&:hover': { // This won't work!
|
|
131
|
+
color: 'red'
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
'&.active': { // This won't work!
|
|
135
|
+
fontWeight: 'bold'
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ✅ GOOD: use direct selectors
|
|
141
|
+
export default {
|
|
142
|
+
button: {
|
|
143
|
+
color: 'blue',
|
|
144
|
+
|
|
145
|
+
':hover': { // Direct pseudo-class
|
|
146
|
+
color: 'red'
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
active: { // Class name (if not HTML tag)
|
|
150
|
+
fontWeight: 'bold'
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### ❌ DON'T: Use BEM-like naming
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
// ❌ BAD: BEM-style naming
|
|
160
|
+
export default {
|
|
161
|
+
card: {
|
|
162
|
+
padding: '20px',
|
|
163
|
+
|
|
164
|
+
card__header: { // Avoid block__element
|
|
165
|
+
marginBottom: '16px'
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
card__title: { // Repetitive naming
|
|
169
|
+
fontSize: '1.5rem'
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
'card--featured': { // Avoid block--modifier
|
|
173
|
+
border: '2px solid gold'
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ✅ GOOD: semantic nesting
|
|
179
|
+
export default {
|
|
180
|
+
card: {
|
|
181
|
+
padding: '20px',
|
|
182
|
+
|
|
183
|
+
header: { // Simple, semantic
|
|
184
|
+
marginBottom: '16px',
|
|
185
|
+
|
|
186
|
+
title: { // Nested naturally
|
|
187
|
+
fontSize: '1.5rem'
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
featured: { // Clear modifier
|
|
192
|
+
border: '2px solid gold'
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### ❌ DON'T: Use dashes in class names
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
// ❌ BAD: dashes require quotes in JavaScript
|
|
202
|
+
export default {
|
|
203
|
+
'navigation-menu': { // Needs quotes
|
|
204
|
+
display: 'flex'
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
'user-profile': { // Harder to work with in JS
|
|
208
|
+
padding: '20px'
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ✅ GOOD: use camelCase for easier JS handling
|
|
213
|
+
export default {
|
|
214
|
+
navigationMenu: { // No quotes needed
|
|
215
|
+
display: 'flex'
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
userProfile: { // Easy to reference in JS
|
|
219
|
+
padding: '20px'
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Configuration
|
|
225
|
+
|
|
226
|
+
### ✅ DO: Order floors logically
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
// ✅ GOOD: logical cascade order
|
|
230
|
+
floors: [
|
|
231
|
+
{ source: 'defaults', layer: 'defaults' }, // Reset, base styles
|
|
232
|
+
{ source: 'components', layer: 'components' }, // Component styles
|
|
233
|
+
{ source: 'layout', layer: 'layout' }, // Layout styles
|
|
234
|
+
{ source: 'utilities', layer: 'utilities' }, // Utility classes
|
|
235
|
+
{ source: 'overrides' }, // High-specificity overrides
|
|
236
|
+
]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### ✅ DO: Use meaningful breakpoint names
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
// ✅ GOOD: semantic breakpoints
|
|
243
|
+
const breakpoints = {
|
|
244
|
+
mobile: 499,
|
|
245
|
+
tablet: 1024,
|
|
246
|
+
desktop: 1440,
|
|
247
|
+
wide: 1920,
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### ❌ DON'T: Use arbitrary breakpoint names
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
// ❌ BAD: meaningless names
|
|
255
|
+
const breakpoints = {
|
|
256
|
+
sm: 499,
|
|
257
|
+
md: 1024,
|
|
258
|
+
lg: 1440,
|
|
259
|
+
xl: 1920,
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### ✅ DO: Group related media types
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
// ✅ GOOD: logical grouping
|
|
267
|
+
media: {
|
|
268
|
+
theme: ['light', 'dark', 'high-contrast'],
|
|
269
|
+
device: ['mobile', 'tablet', 'desktop'],
|
|
270
|
+
preference: ['reduced-motion', 'high-contrast']
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Style Structure
|
|
275
|
+
|
|
276
|
+
### ✅ DO: Use semantic HTML with logical nesting
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
// ✅ GOOD: semantic tags with meaningful structure
|
|
280
|
+
export default {
|
|
281
|
+
article: {
|
|
282
|
+
padding: '20px',
|
|
283
|
+
borderRadius: '8px',
|
|
284
|
+
|
|
285
|
+
header: {
|
|
286
|
+
marginBottom: '16px',
|
|
287
|
+
|
|
288
|
+
h2: {
|
|
289
|
+
// Semantic heading tag
|
|
290
|
+
fontSize: '1.5rem',
|
|
291
|
+
fontWeight: 'bold',
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
time: {
|
|
295
|
+
// Semantic time element
|
|
296
|
+
fontSize: '0.9rem',
|
|
297
|
+
color: 'gray',
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
p: {
|
|
302
|
+
// Content in paragraphs
|
|
303
|
+
lineHeight: 1.6,
|
|
304
|
+
marginBottom: '1rem',
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
footer: {
|
|
308
|
+
marginTop: '16px',
|
|
309
|
+
textAlign: 'right',
|
|
310
|
+
|
|
311
|
+
button: {
|
|
312
|
+
// Semantic button
|
|
313
|
+
padding: '8px 16px',
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### ✅ DO: Rely on semantic elements over generic divs
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
// ❌ BAD: everything is a div with classes
|
|
324
|
+
export default {
|
|
325
|
+
'story-container': {
|
|
326
|
+
padding: '20px',
|
|
327
|
+
|
|
328
|
+
'story-header': {
|
|
329
|
+
marginBottom: '16px',
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
'story-title': {
|
|
333
|
+
fontSize: '1.5rem',
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
'story-content': {
|
|
337
|
+
lineHeight: 1.6,
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
'story-actions': {
|
|
341
|
+
marginTop: '16px',
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ✅ GOOD: semantic HTML structure
|
|
347
|
+
export default {
|
|
348
|
+
section: { // Semantic section
|
|
349
|
+
story: { // Custom component class
|
|
350
|
+
padding: '20px',
|
|
351
|
+
|
|
352
|
+
header: { // Semantic header
|
|
353
|
+
marginBottom: '16px',
|
|
354
|
+
|
|
355
|
+
h3: { // Proper heading hierarchy
|
|
356
|
+
fontSize: '1.5rem',
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
main: { // Main content area
|
|
361
|
+
lineHeight: 1.6,
|
|
362
|
+
|
|
363
|
+
p: { // Paragraphs for text
|
|
364
|
+
marginBottom: '1rem',
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
nav: { // Navigation for actions
|
|
369
|
+
marginTop: '16px',
|
|
370
|
+
|
|
371
|
+
button: { // Semantic buttons
|
|
372
|
+
marginRight: '8px',
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### ❌ DON'T: Repeat class names or create redundant nesting
|
|
381
|
+
|
|
382
|
+
```js
|
|
383
|
+
// ❌ BAD: repetitive naming and poor structure
|
|
384
|
+
export default {
|
|
385
|
+
'story-list': {
|
|
386
|
+
'story-item': {
|
|
387
|
+
'story-item-content': {
|
|
388
|
+
'story-item-title': { // Too repetitive!
|
|
389
|
+
fontSize: '1.2rem',
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
'story-item-message': { // div.story div.story_message
|
|
393
|
+
padding: '10px',
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ✅ GOOD: semantic structure without repetition
|
|
401
|
+
export default {
|
|
402
|
+
section: {
|
|
403
|
+
story: { // section.story (semantic)
|
|
404
|
+
padding: '20px',
|
|
405
|
+
|
|
406
|
+
h3: { // story h3 (semantic heading)
|
|
407
|
+
fontSize: '1.2rem',
|
|
408
|
+
},
|
|
409
|
+
|
|
410
|
+
article: { // story article (semantic content)
|
|
411
|
+
message: { // story article.message
|
|
412
|
+
padding: '10px',
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### ❌ DON'T: Over-nest selectors
|
|
421
|
+
|
|
422
|
+
```js
|
|
423
|
+
// ❌ BAD: too deeply nested
|
|
424
|
+
export default {
|
|
425
|
+
page: {
|
|
426
|
+
main: {
|
|
427
|
+
section: {
|
|
428
|
+
article: {
|
|
429
|
+
div: {
|
|
430
|
+
p: {
|
|
431
|
+
span: {
|
|
432
|
+
color: 'red', // 7 levels deep!
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### ✅ DO: Use composition for reusable styles
|
|
444
|
+
|
|
445
|
+
```js
|
|
446
|
+
// ✅ GOOD: reusable patterns
|
|
447
|
+
const flexCenter = {
|
|
448
|
+
display: 'flex',
|
|
449
|
+
alignItems: 'center',
|
|
450
|
+
justifyContent: 'center',
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const cardBase = {
|
|
454
|
+
padding: '20px',
|
|
455
|
+
borderRadius: '8px',
|
|
456
|
+
backgroundColor: 'white',
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export default {
|
|
460
|
+
modal: {
|
|
461
|
+
...flexCenter,
|
|
462
|
+
position: 'fixed',
|
|
463
|
+
inset: 0,
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
productCard: {
|
|
467
|
+
...cardBase,
|
|
468
|
+
border: '1px solid #eee',
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
alertCard: {
|
|
472
|
+
...cardBase,
|
|
473
|
+
border: '2px solid red',
|
|
474
|
+
},
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## CSS Variables
|
|
479
|
+
|
|
480
|
+
### ✅ DO: Use semantic variable names
|
|
481
|
+
|
|
482
|
+
```js
|
|
483
|
+
// ✅ GOOD: semantic naming
|
|
484
|
+
export default {
|
|
485
|
+
colors: {
|
|
486
|
+
primary: '#4285f4',
|
|
487
|
+
secondary: '#34a853',
|
|
488
|
+
danger: '#ea4335',
|
|
489
|
+
surface: '#ffffff',
|
|
490
|
+
onSurface: '#000000',
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
spacing: {
|
|
494
|
+
unit: '8px',
|
|
495
|
+
small: '16px',
|
|
496
|
+
medium: '24px',
|
|
497
|
+
large: '32px',
|
|
498
|
+
},
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### ✅ DO: Create consistent theme structures
|
|
503
|
+
|
|
504
|
+
```js
|
|
505
|
+
// light.styles.mjs
|
|
506
|
+
export default {
|
|
507
|
+
surface: {
|
|
508
|
+
primary: '#ffffff',
|
|
509
|
+
secondary: '#f5f5f5',
|
|
510
|
+
accent: '#e3f2fd'
|
|
511
|
+
},
|
|
512
|
+
text: {
|
|
513
|
+
primary: '#212121',
|
|
514
|
+
secondary: '#757575',
|
|
515
|
+
disabled: '#bdbdbd'
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// dark.styles.mjs
|
|
520
|
+
export default {
|
|
521
|
+
surface: {
|
|
522
|
+
primary: '#121212',
|
|
523
|
+
secondary: '#1e1e1e',
|
|
524
|
+
accent: '#263238'
|
|
525
|
+
},
|
|
526
|
+
text: {
|
|
527
|
+
primary: '#ffffff',
|
|
528
|
+
secondary: '#b3b3b3',
|
|
529
|
+
disabled: '#666666'
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### ❌ DON'T: Hardcode theme-specific values in components
|
|
535
|
+
|
|
536
|
+
```js
|
|
537
|
+
// ❌ BAD: hardcoded colors
|
|
538
|
+
export default {
|
|
539
|
+
button: {
|
|
540
|
+
backgroundColor: '#ffffff', // What about dark theme?
|
|
541
|
+
color: '#000000'
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ✅ GOOD: use theme variables
|
|
546
|
+
import $theme from './$theme.mjs'
|
|
547
|
+
|
|
548
|
+
export default {
|
|
549
|
+
button: {
|
|
550
|
+
backgroundColor: $theme.surface.primary,
|
|
551
|
+
color: $theme.text.primary
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
## Media Queries
|
|
557
|
+
|
|
558
|
+
### ✅ DO: Use mobile-first approach
|
|
559
|
+
|
|
560
|
+
```js
|
|
561
|
+
// ✅ GOOD: mobile-first
|
|
562
|
+
export default {
|
|
563
|
+
container: {
|
|
564
|
+
padding: '16px', // Mobile default
|
|
565
|
+
|
|
566
|
+
'@min-tablet': {
|
|
567
|
+
padding: '24px', // Tablet and up
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
'@min-desktop': {
|
|
571
|
+
padding: '32px', // Desktop and up
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
}
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### ✅ DO: Use semantic media query names
|
|
578
|
+
|
|
579
|
+
```js
|
|
580
|
+
// ✅ GOOD: descriptive names
|
|
581
|
+
mediaQueries: {
|
|
582
|
+
'reduced-motion': '(prefers-reduced-motion: reduce)',
|
|
583
|
+
'high-contrast': '(prefers-contrast: high)',
|
|
584
|
+
'touch-device': '(hover: none) and (pointer: coarse)',
|
|
585
|
+
'print': 'print'
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### ❌ DON'T: Repeat media queries
|
|
590
|
+
|
|
591
|
+
```js
|
|
592
|
+
// ❌ BAD: repeated media queries
|
|
593
|
+
export default {
|
|
594
|
+
header: {
|
|
595
|
+
'@media (max-width: 768px)': {
|
|
596
|
+
fontSize: '1.2rem'
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
|
|
600
|
+
nav: {
|
|
601
|
+
'@media (max-width: 768px)': { // Same breakpoint repeated
|
|
602
|
+
display: 'none'
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// ✅ GOOD: use named queries
|
|
608
|
+
export default {
|
|
609
|
+
header: {
|
|
610
|
+
'@mobile': {
|
|
611
|
+
fontSize: '1.2rem'
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
|
|
615
|
+
nav: {
|
|
616
|
+
'@mobile': {
|
|
617
|
+
display: 'none'
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
## Performance
|
|
624
|
+
|
|
625
|
+
### ✅ DO: Use efficient selectors
|
|
626
|
+
|
|
627
|
+
```js
|
|
628
|
+
// ✅ GOOD: specific, efficient selectors
|
|
629
|
+
export default {
|
|
630
|
+
navigationMenu: {
|
|
631
|
+
display: 'flex',
|
|
632
|
+
|
|
633
|
+
menuItem: {
|
|
634
|
+
padding: '8px',
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### ❌ DON'T: Use overly complex selectors
|
|
641
|
+
|
|
642
|
+
```js
|
|
643
|
+
// ❌ BAD: complex, inefficient selectors
|
|
644
|
+
export default {
|
|
645
|
+
'div > ul li:nth-child(odd) a[href*="example"]:not(.active)': {
|
|
646
|
+
color: 'red', // Too complex!
|
|
647
|
+
},
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### ✅ DO: Minimize CSS output size
|
|
652
|
+
|
|
653
|
+
```js
|
|
654
|
+
// ✅ GOOD: group similar styles
|
|
655
|
+
const buttonBase = {
|
|
656
|
+
padding: '10px 20px',
|
|
657
|
+
border: 'none',
|
|
658
|
+
borderRadius: '4px',
|
|
659
|
+
cursor: 'pointer',
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export default {
|
|
663
|
+
primaryButton: {
|
|
664
|
+
...buttonBase,
|
|
665
|
+
backgroundColor: 'blue',
|
|
666
|
+
color: 'white',
|
|
667
|
+
},
|
|
668
|
+
|
|
669
|
+
secondaryButton: {
|
|
670
|
+
...buttonBase,
|
|
671
|
+
backgroundColor: 'gray',
|
|
672
|
+
color: 'black',
|
|
673
|
+
},
|
|
674
|
+
}
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
## Common Pitfalls
|
|
678
|
+
|
|
679
|
+
### ❌ DON'T: Forget about CSS specificity
|
|
680
|
+
|
|
681
|
+
```js
|
|
682
|
+
// ❌ PROBLEM: specificity conflicts
|
|
683
|
+
export default {
|
|
684
|
+
button: {
|
|
685
|
+
color: 'blue',
|
|
686
|
+
|
|
687
|
+
primary: {
|
|
688
|
+
color: 'white', // Might not override due to specificity
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// ✅ SOLUTION: use layers or more specific selectors
|
|
694
|
+
floors: [
|
|
695
|
+
{ source: 'base', layer: 'base' },
|
|
696
|
+
{ source: 'components', layer: 'components' },
|
|
697
|
+
{ source: 'overrides', layer: 'overrides' }, // Higher specificity layer
|
|
698
|
+
]
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
### ❌ DON'T: Mix units inconsistently
|
|
702
|
+
|
|
703
|
+
```js
|
|
704
|
+
// ❌ BAD: mixed units
|
|
705
|
+
export default {
|
|
706
|
+
container: {
|
|
707
|
+
padding: '16px',
|
|
708
|
+
margin: '1rem',
|
|
709
|
+
width: '50%',
|
|
710
|
+
height: '200pt' // Inconsistent!
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// ✅ GOOD: consistent units
|
|
715
|
+
export default {
|
|
716
|
+
container: {
|
|
717
|
+
padding: '1rem',
|
|
718
|
+
margin: '1rem',
|
|
719
|
+
width: '50%',
|
|
720
|
+
height: '12.5rem' // Consistent rem units
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### ❌ DON'T: Forget about accessibility
|
|
726
|
+
|
|
727
|
+
```js
|
|
728
|
+
// ❌ BAD: ignores accessibility
|
|
729
|
+
export default {
|
|
730
|
+
button: {
|
|
731
|
+
backgroundColor: '#ff0000',
|
|
732
|
+
color: '#ff9999' // Poor contrast!
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// ✅ GOOD: considers accessibility
|
|
737
|
+
export default {
|
|
738
|
+
button: {
|
|
739
|
+
backgroundColor: '#d32f2f',
|
|
740
|
+
color: '#ffffff', // Good contrast
|
|
741
|
+
fontSize: '16px', // Large enough for readability
|
|
742
|
+
padding: '12px 24px', // Adequate touch target size
|
|
743
|
+
|
|
744
|
+
'@reduced-motion': {
|
|
745
|
+
transition: 'none' // Respects user preferences
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
### ❌ DON'T: Hardcode magic numbers
|
|
752
|
+
|
|
753
|
+
```js
|
|
754
|
+
// ❌ BAD: magic numbers
|
|
755
|
+
export default {
|
|
756
|
+
modal: {
|
|
757
|
+
zIndex: 9999, // Why 9999?
|
|
758
|
+
top: '73px' // Why 73px?
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// ✅ GOOD: use meaningful variables
|
|
763
|
+
const zIndexes = {
|
|
764
|
+
modal: 1000,
|
|
765
|
+
tooltip: 1100,
|
|
766
|
+
dropdown: 1200
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const headerHeight = '72px'
|
|
770
|
+
|
|
771
|
+
export default {
|
|
772
|
+
modal: {
|
|
773
|
+
zIndex: zIndexes.modal,
|
|
774
|
+
top: `calc(${headerHeight} + 1px)`
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
## Summary
|
|
780
|
+
|
|
781
|
+
- **Organize**: Use clear file structure and consistent naming
|
|
782
|
+
- **Naming**: Use camelCase, avoid BEM, avoid dashes, prefer semantic names
|
|
783
|
+
- **Structure**: Rely on semantic HTML tags over generic divs and classes
|
|
784
|
+
- **Nesting**: Use logical nesting, avoid repetitive class names
|
|
785
|
+
- **Syntax**: No ampersand (&) syntax, use direct selectors and pseudo-classes
|
|
786
|
+
- **Compose**: Reuse common patterns and avoid duplication
|
|
787
|
+
- **Configure**: Set up logical floors and meaningful breakpoints
|
|
788
|
+
- **Variables**: Use semantic names and consistent theme structures
|
|
789
|
+
- **Performance**: Write efficient selectors and minimize output
|
|
790
|
+
- **Accessibility**: Consider contrast, motion preferences, and usability
|
|
791
|
+
- **Maintainability**: Avoid magic numbers and overly complex selectors
|
|
792
|
+
|
|
793
|
+
Following these practices will help you create maintainable, performant, and accessible stylesheets with ESM Styles.
|