@fragments-sdk/cli 0.5.1 → 0.6.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/dist/bin.js +712 -39
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
- package/dist/chunk-6JBGU74P.js.map +1 -0
- package/dist/{chunk-U4GQ2JTD.js → chunk-D35RGPAG.js} +412 -35
- package/dist/chunk-D35RGPAG.js.map +1 -0
- package/dist/{chunk-XNWDI6UT.js → chunk-F7ITZPDJ.js} +5 -5
- package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
- package/dist/{chunk-V7YLRR4C.js → chunk-Q7GOHVOK.js} +3 -3
- package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
- package/dist/{chunk-2H2JAA3U.js → chunk-SSLQXHNX.js} +3 -3
- package/dist/{core-DKHB7FYV.js → core-SKRPJQZG.js} +4 -4
- package/dist/{generate-KL24VZVD.js → generate-7AF7WRVK.js} +5 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-NZB55B5O.js → init-WKGDPYI4.js} +7 -7
- package/dist/init-WKGDPYI4.js.map +1 -0
- package/dist/mcp-bin.js +8 -220
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-K6JNMCGM.js +12 -0
- package/dist/{service-RWUMZ3EW.js → service-F3E4JJM7.js} +5 -5
- package/dist/static-viewer-4LQZ5AGA.js +12 -0
- package/dist/{test-ECPEXFDN.js → test-CJDNJTPZ.js} +4 -4
- package/dist/{tokens-ITADYVPF.js → tokens-JAJABYXP.js} +6 -6
- package/dist/viewer-R3Q6WAMJ.js +1822 -0
- package/dist/viewer-R3Q6WAMJ.js.map +1 -0
- package/package.json +5 -4
- package/src/bin.ts +8 -0
- package/src/build.ts +104 -13
- package/src/cli-commands.ts +18 -0
- package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
- package/src/commands/a11y-report.ts +625 -0
- package/src/commands/a11y.ts +168 -14
- package/src/commands/build.ts +16 -0
- package/src/commands/init.ts +2 -2
- package/src/core/auto-props.ts +464 -0
- package/src/core/schema.ts +2 -0
- package/src/core/types.ts +3 -1
- package/src/index.ts +4 -0
- package/src/mcp/server.ts +13 -220
- package/src/theme/__tests__/component-contrast.test.ts +338 -0
- package/src/theme/__tests__/contrast-validation.test.ts +326 -0
- package/src/theme/contrast.test.ts +331 -0
- package/src/theme/contrast.ts +246 -0
- package/src/theme/generator.ts +213 -1
- package/src/theme/index.ts +16 -0
- package/src/theme/types.ts +51 -0
- package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
- package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
- package/src/viewer/components/AccessibilityPanel.tsx +493 -433
- package/src/viewer/components/ActionCapture.tsx +1 -1
- package/src/viewer/components/ActionsPanel.tsx +142 -183
- package/src/viewer/components/App.tsx +159 -164
- package/src/viewer/components/BottomPanel.tsx +40 -80
- package/src/viewer/components/CodePanel.tsx +9 -87
- package/src/viewer/components/CommandPalette.tsx +117 -74
- package/src/viewer/components/ComponentGraph.tsx +143 -126
- package/src/viewer/components/ComponentHeader.tsx +46 -43
- package/src/viewer/components/ContractPanel.tsx +124 -117
- package/src/viewer/components/ErrorBoundary.tsx +47 -35
- package/src/viewer/components/FigmaEmbed.tsx +18 -13
- package/src/viewer/components/FragmentEditor.tsx +126 -63
- package/src/viewer/components/HealthDashboard.tsx +146 -171
- package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
- package/src/viewer/components/Icons.tsx +99 -98
- package/src/viewer/components/InteractionsPanel.tsx +317 -264
- package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
- package/src/viewer/components/IsolatedRender.tsx +12 -6
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
- package/src/viewer/components/LandingPage.tsx +285 -305
- package/src/viewer/components/Layout.tsx +7 -9
- package/src/viewer/components/LeftSidebar.tsx +78 -108
- package/src/viewer/components/MultiViewportPreview.tsx +254 -63
- package/src/viewer/components/PreviewArea.tsx +113 -44
- package/src/viewer/components/PreviewFrameHost.tsx +6 -5
- package/src/viewer/components/PreviewPane.tsx +2 -3
- package/src/viewer/components/PreviewToolbar.tsx +61 -104
- package/src/viewer/components/PropsEditor.tsx +154 -74
- package/src/viewer/components/PropsTable.tsx +95 -82
- package/src/viewer/components/RelationsSection.tsx +71 -40
- package/src/viewer/components/ResizablePanel.tsx +158 -55
- package/src/viewer/components/RightSidebar.tsx +46 -56
- package/src/viewer/components/ScreenshotButton.tsx +12 -12
- package/src/viewer/components/SkeletonLoader.tsx +99 -83
- package/src/viewer/components/StoryRenderer.tsx +4 -11
- package/src/viewer/components/Toast.tsx +3 -67
- package/src/viewer/components/TokenStylePanel.tsx +136 -118
- package/src/viewer/components/UsageSection.tsx +26 -26
- package/src/viewer/components/VariantMatrix.tsx +140 -47
- package/src/viewer/components/VariantTabs.tsx +24 -68
- package/src/viewer/components/ViewportSelector.tsx +106 -110
- package/src/viewer/constants/ui.ts +19 -18
- package/src/viewer/entry.tsx +8 -3
- package/src/viewer/index.ts +3 -6
- package/src/viewer/preview-frame.html +21 -5
- package/src/viewer/server.ts +7 -16
- package/src/viewer/styles/globals.css +4 -4
- package/src/viewer/utils/a11y-fixes.ts +53 -30
- package/dist/chunk-ICAIQ57V.js.map +0 -1
- package/dist/chunk-U4GQ2JTD.js.map +0 -1
- package/dist/init-NZB55B5O.js.map +0 -1
- package/dist/scan-ESEXV7LF.js +0 -12
- package/dist/static-viewer-O37MJ5B6.js +0 -12
- package/dist/viewer-YDGFDTK5.js +0 -11104
- package/dist/viewer-YDGFDTK5.js.map +0 -1
- package/src/viewer/postcss.config.js +0 -6
- package/src/viewer/tailwind.config.js +0 -37
- /package/dist/{chunk-XNWDI6UT.js.map → chunk-F7ITZPDJ.js.map} +0 -0
- /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
- /package/dist/{chunk-V7YLRR4C.js.map → chunk-Q7GOHVOK.js.map} +0 -0
- /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
- /package/dist/{chunk-2H2JAA3U.js.map → chunk-SSLQXHNX.js.map} +0 -0
- /package/dist/{core-DKHB7FYV.js.map → core-SKRPJQZG.js.map} +0 -0
- /package/dist/{generate-KL24VZVD.js.map → generate-7AF7WRVK.js.map} +0 -0
- /package/dist/{scan-ESEXV7LF.js.map → scan-K6JNMCGM.js.map} +0 -0
- /package/dist/{service-RWUMZ3EW.js.map → service-F3E4JJM7.js.map} +0 -0
- /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-4LQZ5AGA.js.map} +0 -0
- /package/dist/{test-ECPEXFDN.js.map → test-CJDNJTPZ.js.map} +0 -0
- /package/dist/{tokens-ITADYVPF.js.map → tokens-JAJABYXP.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createRequire } from 'module'; const require =
|
|
2
|
+
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
buildFragmentsDir,
|
|
5
5
|
buildSegments,
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
validateAll,
|
|
10
10
|
validateCoverage,
|
|
11
11
|
validateSchema
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-D35RGPAG.js";
|
|
13
13
|
import {
|
|
14
14
|
scan
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-F7ITZPDJ.js";
|
|
16
16
|
import {
|
|
17
17
|
FigmaClient,
|
|
18
18
|
StorageManager,
|
|
@@ -28,18 +28,18 @@ import {
|
|
|
28
28
|
renderAllComponentVariants,
|
|
29
29
|
scanCodebase,
|
|
30
30
|
shutdownSharedPool
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-NWQ4CJOQ.js";
|
|
32
32
|
import {
|
|
33
33
|
discoverSegmentFiles,
|
|
34
34
|
loadConfig,
|
|
35
35
|
loadSegmentFile
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-SSLQXHNX.js";
|
|
37
37
|
import {
|
|
38
38
|
generateContext
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-Q7GOHVOK.js";
|
|
40
40
|
import {
|
|
41
41
|
BRAND
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-6JBGU74P.js";
|
|
43
43
|
|
|
44
44
|
// src/bin.ts
|
|
45
45
|
import { Command } from "commander";
|
|
@@ -145,6 +145,13 @@ ${BRAND.name} Build
|
|
|
145
145
|
}
|
|
146
146
|
console.log();
|
|
147
147
|
}
|
|
148
|
+
if (result.warnings.length > 0) {
|
|
149
|
+
console.log(pc2.yellow("Build warnings:\n"));
|
|
150
|
+
for (const warning of result.warnings) {
|
|
151
|
+
console.log(` ${pc2.yellow("\u26A0")} ${warning.file}: ${warning.warning}`);
|
|
152
|
+
}
|
|
153
|
+
console.log();
|
|
154
|
+
}
|
|
148
155
|
segmentCount = result.segmentCount;
|
|
149
156
|
outputPath = result.outputPath;
|
|
150
157
|
console.log(pc2.green(`\u2713 Built ${result.segmentCount} fragment(s)`));
|
|
@@ -162,6 +169,13 @@ ${BRAND.name} Build
|
|
|
162
169
|
}
|
|
163
170
|
console.log();
|
|
164
171
|
}
|
|
172
|
+
if (fragmentsResult.warnings.length > 0) {
|
|
173
|
+
console.log(pc2.yellow("Registry warnings:\n"));
|
|
174
|
+
for (const warning of fragmentsResult.warnings) {
|
|
175
|
+
console.log(` ${pc2.yellow("\u26A0")} ${warning.file}: ${warning.warning}`);
|
|
176
|
+
}
|
|
177
|
+
console.log();
|
|
178
|
+
}
|
|
165
179
|
componentCount = fragmentsResult.componentCount;
|
|
166
180
|
registryPath = fragmentsResult.registryPath;
|
|
167
181
|
contextPath = fragmentsResult.contextPath;
|
|
@@ -1614,19 +1628,19 @@ async function discoverStoryFiles(projectRoot, patterns) {
|
|
|
1614
1628
|
|
|
1615
1629
|
// src/setup.ts
|
|
1616
1630
|
async function isSegmentsJsonStale(configDir, outFile) {
|
|
1617
|
-
const
|
|
1631
|
+
const fs2 = await import("fs/promises");
|
|
1618
1632
|
const path = await import("path");
|
|
1619
1633
|
const fg4 = await import("fast-glob");
|
|
1620
1634
|
const segmentsJsonPath = path.join(configDir, outFile);
|
|
1621
1635
|
try {
|
|
1622
|
-
const segmentsJsonStat = await
|
|
1636
|
+
const segmentsJsonStat = await fs2.stat(segmentsJsonPath);
|
|
1623
1637
|
const segmentFiles = await fg4.default(`**/*${BRAND.fileExtension}`, {
|
|
1624
1638
|
cwd: configDir,
|
|
1625
1639
|
ignore: ["**/node_modules/**"],
|
|
1626
1640
|
absolute: true
|
|
1627
1641
|
});
|
|
1628
1642
|
for (const file of segmentFiles) {
|
|
1629
|
-
const stat3 = await
|
|
1643
|
+
const stat3 = await fs2.stat(file);
|
|
1630
1644
|
if (stat3.mtimeMs > segmentsJsonStat.mtimeMs) {
|
|
1631
1645
|
return { stale: true, missing: false };
|
|
1632
1646
|
}
|
|
@@ -1637,11 +1651,11 @@ async function isSegmentsJsonStale(configDir, outFile) {
|
|
|
1637
1651
|
}
|
|
1638
1652
|
}
|
|
1639
1653
|
async function loadSegmentInfo(segmentFiles) {
|
|
1640
|
-
const
|
|
1654
|
+
const fs2 = await import("fs/promises");
|
|
1641
1655
|
const segments = [];
|
|
1642
1656
|
for (const file of segmentFiles) {
|
|
1643
1657
|
try {
|
|
1644
|
-
const content = await
|
|
1658
|
+
const content = await fs2.readFile(file.absolutePath, "utf-8");
|
|
1645
1659
|
const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/);
|
|
1646
1660
|
const hasFigma = /meta:\s*\{[^}]*figma:\s*['"]https?:/.test(content);
|
|
1647
1661
|
if (nameMatch) {
|
|
@@ -1657,7 +1671,7 @@ async function loadSegmentInfo(segmentFiles) {
|
|
|
1657
1671
|
return segments;
|
|
1658
1672
|
}
|
|
1659
1673
|
async function runSetup(options = {}) {
|
|
1660
|
-
const
|
|
1674
|
+
const fs2 = await import("fs/promises");
|
|
1661
1675
|
const path = await import("path");
|
|
1662
1676
|
const result = {
|
|
1663
1677
|
segmentFilesCreated: 0,
|
|
@@ -1685,8 +1699,8 @@ async function runSetup(options = {}) {
|
|
|
1685
1699
|
try {
|
|
1686
1700
|
const parsed = await parseStoryFile(storyFile);
|
|
1687
1701
|
const segmentResult = convertToSegment(parsed);
|
|
1688
|
-
await
|
|
1689
|
-
await
|
|
1702
|
+
await fs2.mkdir(path.dirname(segmentResult.outputFile), { recursive: true });
|
|
1703
|
+
await fs2.writeFile(segmentResult.outputFile, segmentResult.code);
|
|
1690
1704
|
converted++;
|
|
1691
1705
|
} catch {
|
|
1692
1706
|
}
|
|
@@ -1771,7 +1785,7 @@ ${BRAND.name} Dev Server
|
|
|
1771
1785
|
}
|
|
1772
1786
|
}
|
|
1773
1787
|
}
|
|
1774
|
-
const { createDevServer } = await import("./viewer-
|
|
1788
|
+
const { createDevServer } = await import("./viewer-R3Q6WAMJ.js");
|
|
1775
1789
|
console.log(pc7.dim("\nStarting dev server..."));
|
|
1776
1790
|
const parsedPort = typeof port === "string" ? parseInt(port, 10) : port;
|
|
1777
1791
|
try {
|
|
@@ -2402,13 +2416,636 @@ ${BRAND.name} Design System Audit
|
|
|
2402
2416
|
}
|
|
2403
2417
|
|
|
2404
2418
|
// src/commands/a11y.ts
|
|
2419
|
+
import fs from "fs";
|
|
2405
2420
|
import pc12 from "picocolors";
|
|
2421
|
+
|
|
2422
|
+
// src/commands/a11y-report.ts
|
|
2423
|
+
function generateA11yReport(summary) {
|
|
2424
|
+
const score = summary.score ?? calculateA11yScore(summary);
|
|
2425
|
+
return `<!DOCTYPE html>
|
|
2426
|
+
<html lang="en">
|
|
2427
|
+
<head>
|
|
2428
|
+
<meta charset="UTF-8">
|
|
2429
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2430
|
+
<title>${BRAND.name} Accessibility Report</title>
|
|
2431
|
+
<style>
|
|
2432
|
+
${getStyles()}
|
|
2433
|
+
</style>
|
|
2434
|
+
</head>
|
|
2435
|
+
<body>
|
|
2436
|
+
<div class="container">
|
|
2437
|
+
${renderHeader(score)}
|
|
2438
|
+
${renderSummaryCards(summary, score)}
|
|
2439
|
+
${renderComponentGrid(summary.components)}
|
|
2440
|
+
${renderFooter()}
|
|
2441
|
+
</div>
|
|
2442
|
+
<script>
|
|
2443
|
+
${getScripts()}
|
|
2444
|
+
</script>
|
|
2445
|
+
</body>
|
|
2446
|
+
</html>`;
|
|
2447
|
+
}
|
|
2448
|
+
function getStyles() {
|
|
2449
|
+
return `
|
|
2450
|
+
:root {
|
|
2451
|
+
--bg: #0a0a0a;
|
|
2452
|
+
--bg-card: #141414;
|
|
2453
|
+
--bg-hover: #1a1a1a;
|
|
2454
|
+
--border: #262626;
|
|
2455
|
+
--text: #f2f2f2;
|
|
2456
|
+
--text-secondary: #a1a1aa;
|
|
2457
|
+
--text-muted: #71717a;
|
|
2458
|
+
--accent: #10b981;
|
|
2459
|
+
--accent-light: #34d399;
|
|
2460
|
+
--danger: #ef4444;
|
|
2461
|
+
--warning: #eab308;
|
|
2462
|
+
--success: #22c55e;
|
|
2463
|
+
--radius: 12px;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
@media (prefers-color-scheme: light) {
|
|
2467
|
+
:root:not(.dark) {
|
|
2468
|
+
--bg: #f8f8f8;
|
|
2469
|
+
--bg-card: #ffffff;
|
|
2470
|
+
--bg-hover: #f0f0f0;
|
|
2471
|
+
--border: #e0e0e0;
|
|
2472
|
+
--text: #171717;
|
|
2473
|
+
--text-secondary: #525252;
|
|
2474
|
+
--text-muted: #737373;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
.dark {
|
|
2479
|
+
--bg: #0a0a0a;
|
|
2480
|
+
--bg-card: #141414;
|
|
2481
|
+
--bg-hover: #1a1a1a;
|
|
2482
|
+
--border: #262626;
|
|
2483
|
+
--text: #f2f2f2;
|
|
2484
|
+
--text-secondary: #a1a1aa;
|
|
2485
|
+
--text-muted: #71717a;
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
.light {
|
|
2489
|
+
--bg: #f8f8f8;
|
|
2490
|
+
--bg-card: #ffffff;
|
|
2491
|
+
--bg-hover: #f0f0f0;
|
|
2492
|
+
--border: #e0e0e0;
|
|
2493
|
+
--text: #171717;
|
|
2494
|
+
--text-secondary: #525252;
|
|
2495
|
+
--text-muted: #737373;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
* {
|
|
2499
|
+
margin: 0;
|
|
2500
|
+
padding: 0;
|
|
2501
|
+
box-sizing: border-box;
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
body {
|
|
2505
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
2506
|
+
background: var(--bg);
|
|
2507
|
+
color: var(--text);
|
|
2508
|
+
line-height: 1.6;
|
|
2509
|
+
-webkit-font-smoothing: antialiased;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
.container {
|
|
2513
|
+
max-width: 1200px;
|
|
2514
|
+
margin: 0 auto;
|
|
2515
|
+
padding: 48px 24px;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
/* Header */
|
|
2519
|
+
.header {
|
|
2520
|
+
display: flex;
|
|
2521
|
+
justify-content: space-between;
|
|
2522
|
+
align-items: flex-start;
|
|
2523
|
+
margin-bottom: 48px;
|
|
2524
|
+
padding-bottom: 32px;
|
|
2525
|
+
border-bottom: 1px solid var(--border);
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
.header-left h1 {
|
|
2529
|
+
font-size: 32px;
|
|
2530
|
+
font-weight: 700;
|
|
2531
|
+
margin-bottom: 8px;
|
|
2532
|
+
background: linear-gradient(135deg, var(--text), var(--accent));
|
|
2533
|
+
-webkit-background-clip: text;
|
|
2534
|
+
-webkit-text-fill-color: transparent;
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
.header-left p {
|
|
2538
|
+
color: var(--text-secondary);
|
|
2539
|
+
font-size: 14px;
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
.header-right {
|
|
2543
|
+
display: flex;
|
|
2544
|
+
align-items: flex-start;
|
|
2545
|
+
gap: 16px;
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
.theme-toggle {
|
|
2549
|
+
background: var(--bg-card);
|
|
2550
|
+
border: 1px solid var(--border);
|
|
2551
|
+
border-radius: 8px;
|
|
2552
|
+
color: var(--text-secondary);
|
|
2553
|
+
cursor: pointer;
|
|
2554
|
+
padding: 8px 12px;
|
|
2555
|
+
font-size: 14px;
|
|
2556
|
+
line-height: 1;
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
.theme-toggle:hover {
|
|
2560
|
+
border-color: var(--accent);
|
|
2561
|
+
color: var(--text);
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
.grade-badge {
|
|
2565
|
+
display: flex;
|
|
2566
|
+
flex-direction: column;
|
|
2567
|
+
align-items: center;
|
|
2568
|
+
padding: 24px 32px;
|
|
2569
|
+
background: var(--bg-card);
|
|
2570
|
+
border: 1px solid var(--border);
|
|
2571
|
+
border-radius: var(--radius);
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
.grade-score-large {
|
|
2575
|
+
font-size: 56px;
|
|
2576
|
+
font-weight: 800;
|
|
2577
|
+
line-height: 1;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
.grade-label {
|
|
2581
|
+
font-size: 14px;
|
|
2582
|
+
color: var(--text-secondary);
|
|
2583
|
+
margin-top: 8px;
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
/* Summary Cards */
|
|
2587
|
+
.summary-grid {
|
|
2588
|
+
display: grid;
|
|
2589
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
2590
|
+
gap: 16px;
|
|
2591
|
+
margin-bottom: 48px;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
.summary-card {
|
|
2595
|
+
background: var(--bg-card);
|
|
2596
|
+
border: 1px solid var(--border);
|
|
2597
|
+
border-radius: var(--radius);
|
|
2598
|
+
padding: 24px;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
.summary-card-label {
|
|
2602
|
+
font-size: 13px;
|
|
2603
|
+
color: var(--text-muted);
|
|
2604
|
+
text-transform: uppercase;
|
|
2605
|
+
letter-spacing: 0.05em;
|
|
2606
|
+
margin-bottom: 8px;
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
.summary-card-value {
|
|
2610
|
+
font-size: 36px;
|
|
2611
|
+
font-weight: 700;
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
.summary-card-sub {
|
|
2615
|
+
font-size: 13px;
|
|
2616
|
+
color: var(--text-secondary);
|
|
2617
|
+
margin-top: 4px;
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
/* Section */
|
|
2621
|
+
.section {
|
|
2622
|
+
margin-bottom: 48px;
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
.section-title {
|
|
2626
|
+
font-size: 20px;
|
|
2627
|
+
font-weight: 600;
|
|
2628
|
+
margin-bottom: 24px;
|
|
2629
|
+
display: flex;
|
|
2630
|
+
align-items: center;
|
|
2631
|
+
gap: 12px;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
.section-title::before {
|
|
2635
|
+
content: '';
|
|
2636
|
+
width: 4px;
|
|
2637
|
+
height: 24px;
|
|
2638
|
+
background: var(--accent);
|
|
2639
|
+
border-radius: 2px;
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
/* Component Grid */
|
|
2643
|
+
.component-grid {
|
|
2644
|
+
display: grid;
|
|
2645
|
+
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
|
|
2646
|
+
gap: 16px;
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
.component-card {
|
|
2650
|
+
background: var(--bg-card);
|
|
2651
|
+
border: 1px solid var(--border);
|
|
2652
|
+
border-radius: var(--radius);
|
|
2653
|
+
padding: 20px;
|
|
2654
|
+
transition: border-color 0.2s;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
.component-card:hover {
|
|
2658
|
+
border-color: var(--accent);
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
.component-card-header {
|
|
2662
|
+
display: flex;
|
|
2663
|
+
justify-content: space-between;
|
|
2664
|
+
align-items: center;
|
|
2665
|
+
margin-bottom: 12px;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
.component-name {
|
|
2669
|
+
font-weight: 600;
|
|
2670
|
+
font-size: 16px;
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
.status-badge {
|
|
2674
|
+
font-size: 12px;
|
|
2675
|
+
font-weight: 700;
|
|
2676
|
+
padding: 3px 10px;
|
|
2677
|
+
border-radius: 6px;
|
|
2678
|
+
text-transform: uppercase;
|
|
2679
|
+
letter-spacing: 0.03em;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
.status-pass { background: rgba(34, 197, 94, 0.15); color: #22c55e; }
|
|
2683
|
+
.status-warn { background: rgba(234, 179, 8, 0.15); color: #eab308; }
|
|
2684
|
+
.status-fail { background: rgba(239, 68, 68, 0.15); color: #ef4444; }
|
|
2685
|
+
|
|
2686
|
+
.component-stats {
|
|
2687
|
+
display: flex;
|
|
2688
|
+
gap: 16px;
|
|
2689
|
+
font-size: 13px;
|
|
2690
|
+
color: var(--text-secondary);
|
|
2691
|
+
margin-bottom: 8px;
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
.severity-bar {
|
|
2695
|
+
display: flex;
|
|
2696
|
+
gap: 8px;
|
|
2697
|
+
flex-wrap: wrap;
|
|
2698
|
+
margin-bottom: 12px;
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
.severity-chip {
|
|
2702
|
+
font-size: 12px;
|
|
2703
|
+
padding: 3px 10px;
|
|
2704
|
+
border-radius: 6px;
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
.severity-critical { background: rgba(239, 68, 68, 0.15); color: #ef4444; }
|
|
2708
|
+
.severity-serious { background: rgba(239, 68, 68, 0.10); color: #f87171; }
|
|
2709
|
+
.severity-moderate { background: rgba(234, 179, 8, 0.15); color: #eab308; }
|
|
2710
|
+
.severity-minor { background: rgba(113, 113, 122, 0.15); color: var(--text-muted); }
|
|
2711
|
+
|
|
2712
|
+
/* Expandable violation details */
|
|
2713
|
+
.violation-details {
|
|
2714
|
+
margin-top: 8px;
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
.violation-details summary {
|
|
2718
|
+
cursor: pointer;
|
|
2719
|
+
font-size: 13px;
|
|
2720
|
+
color: var(--text-secondary);
|
|
2721
|
+
padding: 4px 0;
|
|
2722
|
+
user-select: none;
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
.violation-details summary:hover {
|
|
2726
|
+
color: var(--text);
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
.violation-details[open] summary {
|
|
2730
|
+
margin-bottom: 8px;
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
.violation-list {
|
|
2734
|
+
display: flex;
|
|
2735
|
+
flex-direction: column;
|
|
2736
|
+
gap: 8px;
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
.violation-item {
|
|
2740
|
+
background: var(--bg);
|
|
2741
|
+
border-radius: 8px;
|
|
2742
|
+
padding: 12px;
|
|
2743
|
+
font-size: 13px;
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
.violation-item-header {
|
|
2747
|
+
display: flex;
|
|
2748
|
+
align-items: center;
|
|
2749
|
+
gap: 8px;
|
|
2750
|
+
margin-bottom: 4px;
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
.violation-rule {
|
|
2754
|
+
font-weight: 600;
|
|
2755
|
+
color: var(--text);
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
.violation-impact {
|
|
2759
|
+
font-size: 11px;
|
|
2760
|
+
font-weight: 600;
|
|
2761
|
+
padding: 1px 6px;
|
|
2762
|
+
border-radius: 4px;
|
|
2763
|
+
text-transform: uppercase;
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
.violation-description {
|
|
2767
|
+
color: var(--text-secondary);
|
|
2768
|
+
line-height: 1.5;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
.violation-help {
|
|
2772
|
+
color: var(--accent);
|
|
2773
|
+
font-size: 12px;
|
|
2774
|
+
margin-top: 4px;
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
/* Footer */
|
|
2778
|
+
.footer {
|
|
2779
|
+
margin-top: 64px;
|
|
2780
|
+
padding-top: 32px;
|
|
2781
|
+
border-top: 1px solid var(--border);
|
|
2782
|
+
text-align: center;
|
|
2783
|
+
color: var(--text-muted);
|
|
2784
|
+
font-size: 13px;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
.footer a {
|
|
2788
|
+
color: var(--accent);
|
|
2789
|
+
text-decoration: none;
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
/* Responsive */
|
|
2793
|
+
@media (max-width: 768px) {
|
|
2794
|
+
.header {
|
|
2795
|
+
flex-direction: column;
|
|
2796
|
+
gap: 24px;
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
.header-right {
|
|
2800
|
+
align-self: flex-start;
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
.component-grid {
|
|
2804
|
+
grid-template-columns: 1fr;
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
`;
|
|
2808
|
+
}
|
|
2809
|
+
function getScoreColor(score) {
|
|
2810
|
+
if (score >= 90) return "#22c55e";
|
|
2811
|
+
if (score >= 70) return "#eab308";
|
|
2812
|
+
return "#ef4444";
|
|
2813
|
+
}
|
|
2814
|
+
function escapeHtml(str) {
|
|
2815
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2816
|
+
}
|
|
2817
|
+
function renderHeader(score) {
|
|
2818
|
+
const color = getScoreColor(score.score);
|
|
2819
|
+
const now = /* @__PURE__ */ new Date();
|
|
2820
|
+
const timestamp = now.toLocaleDateString("en-US", {
|
|
2821
|
+
weekday: "long",
|
|
2822
|
+
year: "numeric",
|
|
2823
|
+
month: "long",
|
|
2824
|
+
day: "numeric",
|
|
2825
|
+
hour: "2-digit",
|
|
2826
|
+
minute: "2-digit"
|
|
2827
|
+
});
|
|
2828
|
+
return `
|
|
2829
|
+
<header class="header">
|
|
2830
|
+
<div class="header-left">
|
|
2831
|
+
<h1>${BRAND.name} Accessibility Report</h1>
|
|
2832
|
+
<p>Generated ${escapeHtml(timestamp)}</p>
|
|
2833
|
+
</div>
|
|
2834
|
+
<div class="header-right">
|
|
2835
|
+
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle light/dark theme">Toggle theme</button>
|
|
2836
|
+
<div class="grade-badge">
|
|
2837
|
+
<div class="grade-score-large" style="color: ${color}">${score.score}</div>
|
|
2838
|
+
<div class="grade-label">/ 100</div>
|
|
2839
|
+
</div>
|
|
2840
|
+
</div>
|
|
2841
|
+
</header>
|
|
2842
|
+
`;
|
|
2843
|
+
}
|
|
2844
|
+
function renderSummaryCards(summary, score) {
|
|
2845
|
+
return `
|
|
2846
|
+
<div class="summary-grid">
|
|
2847
|
+
<div class="summary-card">
|
|
2848
|
+
<div class="summary-card-label">Components</div>
|
|
2849
|
+
<div class="summary-card-value">${summary.totalComponents}</div>
|
|
2850
|
+
<div class="summary-card-sub">${summary.accessibleComponents} accessible (${summary.accessiblePercent}%)</div>
|
|
2851
|
+
</div>
|
|
2852
|
+
<div class="summary-card">
|
|
2853
|
+
<div class="summary-card-label">AA Compliance</div>
|
|
2854
|
+
<div class="summary-card-value" style="color: ${getScoreColor(score.aaPercent)}">${score.aaPercent}%</div>
|
|
2855
|
+
<div class="summary-card-sub">No critical/serious violations</div>
|
|
2856
|
+
</div>
|
|
2857
|
+
<div class="summary-card">
|
|
2858
|
+
<div class="summary-card-label">AAA Compliance</div>
|
|
2859
|
+
<div class="summary-card-value" style="color: ${getScoreColor(score.aaaPercent)}">${score.aaaPercent}%</div>
|
|
2860
|
+
<div class="summary-card-sub">Zero violations</div>
|
|
2861
|
+
</div>
|
|
2862
|
+
<div class="summary-card">
|
|
2863
|
+
<div class="summary-card-label">Total Violations</div>
|
|
2864
|
+
<div class="summary-card-value" style="color: ${summary.totalViolations === 0 ? "#22c55e" : "#ef4444"}">${summary.totalViolations}</div>
|
|
2865
|
+
<div class="summary-card-sub">${summary.totalCritical} critical, ${summary.totalSerious} serious, ${summary.totalModerate} moderate, ${summary.totalMinor} minor</div>
|
|
2866
|
+
</div>
|
|
2867
|
+
</div>
|
|
2868
|
+
`;
|
|
2869
|
+
}
|
|
2870
|
+
function renderComponentCard(comp) {
|
|
2871
|
+
const statusClass = comp.status === "PASS" ? "status-pass" : comp.status === "WARN" ? "status-warn" : "status-fail";
|
|
2872
|
+
const variantCount = comp.results.length || 1;
|
|
2873
|
+
let critical = 0;
|
|
2874
|
+
let serious = 0;
|
|
2875
|
+
let moderate = 0;
|
|
2876
|
+
let minor = 0;
|
|
2877
|
+
for (const r of comp.results) {
|
|
2878
|
+
critical += r.summary.critical;
|
|
2879
|
+
serious += r.summary.serious;
|
|
2880
|
+
moderate += r.summary.moderate;
|
|
2881
|
+
minor += r.summary.minor;
|
|
2882
|
+
}
|
|
2883
|
+
const severityChips = [];
|
|
2884
|
+
if (critical > 0) severityChips.push(`<span class="severity-chip severity-critical">${critical} critical</span>`);
|
|
2885
|
+
if (serious > 0) severityChips.push(`<span class="severity-chip severity-serious">${serious} serious</span>`);
|
|
2886
|
+
if (moderate > 0) severityChips.push(`<span class="severity-chip severity-moderate">${moderate} moderate</span>`);
|
|
2887
|
+
if (minor > 0) severityChips.push(`<span class="severity-chip severity-minor">${minor} minor</span>`);
|
|
2888
|
+
let violationDetailsHtml = "";
|
|
2889
|
+
const variantsWithViolations = comp.results.filter((r) => r.summary.total > 0);
|
|
2890
|
+
if (variantsWithViolations.length > 0) {
|
|
2891
|
+
const variantItems = variantsWithViolations.map((r) => {
|
|
2892
|
+
const counts = [];
|
|
2893
|
+
if (r.summary.critical > 0) counts.push(`${r.summary.critical} critical`);
|
|
2894
|
+
if (r.summary.serious > 0) counts.push(`${r.summary.serious} serious`);
|
|
2895
|
+
if (r.summary.moderate > 0) counts.push(`${r.summary.moderate} moderate`);
|
|
2896
|
+
if (r.summary.minor > 0) counts.push(`${r.summary.minor} minor`);
|
|
2897
|
+
return `
|
|
2898
|
+
<div class="violation-item">
|
|
2899
|
+
<div class="violation-item-header">
|
|
2900
|
+
<span class="violation-rule">${escapeHtml(r.variant)}</span>
|
|
2901
|
+
</div>
|
|
2902
|
+
<div class="violation-description">${counts.join(", ")}</div>
|
|
2903
|
+
</div>
|
|
2904
|
+
`;
|
|
2905
|
+
}).join("");
|
|
2906
|
+
violationDetailsHtml = `
|
|
2907
|
+
<details class="violation-details">
|
|
2908
|
+
<summary>View variant breakdown (${variantsWithViolations.length} variant${variantsWithViolations.length === 1 ? "" : "s"} with issues)</summary>
|
|
2909
|
+
<div class="violation-list">
|
|
2910
|
+
${variantItems}
|
|
2911
|
+
</div>
|
|
2912
|
+
</details>
|
|
2913
|
+
`;
|
|
2914
|
+
}
|
|
2915
|
+
return `
|
|
2916
|
+
<div class="component-card">
|
|
2917
|
+
<div class="component-card-header">
|
|
2918
|
+
<span class="component-name">${escapeHtml(comp.component)}</span>
|
|
2919
|
+
<span class="status-badge ${statusClass}">${comp.status}</span>
|
|
2920
|
+
</div>
|
|
2921
|
+
<div class="component-stats">
|
|
2922
|
+
<span>${variantCount} variant${variantCount === 1 ? "" : "s"}</span>
|
|
2923
|
+
<span>${comp.totalViolations} violation${comp.totalViolations === 1 ? "" : "s"}</span>
|
|
2924
|
+
</div>
|
|
2925
|
+
${severityChips.length > 0 ? `<div class="severity-bar">${severityChips.join("")}</div>` : ""}
|
|
2926
|
+
${violationDetailsHtml}
|
|
2927
|
+
</div>
|
|
2928
|
+
`;
|
|
2929
|
+
}
|
|
2930
|
+
function renderComponentGrid(components) {
|
|
2931
|
+
if (components.length === 0) {
|
|
2932
|
+
return `
|
|
2933
|
+
<section class="section">
|
|
2934
|
+
<h2 class="section-title">Components</h2>
|
|
2935
|
+
<p style="color: var(--text-secondary)">No components found.</p>
|
|
2936
|
+
</section>
|
|
2937
|
+
`;
|
|
2938
|
+
}
|
|
2939
|
+
const order = { FAIL: 0, WARN: 1, PASS: 2 };
|
|
2940
|
+
const sorted = [...components].sort((a, b) => order[a.status] - order[b.status]);
|
|
2941
|
+
return `
|
|
2942
|
+
<section class="section">
|
|
2943
|
+
<h2 class="section-title">Components</h2>
|
|
2944
|
+
<div class="component-grid">
|
|
2945
|
+
${sorted.map(renderComponentCard).join("")}
|
|
2946
|
+
</div>
|
|
2947
|
+
</section>
|
|
2948
|
+
`;
|
|
2949
|
+
}
|
|
2950
|
+
function renderFooter() {
|
|
2951
|
+
return `
|
|
2952
|
+
<footer class="footer">
|
|
2953
|
+
<p>Generated by <a href="#">${BRAND.name}</a> — AI-first design system documentation</p>
|
|
2954
|
+
</footer>
|
|
2955
|
+
`;
|
|
2956
|
+
}
|
|
2957
|
+
function getScripts() {
|
|
2958
|
+
return `
|
|
2959
|
+
// Theme toggle
|
|
2960
|
+
(function() {
|
|
2961
|
+
var toggle = document.getElementById('theme-toggle');
|
|
2962
|
+
var root = document.documentElement;
|
|
2963
|
+
|
|
2964
|
+
function setTheme(theme) {
|
|
2965
|
+
root.classList.remove('light', 'dark');
|
|
2966
|
+
root.classList.add(theme);
|
|
2967
|
+
toggle.textContent = theme === 'dark' ? 'Light mode' : 'Dark mode';
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
// Default to dark
|
|
2971
|
+
var preferred = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
|
2972
|
+
setTheme(preferred);
|
|
2973
|
+
|
|
2974
|
+
toggle.addEventListener('click', function() {
|
|
2975
|
+
var current = root.classList.contains('dark') ? 'dark' : 'light';
|
|
2976
|
+
setTheme(current === 'dark' ? 'light' : 'dark');
|
|
2977
|
+
});
|
|
2978
|
+
})();
|
|
2979
|
+
|
|
2980
|
+
// Animate bars on load
|
|
2981
|
+
document.querySelectorAll('.bar-fill, .coverage-bar-fill').forEach(function(el) {
|
|
2982
|
+
var width = el.style.width;
|
|
2983
|
+
el.style.width = '0';
|
|
2984
|
+
setTimeout(function() {
|
|
2985
|
+
el.style.width = width;
|
|
2986
|
+
}, 100);
|
|
2987
|
+
});
|
|
2988
|
+
`;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
// src/commands/a11y.ts
|
|
2992
|
+
function calculateA11yScore(summary) {
|
|
2993
|
+
const deductions = summary.totalCritical * 10 + summary.totalSerious * 5 + summary.totalModerate * 2 + summary.totalMinor * 1;
|
|
2994
|
+
const score = Math.max(0, 100 - deductions);
|
|
2995
|
+
const aaComponents = summary.components.filter(
|
|
2996
|
+
(c) => c.totalCritical === 0 && c.totalSerious === 0
|
|
2997
|
+
).length;
|
|
2998
|
+
const aaPercent = summary.totalComponents > 0 ? Math.round(aaComponents / summary.totalComponents * 100) : 100;
|
|
2999
|
+
const aaaComponents = summary.components.filter(
|
|
3000
|
+
(c) => c.totalViolations === 0
|
|
3001
|
+
).length;
|
|
3002
|
+
const aaaPercent = summary.totalComponents > 0 ? Math.round(aaaComponents / summary.totalComponents * 100) : 100;
|
|
3003
|
+
return { score, aaPercent, aaaPercent };
|
|
3004
|
+
}
|
|
3005
|
+
function formatGitHub(summary) {
|
|
3006
|
+
const score = summary.score ?? calculateA11yScore(summary);
|
|
3007
|
+
const badge = summary.passed ? "passing" : "failing";
|
|
3008
|
+
const badgeColor = summary.passed ? "brightgreen" : "red";
|
|
3009
|
+
const lines = [];
|
|
3010
|
+
lines.push(`## ${BRAND.name} Accessibility Report`);
|
|
3011
|
+
lines.push("");
|
|
3012
|
+
lines.push(``);
|
|
3013
|
+
lines.push(`**Score:** ${score.score}/100 | **AA:** ${score.aaPercent}% | **AAA:** ${score.aaaPercent}%`);
|
|
3014
|
+
lines.push("");
|
|
3015
|
+
lines.push("| Component | Variants | Violations | Critical | Serious | Status |");
|
|
3016
|
+
lines.push("|-----------|----------|------------|----------|---------|--------|");
|
|
3017
|
+
for (const result of summary.components) {
|
|
3018
|
+
const statusIcon = result.status === "PASS" ? "PASS" : result.status === "WARN" ? "WARN" : "FAIL";
|
|
3019
|
+
const variantCount = result.results.length || 1;
|
|
3020
|
+
lines.push(
|
|
3021
|
+
`| ${result.component} | ${variantCount} | ${result.totalViolations} | ${result.totalCritical} | ${result.totalSerious} | ${statusIcon} |`
|
|
3022
|
+
);
|
|
3023
|
+
}
|
|
3024
|
+
lines.push("");
|
|
3025
|
+
lines.push(`**Summary:** ${summary.accessibleComponents}/${summary.totalComponents} components accessible (${summary.accessiblePercent}%)`);
|
|
3026
|
+
lines.push(`**Violations:** ${summary.totalViolations} total (${summary.totalCritical} critical, ${summary.totalSerious} serious, ${summary.totalModerate} moderate, ${summary.totalMinor} minor)`);
|
|
3027
|
+
if (summary.totalCritical + summary.totalSerious > 0) {
|
|
3028
|
+
lines.push("");
|
|
3029
|
+
lines.push(`**Blocking:** ${summary.totalCritical + summary.totalSerious} critical/serious issues must be fixed`);
|
|
3030
|
+
}
|
|
3031
|
+
lines.push("");
|
|
3032
|
+
return lines.join("\n");
|
|
3033
|
+
}
|
|
2406
3034
|
async function a11y(options = {}) {
|
|
2407
|
-
const {
|
|
3035
|
+
const {
|
|
3036
|
+
config: configPath,
|
|
3037
|
+
json = false,
|
|
3038
|
+
ci = false,
|
|
3039
|
+
component,
|
|
3040
|
+
port = 6006,
|
|
3041
|
+
format = json ? "json" : "table",
|
|
3042
|
+
standard = "AA"
|
|
3043
|
+
} = options;
|
|
2408
3044
|
await loadConfig(configPath);
|
|
2409
3045
|
const client = createDevServerClient(port);
|
|
2410
3046
|
const componentResults = [];
|
|
2411
|
-
|
|
3047
|
+
const isJsonOutput = format === "json" || json;
|
|
3048
|
+
if (!isJsonOutput && format !== "github") {
|
|
2412
3049
|
console.log(pc12.cyan(`
|
|
2413
3050
|
${BRAND.name} Accessibility Report
|
|
2414
3051
|
`));
|
|
@@ -2422,7 +3059,7 @@ ${BRAND.name} Accessibility Report
|
|
|
2422
3059
|
}
|
|
2423
3060
|
const segments = await client.getSegments();
|
|
2424
3061
|
if (segments.length === 0) {
|
|
2425
|
-
if (
|
|
3062
|
+
if (isJsonOutput) {
|
|
2426
3063
|
console.log(JSON.stringify({ error: "No fragments found", components: [] }));
|
|
2427
3064
|
} else {
|
|
2428
3065
|
console.log(pc12.yellow("No fragments found.\n"));
|
|
@@ -2443,14 +3080,14 @@ ${BRAND.name} Accessibility Report
|
|
|
2443
3080
|
const componentsToCheck = component ? segments.filter((s) => s.name.toLowerCase() === component.toLowerCase()) : segments;
|
|
2444
3081
|
if (component && componentsToCheck.length === 0) {
|
|
2445
3082
|
const error = `Component '${component}' not found. Available: ${segments.map((s) => s.name).join(", ")}`;
|
|
2446
|
-
if (
|
|
3083
|
+
if (isJsonOutput) {
|
|
2447
3084
|
console.log(JSON.stringify({ error }));
|
|
2448
3085
|
} else {
|
|
2449
3086
|
console.log(pc12.red(error));
|
|
2450
3087
|
}
|
|
2451
3088
|
throw new Error(error);
|
|
2452
3089
|
}
|
|
2453
|
-
if (!
|
|
3090
|
+
if (!isJsonOutput && format !== "github") {
|
|
2454
3091
|
console.log(pc12.dim(`Checking ${componentsToCheck.length} component(s) for accessibility issues...
|
|
2455
3092
|
`));
|
|
2456
3093
|
}
|
|
@@ -2471,6 +3108,9 @@ ${BRAND.name} Accessibility Report
|
|
|
2471
3108
|
} else if (totalViolations2 > 0) {
|
|
2472
3109
|
status = "WARN";
|
|
2473
3110
|
}
|
|
3111
|
+
if (standard === "AAA" && totalViolations2 > 0) {
|
|
3112
|
+
status = "FAIL";
|
|
3113
|
+
}
|
|
2474
3114
|
componentResults.push({
|
|
2475
3115
|
component: seg.name,
|
|
2476
3116
|
results: a11yResult.results,
|
|
@@ -2479,7 +3119,7 @@ ${BRAND.name} Accessibility Report
|
|
|
2479
3119
|
totalCritical: totalCritical2,
|
|
2480
3120
|
totalSerious: totalSerious2
|
|
2481
3121
|
});
|
|
2482
|
-
} catch
|
|
3122
|
+
} catch {
|
|
2483
3123
|
componentResults.push({
|
|
2484
3124
|
component: seg.name,
|
|
2485
3125
|
results: [],
|
|
@@ -2502,6 +3142,7 @@ ${BRAND.name} Accessibility Report
|
|
|
2502
3142
|
totalMinor += result.summary.minor;
|
|
2503
3143
|
}
|
|
2504
3144
|
}
|
|
3145
|
+
const passed = standard === "AAA" ? totalViolations === 0 : totalCritical === 0 && totalSerious === 0;
|
|
2505
3146
|
const summary = {
|
|
2506
3147
|
totalComponents: componentResults.length,
|
|
2507
3148
|
accessibleComponents,
|
|
@@ -2512,9 +3153,12 @@ ${BRAND.name} Accessibility Report
|
|
|
2512
3153
|
totalSerious,
|
|
2513
3154
|
totalModerate,
|
|
2514
3155
|
totalMinor,
|
|
2515
|
-
passed
|
|
3156
|
+
passed
|
|
2516
3157
|
};
|
|
2517
|
-
|
|
3158
|
+
summary.score = calculateA11yScore(summary);
|
|
3159
|
+
if (format === "github") {
|
|
3160
|
+
console.log(formatGitHub(summary));
|
|
3161
|
+
} else if (isJsonOutput) {
|
|
2518
3162
|
console.log(JSON.stringify(summary, null, 2));
|
|
2519
3163
|
} else {
|
|
2520
3164
|
console.log(pc12.bold(
|
|
@@ -2530,21 +3174,46 @@ ${BRAND.name} Accessibility Report
|
|
|
2530
3174
|
}
|
|
2531
3175
|
console.log(pc12.dim("\u2500".repeat(72)));
|
|
2532
3176
|
console.log();
|
|
3177
|
+
const categories = [
|
|
3178
|
+
{ label: "Critical", count: totalCritical, color: pc12.red },
|
|
3179
|
+
{ label: "Serious", count: totalSerious, color: pc12.red },
|
|
3180
|
+
{ label: "Moderate", count: totalModerate, color: pc12.yellow },
|
|
3181
|
+
{ label: "Minor", count: totalMinor, color: pc12.dim }
|
|
3182
|
+
];
|
|
3183
|
+
for (const cat of categories) {
|
|
3184
|
+
if (cat.count > 0) {
|
|
3185
|
+
const dots = ".".repeat(Math.max(1, 30 - cat.label.length));
|
|
3186
|
+
console.log(` ${cat.label} ${pc12.dim(dots)} ${cat.color(String(cat.count))}`);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
const { score, aaPercent, aaaPercent } = summary.score;
|
|
3190
|
+
console.log();
|
|
3191
|
+
console.log(pc12.bold(` Score: ${score}/100`));
|
|
3192
|
+
console.log(` AA compliance .... ${aaPercent}%`);
|
|
3193
|
+
console.log(` AAA compliance ... ${aaaPercent}%`);
|
|
3194
|
+
console.log(` Standard ......... WCAG ${standard}`);
|
|
3195
|
+
console.log();
|
|
2533
3196
|
console.log(pc12.bold("Summary:"));
|
|
2534
3197
|
console.log(` ${accessibleComponents}/${componentResults.length} components accessible (${summary.accessiblePercent}%)`);
|
|
2535
3198
|
console.log(` Total violations: ${totalViolations} (${totalCritical} critical, ${totalSerious} serious, ${totalModerate} moderate, ${totalMinor} minor)`);
|
|
2536
3199
|
if (!summary.passed) {
|
|
2537
3200
|
console.log();
|
|
2538
|
-
console.log(pc12.red("
|
|
3201
|
+
console.log(pc12.red("x Accessibility check failed - critical/serious violations found"));
|
|
2539
3202
|
} else if (totalViolations > 0) {
|
|
2540
3203
|
console.log();
|
|
2541
|
-
console.log(pc12.yellow("
|
|
3204
|
+
console.log(pc12.yellow("! Minor/moderate violations found - consider fixing for better accessibility"));
|
|
2542
3205
|
} else {
|
|
2543
3206
|
console.log();
|
|
2544
|
-
console.log(pc12.green("
|
|
3207
|
+
console.log(pc12.green("v All components pass accessibility checks"));
|
|
2545
3208
|
}
|
|
2546
3209
|
console.log();
|
|
2547
3210
|
}
|
|
3211
|
+
if (options.report) {
|
|
3212
|
+
const outputPath = options.output ?? "a11y-report.html";
|
|
3213
|
+
const html = generateA11yReport(summary);
|
|
3214
|
+
fs.writeFileSync(outputPath, html, "utf-8");
|
|
3215
|
+
console.log(pc12.green("v Report generated: " + outputPath));
|
|
3216
|
+
}
|
|
2548
3217
|
if (ci && !summary.passed) {
|
|
2549
3218
|
throw new Error(`Accessibility check failed: ${totalCritical} critical, ${totalSerious} serious violations found`);
|
|
2550
3219
|
}
|
|
@@ -3825,8 +4494,8 @@ ${BRAND.name} AI Enhancement
|
|
|
3825
4494
|
];
|
|
3826
4495
|
for (const srcPath of possiblePaths) {
|
|
3827
4496
|
try {
|
|
3828
|
-
const
|
|
3829
|
-
if (
|
|
4497
|
+
const fs2 = await import("fs");
|
|
4498
|
+
if (fs2.existsSync(srcPath)) {
|
|
3830
4499
|
const extraction = await extractPropsFromFile(srcPath, {
|
|
3831
4500
|
propsTypeName: `${compName}Props`
|
|
3832
4501
|
});
|
|
@@ -4535,14 +5204,18 @@ program.command("audit").description("Scan all fragments and show compliance met
|
|
|
4535
5204
|
process.exit(1);
|
|
4536
5205
|
}
|
|
4537
5206
|
});
|
|
4538
|
-
program.command("a11y").description("Run accessibility checks on all component variants").option("-c, --config <path>", "Path to config file").option("--json", "Output results as JSON").option("--ci", "CI mode (exit code 1 if any critical/serious violations)").option("--component <name>", "Check specific component only").option("-p, --port <port>", "Dev server port", "6006").action(async (options) => {
|
|
5207
|
+
program.command("a11y").description("Run accessibility checks on all component variants").option("-c, --config <path>", "Path to config file").option("--json", "Output results as JSON").option("--ci", "CI mode (exit code 1 if any critical/serious violations)").option("--component <name>", "Check specific component only").option("-p, --port <port>", "Dev server port", "6006").option("--format <format>", "Output format (table, json, github)", "table").option("--standard <level>", "WCAG level (AA or AAA)", "AA").option("--report", "Generate standalone HTML compliance report").option("-o, --output <path>", "Output path for report", "a11y-report.html").action(async (options) => {
|
|
4539
5208
|
try {
|
|
4540
5209
|
await a11y({
|
|
4541
5210
|
config: options.config,
|
|
4542
5211
|
json: options.json,
|
|
4543
5212
|
ci: options.ci,
|
|
4544
5213
|
component: options.component,
|
|
4545
|
-
port: options.port
|
|
5214
|
+
port: options.port,
|
|
5215
|
+
format: options.format,
|
|
5216
|
+
standard: options.standard,
|
|
5217
|
+
report: options.report,
|
|
5218
|
+
output: options.output
|
|
4546
5219
|
});
|
|
4547
5220
|
} catch (error) {
|
|
4548
5221
|
if (options.json) {
|
|
@@ -4641,8 +5314,8 @@ Make sure the dev server is running: ${BRAND.cliCommand} dev`));
|
|
|
4641
5314
|
});
|
|
4642
5315
|
program.command("view").description(`Generate a static HTML viewer for ${BRAND.outFile}`).option("-i, --input <path>", `Path to ${BRAND.outFile}`, BRAND.outFile).option("-o, --output <path>", "Output HTML file path", BRAND.viewerHtmlFile).option("--open", "Open in browser after generation").action(async (options) => {
|
|
4643
5316
|
try {
|
|
4644
|
-
const { generateViewerFromJson } = await import("./static-viewer-
|
|
4645
|
-
const
|
|
5317
|
+
const { generateViewerFromJson } = await import("./static-viewer-4LQZ5AGA.js");
|
|
5318
|
+
const fs2 = await import("fs/promises");
|
|
4646
5319
|
const path = await import("path");
|
|
4647
5320
|
const inputPath = path.resolve(process.cwd(), options.input);
|
|
4648
5321
|
const outputPath = path.resolve(process.cwd(), options.output);
|
|
@@ -4650,7 +5323,7 @@ program.command("view").description(`Generate a static HTML viewer for ${BRAND.o
|
|
|
4650
5323
|
${BRAND.name} Viewer Generator
|
|
4651
5324
|
`));
|
|
4652
5325
|
try {
|
|
4653
|
-
await
|
|
5326
|
+
await fs2.access(inputPath);
|
|
4654
5327
|
} catch {
|
|
4655
5328
|
console.log(pc20.red(`Error: ${options.input} not found.`));
|
|
4656
5329
|
console.log(pc20.dim(`
|
|
@@ -4660,7 +5333,7 @@ Run ${pc20.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
|
|
|
4660
5333
|
}
|
|
4661
5334
|
console.log(pc20.dim(`Reading: ${options.input}`));
|
|
4662
5335
|
const html = await generateViewerFromJson(inputPath);
|
|
4663
|
-
await
|
|
5336
|
+
await fs2.writeFile(outputPath, html);
|
|
4664
5337
|
console.log(pc20.green(`
|
|
4665
5338
|
\u2713 Generated: ${options.output}
|
|
4666
5339
|
`));
|
|
@@ -4689,7 +5362,7 @@ program.command("add").argument("[name]", 'Component name (e.g., "Button", "Text
|
|
|
4689
5362
|
});
|
|
4690
5363
|
program.command("init").description("Initialize fragments in a project (interactive by default)").option("--force", "Overwrite existing config").option("-y, --yes", "Non-interactive mode - auto-detect and use defaults").action(async (options) => {
|
|
4691
5364
|
try {
|
|
4692
|
-
const { init } = await import("./init-
|
|
5365
|
+
const { init } = await import("./init-WKGDPYI4.js");
|
|
4693
5366
|
const result = await init({
|
|
4694
5367
|
projectRoot: process.cwd(),
|
|
4695
5368
|
force: options.force,
|
|
@@ -4709,7 +5382,7 @@ program.command("init").description("Initialize fragments in a project (interact
|
|
|
4709
5382
|
});
|
|
4710
5383
|
program.command("tokens").description("Discover and list design tokens from CSS/SCSS files").option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--categories", "Group tokens by category").option("--theme <theme>", "Filter by theme name").option("--category <category>", "Filter by category (color, spacing, typography, etc.)").option("--verbose", "Show all tokens (no truncation)").action(async (options) => {
|
|
4711
5384
|
try {
|
|
4712
|
-
const { tokens } = await import("./tokens-
|
|
5385
|
+
const { tokens } = await import("./tokens-JAJABYXP.js");
|
|
4713
5386
|
const result = await tokens({
|
|
4714
5387
|
config: options.config,
|
|
4715
5388
|
json: options.json,
|
|
@@ -4728,7 +5401,7 @@ program.command("tokens").description("Discover and list design tokens from CSS/
|
|
|
4728
5401
|
});
|
|
4729
5402
|
program.command("generate").description("Generate fragment files from component source code").argument("[component]", "Specific component name to generate (optional)").option("--force", "Overwrite existing fragment files").option("--pattern <glob>", "Pattern for component files", "src/components/**/*.tsx").action(async (component, options) => {
|
|
4730
5403
|
try {
|
|
4731
|
-
const { generate } = await import("./generate-
|
|
5404
|
+
const { generate } = await import("./generate-7AF7WRVK.js");
|
|
4732
5405
|
const result = await generate({
|
|
4733
5406
|
projectRoot: process.cwd(),
|
|
4734
5407
|
component,
|
|
@@ -4747,7 +5420,7 @@ program.command("generate").description("Generate fragment files from component
|
|
|
4747
5420
|
program.command("test").description("Run interaction tests for fragments with play functions").option("-c, --config <path>", "Path to config file").option("--component <name>", "Filter by component name").option("--tags <tags>", "Filter by tags (comma-separated)").option("--grep <pattern>", "Filter by variant name pattern").option("--exclude <pattern>", "Exclude tests matching pattern").option("--parallel <count>", "Number of parallel browser contexts", parseInt, 4).option("--timeout <ms>", "Timeout per test in milliseconds", parseInt, 3e4).option("--retries <count>", "Number of retries for failed tests", parseInt, 0).option("--bail", "Stop on first failure").option("--browser <name>", "Browser to use (chromium, firefox, webkit)", "chromium").option("--headed", "Run in headed mode (show browser)").option("--a11y", "Run accessibility checks with axe-core").option("--visual", "Capture screenshots for visual regression").option("--update-snapshots", "Update visual snapshots").option("--watch", "Watch mode - re-run on file changes").option("--reporters <names>", "Reporters to use (console, junit, json)", "console").option("-o, --output <dir>", "Output directory for results", "./test-results").option("--server-url <url>", "URL of running dev server (skips starting server)").option("-p, --port <port>", "Port for dev server", parseInt, 6006).option("--ci", "CI mode - non-interactive, exit with code 1 on failure").option("--list", "List available tests without running them").action(async (options) => {
|
|
4748
5421
|
try {
|
|
4749
5422
|
const { config, configDir } = await loadConfig(options.config);
|
|
4750
|
-
const { runTestCommand, listTests } = await import("./test-
|
|
5423
|
+
const { runTestCommand, listTests } = await import("./test-CJDNJTPZ.js");
|
|
4751
5424
|
if (options.list) {
|
|
4752
5425
|
await listTests(config, configDir, {
|
|
4753
5426
|
component: options.component,
|