@tenphi/tasty 0.6.0 → 0.7.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.
@@ -1,4 +1,4 @@
1
- import { getRgbValuesFromRgbaString, strToRgb } from "../utils/styles.js";
1
+ import { RE_NUMBER, RE_RAW_UNIT } from "../parser/const.js";
2
2
 
3
3
  //#region src/properties/index.ts
4
4
  const PROPERTIES_KEY = "@properties";
@@ -88,7 +88,11 @@ function parsePropertyToken(token) {
88
88
  }
89
89
  /**
90
90
  * Normalize a property definition to a consistent string representation.
91
- * Used for comparing definitions to detect changes/conflicts.
91
+ * Used for comparing definitions to detect type conflicts.
92
+ *
93
+ * Only `syntax` and `inherits` are compared — `initialValue` is intentionally
94
+ * excluded because different components may set different defaults for the
95
+ * same typed property (e.g. auto-inferred `0px` vs explicit `6px`).
92
96
  *
93
97
  * Keys are sorted alphabetically to ensure consistent comparison:
94
98
  * { inherits: true, syntax: '<color>' } === { syntax: '<color>', inherits: true }
@@ -96,7 +100,6 @@ function parsePropertyToken(token) {
96
100
  function normalizePropertyDefinition(def) {
97
101
  const normalized = {};
98
102
  if (def.inherits !== void 0) normalized.inherits = def.inherits;
99
- if (def.initialValue !== void 0) normalized.initialValue = String(def.initialValue);
100
103
  if (def.syntax !== void 0) normalized.syntax = def.syntax;
101
104
  return JSON.stringify(normalized);
102
105
  }
@@ -134,25 +137,82 @@ function getEffectiveDefinition(token, userDefinition) {
134
137
  isValid: true
135
138
  };
136
139
  }
140
+ const UNIT_TO_SYNTAX = {};
141
+ const LENGTH_UNITS = [
142
+ "px",
143
+ "em",
144
+ "rem",
145
+ "vw",
146
+ "vh",
147
+ "vmin",
148
+ "vmax",
149
+ "ch",
150
+ "ex",
151
+ "cap",
152
+ "ic",
153
+ "lh",
154
+ "rlh",
155
+ "svw",
156
+ "svh",
157
+ "lvw",
158
+ "lvh",
159
+ "dvw",
160
+ "dvh",
161
+ "cqw",
162
+ "cqh",
163
+ "cqi",
164
+ "cqb",
165
+ "cqmin",
166
+ "cqmax"
167
+ ];
168
+ const ANGLE_UNITS = [
169
+ "deg",
170
+ "rad",
171
+ "grad",
172
+ "turn"
173
+ ];
174
+ const TIME_UNITS = ["ms", "s"];
175
+ for (const u of LENGTH_UNITS) UNIT_TO_SYNTAX[u] = {
176
+ syntax: "<length>",
177
+ initialValue: "0px"
178
+ };
179
+ UNIT_TO_SYNTAX["%"] = {
180
+ syntax: "<percentage>",
181
+ initialValue: "0%"
182
+ };
183
+ for (const u of ANGLE_UNITS) UNIT_TO_SYNTAX[u] = {
184
+ syntax: "<angle>",
185
+ initialValue: "0deg"
186
+ };
187
+ for (const u of TIME_UNITS) UNIT_TO_SYNTAX[u] = {
188
+ syntax: "<time>",
189
+ initialValue: "0s"
190
+ };
137
191
  /**
138
- * Extract RGB triplet string from a color initial value.
139
- * Used when auto-creating the companion `-rgb` property for color @property definitions.
192
+ * Infer CSS @property syntax from a concrete value.
193
+ * Only detects numeric types: \<number\>, \<length\>, \<percentage\>, \<angle\>, \<time\>.
194
+ * Color properties are handled separately via the #name token convention
195
+ * (--name-color gets \<color\> syntax automatically in getEffectiveDefinition).
140
196
  *
141
- * @param initialValue - The color property's initial value (e.g., 'rgb(255 255 255)', 'transparent', '#fff')
142
- * @returns Space-separated RGB triplet (e.g., '255 255 255'), defaults to '0 0 0'
197
+ * @param value - The CSS value to infer from (e.g. '10px', '1', '45deg')
198
+ * @returns Inferred syntax and initial value, or null if not inferable
143
199
  */
144
- function colorInitialValueToRgb(initialValue) {
145
- if (initialValue == null) return "0 0 0";
146
- const str = String(initialValue).trim();
147
- if (!str || str === "transparent") return "0 0 0";
148
- const rgba = strToRgb(str);
149
- if (rgba) {
150
- const values = getRgbValuesFromRgbaString(rgba);
151
- if (values.length >= 3) return values.slice(0, 3).join(" ");
200
+ function inferSyntaxFromValue(value) {
201
+ if (!value || typeof value !== "string") return null;
202
+ const trimmed = value.trim();
203
+ if (!trimmed) return null;
204
+ if (RE_NUMBER.test(trimmed)) return {
205
+ syntax: "<number>",
206
+ initialValue: "0"
207
+ };
208
+ const unitMatch = trimmed.match(RE_RAW_UNIT);
209
+ if (unitMatch) {
210
+ const mapping = UNIT_TO_SYNTAX[unitMatch[2]];
211
+ if (mapping) return mapping;
152
212
  }
153
- return "0 0 0";
213
+ return null;
154
214
  }
155
215
 
156
216
  //#endregion
157
- export { colorInitialValueToRgb, extractLocalProperties, getEffectiveDefinition, hasLocalProperties, normalizePropertyDefinition };
217
+ export { extractLocalProperties, getEffectiveDefinition, hasLocalProperties, inferSyntaxFromValue, normalizePropertyDefinition };
158
218
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/properties/index.ts"],"sourcesContent":["/**\n * Properties Utilities\n *\n * Utilities for extracting and processing CSS @property definitions in styles.\n * Unlike keyframes, properties are permanent once registered and don't need cleanup.\n *\n * Property names use tasty token syntax:\n * - `$name` for regular properties → `--name`\n * - `#name` for color properties → `--name-color` (auto-sets syntax: '<color>')\n */\n\nimport type { PropertyDefinition } from '../injector/types';\nimport type { Styles } from '../styles/types';\nimport { getRgbValuesFromRgbaString, strToRgb } from '../utils/styles';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst PROPERTIES_KEY = '@properties';\n\n/**\n * Valid CSS custom property name pattern (after the -- prefix).\n * Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.\n */\nconst VALID_PROPERTY_NAME_PATTERN = /^[a-z_][a-z0-9-_]*$/i;\n\n// ============================================================================\n// Validation Functions\n// ============================================================================\n\n/**\n * Validate a CSS custom property name (the part after --).\n * Returns true if the name is valid for use as a CSS custom property.\n */\nexport function isValidPropertyName(name: string): boolean {\n return VALID_PROPERTY_NAME_PATTERN.test(name);\n}\n\n/**\n * Result of parsing a property token.\n */\nexport interface ParsedPropertyToken {\n /** The CSS custom property name (e.g., '--my-prop') */\n cssName: string;\n /** Whether this is a color property */\n isColor: boolean;\n /** Whether the token was valid */\n isValid: boolean;\n /** Error message if invalid */\n error?: string;\n}\n\n// ============================================================================\n// Extraction Functions\n// ============================================================================\n\n/**\n * Check if styles object has local @properties definition.\n * Fast path: single property lookup.\n */\nexport function hasLocalProperties(styles: Styles): boolean {\n return PROPERTIES_KEY in styles;\n}\n\n/**\n * Extract local @properties from styles object.\n * Returns null if no local properties (fast path).\n */\nexport function extractLocalProperties(\n styles: Styles,\n): Record<string, PropertyDefinition> | null {\n const properties = styles[PROPERTIES_KEY];\n if (!properties || typeof properties !== 'object') {\n return null;\n }\n return properties as Record<string, PropertyDefinition>;\n}\n\n// ============================================================================\n// Token Parsing Functions\n// ============================================================================\n\n/**\n * Parse a property token name and return the CSS property name and whether it's a color.\n * Supports tasty token syntax and validates the property name.\n *\n * Token formats:\n * - `$name` → { cssName: '--name', isColor: false }\n * - `#name` → { cssName: '--name-color', isColor: true }\n * - `--name` → { cssName: '--name', isColor: false } (legacy, auto-detect color by suffix)\n * - `name` → { cssName: '--name', isColor: false } (legacy)\n *\n * @param token - The property token to parse\n * @returns Parsed result with cssName, isColor, isValid, and optional error\n */\nexport function parsePropertyToken(token: string): ParsedPropertyToken {\n if (!token || typeof token !== 'string') {\n return {\n cssName: '',\n isColor: false,\n isValid: false,\n error: 'Property token must be a non-empty string',\n };\n }\n\n let name: string;\n let isColor: boolean;\n\n if (token.startsWith('$')) {\n // Regular property token: $name → --name\n name = token.slice(1);\n isColor = false;\n } else if (token.startsWith('#')) {\n // Color property token: #name → --name-color\n name = token.slice(1);\n isColor = true;\n } else if (token.startsWith('--')) {\n // Legacy format with -- prefix\n name = token.slice(2);\n isColor = token.endsWith('-color');\n } else {\n // Legacy format without prefix\n name = token;\n isColor = token.endsWith('-color');\n }\n\n // Validate the name\n if (!name) {\n return {\n cssName: '',\n isColor,\n isValid: false,\n error: 'Property name cannot be empty',\n };\n }\n\n if (!isValidPropertyName(name)) {\n return {\n cssName: '',\n isColor,\n isValid: false,\n error: `Invalid property name \"${name}\". Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.`,\n };\n }\n\n // Build the CSS custom property name\n // For #name tokens, we add -color suffix\n // For legacy formats (--name-color or name-color), the name already includes -color\n let cssName: string;\n if (token.startsWith('#')) {\n // Color token: #name → --name-color\n cssName = `--${name}-color`;\n } else {\n // All other formats: just add -- prefix\n cssName = `--${name}`;\n }\n\n return {\n cssName,\n isColor,\n isValid: true,\n };\n}\n\n// ============================================================================\n// Normalization Functions\n// ============================================================================\n\n/**\n * Normalize a property name to the CSS custom property format.\n *\n * @deprecated Use parsePropertyToken instead for proper token handling\n */\nexport function normalizePropertyName(name: string): string {\n const result = parsePropertyToken(name);\n return result.isValid ? result.cssName : `--${name}`;\n}\n\n/**\n * Normalize a property definition to a consistent string representation.\n * Used for comparing definitions to detect changes/conflicts.\n *\n * Keys are sorted alphabetically to ensure consistent comparison:\n * { inherits: true, syntax: '<color>' } === { syntax: '<color>', inherits: true }\n */\nexport function normalizePropertyDefinition(def: PropertyDefinition): string {\n const normalized: Record<string, unknown> = {};\n\n // Add properties in alphabetical order\n if (def.inherits !== undefined) {\n normalized.inherits = def.inherits;\n }\n if (def.initialValue !== undefined) {\n normalized.initialValue = String(def.initialValue);\n }\n if (def.syntax !== undefined) {\n normalized.syntax = def.syntax;\n }\n\n return JSON.stringify(normalized);\n}\n\n/**\n * Result of getEffectiveDefinition.\n */\nexport interface EffectiveDefinitionResult {\n /** The CSS custom property name */\n cssName: string;\n /** The effective property definition */\n definition: PropertyDefinition;\n /** Whether this is a color property */\n isColor: boolean;\n /** Whether the token was valid */\n isValid: boolean;\n /** Error message if invalid */\n error?: string;\n}\n\n/**\n * Get the effective property definition for a token.\n * For color tokens (#name), auto-sets syntax to '<color>' and defaults initialValue to 'transparent'.\n *\n * @param token - Property token ($name, #name, --name, or plain name)\n * @param userDefinition - User-provided definition options\n * @returns Effective definition with cssName, definition, isValid, and optional error\n */\nexport function getEffectiveDefinition(\n token: string,\n userDefinition: PropertyDefinition,\n): EffectiveDefinitionResult {\n const parsed = parsePropertyToken(token);\n\n if (!parsed.isValid) {\n return {\n cssName: '',\n definition: userDefinition,\n isColor: false,\n isValid: false,\n error: parsed.error,\n };\n }\n\n if (parsed.isColor) {\n // Color properties have fixed syntax and default initialValue\n return {\n cssName: parsed.cssName,\n definition: {\n syntax: '<color>', // Always '<color>' for color tokens, cannot be overridden\n inherits: userDefinition.inherits, // Allow inherits to be customized\n initialValue: userDefinition.initialValue ?? 'transparent', // Default to transparent\n },\n isColor: true,\n isValid: true,\n };\n }\n\n // Regular properties use the definition as-is\n return {\n cssName: parsed.cssName,\n definition: userDefinition,\n isColor: false,\n isValid: true,\n };\n}\n\n// ============================================================================\n// Color RGB Companion Helpers\n// ============================================================================\n\n/**\n * Extract RGB triplet string from a color initial value.\n * Used when auto-creating the companion `-rgb` property for color @property definitions.\n *\n * @param initialValue - The color property's initial value (e.g., 'rgb(255 255 255)', 'transparent', '#fff')\n * @returns Space-separated RGB triplet (e.g., '255 255 255'), defaults to '0 0 0'\n */\nexport function colorInitialValueToRgb(initialValue?: string | number): string {\n if (initialValue == null) return '0 0 0';\n\n const str = String(initialValue).trim();\n if (!str || str === 'transparent') return '0 0 0';\n\n const rgba = strToRgb(str);\n if (rgba) {\n const values = getRgbValuesFromRgbaString(rgba);\n if (values.length >= 3) return values.slice(0, 3).join(' ');\n }\n\n return '0 0 0';\n}\n"],"mappings":";;;AAmBA,MAAM,iBAAiB;;;;;AAMvB,MAAM,8BAA8B;;;;;AAUpC,SAAgB,oBAAoB,MAAuB;AACzD,QAAO,4BAA4B,KAAK,KAAK;;;;;;AAyB/C,SAAgB,mBAAmB,QAAyB;AAC1D,QAAO,kBAAkB;;;;;;AAO3B,SAAgB,uBACd,QAC2C;CAC3C,MAAM,aAAa,OAAO;AAC1B,KAAI,CAAC,cAAc,OAAO,eAAe,SACvC,QAAO;AAET,QAAO;;;;;;;;;;;;;;;AAoBT,SAAgB,mBAAmB,OAAoC;AACrE,KAAI,CAAC,SAAS,OAAO,UAAU,SAC7B,QAAO;EACL,SAAS;EACT,SAAS;EACT,SAAS;EACT,OAAO;EACR;CAGH,IAAI;CACJ,IAAI;AAEJ,KAAI,MAAM,WAAW,IAAI,EAAE;AAEzB,SAAO,MAAM,MAAM,EAAE;AACrB,YAAU;YACD,MAAM,WAAW,IAAI,EAAE;AAEhC,SAAO,MAAM,MAAM,EAAE;AACrB,YAAU;YACD,MAAM,WAAW,KAAK,EAAE;AAEjC,SAAO,MAAM,MAAM,EAAE;AACrB,YAAU,MAAM,SAAS,SAAS;QAC7B;AAEL,SAAO;AACP,YAAU,MAAM,SAAS,SAAS;;AAIpC,KAAI,CAAC,KACH,QAAO;EACL,SAAS;EACT;EACA,SAAS;EACT,OAAO;EACR;AAGH,KAAI,CAAC,oBAAoB,KAAK,CAC5B,QAAO;EACL,SAAS;EACT;EACA,SAAS;EACT,OAAO,0BAA0B,KAAK;EACvC;CAMH,IAAI;AACJ,KAAI,MAAM,WAAW,IAAI,CAEvB,WAAU,KAAK,KAAK;KAGpB,WAAU,KAAK;AAGjB,QAAO;EACL;EACA;EACA,SAAS;EACV;;;;;;;;;AAwBH,SAAgB,4BAA4B,KAAiC;CAC3E,MAAM,aAAsC,EAAE;AAG9C,KAAI,IAAI,aAAa,OACnB,YAAW,WAAW,IAAI;AAE5B,KAAI,IAAI,iBAAiB,OACvB,YAAW,eAAe,OAAO,IAAI,aAAa;AAEpD,KAAI,IAAI,WAAW,OACjB,YAAW,SAAS,IAAI;AAG1B,QAAO,KAAK,UAAU,WAAW;;;;;;;;;;AA2BnC,SAAgB,uBACd,OACA,gBAC2B;CAC3B,MAAM,SAAS,mBAAmB,MAAM;AAExC,KAAI,CAAC,OAAO,QACV,QAAO;EACL,SAAS;EACT,YAAY;EACZ,SAAS;EACT,SAAS;EACT,OAAO,OAAO;EACf;AAGH,KAAI,OAAO,QAET,QAAO;EACL,SAAS,OAAO;EAChB,YAAY;GACV,QAAQ;GACR,UAAU,eAAe;GACzB,cAAc,eAAe,gBAAgB;GAC9C;EACD,SAAS;EACT,SAAS;EACV;AAIH,QAAO;EACL,SAAS,OAAO;EAChB,YAAY;EACZ,SAAS;EACT,SAAS;EACV;;;;;;;;;AAcH,SAAgB,uBAAuB,cAAwC;AAC7E,KAAI,gBAAgB,KAAM,QAAO;CAEjC,MAAM,MAAM,OAAO,aAAa,CAAC,MAAM;AACvC,KAAI,CAAC,OAAO,QAAQ,cAAe,QAAO;CAE1C,MAAM,OAAO,SAAS,IAAI;AAC1B,KAAI,MAAM;EACR,MAAM,SAAS,2BAA2B,KAAK;AAC/C,MAAI,OAAO,UAAU,EAAG,QAAO,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;;AAG7D,QAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/properties/index.ts"],"sourcesContent":["/**\n * Properties Utilities\n *\n * Utilities for extracting and processing CSS @property definitions in styles.\n * Unlike keyframes, properties are permanent once registered and don't need cleanup.\n *\n * Property names use tasty token syntax:\n * - `$name` for regular properties → `--name`\n * - `#name` for color properties → `--name-color` (auto-sets syntax: '<color>')\n */\n\nimport type { PropertyDefinition } from '../injector/types';\nimport { RE_NUMBER, RE_RAW_UNIT } from '../parser/const';\nimport type { Styles } from '../styles/types';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst PROPERTIES_KEY = '@properties';\n\n/**\n * Valid CSS custom property name pattern (after the -- prefix).\n * Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.\n */\nconst VALID_PROPERTY_NAME_PATTERN = /^[a-z_][a-z0-9-_]*$/i;\n\n// ============================================================================\n// Validation Functions\n// ============================================================================\n\n/**\n * Validate a CSS custom property name (the part after --).\n * Returns true if the name is valid for use as a CSS custom property.\n */\nexport function isValidPropertyName(name: string): boolean {\n return VALID_PROPERTY_NAME_PATTERN.test(name);\n}\n\n/**\n * Result of parsing a property token.\n */\nexport interface ParsedPropertyToken {\n /** The CSS custom property name (e.g., '--my-prop') */\n cssName: string;\n /** Whether this is a color property */\n isColor: boolean;\n /** Whether the token was valid */\n isValid: boolean;\n /** Error message if invalid */\n error?: string;\n}\n\n// ============================================================================\n// Extraction Functions\n// ============================================================================\n\n/**\n * Check if styles object has local @properties definition.\n * Fast path: single property lookup.\n */\nexport function hasLocalProperties(styles: Styles): boolean {\n return PROPERTIES_KEY in styles;\n}\n\n/**\n * Extract local @properties from styles object.\n * Returns null if no local properties (fast path).\n */\nexport function extractLocalProperties(\n styles: Styles,\n): Record<string, PropertyDefinition> | null {\n const properties = styles[PROPERTIES_KEY];\n if (!properties || typeof properties !== 'object') {\n return null;\n }\n return properties as Record<string, PropertyDefinition>;\n}\n\n// ============================================================================\n// Token Parsing Functions\n// ============================================================================\n\n/**\n * Parse a property token name and return the CSS property name and whether it's a color.\n * Supports tasty token syntax and validates the property name.\n *\n * Token formats:\n * - `$name` → { cssName: '--name', isColor: false }\n * - `#name` → { cssName: '--name-color', isColor: true }\n * - `--name` → { cssName: '--name', isColor: false } (legacy, auto-detect color by suffix)\n * - `name` → { cssName: '--name', isColor: false } (legacy)\n *\n * @param token - The property token to parse\n * @returns Parsed result with cssName, isColor, isValid, and optional error\n */\nexport function parsePropertyToken(token: string): ParsedPropertyToken {\n if (!token || typeof token !== 'string') {\n return {\n cssName: '',\n isColor: false,\n isValid: false,\n error: 'Property token must be a non-empty string',\n };\n }\n\n let name: string;\n let isColor: boolean;\n\n if (token.startsWith('$')) {\n // Regular property token: $name → --name\n name = token.slice(1);\n isColor = false;\n } else if (token.startsWith('#')) {\n // Color property token: #name → --name-color\n name = token.slice(1);\n isColor = true;\n } else if (token.startsWith('--')) {\n // Legacy format with -- prefix\n name = token.slice(2);\n isColor = token.endsWith('-color');\n } else {\n // Legacy format without prefix\n name = token;\n isColor = token.endsWith('-color');\n }\n\n // Validate the name\n if (!name) {\n return {\n cssName: '',\n isColor,\n isValid: false,\n error: 'Property name cannot be empty',\n };\n }\n\n if (!isValidPropertyName(name)) {\n return {\n cssName: '',\n isColor,\n isValid: false,\n error: `Invalid property name \"${name}\". Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.`,\n };\n }\n\n // Build the CSS custom property name\n // For #name tokens, we add -color suffix\n // For legacy formats (--name-color or name-color), the name already includes -color\n let cssName: string;\n if (token.startsWith('#')) {\n // Color token: #name → --name-color\n cssName = `--${name}-color`;\n } else {\n // All other formats: just add -- prefix\n cssName = `--${name}`;\n }\n\n return {\n cssName,\n isColor,\n isValid: true,\n };\n}\n\n// ============================================================================\n// Normalization Functions\n// ============================================================================\n\n/**\n * Normalize a property name to the CSS custom property format.\n *\n * @deprecated Use parsePropertyToken instead for proper token handling\n */\nexport function normalizePropertyName(name: string): string {\n const result = parsePropertyToken(name);\n return result.isValid ? result.cssName : `--${name}`;\n}\n\n/**\n * Normalize a property definition to a consistent string representation.\n * Used for comparing definitions to detect type conflicts.\n *\n * Only `syntax` and `inherits` are compared — `initialValue` is intentionally\n * excluded because different components may set different defaults for the\n * same typed property (e.g. auto-inferred `0px` vs explicit `6px`).\n *\n * Keys are sorted alphabetically to ensure consistent comparison:\n * { inherits: true, syntax: '<color>' } === { syntax: '<color>', inherits: true }\n */\nexport function normalizePropertyDefinition(def: PropertyDefinition): string {\n const normalized: Record<string, unknown> = {};\n\n if (def.inherits !== undefined) {\n normalized.inherits = def.inherits;\n }\n if (def.syntax !== undefined) {\n normalized.syntax = def.syntax;\n }\n\n return JSON.stringify(normalized);\n}\n\n/**\n * Result of getEffectiveDefinition.\n */\nexport interface EffectiveDefinitionResult {\n /** The CSS custom property name */\n cssName: string;\n /** The effective property definition */\n definition: PropertyDefinition;\n /** Whether this is a color property */\n isColor: boolean;\n /** Whether the token was valid */\n isValid: boolean;\n /** Error message if invalid */\n error?: string;\n}\n\n/**\n * Get the effective property definition for a token.\n * For color tokens (#name), auto-sets syntax to '<color>' and defaults initialValue to 'transparent'.\n *\n * @param token - Property token ($name, #name, --name, or plain name)\n * @param userDefinition - User-provided definition options\n * @returns Effective definition with cssName, definition, isValid, and optional error\n */\nexport function getEffectiveDefinition(\n token: string,\n userDefinition: PropertyDefinition,\n): EffectiveDefinitionResult {\n const parsed = parsePropertyToken(token);\n\n if (!parsed.isValid) {\n return {\n cssName: '',\n definition: userDefinition,\n isColor: false,\n isValid: false,\n error: parsed.error,\n };\n }\n\n if (parsed.isColor) {\n // Color properties have fixed syntax and default initialValue\n return {\n cssName: parsed.cssName,\n definition: {\n syntax: '<color>', // Always '<color>' for color tokens, cannot be overridden\n inherits: userDefinition.inherits, // Allow inherits to be customized\n initialValue: userDefinition.initialValue ?? 'transparent', // Default to transparent\n },\n isColor: true,\n isValid: true,\n };\n }\n\n // Regular properties use the definition as-is\n return {\n cssName: parsed.cssName,\n definition: userDefinition,\n isColor: false,\n isValid: true,\n };\n}\n\n// ============================================================================\n// Value Type Inference\n// ============================================================================\n\n/**\n * Result of inferring a CSS @property syntax from a value.\n */\nexport interface InferredSyntax {\n syntax: string;\n initialValue: string;\n}\n\nconst UNIT_TO_SYNTAX: Record<string, InferredSyntax> = {};\n\nconst LENGTH_UNITS = [\n 'px',\n 'em',\n 'rem',\n 'vw',\n 'vh',\n 'vmin',\n 'vmax',\n 'ch',\n 'ex',\n 'cap',\n 'ic',\n 'lh',\n 'rlh',\n 'svw',\n 'svh',\n 'lvw',\n 'lvh',\n 'dvw',\n 'dvh',\n 'cqw',\n 'cqh',\n 'cqi',\n 'cqb',\n 'cqmin',\n 'cqmax',\n];\n\nconst ANGLE_UNITS = ['deg', 'rad', 'grad', 'turn'];\nconst TIME_UNITS = ['ms', 's'];\n\nfor (const u of LENGTH_UNITS) {\n UNIT_TO_SYNTAX[u] = { syntax: '<length>', initialValue: '0px' };\n}\nUNIT_TO_SYNTAX['%'] = { syntax: '<percentage>', initialValue: '0%' };\nfor (const u of ANGLE_UNITS) {\n UNIT_TO_SYNTAX[u] = { syntax: '<angle>', initialValue: '0deg' };\n}\nfor (const u of TIME_UNITS) {\n UNIT_TO_SYNTAX[u] = { syntax: '<time>', initialValue: '0s' };\n}\n\n/**\n * Infer CSS @property syntax from a concrete value.\n * Only detects numeric types: \\<number\\>, \\<length\\>, \\<percentage\\>, \\<angle\\>, \\<time\\>.\n * Color properties are handled separately via the #name token convention\n * (--name-color gets \\<color\\> syntax automatically in getEffectiveDefinition).\n *\n * @param value - The CSS value to infer from (e.g. '10px', '1', '45deg')\n * @returns Inferred syntax and initial value, or null if not inferable\n */\nexport function inferSyntaxFromValue(value: string): InferredSyntax | null {\n if (!value || typeof value !== 'string') return null;\n\n const trimmed = value.trim();\n if (!trimmed) return null;\n\n if (RE_NUMBER.test(trimmed)) {\n return { syntax: '<number>', initialValue: '0' };\n }\n\n const unitMatch = trimmed.match(RE_RAW_UNIT);\n if (unitMatch) {\n const unit = unitMatch[2];\n const mapping = UNIT_TO_SYNTAX[unit];\n if (mapping) return mapping;\n }\n\n return null;\n}\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ //#region src/properties/property-type-resolver.d.ts
2
+ /**
3
+ * PropertyTypeResolver
4
+ *
5
+ * Automatically infers CSS @property types from custom property values.
6
+ * Supports deferred resolution for var() reference chains of arbitrary depth.
7
+ */
8
+ declare class PropertyTypeResolver {
9
+ /** propName → the prop it depends on */
10
+ private pendingDeps;
11
+ /** propName → list of props waiting on it */
12
+ private reverseDeps;
13
+ /**
14
+ * Scan CSS declarations and auto-register @property for custom properties
15
+ * whose types can be inferred from their values.
16
+ */
17
+ scanDeclarations(declarations: string, isPropertyDefined: (name: string) => boolean, registerProperty: (name: string, syntax: string, initialValue: string) => void): void;
18
+ private addDependency;
19
+ private resolve;
20
+ private isComplexValue;
21
+ }
22
+ //#endregion
23
+ export { PropertyTypeResolver };
24
+ //# sourceMappingURL=property-type-resolver.d.ts.map
@@ -0,0 +1,83 @@
1
+ import { inferSyntaxFromValue } from "./index.js";
2
+
3
+ //#region src/properties/property-type-resolver.ts
4
+ /**
5
+ * PropertyTypeResolver
6
+ *
7
+ * Automatically infers CSS @property types from custom property values.
8
+ * Supports deferred resolution for var() reference chains of arbitrary depth.
9
+ */
10
+ const CUSTOM_PROP_DECL = /^\s*(--[a-z0-9_-]+)\s*:\s*(.+?)\s*$/i;
11
+ const SINGLE_VAR_REF = /^var\((--[a-z0-9_-]+)\)$/i;
12
+ var PropertyTypeResolver = class {
13
+ /** propName → the prop it depends on */
14
+ pendingDeps = /* @__PURE__ */ new Map();
15
+ /** propName → list of props waiting on it */
16
+ reverseDeps = /* @__PURE__ */ new Map();
17
+ /**
18
+ * Scan CSS declarations and auto-register @property for custom properties
19
+ * whose types can be inferred from their values.
20
+ */
21
+ scanDeclarations(declarations, isPropertyDefined, registerProperty) {
22
+ if (!declarations.includes("--")) return;
23
+ const parts = declarations.split(/;+/);
24
+ for (const part of parts) {
25
+ if (!part.trim()) continue;
26
+ const match = CUSTOM_PROP_DECL.exec(part);
27
+ if (!match) continue;
28
+ const propName = match[1];
29
+ const value = match[2].trim();
30
+ if (isPropertyDefined(propName)) continue;
31
+ if (propName.endsWith("-color")) {
32
+ registerProperty(propName, "<color>", "transparent");
33
+ continue;
34
+ }
35
+ const varMatch = SINGLE_VAR_REF.exec(value);
36
+ if (varMatch) {
37
+ const depName = varMatch[1];
38
+ this.addDependency(propName, depName);
39
+ continue;
40
+ }
41
+ if (this.isComplexValue(value)) continue;
42
+ const inferred = inferSyntaxFromValue(value);
43
+ if (!inferred) continue;
44
+ this.resolve(propName, inferred.syntax, inferred.initialValue, isPropertyDefined, registerProperty);
45
+ }
46
+ }
47
+ addDependency(propName, depName) {
48
+ if (propName === depName) return;
49
+ this.pendingDeps.set(propName, depName);
50
+ let dependents = this.reverseDeps.get(depName);
51
+ if (!dependents) {
52
+ dependents = [];
53
+ this.reverseDeps.set(depName, dependents);
54
+ }
55
+ if (!dependents.includes(propName)) dependents.push(propName);
56
+ }
57
+ resolve(propName, syntax, initialValue, isPropertyDefined, registerProperty, resolving) {
58
+ if (!resolving) resolving = /* @__PURE__ */ new Set();
59
+ if (resolving.has(propName)) return;
60
+ resolving.add(propName);
61
+ if (!isPropertyDefined(propName)) registerProperty(propName, syntax, initialValue);
62
+ const dependents = this.reverseDeps.get(propName);
63
+ if (dependents) {
64
+ this.reverseDeps.delete(propName);
65
+ for (const dependent of dependents) {
66
+ this.pendingDeps.delete(dependent);
67
+ if (isPropertyDefined(dependent)) continue;
68
+ this.resolve(dependent, syntax, initialValue, isPropertyDefined, registerProperty, resolving);
69
+ }
70
+ }
71
+ }
72
+ isComplexValue(value) {
73
+ if (value.includes("calc(")) return true;
74
+ const firstVar = value.indexOf("var(");
75
+ if (firstVar === -1) return false;
76
+ if (value.indexOf("var(", firstVar + 4) !== -1) return true;
77
+ return !SINGLE_VAR_REF.test(value);
78
+ }
79
+ };
80
+
81
+ //#endregion
82
+ export { PropertyTypeResolver };
83
+ //# sourceMappingURL=property-type-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-type-resolver.js","names":[],"sources":["../../src/properties/property-type-resolver.ts"],"sourcesContent":["/**\n * PropertyTypeResolver\n *\n * Automatically infers CSS @property types from custom property values.\n * Supports deferred resolution for var() reference chains of arbitrary depth.\n */\n\nimport { inferSyntaxFromValue } from './index';\n\nconst CUSTOM_PROP_DECL = /^\\s*(--[a-z0-9_-]+)\\s*:\\s*(.+?)\\s*$/i;\nconst SINGLE_VAR_REF = /^var\\((--[a-z0-9_-]+)\\)$/i;\n\nexport class PropertyTypeResolver {\n /** propName → the prop it depends on */\n private pendingDeps = new Map<string, string>();\n /** propName → list of props waiting on it */\n private reverseDeps = new Map<string, string[]>();\n\n /**\n * Scan CSS declarations and auto-register @property for custom properties\n * whose types can be inferred from their values.\n */\n scanDeclarations(\n declarations: string,\n isPropertyDefined: (name: string) => boolean,\n registerProperty: (\n name: string,\n syntax: string,\n initialValue: string,\n ) => void,\n ): void {\n if (!declarations.includes('--')) return;\n\n const parts = declarations.split(/;+/);\n\n for (const part of parts) {\n if (!part.trim()) continue;\n\n const match = CUSTOM_PROP_DECL.exec(part);\n if (!match) continue;\n\n const propName = match[1];\n const value = match[2].trim();\n\n if (isPropertyDefined(propName)) continue;\n\n // Name-based: --*-color properties are always <color> (from #name tokens)\n if (propName.endsWith('-color')) {\n registerProperty(propName, '<color>', 'transparent');\n continue;\n }\n\n // Single var() reference → record dependency for deferred resolution\n const varMatch = SINGLE_VAR_REF.exec(value);\n if (varMatch) {\n const depName = varMatch[1];\n this.addDependency(propName, depName);\n continue;\n }\n\n // Skip complex expressions (calc, multiple var, etc.)\n if (this.isComplexValue(value)) continue;\n\n const inferred = inferSyntaxFromValue(value);\n if (!inferred) continue;\n\n this.resolve(\n propName,\n inferred.syntax,\n inferred.initialValue,\n isPropertyDefined,\n registerProperty,\n );\n }\n }\n\n private addDependency(propName: string, depName: string): void {\n if (propName === depName) return;\n\n this.pendingDeps.set(propName, depName);\n\n let dependents = this.reverseDeps.get(depName);\n if (!dependents) {\n dependents = [];\n this.reverseDeps.set(depName, dependents);\n }\n if (!dependents.includes(propName)) {\n dependents.push(propName);\n }\n }\n\n private resolve(\n propName: string,\n syntax: string,\n initialValue: string,\n isPropertyDefined: (name: string) => boolean,\n registerProperty: (\n name: string,\n syntax: string,\n initialValue: string,\n ) => void,\n resolving?: Set<string>,\n ): void {\n if (!resolving) resolving = new Set();\n if (resolving.has(propName)) return;\n resolving.add(propName);\n\n if (!isPropertyDefined(propName)) {\n registerProperty(propName, syntax, initialValue);\n }\n\n const dependents = this.reverseDeps.get(propName);\n if (dependents) {\n this.reverseDeps.delete(propName);\n\n for (const dependent of dependents) {\n this.pendingDeps.delete(dependent);\n\n if (isPropertyDefined(dependent)) continue;\n\n this.resolve(\n dependent,\n syntax,\n initialValue,\n isPropertyDefined,\n registerProperty,\n resolving,\n );\n }\n }\n }\n\n private isComplexValue(value: string): boolean {\n if (value.includes('calc(')) return true;\n const firstVar = value.indexOf('var(');\n if (firstVar === -1) return false;\n if (value.indexOf('var(', firstVar + 4) !== -1) return true;\n return !SINGLE_VAR_REF.test(value);\n }\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;AAEvB,IAAa,uBAAb,MAAkC;;CAEhC,AAAQ,8BAAc,IAAI,KAAqB;;CAE/C,AAAQ,8BAAc,IAAI,KAAuB;;;;;CAMjD,iBACE,cACA,mBACA,kBAKM;AACN,MAAI,CAAC,aAAa,SAAS,KAAK,CAAE;EAElC,MAAM,QAAQ,aAAa,MAAM,KAAK;AAEtC,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,CAAC,KAAK,MAAM,CAAE;GAElB,MAAM,QAAQ,iBAAiB,KAAK,KAAK;AACzC,OAAI,CAAC,MAAO;GAEZ,MAAM,WAAW,MAAM;GACvB,MAAM,QAAQ,MAAM,GAAG,MAAM;AAE7B,OAAI,kBAAkB,SAAS,CAAE;AAGjC,OAAI,SAAS,SAAS,SAAS,EAAE;AAC/B,qBAAiB,UAAU,WAAW,cAAc;AACpD;;GAIF,MAAM,WAAW,eAAe,KAAK,MAAM;AAC3C,OAAI,UAAU;IACZ,MAAM,UAAU,SAAS;AACzB,SAAK,cAAc,UAAU,QAAQ;AACrC;;AAIF,OAAI,KAAK,eAAe,MAAM,CAAE;GAEhC,MAAM,WAAW,qBAAqB,MAAM;AAC5C,OAAI,CAAC,SAAU;AAEf,QAAK,QACH,UACA,SAAS,QACT,SAAS,cACT,mBACA,iBACD;;;CAIL,AAAQ,cAAc,UAAkB,SAAuB;AAC7D,MAAI,aAAa,QAAS;AAE1B,OAAK,YAAY,IAAI,UAAU,QAAQ;EAEvC,IAAI,aAAa,KAAK,YAAY,IAAI,QAAQ;AAC9C,MAAI,CAAC,YAAY;AACf,gBAAa,EAAE;AACf,QAAK,YAAY,IAAI,SAAS,WAAW;;AAE3C,MAAI,CAAC,WAAW,SAAS,SAAS,CAChC,YAAW,KAAK,SAAS;;CAI7B,AAAQ,QACN,UACA,QACA,cACA,mBACA,kBAKA,WACM;AACN,MAAI,CAAC,UAAW,6BAAY,IAAI,KAAK;AACrC,MAAI,UAAU,IAAI,SAAS,CAAE;AAC7B,YAAU,IAAI,SAAS;AAEvB,MAAI,CAAC,kBAAkB,SAAS,CAC9B,kBAAiB,UAAU,QAAQ,aAAa;EAGlD,MAAM,aAAa,KAAK,YAAY,IAAI,SAAS;AACjD,MAAI,YAAY;AACd,QAAK,YAAY,OAAO,SAAS;AAEjC,QAAK,MAAM,aAAa,YAAY;AAClC,SAAK,YAAY,OAAO,UAAU;AAElC,QAAI,kBAAkB,UAAU,CAAE;AAElC,SAAK,QACH,WACA,QACA,cACA,mBACA,kBACA,UACD;;;;CAKP,AAAQ,eAAe,OAAwB;AAC7C,MAAI,MAAM,SAAS,QAAQ,CAAE,QAAO;EACpC,MAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,MAAI,aAAa,GAAI,QAAO;AAC5B,MAAI,MAAM,QAAQ,QAAQ,WAAW,EAAE,KAAK,GAAI,QAAO;AACvD,SAAO,CAAC,eAAe,KAAK,MAAM"}
@@ -10,13 +10,14 @@ function fillStyle({ fill, backgroundColor, image, backgroundImage, backgroundPo
10
10
  const firstColor = parsed.groups[0]?.colors[0];
11
11
  const secondColor = parsed.groups[0]?.colors[1];
12
12
  result["background-color"] = firstColor || colorValue;
13
- if (secondColor && !backgroundImage && !image) {
14
- result["--tasty-second-fill-color"] = secondColor;
15
- result["background-image"] = "linear-gradient(var(--tasty-second-fill-color), var(--tasty-second-fill-color))";
16
- }
13
+ if (secondColor) result["--tasty-second-fill-color"] = secondColor;
17
14
  }
15
+ const gradientLayer = result["--tasty-second-fill-color"] ? "linear-gradient(var(--tasty-second-fill-color), var(--tasty-second-fill-color))" : null;
18
16
  const imageValue = backgroundImage ?? image;
19
- if (imageValue) result["background-image"] = parseStyle(imageValue).output || imageValue;
17
+ if (imageValue) {
18
+ const imgCss = parseStyle(imageValue).output || imageValue;
19
+ result["background-image"] = gradientLayer ? `${imgCss}, ${gradientLayer}` : imgCss;
20
+ } else if (gradientLayer) result["background-image"] = gradientLayer;
20
21
  if (backgroundPosition) result["background-position"] = parseStyle(backgroundPosition).output || backgroundPosition;
21
22
  if (backgroundSize) result["background-size"] = parseStyle(backgroundSize).output || backgroundSize;
22
23
  if (backgroundRepeat) result["background-repeat"] = backgroundRepeat;
@@ -1 +1 @@
1
- {"version":3,"file":"fill.js","names":[],"sources":["../../src/styles/fill.ts"],"sourcesContent":["import { parseStyle } from '../utils/styles';\n\nexport function fillStyle({\n fill,\n backgroundColor,\n image,\n backgroundImage,\n backgroundPosition,\n backgroundSize,\n backgroundRepeat,\n backgroundAttachment,\n backgroundOrigin,\n backgroundClip,\n background,\n}: {\n fill?: string;\n backgroundColor?: string;\n image?: string;\n backgroundImage?: string;\n backgroundPosition?: string;\n backgroundSize?: string;\n backgroundRepeat?: string;\n backgroundAttachment?: string;\n backgroundOrigin?: string;\n backgroundClip?: string;\n background?: string;\n}) {\n // If background is set, it overrides everything\n if (background) {\n const processed = parseStyle(background);\n return { background: processed.output || background };\n }\n\n const result: Record<string, string> = {};\n\n // Priority: backgroundColor > fill\n const colorValue = backgroundColor ?? fill;\n if (colorValue) {\n const parsed = parseStyle(colorValue);\n const firstColor = parsed.groups[0]?.colors[0];\n const secondColor = parsed.groups[0]?.colors[1];\n\n result['background-color'] = firstColor || colorValue;\n\n // Apply second color as gradient layer (only if no explicit backgroundImage/image)\n // Uses a registered custom property to enable CSS transitions\n if (secondColor && !backgroundImage && !image) {\n result['--tasty-second-fill-color'] = secondColor;\n result['background-image'] =\n 'linear-gradient(var(--tasty-second-fill-color), var(--tasty-second-fill-color))';\n }\n }\n\n // Priority: backgroundImage > image (overrides second fill color if set)\n const imageValue = backgroundImage ?? image;\n if (imageValue) {\n const parsed = parseStyle(imageValue);\n result['background-image'] = parsed.output || imageValue;\n }\n\n // Other background properties (pass through with parseStyle for token support)\n if (backgroundPosition) {\n result['background-position'] =\n parseStyle(backgroundPosition).output || backgroundPosition;\n }\n if (backgroundSize) {\n result['background-size'] =\n parseStyle(backgroundSize).output || backgroundSize;\n }\n if (backgroundRepeat) {\n result['background-repeat'] = backgroundRepeat;\n }\n if (backgroundAttachment) {\n result['background-attachment'] = backgroundAttachment;\n }\n if (backgroundOrigin) {\n result['background-origin'] = backgroundOrigin;\n }\n if (backgroundClip) {\n result['background-clip'] = backgroundClip;\n }\n\n if (Object.keys(result).length === 0) return;\n return result;\n}\n\nfillStyle.__lookupStyles = [\n 'fill',\n 'backgroundColor',\n 'image',\n 'backgroundImage',\n 'backgroundPosition',\n 'backgroundSize',\n 'backgroundRepeat',\n 'backgroundAttachment',\n 'backgroundOrigin',\n 'backgroundClip',\n 'background',\n];\n\nexport function svgFillStyle({ svgFill }: { svgFill?: string }) {\n if (!svgFill) return;\n\n const processed = parseStyle(svgFill);\n svgFill = processed.groups[0]?.colors[0] || svgFill;\n\n return { fill: svgFill };\n}\n\nsvgFillStyle.__lookupStyles = ['svgFill'];\n"],"mappings":";;;AAEA,SAAgB,UAAU,EACxB,MACA,iBACA,OACA,iBACA,oBACA,gBACA,kBACA,sBACA,kBACA,gBACA,cAaC;AAED,KAAI,WAEF,QAAO,EAAE,YADS,WAAW,WAAW,CACT,UAAU,YAAY;CAGvD,MAAM,SAAiC,EAAE;CAGzC,MAAM,aAAa,mBAAmB;AACtC,KAAI,YAAY;EACd,MAAM,SAAS,WAAW,WAAW;EACrC,MAAM,aAAa,OAAO,OAAO,IAAI,OAAO;EAC5C,MAAM,cAAc,OAAO,OAAO,IAAI,OAAO;AAE7C,SAAO,sBAAsB,cAAc;AAI3C,MAAI,eAAe,CAAC,mBAAmB,CAAC,OAAO;AAC7C,UAAO,+BAA+B;AACtC,UAAO,sBACL;;;CAKN,MAAM,aAAa,mBAAmB;AACtC,KAAI,WAEF,QAAO,sBADQ,WAAW,WAAW,CACD,UAAU;AAIhD,KAAI,mBACF,QAAO,yBACL,WAAW,mBAAmB,CAAC,UAAU;AAE7C,KAAI,eACF,QAAO,qBACL,WAAW,eAAe,CAAC,UAAU;AAEzC,KAAI,iBACF,QAAO,uBAAuB;AAEhC,KAAI,qBACF,QAAO,2BAA2B;AAEpC,KAAI,iBACF,QAAO,uBAAuB;AAEhC,KAAI,eACF,QAAO,qBAAqB;AAG9B,KAAI,OAAO,KAAK,OAAO,CAAC,WAAW,EAAG;AACtC,QAAO;;AAGT,UAAU,iBAAiB;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,aAAa,EAAE,WAAiC;AAC9D,KAAI,CAAC,QAAS;AAGd,WADkB,WAAW,QAAQ,CACjB,OAAO,IAAI,OAAO,MAAM;AAE5C,QAAO,EAAE,MAAM,SAAS;;AAG1B,aAAa,iBAAiB,CAAC,UAAU"}
1
+ {"version":3,"file":"fill.js","names":[],"sources":["../../src/styles/fill.ts"],"sourcesContent":["import { parseStyle } from '../utils/styles';\n\nexport function fillStyle({\n fill,\n backgroundColor,\n image,\n backgroundImage,\n backgroundPosition,\n backgroundSize,\n backgroundRepeat,\n backgroundAttachment,\n backgroundOrigin,\n backgroundClip,\n background,\n}: {\n fill?: string;\n backgroundColor?: string;\n image?: string;\n backgroundImage?: string;\n backgroundPosition?: string;\n backgroundSize?: string;\n backgroundRepeat?: string;\n backgroundAttachment?: string;\n backgroundOrigin?: string;\n backgroundClip?: string;\n background?: string;\n}) {\n // If background is set, it overrides everything\n if (background) {\n const processed = parseStyle(background);\n return { background: processed.output || background };\n }\n\n const result: Record<string, string> = {};\n\n // Priority: backgroundColor > fill\n const colorValue = backgroundColor ?? fill;\n if (colorValue) {\n const parsed = parseStyle(colorValue);\n const firstColor = parsed.groups[0]?.colors[0];\n const secondColor = parsed.groups[0]?.colors[1];\n\n result['background-color'] = firstColor || colorValue;\n\n if (secondColor) {\n result['--tasty-second-fill-color'] = secondColor;\n }\n }\n\n const gradientLayer = result['--tasty-second-fill-color']\n ? 'linear-gradient(var(--tasty-second-fill-color), var(--tasty-second-fill-color))'\n : null;\n\n // Priority: backgroundImage > image\n const imageValue = backgroundImage ?? image;\n if (imageValue) {\n const parsed = parseStyle(imageValue);\n const imgCss = parsed.output || imageValue;\n\n result['background-image'] = gradientLayer\n ? `${imgCss}, ${gradientLayer}`\n : imgCss;\n } else if (gradientLayer) {\n result['background-image'] = gradientLayer;\n }\n\n // Other background properties (pass through with parseStyle for token support)\n if (backgroundPosition) {\n result['background-position'] =\n parseStyle(backgroundPosition).output || backgroundPosition;\n }\n if (backgroundSize) {\n result['background-size'] =\n parseStyle(backgroundSize).output || backgroundSize;\n }\n if (backgroundRepeat) {\n result['background-repeat'] = backgroundRepeat;\n }\n if (backgroundAttachment) {\n result['background-attachment'] = backgroundAttachment;\n }\n if (backgroundOrigin) {\n result['background-origin'] = backgroundOrigin;\n }\n if (backgroundClip) {\n result['background-clip'] = backgroundClip;\n }\n\n if (Object.keys(result).length === 0) return;\n return result;\n}\n\nfillStyle.__lookupStyles = [\n 'fill',\n 'backgroundColor',\n 'image',\n 'backgroundImage',\n 'backgroundPosition',\n 'backgroundSize',\n 'backgroundRepeat',\n 'backgroundAttachment',\n 'backgroundOrigin',\n 'backgroundClip',\n 'background',\n];\n\nexport function svgFillStyle({ svgFill }: { svgFill?: string }) {\n if (!svgFill) return;\n\n const processed = parseStyle(svgFill);\n svgFill = processed.groups[0]?.colors[0] || svgFill;\n\n return { fill: svgFill };\n}\n\nsvgFillStyle.__lookupStyles = ['svgFill'];\n"],"mappings":";;;AAEA,SAAgB,UAAU,EACxB,MACA,iBACA,OACA,iBACA,oBACA,gBACA,kBACA,sBACA,kBACA,gBACA,cAaC;AAED,KAAI,WAEF,QAAO,EAAE,YADS,WAAW,WAAW,CACT,UAAU,YAAY;CAGvD,MAAM,SAAiC,EAAE;CAGzC,MAAM,aAAa,mBAAmB;AACtC,KAAI,YAAY;EACd,MAAM,SAAS,WAAW,WAAW;EACrC,MAAM,aAAa,OAAO,OAAO,IAAI,OAAO;EAC5C,MAAM,cAAc,OAAO,OAAO,IAAI,OAAO;AAE7C,SAAO,sBAAsB,cAAc;AAE3C,MAAI,YACF,QAAO,+BAA+B;;CAI1C,MAAM,gBAAgB,OAAO,+BACzB,oFACA;CAGJ,MAAM,aAAa,mBAAmB;AACtC,KAAI,YAAY;EAEd,MAAM,SADS,WAAW,WAAW,CACf,UAAU;AAEhC,SAAO,sBAAsB,gBACzB,GAAG,OAAO,IAAI,kBACd;YACK,cACT,QAAO,sBAAsB;AAI/B,KAAI,mBACF,QAAO,yBACL,WAAW,mBAAmB,CAAC,UAAU;AAE7C,KAAI,eACF,QAAO,qBACL,WAAW,eAAe,CAAC,UAAU;AAEzC,KAAI,iBACF,QAAO,uBAAuB;AAEhC,KAAI,qBACF,QAAO,2BAA2B;AAEpC,KAAI,iBACF,QAAO,uBAAuB;AAEhC,KAAI,eACF,QAAO,qBAAqB;AAG9B,KAAI,OAAO,KAAK,OAAO,CAAC,WAAW,EAAG;AACtC,QAAO;;AAGT,UAAU,iBAAiB;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,aAAa,EAAE,WAAiC;AAC9D,KAAI,CAAC,QAAS;AAGd,WADkB,WAAW,QAAQ,CACjB,OAAO,IAAI,OAAO,MAAM;AAE5C,QAAO,EAAE,MAAM,SAAS;;AAG1B,aAAa,iBAAiB,CAAC,UAAU"}
@@ -200,12 +200,173 @@ function parseColor(val, ignoreError = false) {
200
200
  opacity
201
201
  };
202
202
  }
203
+ /**
204
+ * CSS named color keywords → hex values.
205
+ * Lazy-initialized on first use to avoid up-front cost.
206
+ */
207
+ let _namedColorHex = null;
208
+ function getNamedColorHex() {
209
+ if (_namedColorHex) return _namedColorHex;
210
+ _namedColorHex = new Map([
211
+ ["aliceblue", "#f0f8ff"],
212
+ ["antiquewhite", "#faebd7"],
213
+ ["aqua", "#00ffff"],
214
+ ["aquamarine", "#7fffd4"],
215
+ ["azure", "#f0ffff"],
216
+ ["beige", "#f5f5dc"],
217
+ ["bisque", "#ffe4c4"],
218
+ ["black", "#000000"],
219
+ ["blanchedalmond", "#ffebcd"],
220
+ ["blue", "#0000ff"],
221
+ ["blueviolet", "#8a2be2"],
222
+ ["brown", "#a52a2a"],
223
+ ["burlywood", "#deb887"],
224
+ ["cadetblue", "#5f9ea0"],
225
+ ["chartreuse", "#7fff00"],
226
+ ["chocolate", "#d2691e"],
227
+ ["coral", "#ff7f50"],
228
+ ["cornflowerblue", "#6495ed"],
229
+ ["cornsilk", "#fff8dc"],
230
+ ["crimson", "#dc143c"],
231
+ ["cyan", "#00ffff"],
232
+ ["darkblue", "#00008b"],
233
+ ["darkcyan", "#008b8b"],
234
+ ["darkgoldenrod", "#b8860b"],
235
+ ["darkgray", "#a9a9a9"],
236
+ ["darkgreen", "#006400"],
237
+ ["darkgrey", "#a9a9a9"],
238
+ ["darkkhaki", "#bdb76b"],
239
+ ["darkmagenta", "#8b008b"],
240
+ ["darkolivegreen", "#556b2f"],
241
+ ["darkorange", "#ff8c00"],
242
+ ["darkorchid", "#9932cc"],
243
+ ["darkred", "#8b0000"],
244
+ ["darksalmon", "#e9967a"],
245
+ ["darkseagreen", "#8fbc8f"],
246
+ ["darkslateblue", "#483d8b"],
247
+ ["darkslategray", "#2f4f4f"],
248
+ ["darkslategrey", "#2f4f4f"],
249
+ ["darkturquoise", "#00ced1"],
250
+ ["darkviolet", "#9400d3"],
251
+ ["deeppink", "#ff1493"],
252
+ ["deepskyblue", "#00bfff"],
253
+ ["dimgray", "#696969"],
254
+ ["dimgrey", "#696969"],
255
+ ["dodgerblue", "#1e90ff"],
256
+ ["firebrick", "#b22222"],
257
+ ["floralwhite", "#fffaf0"],
258
+ ["forestgreen", "#228b22"],
259
+ ["fuchsia", "#ff00ff"],
260
+ ["gainsboro", "#dcdcdc"],
261
+ ["ghostwhite", "#f8f8ff"],
262
+ ["gold", "#ffd700"],
263
+ ["goldenrod", "#daa520"],
264
+ ["gray", "#808080"],
265
+ ["green", "#008000"],
266
+ ["greenyellow", "#adff2f"],
267
+ ["grey", "#808080"],
268
+ ["honeydew", "#f0fff0"],
269
+ ["hotpink", "#ff69b4"],
270
+ ["indianred", "#cd5c5c"],
271
+ ["indigo", "#4b0082"],
272
+ ["ivory", "#fffff0"],
273
+ ["khaki", "#f0e68c"],
274
+ ["lavender", "#e6e6fa"],
275
+ ["lavenderblush", "#fff0f5"],
276
+ ["lawngreen", "#7cfc00"],
277
+ ["lemonchiffon", "#fffacd"],
278
+ ["lightblue", "#add8e6"],
279
+ ["lightcoral", "#f08080"],
280
+ ["lightcyan", "#e0ffff"],
281
+ ["lightgoldenrodyellow", "#fafad2"],
282
+ ["lightgray", "#d3d3d3"],
283
+ ["lightgreen", "#90ee90"],
284
+ ["lightgrey", "#d3d3d3"],
285
+ ["lightpink", "#ffb6c1"],
286
+ ["lightsalmon", "#ffa07a"],
287
+ ["lightseagreen", "#20b2aa"],
288
+ ["lightskyblue", "#87cefa"],
289
+ ["lightslategray", "#778899"],
290
+ ["lightslategrey", "#778899"],
291
+ ["lightsteelblue", "#b0c4de"],
292
+ ["lightyellow", "#ffffe0"],
293
+ ["lime", "#00ff00"],
294
+ ["limegreen", "#32cd32"],
295
+ ["linen", "#faf0e6"],
296
+ ["magenta", "#ff00ff"],
297
+ ["maroon", "#800000"],
298
+ ["mediumaquamarine", "#66cdaa"],
299
+ ["mediumblue", "#0000cd"],
300
+ ["mediumorchid", "#ba55d3"],
301
+ ["mediumpurple", "#9370db"],
302
+ ["mediumseagreen", "#3cb371"],
303
+ ["mediumslateblue", "#7b68ee"],
304
+ ["mediumspringgreen", "#00fa9a"],
305
+ ["mediumturquoise", "#48d1cc"],
306
+ ["mediumvioletred", "#c71585"],
307
+ ["midnightblue", "#191970"],
308
+ ["mintcream", "#f5fffa"],
309
+ ["mistyrose", "#ffe4e1"],
310
+ ["moccasin", "#ffe4b5"],
311
+ ["navajowhite", "#ffdead"],
312
+ ["navy", "#000080"],
313
+ ["oldlace", "#fdf5e6"],
314
+ ["olive", "#808000"],
315
+ ["olivedrab", "#6b8e23"],
316
+ ["orange", "#ffa500"],
317
+ ["orangered", "#ff4500"],
318
+ ["orchid", "#da70d6"],
319
+ ["palegoldenrod", "#eee8aa"],
320
+ ["palegreen", "#98fb98"],
321
+ ["paleturquoise", "#afeeee"],
322
+ ["palevioletred", "#db7093"],
323
+ ["papayawhip", "#ffefd5"],
324
+ ["peachpuff", "#ffdab9"],
325
+ ["peru", "#cd853f"],
326
+ ["pink", "#ffc0cb"],
327
+ ["plum", "#dda0dd"],
328
+ ["powderblue", "#b0e0e6"],
329
+ ["purple", "#800080"],
330
+ ["rebeccapurple", "#663399"],
331
+ ["red", "#ff0000"],
332
+ ["rosybrown", "#bc8f8f"],
333
+ ["royalblue", "#4169e1"],
334
+ ["saddlebrown", "#8b4513"],
335
+ ["salmon", "#fa8072"],
336
+ ["sandybrown", "#f4a460"],
337
+ ["seagreen", "#2e8b57"],
338
+ ["seashell", "#fff5ee"],
339
+ ["sienna", "#a0522d"],
340
+ ["silver", "#c0c0c0"],
341
+ ["skyblue", "#87ceeb"],
342
+ ["slateblue", "#6a5acd"],
343
+ ["slategray", "#708090"],
344
+ ["slategrey", "#708090"],
345
+ ["snow", "#fffafa"],
346
+ ["springgreen", "#00ff7f"],
347
+ ["steelblue", "#4682b4"],
348
+ ["tan", "#d2b48c"],
349
+ ["teal", "#008080"],
350
+ ["thistle", "#d8bfd8"],
351
+ ["tomato", "#ff6347"],
352
+ ["turquoise", "#40e0d0"],
353
+ ["violet", "#ee82ee"],
354
+ ["wheat", "#f5deb3"],
355
+ ["white", "#ffffff"],
356
+ ["whitesmoke", "#f5f5f5"],
357
+ ["yellow", "#ffff00"],
358
+ ["yellowgreen", "#9acd32"]
359
+ ]);
360
+ return _namedColorHex;
361
+ }
203
362
  function strToRgb(color, _ignoreAlpha = false) {
204
363
  if (!color) return void 0;
205
364
  if (color.startsWith("rgb")) return color;
206
365
  if (color.startsWith("#")) return hexToRgb(color);
207
366
  if (color.startsWith("okhsl(")) return okhslToRgb(color);
208
367
  if (color.startsWith("hsl")) return hslToRgb(color);
368
+ const namedHex = getNamedColorHex().get(color.toLowerCase());
369
+ if (namedHex) return hexToRgb(namedHex);
209
370
  return null;
210
371
  }
211
372
  /**