@tenphi/tasty 3.7.0 → 3.8.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.
Files changed (54) hide show
  1. package/dist/{astro-CeYENy2x.js → astro-w8r-u4qo.js} +3 -3
  2. package/dist/{astro-CeYENy2x.js.map → astro-w8r-u4qo.js.map} +1 -1
  3. package/dist/{babel-DWCsafYw.d.ts → babel-Cp_dRhuS.d.ts} +2 -2
  4. package/dist/{collector-B3OsM252.js → collector-C7YBiY7D.js} +3 -3
  5. package/dist/{collector-B3OsM252.js.map → collector-C7YBiY7D.js.map} +1 -1
  6. package/dist/{collector-BTVXj2hS.d.ts → collector-CUtAFyIb.d.ts} +2 -2
  7. package/dist/{config-LfIDmVHx.d.ts → config-BY1e5y7V.d.ts} +2 -2
  8. package/dist/{config-B3gPdCqd.js → config-yXpe2jAV.js} +404 -157
  9. package/dist/config-yXpe2jAV.js.map +1 -0
  10. package/dist/core/index.d.ts +4 -4
  11. package/dist/core/index.js +5 -5
  12. package/dist/{core-DGm0CFHP.js → core-DoEZAHJq.js} +5 -5
  13. package/dist/{core-DGm0CFHP.js.map → core-DoEZAHJq.js.map} +1 -1
  14. package/dist/{css-writer-Bgowobbx.js → css-writer-BiumPvq-.js} +3 -3
  15. package/dist/{css-writer-Bgowobbx.js.map → css-writer-BiumPvq-.js.map} +1 -1
  16. package/dist/{format-rules-XRw9u7d4.js → format-rules-DHuG25KU.js} +2 -2
  17. package/dist/{format-rules-XRw9u7d4.js.map → format-rules-DHuG25KU.js.map} +1 -1
  18. package/dist/{hydrate-CNOmZprz.js → hydrate-BvRP6QEC.js} +2 -2
  19. package/dist/{hydrate-CNOmZprz.js.map → hydrate-BvRP6QEC.js.map} +1 -1
  20. package/dist/{index-BmogEzTo.d.ts → index-DBjd4MAb.d.ts} +46 -5
  21. package/dist/{index-Bm9ltvdH.d.ts → index-uX74k5jG.d.ts} +93 -46
  22. package/dist/index.d.ts +4 -4
  23. package/dist/index.js +6 -6
  24. package/dist/{keyframes-DE-OE76F.js → keyframes-B8uPLZwx.js} +2 -2
  25. package/dist/{keyframes-DE-OE76F.js.map → keyframes-B8uPLZwx.js.map} +1 -1
  26. package/dist/{merge-styles-DuoZEsm9.js → merge-styles-ChW1k-ZM.js} +2 -2
  27. package/dist/{merge-styles-DuoZEsm9.js.map → merge-styles-ChW1k-ZM.js.map} +1 -1
  28. package/dist/{merge-styles-BcLl1u_d.d.ts → merge-styles-pf6iPs82.d.ts} +2 -2
  29. package/dist/{resolve-recipes-H9NqOQuP.js → resolve-recipes-BNmjANzT.js} +3 -3
  30. package/dist/{resolve-recipes-H9NqOQuP.js.map → resolve-recipes-BNmjANzT.js.map} +1 -1
  31. package/dist/ssr/astro-client.js +1 -1
  32. package/dist/ssr/astro-middleware-extract-static.js +1 -1
  33. package/dist/ssr/astro-middleware-extract.js +1 -1
  34. package/dist/ssr/astro-middleware-static.js +1 -1
  35. package/dist/ssr/astro-middleware.js +1 -1
  36. package/dist/ssr/astro.js +1 -1
  37. package/dist/ssr/index.d.ts +1 -1
  38. package/dist/ssr/index.js +2 -2
  39. package/dist/ssr/next-config.d.ts +1 -1
  40. package/dist/ssr/next-config.js +2 -2
  41. package/dist/ssr/next.d.ts +1 -1
  42. package/dist/ssr/next.js +3 -3
  43. package/dist/static/index.d.ts +2 -2
  44. package/dist/static/index.js +1 -1
  45. package/dist/zero/babel.d.ts +1 -1
  46. package/dist/zero/babel.js +4 -4
  47. package/dist/zero/index.d.ts +1 -1
  48. package/dist/zero/index.js +1 -1
  49. package/dist/zero/next.d.ts +1 -1
  50. package/docs/ai-agents.md +8 -3
  51. package/docs/methodology.md +3 -3
  52. package/docs/styles.md +43 -11
  53. package/package.json +6 -6
  54. package/dist/config-B3gPdCqd.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"merge-styles-DuoZEsm9.js","names":[],"sources":["../src/utils/merge-styles.ts"],"sourcesContent":["import { isSelector } from '../pipeline';\nimport type { Styles, StylesWithoutSelectors } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\n\nconst devMode = isDevEnv();\n\nconst INHERIT_VALUE = '@inherit';\n\n/**\n * Check if a value is a state map (object, not array).\n */\nfunction isStateMap(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Normalize a parent value to a state map.\n * - Already a state map → return as-is\n * - Non-null, non-false primitive → wrap as `{ '': value }`\n * - null / undefined / false → return null (no parent to merge with)\n */\nfunction normalizeToStateMap(value: unknown): Record<string, unknown> | null {\n if (isStateMap(value)) return value as Record<string, unknown>;\n if (value != null && value !== false) return { '': value };\n return null;\n}\n\n/**\n * Resolve a child state map against a parent value.\n *\n * Mode is determined by whether the child contains a `''` (default) key:\n * - No `''` → extend mode: parent entries preserved, child adds/overrides/repositions\n * - Has `''` → replace mode: child defines everything, `@inherit` cherry-picks from parent\n *\n * In both modes:\n * - `@inherit` value → resolve from parent state map\n * - `null` value → remove this state from the result\n * - `false` value → tombstone, persists through all layers, blocks recipe\n */\nfunction resolveStateMap(\n parentValue: unknown,\n childMap: Record<string, unknown>,\n): Record<string, unknown> {\n const isExtend = !('' in childMap);\n const parentMap = normalizeToStateMap(parentValue);\n\n if (!parentMap) {\n // No parent to merge with — strip nulls and @inherit, return child entries\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(childMap)) {\n const val = childMap[key];\n if (val === null || val === INHERIT_VALUE) continue;\n result[key] = val;\n }\n return result;\n }\n\n if (isExtend) {\n return resolveExtendMode(parentMap, childMap);\n }\n\n return resolveReplaceMode(parentMap, childMap);\n}\n\n/**\n * Extend mode: parent entries are preserved, child entries add/override/reposition.\n */\nfunction resolveExtendMode(\n parentMap: Record<string, unknown>,\n childMap: Record<string, unknown>,\n): Record<string, unknown> {\n const inheritKeys = new Set<string>();\n const removeKeys = new Set<string>();\n const overrideKeys = new Map<string, unknown>();\n\n for (const key of Object.keys(childMap)) {\n const val = childMap[key];\n if (val === INHERIT_VALUE) {\n if (key in parentMap) {\n inheritKeys.add(key);\n } else if (devMode) {\n console.warn(\n `[Tasty] @inherit used for state '${key}' that does not exist in the parent style map. Entry skipped.`,\n );\n }\n } else if (val === null) {\n removeKeys.add(key);\n } else if (key in parentMap) {\n overrideKeys.set(key, val);\n }\n }\n\n // 1. Parent entries in order (skip removed, skip repositioned, apply overrides)\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(parentMap)) {\n if (removeKeys.has(key)) continue;\n if (inheritKeys.has(key)) continue;\n if (overrideKeys.has(key)) {\n result[key] = overrideKeys.get(key);\n } else {\n result[key] = parentMap[key];\n }\n }\n\n // 2. Append new + repositioned entries in child declaration order\n for (const key of Object.keys(childMap)) {\n if (inheritKeys.has(key)) {\n result[key] = parentMap[key];\n } else if (\n !removeKeys.has(key) &&\n !overrideKeys.has(key) &&\n // Skip @inherit for keys that weren't in the parent (already warned above)\n childMap[key] !== INHERIT_VALUE\n ) {\n result[key] = childMap[key];\n }\n }\n\n return result;\n}\n\n/**\n * Replace mode: child entries define the result, `@inherit` pulls from parent.\n */\nfunction resolveReplaceMode(\n parentMap: Record<string, unknown>,\n childMap: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const key of Object.keys(childMap)) {\n const val = childMap[key];\n if (val === INHERIT_VALUE) {\n if (key in parentMap) {\n result[key] = parentMap[key];\n } else if (devMode) {\n console.warn(\n `[Tasty] @inherit used for state '${key}' that does not exist in the parent style map. Entry skipped.`,\n );\n }\n } else if (val !== null) {\n result[key] = val;\n }\n }\n\n return result;\n}\n\n/**\n * Merge sub-element properties with state map / null / undefined support.\n */\nfunction mergeSubElementStyles(\n parentSub: StylesWithoutSelectors | undefined,\n childSub: StylesWithoutSelectors,\n): StylesWithoutSelectors {\n const parent = parentSub as Record<string, unknown> | undefined;\n const child = childSub as Record<string, unknown>;\n const merged: Record<string, unknown> = { ...parent, ...child };\n\n for (const key of Object.keys(child)) {\n const val = child[key];\n\n if (val === undefined) {\n if (parent && key in parent) {\n merged[key] = parent[key];\n }\n } else if (val === null) {\n delete merged[key];\n } else if (isStateMap(val)) {\n merged[key] = resolveStateMap(\n parent ? parent[key] : undefined,\n val as Record<string, unknown>,\n );\n }\n }\n\n return merged as StylesWithoutSelectors;\n}\n\nexport function mergeStyles(...objects: (Styles | undefined | null)[]): Styles {\n let styles: Styles = objects[0] ? { ...objects[0] } : {};\n let pos = 1;\n\n while (pos in objects) {\n const selectorKeys = Object.keys(styles).filter(\n (key) => isSelector(key) && styles[key],\n );\n const newStyles = objects[pos];\n\n if (newStyles) {\n const resultStyles = { ...styles, ...newStyles };\n\n // Collect all selector keys from both parent and child\n const newSelectorKeys = Object.keys(newStyles).filter(isSelector);\n const allSelectorKeys = new Set([...selectorKeys, ...newSelectorKeys]);\n\n for (const key of allSelectorKeys) {\n const newValue = newStyles?.[key];\n\n if (newValue === false || newValue === null) {\n delete resultStyles[key];\n } else if (newValue === undefined) {\n resultStyles[key] = styles[key];\n } else if (newValue) {\n resultStyles[key] = mergeSubElementStyles(\n styles[key] as StylesWithoutSelectors,\n newValue as StylesWithoutSelectors,\n );\n }\n }\n\n // Handle non-selector properties: state maps, null, undefined\n for (const key of Object.keys(newStyles)) {\n if (isSelector(key)) continue;\n\n const newValue = newStyles[key];\n\n if (newValue === undefined) {\n if (key in styles) {\n resultStyles[key] = styles[key];\n } else {\n delete resultStyles[key];\n }\n } else if (newValue === null) {\n delete resultStyles[key];\n } else if (isStateMap(newValue)) {\n (resultStyles as Record<string, unknown>)[key] = resolveStateMap(\n styles[key],\n newValue as Record<string, unknown>,\n );\n }\n }\n\n styles = resultStyles;\n }\n\n pos++;\n }\n\n return styles;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"merge-styles-ChW1k-ZM.js","names":[],"sources":["../src/utils/merge-styles.ts"],"sourcesContent":["import { isSelector } from '../pipeline';\nimport type { Styles, StylesWithoutSelectors } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\n\nconst devMode = isDevEnv();\n\nconst INHERIT_VALUE = '@inherit';\n\n/**\n * Check if a value is a state map (object, not array).\n */\nfunction isStateMap(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Normalize a parent value to a state map.\n * - Already a state map → return as-is\n * - Non-null, non-false primitive → wrap as `{ '': value }`\n * - null / undefined / false → return null (no parent to merge with)\n */\nfunction normalizeToStateMap(value: unknown): Record<string, unknown> | null {\n if (isStateMap(value)) return value as Record<string, unknown>;\n if (value != null && value !== false) return { '': value };\n return null;\n}\n\n/**\n * Resolve a child state map against a parent value.\n *\n * Mode is determined by whether the child contains a `''` (default) key:\n * - No `''` → extend mode: parent entries preserved, child adds/overrides/repositions\n * - Has `''` → replace mode: child defines everything, `@inherit` cherry-picks from parent\n *\n * In both modes:\n * - `@inherit` value → resolve from parent state map\n * - `null` value → remove this state from the result\n * - `false` value → tombstone, persists through all layers, blocks recipe\n */\nfunction resolveStateMap(\n parentValue: unknown,\n childMap: Record<string, unknown>,\n): Record<string, unknown> {\n const isExtend = !('' in childMap);\n const parentMap = normalizeToStateMap(parentValue);\n\n if (!parentMap) {\n // No parent to merge with — strip nulls and @inherit, return child entries\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(childMap)) {\n const val = childMap[key];\n if (val === null || val === INHERIT_VALUE) continue;\n result[key] = val;\n }\n return result;\n }\n\n if (isExtend) {\n return resolveExtendMode(parentMap, childMap);\n }\n\n return resolveReplaceMode(parentMap, childMap);\n}\n\n/**\n * Extend mode: parent entries are preserved, child entries add/override/reposition.\n */\nfunction resolveExtendMode(\n parentMap: Record<string, unknown>,\n childMap: Record<string, unknown>,\n): Record<string, unknown> {\n const inheritKeys = new Set<string>();\n const removeKeys = new Set<string>();\n const overrideKeys = new Map<string, unknown>();\n\n for (const key of Object.keys(childMap)) {\n const val = childMap[key];\n if (val === INHERIT_VALUE) {\n if (key in parentMap) {\n inheritKeys.add(key);\n } else if (devMode) {\n console.warn(\n `[Tasty] @inherit used for state '${key}' that does not exist in the parent style map. Entry skipped.`,\n );\n }\n } else if (val === null) {\n removeKeys.add(key);\n } else if (key in parentMap) {\n overrideKeys.set(key, val);\n }\n }\n\n // 1. Parent entries in order (skip removed, skip repositioned, apply overrides)\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(parentMap)) {\n if (removeKeys.has(key)) continue;\n if (inheritKeys.has(key)) continue;\n if (overrideKeys.has(key)) {\n result[key] = overrideKeys.get(key);\n } else {\n result[key] = parentMap[key];\n }\n }\n\n // 2. Append new + repositioned entries in child declaration order\n for (const key of Object.keys(childMap)) {\n if (inheritKeys.has(key)) {\n result[key] = parentMap[key];\n } else if (\n !removeKeys.has(key) &&\n !overrideKeys.has(key) &&\n // Skip @inherit for keys that weren't in the parent (already warned above)\n childMap[key] !== INHERIT_VALUE\n ) {\n result[key] = childMap[key];\n }\n }\n\n return result;\n}\n\n/**\n * Replace mode: child entries define the result, `@inherit` pulls from parent.\n */\nfunction resolveReplaceMode(\n parentMap: Record<string, unknown>,\n childMap: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const key of Object.keys(childMap)) {\n const val = childMap[key];\n if (val === INHERIT_VALUE) {\n if (key in parentMap) {\n result[key] = parentMap[key];\n } else if (devMode) {\n console.warn(\n `[Tasty] @inherit used for state '${key}' that does not exist in the parent style map. Entry skipped.`,\n );\n }\n } else if (val !== null) {\n result[key] = val;\n }\n }\n\n return result;\n}\n\n/**\n * Merge sub-element properties with state map / null / undefined support.\n */\nfunction mergeSubElementStyles(\n parentSub: StylesWithoutSelectors | undefined,\n childSub: StylesWithoutSelectors,\n): StylesWithoutSelectors {\n const parent = parentSub as Record<string, unknown> | undefined;\n const child = childSub as Record<string, unknown>;\n const merged: Record<string, unknown> = { ...parent, ...child };\n\n for (const key of Object.keys(child)) {\n const val = child[key];\n\n if (val === undefined) {\n if (parent && key in parent) {\n merged[key] = parent[key];\n }\n } else if (val === null) {\n delete merged[key];\n } else if (isStateMap(val)) {\n merged[key] = resolveStateMap(\n parent ? parent[key] : undefined,\n val as Record<string, unknown>,\n );\n }\n }\n\n return merged as StylesWithoutSelectors;\n}\n\nexport function mergeStyles(...objects: (Styles | undefined | null)[]): Styles {\n let styles: Styles = objects[0] ? { ...objects[0] } : {};\n let pos = 1;\n\n while (pos in objects) {\n const selectorKeys = Object.keys(styles).filter(\n (key) => isSelector(key) && styles[key],\n );\n const newStyles = objects[pos];\n\n if (newStyles) {\n const resultStyles = { ...styles, ...newStyles };\n\n // Collect all selector keys from both parent and child\n const newSelectorKeys = Object.keys(newStyles).filter(isSelector);\n const allSelectorKeys = new Set([...selectorKeys, ...newSelectorKeys]);\n\n for (const key of allSelectorKeys) {\n const newValue = newStyles?.[key];\n\n if (newValue === false || newValue === null) {\n delete resultStyles[key];\n } else if (newValue === undefined) {\n resultStyles[key] = styles[key];\n } else if (newValue) {\n resultStyles[key] = mergeSubElementStyles(\n styles[key] as StylesWithoutSelectors,\n newValue as StylesWithoutSelectors,\n );\n }\n }\n\n // Handle non-selector properties: state maps, null, undefined\n for (const key of Object.keys(newStyles)) {\n if (isSelector(key)) continue;\n\n const newValue = newStyles[key];\n\n if (newValue === undefined) {\n if (key in styles) {\n resultStyles[key] = styles[key];\n } else {\n delete resultStyles[key];\n }\n } else if (newValue === null) {\n delete resultStyles[key];\n } else if (isStateMap(newValue)) {\n (resultStyles as Record<string, unknown>)[key] = resolveStateMap(\n styles[key],\n newValue as Record<string, unknown>,\n );\n }\n }\n\n styles = resultStyles;\n }\n\n pos++;\n }\n\n return styles;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { b as Styles } from "./index-BmogEzTo.js";
1
+ import { b as Styles } from "./index-DBjd4MAb.js";
2
2
 
3
3
  //#region src/utils/merge-styles.d.ts
4
4
  declare function mergeStyles(...objects: (Styles | undefined | null)[]): Styles;
5
5
  //#endregion
6
6
  export { mergeStyles as t };
7
- //# sourceMappingURL=merge-styles-BcLl1u_d.d.ts.map
7
+ //# sourceMappingURL=merge-styles-pf6iPs82.d.ts.map
@@ -1,5 +1,5 @@
1
- import { I as isSelector, Qt as isDevEnv, u as getGlobalRecipes } from "./config-B3gPdCqd.js";
2
- import { t as mergeStyles } from "./merge-styles-DuoZEsm9.js";
1
+ import { I as isSelector, Qt as isDevEnv, u as getGlobalRecipes } from "./config-yXpe2jAV.js";
2
+ import { t as mergeStyles } from "./merge-styles-ChW1k-ZM.js";
3
3
  //#region src/utils/resolve-recipes.ts
4
4
  /**
5
5
  * Recipe resolution utility.
@@ -141,4 +141,4 @@ function resolveRecipes(styles) {
141
141
  //#endregion
142
142
  export { resolveRecipes as t };
143
143
 
144
- //# sourceMappingURL=resolve-recipes-H9NqOQuP.js.map
144
+ //# sourceMappingURL=resolve-recipes-BNmjANzT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-recipes-H9NqOQuP.js","names":[],"sources":["../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `/` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 / post1 post2'`\n * - Names are space-separated within each group\n * - `/` separates base (before component) from post (after component) groups\n * - `/` is optional; if absent, all names are base\n * - `none` as the sole base value means \"no base recipes\"\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const slashIndex = trimmed.indexOf('/');\n\n if (slashIndex === -1) {\n if (trimmed === 'none') return empty;\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, slashIndex);\n const postPart = trimmed.slice(slashIndex + 1);\n\n return {\n base: basePart.trim() === 'none' ? null : splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n\n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,SAAS;;;;;;;;;;;;AAkBzB,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;CAAK;CAE3D,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,YAAY,IAAI,OAAO;CAE3B,MAAM,aAAa,QAAQ,QAAQ,GAAG;CAEtC,IAAI,eAAe,IAAI;EACrB,IAAI,YAAY,QAAQ,OAAO;EAE/B,OAAO;GAAE,MADK,WAAW,OACN;GAAG,MAAM;EAAK;CACnC;CAEA,MAAM,WAAW,QAAQ,MAAM,GAAG,UAAU;CAC5C,MAAM,WAAW,QAAQ,MAAM,aAAa,CAAC;CAE7C,OAAO;EACL,MAAM,SAAS,KAAK,MAAM,SAAS,OAAO,WAAW,QAAQ;EAC7D,MAAM,WAAW,QAAQ;CAC3B;AACF;AAEA,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CAC3C,OAAO,MAAM,SAAS,IAAI,QAAQ;AACpC;;;;;AAMA,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,CAAC;CAEvC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;EAE7B,IAAI,CAAC,cAAc;GACjB,IAAI,SACF,QAAQ,KACN,mBAAmB,KAAK,yEAE1B;GAEF;EACF;EAEA,SAAS;GAAE,GAAG;GAAQ,GAAI;EAAyC;CACrE;CAEA,OAAO;AACT;;;;;;;AAQA,SAAS,uBACP,QACA,SACgC;CAChC,IAAI,EAAE,YAAY,SAAS,OAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,MAAM;CAMrD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,CAAC;CAC7C,MAAM,iBAA0C,CAAC;CAEjD,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACnC,IAAI,WAAW,GAAG,GAChB,eAAe,OAAO,QAAQ;MAE9B,WAAW,OAAO,QAAQ;CAI9B,IAAI,CAAC,QAAQ,CAAC,MACZ,OAAO;CAIT,IAAI;CAEJ,IAAI,MAEF,SAAS,YADU,oBAAoB,MAAM,OACf,GAAa,UAAoB;MAK/D,SAAS,EAAE,GAAG,WAAW;CAI3B,IAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,OAAO;EACpD,SAAS,YAAY,QAAkB,UAAoB;CAI7D;CAGA,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,GAC1C,OAAO,OAAO,eAAe;CAG/B,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,iBAAiB;CAGjC,IAAI,CAAC,SAAS,OAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,OACF;CAEA,IAAI;CAEJ,IAAI,aAAa;EACf,UAAU;EACV,SAAS;CACX,OAEE,SAAS;CAIX,MAAM,OAAO,OAAO,KAAK,MAAM;CAE/B,KAAK,MAAM,OAAO,MAAM;EACtB,IAAI,CAAC,WAAW,GAAG,GAAG;EAEtB,MAAM,YAAY,OAAO;EAEzB,IACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,GAEvB;EAGF,MAAM,YAAY;EAElB,IAAI,EAAE,YAAY,YAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,OAAO;EAE7D,IAAI,aAAa;GACf,IAAI,CAAC,SAAS;IAEZ,UAAU;IACV,SAAS,EAAE,GAAI,OAAmC;GACpD;GACA,OAAO,OAAO;EAChB;CACF;CAEA,OAAO,UAAW,SAAoB;AACxC"}
1
+ {"version":3,"file":"resolve-recipes-BNmjANzT.js","names":[],"sources":["../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `/` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 / post1 post2'`\n * - Names are space-separated within each group\n * - `/` separates base (before component) from post (after component) groups\n * - `/` is optional; if absent, all names are base\n * - `none` as the sole base value means \"no base recipes\"\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const slashIndex = trimmed.indexOf('/');\n\n if (slashIndex === -1) {\n if (trimmed === 'none') return empty;\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, slashIndex);\n const postPart = trimmed.slice(slashIndex + 1);\n\n return {\n base: basePart.trim() === 'none' ? null : splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n\n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,SAAS;;;;;;;;;;;;AAkBzB,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;CAAK;CAE3D,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,YAAY,IAAI,OAAO;CAE3B,MAAM,aAAa,QAAQ,QAAQ,GAAG;CAEtC,IAAI,eAAe,IAAI;EACrB,IAAI,YAAY,QAAQ,OAAO;EAE/B,OAAO;GAAE,MADK,WAAW,OACN;GAAG,MAAM;EAAK;CACnC;CAEA,MAAM,WAAW,QAAQ,MAAM,GAAG,UAAU;CAC5C,MAAM,WAAW,QAAQ,MAAM,aAAa,CAAC;CAE7C,OAAO;EACL,MAAM,SAAS,KAAK,MAAM,SAAS,OAAO,WAAW,QAAQ;EAC7D,MAAM,WAAW,QAAQ;CAC3B;AACF;AAEA,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CAC3C,OAAO,MAAM,SAAS,IAAI,QAAQ;AACpC;;;;;AAMA,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,CAAC;CAEvC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;EAE7B,IAAI,CAAC,cAAc;GACjB,IAAI,SACF,QAAQ,KACN,mBAAmB,KAAK,yEAE1B;GAEF;EACF;EAEA,SAAS;GAAE,GAAG;GAAQ,GAAI;EAAyC;CACrE;CAEA,OAAO;AACT;;;;;;;AAQA,SAAS,uBACP,QACA,SACgC;CAChC,IAAI,EAAE,YAAY,SAAS,OAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,MAAM;CAMrD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,CAAC;CAC7C,MAAM,iBAA0C,CAAC;CAEjD,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACnC,IAAI,WAAW,GAAG,GAChB,eAAe,OAAO,QAAQ;MAE9B,WAAW,OAAO,QAAQ;CAI9B,IAAI,CAAC,QAAQ,CAAC,MACZ,OAAO;CAIT,IAAI;CAEJ,IAAI,MAEF,SAAS,YADU,oBAAoB,MAAM,OACf,GAAa,UAAoB;MAK/D,SAAS,EAAE,GAAG,WAAW;CAI3B,IAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,OAAO;EACpD,SAAS,YAAY,QAAkB,UAAoB;CAI7D;CAGA,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,GAC1C,OAAO,OAAO,eAAe;CAG/B,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,iBAAiB;CAGjC,IAAI,CAAC,SAAS,OAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,OACF;CAEA,IAAI;CAEJ,IAAI,aAAa;EACf,UAAU;EACV,SAAS;CACX,OAEE,SAAS;CAIX,MAAM,OAAO,OAAO,KAAK,MAAM;CAE/B,KAAK,MAAM,OAAO,MAAM;EACtB,IAAI,CAAC,WAAW,GAAG,GAAG;EAEtB,MAAM,YAAY,OAAO;EAEzB,IACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,GAEvB;EAGF,MAAM,YAAY;EAElB,IAAI,EAAE,YAAY,YAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,OAAO;EAE7D,IAAI,aAAa;GACf,IAAI,CAAC,SAAS;IAEZ,UAAU;IACV,SAAS,EAAE,GAAI,OAAmC;GACpD;GACA,OAAO,OAAO;EAChB;CACF;CAEA,OAAO,UAAW,SAAoB;AACxC"}
@@ -1,4 +1,4 @@
1
- import { t as hydrateTastyClasses } from "../hydrate-CNOmZprz.js";
1
+ import { t as hydrateTastyClasses } from "../hydrate-BvRP6QEC.js";
2
2
  //#region src/ssr/astro-client.ts
3
3
  /**
4
4
  * Client-side cache hydration for Astro islands.
@@ -1,4 +1,4 @@
1
- import { n as tastyMiddleware } from "../astro-CeYENy2x.js";
1
+ import { n as tastyMiddleware } from "../astro-w8r-u4qo.js";
2
2
  const onRequest = tastyMiddleware({
3
3
  transferCache: false,
4
4
  extractionMetadata: true
@@ -1,4 +1,4 @@
1
- import { n as tastyMiddleware } from "../astro-CeYENy2x.js";
1
+ import { n as tastyMiddleware } from "../astro-w8r-u4qo.js";
2
2
  const onRequest = tastyMiddleware({
3
3
  transferCache: true,
4
4
  extractionMetadata: true
@@ -1,4 +1,4 @@
1
- import { n as tastyMiddleware } from "../astro-CeYENy2x.js";
1
+ import { n as tastyMiddleware } from "../astro-w8r-u4qo.js";
2
2
  //#region src/ssr/astro-middleware-static.ts
3
3
  /**
4
4
  * Astro middleware entrypoint for `tastyIntegration({ islands: false })`.
@@ -1,4 +1,4 @@
1
- import { n as tastyMiddleware } from "../astro-CeYENy2x.js";
1
+ import { n as tastyMiddleware } from "../astro-w8r-u4qo.js";
2
2
  //#region src/ssr/astro-middleware.ts
3
3
  /**
4
4
  * Astro middleware entrypoint for `tastyIntegration()`.
package/dist/ssr/astro.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as tastyMiddleware, t as tastyIntegration } from "../astro-CeYENy2x.js";
1
+ import { n as tastyMiddleware, t as tastyIntegration } from "../astro-w8r-u4qo.js";
2
2
  export { tastyIntegration, tastyMiddleware };
@@ -1,4 +1,4 @@
1
- import { i as createServerStyleCollector, n as ServerStyleArtifactKind, r as ServerStyleCollector, t as ServerStyleArtifact } from "../collector-BTVXj2hS.js";
1
+ import { i as createServerStyleCollector, n as ServerStyleArtifactKind, r as ServerStyleCollector, t as ServerStyleArtifact } from "../collector-CUtAFyIb.js";
2
2
 
3
3
  //#region src/ssr/async-storage.d.ts
4
4
  /**
package/dist/ssr/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { r as registerSSRCollectorGetterGlobal } from "../ssr-collector-ref-COs_ioWl.js";
2
- import { n as createServerStyleCollector, t as ServerStyleCollector } from "../collector-B3OsM252.js";
2
+ import { n as createServerStyleCollector, t as ServerStyleCollector } from "../collector-C7YBiY7D.js";
3
3
  import { n as runWithCollector, t as getSSRCollector } from "../async-storage-DKK-wTD4.js";
4
- import { t as hydrateTastyClasses } from "../hydrate-CNOmZprz.js";
4
+ import { t as hydrateTastyClasses } from "../hydrate-BvRP6QEC.js";
5
5
  //#region src/ssr/index.ts
6
6
  registerSSRCollectorGetterGlobal(getSSRCollector);
7
7
  //#endregion
@@ -1,4 +1,4 @@
1
- import { t as TastyConfig } from "../config-LfIDmVHx.js";
1
+ import { t as TastyConfig } from "../config-BY1e5y7V.js";
2
2
 
3
3
  //#region src/ssr/next-config.d.ts
4
4
  interface NextConfig {
@@ -1,5 +1,5 @@
1
- import { b as resetConfig, t as configure } from "../config-B3gPdCqd.js";
2
- import { t as ServerStyleCollector } from "../collector-B3OsM252.js";
1
+ import { b as resetConfig, t as configure } from "../config-yXpe2jAV.js";
2
+ import { t as ServerStyleCollector } from "../collector-C7YBiY7D.js";
3
3
  import { t as findUnsafeCSSResource } from "../css-resources-Cyl_axbI.js";
4
4
  import { createHash } from "node:crypto";
5
5
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -1,4 +1,4 @@
1
- import { r as ServerStyleCollector } from "../collector-BTVXj2hS.js";
1
+ import { r as ServerStyleCollector } from "../collector-CUtAFyIb.js";
2
2
  import { ReactNode } from "react";
3
3
 
4
4
  //#region src/ssr/next.d.ts
package/dist/ssr/next.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use client";
2
- import { n as getConfig } from "../config-B3gPdCqd.js";
2
+ import { n as getConfig } from "../config-yXpe2jAV.js";
3
3
  import { n as registerSSRCollectorGetter } from "../ssr-collector-ref-COs_ioWl.js";
4
4
  import { t as getTastySSRContext } from "../context-CA8YKeMn.js";
5
- import { t as ServerStyleCollector } from "../collector-B3OsM252.js";
6
- import { t as hydrateTastyClasses } from "../hydrate-CNOmZprz.js";
5
+ import { t as ServerStyleCollector } from "../collector-C7YBiY7D.js";
6
+ import { t as hydrateTastyClasses } from "../hydrate-BvRP6QEC.js";
7
7
  import { Fragment, createElement, useState } from "react";
8
8
  import { useServerInsertedHTML } from "next/navigation";
9
9
  //#region src/ssr/next.ts
@@ -1,5 +1,5 @@
1
- import { b as Styles } from "../index-BmogEzTo.js";
2
- import { t as mergeStyles } from "../merge-styles-BcLl1u_d.js";
1
+ import { b as Styles } from "../index-DBjd4MAb.js";
2
+ import { t as mergeStyles } from "../merge-styles-pf6iPs82.js";
3
3
 
4
4
  //#region src/static/types.d.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { t as mergeStyles } from "../merge-styles-DuoZEsm9.js";
1
+ import { t as mergeStyles } from "../merge-styles-ChW1k-ZM.js";
2
2
  //#region src/static/types.ts
3
3
  /**
4
4
  * Create a StaticStyle object.
@@ -1,2 +1,2 @@
1
- import { n as _default, r as TastyZeroConfig, t as TastyZeroBabelOptions } from "../babel-DWCsafYw.js";
1
+ import { n as _default, r as TastyZeroConfig, t as TastyZeroBabelOptions } from "../babel-Cp_dRhuS.js";
2
2
  export { TastyZeroBabelOptions, type TastyZeroConfig, _default as default };
@@ -1,7 +1,7 @@
1
- import { b as resetConfig, d as getGlobalStyles, i as getGlobalConfigTokens, s as getGlobalFunctions, t as configure } from "../config-B3gPdCqd.js";
2
- import { t as mergeStyles } from "../merge-styles-DuoZEsm9.js";
3
- import { t as resolveRecipes } from "../resolve-recipes-H9NqOQuP.js";
4
- import { a as extractFunctionsFromStyles, c as extractStylesForSelector, i as extractFontFaceFromStyles, l as extractStylesWithChunks, o as extractKeyframesFromStyles, r as extractCounterStyleFromStyles, s as extractPropertiesFromStyles, t as CSSWriter, u as setExtractorNamePrefix } from "../css-writer-Bgowobbx.js";
1
+ import { b as resetConfig, d as getGlobalStyles, i as getGlobalConfigTokens, s as getGlobalFunctions, t as configure } from "../config-yXpe2jAV.js";
2
+ import { t as mergeStyles } from "../merge-styles-ChW1k-ZM.js";
3
+ import { t as resolveRecipes } from "../resolve-recipes-BNmjANzT.js";
4
+ import { a as extractFunctionsFromStyles, c as extractStylesForSelector, i as extractFontFaceFromStyles, l as extractStylesWithChunks, o as extractKeyframesFromStyles, r as extractCounterStyleFromStyles, s as extractPropertiesFromStyles, t as CSSWriter, u as setExtractorNamePrefix } from "../css-writer-BiumPvq-.js";
5
5
  import * as fs from "fs";
6
6
  import * as path from "path";
7
7
  import { declare } from "@babel/helper-plugin-utils";
@@ -1,4 +1,4 @@
1
- import { b as Styles } from "../index-BmogEzTo.js";
1
+ import { b as Styles } from "../index-DBjd4MAb.js";
2
2
 
3
3
  //#region src/zero/extractor.d.ts
4
4
  interface ExtractedChunk {
@@ -1,2 +1,2 @@
1
- import { c as extractStylesForSelector, l as extractStylesWithChunks, n as createCSSWriter, t as CSSWriter } from "../css-writer-Bgowobbx.js";
1
+ import { c as extractStylesForSelector, l as extractStylesWithChunks, n as createCSSWriter, t as CSSWriter } from "../css-writer-BiumPvq-.js";
2
2
  export { CSSWriter, createCSSWriter, extractStylesForSelector, extractStylesWithChunks };
@@ -1,4 +1,4 @@
1
- import { r as TastyZeroConfig } from "../babel-DWCsafYw.js";
1
+ import { r as TastyZeroConfig } from "../babel-Cp_dRhuS.js";
2
2
 
3
3
  //#region src/zero/next.d.ts
4
4
  interface WebpackConfigContext {
package/docs/ai-agents.md CHANGED
@@ -50,6 +50,8 @@ Prefer the Tasty shorthand over its CSS equivalents:
50
50
  | `hide: true` | `display: 'none'` |
51
51
  | `flexGrow` / `flexShrink` / `flexBasis` | `flex` |
52
52
 
53
+ Use logical category styles when layout must follow writing mode or direction: `blockSize`/`inlineSize`, `blockPadding`/`inlinePadding`, `blockMargin`/`inlineMargin`, `blockInset`/`inlineInset`, `blockScrollMargin`/`inlineScrollMargin`, `blockScrollPadding`/`inlineScrollPadding`, and `blockBorder`/`inlineBorder`. Use `start`/`end` value modifiers; do not replace them with physical sides. Native logical CSS properties remain valid as ordinary keys when an individual declaration is needed.
54
+
53
55
  The last row goes the other way — longhands over the shorthand — because `flex` is lossy: it resets the components you omit to non-initial values (`flex: '0'` silently sets `flex-basis: 0%`) and cannot express `flexShrink: 0` at all. The longhands also carry separate state maps.
54
56
 
55
57
  ## 3. Values
@@ -86,7 +88,7 @@ Prefer units over raw pixels: `8px` → `1x` … `64px` → `8x`; `radius: '6px'
86
88
 
87
89
  ### `true`
88
90
 
89
- `true` means "the design-system default" and is accepted **only** by: `border`, `radius`, `outline`, `shadow`, `padding`, `margin`, `gap`, `inset`, `width`, `height`, `fill`, `color`, `fade`, `preset`, `font`, `scrollbar`, `hide`. Anywhere else it is an error.
91
+ `true` means "the design-system default" and is accepted **only** by: `border`, `radius`, `outline`, `shadow`, `padding`, `margin`, `gap`, `inset`, `width`, `height`, `fill`, `color`, `fade`, `preset`, `font`, `scrollbar`, `scrollMargin`, `scrollPadding`, and the enhanced logical category styles listed above, plus `hide`. Anywhere else it is an error.
90
92
 
91
93
  ### `!important`
92
94
 
@@ -100,9 +102,11 @@ A value is `[values…] [modifiers…]`, and several groups can be comma-separat
100
102
  | ------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
101
103
  | `padding`, `margin`, `inset`, `fade` | `top` `right` `bottom` `left` |
102
104
  | `border` | the four directions + `solid` `dashed` `dotted` `double` `groove` `ridge` `inset` `outset` `none` `hidden` |
105
+ | logical axis spacing/inset styles | `start` `end` |
106
+ | `blockBorder`, `inlineBorder` | `start` `end` + the border style keywords above |
103
107
  | `outline` | the style keywords above |
104
108
  | `radius` | `top` `right` `bottom` `left` + shapes `round` `ellipse` `leaf` `backleaf` |
105
- | `width`, `height` | `min` `max` `fixed` |
109
+ | `width`, `height`, `blockSize`, `inlineSize` | `min` `max` `fixed` |
106
110
  | `flow` | `row` `column` `row-reverse` `column-reverse` `wrap` `nowrap` `dense` |
107
111
  | `overflow` | `visible` `hidden` `scroll` `clip` `auto` `overlay` |
108
112
  | `position` | `static` `relative` `absolute` `fixed` `sticky` |
@@ -118,7 +122,7 @@ Directional modifiers beat placeholder zeros:
118
122
  - ❌ `padding: '2x 4x top right'` — a directional group takes one value, so `4x` is dropped → ✅ `padding: '2x top, 4x right'`
119
123
  - ❌ `fade: '3x 1x top bottom'` → ✅ `fade: '3x top, 1x bottom'`
120
124
 
121
- Value-only properties reject both colors and modifiers: `gap`, `columnGap`, `rowGap`, `opacity`, `zIndex`, `order`, `flexGrow`, `flexShrink`, `flexBasis`, `aspectRatio`, `lineClamp`, `tabSize`, `paddingInline`, `paddingBlock`. `fill` and `color` take a color (plus `none` / `transparent`); `caretColor` and `accentColor` take a color only.
125
+ Value-only properties reject both colors and modifiers: `gap`, `columnGap`, `rowGap`, `opacity`, `zIndex`, `order`, `flexGrow`, `flexShrink`, `flexBasis`, `aspectRatio`, `lineClamp`, and `tabSize`. Logical axis category styles accept one or two values in start/end order and the `start`, `end`, and `longhand` modifiers. `fill` and `color` take a color (plus `none` / `transparent`); `caretColor` and `accentColor` take a color only.
122
126
 
123
127
  ### `transition`
124
128
 
@@ -214,6 +218,7 @@ The selector must be a string literal and valid CSS. Values must be static — s
214
218
 
215
219
  - Token, preset, recipe, unit and `@alias` names exist in the project config.
216
220
  - Tasty shorthand chosen over CSS longhands; `flexGrow`/`flexShrink`/`flexBasis` over `flex`; `hide: true` over `display: 'none'`.
221
+ - Logical category styles keep native `block`/`inline` declarations and accept `start`/`end`, never physical direction modifiers.
217
222
  - Colors are `#tokens`, not hex/rgb/oklch/named; `$prop` not `var(--prop)`.
218
223
  - Spacing uses `x`/`r`/`bw`/`ow` units; math uses `(…)`, not `calc(…)`.
219
224
  - `true` only on the properties that accept it; no `!important`.
@@ -122,10 +122,10 @@ Tasty exports predefined style prop lists that group properties by role. Use the
122
122
  | Preset | Properties | Typical use |
123
123
  | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------- |
124
124
  | `FLOW_STYLES` | flow, gap, columnGap, rowGap, align, justify, placeItems, placeContent, alignItems, alignContent, justifyItems, justifyContent, gridColumns, gridRows, gridTemplate, gridAreas | Layout containers (`Space`, `Grid`) |
125
- | `POSITION_STYLES` | gridArea, gridColumn, gridRow, order, placeSelf, alignSelf, justifySelf, zIndex, margin, inset, position | Positioned elements (`Button`, `Badge`) |
126
- | `DIMENSION_STYLES` | width, height, flexBasis, flexGrow, flexShrink, flex | Sized elements |
125
+ | `POSITION_STYLES` | gridArea, gridColumn, gridRow, order, placeSelf, alignSelf, justifySelf, zIndex, physical/logical margin, inset, scroll margin/padding, position | Positioned elements (`Button`, `Badge`) |
126
+ | `DIMENSION_STYLES` | width, height, blockSize, inlineSize, flexBasis, flexGrow, flexShrink, flex | Sized elements |
127
127
  | `COLOR_STYLES` | color, fill, fade, image | Color-customizable elements |
128
- | `BLOCK_STYLES` | padding, paddingInline, paddingBlock, overflow, scrollbar, textAlign, border, radius, shadow, outline | Block-level containers |
128
+ | `BLOCK_STYLES` | padding, blockPadding, inlinePadding, overflow, scrollbar, textAlign, border, blockBorder, inlineBorder, radius, shadow, outline | Block-level containers |
129
129
  | `CONTAINER_STYLES` | All of the above combined (+ BASE_STYLES) | Fully flexible containers |
130
130
  | `OUTER_STYLES` | POSITION_STYLES + DIMENSION_STYLES + block outer (border, radius, shadow, outline) | Components whose outer shell is customizable |
131
131
  | `INNER_STYLES` | BASE_STYLES + COLOR_STYLES + block inner (padding, overflow, scrollbar) + FLOW_STYLES | Components whose inner layout is customizable |
package/docs/styles.md CHANGED
@@ -20,9 +20,10 @@ Use these instead of their raw CSS counterparts:
20
20
  |-----|------------|
21
21
  | `fill` | `backgroundColor`, `background` |
22
22
  | `image` | `backgroundImage` |
23
- | `padding` (with direction modifiers) | `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft` |
24
- | `margin` (with direction modifiers) | `marginTop`, `marginRight`, `marginBottom`, `marginLeft` |
25
- | `width` (with `min`/`max` modifiers) | `minWidth`, `maxWidth` |
23
+ | `padding` (with physical direction modifiers) | `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft` |
24
+ | `margin` (with physical direction modifiers) | `marginTop`, `marginRight`, `marginBottom`, `marginLeft` |
25
+ | Logical block/inline category styles (with `start`/`end` modifiers) | Native logical axis/edge declarations |
26
+ | `width` / `blockSize` / `inlineSize` (with `min`/`max` modifiers) | Their separate min/max declarations when one state map is sufficient |
26
27
  | `height` (with `min`/`max` modifiers) | `minHeight`, `maxHeight` |
27
28
  | `border` | `borderColor`, `borderWidth`, `borderStyle` |
28
29
  | `radius` | `borderRadius` |
@@ -125,9 +126,7 @@ A group that names *no* direction keeps plain CSS shorthand order, which is unam
125
126
 
126
127
  Later comma-separated groups override earlier groups for conflicting directions.
127
128
 
128
- Individual props `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft`, `paddingBlock`, `paddingInline` are supported but `padding` with modifiers is recommended.
129
-
130
- **Priority:** `padding` < `paddingBlock`/`paddingInline` < `paddingTop`/`paddingRight`/`paddingBottom`/`paddingLeft`
129
+ Individual physical props `paddingTop`, `paddingRight`, `paddingBottom`, and `paddingLeft` are supported, but `padding` with modifiers is recommended. Use `blockPadding` or `inlinePadding` for an enhanced logical-axis shorthand; native declarations such as `paddingInlineStart` remain ordinary CSS properties. See [Logical properties](#logical-properties).
131
130
 
132
131
  ### `margin`
133
132
 
@@ -153,9 +152,7 @@ The [one-value-per-directional-group rule](#padding) from `padding` applies here
153
152
 
154
153
  Later comma-separated groups override earlier groups for conflicting directions.
155
154
 
156
- Individual props `marginTop`, `marginRight`, `marginBottom`, `marginLeft`, `marginBlock`, `marginInline` are supported but `margin` with modifiers is recommended.
157
-
158
- **Priority:** `margin` < `marginBlock`/`marginInline` < `marginTop`/`marginRight`/`marginBottom`/`marginLeft`
155
+ Individual physical props `marginTop`, `marginRight`, `marginBottom`, and `marginLeft` are supported, but `margin` with modifiers is recommended. Use `blockMargin` or `inlineMargin` for an enhanced logical-axis shorthand; native declarations such as `marginInlineStart` remain ordinary CSS properties. See [Logical properties](#logical-properties).
159
156
 
160
157
  ### `width`
161
158
 
@@ -234,9 +231,44 @@ second to every side they span.
234
231
 
235
232
  Later comma-separated groups override earlier groups for conflicting directions.
236
233
 
237
- Individual props `top`, `right`, `bottom`, `left`, `insetBlock`, `insetInline` are supported. When only individual direction props are used (without `inset`), individual CSS properties are output for correct cascade behavior with state overrides.
234
+ Individual physical props `top`, `right`, `bottom`, and `left` are supported. When only those props are used (without `inset`), individual CSS properties are output for correct cascade behavior with state overrides. Use `blockInset` or `inlineInset` for logical axes; native declarations such as `insetInlineStart` remain ordinary CSS properties.
235
+
236
+ ### Logical properties
237
+
238
+ Tasty provides one enhanced handler for each logical axis/category pair. Each handler emits native logical CSS instead of converting values to `top`, `right`, `bottom`, `left`, `width`, or `height`. The browser therefore resolves `block`, `inline`, `start`, and `end` from the element's `writingMode` and `direction`.
239
+
240
+ ```jsx
241
+ styles: {
242
+ direction: 'rtl',
243
+ inlinePadding: '1x start, 2x end',
244
+ inlineBorder: '1bw solid #accent start',
245
+ blockInset: '0 end',
246
+ }
247
+ ```
248
+
249
+ An axis handler accepts one value for both edges or two values in logical **start/end** order. Use `start` and `end` modifiers, with comma-separated groups for different edge values. Unspecified padding, margin, and scroll edges reset to `0`; unspecified inset edges reset to `auto`. The `longhand` modifier emits the two native start/end declarations.
250
+
251
+ | Category | Block axis | Inline axis |
252
+ |----------|------------|-------------|
253
+ | Size | `blockSize` | `inlineSize` |
254
+ | Padding | `blockPadding` | `inlinePadding` |
255
+ | Margin | `blockMargin` | `inlineMargin` |
256
+ | Inset | `blockInset` | `inlineInset` |
257
+ | Scroll margin | `blockScrollMargin` | `inlineScrollMargin` |
258
+ | Scroll padding | `blockScrollPadding` | `inlineScrollPadding` |
259
+ | Border | `blockBorder` | `inlineBorder` |
260
+
261
+ `blockSize` and `inlineSize` use the same enhanced one/two/three-value and `min`/`max`/`fixed` syntax as `width`. Separate `minBlockSize`, `maxBlockSize`, `minInlineSize`, and `maxInlineSize` declarations override constraints produced by that shorthand.
262
+
263
+ `blockBorder` and `inlineBorder` use the same width/style/color parsing and defaults as `border`, including `1bw`, color tokens, and `true`. Logical padding, margin, scroll margin, and scroll padding default to `1x` for `true`; logical inset defaults to `0`.
264
+
265
+ Native logical CSS names—such as `paddingInlineStart`, `borderBlockColor`, and `borderStartStartRadius`—remain available as ordinary CSS properties. They support Tasty tokens and units through the normal value parser, but do not get category defaults or directional shorthand behavior.
266
+
267
+ The physical `scrollMargin` and `scrollPadding` shorthands support the same values and physical direction modifiers as `padding`. Their logical counterparts stay in separate axis handlers.
268
+
269
+ `writingMode`, `direction`, and `textOrientation` are standard CSS control properties. They use Tasty's normal value parser and need no special handler.
238
270
 
239
- **Priority:** `inset` < `insetBlock`/`insetInline` < `top`/`right`/`bottom`/`left`
271
+ Avoid setting physical and logical declarations that resolve to the same edge in one style block. Tasty keeps them separate and lets the native CSS cascade resolve the overlap, whose result also depends on writing mode and direction.
240
272
 
241
273
  ---
242
274
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenphi/tasty",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
4
4
  "description": "A design-system-integrated styling system and DSL for concise, state-aware UI styling",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -200,19 +200,19 @@
200
200
  "name": "main (import *)",
201
201
  "path": "dist/index.js",
202
202
  "import": "*",
203
- "limit": "57.5 kB"
203
+ "limit": "58.5 kB"
204
204
  },
205
205
  {
206
206
  "name": "core (import *)",
207
207
  "path": "dist/core/index.js",
208
208
  "import": "*",
209
- "limit": "54.5 kB"
209
+ "limit": "55.5 kB"
210
210
  },
211
211
  {
212
212
  "name": "static",
213
213
  "path": "dist/static/index.js",
214
214
  "import": "*",
215
- "limit": "16.8 kB"
215
+ "limit": "17.6 kB"
216
216
  },
217
217
  {
218
218
  "name": "zero",
@@ -223,7 +223,7 @@
223
223
  "path",
224
224
  "crypto"
225
225
  ],
226
- "limit": "31.25 kB"
226
+ "limit": "32.2 kB"
227
227
  },
228
228
  {
229
229
  "name": "babel-plugin",
@@ -234,7 +234,7 @@
234
234
  "path",
235
235
  "crypto"
236
236
  ],
237
- "limit": "49.6 kB"
237
+ "limit": "50.6 kB"
238
238
  }
239
239
  ],
240
240
  "scripts": {