@design-guard/core 0.3.1

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 (133) hide show
  1. package/dist/index.d.ts +20 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +18 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/research/business-researcher.d.ts +42 -0
  6. package/dist/research/business-researcher.d.ts.map +1 -0
  7. package/dist/research/business-researcher.js +888 -0
  8. package/dist/research/business-researcher.js.map +1 -0
  9. package/dist/research/design-synthesizer.d.ts +47 -0
  10. package/dist/research/design-synthesizer.d.ts.map +1 -0
  11. package/dist/research/design-synthesizer.js +628 -0
  12. package/dist/research/design-synthesizer.js.map +1 -0
  13. package/dist/research/research-cache.d.ts +7 -0
  14. package/dist/research/research-cache.d.ts.map +1 -0
  15. package/dist/research/research-cache.js +62 -0
  16. package/dist/research/research-cache.js.map +1 -0
  17. package/dist/research/types.d.ts +99 -0
  18. package/dist/research/types.d.ts.map +1 -0
  19. package/dist/research/types.js +6 -0
  20. package/dist/research/types.js.map +1 -0
  21. package/dist/templates/design-md.d.ts +53 -0
  22. package/dist/templates/design-md.d.ts.map +1 -0
  23. package/dist/templates/design-md.js +315 -0
  24. package/dist/templates/design-md.js.map +1 -0
  25. package/dist/templates/prompts.d.ts +32 -0
  26. package/dist/templates/prompts.d.ts.map +1 -0
  27. package/dist/templates/prompts.js +39 -0
  28. package/dist/templates/prompts.js.map +1 -0
  29. package/dist/tokens/dtcg-converter.d.ts +62 -0
  30. package/dist/tokens/dtcg-converter.d.ts.map +1 -0
  31. package/dist/tokens/dtcg-converter.js +385 -0
  32. package/dist/tokens/dtcg-converter.js.map +1 -0
  33. package/dist/utils/prompt-enhancer.d.ts +22 -0
  34. package/dist/utils/prompt-enhancer.d.ts.map +1 -0
  35. package/dist/utils/prompt-enhancer.js +104 -0
  36. package/dist/utils/prompt-enhancer.js.map +1 -0
  37. package/dist/utils/validators.d.ts +126 -0
  38. package/dist/utils/validators.d.ts.map +1 -0
  39. package/dist/utils/validators.js +110 -0
  40. package/dist/utils/validators.js.map +1 -0
  41. package/dist/validation/design-validator.d.ts +45 -0
  42. package/dist/validation/design-validator.d.ts.map +1 -0
  43. package/dist/validation/design-validator.js +396 -0
  44. package/dist/validation/design-validator.js.map +1 -0
  45. package/dist/validation/output-validator.d.ts +46 -0
  46. package/dist/validation/output-validator.d.ts.map +1 -0
  47. package/dist/validation/output-validator.js +146 -0
  48. package/dist/validation/output-validator.js.map +1 -0
  49. package/dist/validation/rules/alt-text.d.ts +8 -0
  50. package/dist/validation/rules/alt-text.d.ts.map +1 -0
  51. package/dist/validation/rules/alt-text.js +35 -0
  52. package/dist/validation/rules/alt-text.js.map +1 -0
  53. package/dist/validation/rules/business-alignment.d.ts +7 -0
  54. package/dist/validation/rules/business-alignment.d.ts.map +1 -0
  55. package/dist/validation/rules/business-alignment.js +98 -0
  56. package/dist/validation/rules/business-alignment.js.map +1 -0
  57. package/dist/validation/rules/color-adherence.d.ts +8 -0
  58. package/dist/validation/rules/color-adherence.d.ts.map +1 -0
  59. package/dist/validation/rules/color-adherence.js +92 -0
  60. package/dist/validation/rules/color-adherence.js.map +1 -0
  61. package/dist/validation/rules/empty-body.d.ts +6 -0
  62. package/dist/validation/rules/empty-body.d.ts.map +1 -0
  63. package/dist/validation/rules/empty-body.js +24 -0
  64. package/dist/validation/rules/empty-body.js.map +1 -0
  65. package/dist/validation/rules/heading-hierarchy.d.ts +8 -0
  66. package/dist/validation/rules/heading-hierarchy.d.ts.map +1 -0
  67. package/dist/validation/rules/heading-hierarchy.js +48 -0
  68. package/dist/validation/rules/heading-hierarchy.js.map +1 -0
  69. package/dist/validation/rules/index.d.ts +21 -0
  70. package/dist/validation/rules/index.d.ts.map +1 -0
  71. package/dist/validation/rules/index.js +66 -0
  72. package/dist/validation/rules/index.js.map +1 -0
  73. package/dist/validation/rules/no-centered-everything.d.ts +6 -0
  74. package/dist/validation/rules/no-centered-everything.d.ts.map +1 -0
  75. package/dist/validation/rules/no-centered-everything.js +59 -0
  76. package/dist/validation/rules/no-centered-everything.js.map +1 -0
  77. package/dist/validation/rules/no-default-fonts.d.ts +8 -0
  78. package/dist/validation/rules/no-default-fonts.d.ts.map +1 -0
  79. package/dist/validation/rules/no-default-fonts.js +111 -0
  80. package/dist/validation/rules/no-default-fonts.js.map +1 -0
  81. package/dist/validation/rules/no-div-soup.d.ts +6 -0
  82. package/dist/validation/rules/no-div-soup.d.ts.map +1 -0
  83. package/dist/validation/rules/no-div-soup.js +45 -0
  84. package/dist/validation/rules/no-div-soup.js.map +1 -0
  85. package/dist/validation/rules/no-duplicate-ctas.d.ts +6 -0
  86. package/dist/validation/rules/no-duplicate-ctas.d.ts.map +1 -0
  87. package/dist/validation/rules/no-duplicate-ctas.js +32 -0
  88. package/dist/validation/rules/no-duplicate-ctas.js.map +1 -0
  89. package/dist/validation/rules/no-generic-hero.d.ts +7 -0
  90. package/dist/validation/rules/no-generic-hero.d.ts.map +1 -0
  91. package/dist/validation/rules/no-generic-hero.js +46 -0
  92. package/dist/validation/rules/no-generic-hero.js.map +1 -0
  93. package/dist/validation/rules/no-icon-grid.d.ts +8 -0
  94. package/dist/validation/rules/no-icon-grid.d.ts.map +1 -0
  95. package/dist/validation/rules/no-icon-grid.js +43 -0
  96. package/dist/validation/rules/no-icon-grid.js.map +1 -0
  97. package/dist/validation/rules/no-lorem-ipsum.d.ts +6 -0
  98. package/dist/validation/rules/no-lorem-ipsum.d.ts.map +1 -0
  99. package/dist/validation/rules/no-lorem-ipsum.js +66 -0
  100. package/dist/validation/rules/no-lorem-ipsum.js.map +1 -0
  101. package/dist/validation/rules/no-missing-meta.d.ts +6 -0
  102. package/dist/validation/rules/no-missing-meta.d.ts.map +1 -0
  103. package/dist/validation/rules/no-missing-meta.js +49 -0
  104. package/dist/validation/rules/no-missing-meta.js.map +1 -0
  105. package/dist/validation/rules/no-missing-responsive.d.ts +6 -0
  106. package/dist/validation/rules/no-missing-responsive.d.ts.map +1 -0
  107. package/dist/validation/rules/no-missing-responsive.js +30 -0
  108. package/dist/validation/rules/no-missing-responsive.js.map +1 -0
  109. package/dist/validation/rules/no-placeholder-images.d.ts +6 -0
  110. package/dist/validation/rules/no-placeholder-images.d.ts.map +1 -0
  111. package/dist/validation/rules/no-placeholder-images.js +73 -0
  112. package/dist/validation/rules/no-placeholder-images.js.map +1 -0
  113. package/dist/validation/rules/no-saas-speak.d.ts +6 -0
  114. package/dist/validation/rules/no-saas-speak.d.ts.map +1 -0
  115. package/dist/validation/rules/no-saas-speak.js +74 -0
  116. package/dist/validation/rules/no-saas-speak.js.map +1 -0
  117. package/dist/validation/rules/no-slop-gradients.d.ts +6 -0
  118. package/dist/validation/rules/no-slop-gradients.d.ts.map +1 -0
  119. package/dist/validation/rules/no-slop-gradients.js +24 -0
  120. package/dist/validation/rules/no-slop-gradients.js.map +1 -0
  121. package/dist/validation/rules/no-uniform-spacing.d.ts +6 -0
  122. package/dist/validation/rules/no-uniform-spacing.d.ts.map +1 -0
  123. package/dist/validation/rules/no-uniform-spacing.js +64 -0
  124. package/dist/validation/rules/no-uniform-spacing.js.map +1 -0
  125. package/dist/validation/rules/types.d.ts +36 -0
  126. package/dist/validation/rules/types.d.ts.map +1 -0
  127. package/dist/validation/rules/types.js +7 -0
  128. package/dist/validation/rules/types.js.map +1 -0
  129. package/dist/validation/tailwind-parser.d.ts +36 -0
  130. package/dist/validation/tailwind-parser.d.ts.map +1 -0
  131. package/dist/validation/tailwind-parser.js +214 -0
  132. package/dist/validation/tailwind-parser.js.map +1 -0
  133. package/package.json +45 -0
@@ -0,0 +1,73 @@
1
+ const PLACEHOLDER_SRC_PATTERNS = [
2
+ /placeholder/i,
3
+ /picsum/i,
4
+ /unsplash\.com\/random/i,
5
+ /via\.placeholder\.com/i,
6
+ /placehold\.it/i,
7
+ /placekitten/i,
8
+ /placebear/i,
9
+ ];
10
+ const STOCK_SRC_PATTERNS = [
11
+ /\bstock\b/i,
12
+ /shutterstock/i,
13
+ /istockphoto/i,
14
+ /gettyimages/i,
15
+ ];
16
+ /**
17
+ * Detects placeholder and stock image patterns.
18
+ */
19
+ export const noPlaceholderImages = {
20
+ id: 'no-placeholder-images',
21
+ name: 'No Placeholder Images',
22
+ description: 'Flags placeholder service URLs, stock photo URLs, and images with missing or empty src.',
23
+ severity: 'warning',
24
+ category: 'content',
25
+ check(context) {
26
+ const issues = [];
27
+ const { $ } = context;
28
+ $('img').each((_i, el) => {
29
+ const src = $(el).attr('src');
30
+ // Check for missing or empty src
31
+ if (src === undefined) {
32
+ issues.push({
33
+ type: 'warning',
34
+ category: 'content',
35
+ message: 'Image with no src attribute.',
36
+ });
37
+ return;
38
+ }
39
+ if (src === '' || src === '#') {
40
+ issues.push({
41
+ type: 'warning',
42
+ category: 'content',
43
+ message: `Image with empty/placeholder src="${src}".`,
44
+ });
45
+ return;
46
+ }
47
+ // Check for placeholder service URLs
48
+ for (const pattern of PLACEHOLDER_SRC_PATTERNS) {
49
+ if (pattern.test(src)) {
50
+ issues.push({
51
+ type: 'warning',
52
+ category: 'content',
53
+ message: `Placeholder image detected: "${src}".`,
54
+ });
55
+ return;
56
+ }
57
+ }
58
+ // Check for stock photo URLs
59
+ for (const pattern of STOCK_SRC_PATTERNS) {
60
+ if (pattern.test(src)) {
61
+ issues.push({
62
+ type: 'warning',
63
+ category: 'content',
64
+ message: `Stock photo URL detected: "${src}".`,
65
+ });
66
+ return;
67
+ }
68
+ }
69
+ });
70
+ return issues;
71
+ },
72
+ };
73
+ //# sourceMappingURL=no-placeholder-images.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-placeholder-images.js","sourceRoot":"","sources":["../../../src/validation/rules/no-placeholder-images.ts"],"names":[],"mappings":"AAGA,MAAM,wBAAwB,GAAa;IACzC,cAAc;IACd,SAAS;IACT,wBAAwB;IACxB,wBAAwB;IACxB,gBAAgB;IAChB,cAAc;IACd,YAAY;CACb,CAAC;AAEF,MAAM,kBAAkB,GAAa;IACnC,YAAY;IACZ,eAAe;IACf,cAAc;IACd,cAAc;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAa;IAC3C,EAAE,EAAE,uBAAuB;IAC3B,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EAAE,yFAAyF;IACtG,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,SAAS;IAEnB,KAAK,CAAC,OAAoB;QACxB,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC;QAEtB,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;YACvB,MAAM,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAE9B,iCAAiC;YACjC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,SAAS;oBACf,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,8BAA8B;iBACxC,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;gBAC9B,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,SAAS;oBACf,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,qCAAqC,GAAG,IAAI;iBACtD,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,qCAAqC;YACrC,KAAK,MAAM,OAAO,IAAI,wBAAwB,EAAE,CAAC;gBAC/C,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,SAAS;wBACf,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,gCAAgC,GAAG,IAAI;qBACjD,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;YACH,CAAC;YAED,6BAA6B;YAC7B,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;gBACzC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,SAAS;wBACf,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,8BAA8B,GAAG,IAAI;qBAC/C,CAAC,CAAC;oBACH,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { LintRule } from './types.js';
2
+ /**
3
+ * Detects generic AI marketing language (SaaS-speak).
4
+ */
5
+ export declare const noSaasSpeak: LintRule;
6
+ //# sourceMappingURL=no-saas-speak.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-saas-speak.d.ts","sourceRoot":"","sources":["../../../src/validation/rules/no-saas-speak.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AAsCxD;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,QA2CzB,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Blocklist of generic AI marketing phrases.
3
+ * Case-insensitive matching is applied.
4
+ */
5
+ const SAAS_SPEAK_PHRASES = [
6
+ 'transform your workflow',
7
+ 'seamless integration',
8
+ 'revolutionize',
9
+ 'unlock the power',
10
+ 'take your .+ to the next level',
11
+ 'built for teams',
12
+ 'trusted by thousands',
13
+ 'join \\d+\\+? users',
14
+ 'why choose us',
15
+ 'our mission',
16
+ 'our values',
17
+ 'supercharge your',
18
+ 'streamline your',
19
+ 'empower your',
20
+ 'cutting-edge',
21
+ 'best-in-class',
22
+ 'world-class',
23
+ 'game-changing',
24
+ 'all-in-one platform',
25
+ 'everything you need',
26
+ 'start your journey',
27
+ 'the future of',
28
+ 'designed for the modern',
29
+ 'scale with confidence',
30
+ ];
31
+ const SAAS_SPEAK_REGEXES = SAAS_SPEAK_PHRASES.map((phrase) => new RegExp(phrase, 'i'));
32
+ /**
33
+ * Detects generic AI marketing language (SaaS-speak).
34
+ */
35
+ export const noSaasSpeak = {
36
+ id: 'no-saas-speak',
37
+ name: 'No SaaS Speak',
38
+ description: 'Detects generic AI marketing language commonly found in AI-generated landing pages.',
39
+ severity: 'warning',
40
+ category: 'content',
41
+ check(context) {
42
+ const issues = [];
43
+ const { $ } = context;
44
+ // Extract visible text, excluding code blocks and comments
45
+ const visibleText = [];
46
+ $('body *')
47
+ .not('script, style, code, pre, noscript')
48
+ .each((_i, el) => {
49
+ const directText = $(el)
50
+ .contents()
51
+ .filter(function () {
52
+ return this.type === 'text';
53
+ })
54
+ .text()
55
+ .trim();
56
+ if (directText) {
57
+ visibleText.push(directText);
58
+ }
59
+ });
60
+ const fullText = visibleText.join(' ');
61
+ for (const regex of SAAS_SPEAK_REGEXES) {
62
+ const match = fullText.match(regex);
63
+ if (match) {
64
+ issues.push({
65
+ type: 'warning',
66
+ category: 'content',
67
+ message: `Generic AI marketing phrase detected: "${match[0]}".`,
68
+ });
69
+ }
70
+ }
71
+ return issues;
72
+ },
73
+ };
74
+ //# sourceMappingURL=no-saas-speak.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-saas-speak.js","sourceRoot":"","sources":["../../../src/validation/rules/no-saas-speak.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,kBAAkB,GAAa;IACnC,yBAAyB;IACzB,sBAAsB;IACtB,eAAe;IACf,kBAAkB;IAClB,gCAAgC;IAChC,iBAAiB;IACjB,sBAAsB;IACtB,qBAAqB;IACrB,eAAe;IACf,aAAa;IACb,YAAY;IACZ,kBAAkB;IAClB,iBAAiB;IACjB,cAAc;IACd,cAAc;IACd,eAAe;IACf,aAAa;IACb,eAAe;IACf,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,eAAe;IACf,yBAAyB;IACzB,uBAAuB;CACxB,CAAC;AAEF,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,GAAG,CAC/C,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CACpC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAa;IACnC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,eAAe;IACrB,WAAW,EAAE,qFAAqF;IAClG,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,SAAS;IAEnB,KAAK,CAAC,OAAoB;QACxB,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC;QAEtB,2DAA2D;QAC3D,MAAM,WAAW,GAAa,EAAE,CAAC;QACjC,CAAC,CAAC,QAAQ,CAAC;aACR,GAAG,CAAC,oCAAoC,CAAC;aACzC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;YACf,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC;iBACrB,QAAQ,EAAE;iBACV,MAAM,CAAC;gBACN,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;YAC9B,CAAC,CAAC;iBACD,IAAI,EAAE;iBACN,IAAI,EAAE,CAAC;YACV,IAAI,UAAU,EAAE,CAAC;gBACf,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC,CAAC;QAEL,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEvC,KAAK,MAAM,KAAK,IAAI,kBAAkB,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACpC,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,SAAS;oBACf,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,0CAA0C,KAAK,CAAC,CAAC,CAAC,IAAI;iBAChE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { LintRule } from './types.js';
2
+ /**
3
+ * Flags purple-to-blue AI gradients.
4
+ */
5
+ export declare const noSlopGradients: LintRule;
6
+ //# sourceMappingURL=no-slop-gradients.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-slop-gradients.d.ts","sourceRoot":"","sources":["../../../src/validation/rules/no-slop-gradients.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AAGxD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,QAwB7B,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Flags purple-to-blue AI gradients.
3
+ */
4
+ export const noSlopGradients = {
5
+ id: 'no-slop-gradients',
6
+ name: 'No Slop Gradients',
7
+ description: 'Flags the ubiquitous purple-to-blue gradient pattern common in AI-generated designs.',
8
+ severity: 'warning',
9
+ category: 'slop',
10
+ check(context) {
11
+ const issues = [];
12
+ const { allStyles } = context;
13
+ if (/gradient[^;]*(purple|#[89a-f][0-9a-f]{5})[^;]*(blue|#[0-5][0-9a-f]{5})/i.test(allStyles) ||
14
+ /gradient[^;]*(blue|#[0-5][0-9a-f]{5})[^;]*(purple|#[89a-f][0-9a-f]{5})/i.test(allStyles)) {
15
+ issues.push({
16
+ type: 'warning',
17
+ category: 'slop',
18
+ message: 'Detected purple-to-blue gradient — extremely common AI pattern.',
19
+ });
20
+ }
21
+ return issues;
22
+ },
23
+ };
24
+ //# sourceMappingURL=no-slop-gradients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-slop-gradients.js","sourceRoot":"","sources":["../../../src/validation/rules/no-slop-gradients.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAa;IACvC,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,mBAAmB;IACzB,WAAW,EAAE,sFAAsF;IACnG,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE,MAAM;IAEhB,KAAK,CAAC,OAAoB;QACxB,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;QAE9B,IACE,yEAAyE,CAAC,IAAI,CAAC,SAAS,CAAC;YACzF,yEAAyE,CAAC,IAAI,CAAC,SAAS,CAAC,EACzF,CAAC;YACD,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,iEAAiE;aAC3E,CAAC,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { LintRule } from './types.js';
2
+ /**
3
+ * Detects robotic uniform spacing where the same value dominates.
4
+ */
5
+ export declare const noUniformSpacing: LintRule;
6
+ //# sourceMappingURL=no-uniform-spacing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-uniform-spacing.d.ts","sourceRoot":"","sources":["../../../src/validation/rules/no-uniform-spacing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AA+BxD;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAyC9B,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Extracts spacing values from CSS margin/padding declarations and Tailwind classes.
3
+ */
4
+ function extractSpacingValues(allStyles, allClasses) {
5
+ const values = [];
6
+ // Extract from CSS margin/padding values
7
+ const cssPattern = /(?:margin|padding)(?:-(?:top|right|bottom|left))?\s*:\s*([^;]+)/gi;
8
+ let match;
9
+ while ((match = cssPattern.exec(allStyles)) !== null) {
10
+ // Split shorthand values (e.g. "10px 20px 10px 20px")
11
+ const parts = match[1].trim().split(/\s+/);
12
+ for (const part of parts) {
13
+ if (/^\d+(?:px|rem|em)$/.test(part)) {
14
+ values.push(part);
15
+ }
16
+ }
17
+ }
18
+ // Extract from Tailwind spacing classes
19
+ const twPattern = /\b(?:m|p|mx|my|mt|mr|mb|ml|px|py|pt|pr|pb|pl)-(\d+)\b/g;
20
+ while ((match = twPattern.exec(allClasses)) !== null) {
21
+ values.push(`tw-${match[1]}`);
22
+ }
23
+ return values;
24
+ }
25
+ /**
26
+ * Detects robotic uniform spacing where the same value dominates.
27
+ */
28
+ export const noUniformSpacing = {
29
+ id: 'no-uniform-spacing',
30
+ name: 'No Uniform Spacing',
31
+ description: 'Flags robotic uniform spacing where the same value appears 10+ times with no variation.',
32
+ severity: 'info',
33
+ category: 'layout',
34
+ check(context) {
35
+ const issues = [];
36
+ const { allStyles, allClasses } = context;
37
+ const spacingValues = extractSpacingValues(allStyles, allClasses);
38
+ if (spacingValues.length === 0)
39
+ return issues;
40
+ // Count occurrences of each spacing value
41
+ const counts = {};
42
+ for (const val of spacingValues) {
43
+ counts[val] = (counts[val] || 0) + 1;
44
+ }
45
+ // Base spacing values that are OK in moderation (16px/1rem = 4 in Tailwind)
46
+ const baseSpacingExceptions = new Set(['4px', '8px', '16px', '0.25rem', '0.5rem', '1rem', 'tw-1', 'tw-2', 'tw-4']);
47
+ // Check how many unique values exist
48
+ const uniqueValues = Object.keys(counts);
49
+ for (const [value, count] of Object.entries(counts)) {
50
+ if (count > 10 && !baseSpacingExceptions.has(value)) {
51
+ // Only flag if there is very low variation (1-2 unique values total)
52
+ if (uniqueValues.length <= 2) {
53
+ issues.push({
54
+ type: 'info',
55
+ category: 'layout',
56
+ message: `Spacing value "${value}" appears ${count} times with almost no variation — creates a robotic feel.`,
57
+ });
58
+ }
59
+ }
60
+ }
61
+ return issues;
62
+ },
63
+ };
64
+ //# sourceMappingURL=no-uniform-spacing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-uniform-spacing.js","sourceRoot":"","sources":["../../../src/validation/rules/no-uniform-spacing.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,SAAS,oBAAoB,CAAC,SAAiB,EAAE,UAAkB;IACjE,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,yCAAyC;IACzC,MAAM,UAAU,GAAG,mEAAmE,CAAC;IACvF,IAAI,KAA6B,CAAC;IAClC,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACrD,sDAAsD;QACtD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAED,wCAAwC;IACxC,MAAM,SAAS,GAAG,wDAAwD,CAAC;IAC3E,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAa;IACxC,EAAE,EAAE,oBAAoB;IACxB,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EAAE,yFAAyF;IACtG,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,QAAQ;IAElB,KAAK,CAAC,OAAoB;QACxB,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QAE1C,MAAM,aAAa,GAAG,oBAAoB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAClE,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QAE9C,0CAA0C;QAC1C,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;QAED,4EAA4E;QAC5E,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAEnH,qCAAqC;QACrC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEzC,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,IAAI,KAAK,GAAG,EAAE,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpD,qEAAqE;gBACrE,IAAI,YAAY,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;oBAC7B,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,MAAM;wBACZ,QAAQ,EAAE,QAAQ;wBAClB,OAAO,EAAE,kBAAkB,KAAK,aAAa,KAAK,2DAA2D;qBAC9G,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Lint rule interface for modular output validation.
3
+ *
4
+ * Each rule is independently testable, configurable, and extensible.
5
+ */
6
+ import type { CheerioAPI } from 'cheerio';
7
+ import type { ValidationIssue } from '../output-validator.js';
8
+ export interface LintContext {
9
+ /** The full HTML string being validated */
10
+ html: string;
11
+ /** All CSS from <style> tags and inline styles, concatenated */
12
+ allStyles: string;
13
+ /** All class attribute values, concatenated */
14
+ allClasses: string;
15
+ /** Cheerio instance for DOM querying */
16
+ $: CheerioAPI;
17
+ /** Content of DESIGN.md if available, undefined otherwise */
18
+ designMdContent?: string;
19
+ }
20
+ export interface LintRule {
21
+ /** Unique identifier for the rule, e.g. 'no-default-fonts' */
22
+ id: string;
23
+ /** Human-readable name */
24
+ name: string;
25
+ /** What the rule checks for */
26
+ description: string;
27
+ /** Default severity */
28
+ severity: 'error' | 'warning' | 'info';
29
+ /** Category for grouping */
30
+ category: 'color' | 'typography' | 'accessibility' | 'slop' | 'structure' | 'content' | 'layout';
31
+ /** If true the rule requires a DESIGN.md to be useful */
32
+ requiresDesign?: boolean;
33
+ /** Run the check and return any issues found */
34
+ check(context: LintContext): ValidationIssue[];
35
+ }
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/validation/rules/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,CAAC,EAAE,UAAU,CAAC;IACd,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,4BAA4B;IAC5B,QAAQ,EAAE,OAAO,GAAG,YAAY,GAAG,eAAe,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjG,yDAAyD;IACzD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gDAAgD;IAChD,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,eAAe,EAAE,CAAC;CAChD"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Lint rule interface for modular output validation.
3
+ *
4
+ * Each rule is independently testable, configurable, and extensible.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/validation/rules/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tailwind CSS class parser for design-guard validation.
3
+ *
4
+ * Extracts design values (colors, fonts, spacing, alignment, responsive
5
+ * prefixes, display classes) from Tailwind CSS classes found in HTML markup.
6
+ */
7
+ export interface TailwindExtraction {
8
+ /** Hex color values from text-[#xxx], bg-[#xxx], border-[#xxx] arbitrary values */
9
+ colors: string[];
10
+ /** Font families from font-['Font Name'] arbitrary value classes */
11
+ fontFamilies: string[];
12
+ /** Spacing utility classes (p-X, m-X, gap-X, etc.) */
13
+ spacing: string[];
14
+ /** Text alignment classes (text-center, text-left, text-right, text-justify) */
15
+ textAlignment: string[];
16
+ /** Whether any responsive prefixes (sm:, md:, lg:, xl:, 2xl:) are present */
17
+ responsive: boolean;
18
+ /** Display-related classes (flex, grid, block, inline, hidden, etc.) */
19
+ displayClasses: string[];
20
+ /** Named Tailwind color classes like text-red-500, bg-blue-600 */
21
+ namedColors: string[];
22
+ }
23
+ /**
24
+ * Extract design values from Tailwind CSS classes in HTML.
25
+ *
26
+ * Parses all class attributes in the HTML and identifies:
27
+ * - Arbitrary hex colors (text-[#RRGGBB], bg-[#RRGGBB], etc.)
28
+ * - Named Tailwind colors (text-red-500, bg-blue-600, etc.)
29
+ * - Font families (font-['Font Name'] and font-sans/serif/mono)
30
+ * - Spacing utilities (p-4, mx-8, gap-6, etc.)
31
+ * - Text alignment (text-center, text-left, etc.)
32
+ * - Responsive prefixes (sm:, md:, lg:, xl:, 2xl:)
33
+ * - Display classes (flex, grid, block, hidden, etc.)
34
+ */
35
+ export declare function extractTailwindValues(html: string): TailwindExtraction;
36
+ //# sourceMappingURL=tailwind-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind-parser.d.ts","sourceRoot":"","sources":["../../src/validation/tailwind-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,oEAAoE;IACpE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gFAAgF;IAChF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,6EAA6E;IAC7E,UAAU,EAAE,OAAO,CAAC;IACpB,wEAAwE;IACxE,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAoMD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAYtE"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Tailwind CSS class parser for design-guard validation.
3
+ *
4
+ * Extracts design values (colors, fonts, spacing, alignment, responsive
5
+ * prefixes, display classes) from Tailwind CSS classes found in HTML markup.
6
+ */
7
+ /**
8
+ * Extract all class attribute values from HTML as a single string.
9
+ */
10
+ function extractAllClasses(html) {
11
+ const classRegex = /class\s*=\s*"([^"]*)"/gi;
12
+ const classes = [];
13
+ let match;
14
+ while ((match = classRegex.exec(html)) !== null) {
15
+ classes.push(match[1]);
16
+ }
17
+ // Also handle single-quoted class attributes
18
+ const singleQuoteRegex = /class\s*=\s*'([^']*)'/gi;
19
+ while ((match = singleQuoteRegex.exec(html)) !== null) {
20
+ classes.push(match[1]);
21
+ }
22
+ return classes.join(' ');
23
+ }
24
+ /**
25
+ * Extract hex colors from Tailwind arbitrary value syntax:
26
+ * text-[#RRGGBB], bg-[#RRGGBB], border-[#RRGGBB], from-[#RRGGBB],
27
+ * to-[#RRGGBB], via-[#RRGGBB], ring-[#RRGGBB], accent-[#RRGGBB],
28
+ * shadow-[#RRGGBB], outline-[#RRGGBB], decoration-[#RRGGBB],
29
+ * divide-[#RRGGBB], placeholder-[#RRGGBB], caret-[#RRGGBB],
30
+ * fill-[#RRGGBB], stroke-[#RRGGBB]
31
+ */
32
+ function extractArbitraryColors(classStr) {
33
+ const colorPrefixes = [
34
+ 'text', 'bg', 'border', 'from', 'to', 'via', 'ring',
35
+ 'accent', 'shadow', 'outline', 'decoration', 'divide',
36
+ 'placeholder', 'caret', 'fill', 'stroke',
37
+ ];
38
+ const prefixPattern = colorPrefixes.join('|');
39
+ // Match both 3-char and 6-char hex with optional alpha
40
+ const regex = new RegExp(`(?:${prefixPattern})-\\[#([0-9A-Fa-f]{3,8})\\]`, 'g');
41
+ const colors = [];
42
+ let match;
43
+ while ((match = regex.exec(classStr)) !== null) {
44
+ const hex = match[1];
45
+ // Normalize 3-char hex to 6-char
46
+ if (hex.length === 3) {
47
+ colors.push(`#${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`.toUpperCase());
48
+ }
49
+ else {
50
+ colors.push(`#${hex.slice(0, 6)}`.toUpperCase());
51
+ }
52
+ }
53
+ return [...new Set(colors)];
54
+ }
55
+ /**
56
+ * Extract named Tailwind color classes like text-red-500, bg-blue-600.
57
+ * Returns the full class name for reference.
58
+ */
59
+ function extractNamedColors(classStr) {
60
+ const colorNames = [
61
+ 'slate', 'gray', 'zinc', 'neutral', 'stone',
62
+ 'red', 'orange', 'amber', 'yellow', 'lime',
63
+ 'green', 'emerald', 'teal', 'cyan', 'sky',
64
+ 'blue', 'indigo', 'violet', 'purple', 'fuchsia',
65
+ 'pink', 'rose',
66
+ ];
67
+ const prefixes = ['text', 'bg', 'border', 'from', 'to', 'via', 'ring', 'accent', 'divide', 'decoration', 'fill', 'stroke'];
68
+ const shades = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950'];
69
+ const results = [];
70
+ const tokens = classStr.split(/\s+/);
71
+ for (const token of tokens) {
72
+ // Strip responsive/state prefixes like sm:, hover:, etc.
73
+ const baseClass = token.replace(/^(?:[a-z0-9]+:)*/, '');
74
+ for (const prefix of prefixes) {
75
+ for (const color of colorNames) {
76
+ for (const shade of shades) {
77
+ if (baseClass === `${prefix}-${color}-${shade}`) {
78
+ results.push(baseClass);
79
+ }
80
+ }
81
+ // Also match without shade (e.g., text-white, bg-black, text-inherit)
82
+ if (baseClass === `${prefix}-${color}`) {
83
+ results.push(baseClass);
84
+ }
85
+ }
86
+ // Special color keywords
87
+ for (const special of ['white', 'black', 'transparent', 'current', 'inherit']) {
88
+ if (baseClass === `${prefix}-${special}`) {
89
+ results.push(baseClass);
90
+ }
91
+ }
92
+ }
93
+ }
94
+ return [...new Set(results)];
95
+ }
96
+ /**
97
+ * Extract font families from Tailwind arbitrary font classes: font-['Font Name']
98
+ * and font-["Font Name"].
99
+ */
100
+ function extractFontFamilies(classStr) {
101
+ const regex = /font-\[['"]([^'"]+)['"]\]/g;
102
+ const fonts = [];
103
+ let match;
104
+ while ((match = regex.exec(classStr)) !== null) {
105
+ fonts.push(match[1]);
106
+ }
107
+ // Also detect standard Tailwind font classes
108
+ const standardFonts = {
109
+ 'font-sans': 'system-ui, sans-serif',
110
+ 'font-serif': 'Georgia, serif',
111
+ 'font-mono': 'monospace',
112
+ };
113
+ const tokens = classStr.split(/\s+/);
114
+ for (const token of tokens) {
115
+ const baseClass = token.replace(/^(?:[a-z0-9]+:)*/, '');
116
+ if (baseClass in standardFonts) {
117
+ fonts.push(standardFonts[baseClass]);
118
+ }
119
+ }
120
+ return [...new Set(fonts)];
121
+ }
122
+ /**
123
+ * Extract spacing utility classes: p-X, px-X, py-X, pt-X, pr-X, pb-X, pl-X,
124
+ * m-X, mx-X, my-X, mt-X, mr-X, mb-X, ml-X, gap-X, gap-x-X, gap-y-X,
125
+ * space-x-X, space-y-X, and arbitrary values like p-[20px].
126
+ */
127
+ function extractSpacing(classStr) {
128
+ const spacingPrefixes = [
129
+ 'p', 'px', 'py', 'pt', 'pr', 'pb', 'pl',
130
+ 'm', 'mx', 'my', 'mt', 'mr', 'mb', 'ml',
131
+ 'gap', 'gap-x', 'gap-y', 'space-x', 'space-y',
132
+ ];
133
+ const results = [];
134
+ const tokens = classStr.split(/\s+/);
135
+ for (const token of tokens) {
136
+ // Strip responsive/state prefixes
137
+ const baseClass = token.replace(/^(?:[a-z0-9]+:)*/, '');
138
+ for (const prefix of spacingPrefixes) {
139
+ // Match: prefix-N, prefix-N.N, prefix-auto, prefix-[Npx], prefix-[Nrem], etc.
140
+ const pattern = new RegExp(`^${prefix.replace('-', '\\-')}-(?:\\d+(?:\\.\\d+)?|auto|\\[.+\\])$`);
141
+ if (pattern.test(baseClass)) {
142
+ results.push(baseClass);
143
+ break;
144
+ }
145
+ }
146
+ }
147
+ return [...new Set(results)];
148
+ }
149
+ /**
150
+ * Extract text alignment classes.
151
+ */
152
+ function extractTextAlignment(classStr) {
153
+ const alignments = ['text-center', 'text-left', 'text-right', 'text-justify', 'text-start', 'text-end'];
154
+ const results = [];
155
+ const tokens = classStr.split(/\s+/);
156
+ for (const token of tokens) {
157
+ const baseClass = token.replace(/^(?:[a-z0-9]+:)*/, '');
158
+ if (alignments.includes(baseClass)) {
159
+ results.push(baseClass);
160
+ }
161
+ }
162
+ return [...new Set(results)];
163
+ }
164
+ /**
165
+ * Check for responsive prefixes: sm:, md:, lg:, xl:, 2xl:
166
+ */
167
+ function hasResponsivePrefixes(classStr) {
168
+ return /\b(?:sm|md|lg|xl|2xl):/.test(classStr);
169
+ }
170
+ /**
171
+ * Extract display-related classes.
172
+ */
173
+ function extractDisplayClasses(classStr) {
174
+ const displayValues = [
175
+ 'flex', 'inline-flex', 'grid', 'inline-grid',
176
+ 'block', 'inline-block', 'inline', 'hidden',
177
+ 'table', 'table-row', 'table-cell',
178
+ 'contents', 'flow-root', 'list-item',
179
+ ];
180
+ const results = [];
181
+ const tokens = classStr.split(/\s+/);
182
+ for (const token of tokens) {
183
+ const baseClass = token.replace(/^(?:[a-z0-9]+:)*/, '');
184
+ if (displayValues.includes(baseClass)) {
185
+ results.push(baseClass);
186
+ }
187
+ }
188
+ return [...new Set(results)];
189
+ }
190
+ /**
191
+ * Extract design values from Tailwind CSS classes in HTML.
192
+ *
193
+ * Parses all class attributes in the HTML and identifies:
194
+ * - Arbitrary hex colors (text-[#RRGGBB], bg-[#RRGGBB], etc.)
195
+ * - Named Tailwind colors (text-red-500, bg-blue-600, etc.)
196
+ * - Font families (font-['Font Name'] and font-sans/serif/mono)
197
+ * - Spacing utilities (p-4, mx-8, gap-6, etc.)
198
+ * - Text alignment (text-center, text-left, etc.)
199
+ * - Responsive prefixes (sm:, md:, lg:, xl:, 2xl:)
200
+ * - Display classes (flex, grid, block, hidden, etc.)
201
+ */
202
+ export function extractTailwindValues(html) {
203
+ const allClasses = extractAllClasses(html);
204
+ return {
205
+ colors: extractArbitraryColors(allClasses),
206
+ fontFamilies: extractFontFamilies(allClasses),
207
+ spacing: extractSpacing(allClasses),
208
+ textAlignment: extractTextAlignment(allClasses),
209
+ responsive: hasResponsivePrefixes(allClasses),
210
+ displayClasses: extractDisplayClasses(allClasses),
211
+ namedColors: extractNamedColors(allClasses),
212
+ };
213
+ }
214
+ //# sourceMappingURL=tailwind-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind-parser.js","sourceRoot":"","sources":["../../src/validation/tailwind-parser.ts"],"names":[],"mappings":"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"}