typestyles 0.2.0 → 0.4.0

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/dist/index.cjs CHANGED
@@ -67,11 +67,9 @@ function serializeStyle(selector, properties) {
67
67
  const declarations = [];
68
68
  for (const [prop, value] of Object.entries(properties)) {
69
69
  if (value == null) continue;
70
- if (prop.startsWith("&")) {
71
- const nestedSelector = prop.replace(/&/g, selector);
72
- rules.push(
73
- ...serializeStyle(nestedSelector, value)
74
- );
70
+ const nestedSelector = resolveNestedSelector(selector, prop);
71
+ if (nestedSelector) {
72
+ rules.push(...serializeStyle(nestedSelector, value));
75
73
  } else if (prop.startsWith("@")) {
76
74
  const innerRules = serializeStyle(selector, value);
77
75
  for (const inner of innerRules) {
@@ -82,9 +80,7 @@ function serializeStyle(selector, properties) {
82
80
  }
83
81
  } else {
84
82
  const kebabProp = toKebabCase(prop);
85
- declarations.push(
86
- `${kebabProp}: ${serializeValue(prop, value)}`
87
- );
83
+ declarations.push(`${kebabProp}: ${serializeValue(prop, value)}`);
88
84
  }
89
85
  }
90
86
  if (declarations.length > 0) {
@@ -95,10 +91,58 @@ function serializeStyle(selector, properties) {
95
91
  }
96
92
  return rules;
97
93
  }
94
+ function resolveNestedSelector(parentSelector, key) {
95
+ if (key.startsWith("&")) {
96
+ return key.replace(/&/g, parentSelector);
97
+ }
98
+ if (key.startsWith("[")) {
99
+ const parts = splitSelectorList(key);
100
+ return parts.map((part) => `${parentSelector}${part.trimStart()}`).join(", ");
101
+ }
102
+ return null;
103
+ }
104
+ function splitSelectorList(selector) {
105
+ const result = [];
106
+ let current2 = "";
107
+ let bracketDepth = 0;
108
+ let parenDepth = 0;
109
+ let quote = null;
110
+ for (let i = 0; i < selector.length; i++) {
111
+ const char = selector[i];
112
+ const prev = i > 0 ? selector[i - 1] : "";
113
+ if (quote) {
114
+ current2 += char;
115
+ if (char === quote && prev !== "\\") {
116
+ quote = null;
117
+ }
118
+ continue;
119
+ }
120
+ if (char === '"' || char === "'") {
121
+ quote = char;
122
+ current2 += char;
123
+ continue;
124
+ }
125
+ if (char === "[") bracketDepth++;
126
+ if (char === "]") bracketDepth = Math.max(0, bracketDepth - 1);
127
+ if (char === "(") parenDepth++;
128
+ if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
129
+ if (char === "," && bracketDepth === 0 && parenDepth === 0) {
130
+ result.push(current2.trim());
131
+ current2 = "";
132
+ continue;
133
+ }
134
+ current2 += char;
135
+ }
136
+ if (current2.trim()) {
137
+ result.push(current2.trim());
138
+ }
139
+ return result;
140
+ }
98
141
 
99
142
  // src/sheet.ts
100
143
  var STYLE_ELEMENT_ID = "typestyles";
101
144
  var insertedRules = /* @__PURE__ */ new Set();
145
+ var RUNTIME_DISABLED = typeof __TYPESTYLES_RUNTIME_DISABLED__ !== "undefined" && __TYPESTYLES_RUNTIME_DISABLED__ === "true";
102
146
  var pendingRules = [];
103
147
  var allRules = [];
104
148
  var flushScheduled = false;
@@ -128,7 +172,7 @@ function flush() {
128
172
  ssrBuffer.push(...rules);
129
173
  return;
130
174
  }
131
- if (!isBrowser) return;
175
+ if (!isBrowser || RUNTIME_DISABLED) return;
132
176
  const el = getStyleElement();
133
177
  const sheet = el.sheet;
134
178
  if (sheet) {
@@ -150,15 +194,16 @@ function scheduleFlush() {
150
194
  flush();
151
195
  return;
152
196
  }
153
- if (isBrowser) {
197
+ if (isBrowser && !RUNTIME_DISABLED) {
154
198
  queueMicrotask(flush);
155
199
  }
156
200
  }
157
201
  function insertRule(key, css) {
158
202
  if (insertedRules.has(key)) return;
159
203
  insertedRules.add(key);
160
- pendingRules.push(css);
161
204
  allRules.push(css);
205
+ if (RUNTIME_DISABLED && !ssrBuffer) return;
206
+ pendingRules.push(css);
162
207
  scheduleFlush();
163
208
  }
164
209
  function insertRules(rules) {
@@ -166,9 +211,11 @@ function insertRules(rules) {
166
211
  for (const { key, css } of rules) {
167
212
  if (insertedRules.has(key)) continue;
168
213
  insertedRules.add(key);
169
- pendingRules.push(css);
170
214
  allRules.push(css);
171
- added = true;
215
+ if (!RUNTIME_DISABLED || ssrBuffer) {
216
+ pendingRules.push(css);
217
+ added = true;
218
+ }
172
219
  }
173
220
  if (added) scheduleFlush();
174
221
  }
@@ -193,8 +240,97 @@ function flushSync() {
193
240
  // src/registry.ts
194
241
  var registeredNamespaces = /* @__PURE__ */ new Set();
195
242
 
243
+ // src/class-naming.ts
244
+ var defaultConfig = {
245
+ mode: "semantic",
246
+ prefix: "ts",
247
+ scopeId: ""
248
+ };
249
+ var current = { ...defaultConfig };
250
+ function getClassNamingConfig() {
251
+ return current;
252
+ }
253
+ function configureClassNaming(partial) {
254
+ current = { ...current, ...partial };
255
+ }
256
+ function resetClassNaming() {
257
+ current = { ...defaultConfig };
258
+ }
259
+ function stableSerialize(value) {
260
+ if (value === null) return "null";
261
+ if (typeof value !== "object") return JSON.stringify(value);
262
+ if (Array.isArray(value)) return `[${value.map((v) => stableSerialize(v)).join(",")}]`;
263
+ const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${stableSerialize(v)}`);
264
+ return `{${entries.join(",")}}`;
265
+ }
266
+ function hashString(input) {
267
+ let hash = 2166136261;
268
+ for (let i = 0; i < input.length; i++) {
269
+ hash ^= input.charCodeAt(i);
270
+ hash = Math.imul(hash, 16777619);
271
+ }
272
+ return (hash >>> 0).toString(36);
273
+ }
274
+ function sanitizeClassSegment(label) {
275
+ const normalized = label.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-");
276
+ return normalized.replace(/-+/g, "-").replace(/^-|-$/g, "") || "style";
277
+ }
278
+ function buildSingleClassName(name, properties) {
279
+ const cfg = getClassNamingConfig();
280
+ if (cfg.mode === "semantic") return name;
281
+ const payload = stableSerialize({
282
+ ...cfg.scopeId ? { scope: cfg.scopeId } : {},
283
+ namespace: name,
284
+ suffix: "",
285
+ properties
286
+ });
287
+ const h = hashString(payload);
288
+ if (cfg.mode === "atomic") return `${cfg.prefix}-${h}`;
289
+ return `${cfg.prefix}-${sanitizeClassSegment(name)}-${h}`;
290
+ }
291
+ function buildRecipeClassName(namespace, suffix, properties) {
292
+ const cfg = getClassNamingConfig();
293
+ if (cfg.mode === "semantic") return `${namespace}-${suffix}`;
294
+ const payload = stableSerialize({
295
+ ...cfg.scopeId ? { scope: cfg.scopeId } : {},
296
+ namespace,
297
+ suffix,
298
+ properties
299
+ });
300
+ const h = hashString(payload);
301
+ if (cfg.mode === "atomic") return `${cfg.prefix}-${h}`;
302
+ return `${cfg.prefix}-${sanitizeClassSegment(namespace)}-${h}`;
303
+ }
304
+
196
305
  // src/styles.ts
197
- function createStyles(namespace, definitions) {
306
+ function createClass(name, properties) {
307
+ if (process.env.NODE_ENV !== "production") {
308
+ if (registeredNamespaces.has(name)) {
309
+ console.warn(
310
+ `[typestyles] styles.class('${name}', ...) called more than once. This will cause class name collisions. Each class name should be unique.`
311
+ );
312
+ }
313
+ }
314
+ registeredNamespaces.add(name);
315
+ const className = buildSingleClassName(name, properties);
316
+ const selector = `.${className}`;
317
+ const rules = serializeStyle(selector, properties);
318
+ insertRules(rules);
319
+ return className;
320
+ }
321
+ function createHashClass(properties, label) {
322
+ const cfg = getClassNamingConfig();
323
+ const serialized = cfg.scopeId !== "" ? stableSerialize({ scope: cfg.scopeId, properties }) : stableSerialize(properties);
324
+ const hash = hashString(serialized);
325
+ const className = label ? `${cfg.prefix}-${sanitizeClassSegment(label)}-${hash}` : `${cfg.prefix}-${hash}`;
326
+ const selector = `.${className}`;
327
+ const rules = serializeStyle(selector, properties);
328
+ insertRules(rules);
329
+ return className;
330
+ }
331
+ function createStyles(namespace, baseOrDefinitions, variants) {
332
+ const definitions = variants !== void 0 ? { base: baseOrDefinitions, ...variants } : baseOrDefinitions;
333
+ const withBase = variants !== void 0;
198
334
  if (process.env.NODE_ENV !== "production") {
199
335
  if (registeredNamespaces.has(namespace)) {
200
336
  console.warn(
@@ -204,15 +340,20 @@ function createStyles(namespace, definitions) {
204
340
  }
205
341
  registeredNamespaces.add(namespace);
206
342
  const rules = [];
343
+ const variantToClass = {};
207
344
  for (const [variant, properties] of Object.entries(definitions)) {
208
- const className = `${namespace}-${variant}`;
345
+ const props = properties;
346
+ const className = buildRecipeClassName(namespace, variant, props);
347
+ variantToClass[variant] = className;
209
348
  const selector = `.${className}`;
210
- const variantRules = serializeStyle(selector, properties);
349
+ const variantRules = serializeStyle(selector, props);
211
350
  rules.push(...variantRules);
212
351
  }
213
352
  insertRules(rules);
214
- const selectorFn = (...variants) => {
215
- return variants.filter(Boolean).map((v) => `${namespace}-${v}`).join(" ");
353
+ const selectorFn = (...variants2) => {
354
+ const filtered = variants2.filter(Boolean);
355
+ const classes = withBase ? ["base", ...filtered.filter((v) => v !== "base")] : filtered;
356
+ return classes.map((v) => variantToClass[v] ?? "").filter(Boolean).join(" ");
216
357
  };
217
358
  return selectorFn;
218
359
  }
@@ -231,6 +372,82 @@ function compose(...selectors) {
231
372
  return classNames.join(" ");
232
373
  };
233
374
  }
375
+ function createStylesWithUtils(utils) {
376
+ const apply = (properties) => expandStyleWithUtils(properties, utils);
377
+ function create(namespace, baseOrDefinitions, variants) {
378
+ if (variants !== void 0) {
379
+ const transformedVariants = Object.fromEntries(
380
+ Object.entries(variants).map(([variant, properties]) => [variant, apply(properties)])
381
+ );
382
+ return createStyles(namespace, apply(baseOrDefinitions), transformedVariants);
383
+ }
384
+ const transformedDefinitions = Object.fromEntries(
385
+ Object.entries(
386
+ baseOrDefinitions
387
+ ).map(([variant, properties]) => [variant, apply(properties)])
388
+ );
389
+ return createStyles(namespace, transformedDefinitions);
390
+ }
391
+ return {
392
+ class: (name, properties) => createClass(name, apply(properties)),
393
+ hashClass: (properties, label) => createHashClass(apply(properties), label),
394
+ create,
395
+ compose
396
+ };
397
+ }
398
+ function expandStyleWithUtils(properties, utils) {
399
+ const expanded = {};
400
+ for (const [key, value] of Object.entries(properties)) {
401
+ if (value == null) continue;
402
+ if (key.startsWith("&") || key.startsWith("[") || key.startsWith("@")) {
403
+ if (isObject(value)) {
404
+ assignStyleEntry(
405
+ expanded,
406
+ key,
407
+ expandStyleWithUtils(value, utils)
408
+ );
409
+ }
410
+ continue;
411
+ }
412
+ if (Object.prototype.hasOwnProperty.call(utils, key)) {
413
+ const utilFn = utils[key];
414
+ const utilResult = utilFn(value);
415
+ const normalized = expandStyleWithUtils(utilResult, utils);
416
+ for (const [utilKey, utilValue] of Object.entries(normalized)) {
417
+ assignStyleEntry(expanded, utilKey, utilValue);
418
+ }
419
+ continue;
420
+ }
421
+ assignStyleEntry(expanded, key, value);
422
+ }
423
+ return expanded;
424
+ }
425
+ function assignStyleEntry(target, key, value) {
426
+ const targetRecord = target;
427
+ if (isObject(value)) {
428
+ const existing = targetRecord[key];
429
+ if (isObject(existing)) {
430
+ targetRecord[key] = mergeStyleObjects(
431
+ existing,
432
+ value
433
+ );
434
+ return;
435
+ }
436
+ targetRecord[key] = value;
437
+ return;
438
+ }
439
+ targetRecord[key] = value;
440
+ }
441
+ function mergeStyleObjects(base, next) {
442
+ const merged = { ...base };
443
+ for (const [key, value] of Object.entries(next)) {
444
+ assignStyleEntry(merged, key, value);
445
+ }
446
+ return merged;
447
+ }
448
+ function isObject(value) {
449
+ return typeof value === "object" && value !== null && !Array.isArray(value);
450
+ }
234
451
 
235
452
  // src/tokens.ts
236
453
  var registeredNamespaces2 = /* @__PURE__ */ new Set();
@@ -349,6 +566,15 @@ function alpha(colorValue, opacity, colorSpace = "srgb") {
349
566
 
350
567
  // src/component.ts
351
568
  function createComponent(namespace, config) {
569
+ if ("slots" in config) {
570
+ return createSlotComponent(
571
+ namespace,
572
+ config
573
+ );
574
+ }
575
+ return createSingleComponent(namespace, config);
576
+ }
577
+ function createSingleComponent(namespace, config) {
352
578
  const { base, variants = {}, compoundVariants = [], defaultVariants = {} } = config;
353
579
  if (process.env.NODE_ENV !== "production") {
354
580
  if (registeredNamespaces.has(namespace)) {
@@ -359,46 +585,169 @@ function createComponent(namespace, config) {
359
585
  }
360
586
  registeredNamespaces.add(namespace);
361
587
  const rules = [];
588
+ let baseClassName;
589
+ const variantClassByKey = {};
590
+ const compoundClassByIndex = [];
362
591
  if (base) {
363
- rules.push(...serializeStyle(`.${namespace}-base`, base));
592
+ baseClassName = buildRecipeClassName(namespace, "base", base);
593
+ rules.push(...serializeStyle(`.${baseClassName}`, base));
364
594
  }
365
595
  for (const [dimension, options] of Object.entries(variants)) {
366
596
  for (const [option, properties] of Object.entries(options)) {
367
- const className = `.${namespace}-${dimension}-${option}`;
368
- rules.push(...serializeStyle(className, properties));
597
+ const segment = `${dimension}-${option}`;
598
+ const className = buildRecipeClassName(namespace, segment, properties);
599
+ variantClassByKey[segment] = className;
600
+ rules.push(...serializeStyle(`.${className}`, properties));
369
601
  }
370
602
  }
371
603
  compoundVariants.forEach(
372
604
  (cv, index) => {
373
- const className = `.${namespace}-compound-${index}`;
374
- rules.push(...serializeStyle(className, cv.style));
605
+ const className = buildRecipeClassName(namespace, `compound-${index}`, cv.style);
606
+ compoundClassByIndex[index] = className;
607
+ rules.push(...serializeStyle(`.${className}`, cv.style));
375
608
  }
376
609
  );
377
610
  insertRules(rules);
378
611
  return ((selections = {}) => {
379
612
  const classes = [];
380
- if (base) classes.push(`${namespace}-base`);
613
+ if (base && baseClassName) classes.push(baseClassName);
381
614
  const resolvedSelections = {};
382
- for (const dimension of Object.keys(variants)) {
383
- resolvedSelections[dimension] = selections[dimension] ?? defaultVariants[dimension];
615
+ for (const [dimension, options] of Object.entries(variants)) {
616
+ const optionMap = options;
617
+ const explicit = selections[dimension];
618
+ const fallback = defaultVariants[dimension];
619
+ resolvedSelections[dimension] = normalizeSelection(explicit ?? fallback, optionMap);
384
620
  }
385
- for (const dimension of Object.keys(variants)) {
621
+ for (const [dimension, options] of Object.entries(variants)) {
386
622
  const selected = resolvedSelections[dimension];
387
- if (selected != null && selected !== false) {
388
- classes.push(`${namespace}-${dimension}-${String(selected)}`);
623
+ const optionMap = options;
624
+ const selectedKey = normalizeSelection(selected, optionMap);
625
+ if (selectedKey != null) {
626
+ const variantKey = `${dimension}-${selectedKey}`;
627
+ const cn = variantClassByKey[variantKey];
628
+ if (cn) classes.push(cn);
389
629
  }
390
630
  }
391
631
  compoundVariants.forEach(
392
632
  (cv, index) => {
393
- const matches = Object.entries(cv.variants).every(
394
- ([k, v]) => resolvedSelections[k] === v
395
- );
396
- if (matches) classes.push(`${namespace}-compound-${index}`);
633
+ const matches = Object.entries(cv.variants).every(([k, expected]) => {
634
+ const options = variants[k];
635
+ if (!options) return false;
636
+ const selected = normalizeSelection(resolvedSelections[k], options);
637
+ if (selected == null) return false;
638
+ if (Array.isArray(expected)) {
639
+ return expected.some((value) => normalizeSelection(value, options) === selected);
640
+ }
641
+ return normalizeSelection(expected, options) === selected;
642
+ });
643
+ if (matches) {
644
+ const cn = compoundClassByIndex[index];
645
+ if (cn) classes.push(cn);
646
+ }
397
647
  }
398
648
  );
399
649
  return classes.join(" ");
400
650
  });
401
651
  }
652
+ function normalizeSelection(value, options) {
653
+ if (value == null) return void 0;
654
+ if (typeof value === "boolean") {
655
+ const boolKey = String(value);
656
+ if (Object.prototype.hasOwnProperty.call(options, boolKey)) return boolKey;
657
+ if (value === false) return void 0;
658
+ return boolKey;
659
+ }
660
+ return String(value);
661
+ }
662
+ function createSlotComponent(namespace, config) {
663
+ const { slots, base = {}, variants = {}, compoundVariants = [], defaultVariants = {} } = config;
664
+ if (process.env.NODE_ENV !== "production") {
665
+ if (registeredNamespaces.has(namespace)) {
666
+ console.warn(
667
+ `[typestyles] styles.component('${namespace}', ...) called more than once. This will cause class name collisions. Each namespace should be unique.`
668
+ );
669
+ }
670
+ }
671
+ registeredNamespaces.add(namespace);
672
+ const rules = [];
673
+ const baseClassBySlot = {};
674
+ for (const [slot, properties] of Object.entries(base)) {
675
+ const className = buildRecipeClassName(namespace, slot, properties);
676
+ baseClassBySlot[slot] = className;
677
+ rules.push(...serializeStyle(`.${className}`, properties));
678
+ }
679
+ const variantClassByKey = {};
680
+ for (const [dimension, options] of Object.entries(variants)) {
681
+ for (const [option, slotStyles] of Object.entries(options)) {
682
+ for (const [slot, properties] of Object.entries(slotStyles)) {
683
+ const segment = `${slot}-${dimension}-${option}`;
684
+ const className = buildRecipeClassName(namespace, segment, properties);
685
+ variantClassByKey[segment] = className;
686
+ rules.push(...serializeStyle(`.${className}`, properties));
687
+ }
688
+ }
689
+ }
690
+ const slotCompoundClassByKey = {};
691
+ compoundVariants.forEach(
692
+ (cv, index) => {
693
+ for (const [slot, properties] of Object.entries(cv.style)) {
694
+ const segment = `${slot}-compound-${index}`;
695
+ const className = buildRecipeClassName(namespace, segment, properties);
696
+ slotCompoundClassByKey[`${slot}::${index}`] = className;
697
+ rules.push(...serializeStyle(`.${className}`, properties));
698
+ }
699
+ }
700
+ );
701
+ insertRules(rules);
702
+ return ((selections = {}) => {
703
+ const classes = Object.fromEntries(slots.map((slot) => [slot, []]));
704
+ const resolvedSelections = {};
705
+ for (const [dimension, options] of Object.entries(variants)) {
706
+ const optionMap = options;
707
+ const explicit = selections[dimension];
708
+ const fallback = defaultVariants[dimension];
709
+ resolvedSelections[dimension] = normalizeSelection(explicit ?? fallback, optionMap);
710
+ }
711
+ for (const slot of Object.keys(base)) {
712
+ const cn = baseClassBySlot[slot];
713
+ if (cn && classes[slot]) classes[slot].push(cn);
714
+ }
715
+ for (const [dimension, options] of Object.entries(variants)) {
716
+ const optionMap = options;
717
+ const selected = normalizeSelection(resolvedSelections[dimension], optionMap);
718
+ if (selected == null) continue;
719
+ const slotStyles = optionMap[selected];
720
+ if (!slotStyles) continue;
721
+ for (const slot of Object.keys(slotStyles)) {
722
+ const segment = `${slot}-${dimension}-${selected}`;
723
+ const cn = variantClassByKey[segment];
724
+ if (cn && classes[slot]) classes[slot].push(cn);
725
+ }
726
+ }
727
+ compoundVariants.forEach(
728
+ (cv, index) => {
729
+ const matches = Object.entries(cv.variants).every(([k, expected]) => {
730
+ const options = variants[k];
731
+ if (!options) return false;
732
+ const selected = normalizeSelection(resolvedSelections[k], options);
733
+ if (selected == null) return false;
734
+ if (Array.isArray(expected)) {
735
+ return expected.some((value) => normalizeSelection(value, options) === selected);
736
+ }
737
+ return normalizeSelection(expected, options) === selected;
738
+ });
739
+ if (!matches) return;
740
+ for (const slot of Object.keys(cv.style)) {
741
+ const cn = slotCompoundClassByKey[`${slot}::${index}`];
742
+ if (cn && classes[slot]) classes[slot].push(cn);
743
+ }
744
+ }
745
+ );
746
+ return Object.fromEntries(
747
+ slots.map((slot) => [slot, classes[slot].join(" ")])
748
+ );
749
+ });
750
+ }
402
751
 
403
752
  // src/global.ts
404
753
  function globalStyle(selector, properties) {
@@ -435,7 +784,10 @@ function assignVars(vars) {
435
784
  // src/index.ts
436
785
  var styles = {
437
786
  create: createStyles,
787
+ class: createClass,
788
+ hashClass: createHashClass,
438
789
  component: createComponent,
790
+ withUtils: createStylesWithUtils,
439
791
  compose
440
792
  };
441
793
  var global = {
@@ -454,13 +806,16 @@ var color = color_exports;
454
806
 
455
807
  exports.assignVars = assignVars;
456
808
  exports.color = color;
809
+ exports.configureClassNaming = configureClassNaming;
457
810
  exports.createVar = createVar;
458
811
  exports.flushSync = flushSync;
812
+ exports.getClassNamingConfig = getClassNamingConfig;
459
813
  exports.getRegisteredCss = getRegisteredCss;
460
814
  exports.global = global;
461
815
  exports.insertRules = insertRules;
462
816
  exports.keyframes = keyframes;
463
817
  exports.reset = reset;
818
+ exports.resetClassNaming = resetClassNaming;
464
819
  exports.styles = styles;
465
820
  exports.tokens = tokens;
466
821
  //# sourceMappingURL=index.cjs.map