@tenphi/tasty 1.2.0 → 1.3.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.
@@ -0,0 +1,31 @@
1
+ import { Styles } from "./styles/types.js";
2
+ import { ServerStyleCollector } from "./ssr/collector.js";
3
+
4
+ //#region src/compute-styles.d.ts
5
+ interface ComputeStylesResult {
6
+ className: string;
7
+ /** CSS text to emit as an inline <style> tag (RSC mode only). */
8
+ css?: string;
9
+ }
10
+ interface ComputeStylesOptions {
11
+ ssrCollector?: ServerStyleCollector | null;
12
+ }
13
+ /**
14
+ * Synchronous, hook-free style computation.
15
+ *
16
+ * Resolves recipes, categorizes style keys into chunks, renders CSS rules,
17
+ * allocates class names, and injects / collects / returns the CSS.
18
+ *
19
+ * Three code paths:
20
+ * 1. SSR collector — discovered via ALS or passed explicitly; CSS collected
21
+ * 2. RSC inline — no collector and no `document`; CSS returned as `result.css`
22
+ * for the caller to emit as an inline `<style>` tag
23
+ * 3. Client inject — CSS injected synchronously into the DOM (idempotent)
24
+ *
25
+ * @param styles - Tasty styles object (or undefined for no styles)
26
+ * @param options - Optional SSR collector override
27
+ */
28
+ declare function computeStyles(styles: Styles | undefined, options?: ComputeStylesOptions): ComputeStylesResult;
29
+ //#endregion
30
+ export { ComputeStylesOptions, ComputeStylesResult, computeStyles };
31
+ //# sourceMappingURL=compute-styles.d.ts.map
@@ -0,0 +1,356 @@
1
+ import { extractLocalProperties, hasLocalProperties } from "./properties/index.js";
2
+ import { extractLocalFontFace, fontFaceContentHash, formatFontFaceRule, hasLocalFontFace } from "./font-face/index.js";
3
+ import { extractLocalCounterStyle, formatCounterStyleRule, hasLocalCounterStyle } from "./counter-style/index.js";
4
+ import { renderStyles } from "./pipeline/index.js";
5
+ import { getConfig, getEffectiveProperties, getGlobalConfigTokens, getGlobalCounterStyle, getGlobalFontFace, getGlobalKeyframes, hasGlobalKeyframes } from "./config.js";
6
+ import { categorizeStyleKeys } from "./chunks/definitions.js";
7
+ import { generateChunkCacheKey } from "./chunks/cacheKey.js";
8
+ import { renderStylesForChunk } from "./chunks/renderChunk.js";
9
+ import { counterStyle, fontFace, inject, keyframes, property } from "./injector/index.js";
10
+ import { extractAnimationNamesFromStyles, extractLocalKeyframes, filterUsedKeyframes, hasLocalKeyframes, mergeKeyframes, replaceAnimationNames } from "./keyframes/index.js";
11
+ import { formatPropertyCSS } from "./ssr/format-property.js";
12
+ import { collectAutoInferredProperties } from "./ssr/collect-auto-properties.js";
13
+ import { formatGlobalRules } from "./ssr/format-global-rules.js";
14
+ import { formatKeyframesCSS } from "./ssr/format-keyframes.js";
15
+ import { formatRules } from "./ssr/format-rules.js";
16
+ import { getRegisteredSSRCollector } from "./ssr/ssr-collector-ref.js";
17
+ import { hasKeys } from "./utils/has-keys.js";
18
+ import { resolveRecipes } from "./utils/resolve-recipes.js";
19
+ import { cache } from "react";
20
+ //#region src/compute-styles.ts
21
+ /**
22
+ * Hook-free, synchronous style computation.
23
+ *
24
+ * Extracts the core logic from useStyles() into a plain function that can
25
+ * be called during React render without any hooks. Three code paths:
26
+ *
27
+ * 1. SSR collector — styles collected via ServerStyleCollector
28
+ * 2. Client inject — styles injected synchronously into the DOM
29
+ * 3. RSC inline — styles returned as CSS strings for inline <style> emission
30
+ *
31
+ * This enables tasty() components to work as React Server Components.
32
+ */
33
+ const EMPTY_RESULT = { className: "" };
34
+ /**
35
+ * Per-request RSC style cache using React.cache.
36
+ * React.cache provides per-request memoization in Server Components,
37
+ * so each request gets its own isolated cache.
38
+ */
39
+ const getRSCCache = cache(() => ({
40
+ cacheKeyToClassName: /* @__PURE__ */ new Map(),
41
+ classCounter: 0,
42
+ emittedKeys: /* @__PURE__ */ new Set(),
43
+ internalsEmitted: false
44
+ }));
45
+ function rscAllocateClassName(rscCache, cacheKey) {
46
+ const existing = rscCache.cacheKeyToClassName.get(cacheKey);
47
+ if (existing) return {
48
+ className: existing,
49
+ isNew: false
50
+ };
51
+ const className = `r${rscCache.classCounter++}`;
52
+ rscCache.cacheKeyToClassName.set(cacheKey, className);
53
+ return {
54
+ className,
55
+ isNew: true
56
+ };
57
+ }
58
+ /**
59
+ * Collect internals CSS for RSC — mirrors ServerStyleCollector.collectInternals().
60
+ * Emitted once per request (tracked via rscCache.internalsEmitted).
61
+ */
62
+ function collectInternalsRSC(rscCache) {
63
+ if (rscCache.internalsEmitted) return "";
64
+ rscCache.internalsEmitted = true;
65
+ const parts = [];
66
+ for (const [token, definition] of Object.entries(getEffectiveProperties())) {
67
+ const css = formatPropertyCSS(token, definition);
68
+ if (css) parts.push(css);
69
+ }
70
+ const tokenStyles = getGlobalConfigTokens();
71
+ if (tokenStyles && Object.keys(tokenStyles).length > 0) {
72
+ const tokenRules = renderStyles(tokenStyles, ":root");
73
+ if (tokenRules.length > 0) {
74
+ const css = formatGlobalRules(tokenRules);
75
+ if (css) parts.push(css);
76
+ }
77
+ }
78
+ const globalFF = getGlobalFontFace();
79
+ if (globalFF) for (const [family, input] of Object.entries(globalFF)) {
80
+ const descriptors = Array.isArray(input) ? input : [input];
81
+ for (const desc of descriptors) parts.push(formatFontFaceRule(family, desc));
82
+ }
83
+ const globalCS = getGlobalCounterStyle();
84
+ if (globalCS) for (const [name, descriptors] of Object.entries(globalCS)) parts.push(formatCounterStyleRule(name, descriptors));
85
+ return parts.join("\n");
86
+ }
87
+ /**
88
+ * Collect per-component ancillary CSS (keyframes, @property, font-face,
89
+ * counter-style) for RSC mode.
90
+ */
91
+ function collectAncillaryRSC(rscCache, styles) {
92
+ const parts = [];
93
+ const usedKf = getUsedKeyframes(styles);
94
+ if (usedKf) for (const [name, steps] of Object.entries(usedKf)) {
95
+ const key = `__kf:${name}`;
96
+ if (!rscCache.emittedKeys.has(key)) {
97
+ rscCache.emittedKeys.add(key);
98
+ parts.push(formatKeyframesCSS(name, steps));
99
+ }
100
+ }
101
+ if (hasLocalProperties(styles)) {
102
+ const localProperties = extractLocalProperties(styles);
103
+ if (localProperties) for (const [token, definition] of Object.entries(localProperties)) {
104
+ const key = `__prop:${token}`;
105
+ if (!rscCache.emittedKeys.has(key)) {
106
+ rscCache.emittedKeys.add(key);
107
+ const css = formatPropertyCSS(token, definition);
108
+ if (css) parts.push(css);
109
+ }
110
+ }
111
+ }
112
+ if (hasLocalFontFace(styles)) {
113
+ const localFontFace = extractLocalFontFace(styles);
114
+ if (localFontFace) for (const [family, input] of Object.entries(localFontFace)) {
115
+ const descriptors = Array.isArray(input) ? input : [input];
116
+ for (const desc of descriptors) {
117
+ const key = `__ff:${fontFaceContentHash(family, desc)}`;
118
+ if (!rscCache.emittedKeys.has(key)) {
119
+ rscCache.emittedKeys.add(key);
120
+ parts.push(formatFontFaceRule(family, desc));
121
+ }
122
+ }
123
+ }
124
+ }
125
+ if (hasLocalCounterStyle(styles)) {
126
+ const localCounterStyle = extractLocalCounterStyle(styles);
127
+ if (localCounterStyle) for (const [name, descriptors] of Object.entries(localCounterStyle)) {
128
+ const key = `__cs:${name}`;
129
+ if (!rscCache.emittedKeys.has(key)) {
130
+ rscCache.emittedKeys.add(key);
131
+ parts.push(formatCounterStyleRule(name, descriptors));
132
+ }
133
+ }
134
+ }
135
+ return parts.join("\n");
136
+ }
137
+ /**
138
+ * Process all chunks in RSC mode: render CSS to strings, allocate classNames,
139
+ * and return combined { className, css }.
140
+ */
141
+ function computeStylesRSC(styles, chunkMap) {
142
+ const rscCache = getRSCCache();
143
+ const cssParts = [];
144
+ const classNames = [];
145
+ const internalsCSS = collectInternalsRSC(rscCache);
146
+ if (internalsCSS) cssParts.push(internalsCSS);
147
+ for (const [chunkName, chunkStyleKeys] of chunkMap) {
148
+ if (chunkStyleKeys.length === 0) continue;
149
+ const { className, isNew } = rscAllocateClassName(rscCache, generateChunkCacheKey(styles, chunkName, chunkStyleKeys));
150
+ classNames.push(className);
151
+ if (isNew) {
152
+ const renderResult = renderStylesForChunk(styles, chunkName, chunkStyleKeys);
153
+ if (renderResult.rules.length > 0) {
154
+ const css = formatRules(renderResult.rules, className);
155
+ if (css) cssParts.push(css);
156
+ }
157
+ }
158
+ }
159
+ const ancillaryCSS = collectAncillaryRSC(rscCache, styles);
160
+ if (ancillaryCSS) cssParts.push(ancillaryCSS);
161
+ if (classNames.length === 0) return EMPTY_RESULT;
162
+ const css = cssParts.join("\n");
163
+ return {
164
+ className: classNames.join(" "),
165
+ css: css || void 0
166
+ };
167
+ }
168
+ /**
169
+ * Get keyframes that are actually used in styles.
170
+ * Returns null if no keyframes are used (fast path for zero overhead).
171
+ */
172
+ function getUsedKeyframes(styles) {
173
+ const hasLocal = hasLocalKeyframes(styles);
174
+ const hasGlobal = hasGlobalKeyframes();
175
+ if (!hasLocal && !hasGlobal) return null;
176
+ const usedNames = extractAnimationNamesFromStyles(styles);
177
+ if (usedNames.size === 0) return null;
178
+ return filterUsedKeyframes(mergeKeyframes(hasLocal ? extractLocalKeyframes(styles) : null, hasGlobal ? getGlobalKeyframes() : null), usedNames);
179
+ }
180
+ /**
181
+ * Process a chunk on the SSR path: allocate via collector, render, collect CSS.
182
+ */
183
+ function processChunkSSR(collector, styles, chunkName, styleKeys) {
184
+ if (styleKeys.length === 0) return null;
185
+ const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);
186
+ const { className, isNewAllocation } = collector.allocateClassName(cacheKey);
187
+ if (isNewAllocation) {
188
+ const renderResult = renderStylesForChunk(styles, chunkName, styleKeys);
189
+ if (renderResult.rules.length > 0) {
190
+ collector.collectChunk(cacheKey, className, renderResult.rules);
191
+ return {
192
+ name: chunkName,
193
+ styleKeys,
194
+ cacheKey,
195
+ renderResult,
196
+ className
197
+ };
198
+ }
199
+ return null;
200
+ }
201
+ return {
202
+ name: chunkName,
203
+ styleKeys,
204
+ cacheKey,
205
+ renderResult: { rules: [] },
206
+ className
207
+ };
208
+ }
209
+ /**
210
+ * Process a chunk on the client: render, allocate className, and inject
211
+ * CSS synchronously. The injector's cache makes this idempotent.
212
+ */
213
+ function processChunkSync(styles, chunkName, styleKeys) {
214
+ if (styleKeys.length === 0) return null;
215
+ const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);
216
+ const renderResult = renderStylesForChunk(styles, chunkName, styleKeys, cacheKey);
217
+ if (renderResult.rules.length === 0) return null;
218
+ const { className } = inject(renderResult.rules, { cacheKey });
219
+ return {
220
+ name: chunkName,
221
+ styleKeys,
222
+ cacheKey,
223
+ renderResult,
224
+ className
225
+ };
226
+ }
227
+ /**
228
+ * Inject keyframes synchronously and return a name replacement map.
229
+ * On the client, keyframes are injected into the DOM.
230
+ */
231
+ function injectKeyframesSync(usedKeyframes) {
232
+ let nameMap = null;
233
+ for (const [name, steps] of Object.entries(usedKeyframes)) {
234
+ const injectedName = keyframes(steps, { name }).toString();
235
+ if (injectedName !== name) {
236
+ if (!nameMap) nameMap = /* @__PURE__ */ new Map();
237
+ nameMap.set(name, injectedName);
238
+ }
239
+ }
240
+ return nameMap;
241
+ }
242
+ /**
243
+ * Inject chunk rules synchronously, replacing animation names if needed.
244
+ */
245
+ function injectChunkRulesSync(chunks, nameMap) {
246
+ for (const chunk of chunks) if (chunk.renderResult.rules.length > 0) inject(nameMap ? chunk.renderResult.rules.map((rule) => ({
247
+ ...rule,
248
+ declarations: replaceAnimationNames(rule.declarations, nameMap)
249
+ })) : chunk.renderResult.rules, { cacheKey: chunk.cacheKey });
250
+ }
251
+ /**
252
+ * Inject all ancillary rules (properties, font-faces, counter-styles) synchronously.
253
+ */
254
+ function injectAncillarySync(styles) {
255
+ if (hasLocalProperties(styles)) {
256
+ const localProperties = extractLocalProperties(styles);
257
+ if (localProperties) for (const [token, definition] of Object.entries(localProperties)) property(token, definition);
258
+ }
259
+ if (hasLocalFontFace(styles)) {
260
+ const localFontFace = extractLocalFontFace(styles);
261
+ if (localFontFace) for (const [family, input] of Object.entries(localFontFace)) {
262
+ const descriptors = Array.isArray(input) ? input : [input];
263
+ for (const desc of descriptors) fontFace(family, desc);
264
+ }
265
+ }
266
+ if (hasLocalCounterStyle(styles)) {
267
+ const localCounterStyle = extractLocalCounterStyle(styles);
268
+ if (localCounterStyle) for (const [name, descriptors] of Object.entries(localCounterStyle)) counterStyle(name, descriptors);
269
+ }
270
+ }
271
+ /**
272
+ * Collect all ancillary rules into the SSR collector.
273
+ */
274
+ function collectAncillarySSR(collector, styles, chunks) {
275
+ const usedKf = getUsedKeyframes(styles);
276
+ if (usedKf) for (const [name, steps] of Object.entries(usedKf)) {
277
+ const css = formatKeyframesCSS(name, steps);
278
+ collector.collectKeyframes(name, css);
279
+ }
280
+ if (hasLocalProperties(styles)) {
281
+ const localProperties = extractLocalProperties(styles);
282
+ if (localProperties) for (const [token, definition] of Object.entries(localProperties)) {
283
+ const css = formatPropertyCSS(token, definition);
284
+ if (css) collector.collectProperty(token, css);
285
+ }
286
+ }
287
+ if (hasLocalFontFace(styles)) {
288
+ const localFontFace = extractLocalFontFace(styles);
289
+ if (localFontFace) for (const [family, input] of Object.entries(localFontFace)) {
290
+ const descriptors = Array.isArray(input) ? input : [input];
291
+ for (const desc of descriptors) {
292
+ const hash = fontFaceContentHash(family, desc);
293
+ const css = formatFontFaceRule(family, desc);
294
+ collector.collectFontFace(hash, css);
295
+ }
296
+ }
297
+ }
298
+ if (hasLocalCounterStyle(styles)) {
299
+ const localCounterStyle = extractLocalCounterStyle(styles);
300
+ if (localCounterStyle) for (const [name, descriptors] of Object.entries(localCounterStyle)) {
301
+ const css = formatCounterStyleRule(name, descriptors);
302
+ collector.collectCounterStyle(name, css);
303
+ }
304
+ }
305
+ if (getConfig().autoPropertyTypes !== false) {
306
+ const allRules = chunks.flatMap((c) => c.renderResult.rules);
307
+ if (allRules.length > 0) collectAutoInferredProperties(allRules, collector, styles);
308
+ }
309
+ }
310
+ /**
311
+ * Synchronous, hook-free style computation.
312
+ *
313
+ * Resolves recipes, categorizes style keys into chunks, renders CSS rules,
314
+ * allocates class names, and injects / collects / returns the CSS.
315
+ *
316
+ * Three code paths:
317
+ * 1. SSR collector — discovered via ALS or passed explicitly; CSS collected
318
+ * 2. RSC inline — no collector and no `document`; CSS returned as `result.css`
319
+ * for the caller to emit as an inline `<style>` tag
320
+ * 3. Client inject — CSS injected synchronously into the DOM (idempotent)
321
+ *
322
+ * @param styles - Tasty styles object (or undefined for no styles)
323
+ * @param options - Optional SSR collector override
324
+ */
325
+ function computeStyles(styles, options) {
326
+ if (!styles || !hasKeys(styles)) return EMPTY_RESULT;
327
+ const resolved = resolveRecipes(styles);
328
+ const chunkMap = categorizeStyleKeys(resolved);
329
+ const collector = options?.ssrCollector !== void 0 ? options.ssrCollector : getRegisteredSSRCollector();
330
+ const chunks = [];
331
+ if (collector) {
332
+ collector.collectInternals();
333
+ for (const [chunkName, chunkStyleKeys] of chunkMap) {
334
+ const chunk = processChunkSSR(collector, resolved, chunkName, chunkStyleKeys);
335
+ if (chunk) chunks.push(chunk);
336
+ }
337
+ collectAncillarySSR(collector, resolved, chunks);
338
+ } else if (typeof document === "undefined") return computeStylesRSC(resolved, chunkMap);
339
+ else {
340
+ injectAncillarySync(resolved);
341
+ const usedKf = getUsedKeyframes(resolved);
342
+ const nameMap = usedKf ? injectKeyframesSync(usedKf) : null;
343
+ for (const [chunkName, chunkStyleKeys] of chunkMap) {
344
+ const chunk = processChunkSync(resolved, chunkName, chunkStyleKeys);
345
+ if (chunk) chunks.push(chunk);
346
+ }
347
+ if (nameMap) injectChunkRulesSync(chunks, nameMap);
348
+ }
349
+ if (chunks.length === 0) return EMPTY_RESULT;
350
+ if (chunks.length === 1) return { className: chunks[0].className };
351
+ return { className: chunks.map((c) => c.className).join(" ") };
352
+ }
353
+ //#endregion
354
+ export { computeStyles };
355
+
356
+ //# sourceMappingURL=compute-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-styles.js","names":[],"sources":["../src/compute-styles.ts"],"sourcesContent":["/**\n * Hook-free, synchronous style computation.\n *\n * Extracts the core logic from useStyles() into a plain function that can\n * be called during React render without any hooks. Three code paths:\n *\n * 1. SSR collector — styles collected via ServerStyleCollector\n * 2. Client inject — styles injected synchronously into the DOM\n * 3. RSC inline — styles returned as CSS strings for inline <style> emission\n *\n * This enables tasty() components to work as React Server Components.\n */\n\nimport { cache } from 'react';\n\nimport {\n categorizeStyleKeys,\n generateChunkCacheKey,\n renderStylesForChunk,\n} from './chunks';\nimport {\n getConfig,\n getGlobalConfigTokens,\n getGlobalCounterStyle,\n getEffectiveProperties,\n getGlobalFontFace,\n getGlobalKeyframes,\n hasGlobalKeyframes,\n} from './config';\nimport {\n counterStyle,\n fontFace,\n inject,\n keyframes,\n property,\n} from './injector';\nimport type { FontFaceDescriptors, KeyframesSteps } from './injector/types';\nimport {\n extractLocalCounterStyle,\n formatCounterStyleRule,\n hasLocalCounterStyle,\n} from './counter-style';\nimport {\n extractLocalFontFace,\n fontFaceContentHash,\n formatFontFaceRule,\n hasLocalFontFace,\n} from './font-face';\nimport {\n extractAnimationNamesFromStyles,\n extractLocalKeyframes,\n filterUsedKeyframes,\n hasLocalKeyframes,\n mergeKeyframes,\n replaceAnimationNames,\n} from './keyframes';\nimport type { RenderResult, StyleResult } from './pipeline';\nimport { renderStyles } from './pipeline';\nimport { extractLocalProperties, hasLocalProperties } from './properties';\nimport { collectAutoInferredProperties } from './ssr/collect-auto-properties';\nimport type { ServerStyleCollector } from './ssr/collector';\nimport { formatGlobalRules } from './ssr/format-global-rules';\nimport { formatKeyframesCSS } from './ssr/format-keyframes';\nimport { formatPropertyCSS } from './ssr/format-property';\nimport { formatRules } from './ssr/format-rules';\nimport { getRegisteredSSRCollector } from './ssr/ssr-collector-ref';\nimport type { Styles } from './styles/types';\nimport { hasKeys } from './utils/has-keys';\nimport { resolveRecipes } from './utils/resolve-recipes';\n\nexport interface ComputeStylesResult {\n className: string;\n /** CSS text to emit as an inline <style> tag (RSC mode only). */\n css?: string;\n}\n\nexport interface ComputeStylesOptions {\n ssrCollector?: ServerStyleCollector | null;\n}\n\ninterface ProcessedChunk {\n name: string;\n styleKeys: string[];\n cacheKey: string;\n renderResult: RenderResult;\n className: string;\n}\n\nconst EMPTY_RESULT: ComputeStylesResult = { className: '' };\n\n// ---------------------------------------------------------------------------\n// RSC (React Server Components) inline style support\n// ---------------------------------------------------------------------------\n\ninterface RSCStyleCache {\n cacheKeyToClassName: Map<string, string>;\n classCounter: number;\n emittedKeys: Set<string>;\n internalsEmitted: boolean;\n}\n\n/**\n * Per-request RSC style cache using React.cache.\n * React.cache provides per-request memoization in Server Components,\n * so each request gets its own isolated cache.\n */\nconst getRSCCache = cache(\n (): RSCStyleCache => ({\n cacheKeyToClassName: new Map(),\n classCounter: 0,\n emittedKeys: new Set(),\n internalsEmitted: false,\n }),\n);\n\nfunction rscAllocateClassName(\n rscCache: RSCStyleCache,\n cacheKey: string,\n): { className: string; isNew: boolean } {\n const existing = rscCache.cacheKeyToClassName.get(cacheKey);\n if (existing) return { className: existing, isNew: false };\n\n // Use 'r' prefix to avoid collisions with SSR collector's 't' prefix\n const className = `r${rscCache.classCounter++}`;\n rscCache.cacheKeyToClassName.set(cacheKey, className);\n return { className, isNew: true };\n}\n\n/**\n * Collect internals CSS for RSC — mirrors ServerStyleCollector.collectInternals().\n * Emitted once per request (tracked via rscCache.internalsEmitted).\n */\nfunction collectInternalsRSC(rscCache: RSCStyleCache): string {\n if (rscCache.internalsEmitted) return '';\n rscCache.internalsEmitted = true;\n\n const parts: string[] = [];\n\n for (const [token, definition] of Object.entries(getEffectiveProperties())) {\n const css = formatPropertyCSS(token, definition);\n if (css) parts.push(css);\n }\n\n const tokenStyles = getGlobalConfigTokens();\n if (tokenStyles && Object.keys(tokenStyles).length > 0) {\n const tokenRules = renderStyles(tokenStyles, ':root') as StyleResult[];\n if (tokenRules.length > 0) {\n const css = formatGlobalRules(tokenRules);\n if (css) parts.push(css);\n }\n }\n\n const globalFF = getGlobalFontFace();\n if (globalFF) {\n for (const [family, input] of Object.entries(globalFF)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n parts.push(formatFontFaceRule(family, desc));\n }\n }\n }\n\n const globalCS = getGlobalCounterStyle();\n if (globalCS) {\n for (const [name, descriptors] of Object.entries(globalCS)) {\n parts.push(formatCounterStyleRule(name, descriptors));\n }\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Collect per-component ancillary CSS (keyframes, @property, font-face,\n * counter-style) for RSC mode.\n */\nfunction collectAncillaryRSC(rscCache: RSCStyleCache, styles: Styles): string {\n const parts: string[] = [];\n\n const usedKf = getUsedKeyframes(styles);\n if (usedKf) {\n for (const [name, steps] of Object.entries(usedKf)) {\n const key = `__kf:${name}`;\n if (!rscCache.emittedKeys.has(key)) {\n rscCache.emittedKeys.add(key);\n parts.push(formatKeyframesCSS(name, steps));\n }\n }\n }\n\n if (hasLocalProperties(styles)) {\n const localProperties = extractLocalProperties(styles);\n if (localProperties) {\n for (const [token, definition] of Object.entries(localProperties)) {\n const key = `__prop:${token}`;\n if (!rscCache.emittedKeys.has(key)) {\n rscCache.emittedKeys.add(key);\n const css = formatPropertyCSS(token, definition);\n if (css) parts.push(css);\n }\n }\n }\n }\n\n if (hasLocalFontFace(styles)) {\n const localFontFace = extractLocalFontFace(styles);\n if (localFontFace) {\n for (const [family, input] of Object.entries(localFontFace)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n const hash = fontFaceContentHash(family, desc);\n const key = `__ff:${hash}`;\n if (!rscCache.emittedKeys.has(key)) {\n rscCache.emittedKeys.add(key);\n parts.push(formatFontFaceRule(family, desc));\n }\n }\n }\n }\n }\n\n if (hasLocalCounterStyle(styles)) {\n const localCounterStyle = extractLocalCounterStyle(styles);\n if (localCounterStyle) {\n for (const [name, descriptors] of Object.entries(localCounterStyle)) {\n const key = `__cs:${name}`;\n if (!rscCache.emittedKeys.has(key)) {\n rscCache.emittedKeys.add(key);\n parts.push(formatCounterStyleRule(name, descriptors));\n }\n }\n }\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Process all chunks in RSC mode: render CSS to strings, allocate classNames,\n * and return combined { className, css }.\n */\nfunction computeStylesRSC(\n styles: Styles,\n chunkMap: Map<string, string[]>,\n): ComputeStylesResult {\n const rscCache = getRSCCache();\n const cssParts: string[] = [];\n const classNames: string[] = [];\n\n const internalsCSS = collectInternalsRSC(rscCache);\n if (internalsCSS) cssParts.push(internalsCSS);\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n if (chunkStyleKeys.length === 0) continue;\n\n const cacheKey = generateChunkCacheKey(styles, chunkName, chunkStyleKeys);\n const { className, isNew } = rscAllocateClassName(rscCache, cacheKey);\n classNames.push(className);\n\n if (isNew) {\n const renderResult = renderStylesForChunk(\n styles,\n chunkName,\n chunkStyleKeys,\n );\n if (renderResult.rules.length > 0) {\n const css = formatRules(renderResult.rules, className);\n if (css) cssParts.push(css);\n }\n }\n }\n\n const ancillaryCSS = collectAncillaryRSC(rscCache, styles);\n if (ancillaryCSS) cssParts.push(ancillaryCSS);\n\n if (classNames.length === 0) return EMPTY_RESULT;\n\n const css = cssParts.join('\\n');\n\n return {\n className: classNames.join(' '),\n css: css || undefined,\n };\n}\n\n/**\n * Get keyframes that are actually used in styles.\n * Returns null if no keyframes are used (fast path for zero overhead).\n */\nfunction getUsedKeyframes(\n styles: Styles,\n): Record<string, KeyframesSteps> | null {\n const hasLocal = hasLocalKeyframes(styles);\n const hasGlobal = hasGlobalKeyframes();\n if (!hasLocal && !hasGlobal) return null;\n\n const usedNames = extractAnimationNamesFromStyles(styles);\n if (usedNames.size === 0) return null;\n\n const local = hasLocal ? extractLocalKeyframes(styles) : null;\n const global = hasGlobal ? getGlobalKeyframes() : null;\n const allKeyframes = mergeKeyframes(local, global);\n\n return filterUsedKeyframes(allKeyframes, usedNames);\n}\n\n/**\n * Process a chunk on the SSR path: allocate via collector, render, collect CSS.\n */\nfunction processChunkSSR(\n collector: ServerStyleCollector,\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): ProcessedChunk | null {\n if (styleKeys.length === 0) return null;\n\n const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);\n const { className, isNewAllocation } = collector.allocateClassName(cacheKey);\n\n if (isNewAllocation) {\n const renderResult = renderStylesForChunk(styles, chunkName, styleKeys);\n if (renderResult.rules.length > 0) {\n collector.collectChunk(cacheKey, className, renderResult.rules);\n return { name: chunkName, styleKeys, cacheKey, renderResult, className };\n }\n return null;\n }\n\n return {\n name: chunkName,\n styleKeys,\n cacheKey,\n renderResult: { rules: [] },\n className,\n };\n}\n\n/**\n * Process a chunk on the client: render, allocate className, and inject\n * CSS synchronously. The injector's cache makes this idempotent.\n */\nfunction processChunkSync(\n styles: Styles,\n chunkName: string,\n styleKeys: string[],\n): ProcessedChunk | null {\n if (styleKeys.length === 0) return null;\n\n const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);\n const renderResult = renderStylesForChunk(\n styles,\n chunkName,\n styleKeys,\n cacheKey,\n );\n if (renderResult.rules.length === 0) return null;\n\n const { className } = inject(renderResult.rules, { cacheKey });\n\n return { name: chunkName, styleKeys, cacheKey, renderResult, className };\n}\n\n/**\n * Inject keyframes synchronously and return a name replacement map.\n * On the client, keyframes are injected into the DOM.\n */\nfunction injectKeyframesSync(\n usedKeyframes: Record<string, KeyframesSteps>,\n): Map<string, string> | null {\n let nameMap: Map<string, string> | null = null;\n\n for (const [name, steps] of Object.entries(usedKeyframes)) {\n const result = keyframes(steps, { name });\n const injectedName = result.toString();\n if (injectedName !== name) {\n if (!nameMap) nameMap = new Map();\n nameMap.set(name, injectedName);\n }\n }\n\n return nameMap;\n}\n\n/**\n * Inject chunk rules synchronously, replacing animation names if needed.\n */\nfunction injectChunkRulesSync(\n chunks: ProcessedChunk[],\n nameMap: Map<string, string> | null,\n): void {\n for (const chunk of chunks) {\n if (chunk.renderResult.rules.length > 0) {\n const rulesToInject: StyleResult[] = nameMap\n ? chunk.renderResult.rules.map((rule) => ({\n ...rule,\n declarations: replaceAnimationNames(rule.declarations, nameMap!),\n }))\n : chunk.renderResult.rules;\n\n inject(rulesToInject, { cacheKey: chunk.cacheKey });\n }\n }\n}\n\n/**\n * Inject all ancillary rules (properties, font-faces, counter-styles) synchronously.\n */\nfunction injectAncillarySync(styles: Styles): void {\n if (hasLocalProperties(styles)) {\n const localProperties = extractLocalProperties(styles);\n if (localProperties) {\n for (const [token, definition] of Object.entries(localProperties)) {\n property(token, definition);\n }\n }\n }\n\n if (hasLocalFontFace(styles)) {\n const localFontFace = extractLocalFontFace(styles);\n if (localFontFace) {\n for (const [family, input] of Object.entries(localFontFace)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n fontFace(family, desc);\n }\n }\n }\n }\n\n if (hasLocalCounterStyle(styles)) {\n const localCounterStyle = extractLocalCounterStyle(styles);\n if (localCounterStyle) {\n for (const [name, descriptors] of Object.entries(localCounterStyle)) {\n counterStyle(name, descriptors);\n }\n }\n }\n}\n\n/**\n * Collect all ancillary rules into the SSR collector.\n */\nfunction collectAncillarySSR(\n collector: ServerStyleCollector,\n styles: Styles,\n chunks: ProcessedChunk[],\n): void {\n const usedKf = getUsedKeyframes(styles);\n if (usedKf) {\n for (const [name, steps] of Object.entries(usedKf)) {\n const css = formatKeyframesCSS(name, steps);\n collector.collectKeyframes(name, css);\n }\n }\n\n if (hasLocalProperties(styles)) {\n const localProperties = extractLocalProperties(styles);\n if (localProperties) {\n for (const [token, definition] of Object.entries(localProperties)) {\n const css = formatPropertyCSS(token, definition);\n if (css) {\n collector.collectProperty(token, css);\n }\n }\n }\n }\n\n if (hasLocalFontFace(styles)) {\n const localFontFace = extractLocalFontFace(styles);\n if (localFontFace) {\n for (const [family, input] of Object.entries(localFontFace)) {\n const descriptors: FontFaceDescriptors[] = Array.isArray(input)\n ? input\n : [input];\n for (const desc of descriptors) {\n const hash = fontFaceContentHash(family, desc);\n const css = formatFontFaceRule(family, desc);\n collector.collectFontFace(hash, css);\n }\n }\n }\n }\n\n if (hasLocalCounterStyle(styles)) {\n const localCounterStyle = extractLocalCounterStyle(styles);\n if (localCounterStyle) {\n for (const [name, descriptors] of Object.entries(localCounterStyle)) {\n const css = formatCounterStyleRule(name, descriptors);\n collector.collectCounterStyle(name, css);\n }\n }\n }\n\n if (getConfig().autoPropertyTypes !== false) {\n const allRules = chunks.flatMap((c) => c.renderResult.rules);\n if (allRules.length > 0) {\n collectAutoInferredProperties(allRules, collector, styles);\n }\n }\n}\n\n/**\n * Synchronous, hook-free style computation.\n *\n * Resolves recipes, categorizes style keys into chunks, renders CSS rules,\n * allocates class names, and injects / collects / returns the CSS.\n *\n * Three code paths:\n * 1. SSR collector — discovered via ALS or passed explicitly; CSS collected\n * 2. RSC inline — no collector and no `document`; CSS returned as `result.css`\n * for the caller to emit as an inline `<style>` tag\n * 3. Client inject — CSS injected synchronously into the DOM (idempotent)\n *\n * @param styles - Tasty styles object (or undefined for no styles)\n * @param options - Optional SSR collector override\n */\nexport function computeStyles(\n styles: Styles | undefined,\n options?: ComputeStylesOptions,\n): ComputeStylesResult {\n if (!styles || !hasKeys(styles as Record<string, unknown>)) {\n return EMPTY_RESULT;\n }\n\n const resolved = resolveRecipes(styles);\n const chunkMap = categorizeStyleKeys(resolved as Record<string, unknown>);\n\n const collector =\n options?.ssrCollector !== undefined\n ? options.ssrCollector\n : getRegisteredSSRCollector();\n\n const chunks: ProcessedChunk[] = [];\n\n if (collector) {\n collector.collectInternals();\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n const chunk = processChunkSSR(\n collector,\n resolved,\n chunkName,\n chunkStyleKeys,\n );\n if (chunk) chunks.push(chunk);\n }\n\n collectAncillarySSR(collector, resolved, chunks);\n } else if (typeof document === 'undefined') {\n // RSC path: render CSS to strings for inline <style> emission\n return computeStylesRSC(resolved, chunkMap);\n } else {\n injectAncillarySync(resolved);\n\n const usedKf = getUsedKeyframes(resolved);\n const nameMap = usedKf ? injectKeyframesSync(usedKf) : null;\n\n for (const [chunkName, chunkStyleKeys] of chunkMap) {\n const chunk = processChunkSync(resolved, chunkName, chunkStyleKeys);\n if (chunk) chunks.push(chunk);\n }\n\n if (nameMap) {\n injectChunkRulesSync(chunks, nameMap);\n }\n }\n\n if (chunks.length === 0) return EMPTY_RESULT;\n if (chunks.length === 1) return { className: chunks[0].className };\n\n return { className: chunks.map((c) => c.className).join(' ') };\n}\n"],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -109,7 +109,11 @@ interface TastyConfig {
109
109
  /**
110
110
  * Global CSS @property definitions for custom properties.
111
111
  * Keys use tasty token syntax ($name for properties, #name for colors).
112
- * Properties are only injected when the component using them is rendered.
112
+ *
113
+ * Tasty ships with `DEFAULT_PROPERTIES` (e.g. `$gap`, `$radius`, `#white`,
114
+ * `#black`, `#clear`, `#border`, etc.) that are always included.
115
+ * Properties you specify here are merged on top, so you can override any
116
+ * default by using the same key.
113
117
  *
114
118
  * For color tokens (#name), `syntax: '<color>'` is auto-set and
115
119
  * `initialValue` defaults to `'transparent'` if not specified.
@@ -121,6 +125,8 @@ interface TastyConfig {
121
125
  * '$rotation': { syntax: '<angle>', initialValue: '0deg' },
122
126
  * '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },
123
127
  * '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set
128
+ * // Override a default property:
129
+ * '$gap': { syntax: '<length>', inherits: true, initialValue: '8px' },
124
130
  * },
125
131
  * });
126
132
  *
package/dist/config.js CHANGED
@@ -38,14 +38,14 @@ let globalProperties = null;
38
38
  let globalRecipes = null;
39
39
  let globalConfigTokens = null;
40
40
  /**
41
- * Internal properties required by tasty core features.
42
- * These are always injected when styles are first generated.
41
+ * Default properties shipped with tasty.
42
+ * These are always included unless explicitly overridden via `configure({ properties })`.
43
43
  * Keys use tasty token syntax (#name for colors, $name for other properties).
44
44
  *
45
45
  * For properties with CSS @property-compatible types (length, time, number, color),
46
46
  * an `initialValue` is provided so the property works even without a project-level token.
47
47
  */
48
- const INTERNAL_PROPERTIES = {
48
+ const DEFAULT_PROPERTIES = {
49
49
  "#tasty-second-fill": {
50
50
  inherits: false,
51
51
  initialValue: "transparent"
@@ -62,6 +62,14 @@ const INTERNAL_PROPERTIES = {
62
62
  inherits: true,
63
63
  initialValue: "rgb(0 0 0)"
64
64
  },
65
+ "#clear": {
66
+ inherits: true,
67
+ initialValue: "transparent"
68
+ },
69
+ "#border": {
70
+ inherits: true,
71
+ initialValue: "rgb(0 0 0)"
72
+ },
65
73
  $gap: {
66
74
  syntax: "<length>",
67
75
  inherits: true,
@@ -148,8 +156,7 @@ function markStylesGenerated() {
148
156
  if (stylesGenerated) return;
149
157
  stylesGenerated = true;
150
158
  const injector = getGlobalInjector();
151
- for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) injector.property(token, definition);
152
- if (globalProperties && Object.keys(globalProperties).length > 0) for (const [token, definition] of Object.entries(globalProperties)) injector.property(token, definition);
159
+ for (const [token, definition] of Object.entries(getEffectiveProperties())) injector.property(token, definition);
153
160
  if (globalFontFace && Object.keys(globalFontFace).length > 0) for (const [family, input] of Object.entries(globalFontFace)) {
154
161
  const descriptors = Array.isArray(input) ? input : [input];
155
162
  for (const desc of descriptors) injector.fontFace(family, desc);
@@ -194,20 +201,6 @@ function setGlobalKeyframes(keyframes) {
194
201
  _hasGlobalKeyframes = Object.keys(keyframes).length > 0;
195
202
  }
196
203
  /**
197
- * Check if any global properties are configured.
198
- * Fast path: returns false if no properties were ever set.
199
- */
200
- function hasGlobalProperties() {
201
- return globalProperties !== null && Object.keys(globalProperties).length > 0;
202
- }
203
- /**
204
- * Get global properties configuration.
205
- * Returns null if no properties configured (fast path for zero-overhead).
206
- */
207
- function getGlobalProperties() {
208
- return globalProperties;
209
- }
210
- /**
211
204
  * Set global properties (called from configure).
212
205
  * Internal use only.
213
206
  */
@@ -219,6 +212,17 @@ function setGlobalProperties(properties) {
219
212
  globalProperties = properties;
220
213
  }
221
214
  /**
215
+ * Get the effective properties: DEFAULT_PROPERTIES merged with user-configured
216
+ * properties. User properties override defaults with matching keys.
217
+ */
218
+ function getEffectiveProperties() {
219
+ if (!globalProperties) return DEFAULT_PROPERTIES;
220
+ return {
221
+ ...DEFAULT_PROPERTIES,
222
+ ...globalProperties
223
+ };
224
+ }
225
+ /**
222
226
  * Get global font-face configuration.
223
227
  * Returns null if no font faces configured.
224
228
  */
@@ -498,6 +502,6 @@ function resetConfig() {
498
502
  delete storage[GLOBAL_INJECTOR_KEY];
499
503
  }
500
504
  //#endregion
501
- export { INTERNAL_PROPERTIES, configure, getConfig, getGlobalConfigTokens, getGlobalCounterStyle, getGlobalFontFace, getGlobalInjector, getGlobalKeyframes, getGlobalProperties, getGlobalRecipes, hasGlobalKeyframes, hasGlobalProperties, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, markStylesGenerated, resetConfig };
505
+ export { configure, getConfig, getEffectiveProperties, getGlobalConfigTokens, getGlobalCounterStyle, getGlobalFontFace, getGlobalInjector, getGlobalKeyframes, getGlobalRecipes, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, markStylesGenerated, resetConfig };
502
506
 
503
507
  //# sourceMappingURL=config.js.map