@fragments-sdk/cli 0.2.2

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 (259) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +4783 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chunk-4FDQSGKX.js +786 -0
  7. package/dist/chunk-4FDQSGKX.js.map +1 -0
  8. package/dist/chunk-7H2MMGYG.js +369 -0
  9. package/dist/chunk-7H2MMGYG.js.map +1 -0
  10. package/dist/chunk-BSCG3IP7.js +619 -0
  11. package/dist/chunk-BSCG3IP7.js.map +1 -0
  12. package/dist/chunk-LY2CFFPY.js +898 -0
  13. package/dist/chunk-LY2CFFPY.js.map +1 -0
  14. package/dist/chunk-MUZ6CM66.js +6636 -0
  15. package/dist/chunk-MUZ6CM66.js.map +1 -0
  16. package/dist/chunk-OAENNG3G.js +1489 -0
  17. package/dist/chunk-OAENNG3G.js.map +1 -0
  18. package/dist/chunk-XHNKNI6J.js +235 -0
  19. package/dist/chunk-XHNKNI6J.js.map +1 -0
  20. package/dist/core-DWKLGY4N.js +68 -0
  21. package/dist/core-DWKLGY4N.js.map +1 -0
  22. package/dist/generate-4LQNJ7SX.js +249 -0
  23. package/dist/generate-4LQNJ7SX.js.map +1 -0
  24. package/dist/index.d.ts +775 -0
  25. package/dist/index.js +41 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/init-EMVI47QG.js +416 -0
  28. package/dist/init-EMVI47QG.js.map +1 -0
  29. package/dist/mcp-bin.d.ts +1 -0
  30. package/dist/mcp-bin.js +1117 -0
  31. package/dist/mcp-bin.js.map +1 -0
  32. package/dist/scan-4YPRF7FV.js +12 -0
  33. package/dist/scan-4YPRF7FV.js.map +1 -0
  34. package/dist/service-QSZMZJBJ.js +208 -0
  35. package/dist/service-QSZMZJBJ.js.map +1 -0
  36. package/dist/static-viewer-MIPGZ4Z7.js +12 -0
  37. package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
  38. package/dist/test-SQ5ZHXWU.js +1067 -0
  39. package/dist/test-SQ5ZHXWU.js.map +1 -0
  40. package/dist/tokens-HSGMYK64.js +173 -0
  41. package/dist/tokens-HSGMYK64.js.map +1 -0
  42. package/dist/viewer-YRF4SQE4.js +11101 -0
  43. package/dist/viewer-YRF4SQE4.js.map +1 -0
  44. package/package.json +107 -0
  45. package/src/ai.ts +266 -0
  46. package/src/analyze.ts +265 -0
  47. package/src/bin.ts +916 -0
  48. package/src/build.ts +248 -0
  49. package/src/commands/a11y.ts +302 -0
  50. package/src/commands/add.ts +313 -0
  51. package/src/commands/audit.ts +195 -0
  52. package/src/commands/baseline.ts +221 -0
  53. package/src/commands/build.ts +144 -0
  54. package/src/commands/compare.ts +337 -0
  55. package/src/commands/context.ts +107 -0
  56. package/src/commands/dev.ts +107 -0
  57. package/src/commands/enhance.ts +858 -0
  58. package/src/commands/generate.ts +391 -0
  59. package/src/commands/init.ts +531 -0
  60. package/src/commands/link/figma.ts +645 -0
  61. package/src/commands/link/index.ts +10 -0
  62. package/src/commands/link/storybook.ts +267 -0
  63. package/src/commands/list.ts +49 -0
  64. package/src/commands/metrics.ts +114 -0
  65. package/src/commands/reset.ts +242 -0
  66. package/src/commands/scan.ts +537 -0
  67. package/src/commands/storygen.ts +207 -0
  68. package/src/commands/tokens.ts +251 -0
  69. package/src/commands/validate.ts +93 -0
  70. package/src/commands/verify.ts +215 -0
  71. package/src/core/composition.test.ts +262 -0
  72. package/src/core/composition.ts +255 -0
  73. package/src/core/config.ts +84 -0
  74. package/src/core/constants.ts +111 -0
  75. package/src/core/context.ts +380 -0
  76. package/src/core/defineSegment.ts +137 -0
  77. package/src/core/discovery.ts +337 -0
  78. package/src/core/figma.ts +263 -0
  79. package/src/core/fragment-types.ts +214 -0
  80. package/src/core/generators/context.ts +389 -0
  81. package/src/core/generators/index.ts +23 -0
  82. package/src/core/generators/registry.ts +364 -0
  83. package/src/core/generators/typescript-extractor.ts +374 -0
  84. package/src/core/importAnalyzer.ts +217 -0
  85. package/src/core/index.ts +149 -0
  86. package/src/core/loader.ts +155 -0
  87. package/src/core/node.ts +63 -0
  88. package/src/core/parser.ts +551 -0
  89. package/src/core/previewLoader.ts +172 -0
  90. package/src/core/schema/fragment.schema.json +189 -0
  91. package/src/core/schema/registry.schema.json +137 -0
  92. package/src/core/schema.ts +182 -0
  93. package/src/core/storyAdapter.test.ts +571 -0
  94. package/src/core/storyAdapter.ts +761 -0
  95. package/src/core/token-types.ts +287 -0
  96. package/src/core/types.ts +754 -0
  97. package/src/diff.ts +323 -0
  98. package/src/index.ts +43 -0
  99. package/src/mcp/__tests__/projectFields.test.ts +130 -0
  100. package/src/mcp/bin.ts +36 -0
  101. package/src/mcp/index.ts +8 -0
  102. package/src/mcp/server.ts +1310 -0
  103. package/src/mcp/utils.ts +54 -0
  104. package/src/mcp-bin.ts +36 -0
  105. package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
  106. package/src/migrate/__tests__/args/args.test.ts +452 -0
  107. package/src/migrate/__tests__/meta/meta.test.ts +198 -0
  108. package/src/migrate/__tests__/stories/stories.test.ts +278 -0
  109. package/src/migrate/__tests__/utils/utils.test.ts +371 -0
  110. package/src/migrate/__tests__/values/values.test.ts +303 -0
  111. package/src/migrate/bin.ts +108 -0
  112. package/src/migrate/converter.ts +658 -0
  113. package/src/migrate/detect.ts +196 -0
  114. package/src/migrate/index.ts +45 -0
  115. package/src/migrate/migrate.ts +163 -0
  116. package/src/migrate/parser.ts +1136 -0
  117. package/src/migrate/report.ts +624 -0
  118. package/src/migrate/types.ts +169 -0
  119. package/src/screenshot.ts +249 -0
  120. package/src/service/__tests__/ast-utils.test.ts +426 -0
  121. package/src/service/__tests__/enhance-scanner.test.ts +200 -0
  122. package/src/service/__tests__/figma/figma.test.ts +652 -0
  123. package/src/service/__tests__/metrics-store.test.ts +409 -0
  124. package/src/service/__tests__/patch-generator.test.ts +186 -0
  125. package/src/service/__tests__/props-extractor.test.ts +365 -0
  126. package/src/service/__tests__/token-registry.test.ts +267 -0
  127. package/src/service/analytics.ts +659 -0
  128. package/src/service/ast-utils.ts +444 -0
  129. package/src/service/browser-pool.ts +339 -0
  130. package/src/service/capture.ts +267 -0
  131. package/src/service/diff.ts +279 -0
  132. package/src/service/enhance/aggregator.ts +489 -0
  133. package/src/service/enhance/cache.ts +275 -0
  134. package/src/service/enhance/codebase-scanner.ts +357 -0
  135. package/src/service/enhance/context-generator.ts +529 -0
  136. package/src/service/enhance/doc-extractor.ts +523 -0
  137. package/src/service/enhance/index.ts +131 -0
  138. package/src/service/enhance/props-extractor.ts +665 -0
  139. package/src/service/enhance/scanner.ts +445 -0
  140. package/src/service/enhance/storybook-parser.ts +552 -0
  141. package/src/service/enhance/types.ts +346 -0
  142. package/src/service/enhance/variant-renderer.ts +479 -0
  143. package/src/service/figma.ts +1008 -0
  144. package/src/service/index.ts +249 -0
  145. package/src/service/metrics-store.ts +333 -0
  146. package/src/service/patch-generator.ts +349 -0
  147. package/src/service/report.ts +854 -0
  148. package/src/service/storage.ts +401 -0
  149. package/src/service/token-fixes.ts +281 -0
  150. package/src/service/token-parser.ts +504 -0
  151. package/src/service/token-registry.ts +721 -0
  152. package/src/service/utils.ts +172 -0
  153. package/src/setup.ts +241 -0
  154. package/src/shared/command-wrapper.ts +81 -0
  155. package/src/shared/dev-server-client.ts +199 -0
  156. package/src/shared/index.ts +8 -0
  157. package/src/shared/segment-loader.ts +59 -0
  158. package/src/shared/types.ts +147 -0
  159. package/src/static-viewer.ts +715 -0
  160. package/src/test/discovery.ts +172 -0
  161. package/src/test/index.ts +281 -0
  162. package/src/test/reporters/console.ts +194 -0
  163. package/src/test/reporters/json.ts +190 -0
  164. package/src/test/reporters/junit.ts +186 -0
  165. package/src/test/runner.ts +598 -0
  166. package/src/test/types.ts +245 -0
  167. package/src/test/watch.ts +200 -0
  168. package/src/validators.ts +152 -0
  169. package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
  170. package/src/viewer/__tests__/render-utils.test.ts +232 -0
  171. package/src/viewer/__tests__/style-utils.test.ts +404 -0
  172. package/src/viewer/bin.ts +86 -0
  173. package/src/viewer/cli/health.ts +256 -0
  174. package/src/viewer/cli/index.ts +33 -0
  175. package/src/viewer/cli/scan.ts +124 -0
  176. package/src/viewer/cli/utils.ts +174 -0
  177. package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
  178. package/src/viewer/components/ActionCapture.tsx +172 -0
  179. package/src/viewer/components/ActionsPanel.tsx +371 -0
  180. package/src/viewer/components/App.tsx +638 -0
  181. package/src/viewer/components/BottomPanel.tsx +224 -0
  182. package/src/viewer/components/CodePanel.tsx +589 -0
  183. package/src/viewer/components/CommandPalette.tsx +336 -0
  184. package/src/viewer/components/ComponentGraph.tsx +394 -0
  185. package/src/viewer/components/ComponentHeader.tsx +85 -0
  186. package/src/viewer/components/ContractPanel.tsx +234 -0
  187. package/src/viewer/components/ErrorBoundary.tsx +85 -0
  188. package/src/viewer/components/FigmaEmbed.tsx +231 -0
  189. package/src/viewer/components/FragmentEditor.tsx +485 -0
  190. package/src/viewer/components/HealthDashboard.tsx +452 -0
  191. package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
  192. package/src/viewer/components/Icons.tsx +417 -0
  193. package/src/viewer/components/InteractionsPanel.tsx +720 -0
  194. package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
  195. package/src/viewer/components/IsolatedRender.tsx +111 -0
  196. package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
  197. package/src/viewer/components/LandingPage.tsx +441 -0
  198. package/src/viewer/components/Layout.tsx +22 -0
  199. package/src/viewer/components/LeftSidebar.tsx +391 -0
  200. package/src/viewer/components/MultiViewportPreview.tsx +429 -0
  201. package/src/viewer/components/PreviewArea.tsx +404 -0
  202. package/src/viewer/components/PreviewFrameHost.tsx +310 -0
  203. package/src/viewer/components/PreviewPane.tsx +150 -0
  204. package/src/viewer/components/PreviewToolbar.tsx +176 -0
  205. package/src/viewer/components/PropsEditor.tsx +512 -0
  206. package/src/viewer/components/PropsTable.tsx +98 -0
  207. package/src/viewer/components/RelationsSection.tsx +57 -0
  208. package/src/viewer/components/ResizablePanel.tsx +328 -0
  209. package/src/viewer/components/RightSidebar.tsx +118 -0
  210. package/src/viewer/components/ScreenshotButton.tsx +90 -0
  211. package/src/viewer/components/Sidebar.tsx +169 -0
  212. package/src/viewer/components/SkeletonLoader.tsx +156 -0
  213. package/src/viewer/components/StoryRenderer.tsx +128 -0
  214. package/src/viewer/components/ThemeProvider.tsx +96 -0
  215. package/src/viewer/components/Toast.tsx +67 -0
  216. package/src/viewer/components/TokenStylePanel.tsx +708 -0
  217. package/src/viewer/components/UsageSection.tsx +95 -0
  218. package/src/viewer/components/VariantMatrix.tsx +350 -0
  219. package/src/viewer/components/VariantRenderer.tsx +131 -0
  220. package/src/viewer/components/VariantTabs.tsx +84 -0
  221. package/src/viewer/components/ViewportSelector.tsx +165 -0
  222. package/src/viewer/components/_future/CreatePage.tsx +836 -0
  223. package/src/viewer/composition-renderer.ts +381 -0
  224. package/src/viewer/constants/index.ts +1 -0
  225. package/src/viewer/constants/ui.ts +185 -0
  226. package/src/viewer/entry.tsx +299 -0
  227. package/src/viewer/hooks/index.ts +2 -0
  228. package/src/viewer/hooks/useA11yCache.ts +383 -0
  229. package/src/viewer/hooks/useA11yService.ts +498 -0
  230. package/src/viewer/hooks/useActions.ts +138 -0
  231. package/src/viewer/hooks/useAppState.ts +124 -0
  232. package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
  233. package/src/viewer/hooks/useHmrStatus.ts +109 -0
  234. package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
  235. package/src/viewer/hooks/usePreviewBridge.ts +347 -0
  236. package/src/viewer/hooks/useScrollSpy.ts +78 -0
  237. package/src/viewer/hooks/useUrlState.ts +330 -0
  238. package/src/viewer/hooks/useViewSettings.ts +125 -0
  239. package/src/viewer/index.html +28 -0
  240. package/src/viewer/index.ts +14 -0
  241. package/src/viewer/intelligence/healthReport.ts +505 -0
  242. package/src/viewer/intelligence/styleDrift.ts +340 -0
  243. package/src/viewer/intelligence/usageScanner.ts +309 -0
  244. package/src/viewer/jsx-parser.ts +485 -0
  245. package/src/viewer/postcss.config.js +6 -0
  246. package/src/viewer/preview-frame-entry.tsx +25 -0
  247. package/src/viewer/preview-frame.html +109 -0
  248. package/src/viewer/render-template.html +68 -0
  249. package/src/viewer/render-utils.ts +170 -0
  250. package/src/viewer/server.ts +276 -0
  251. package/src/viewer/style-utils.ts +414 -0
  252. package/src/viewer/styles/globals.css +355 -0
  253. package/src/viewer/tailwind.config.js +37 -0
  254. package/src/viewer/types/a11y.ts +197 -0
  255. package/src/viewer/utils/a11y-fixes.ts +471 -0
  256. package/src/viewer/utils/actionExport.ts +372 -0
  257. package/src/viewer/utils/colorSchemes.ts +201 -0
  258. package/src/viewer/utils/detectRelationships.ts +256 -0
  259. package/src/viewer/vite-plugin.ts +2143 -0
@@ -0,0 +1,854 @@
1
+ /**
2
+ * HTML Report Generator
3
+ *
4
+ * Generates a beautiful, standalone HTML report from analytics data.
5
+ * No external dependencies - everything is inlined.
6
+ */
7
+
8
+ import { BRAND } from "../core/index.js";
9
+ import type {
10
+ DesignSystemAnalytics,
11
+ ComponentSummary,
12
+ Recommendation,
13
+ } from "./analytics.js";
14
+ import { getGrade, getScoreColor } from "./analytics.js";
15
+
16
+ /**
17
+ * Generate a complete HTML report from analytics data
18
+ */
19
+ export function generateHtmlReport(analytics: DesignSystemAnalytics): string {
20
+ const { summary, coverage, quality, distribution, recommendations, inventory } =
21
+ analytics;
22
+
23
+ const grade = getGrade(summary.overallScore);
24
+ const gradeColor = getScoreColor(summary.overallScore);
25
+
26
+ return `<!DOCTYPE html>
27
+ <html lang="en">
28
+ <head>
29
+ <meta charset="UTF-8">
30
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
31
+ <title>${BRAND.name} Design System Report</title>
32
+ <style>
33
+ ${getStyles()}
34
+ </style>
35
+ </head>
36
+ <body>
37
+ <div class="container">
38
+ ${renderHeader(summary, grade, gradeColor, analytics.analyzedAt)}
39
+ ${renderSummaryCards(summary, coverage)}
40
+ ${renderCoverageSection(coverage)}
41
+ ${renderRecommendations(recommendations)}
42
+ ${renderComponentTable(inventory.byCategory)}
43
+ ${renderDistributionCharts(distribution)}
44
+ ${renderQualityIssues(quality)}
45
+ ${renderFooter()}
46
+ </div>
47
+ <script>
48
+ ${getScripts()}
49
+ </script>
50
+ </body>
51
+ </html>`;
52
+ }
53
+
54
+ /**
55
+ * Get CSS styles
56
+ */
57
+ function getStyles(): string {
58
+ return `
59
+ :root {
60
+ --bg: #0a0a0a;
61
+ --bg-card: #141414;
62
+ --bg-hover: #1a1a1a;
63
+ --border: #262626;
64
+ --text: #fafafa;
65
+ --text-secondary: #a1a1aa;
66
+ --text-muted: #71717a;
67
+ --accent: #10b981;
68
+ --accent-light: #34d399;
69
+ --danger: #ef4444;
70
+ --warning: #eab308;
71
+ --success: #22c55e;
72
+ --radius: 12px;
73
+ }
74
+
75
+ * {
76
+ margin: 0;
77
+ padding: 0;
78
+ box-sizing: border-box;
79
+ }
80
+
81
+ body {
82
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
83
+ background: var(--bg);
84
+ color: var(--text);
85
+ line-height: 1.6;
86
+ -webkit-font-smoothing: antialiased;
87
+ }
88
+
89
+ .container {
90
+ max-width: 1200px;
91
+ margin: 0 auto;
92
+ padding: 48px 24px;
93
+ }
94
+
95
+ /* Header */
96
+ .header {
97
+ display: flex;
98
+ justify-content: space-between;
99
+ align-items: flex-start;
100
+ margin-bottom: 48px;
101
+ padding-bottom: 32px;
102
+ border-bottom: 1px solid var(--border);
103
+ }
104
+
105
+ .header-left h1 {
106
+ font-size: 32px;
107
+ font-weight: 700;
108
+ margin-bottom: 8px;
109
+ background: linear-gradient(135deg, var(--text), var(--accent));
110
+ -webkit-background-clip: text;
111
+ -webkit-text-fill-color: transparent;
112
+ }
113
+
114
+ .header-left p {
115
+ color: var(--text-secondary);
116
+ font-size: 14px;
117
+ }
118
+
119
+ .grade-badge {
120
+ display: flex;
121
+ flex-direction: column;
122
+ align-items: center;
123
+ padding: 24px 32px;
124
+ background: var(--bg-card);
125
+ border: 1px solid var(--border);
126
+ border-radius: var(--radius);
127
+ }
128
+
129
+ .grade-letter {
130
+ font-size: 64px;
131
+ font-weight: 800;
132
+ line-height: 1;
133
+ }
134
+
135
+ .grade-score {
136
+ font-size: 14px;
137
+ color: var(--text-secondary);
138
+ margin-top: 8px;
139
+ }
140
+
141
+ /* Summary Cards */
142
+ .summary-grid {
143
+ display: grid;
144
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
145
+ gap: 16px;
146
+ margin-bottom: 48px;
147
+ }
148
+
149
+ .summary-card {
150
+ background: var(--bg-card);
151
+ border: 1px solid var(--border);
152
+ border-radius: var(--radius);
153
+ padding: 24px;
154
+ }
155
+
156
+ .summary-card-label {
157
+ font-size: 13px;
158
+ color: var(--text-muted);
159
+ text-transform: uppercase;
160
+ letter-spacing: 0.05em;
161
+ margin-bottom: 8px;
162
+ }
163
+
164
+ .summary-card-value {
165
+ font-size: 36px;
166
+ font-weight: 700;
167
+ }
168
+
169
+ .summary-card-sub {
170
+ font-size: 13px;
171
+ color: var(--text-secondary);
172
+ margin-top: 4px;
173
+ }
174
+
175
+ /* Sections */
176
+ .section {
177
+ margin-bottom: 48px;
178
+ }
179
+
180
+ .section-title {
181
+ font-size: 20px;
182
+ font-weight: 600;
183
+ margin-bottom: 24px;
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 12px;
187
+ }
188
+
189
+ .section-title::before {
190
+ content: '';
191
+ width: 4px;
192
+ height: 24px;
193
+ background: var(--accent);
194
+ border-radius: 2px;
195
+ }
196
+
197
+ /* Coverage */
198
+ .coverage-grid {
199
+ display: grid;
200
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
201
+ gap: 16px;
202
+ }
203
+
204
+ .coverage-item {
205
+ background: var(--bg-card);
206
+ border: 1px solid var(--border);
207
+ border-radius: var(--radius);
208
+ padding: 20px;
209
+ }
210
+
211
+ .coverage-item-header {
212
+ display: flex;
213
+ justify-content: space-between;
214
+ align-items: center;
215
+ margin-bottom: 12px;
216
+ }
217
+
218
+ .coverage-item-name {
219
+ font-size: 14px;
220
+ font-weight: 500;
221
+ }
222
+
223
+ .coverage-item-value {
224
+ font-size: 14px;
225
+ font-weight: 600;
226
+ }
227
+
228
+ .coverage-bar {
229
+ height: 8px;
230
+ background: var(--bg);
231
+ border-radius: 4px;
232
+ overflow: hidden;
233
+ }
234
+
235
+ .coverage-bar-fill {
236
+ height: 100%;
237
+ border-radius: 4px;
238
+ transition: width 0.5s ease;
239
+ }
240
+
241
+ /* Recommendations */
242
+ .recommendations-list {
243
+ display: flex;
244
+ flex-direction: column;
245
+ gap: 12px;
246
+ }
247
+
248
+ .recommendation {
249
+ background: var(--bg-card);
250
+ border: 1px solid var(--border);
251
+ border-radius: var(--radius);
252
+ padding: 20px;
253
+ border-left: 4px solid;
254
+ }
255
+
256
+ .recommendation.high { border-left-color: var(--danger); }
257
+ .recommendation.medium { border-left-color: var(--warning); }
258
+ .recommendation.low { border-left-color: var(--text-muted); }
259
+
260
+ .recommendation-header {
261
+ display: flex;
262
+ align-items: center;
263
+ gap: 12px;
264
+ margin-bottom: 8px;
265
+ }
266
+
267
+ .recommendation-priority {
268
+ font-size: 11px;
269
+ font-weight: 600;
270
+ text-transform: uppercase;
271
+ padding: 2px 8px;
272
+ border-radius: 4px;
273
+ }
274
+
275
+ .recommendation-priority.high { background: rgba(239, 68, 68, 0.2); color: var(--danger); }
276
+ .recommendation-priority.medium { background: rgba(234, 179, 8, 0.2); color: var(--warning); }
277
+ .recommendation-priority.low { background: rgba(113, 113, 122, 0.2); color: var(--text-muted); }
278
+
279
+ .recommendation-title {
280
+ font-weight: 600;
281
+ font-size: 15px;
282
+ }
283
+
284
+ .recommendation-description {
285
+ color: var(--text-secondary);
286
+ font-size: 14px;
287
+ margin-bottom: 12px;
288
+ }
289
+
290
+ .recommendation-components {
291
+ display: flex;
292
+ flex-wrap: wrap;
293
+ gap: 6px;
294
+ }
295
+
296
+ .component-tag {
297
+ font-size: 12px;
298
+ padding: 4px 10px;
299
+ background: var(--bg);
300
+ border-radius: 6px;
301
+ color: var(--text-secondary);
302
+ }
303
+
304
+ /* Component Table */
305
+ .category-section {
306
+ margin-bottom: 32px;
307
+ }
308
+
309
+ .category-header {
310
+ font-size: 14px;
311
+ font-weight: 600;
312
+ color: var(--text-secondary);
313
+ text-transform: uppercase;
314
+ letter-spacing: 0.05em;
315
+ margin-bottom: 12px;
316
+ padding-left: 12px;
317
+ border-left: 2px solid var(--accent);
318
+ }
319
+
320
+ .component-grid {
321
+ display: grid;
322
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
323
+ gap: 12px;
324
+ }
325
+
326
+ .component-card {
327
+ background: var(--bg-card);
328
+ border: 1px solid var(--border);
329
+ border-radius: var(--radius);
330
+ padding: 16px;
331
+ transition: border-color 0.2s;
332
+ }
333
+
334
+ .component-card:hover {
335
+ border-color: var(--accent);
336
+ }
337
+
338
+ .component-card-header {
339
+ display: flex;
340
+ justify-content: space-between;
341
+ align-items: center;
342
+ margin-bottom: 8px;
343
+ }
344
+
345
+ .component-name {
346
+ font-weight: 600;
347
+ font-size: 15px;
348
+ }
349
+
350
+ .component-score {
351
+ font-size: 13px;
352
+ font-weight: 600;
353
+ padding: 2px 8px;
354
+ border-radius: 4px;
355
+ }
356
+
357
+ .component-stats {
358
+ display: flex;
359
+ gap: 16px;
360
+ font-size: 13px;
361
+ color: var(--text-secondary);
362
+ }
363
+
364
+ .component-badges {
365
+ display: flex;
366
+ gap: 6px;
367
+ margin-top: 8px;
368
+ flex-wrap: wrap;
369
+ }
370
+
371
+ .badge {
372
+ font-size: 11px;
373
+ padding: 2px 8px;
374
+ border-radius: 4px;
375
+ background: var(--bg);
376
+ }
377
+
378
+ .badge.success { background: rgba(34, 197, 94, 0.2); color: var(--success); }
379
+ .badge.warning { background: rgba(234, 179, 8, 0.2); color: var(--warning); }
380
+ .badge.error { background: rgba(239, 68, 68, 0.2); color: var(--danger); }
381
+
382
+ /* Charts */
383
+ .chart-grid {
384
+ display: grid;
385
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
386
+ gap: 24px;
387
+ }
388
+
389
+ .chart-card {
390
+ background: var(--bg-card);
391
+ border: 1px solid var(--border);
392
+ border-radius: var(--radius);
393
+ padding: 24px;
394
+ }
395
+
396
+ .chart-title {
397
+ font-size: 14px;
398
+ font-weight: 600;
399
+ margin-bottom: 20px;
400
+ }
401
+
402
+ .bar-chart {
403
+ display: flex;
404
+ flex-direction: column;
405
+ gap: 12px;
406
+ }
407
+
408
+ .bar-item {
409
+ display: flex;
410
+ align-items: center;
411
+ gap: 12px;
412
+ }
413
+
414
+ .bar-label {
415
+ width: 100px;
416
+ font-size: 13px;
417
+ color: var(--text-secondary);
418
+ text-align: right;
419
+ flex-shrink: 0;
420
+ }
421
+
422
+ .bar-track {
423
+ flex: 1;
424
+ height: 24px;
425
+ background: var(--bg);
426
+ border-radius: 6px;
427
+ overflow: hidden;
428
+ position: relative;
429
+ }
430
+
431
+ .bar-fill {
432
+ height: 100%;
433
+ background: linear-gradient(90deg, var(--accent), var(--accent-light));
434
+ border-radius: 6px;
435
+ display: flex;
436
+ align-items: center;
437
+ justify-content: flex-end;
438
+ padding-right: 8px;
439
+ min-width: fit-content;
440
+ }
441
+
442
+ .bar-value {
443
+ font-size: 12px;
444
+ font-weight: 600;
445
+ color: white;
446
+ }
447
+
448
+ /* Quality Issues */
449
+ .issues-grid {
450
+ display: grid;
451
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
452
+ gap: 16px;
453
+ }
454
+
455
+ .issue-card {
456
+ background: var(--bg-card);
457
+ border: 1px solid var(--border);
458
+ border-radius: var(--radius);
459
+ padding: 20px;
460
+ }
461
+
462
+ .issue-card-header {
463
+ display: flex;
464
+ justify-content: space-between;
465
+ align-items: center;
466
+ margin-bottom: 12px;
467
+ }
468
+
469
+ .issue-title {
470
+ font-size: 14px;
471
+ font-weight: 500;
472
+ }
473
+
474
+ .issue-count {
475
+ font-size: 13px;
476
+ color: var(--text-secondary);
477
+ }
478
+
479
+ .issue-list {
480
+ display: flex;
481
+ flex-wrap: wrap;
482
+ gap: 6px;
483
+ }
484
+
485
+ /* Footer */
486
+ .footer {
487
+ margin-top: 64px;
488
+ padding-top: 32px;
489
+ border-top: 1px solid var(--border);
490
+ text-align: center;
491
+ color: var(--text-muted);
492
+ font-size: 13px;
493
+ }
494
+
495
+ .footer a {
496
+ color: var(--accent);
497
+ text-decoration: none;
498
+ }
499
+
500
+ /* Responsive */
501
+ @media (max-width: 768px) {
502
+ .header {
503
+ flex-direction: column;
504
+ gap: 24px;
505
+ }
506
+
507
+ .grade-badge {
508
+ align-self: flex-start;
509
+ }
510
+ }
511
+ `;
512
+ }
513
+
514
+ /**
515
+ * Render header section
516
+ */
517
+ function renderHeader(
518
+ summary: DesignSystemAnalytics["summary"],
519
+ grade: string,
520
+ gradeColor: string,
521
+ analyzedAt: Date
522
+ ): string {
523
+ return `
524
+ <header class="header">
525
+ <div class="header-left">
526
+ <h1>${BRAND.name} Design System Report</h1>
527
+ <p>Generated ${analyzedAt.toLocaleDateString("en-US", {
528
+ weekday: "long",
529
+ year: "numeric",
530
+ month: "long",
531
+ day: "numeric",
532
+ hour: "2-digit",
533
+ minute: "2-digit",
534
+ })}</p>
535
+ </div>
536
+ <div class="grade-badge">
537
+ <div class="grade-letter" style="color: ${gradeColor}">${grade}</div>
538
+ <div class="grade-score">${summary.overallScore}/100 Overall Score</div>
539
+ </div>
540
+ </header>
541
+ `;
542
+ }
543
+
544
+ /**
545
+ * Render summary cards
546
+ */
547
+ function renderSummaryCards(
548
+ summary: DesignSystemAnalytics["summary"],
549
+ coverage: DesignSystemAnalytics["coverage"]
550
+ ): string {
551
+ return `
552
+ <div class="summary-grid">
553
+ <div class="summary-card">
554
+ <div class="summary-card-label">Components</div>
555
+ <div class="summary-card-value">${summary.totalComponents}</div>
556
+ <div class="summary-card-sub">${summary.categories.length} categories</div>
557
+ </div>
558
+ <div class="summary-card">
559
+ <div class="summary-card-label">Variants</div>
560
+ <div class="summary-card-value">${summary.totalVariants}</div>
561
+ <div class="summary-card-sub">~${Math.round(summary.totalVariants / Math.max(summary.totalComponents, 1))} per component</div>
562
+ </div>
563
+ <div class="summary-card">
564
+ <div class="summary-card-label">Props</div>
565
+ <div class="summary-card-value">${summary.totalProps}</div>
566
+ <div class="summary-card-sub">${coverage.fields.propDescriptions.percentage}% documented</div>
567
+ </div>
568
+ <div class="summary-card">
569
+ <div class="summary-card-label">Coverage</div>
570
+ <div class="summary-card-value" style="color: ${getScoreColor(coverage.overall)}">${coverage.overall}%</div>
571
+ <div class="summary-card-sub">Documentation coverage</div>
572
+ </div>
573
+ </div>
574
+ `;
575
+ }
576
+
577
+ /**
578
+ * Render coverage section
579
+ */
580
+ function renderCoverageSection(coverage: DesignSystemAnalytics["coverage"]): string {
581
+ const fields = [
582
+ { name: "Component Descriptions", ...coverage.fields.description },
583
+ { name: 'Usage "When to use"', ...coverage.fields.usageWhen },
584
+ { name: 'Usage "When NOT to use"', ...coverage.fields.usageWhenNot },
585
+ { name: "Guidelines", ...coverage.fields.guidelines },
586
+ { name: "Accessibility Notes", ...coverage.fields.accessibility },
587
+ { name: "Component Relations", ...coverage.fields.relations },
588
+ { name: "Prop Descriptions", ...coverage.fields.propDescriptions },
589
+ { name: "Prop Constraints", ...coverage.fields.propConstraints },
590
+ ];
591
+
592
+ return `
593
+ <section class="section">
594
+ <h2 class="section-title">Documentation Coverage</h2>
595
+ <div class="coverage-grid">
596
+ ${fields
597
+ .map(
598
+ (field) => `
599
+ <div class="coverage-item">
600
+ <div class="coverage-item-header">
601
+ <span class="coverage-item-name">${field.name}</span>
602
+ <span class="coverage-item-value" style="color: ${getScoreColor(field.percentage)}">${field.covered}/${field.total} (${field.percentage}%)</span>
603
+ </div>
604
+ <div class="coverage-bar">
605
+ <div class="coverage-bar-fill" style="width: ${field.percentage}%; background: ${getScoreColor(field.percentage)}"></div>
606
+ </div>
607
+ </div>
608
+ `
609
+ )
610
+ .join("")}
611
+ </div>
612
+ </section>
613
+ `;
614
+ }
615
+
616
+ /**
617
+ * Render recommendations
618
+ */
619
+ function renderRecommendations(recommendations: Recommendation[]): string {
620
+ if (recommendations.length === 0) {
621
+ return `
622
+ <section class="section">
623
+ <h2 class="section-title">Recommendations</h2>
624
+ <div class="recommendation" style="border-left-color: var(--success)">
625
+ <div class="recommendation-title">🎉 Great job!</div>
626
+ <div class="recommendation-description">Your design system documentation is in excellent shape. No critical issues found.</div>
627
+ </div>
628
+ </section>
629
+ `;
630
+ }
631
+
632
+ return `
633
+ <section class="section">
634
+ <h2 class="section-title">Recommendations</h2>
635
+ <div class="recommendations-list">
636
+ ${recommendations
637
+ .map(
638
+ (rec) => `
639
+ <div class="recommendation ${rec.priority}">
640
+ <div class="recommendation-header">
641
+ <span class="recommendation-priority ${rec.priority}">${rec.priority}</span>
642
+ <span class="recommendation-title">${rec.title}</span>
643
+ </div>
644
+ <div class="recommendation-description">${rec.description}</div>
645
+ ${
646
+ rec.components && rec.components.length > 0
647
+ ? `
648
+ <div class="recommendation-components">
649
+ ${rec.components.map((c) => `<span class="component-tag">${c}</span>`).join("")}
650
+ ${rec.components.length < (rec.components?.length ?? 0) ? `<span class="component-tag">+${(rec.components?.length ?? 0) - rec.components.length} more</span>` : ""}
651
+ </div>
652
+ `
653
+ : ""
654
+ }
655
+ </div>
656
+ `
657
+ )
658
+ .join("")}
659
+ </div>
660
+ </section>
661
+ `;
662
+ }
663
+
664
+ /**
665
+ * Render component table by category
666
+ */
667
+ function renderComponentTable(
668
+ byCategory: Record<string, ComponentSummary[]>
669
+ ): string {
670
+ const categories = Object.entries(byCategory).sort(([a], [b]) =>
671
+ a.localeCompare(b)
672
+ );
673
+
674
+ return `
675
+ <section class="section">
676
+ <h2 class="section-title">Component Inventory</h2>
677
+ ${categories
678
+ .map(
679
+ ([category, components]) => `
680
+ <div class="category-section">
681
+ <div class="category-header">${category} (${components.length})</div>
682
+ <div class="component-grid">
683
+ ${components
684
+ .map(
685
+ (comp) => `
686
+ <div class="component-card">
687
+ <div class="component-card-header">
688
+ <span class="component-name">${comp.name}</span>
689
+ <span class="component-score" style="background: ${getScoreColor(comp.documentationScore)}20; color: ${getScoreColor(comp.documentationScore)}">${comp.documentationScore}%</span>
690
+ </div>
691
+ <div class="component-stats">
692
+ <span>${comp.variantCount} variants</span>
693
+ <span>${comp.propCount} props</span>
694
+ </div>
695
+ <div class="component-badges">
696
+ ${comp.hasUsageWhen ? '<span class="badge success">✓ when</span>' : '<span class="badge error">✗ when</span>'}
697
+ ${comp.hasUsageWhenNot ? '<span class="badge success">✓ whenNot</span>' : '<span class="badge error">✗ whenNot</span>'}
698
+ ${comp.hasRelations ? '<span class="badge success">✓ relations</span>' : '<span class="badge warning">○ relations</span>'}
699
+ </div>
700
+ </div>
701
+ `
702
+ )
703
+ .join("")}
704
+ </div>
705
+ </div>
706
+ `
707
+ )
708
+ .join("")}
709
+ </section>
710
+ `;
711
+ }
712
+
713
+ /**
714
+ * Render distribution charts
715
+ */
716
+ function renderDistributionCharts(
717
+ distribution: DesignSystemAnalytics["distribution"]
718
+ ): string {
719
+ const maxVariants = Math.max(...distribution.variantsPerComponent.map((d) => d.count), 1);
720
+ const maxCategory = Math.max(...distribution.componentsPerCategory.map((d) => d.count), 1);
721
+
722
+ return `
723
+ <section class="section">
724
+ <h2 class="section-title">Distribution</h2>
725
+ <div class="chart-grid">
726
+ <div class="chart-card">
727
+ <div class="chart-title">Variants per Component</div>
728
+ <div class="bar-chart">
729
+ ${distribution.variantsPerComponent
730
+ .slice(0, 8)
731
+ .map(
732
+ (d) => `
733
+ <div class="bar-item">
734
+ <span class="bar-label">${d.name}</span>
735
+ <div class="bar-track">
736
+ <div class="bar-fill" style="width: ${(d.count / maxVariants) * 100}%">
737
+ <span class="bar-value">${d.count}</span>
738
+ </div>
739
+ </div>
740
+ </div>
741
+ `
742
+ )
743
+ .join("")}
744
+ </div>
745
+ </div>
746
+ <div class="chart-card">
747
+ <div class="chart-title">Components per Category</div>
748
+ <div class="bar-chart">
749
+ ${distribution.componentsPerCategory
750
+ .map(
751
+ (d) => `
752
+ <div class="bar-item">
753
+ <span class="bar-label">${d.category}</span>
754
+ <div class="bar-track">
755
+ <div class="bar-fill" style="width: ${(d.count / maxCategory) * 100}%">
756
+ <span class="bar-value">${d.count}</span>
757
+ </div>
758
+ </div>
759
+ </div>
760
+ `
761
+ )
762
+ .join("")}
763
+ </div>
764
+ </div>
765
+ </div>
766
+ </section>
767
+ `;
768
+ }
769
+
770
+ /**
771
+ * Render quality issues
772
+ */
773
+ function renderQualityIssues(quality: DesignSystemAnalytics["quality"]): string {
774
+ const issues = [
775
+ {
776
+ title: "Missing 'When NOT to use'",
777
+ items: quality.missingWhenNot,
778
+ severity: "error",
779
+ },
780
+ {
781
+ title: "No Relations Defined",
782
+ items: quality.isolated,
783
+ severity: "warning",
784
+ },
785
+ {
786
+ title: "Few Variants (<2)",
787
+ items: quality.fewVariants,
788
+ severity: "warning",
789
+ },
790
+ {
791
+ title: "Deprecated Components",
792
+ items: quality.deprecated,
793
+ severity: "error",
794
+ },
795
+ ].filter((issue) => issue.items.length > 0);
796
+
797
+ if (issues.length === 0) {
798
+ return "";
799
+ }
800
+
801
+ return `
802
+ <section class="section">
803
+ <h2 class="section-title">Quality Issues</h2>
804
+ <div class="issues-grid">
805
+ ${issues
806
+ .map(
807
+ (issue) => `
808
+ <div class="issue-card">
809
+ <div class="issue-card-header">
810
+ <span class="issue-title">${issue.title}</span>
811
+ <span class="issue-count">${issue.items.length} component(s)</span>
812
+ </div>
813
+ <div class="issue-list">
814
+ ${issue.items
815
+ .slice(0, 10)
816
+ .map((item) => `<span class="component-tag">${item}</span>`)
817
+ .join("")}
818
+ ${issue.items.length > 10 ? `<span class="component-tag">+${issue.items.length - 10} more</span>` : ""}
819
+ </div>
820
+ </div>
821
+ `
822
+ )
823
+ .join("")}
824
+ </div>
825
+ </section>
826
+ `;
827
+ }
828
+
829
+ /**
830
+ * Render footer
831
+ */
832
+ function renderFooter(): string {
833
+ return `
834
+ <footer class="footer">
835
+ <p>Generated by <a href="#">${BRAND.name}</a> • AI-first design system documentation</p>
836
+ </footer>
837
+ `;
838
+ }
839
+
840
+ /**
841
+ * Get JavaScript for interactivity
842
+ */
843
+ function getScripts(): string {
844
+ return `
845
+ // Animate bars on load
846
+ document.querySelectorAll('.bar-fill, .coverage-bar-fill').forEach(el => {
847
+ const width = el.style.width;
848
+ el.style.width = '0';
849
+ setTimeout(() => {
850
+ el.style.width = width;
851
+ }, 100);
852
+ });
853
+ `;
854
+ }