esm-styles 0.2.1 → 0.2.3

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 CHANGED
@@ -133,11 +133,12 @@ export default {
133
133
  },
134
134
  },
135
135
 
136
- // Media query shorthands
136
+ // Media query shorthands (in addition to media.device and media.theme names)
137
137
  mediaQueries: {
138
- mobile: '(max-width: 767px)',
139
- tablet: '(min-width: 768px) and (max-width: 1024px)',
140
- desktop: '(min-width: 1025px)',
138
+ 'min-tablet': '(min-width: 768px)',
139
+ 'max-tablet': '(max-width: 1024px)',
140
+ hover: '(hover: hover)',
141
+ // ...whatever you want
141
142
  },
142
143
  }
143
144
  ```
@@ -184,16 +185,18 @@ p strong {
184
185
 
185
186
  ```js
186
187
  {
187
- // Class selectors for non-HTML tag names
188
- header: {
189
- backgroundColor: '#f5f5f5',
190
- padding: '20px'
191
- },
192
-
193
- // Class on HTML tag using underscore prefix
194
- p: {
195
- _highlight: {
196
- backgroundColor: 'yellow'
188
+ div: {
189
+ highlighted: {
190
+ // highlighted is not a tag
191
+ border: '1px solid red',
192
+ },
193
+ p: {
194
+ // p is a tag
195
+ fontSize: '16px',
196
+ },
197
+ _video: {
198
+ // video is a tag, but the class is meant, use single underscore prefix
199
+ aspectRatio: 1.77,
197
200
  }
198
201
  }
199
202
  }
@@ -202,13 +205,16 @@ p strong {
202
205
  Compiles to:
203
206
 
204
207
  ```css
205
- .header {
206
- background-color: #f5f5f5;
207
- padding: 20px;
208
+ div.highlighted {
209
+ border: 1px solid red;
210
+ }
211
+
212
+ div p {
213
+ font-size: 16px;
208
214
  }
209
215
 
210
- p.highlight {
211
- background-color: yellow;
216
+ div.video {
217
+ aspect-ratio: 1.77;
212
218
  }
213
219
  ```
214
220
 
package/dist/lib/build.js CHANGED
@@ -123,7 +123,10 @@ export async function build(configPath = 'esm-styles.config.js') {
123
123
  if (key === '') {
124
124
  // Only possible at non-root
125
125
  const varName = '--' + path.map((k) => k.replace(/_/g, '-')).join('-');
126
- const leaf = { var: varName };
126
+ const leaf = {
127
+ var: `var(${varName})`,
128
+ name: varName,
129
+ };
127
130
  for (let i = 0; i < sets.length; i++) {
128
131
  const v = path.length === 0
129
132
  ? mergedSets[sets[i]]
package/dist/lib/index.js CHANGED
@@ -1,19 +1,16 @@
1
1
  import * as utils from './utils/index.js';
2
+ import { toCssValue } from './utils/to-css-value.js';
2
3
  function flatWalk(obj, selectorPath = [], result = { rules: [], media: {}, layers: {} }, options = {}, currentMedia = []) {
3
4
  const props = {};
4
5
  for (const key in obj) {
5
6
  const value = obj[key];
6
7
  if (utils.isEndValue(value)) {
7
8
  const cssKey = utils.jsKeyToCssKey(key);
8
- if (typeof value === 'object' &&
9
- value !== null &&
10
- 'var' in value &&
11
- typeof value.var === 'string') {
12
- // Use CSS variable reference
13
- props[cssKey] = `var(${value.var})`;
9
+ if (cssKey === 'content') {
10
+ props[cssKey] = utils.contentValue(toCssValue(value));
14
11
  }
15
12
  else {
16
- props[cssKey] = cssKey === 'content' ? utils.contentValue(value) : value;
13
+ props[cssKey] = toCssValue(value);
17
14
  }
18
15
  }
19
16
  else if (typeof value === 'object' && value !== null) {
@@ -1,13 +1,33 @@
1
+ const keywords = [
2
+ 'normal',
3
+ 'none',
4
+ 'open-quote',
5
+ 'close-quote',
6
+ 'no-open-quote',
7
+ 'no-close-quote',
8
+ 'inherit',
9
+ 'initial',
10
+ 'revert',
11
+ 'revert-layer',
12
+ 'unset',
13
+ ];
1
14
  export const contentValue = (value) => {
2
15
  if (typeof value !== 'string')
3
16
  return value;
4
- // If already quoted, return as is
5
- if (/^'.*'$/.test(value) || /^".*"$/.test(value))
17
+ if (keywords.some((keyword) => {
18
+ const regex = new RegExp(`\\b${keyword}\\b`);
19
+ return regex.test(value);
20
+ }))
6
21
  return value;
7
- // If all characters are printable ASCII, return as it is
8
- if (/^[\x20-\x7E]*$/.test(value)) {
22
+ // If already contains quoted values, return as is
23
+ if (/'.*'/.test(value) || /".*"/.test(value))
9
24
  return value;
10
- }
25
+ // If contains functions, return as is
26
+ if (/[a-z]+\(.*\)/i.test(value))
27
+ return value; // url(...), attr(...), etc.
28
+ // If all characters are printable ASCII, wrap in single quotes
29
+ if (/^[\x20-\x7E]*$/.test(value))
30
+ return `'${value}'`;
11
31
  // Otherwise, convert each character to CSS unicode escape: \00xxxx
12
32
  const unicode = value
13
33
  .split('')
@@ -6,3 +6,4 @@ export * from './format.js';
6
6
  export * from './end-value.js';
7
7
  export * from './get-css-variables.js';
8
8
  export * from './media-shorthand.js';
9
+ export * from './to-css-value.js';
@@ -6,3 +6,4 @@ export * from './format.js';
6
6
  export * from './end-value.js';
7
7
  export * from './get-css-variables.js';
8
8
  export * from './media-shorthand.js';
9
+ export * from './to-css-value.js';
@@ -195,7 +195,16 @@ export const joinSelectorPath = (path) => {
195
195
  return acc + (acc ? ' ' : '') + '.' + part.slice(2);
196
196
  }
197
197
  else if (part.startsWith('_')) {
198
- return acc + (acc ? ' ' : '') + '.' + part.slice(1);
198
+ // Attach class directly to previous part unless prev is combinator or root
199
+ const combinators = ['>', '+', '~'];
200
+ const isPrevCombinator = prev && combinators.some((c) => prev.startsWith(c));
201
+ if (isPrevRoot || isPrevCombinator || !acc) {
202
+ return acc + (acc ? ' ' : '') + '.' + part.slice(1);
203
+ }
204
+ else {
205
+ // Attach directly (no space)
206
+ return acc + '.' + part.slice(1);
207
+ }
199
208
  }
200
209
  else if (part.startsWith('>') ||
201
210
  part.startsWith('+') ||
@@ -224,6 +233,13 @@ export const joinSelectorPath = (path) => {
224
233
  return acc + (acc ? ' ' : '') + match[1] + '.' + match[2];
225
234
  }
226
235
  }
236
+ else if (/^([a-z][a-z0-9]*)#([\w-]+)$/.test(part)) {
237
+ // If part matches 'tag#id' and tag is an HTML tag
238
+ const match = part.match(/^([a-z][a-z0-9]*)#([\w-]+)$/);
239
+ if (match && isHtmlTag(match[1])) {
240
+ return acc + (acc ? ' ' : '') + match[1] + '#' + match[2];
241
+ }
242
+ }
227
243
  // Not a tag, not a special selector: treat as class or custom element
228
244
  // If previous part is a root selector, insert a space
229
245
  if (isPrevRoot) {
@@ -0,0 +1 @@
1
+ export declare function toCssValue(value: any): string;
@@ -0,0 +1,15 @@
1
+ // Utility to convert a value to a CSS string, handling arrays, objects with 'var', and primitives
2
+ export function toCssValue(value) {
3
+ if (Array.isArray(value)) {
4
+ // Join array parts, recursively converting each
5
+ return value.map(toCssValue).join(' ');
6
+ }
7
+ if (typeof value === 'object' &&
8
+ value !== null &&
9
+ 'var' in value &&
10
+ typeof value.var === 'string') {
11
+ return value.var;
12
+ }
13
+ // For primitives (string, number), just return as string
14
+ return String(value);
15
+ }
@@ -307,21 +307,9 @@ Use commas to target multiple selectors:
307
307
  }
308
308
  ```
309
309
 
310
- ### T9: Content Property
311
-
312
- For the `content` property, use JavaScript unicode notation for special characters:
313
-
314
- ```js
315
- {
316
- '::before': {
317
- content: '\u2022', // Bullet character
318
- }
319
- }
320
- ```
321
-
322
310
  ## Layers
323
311
 
324
- CSS layers help manage specificity and provide better organization of styles. ESM Styles supports @layer directives:
312
+ ESM Styles supports @layer directives:
325
313
 
326
314
  ```js
327
315
  {
@@ -551,12 +539,14 @@ The build process automatically generates modules like `$theme.mjs`:
551
539
  export default {
552
540
  colors: {
553
541
  background: {
554
- var: '--colors-background',
542
+ var: 'var(--colors-background)',
543
+ name: '--colors-background',
555
544
  light: '#ffffff',
556
545
  dark: '#121212',
557
546
  },
558
547
  surface: {
559
- var: '--colors-surface',
548
+ var: 'var(--colors-surface)',
549
+ name: '--colors-surface',
560
550
  light: '#f5f5f5',
561
551
  dark: '#222222',
562
552
  },
@@ -575,10 +565,11 @@ import $theme from './$theme.mjs'
575
565
 
576
566
  export default {
577
567
  button: {
578
- backgroundColor: $theme.colors.primary,
568
+ backgroundColor: $theme.colors.primary, // automatically replaced with var(--colors-primary) by compiler
579
569
  color: $theme.colors.background,
580
570
  padding: '10px 20px',
581
571
  borderRadius: '4px',
572
+ border: `1px solid ${$theme.colors.surface.var}`, // in case of concatenation, use .var property, otherwise it will be [object Object], sorry
582
573
  },
583
574
  }
584
575
  ```
@@ -591,6 +582,7 @@ button {
591
582
  color: var(--colors-background);
592
583
  padding: 10px 20px;
593
584
  border-radius: 4px;
585
+ border: 1px solid var(--colors-surface);
594
586
  }
595
587
  ```
596
588
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esm-styles",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "A library for working with ESM styles",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",