chaincss 2.1.38 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ROADMAP.md ADDED
@@ -0,0 +1,31 @@
1
+
2
+ ## v3.0 Ideas
3
+
4
+ ### 1. Predictive & Self-Healing CSS
5
+ - Context-aware typo correction (not just fuzzy matching)
6
+ - Intent detection: `display: "flexbox"` → maps to `display: flex` + applies defaults
7
+ - Modes: strict (error), dev (auto-fix), smart (fix + log)
8
+ - Builds on: suggestions.ts, explain()
9
+
10
+ ### 2. Style Graph Compiler
11
+ - Instead of linear compilation, build a dependency graph of styles
12
+ - Dead style elimination
13
+ - Automatic merging of identical rules
14
+ - Predictive pre-compilation
15
+ - Order-safe CSS output
16
+ - Builds on: compile(), atomic optimizer
17
+
18
+ ### 3. Unit-Aware Math Engine
19
+ - Full CSS expression evaluator
20
+ - `add("10px", "2rem")` → resolves units → optimized calc()
21
+ - `fluidType(14, 20)` → responsive clamp()
22
+ - Context-aware scaling
23
+ - Builds on: helpers.ts
24
+
25
+ ### 4. IDE Intelligence Layer (VS Code Extension)
26
+ - Real-time invalid property detection
27
+ - Shorthand translation hints
28
+ - Animation suggestion
29
+ - Breakpoint inference
30
+ - Style conflict detection
31
+ - Builds on: suggestions.ts
package/dist/cli/index.js CHANGED
@@ -2612,6 +2612,431 @@ var init_content_addressable_cache = __esm({
2612
2612
  }
2613
2613
  });
2614
2614
 
2615
+ // src/compiler/style-graph.ts
2616
+ import crypto4 from "crypto";
2617
+ function calculateSpecificity(selector) {
2618
+ let a = 0;
2619
+ let b = 0;
2620
+ let c = 0;
2621
+ const idMatches = selector.match(/#[a-zA-Z0-9_-]+/g);
2622
+ if (idMatches) a += idMatches.length;
2623
+ const classMatches = selector.match(/\.[a-zA-Z0-9_-]+/g);
2624
+ if (classMatches) b += classMatches.length;
2625
+ const attrMatches = selector.match(/\[[^\]]+\]/g);
2626
+ if (attrMatches) b += attrMatches.length;
2627
+ const pseudoClassMatches = selector.match(/:[a-zA-Z-]+(?:\([^)]*\))?/g);
2628
+ if (pseudoClassMatches) {
2629
+ const notMatches = selector.match(/:not\(([^)]+)\)/g);
2630
+ const regularPseudoClasses = pseudoClassMatches.length - (notMatches?.length || 0);
2631
+ b += Math.max(0, regularPseudoClasses);
2632
+ }
2633
+ const elementMatches = selector.match(/^[a-zA-Z]+|[a-zA-Z]+(?=[.#[:])/g);
2634
+ if (elementMatches) c += elementMatches.length;
2635
+ return a * 1e4 + b * 100 + c;
2636
+ }
2637
+ function hashProperties(properties) {
2638
+ const sorted = Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${v}`).join(";");
2639
+ return crypto4.createHash("md5").update(sorted).digest("hex").slice(0, 8);
2640
+ }
2641
+ function kebab2(prop) {
2642
+ return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
2643
+ }
2644
+ function eliminateDeadStyles(graph, knownSelectors) {
2645
+ if (knownSelectors.length === 0) {
2646
+ return { eliminated: 0, graph };
2647
+ }
2648
+ const reachable = /* @__PURE__ */ new Set();
2649
+ const queue = [];
2650
+ for (const [id, node] of graph.nodes) {
2651
+ if (knownSelectors.some((ks) => node.selector.includes(ks) || ks.includes(node.selector))) {
2652
+ reachable.add(id);
2653
+ queue.push(id);
2654
+ }
2655
+ }
2656
+ while (queue.length > 0) {
2657
+ const current = queue.shift();
2658
+ const node = graph.nodes.get(current);
2659
+ if (!node) continue;
2660
+ for (const depId of node.dependents) {
2661
+ if (!reachable.has(depId)) {
2662
+ reachable.add(depId);
2663
+ queue.push(depId);
2664
+ }
2665
+ }
2666
+ }
2667
+ let eliminated = 0;
2668
+ for (const [id, node] of graph.nodes) {
2669
+ if (!reachable.has(id)) {
2670
+ node.isDead = true;
2671
+ eliminated++;
2672
+ }
2673
+ }
2674
+ return { eliminated, graph };
2675
+ }
2676
+ function mergeIdenticalRules(graph, threshold) {
2677
+ const hashGroups = /* @__PURE__ */ new Map();
2678
+ for (const [, node] of graph.nodes) {
2679
+ if (node.isDead) continue;
2680
+ if (Object.keys(node.properties).length < threshold) continue;
2681
+ const existing = hashGroups.get(node.hash) || [];
2682
+ existing.push(node);
2683
+ hashGroups.set(node.hash, existing);
2684
+ }
2685
+ let merged = 0;
2686
+ for (const [, group] of hashGroups) {
2687
+ if (group.length < 2) continue;
2688
+ const mergedSelector = group.map((n) => n.selector).join(", ");
2689
+ const primary = group[0];
2690
+ primary.selector = mergedSelector;
2691
+ for (let i = 1; i < group.length; i++) {
2692
+ group[i].isDead = true;
2693
+ merged++;
2694
+ }
2695
+ }
2696
+ return { merged, graph };
2697
+ }
2698
+ function topologicalSort(graph) {
2699
+ const visited = /* @__PURE__ */ new Set();
2700
+ const sorted = [];
2701
+ const visiting = /* @__PURE__ */ new Set();
2702
+ function visit(id) {
2703
+ if (visited.has(id)) return true;
2704
+ if (visiting.has(id)) return false;
2705
+ visiting.add(id);
2706
+ const node = graph.nodes.get(id);
2707
+ if (node) {
2708
+ for (const depId of node.dependencies) {
2709
+ if (!visit(depId)) return false;
2710
+ }
2711
+ }
2712
+ visiting.delete(id);
2713
+ visited.add(id);
2714
+ if (node && !node.isDead) {
2715
+ sorted.push(node);
2716
+ }
2717
+ return true;
2718
+ }
2719
+ for (const id of graph.rootNodes) {
2720
+ if (!visit(id)) {
2721
+ return Array.from(graph.nodes.values()).filter((n) => !n.isDead).sort((a, b) => a.sourceComponent?.localeCompare(b.sourceComponent || "") || 0);
2722
+ }
2723
+ }
2724
+ for (const [id] of graph.nodes) {
2725
+ if (!visited.has(id)) {
2726
+ visit(id);
2727
+ }
2728
+ }
2729
+ return sorted;
2730
+ }
2731
+ function generateCSSFromGraph(graph, sortOutput = "specificity") {
2732
+ let nodes;
2733
+ switch (sortOutput) {
2734
+ case "specificity":
2735
+ nodes = Array.from(graph.nodes.values()).filter((n) => !n.isDead).sort((a, b) => a.specificity - b.specificity);
2736
+ break;
2737
+ case "topological":
2738
+ nodes = topologicalSort(graph);
2739
+ break;
2740
+ case "source-order":
2741
+ default:
2742
+ nodes = Array.from(graph.nodes.values()).filter((n) => !n.isDead);
2743
+ break;
2744
+ }
2745
+ let css = "";
2746
+ let currentMediaQuery;
2747
+ for (const node of nodes) {
2748
+ if (node.isDead) continue;
2749
+ if (node.mediaQuery !== currentMediaQuery) {
2750
+ if (currentMediaQuery) {
2751
+ css += "}\n\n";
2752
+ }
2753
+ if (node.mediaQuery) {
2754
+ css += `@media ${node.mediaQuery} {
2755
+ `;
2756
+ }
2757
+ currentMediaQuery = node.mediaQuery;
2758
+ }
2759
+ const rules = Object.entries(node.properties).map(([prop, value]) => ` ${kebab2(prop)}: ${value};`).join("\n");
2760
+ if (rules) {
2761
+ css += `${node.selector} {
2762
+ ${rules}
2763
+ }
2764
+ `;
2765
+ }
2766
+ }
2767
+ if (currentMediaQuery) {
2768
+ css += "}\n";
2769
+ }
2770
+ return css;
2771
+ }
2772
+ var StyleGraphBuilder, StyleGraphCompiler;
2773
+ var init_style_graph = __esm({
2774
+ "src/compiler/style-graph.ts"() {
2775
+ "use strict";
2776
+ StyleGraphBuilder = class {
2777
+ entries = [];
2778
+ nodes = /* @__PURE__ */ new Map();
2779
+ edges = [];
2780
+ orderCounter = 0;
2781
+ addEntry(entry) {
2782
+ entry.sourceOrder = this.orderCounter++;
2783
+ this.entries.push(entry);
2784
+ }
2785
+ build() {
2786
+ this.nodes.clear();
2787
+ this.edges = [];
2788
+ for (const entry of this.entries) {
2789
+ const id = `node-${this.nodes.size}`;
2790
+ const node = {
2791
+ id,
2792
+ selector: entry.selector,
2793
+ properties: entry.properties,
2794
+ specificity: calculateSpecificity(entry.selector),
2795
+ dependencies: [],
2796
+ dependents: [],
2797
+ mediaQuery: entry.mediaQuery,
2798
+ isDead: false,
2799
+ hash: hashProperties(entry.properties),
2800
+ sourceComponent: entry.sourceComponent
2801
+ };
2802
+ this.nodes.set(id, node);
2803
+ }
2804
+ const nodeArray = Array.from(this.nodes.values());
2805
+ for (let i = 0; i < nodeArray.length; i++) {
2806
+ for (let j = i + 1; j < nodeArray.length; j++) {
2807
+ const a = nodeArray[i];
2808
+ const b = nodeArray[j];
2809
+ if (this.selectorsOverlap(a.selector, b.selector)) {
2810
+ if (a.specificity <= b.specificity) {
2811
+ this.edges.push({ from: a.id, to: b.id, type: "overrides" });
2812
+ a.dependents.push(b.id);
2813
+ b.dependencies.push(a.id);
2814
+ }
2815
+ if (b.specificity <= a.specificity) {
2816
+ this.edges.push({ from: b.id, to: a.id, type: "overrides" });
2817
+ b.dependents.push(a.id);
2818
+ a.dependencies.push(b.id);
2819
+ }
2820
+ }
2821
+ }
2822
+ }
2823
+ const rootNodes = nodeArray.filter((n) => n.dependencies.length === 0).map((n) => n.id);
2824
+ const leafNodes = nodeArray.filter((n) => n.dependents.length === 0).map((n) => n.id);
2825
+ return {
2826
+ nodes: this.nodes,
2827
+ edges: this.edges,
2828
+ rootNodes,
2829
+ leafNodes
2830
+ };
2831
+ }
2832
+ selectorsOverlap(a, b) {
2833
+ const partsA = a.split(/[\s>+~]+/).filter(Boolean);
2834
+ const partsB = b.split(/[\s>+~]+/).filter(Boolean);
2835
+ for (const pa of partsA) {
2836
+ for (const pb of partsB) {
2837
+ if (pa === pb) return true;
2838
+ if (pa.startsWith(".") && pb.startsWith(".") && pa === pb) return true;
2839
+ }
2840
+ }
2841
+ return false;
2842
+ }
2843
+ };
2844
+ StyleGraphCompiler = class {
2845
+ options;
2846
+ constructor(options = {}) {
2847
+ this.options = {
2848
+ eliminateDead: options.eliminateDead ?? false,
2849
+ knownSelectors: options.knownSelectors ?? [],
2850
+ mergeIdentical: options.mergeIdentical ?? false,
2851
+ mergeThreshold: options.mergeThreshold ?? 3,
2852
+ sortOutput: options.sortOutput ?? "specificity",
2853
+ verbose: options.verbose ?? false
2854
+ };
2855
+ }
2856
+ /**
2857
+ * Compile a set of style definitions through the graph compiler.
2858
+ */
2859
+ compile(styles) {
2860
+ const startTime = Date.now();
2861
+ const builder = new StyleGraphBuilder();
2862
+ let preOptimizationSize = 0;
2863
+ for (const [componentName, styleDef] of Object.entries(styles)) {
2864
+ if (!styleDef || !styleDef.selectors) continue;
2865
+ for (const selector of styleDef.selectors) {
2866
+ const properties = {};
2867
+ for (const [prop, value] of Object.entries(styleDef)) {
2868
+ if (prop === "selectors" || prop === "atRules" || prop === "nestedRules" || prop === "hover" || prop === "themes" || prop.startsWith("_")) {
2869
+ continue;
2870
+ }
2871
+ if (typeof value === "string" || typeof value === "number") {
2872
+ properties[prop] = String(value);
2873
+ preOptimizationSize += String(value).length + prop.length;
2874
+ }
2875
+ }
2876
+ if (Object.keys(properties).length > 0) {
2877
+ builder.addEntry({
2878
+ selector,
2879
+ properties,
2880
+ sourceComponent: componentName,
2881
+ sourceOrder: 0
2882
+ });
2883
+ }
2884
+ if (styleDef.hover && typeof styleDef.hover === "object") {
2885
+ const hoverProperties = {};
2886
+ for (const [prop, value] of Object.entries(styleDef.hover)) {
2887
+ if (typeof value === "string" || typeof value === "number") {
2888
+ hoverProperties[prop] = String(value);
2889
+ }
2890
+ }
2891
+ if (Object.keys(hoverProperties).length > 0) {
2892
+ builder.addEntry({
2893
+ selector: `${selector}:hover`,
2894
+ properties: hoverProperties,
2895
+ sourceComponent: componentName,
2896
+ sourceOrder: 0
2897
+ });
2898
+ }
2899
+ }
2900
+ if (styleDef.atRules) {
2901
+ for (const rule of styleDef.atRules) {
2902
+ if (rule.type === "media" && rule.styles && rule.query) {
2903
+ const mediaProperties = {};
2904
+ for (const [prop, value] of Object.entries(rule.styles)) {
2905
+ if (typeof value === "string" || typeof value === "number") {
2906
+ mediaProperties[prop] = String(value);
2907
+ }
2908
+ }
2909
+ if (Object.keys(mediaProperties).length > 0) {
2910
+ builder.addEntry({
2911
+ selector,
2912
+ properties: mediaProperties,
2913
+ sourceComponent: componentName,
2914
+ sourceOrder: 0,
2915
+ mediaQuery: rule.query
2916
+ });
2917
+ }
2918
+ }
2919
+ }
2920
+ }
2921
+ }
2922
+ }
2923
+ let graph = builder.build();
2924
+ let eliminatedDead = 0;
2925
+ if (this.options.eliminateDead && this.options.knownSelectors.length > 0) {
2926
+ const result = eliminateDeadStyles(graph, this.options.knownSelectors);
2927
+ eliminatedDead = result.eliminated;
2928
+ graph = result.graph;
2929
+ }
2930
+ let mergedRules = 0;
2931
+ if (this.options.mergeIdentical) {
2932
+ const result = mergeIdenticalRules(graph, this.options.mergeThreshold);
2933
+ mergedRules = result.merged;
2934
+ graph = result.graph;
2935
+ }
2936
+ const css = generateCSSFromGraph(graph, this.options.sortOutput);
2937
+ let postOptimizationSize = css.length;
2938
+ if (postOptimizationSize === 0) {
2939
+ postOptimizationSize = preOptimizationSize;
2940
+ }
2941
+ const classMap = {};
2942
+ for (const [, node] of graph.nodes) {
2943
+ if (!node.isDead && node.sourceComponent) {
2944
+ if (classMap[node.sourceComponent]) {
2945
+ classMap[node.sourceComponent] += ` ${node.selector.replace(/^\./, "")}`;
2946
+ } else {
2947
+ classMap[node.sourceComponent] = node.selector.replace(/^\./, "");
2948
+ }
2949
+ }
2950
+ }
2951
+ const totalNodes = graph.nodes.size;
2952
+ const aliveNodes = totalNodes - eliminatedDead;
2953
+ const savingsPercent = preOptimizationSize > 0 ? `${((preOptimizationSize - postOptimizationSize) / preOptimizationSize * 100).toFixed(1)}%` : "0%";
2954
+ const stats = {
2955
+ totalStyles: totalNodes,
2956
+ atomicStyles: 0,
2957
+ uniqueProperties: new Set(
2958
+ Array.from(graph.nodes.values()).filter((n) => !n.isDead).flatMap((n) => Object.keys(n.properties))
2959
+ ).size,
2960
+ savings: savingsPercent,
2961
+ compileTime: Date.now() - startTime
2962
+ };
2963
+ return {
2964
+ css,
2965
+ classMap,
2966
+ atomicClasses: [],
2967
+ stats,
2968
+ graph,
2969
+ eliminatedDead,
2970
+ mergedRules,
2971
+ optimizationTime: Date.now() - startTime,
2972
+ preOptimizationSize,
2973
+ postOptimizationSize
2974
+ };
2975
+ }
2976
+ /**
2977
+ * Analyze a style graph without generating CSS.
2978
+ */
2979
+ analyze(styles) {
2980
+ const builder = new StyleGraphBuilder();
2981
+ for (const [componentName, styleDef] of Object.entries(styles)) {
2982
+ if (!styleDef || !styleDef.selectors) continue;
2983
+ for (const selector of styleDef.selectors) {
2984
+ const properties = {};
2985
+ for (const [prop, value] of Object.entries(styleDef)) {
2986
+ if (prop === "selectors" || prop.startsWith("_")) continue;
2987
+ if (typeof value === "string" || typeof value === "number") {
2988
+ properties[prop] = String(value);
2989
+ }
2990
+ }
2991
+ if (Object.keys(properties).length > 0) {
2992
+ builder.addEntry({ selector, properties, sourceComponent: componentName, sourceOrder: 0 });
2993
+ }
2994
+ }
2995
+ }
2996
+ return builder.build();
2997
+ }
2998
+ /**
2999
+ * Get optimization statistics for a graph.
3000
+ */
3001
+ getStats(graph) {
3002
+ const nodes = Array.from(graph.nodes.values());
3003
+ const deadNodes = nodes.filter((n) => n.isDead).length;
3004
+ const averageSpecificity = nodes.length > 0 ? nodes.reduce((sum, n) => sum + n.specificity, 0) / nodes.length : 0;
3005
+ let maxDepth = 0;
3006
+ const depths = /* @__PURE__ */ new Map();
3007
+ function getDepth(id) {
3008
+ if (depths.has(id)) return depths.get(id);
3009
+ const node = graph.nodes.get(id);
3010
+ if (!node || node.dependencies.length === 0) {
3011
+ depths.set(id, 0);
3012
+ return 0;
3013
+ }
3014
+ const max = Math.max(...node.dependencies.map((d) => getDepth(d)));
3015
+ const depth = max + 1;
3016
+ depths.set(id, depth);
3017
+ return depth;
3018
+ }
3019
+ for (const [id] of graph.nodes) {
3020
+ maxDepth = Math.max(maxDepth, getDepth(id));
3021
+ }
3022
+ return {
3023
+ totalNodes: nodes.length,
3024
+ deadNodes,
3025
+ mergedGroups: 0,
3026
+ averageSpecificity: Math.round(averageSpecificity * 100) / 100,
3027
+ deepestDependencyChain: maxDepth
3028
+ };
3029
+ }
3030
+ /**
3031
+ * Update options.
3032
+ */
3033
+ configure(options) {
3034
+ this.options = { ...this.options, ...options };
3035
+ }
3036
+ };
3037
+ }
3038
+ });
3039
+
2615
3040
  // src/core/compiler.ts
2616
3041
  var compiler_exports = {};
2617
3042
  __export(compiler_exports, {
@@ -2620,7 +3045,7 @@ __export(compiler_exports, {
2620
3045
  });
2621
3046
  import fs7 from "fs";
2622
3047
  import path6 from "path";
2623
- import crypto4 from "crypto";
3048
+ import crypto5 from "crypto";
2624
3049
  import chalk2 from "chalk";
2625
3050
  import { fileURLToPath, pathToFileURL } from "url";
2626
3051
  async function compileChainCSS(inputFile, outputDir, config) {
@@ -2639,6 +3064,7 @@ var init_compiler = __esm({
2639
3064
  init_cache_manager();
2640
3065
  init_content_addressable_cache();
2641
3066
  init_shorthands();
3067
+ init_style_graph();
2642
3068
  __filename = typeof import.meta !== "undefined" ? (() => {
2643
3069
  try {
2644
3070
  return fileURLToPath(import.meta.url);
@@ -2689,6 +3115,35 @@ var init_compiler = __esm({
2689
3115
  this.initOptimizer();
2690
3116
  this.initPrefixer();
2691
3117
  }
3118
+ /**
3119
+ * Compile using the style graph compiler for advanced optimizations.
3120
+ *
3121
+ * @example
3122
+ * const result = compiler.compileWithGraph(styles, {
3123
+ * eliminateDead: true,
3124
+ * knownSelectors: ['.header', '.footer'],
3125
+ * mergeIdentical: true
3126
+ * });
3127
+ */
3128
+ compileWithGraph(styles, options) {
3129
+ const graphCompiler = new StyleGraphCompiler({
3130
+ ...options,
3131
+ verbose: this.config.verbose
3132
+ });
3133
+ const result = graphCompiler.compile(styles);
3134
+ if (this.config.verbose) {
3135
+ if (result.eliminatedDead > 0) {
3136
+ console.log(` \u{1F9F9} Eliminated ${result.eliminatedDead} dead styles`);
3137
+ }
3138
+ if (result.mergedRules > 0) {
3139
+ console.log(` \u{1F517} Merged ${result.mergedRules} identical rules`);
3140
+ }
3141
+ if (result.optimizationTime > 0) {
3142
+ console.log(` \u26A1 Graph compilation: ${result.optimizationTime}ms`);
3143
+ }
3144
+ }
3145
+ return result;
3146
+ }
2692
3147
  hasStyles() {
2693
3148
  const combined = this.getCombinedCSS();
2694
3149
  return !!(combined && combined.trim().length > 0);
@@ -2726,7 +3181,7 @@ var init_compiler = __esm({
2726
3181
  if (result.css && result.css.trim()) {
2727
3182
  this.accumulatedCSS += result.css + "\n";
2728
3183
  }
2729
- const cacheKey = crypto4.createHash("sha256").update(`${componentName}-${JSON.stringify(styleObj)}`).digest("hex").slice(0, 16);
3184
+ const cacheKey = crypto5.createHash("sha256").update(`${componentName}-${JSON.stringify(styleObj)}`).digest("hex").slice(0, 16);
2730
3185
  this.addToCache(cacheKey, {
2731
3186
  result: {
2732
3187
  css: result.css || "",
@@ -2854,7 +3309,7 @@ var init_compiler = __esm({
2854
3309
  // ============================================================================
2855
3310
  hashStyleDef(styleDef) {
2856
3311
  const { _componentName, _generateComponent, _framework, _propsDefinition, ...relevant } = styleDef;
2857
- return crypto4.createHash("sha256").update(JSON.stringify(relevant)).digest("hex").slice(0, 16);
3312
+ return crypto5.createHash("sha256").update(JSON.stringify(relevant)).digest("hex").slice(0, 16);
2858
3313
  }
2859
3314
  async importModule(filePath) {
2860
3315
  const absolutePath = path6.resolve(filePath);
@@ -0,0 +1,12 @@
1
+ import type { StyleDefinition, StyleDiagnostic, StyleAnalysis, BreakpointInference, DiagnosticSeverity } from '../core/types.js';
2
+ export type { StyleDiagnostic, StyleAnalysis, BreakpointInference, DiagnosticSeverity };
3
+ export declare class StyleAnalyzer {
4
+ private _diagnostics;
5
+ analyzeStyle(selector: string, styles: Record<string, any>, _opts?: any): StyleDiagnostic[];
6
+ analyze(sd: StyleDefinition): StyleAnalysis;
7
+ reset(): void;
8
+ getDiagnostics(): StyleDiagnostic[];
9
+ }
10
+ export declare function analyze(sd: StyleDefinition): StyleAnalysis;
11
+ export declare function analyzeStyle(sd: StyleDefinition): StyleAnalysis;
12
+ export default StyleAnalyzer;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * CSS if() Transpiler
3
+ * Detects conditional style patterns and emits:
4
+ * 1. Native CSS if() — Chrome 137+
5
+ * 2. @supports fallback — Firefox, Safari
6
+ */
7
+ export interface IfCondition {
8
+ property: string;
9
+ variable: string;
10
+ conditions: Record<string, string | number>;
11
+ defaultValue: string | number;
12
+ }
13
+ export interface DetectedCondition {
14
+ property: string;
15
+ variable: string;
16
+ conditions: Record<string, string | number>;
17
+ defaultValue: string | number;
18
+ }
19
+ /**
20
+ * Detect conditional patterns from _conditions metadata.
21
+ * When chain.when() branches set the same property to different values,
22
+ * those can be compiled to CSS if().
23
+ */
24
+ export declare function detectIfPatterns(styles: Record<string, any>): DetectedCondition[];
25
+ /**
26
+ * Generate CSS if() output for detected conditions.
27
+ */
28
+ export declare function emitCSSIf(selector: string, detectedConditions: DetectedCondition[], baseProperties?: Record<string, string | number>): string;
29
+ declare const _default: {
30
+ detectIfPatterns: typeof detectIfPatterns;
31
+ emitCSSIf: typeof emitCSSIf;
32
+ };
33
+ export default _default;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Design System Orchestrator
3
+ *
4
+ * 1. WCAG Contrast Ratio Checker — validates text/background combos at build time
5
+ * 2. Contextual Tokens — tokens that auto-flip based on container context
6
+ * 3. Token Relationship Validator — ensures design tokens are consistent
7
+ */
8
+ export interface ContrastResult {
9
+ foreground: string;
10
+ background: string;
11
+ ratio: number;
12
+ passes: {
13
+ AA: boolean;
14
+ AALarge: boolean;
15
+ AAA: boolean;
16
+ AAALarge: boolean;
17
+ };
18
+ suggestion?: string;
19
+ }
20
+ export interface ContrastReport {
21
+ checks: ContrastResult[];
22
+ failures: ContrastResult[];
23
+ warnings: ContrastResult[];
24
+ passCount: number;
25
+ failCount: number;
26
+ summary: string;
27
+ }
28
+ export interface ContextualToken {
29
+ name: string;
30
+ default: string;
31
+ contexts: Record<string, string>;
32
+ }
33
+ export interface TokenContext {
34
+ name: string;
35
+ parentSelector?: string;
36
+ tokens: Record<string, any>;
37
+ }
38
+ /**
39
+ * Parse CSS color to RGBA components.
40
+ * Supports: hex, rgb(), rgba(), named colors
41
+ */
42
+ declare function parseColor(color: string): {
43
+ r: number;
44
+ g: number;
45
+ b: number;
46
+ a: number;
47
+ } | null;
48
+ /**
49
+ * Calculate WCAG contrast ratio between two colors.
50
+ * Returns value between 1 (no contrast) and 21 (max contrast).
51
+ */
52
+ export declare function contrastRatio(foreground: string, background: string): number;
53
+ /**
54
+ * Check WCAG compliance levels.
55
+ * AA: 4.5:1 normal, 3:1 large text
56
+ * AAA: 7:1 normal, 4.5:1 large text
57
+ */
58
+ export declare function checkContrast(foreground: string, background: string): ContrastResult;
59
+ /**
60
+ * Run contrast checks across a set of style definitions.
61
+ */
62
+ export declare function auditContrast(styles: Array<{
63
+ selector: string;
64
+ color: string;
65
+ backgroundColor: string;
66
+ }>): ContrastReport;
67
+ /**
68
+ * Contextual tokens that auto-resolve based on parent container.
69
+ *
70
+ * @example
71
+ * const buttonText = contextualToken({
72
+ * default: '#1a1a1a',
73
+ * contexts: {
74
+ * '.dark-section': '#ffffff',
75
+ * '.hero': '#ffffff',
76
+ * },
77
+ * });
78
+ *
79
+ * resolveContextual(buttonText, '.dark-section .my-button')
80
+ * // => '#ffffff'
81
+ */
82
+ export declare function createContextualToken(defaultValue: string, contexts?: Record<string, string>): ContextualToken;
83
+ /**
84
+ * Resolve a contextual token based on the current selector path.
85
+ * Matches the most specific context that applies.
86
+ */
87
+ export declare function resolveContextual(token: ContextualToken, selectorPath: string): string;
88
+ /**
89
+ * Generate CSS custom property fallback for contextual tokens.
90
+ *
91
+ * @example
92
+ * generateContextualCSS('button-text', contextualToken)
93
+ * // => "
94
+ * // .my-button { --button-text: #1a1a1a; }
95
+ * // .dark-section .my-button { --button-text: #ffffff; }
96
+ * // "
97
+ */
98
+ export declare function generateContextualCSS(propertyName: string, token: ContextualToken, baseSelector: string): string;
99
+ /**
100
+ * Validate that token references are consistent.
101
+ * E.g., "primary" should have both foreground and background variants
102
+ * that contrast well with each other.
103
+ */
104
+ export declare function validateTokenRelationships(tokens: Record<string, any>, pairs: Array<{
105
+ foreground: string;
106
+ background: string;
107
+ label: string;
108
+ }>): ContrastReport;
109
+ export declare const orchestrator: {
110
+ contrastRatio: typeof contrastRatio;
111
+ checkContrast: typeof checkContrast;
112
+ auditContrast: typeof auditContrast;
113
+ createContextualToken: typeof createContextualToken;
114
+ resolveContextual: typeof resolveContextual;
115
+ generateContextualCSS: typeof generateContextualCSS;
116
+ validateTokenRelationships: typeof validateTokenRelationships;
117
+ parseColor: typeof parseColor;
118
+ };
119
+ export default orchestrator;