@sarjallab09/figma-intelligence 1.1.0 → 1.2.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.
- package/README.md +69 -36
- package/dist/bin/cli.js +2 -0
- package/dist/design-bridge/bridge.js +2 -0
- package/dist/figma-bridge-plugin/bridge-relay.js +2 -0
- package/dist/figma-bridge-plugin/code.js +1 -0
- package/{figma-bridge-plugin → dist/figma-bridge-plugin}/package-lock.json +0 -3
- package/dist/figma-bridge-plugin/ui.html +4970 -0
- package/dist/figma-intelligence-layer/dist/index.js +2 -0
- package/dist/scripts/clean-existing-chunks.js +2 -0
- package/dist/scripts/connect-ai-tool.js +2 -0
- package/dist/scripts/convert-hub-pdfs.js +2 -0
- package/dist/scripts/figma-mcp-status.js +2 -0
- package/dist/scripts/register-codex-mcp.js +2 -0
- package/dist/scripts/test-copilot-chat.js +2 -0
- package/package.json +11 -8
- package/bin/cli.js +0 -859
- package/design-bridge/bridge.js +0 -196
- package/design-bridge/lib/assets.js +0 -367
- package/design-bridge/lib/prompt.js +0 -85
- package/design-bridge/lib/server.js +0 -66
- package/design-bridge/lib/stitch.js +0 -37
- package/design-bridge/lib/tokens.js +0 -82
- package/design-bridge/package-lock.json +0 -579
- package/figma-bridge-plugin/README.md +0 -97
- package/figma-bridge-plugin/anthropic-chat-runner.js +0 -192
- package/figma-bridge-plugin/bridge-relay.js +0 -2505
- package/figma-bridge-plugin/chat-runner.js +0 -485
- package/figma-bridge-plugin/code.js +0 -1534
- package/figma-bridge-plugin/codex-runner.js +0 -505
- package/figma-bridge-plugin/component-schemas.js +0 -110
- package/figma-bridge-plugin/content-context.js +0 -869
- package/figma-bridge-plugin/create-button.js +0 -216
- package/figma-bridge-plugin/gemini-cli-runner.js +0 -291
- package/figma-bridge-plugin/gemini-runner.js +0 -187
- package/figma-bridge-plugin/html-to-figma.js +0 -927
- package/figma-bridge-plugin/knowledge-hub/.gitkeep +0 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/anatomy-spec.md +0 -159
- package/figma-bridge-plugin/knowledge-hub/uspec-references/api-spec.md +0 -162
- package/figma-bridge-plugin/knowledge-hub/uspec-references/color-spec.md +0 -148
- package/figma-bridge-plugin/knowledge-hub/uspec-references/full-spec-template.md +0 -314
- package/figma-bridge-plugin/knowledge-hub/uspec-references/property-spec.md +0 -175
- package/figma-bridge-plugin/knowledge-hub/uspec-references/screen-reader-spec.md +0 -180
- package/figma-bridge-plugin/knowledge-hub/uspec-references/structure-spec.md +0 -165
- package/figma-bridge-plugin/perplexity-runner.js +0 -188
- package/figma-bridge-plugin/references/SKILL.md +0 -178
- package/figma-bridge-plugin/references/anatomy-spec.md +0 -159
- package/figma-bridge-plugin/references/api-spec.md +0 -162
- package/figma-bridge-plugin/references/color-spec.md +0 -148
- package/figma-bridge-plugin/references/full-spec-template.md +0 -314
- package/figma-bridge-plugin/references/property-spec.md +0 -175
- package/figma-bridge-plugin/references/screen-reader-spec.md +0 -180
- package/figma-bridge-plugin/references/structure-spec.md +0 -165
- package/figma-bridge-plugin/shared-prompt-config.js +0 -645
- package/figma-bridge-plugin/spec-helpers/build-table.js +0 -269
- package/figma-bridge-plugin/spec-helpers/classify-elements.js +0 -189
- package/figma-bridge-plugin/spec-helpers/index.js +0 -35
- package/figma-bridge-plugin/spec-helpers/parse-figma-link.js +0 -49
- package/figma-bridge-plugin/spec-helpers/position-markers.js +0 -158
- package/figma-bridge-plugin/stitch-auth.js +0 -322
- package/figma-bridge-plugin/stitch-runner.js +0 -1427
- package/figma-bridge-plugin/token-resolver.js +0 -107
- package/figma-bridge-plugin/ui.html +0 -4542
- package/figma-intelligence-layer/.env.example +0 -39
- package/figma-intelligence-layer/docs/local-image-generation.md +0 -60
- package/figma-intelligence-layer/examples/comfyui-workflow-template.example.json +0 -101
- package/figma-intelligence-layer/jest.config.js +0 -14
- package/figma-intelligence-layer/mcp-config.json +0 -19
- package/figma-intelligence-layer/package-lock.json +0 -5892
- package/figma-intelligence-layer/scripts/setup-comfyui-local.sh +0 -67
- package/figma-intelligence-layer/scripts/start-comfyui.sh +0 -33
- package/figma-intelligence-layer/src/index.ts +0 -2233
- package/figma-intelligence-layer/src/shared/auto-layout-validator.ts +0 -404
- package/figma-intelligence-layer/src/shared/cache.ts +0 -187
- package/figma-intelligence-layer/src/shared/color-operations.ts +0 -533
- package/figma-intelligence-layer/src/shared/color-utils.ts +0 -138
- package/figma-intelligence-layer/src/shared/component-script-builder.ts +0 -413
- package/figma-intelligence-layer/src/shared/component-templates.ts +0 -2767
- package/figma-intelligence-layer/src/shared/concept-taxonomy.ts +0 -694
- package/figma-intelligence-layer/src/shared/decision-log.ts +0 -128
- package/figma-intelligence-layer/src/shared/design-system-context.ts +0 -568
- package/figma-intelligence-layer/src/shared/design-system-intelligence.ts +0 -131
- package/figma-intelligence-layer/src/shared/design-system-matcher.ts +0 -184
- package/figma-intelligence-layer/src/shared/design-system-normalizers.ts +0 -196
- package/figma-intelligence-layer/src/shared/design-system-tokens.ts +0 -295
- package/figma-intelligence-layer/src/shared/dtcg-validator.ts +0 -530
- package/figma-intelligence-layer/src/shared/enrichment-pipeline.ts +0 -671
- package/figma-intelligence-layer/src/shared/figma-bridge.ts +0 -1418
- package/figma-intelligence-layer/src/shared/font-config.ts +0 -126
- package/figma-intelligence-layer/src/shared/icon-catalog.ts +0 -360
- package/figma-intelligence-layer/src/shared/icon-fetch.ts +0 -80
- package/figma-intelligence-layer/src/shared/prototype-script-builder.ts +0 -162
- package/figma-intelligence-layer/src/shared/response-compression.ts +0 -440
- package/figma-intelligence-layer/src/shared/semantic-token-catalog.ts +0 -324
- package/figma-intelligence-layer/src/shared/token-binder.ts +0 -505
- package/figma-intelligence-layer/src/shared/token-math.ts +0 -427
- package/figma-intelligence-layer/src/shared/token-naming.ts +0 -468
- package/figma-intelligence-layer/src/shared/token-utils.ts +0 -420
- package/figma-intelligence-layer/src/shared/types.ts +0 -346
- package/figma-intelligence-layer/src/shared/typography-presets.ts +0 -94
- package/figma-intelligence-layer/src/shared/unsplash.ts +0 -165
- package/figma-intelligence-layer/src/shared/vision-client.ts +0 -607
- package/figma-intelligence-layer/src/shared/vision-provider-anthropic.ts +0 -334
- package/figma-intelligence-layer/src/shared/vision-provider-openai.ts +0 -446
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotate-handler.ts +0 -782
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotate-renderer.ts +0 -496
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotation-kit.ts +0 -230
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/colorblind-sim.ts +0 -66
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/index.ts +0 -810
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-analyzer.ts +0 -1191
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-figma-page.ts +0 -1346
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-handler.ts +0 -148
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/vpat-figma-page.ts +0 -499
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/vpat-report.ts +0 -910
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/wcag-checker.ts +0 -989
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/wcag-criteria.ts +0 -1160
- package/figma-intelligence-layer/src/tools/phase1-vision/design-from-ref/index.ts +0 -424
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/component-recognizer.ts +0 -38
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/ds-matcher.ts +0 -111
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/font-matcher.ts +0 -114
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/icon-resolver.ts +0 -103
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/index.ts +0 -1060
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/layout-segmenter.ts +0 -18
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/token-inferencer.ts +0 -39
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/vision-pipeline.ts +0 -58
- package/figma-intelligence-layer/src/tools/phase1-vision/sketch-to-design/index.ts +0 -298
- package/figma-intelligence-layer/src/tools/phase1-vision/visual-audit/index.ts +0 -197
- package/figma-intelligence-layer/src/tools/phase2-accuracy/component-audit/index.ts +0 -494
- package/figma-intelligence-layer/src/tools/phase2-accuracy/intent-translator/index.ts +0 -356
- package/figma-intelligence-layer/src/tools/phase2-accuracy/layout-intelligence/container-patterns.ts +0 -123
- package/figma-intelligence-layer/src/tools/phase2-accuracy/layout-intelligence/index.ts +0 -663
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/built-in-rules.yaml +0 -56
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/index.ts +0 -614
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/rule-engine.ts +0 -113
- package/figma-intelligence-layer/src/tools/phase2-accuracy/theme-generator/color-theory.ts +0 -178
- package/figma-intelligence-layer/src/tools/phase2-accuracy/theme-generator/index.ts +0 -470
- package/figma-intelligence-layer/src/tools/phase2-accuracy/variant-expander/index.ts +0 -429
- package/figma-intelligence-layer/src/tools/phase2-accuracy/variant-expander/token-override-maps.ts +0 -226
- package/figma-intelligence-layer/src/tools/phase3-generation/ai-image-insert/index.ts +0 -535
- package/figma-intelligence-layer/src/tools/phase3-generation/component-archaeologist/index.ts +0 -660
- package/figma-intelligence-layer/src/tools/phase3-generation/component-archaeologist/pattern-fingerprints.ts +0 -209
- package/figma-intelligence-layer/src/tools/phase3-generation/composition-builder/index.ts +0 -540
- package/figma-intelligence-layer/src/tools/phase3-generation/figma-animated-build.ts +0 -391
- package/figma-intelligence-layer/src/tools/phase3-generation/page-architect/index.ts +0 -2019
- package/figma-intelligence-layer/src/tools/phase3-generation/page-architect/screen-templates.ts +0 -131
- package/figma-intelligence-layer/src/tools/phase3-generation/prototype-map/index.ts +0 -381
- package/figma-intelligence-layer/src/tools/phase3-generation/prototype-wire/index.ts +0 -565
- package/figma-intelligence-layer/src/tools/phase3-generation/swarm-build/index.ts +0 -764
- package/figma-intelligence-layer/src/tools/phase3-generation/system-drift/index.ts +0 -535
- package/figma-intelligence-layer/src/tools/phase3-generation/unsplash-search/index.ts +0 -84
- package/figma-intelligence-layer/src/tools/phase3-generation/url-to-frame/index.ts +0 -401
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/css-animations.ts +0 -68
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/framer-motion.ts +0 -78
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/swift-animations.ts +0 -93
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/index.ts +0 -596
- package/figma-intelligence-layer/src/tools/phase4-sync/ci-check/index.ts +0 -462
- package/figma-intelligence-layer/src/tools/phase4-sync/export-tokens/index.ts +0 -1470
- package/figma-intelligence-layer/src/tools/phase4-sync/generate-component-code/index.ts +0 -829
- package/figma-intelligence-layer/src/tools/phase4-sync/handoff-spec/index.ts +0 -702
- package/figma-intelligence-layer/src/tools/phase4-sync/icon-library-sync/index.ts +0 -483
- package/figma-intelligence-layer/src/tools/phase4-sync/sync-from-code/index.ts +0 -501
- package/figma-intelligence-layer/src/tools/phase4-sync/sync-from-code/storybook-parser.ts +0 -106
- package/figma-intelligence-layer/src/tools/phase4-sync/watch-docs/index.ts +0 -676
- package/figma-intelligence-layer/src/tools/phase4-sync/webhook-listener/index.ts +0 -560
- package/figma-intelligence-layer/src/tools/phase5-governance/apg-doc/index.ts +0 -1043
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/component-detection.ts +0 -620
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/anatomy.ts +0 -331
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/color-tokens.ts +0 -77
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/properties.ts +0 -54
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/snapshot.ts +0 -287
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/spacing.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/states.ts +0 -43
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/typography.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/index.ts +0 -221
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/_default.ts +0 -166
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/accordion.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/alert.ts +0 -234
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/avatar-group.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/avatar.ts +0 -249
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/badge.ts +0 -231
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/banner.ts +0 -293
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/breadcrumb.ts +0 -240
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/button.ts +0 -243
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/calendar.ts +0 -307
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/card.ts +0 -143
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/checkbox.ts +0 -227
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/chip.ts +0 -233
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/combobox.ts +0 -282
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/datepicker.ts +0 -276
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/divider.ts +0 -223
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/drawer.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/dropdown-menu.ts +0 -289
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/empty-state.ts +0 -261
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/file-uploader.ts +0 -290
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/form.ts +0 -265
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/grid.ts +0 -238
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/icon.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/index.ts +0 -128
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/inline-edit.ts +0 -286
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/inline-message.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/input.ts +0 -330
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/link.ts +0 -247
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/list.ts +0 -250
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/menu.ts +0 -247
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/modal.ts +0 -144
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/navbar.ts +0 -264
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/navigation.ts +0 -251
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/number-input.ts +0 -261
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/pagination.ts +0 -248
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/popover.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/progress.ts +0 -251
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/radio.ts +0 -142
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/range-slider.ts +0 -282
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/rating.ts +0 -250
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/search.ts +0 -258
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/segmented-control.ts +0 -265
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/select.ts +0 -319
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/skeleton.ts +0 -256
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/slider.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/spinner.ts +0 -239
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/status-dot.ts +0 -252
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/stepper.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/table.ts +0 -244
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tabs.ts +0 -143
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tag.ts +0 -243
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/textarea.ts +0 -259
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/time-picker.ts +0 -293
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toast.ts +0 -144
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toggle.ts +0 -289
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toolbar.ts +0 -267
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tooltip.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/treeview.ts +0 -257
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/typography.ts +0 -319
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/legacy-compat.ts +0 -121
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/anatomy-diagram.ts +0 -430
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/figma-page.ts +0 -312
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/json.ts +0 -129
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/markdown.ts +0 -78
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/visual-doc.ts +0 -2333
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/accessibility.ts +0 -100
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/anatomy.ts +0 -32
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/color-tokens.ts +0 -59
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/content-guidance.ts +0 -18
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/design-tokens.ts +0 -53
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/interaction-rules.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/overview.ts +0 -91
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/properties-api.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/qa-criteria.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/related-components.ts +0 -110
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/responsive.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/size-specs.ts +0 -67
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/spacing-structure.ts +0 -58
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/state-specs.ts +0 -79
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/states.ts +0 -50
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/type-hierarchy.ts +0 -33
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/typography.ts +0 -55
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/usage-guidelines.ts +0 -73
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/variants.ts +0 -81
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/types.ts +0 -409
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/index.ts +0 -198
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/renderer.ts +0 -701
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/types.ts +0 -88
- package/figma-intelligence-layer/src/tools/phase5-governance/decision-log/index.ts +0 -135
- package/figma-intelligence-layer/src/tools/phase5-governance/design-decision-log/index.ts +0 -491
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-primitives/index.ts +0 -416
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-scaffolder/index.ts +0 -722
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-variables/index.ts +0 -449
- package/figma-intelligence-layer/src/tools/phase5-governance/health-report/index.ts +0 -393
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/index.ts +0 -406
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/figma-page.ts +0 -292
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/json.ts +0 -24
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/markdown.ts +0 -172
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/naming-guide.ts +0 -409
- package/figma-intelligence-layer/src/tools/phase5-governance/token-analytics/index.ts +0 -594
- package/figma-intelligence-layer/src/tools/phase5-governance/token-docs/index.ts +0 -710
- package/figma-intelligence-layer/src/tools/phase5-governance/token-migrate/index.ts +0 -458
- package/figma-intelligence-layer/src/tools/phase5-governance/token-naming/index.ts +0 -134
- package/figma-intelligence-layer/tests/apg-doc.test.ts +0 -101
- package/figma-intelligence-layer/tests/design-system-context.test.ts +0 -152
- package/figma-intelligence-layer/tests/design-system-matcher.test.ts +0 -144
- package/figma-intelligence-layer/tests/figma-bridge.test.ts +0 -83
- package/figma-intelligence-layer/tests/generate-image-and-insert.test.ts +0 -56
- package/figma-intelligence-layer/tests/screen-cloner-regression.test.ts +0 -69
- package/figma-intelligence-layer/tests/smoke.test.ts +0 -174
- package/figma-intelligence-layer/tests/spec-generator.test.ts +0 -127
- package/figma-intelligence-layer/tests/token-migrate.test.ts +0 -21
- package/figma-intelligence-layer/tests/token-naming.test.ts +0 -30
- package/figma-intelligence-layer/tsconfig.json +0 -19
- package/scripts/clean-existing-chunks.js +0 -179
- package/scripts/connect-ai-tool.js +0 -490
- package/scripts/convert-hub-pdfs.js +0 -425
- package/scripts/figma-mcp-status.js +0 -349
- package/scripts/register-codex-mcp.js +0 -96
- /package/{design-bridge → dist/design-bridge}/.env.example +0 -0
- /package/{design-bridge → dist/design-bridge}/package.json +0 -0
- /package/{figma-bridge-plugin → dist/figma-bridge-plugin}/manifest.json +0 -0
- /package/{figma-bridge-plugin → dist/figma-bridge-plugin}/package.json +0 -0
- /package/{figma-intelligence-layer → dist/figma-intelligence-layer}/package.json +0 -0
|
@@ -1,810 +0,0 @@
|
|
|
1
|
-
import { getBridge } from "../../../shared/figma-bridge.js";
|
|
2
|
-
import { decisionLog } from "../../../shared/decision-log.js";
|
|
3
|
-
import {
|
|
4
|
-
computeContrastRatio,
|
|
5
|
-
simulateColorBlindness,
|
|
6
|
-
figmaRgbaToHex,
|
|
7
|
-
meetsWCAG,
|
|
8
|
-
} from "../../../shared/token-utils.js";
|
|
9
|
-
import { FigmaNode, WCAGIssue, RGBA } from "../../../shared/types.js";
|
|
10
|
-
import {
|
|
11
|
-
checkNonTextContent,
|
|
12
|
-
checkInfoAndRelationships,
|
|
13
|
-
checkMeaningfulSequence,
|
|
14
|
-
checkUseOfColor,
|
|
15
|
-
checkNonTextContrast,
|
|
16
|
-
checkTextSpacing,
|
|
17
|
-
checkReflow,
|
|
18
|
-
checkImagesOfText,
|
|
19
|
-
checkContentOnHover,
|
|
20
|
-
checkFocusOrder,
|
|
21
|
-
checkLinkPurpose,
|
|
22
|
-
checkHeadingsAndLabels,
|
|
23
|
-
checkFocusNotObscured,
|
|
24
|
-
checkTargetSizeMinimum,
|
|
25
|
-
checkLabelsOrInstructions,
|
|
26
|
-
checkErrorIdentification,
|
|
27
|
-
checkNameRoleValue,
|
|
28
|
-
checkIdentifyInputPurpose,
|
|
29
|
-
} from "./wcag-checker.js";
|
|
30
|
-
import { buildVPATReport, VPATReport, DesignContext } from "./vpat-report.js";
|
|
31
|
-
import { renderVPATPage } from "./vpat-figma-page.js";
|
|
32
|
-
|
|
33
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
-
// Types
|
|
35
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
-
|
|
37
|
-
export interface A11yAuditArgs {
|
|
38
|
-
nodeId: string;
|
|
39
|
-
wcagLevel: "A" | "AA" | "AAA";
|
|
40
|
-
includeColorBlindSim?: boolean;
|
|
41
|
-
outputFormat: "inline" | "report" | "both";
|
|
42
|
-
autoSuggestFixes?: boolean;
|
|
43
|
-
reportFormat?: "issues-only" | "vpat";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface ColorBlindSimResult {
|
|
47
|
-
profile: string;
|
|
48
|
-
contrastRatio: number;
|
|
49
|
-
passes: boolean;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
interface ExtendedWCAGIssue extends WCAGIssue {
|
|
53
|
-
colorBlindSims?: ColorBlindSimResult[];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface A11yAuditResult {
|
|
57
|
-
nodeId: string;
|
|
58
|
-
wcagLevel: "A" | "AA" | "AAA";
|
|
59
|
-
totalChecks: number;
|
|
60
|
-
passed: number;
|
|
61
|
-
failed: number;
|
|
62
|
-
passRate: string;
|
|
63
|
-
issues: ExtendedWCAGIssue[];
|
|
64
|
-
annotationsAdded: number;
|
|
65
|
-
vpatReport?: VPATReport;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
-
// Constants
|
|
70
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
-
|
|
72
|
-
const MIN_TOUCH_TARGET = 44;
|
|
73
|
-
const CB_PROFILES = [
|
|
74
|
-
"protanopia",
|
|
75
|
-
"deuteranopia",
|
|
76
|
-
"tritanopia",
|
|
77
|
-
"achromatopsia",
|
|
78
|
-
] as const;
|
|
79
|
-
|
|
80
|
-
type CBProfile = typeof CB_PROFILES[number];
|
|
81
|
-
|
|
82
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
83
|
-
// Node tree helpers
|
|
84
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
85
|
-
|
|
86
|
-
function collectNodes(
|
|
87
|
-
node: FigmaNode,
|
|
88
|
-
predicate: (n: FigmaNode) => boolean,
|
|
89
|
-
acc: FigmaNode[] = []
|
|
90
|
-
): FigmaNode[] {
|
|
91
|
-
if (predicate(node)) acc.push(node);
|
|
92
|
-
if (node.children) {
|
|
93
|
-
for (const child of node.children) {
|
|
94
|
-
collectNodes(child, predicate, acc);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return acc;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
101
|
-
// Design context extraction for rich VPAT remarks
|
|
102
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
-
|
|
104
|
-
const NAV_PATTERNS = /\b(nav|navigation|header|top[-_ ]?bar|app[-_ ]?bar|navbar|menu)\b/i;
|
|
105
|
-
const FORM_PATTERNS = /\b(input|field|textbox|text[-_ ]?field|checkbox|radio|toggle|select|dropdown|form)\b/i;
|
|
106
|
-
const HEADING_MIN_SIZE = 20;
|
|
107
|
-
const HEADING_MIN_WEIGHT = 700;
|
|
108
|
-
|
|
109
|
-
function buildDesignContext(
|
|
110
|
-
rootNode: FigmaNode,
|
|
111
|
-
allNodes: FigmaNode[],
|
|
112
|
-
textNodes: FigmaNode[],
|
|
113
|
-
interactiveNodes: FigmaNode[],
|
|
114
|
-
componentSetNodes: FigmaNode[],
|
|
115
|
-
frameNodes: FigmaNode[]
|
|
116
|
-
): DesignContext {
|
|
117
|
-
// Detect landmarks
|
|
118
|
-
const landmarkNames: string[] = [];
|
|
119
|
-
for (const node of allNodes) {
|
|
120
|
-
if (NAV_PATTERNS.test(node.name)) {
|
|
121
|
-
const clean = node.name.replace(/[-_/]/g, " ").trim();
|
|
122
|
-
if (!landmarkNames.includes(clean)) landmarkNames.push(clean);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Detect images/vectors
|
|
127
|
-
const imageNodes = allNodes.filter(
|
|
128
|
-
(n) => n.type === "VECTOR" || n.type === "BOOLEAN_OPERATION" ||
|
|
129
|
-
(n.fills?.some((f) => f.type === "IMAGE"))
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
// Detect headings
|
|
133
|
-
const headingTexts: string[] = [];
|
|
134
|
-
for (const node of textNodes) {
|
|
135
|
-
const fontSize = node.style?.fontSize ?? 16;
|
|
136
|
-
const fontWeight = node.style?.fontWeight ?? 400;
|
|
137
|
-
if ((fontSize >= HEADING_MIN_SIZE || fontWeight >= HEADING_MIN_WEIGHT) && node.characters) {
|
|
138
|
-
const text = node.characters.trim();
|
|
139
|
-
if (text.length > 0 && text.length < 100 && headingTexts.length < 10) {
|
|
140
|
-
headingTexts.push(text);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Detect form inputs
|
|
146
|
-
const hasFormInputs = interactiveNodes.some((n) => FORM_PATTERNS.test(n.name));
|
|
147
|
-
|
|
148
|
-
// Detect navigation
|
|
149
|
-
const hasNavigation = allNodes.some((n) => NAV_PATTERNS.test(n.name));
|
|
150
|
-
|
|
151
|
-
// Detect auto-layout
|
|
152
|
-
const hasAutoLayout = frameNodes.some((n) => n.layoutMode === "HORIZONTAL" || n.layoutMode === "VERTICAL");
|
|
153
|
-
|
|
154
|
-
// Interactive labels (first N)
|
|
155
|
-
const interactiveLabels: string[] = [];
|
|
156
|
-
for (const node of interactiveNodes) {
|
|
157
|
-
let label = "";
|
|
158
|
-
// Try to get text content
|
|
159
|
-
if (node.characters) {
|
|
160
|
-
label = node.characters.trim();
|
|
161
|
-
} else if (node.children) {
|
|
162
|
-
const textChild = findFirstText(node);
|
|
163
|
-
if (textChild) label = textChild;
|
|
164
|
-
}
|
|
165
|
-
if (!label) label = node.name.replace(/[-_/]/g, " ").trim();
|
|
166
|
-
if (label && label.length < 60 && interactiveLabels.length < 15) {
|
|
167
|
-
interactiveLabels.push(label);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Interactive breakdown by inferred type
|
|
172
|
-
const interactiveBreakdown: Record<string, number> = {};
|
|
173
|
-
for (const node of interactiveNodes) {
|
|
174
|
-
const name = node.name.toLowerCase();
|
|
175
|
-
let type = "interactive element";
|
|
176
|
-
if (/button|btn|cta/i.test(name)) type = "button";
|
|
177
|
-
else if (/link|anchor/i.test(name)) type = "link";
|
|
178
|
-
else if (/input|field|textbox/i.test(name)) type = "text input";
|
|
179
|
-
else if (/checkbox/i.test(name)) type = "checkbox";
|
|
180
|
-
else if (/radio/i.test(name)) type = "radio";
|
|
181
|
-
else if (/toggle|switch/i.test(name)) type = "toggle";
|
|
182
|
-
else if (/select|dropdown/i.test(name)) type = "dropdown";
|
|
183
|
-
else if (/search/i.test(name)) type = "search";
|
|
184
|
-
else if (/tab/i.test(name)) type = "tab";
|
|
185
|
-
else if (/menu/i.test(name)) type = "menu";
|
|
186
|
-
else if (/modal|dialog/i.test(name)) type = "modal";
|
|
187
|
-
else if (/slider|range/i.test(name)) type = "slider";
|
|
188
|
-
interactiveBreakdown[type] = (interactiveBreakdown[type] || 0) + 1;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Sample texts
|
|
192
|
-
const sampleTexts: string[] = [];
|
|
193
|
-
for (const node of textNodes) {
|
|
194
|
-
if (node.characters && node.characters.trim().length > 2 && sampleTexts.length < 8) {
|
|
195
|
-
sampleTexts.push(node.characters.trim().slice(0, 80));
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return {
|
|
200
|
-
frameName: rootNode.name,
|
|
201
|
-
totalNodes: allNodes.length,
|
|
202
|
-
textNodeCount: textNodes.length,
|
|
203
|
-
interactiveCount: interactiveNodes.length,
|
|
204
|
-
imageCount: imageNodes.length,
|
|
205
|
-
componentSetCount: componentSetNodes.length,
|
|
206
|
-
frameCount: frameNodes.length,
|
|
207
|
-
landmarkNames,
|
|
208
|
-
interactiveLabels,
|
|
209
|
-
hasFormInputs,
|
|
210
|
-
hasNavigation,
|
|
211
|
-
hasImages: imageNodes.length > 0,
|
|
212
|
-
hasHeadings: headingTexts.length > 0,
|
|
213
|
-
headingTexts,
|
|
214
|
-
hasAutoLayout,
|
|
215
|
-
sampleTexts,
|
|
216
|
-
interactiveBreakdown,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function findFirstText(node: FigmaNode): string | null {
|
|
221
|
-
if (node.type === "TEXT" && node.characters) return node.characters.trim();
|
|
222
|
-
if (node.children) {
|
|
223
|
-
for (const child of node.children) {
|
|
224
|
-
const text = findFirstText(child);
|
|
225
|
-
if (text) return text;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
return null;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function extractSolidColor(node: FigmaNode): RGBA | null {
|
|
232
|
-
if (!node.fills || node.fills.length === 0) return null;
|
|
233
|
-
const solid = node.fills.find((f) => f.type === "SOLID" && f.color);
|
|
234
|
-
return solid?.color ?? null;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/** Walk up the node tree to find the nearest background color. */
|
|
238
|
-
function findParentBgColor(
|
|
239
|
-
nodeId: string,
|
|
240
|
-
allNodes: FigmaNode[]
|
|
241
|
-
): RGBA | null {
|
|
242
|
-
// Build a parent lookup from the flat list
|
|
243
|
-
function findParent(id: string, nodes: FigmaNode[]): FigmaNode | null {
|
|
244
|
-
for (const n of nodes) {
|
|
245
|
-
if (n.children?.some((c) => c.id === id)) return n;
|
|
246
|
-
if (n.children) {
|
|
247
|
-
const found = findParent(id, n.children);
|
|
248
|
-
if (found) return found;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
let current = nodeId;
|
|
255
|
-
for (let depth = 0; depth < 12; depth++) {
|
|
256
|
-
const parent = findParent(current, allNodes);
|
|
257
|
-
if (!parent) break;
|
|
258
|
-
const color = extractSolidColor(parent);
|
|
259
|
-
if (color) return color;
|
|
260
|
-
current = parent.id;
|
|
261
|
-
}
|
|
262
|
-
// Default to white if no bg found
|
|
263
|
-
return { r: 1, g: 1, b: 1, a: 1 };
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
267
|
-
// Audit checks
|
|
268
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
269
|
-
|
|
270
|
-
function checkTextContrast(
|
|
271
|
-
textNode: FigmaNode,
|
|
272
|
-
allNodes: FigmaNode[],
|
|
273
|
-
wcagLevel: "A" | "AA" | "AAA",
|
|
274
|
-
includeColorBlindSim: boolean
|
|
275
|
-
): ExtendedWCAGIssue | null {
|
|
276
|
-
const fgColor = extractSolidColor(textNode);
|
|
277
|
-
if (!fgColor) return null;
|
|
278
|
-
|
|
279
|
-
const bgColor = findParentBgColor(textNode.id, allNodes);
|
|
280
|
-
if (!bgColor) return null;
|
|
281
|
-
|
|
282
|
-
const fgHex = figmaRgbaToHex(fgColor.r, fgColor.g, fgColor.b);
|
|
283
|
-
const bgHex = figmaRgbaToHex(bgColor.r, bgColor.g, bgColor.b);
|
|
284
|
-
|
|
285
|
-
const fontSize = textNode.style?.fontSize ?? 16;
|
|
286
|
-
const fontWeight = textNode.style?.fontWeight ?? 400;
|
|
287
|
-
const isLargeText = fontSize >= 18 || (fontSize >= 14 && fontWeight >= 700);
|
|
288
|
-
|
|
289
|
-
const ratio = computeContrastRatio(fgHex, bgHex);
|
|
290
|
-
|
|
291
|
-
// WCAG A uses AA thresholds for contrast; AAA uses higher thresholds
|
|
292
|
-
const effectiveLevel: "AA" | "AAA" =
|
|
293
|
-
wcagLevel === "AAA" ? "AAA" : "AA";
|
|
294
|
-
const passes = meetsWCAG(ratio, effectiveLevel, isLargeText);
|
|
295
|
-
|
|
296
|
-
if (passes) return null;
|
|
297
|
-
|
|
298
|
-
const issue: ExtendedWCAGIssue = {
|
|
299
|
-
severity: ratio < 3.0 ? "error" : "warning",
|
|
300
|
-
criterion: wcagLevel === "AAA" ? "1.4.6 Contrast (Enhanced)" : "1.4.3 Contrast (Minimum)",
|
|
301
|
-
nodeId: textNode.id,
|
|
302
|
-
nodeName: textNode.name,
|
|
303
|
-
issue: `Text contrast ratio ${ratio.toFixed(2)}:1 is below the ${effectiveLevel} threshold (${isLargeText ? (effectiveLevel === "AAA" ? "4.5" : "3.0") : (effectiveLevel === "AAA" ? "7.0" : "4.5")}:1) for ${isLargeText ? "large" : "normal"} text`,
|
|
304
|
-
currentValue: `${ratio.toFixed(2)}:1`,
|
|
305
|
-
suggestedFix: `Increase contrast to at least ${isLargeText ? (effectiveLevel === "AAA" ? "4.5" : "3.0") : (effectiveLevel === "AAA" ? "7.0" : "4.5")}:1 by darkening text or lightening background`,
|
|
306
|
-
autoFixAvailable: true,
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
if (includeColorBlindSim) {
|
|
310
|
-
issue.colorBlindSims = CB_PROFILES.map((profile: CBProfile) => {
|
|
311
|
-
const simFg = simulateColorBlindness(fgHex, profile);
|
|
312
|
-
const simBg = simulateColorBlindness(bgHex, profile);
|
|
313
|
-
const simRatio = computeContrastRatio(simFg, simBg);
|
|
314
|
-
const simPasses = meetsWCAG(simRatio, effectiveLevel, isLargeText);
|
|
315
|
-
return { profile, contrastRatio: simRatio, passes: simPasses };
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return issue;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function checkTouchTarget(node: FigmaNode): WCAGIssue | null {
|
|
323
|
-
if (!node.absoluteBoundingBox) return null;
|
|
324
|
-
if (!node.width || !node.height) return null;
|
|
325
|
-
|
|
326
|
-
const w = node.width;
|
|
327
|
-
const h = node.height;
|
|
328
|
-
|
|
329
|
-
if (w < MIN_TOUCH_TARGET || h < MIN_TOUCH_TARGET) {
|
|
330
|
-
return {
|
|
331
|
-
severity: "error",
|
|
332
|
-
criterion: "2.5.5 Target Size",
|
|
333
|
-
nodeId: node.id,
|
|
334
|
-
nodeName: node.name,
|
|
335
|
-
issue: `Interactive element is ${w}×${h}px, below the minimum 44×44px touch target`,
|
|
336
|
-
currentValue: `${w}×${h}px`,
|
|
337
|
-
suggestedFix: `Resize to at least ${MIN_TOUCH_TARGET}×${MIN_TOUCH_TARGET}px or add invisible padding`,
|
|
338
|
-
autoFixAvailable: true,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function checkFocusState(node: FigmaNode): WCAGIssue | null {
|
|
345
|
-
if (node.type !== "COMPONENT_SET") return null;
|
|
346
|
-
|
|
347
|
-
const variantProps = node.variantProperties ?? {};
|
|
348
|
-
const hasFocusVariant = Object.values(variantProps).some(
|
|
349
|
-
(v) => typeof v === "string" && v.toLowerCase().includes("focus")
|
|
350
|
-
);
|
|
351
|
-
const childFocus = node.children?.some(
|
|
352
|
-
(c) =>
|
|
353
|
-
c.name.toLowerCase().includes("focus") ||
|
|
354
|
-
Object.values(c.variantProperties ?? {}).some(
|
|
355
|
-
(v) => typeof v === "string" && v.toLowerCase().includes("focus")
|
|
356
|
-
)
|
|
357
|
-
);
|
|
358
|
-
|
|
359
|
-
if (!hasFocusVariant && !childFocus) {
|
|
360
|
-
return {
|
|
361
|
-
severity: "warning",
|
|
362
|
-
criterion: "2.4.7 Focus Visible",
|
|
363
|
-
nodeId: node.id,
|
|
364
|
-
nodeName: node.name,
|
|
365
|
-
issue: "Component set has no focus state variant",
|
|
366
|
-
currentValue: "No focus variant",
|
|
367
|
-
suggestedFix: 'Add a "State=Focus" variant with a visible 2px focus ring',
|
|
368
|
-
autoFixAvailable: false,
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
return null;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function checkFixedHeightTextContainer(node: FigmaNode): WCAGIssue | null {
|
|
375
|
-
if (!node.children) return null;
|
|
376
|
-
|
|
377
|
-
const hasTextChild = node.children.some((c) => c.type === "TEXT");
|
|
378
|
-
if (!hasTextChild) return null;
|
|
379
|
-
|
|
380
|
-
const isFixedHeight =
|
|
381
|
-
node.primaryAxisSizingMode === "FIXED" ||
|
|
382
|
-
(node.height !== undefined && node.primaryAxisSizingMode !== "AUTO");
|
|
383
|
-
|
|
384
|
-
if (isFixedHeight && node.layoutMode && node.layoutMode !== "NONE") {
|
|
385
|
-
return {
|
|
386
|
-
severity: "warning",
|
|
387
|
-
criterion: "1.4.4 Resize Text",
|
|
388
|
-
nodeId: node.id,
|
|
389
|
-
nodeName: node.name,
|
|
390
|
-
issue: "Text container has fixed height which may clip text when font size is increased",
|
|
391
|
-
currentValue: `height: ${node.height}px (fixed)`,
|
|
392
|
-
suggestedFix: 'Set vertical sizing to "Hug contents" (AUTO) so text can reflow',
|
|
393
|
-
autoFixAvailable: true,
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
return null;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
400
|
-
// Inline annotation script
|
|
401
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
402
|
-
|
|
403
|
-
function buildAnnotationScript(issues: WCAGIssue[]): string {
|
|
404
|
-
return `
|
|
405
|
-
(async () => {
|
|
406
|
-
const issues = ${JSON.stringify(issues)};
|
|
407
|
-
const created = [];
|
|
408
|
-
const severityColors = {
|
|
409
|
-
error: { r: 0.96, g: 0.26, b: 0.21 },
|
|
410
|
-
warning: { r: 1, g: 0.76, b: 0 },
|
|
411
|
-
suggestion: { r: 0.13, g: 0.59, b: 0.95 },
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
for (const issue of issues) {
|
|
415
|
-
const target = await figma.getNodeByIdAsync(issue.nodeId);
|
|
416
|
-
if (!target) continue;
|
|
417
|
-
|
|
418
|
-
const sticky = figma.createSticky();
|
|
419
|
-
sticky.text.characters =
|
|
420
|
-
"[" + issue.criterion + "] " + issue.severity.toUpperCase() + "\\n" +
|
|
421
|
-
issue.issue + "\\nFix: " + issue.suggestedFix;
|
|
422
|
-
const color = severityColors[issue.severity] || severityColors.suggestion;
|
|
423
|
-
sticky.fills = [{ type: "SOLID", color }];
|
|
424
|
-
|
|
425
|
-
const bbox = target.absoluteBoundingBox;
|
|
426
|
-
if (bbox) {
|
|
427
|
-
sticky.x = bbox.x + bbox.width + 16;
|
|
428
|
-
sticky.y = bbox.y;
|
|
429
|
-
}
|
|
430
|
-
figma.currentPage.appendChild(sticky);
|
|
431
|
-
created.push(sticky.id);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
return { count: created.length, ids: created };
|
|
435
|
-
})();
|
|
436
|
-
`;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
440
|
-
// Handler
|
|
441
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
442
|
-
|
|
443
|
-
export async function a11yAuditHandler(
|
|
444
|
-
args: A11yAuditArgs
|
|
445
|
-
): Promise<A11yAuditResult> {
|
|
446
|
-
const {
|
|
447
|
-
nodeId,
|
|
448
|
-
wcagLevel,
|
|
449
|
-
includeColorBlindSim = false,
|
|
450
|
-
outputFormat,
|
|
451
|
-
autoSuggestFixes = false,
|
|
452
|
-
reportFormat = "vpat",
|
|
453
|
-
} = args;
|
|
454
|
-
|
|
455
|
-
const bridge = await getBridge();
|
|
456
|
-
|
|
457
|
-
// 1. Retrieve full node tree for the target node
|
|
458
|
-
const treeScript = `
|
|
459
|
-
function serialize(n, depth) {
|
|
460
|
-
if (depth > 8) return null;
|
|
461
|
-
var fills = [];
|
|
462
|
-
try {
|
|
463
|
-
var rawFills = n.fills || [];
|
|
464
|
-
for (var i = 0; i < rawFills.length; i++) {
|
|
465
|
-
var f = rawFills[i];
|
|
466
|
-
fills.push({
|
|
467
|
-
type: f.type,
|
|
468
|
-
color: f.color ? { r: f.color.r, g: f.color.g, b: f.color.b, a: f.color.a } : null,
|
|
469
|
-
opacity: f.opacity || 1,
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
} catch(e) { /* mixed fills */ }
|
|
473
|
-
var kids = [];
|
|
474
|
-
var rawChildren = n.children || [];
|
|
475
|
-
for (var j = 0; j < rawChildren.length; j++) {
|
|
476
|
-
var child = serialize(rawChildren[j], depth + 1);
|
|
477
|
-
if (child) kids.push(child);
|
|
478
|
-
}
|
|
479
|
-
var strokes = [];
|
|
480
|
-
try {
|
|
481
|
-
var rawStrokes = n.strokes || [];
|
|
482
|
-
for (var s = 0; s < rawStrokes.length; s++) {
|
|
483
|
-
var st = rawStrokes[s];
|
|
484
|
-
strokes.push({
|
|
485
|
-
type: st.type,
|
|
486
|
-
color: st.color ? { r: st.color.r, g: st.color.g, b: st.color.b, a: st.color.a } : null,
|
|
487
|
-
opacity: st.opacity || 1,
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
} catch(e) { /* mixed strokes */ }
|
|
491
|
-
var effects = [];
|
|
492
|
-
try {
|
|
493
|
-
var rawEffects = n.effects || [];
|
|
494
|
-
for (var ef = 0; ef < rawEffects.length; ef++) {
|
|
495
|
-
var eff = rawEffects[ef];
|
|
496
|
-
effects.push({
|
|
497
|
-
type: eff.type,
|
|
498
|
-
color: eff.color ? { r: eff.color.r, g: eff.color.g, b: eff.color.b, a: eff.color.a } : null,
|
|
499
|
-
visible: eff.visible !== false,
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
} catch(e) { /* effects */ }
|
|
503
|
-
return {
|
|
504
|
-
id: n.id,
|
|
505
|
-
name: n.name,
|
|
506
|
-
type: n.type,
|
|
507
|
-
width: n.width || null,
|
|
508
|
-
height: n.height || null,
|
|
509
|
-
layoutMode: n.layoutMode || null,
|
|
510
|
-
primaryAxisSizingMode: n.primaryAxisSizingMode || null,
|
|
511
|
-
counterAxisSizingMode: n.counterAxisSizingMode || null,
|
|
512
|
-
variantProperties: n.variantProperties || null,
|
|
513
|
-
absoluteBoundingBox: n.absoluteBoundingBox || null,
|
|
514
|
-
characters: n.characters || null,
|
|
515
|
-
description: n.description || null,
|
|
516
|
-
style: n.style || null,
|
|
517
|
-
fills: fills,
|
|
518
|
-
strokes: strokes,
|
|
519
|
-
effects: effects,
|
|
520
|
-
children: kids,
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
var root = await figma.getNodeByIdAsync(${JSON.stringify(nodeId)});
|
|
524
|
-
if (!root) throw new Error("Node not found: " + ${JSON.stringify(nodeId)});
|
|
525
|
-
return serialize(root, 0);
|
|
526
|
-
`;
|
|
527
|
-
|
|
528
|
-
const treeResult = await bridge.execute(treeScript);
|
|
529
|
-
if (!treeResult.success) {
|
|
530
|
-
throw new Error(`a11yAuditHandler: could not retrieve node tree — ${treeResult.error}`);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
const rootNode = treeResult.result as FigmaNode;
|
|
534
|
-
|
|
535
|
-
// 2. Collect all relevant node types
|
|
536
|
-
const allNodes: FigmaNode[] = [];
|
|
537
|
-
collectNodes(rootNode, () => true, allNodes);
|
|
538
|
-
|
|
539
|
-
const textNodes = allNodes.filter((n) => n.type === "TEXT");
|
|
540
|
-
const interactiveNodes = allNodes.filter(
|
|
541
|
-
(n) =>
|
|
542
|
-
n.type === "INSTANCE" ||
|
|
543
|
-
n.type === "COMPONENT" ||
|
|
544
|
-
(n.name.toLowerCase().includes("button") ||
|
|
545
|
-
n.name.toLowerCase().includes("link") ||
|
|
546
|
-
n.name.toLowerCase().includes("input") ||
|
|
547
|
-
n.name.toLowerCase().includes("checkbox") ||
|
|
548
|
-
n.name.toLowerCase().includes("radio") ||
|
|
549
|
-
n.name.toLowerCase().includes("toggle") ||
|
|
550
|
-
n.name.toLowerCase().includes("select"))
|
|
551
|
-
);
|
|
552
|
-
const componentSetNodes = allNodes.filter((n) => n.type === "COMPONENT_SET");
|
|
553
|
-
const frameNodes = allNodes.filter(
|
|
554
|
-
(n) => n.type === "FRAME" || n.type === "GROUP"
|
|
555
|
-
);
|
|
556
|
-
|
|
557
|
-
const issues: ExtendedWCAGIssue[] = [];
|
|
558
|
-
|
|
559
|
-
// 3. Text contrast checks
|
|
560
|
-
for (const node of textNodes) {
|
|
561
|
-
const issue = checkTextContrast(node, allNodes, wcagLevel, includeColorBlindSim);
|
|
562
|
-
if (issue) issues.push(issue);
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// 4. Touch target size checks (only for WCAG AA and AAA, as 2.5.5 is Level AAA in WCAG 2.1 but commonly applied)
|
|
566
|
-
for (const node of interactiveNodes) {
|
|
567
|
-
const issue = checkTouchTarget(node);
|
|
568
|
-
if (issue) {
|
|
569
|
-
// Level A: skip touch target (not required). AA+: include.
|
|
570
|
-
if (wcagLevel !== "A") issues.push(issue);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// 5. Focus state variant checks
|
|
575
|
-
for (const node of componentSetNodes) {
|
|
576
|
-
const issue = checkFocusState(node);
|
|
577
|
-
if (issue) issues.push(issue);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// 6. Fixed-height text container checks
|
|
581
|
-
for (const node of frameNodes) {
|
|
582
|
-
const issue = checkFixedHeightTextContainer(node);
|
|
583
|
-
if (issue) issues.push(issue);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// ─── New comprehensive checks ─────────────────────────────────────────────
|
|
587
|
-
|
|
588
|
-
// SC 1.1.1 Non-text Content (Level A) – heuristic
|
|
589
|
-
for (const node of allNodes) {
|
|
590
|
-
const issue = checkNonTextContent(node, allNodes);
|
|
591
|
-
if (issue) issues.push(issue);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// SC 1.3.1 Info and Relationships (Level A) – heuristic
|
|
595
|
-
issues.push(...checkInfoAndRelationships(textNodes));
|
|
596
|
-
|
|
597
|
-
// SC 1.3.2 Meaningful Sequence (Level A) – heuristic
|
|
598
|
-
issues.push(...checkMeaningfulSequence(allNodes));
|
|
599
|
-
|
|
600
|
-
// SC 1.4.1 Use of Color (Level A) – heuristic
|
|
601
|
-
issues.push(...checkUseOfColor(componentSetNodes));
|
|
602
|
-
|
|
603
|
-
// SC 3.3.1 Error Identification (Level A) – heuristic
|
|
604
|
-
issues.push(...checkErrorIdentification(componentSetNodes));
|
|
605
|
-
|
|
606
|
-
// SC 3.3.2 Labels or Instructions (Level A) – heuristic
|
|
607
|
-
issues.push(...checkLabelsOrInstructions(interactiveNodes, allNodes));
|
|
608
|
-
|
|
609
|
-
// SC 2.4.3 Focus Order (Level A) – heuristic
|
|
610
|
-
issues.push(...checkFocusOrder(interactiveNodes));
|
|
611
|
-
|
|
612
|
-
// SC 2.4.4 Link Purpose (Level A) – heuristic
|
|
613
|
-
issues.push(...checkLinkPurpose(interactiveNodes));
|
|
614
|
-
|
|
615
|
-
// SC 4.1.2 Name, Role, Value (Level A) – heuristic
|
|
616
|
-
issues.push(...checkNameRoleValue(interactiveNodes));
|
|
617
|
-
|
|
618
|
-
// Level AA+ checks
|
|
619
|
-
if (wcagLevel !== "A") {
|
|
620
|
-
// SC 1.4.11 Non-text Contrast (Level AA) – automated
|
|
621
|
-
for (const node of interactiveNodes) {
|
|
622
|
-
const issue = checkNonTextContrast(node, allNodes);
|
|
623
|
-
if (issue) issues.push(issue);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// SC 1.4.12 Text Spacing (Level AA) – automated
|
|
627
|
-
for (const node of textNodes) {
|
|
628
|
-
const issue = checkTextSpacing(node);
|
|
629
|
-
if (issue) issues.push(issue);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// SC 1.4.10 Reflow (Level AA) – heuristic
|
|
633
|
-
issues.push(...checkReflow(frameNodes));
|
|
634
|
-
|
|
635
|
-
// SC 1.4.5 Images of Text (Level AA) – heuristic
|
|
636
|
-
issues.push(...checkImagesOfText(allNodes));
|
|
637
|
-
|
|
638
|
-
// SC 1.4.13 Content on Hover (Level AA) – heuristic
|
|
639
|
-
issues.push(...checkContentOnHover(componentSetNodes));
|
|
640
|
-
|
|
641
|
-
// SC 2.4.6 Headings and Labels (Level AA) – heuristic
|
|
642
|
-
issues.push(...checkHeadingsAndLabels(frameNodes));
|
|
643
|
-
|
|
644
|
-
// SC 2.4.11 Focus Not Obscured (Level AA) – heuristic
|
|
645
|
-
issues.push(...checkFocusNotObscured(componentSetNodes));
|
|
646
|
-
|
|
647
|
-
// SC 2.5.8 Target Size Minimum 24px (Level AA) – automated
|
|
648
|
-
for (const node of interactiveNodes) {
|
|
649
|
-
const issue = checkTargetSizeMinimum(node);
|
|
650
|
-
if (issue) issues.push(issue);
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// SC 1.3.5 Identify Input Purpose (Level AA) – heuristic
|
|
654
|
-
issues.push(...checkIdentifyInputPurpose(interactiveNodes));
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// 7. Color blind simulation re-run on previously passed text nodes (if requested)
|
|
658
|
-
if (includeColorBlindSim) {
|
|
659
|
-
for (const node of textNodes) {
|
|
660
|
-
const fgColor = extractSolidColor(node);
|
|
661
|
-
if (!fgColor) continue;
|
|
662
|
-
const bgColor = findParentBgColor(node.id, allNodes);
|
|
663
|
-
if (!bgColor) continue;
|
|
664
|
-
|
|
665
|
-
const fgHex = figmaRgbaToHex(fgColor.r, fgColor.g, fgColor.b);
|
|
666
|
-
const bgHex = figmaRgbaToHex(bgColor.r, bgColor.g, bgColor.b);
|
|
667
|
-
const fontSize = node.style?.fontSize ?? 16;
|
|
668
|
-
const fontWeight = node.style?.fontWeight ?? 400;
|
|
669
|
-
const isLargeText = fontSize >= 18 || (fontSize >= 14 && fontWeight >= 700);
|
|
670
|
-
const effectiveLevel: "AA" | "AAA" = wcagLevel === "AAA" ? "AAA" : "AA";
|
|
671
|
-
|
|
672
|
-
// Check if any CB profile causes a failure not already caught
|
|
673
|
-
for (const profile of CB_PROFILES) {
|
|
674
|
-
const simFg = simulateColorBlindness(fgHex, profile);
|
|
675
|
-
const simBg = simulateColorBlindness(bgHex, profile);
|
|
676
|
-
const simRatio = computeContrastRatio(simFg, simBg);
|
|
677
|
-
const simPasses = meetsWCAG(simRatio, effectiveLevel, isLargeText);
|
|
678
|
-
|
|
679
|
-
if (!simPasses) {
|
|
680
|
-
// Only add if not already reported for this node
|
|
681
|
-
const alreadyReported = issues.some(
|
|
682
|
-
(i) => i.nodeId === node.id && i.criterion.includes("1.4.3")
|
|
683
|
-
);
|
|
684
|
-
if (!alreadyReported) {
|
|
685
|
-
issues.push({
|
|
686
|
-
severity: "warning",
|
|
687
|
-
criterion: "1.4.3 Contrast (Minimum) — Color Vision",
|
|
688
|
-
nodeId: node.id,
|
|
689
|
-
nodeName: node.name,
|
|
690
|
-
issue: `Contrast ratio ${simRatio.toFixed(2)}:1 fails under ${profile} simulation`,
|
|
691
|
-
currentValue: `${simRatio.toFixed(2)}:1 (${profile})`,
|
|
692
|
-
suggestedFix:
|
|
693
|
-
"Avoid relying solely on color to convey information; ensure contrast works for all vision types",
|
|
694
|
-
autoFixAvailable: false,
|
|
695
|
-
colorBlindSims: [{ profile, contrastRatio: simRatio, passes: simPasses }],
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// 8. Statistics
|
|
704
|
-
const totalChecks =
|
|
705
|
-
textNodes.length +
|
|
706
|
-
(wcagLevel !== "A" ? interactiveNodes.length : 0) +
|
|
707
|
-
componentSetNodes.length +
|
|
708
|
-
frameNodes.length;
|
|
709
|
-
const failed = issues.length;
|
|
710
|
-
const passed = Math.max(0, totalChecks - failed);
|
|
711
|
-
const passRate =
|
|
712
|
-
totalChecks > 0 ? `${Math.round((passed / totalChecks) * 100)}%` : "100%";
|
|
713
|
-
|
|
714
|
-
// 9. Add auto-fix suggestions where applicable
|
|
715
|
-
if (autoSuggestFixes) {
|
|
716
|
-
for (const issue of issues) {
|
|
717
|
-
if (!issue.autoFixAvailable) {
|
|
718
|
-
issue.suggestedFix += " (manual intervention required)";
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// 10. Inline annotations in Figma
|
|
724
|
-
let annotationsAdded = 0;
|
|
725
|
-
if ((outputFormat === "inline" || outputFormat === "both") && issues.length > 0) {
|
|
726
|
-
const annotScript = buildAnnotationScript(issues);
|
|
727
|
-
const annotResult = await bridge.execute(annotScript);
|
|
728
|
-
if (annotResult.success) {
|
|
729
|
-
const data = annotResult.result as { count: number };
|
|
730
|
-
annotationsAdded = data.count;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// 11. Log action
|
|
735
|
-
await decisionLog.log({
|
|
736
|
-
tool: "figma_a11y_audit",
|
|
737
|
-
nodeIds: [nodeId],
|
|
738
|
-
rationale: `Accessibility audit at WCAG ${wcagLevel} level. Checked ${totalChecks} elements. Pass rate: ${passRate}. Found ${failed} issues across ${textNodes.length} text nodes, ${interactiveNodes.length} interactive elements, ${componentSetNodes.length} component sets.${includeColorBlindSim ? " Color blind simulation included." : ""}`,
|
|
739
|
-
reversible: false,
|
|
740
|
-
metadata: {
|
|
741
|
-
wcagLevel,
|
|
742
|
-
totalChecks,
|
|
743
|
-
passed,
|
|
744
|
-
failed,
|
|
745
|
-
passRate,
|
|
746
|
-
includeColorBlindSim,
|
|
747
|
-
annotationsAdded,
|
|
748
|
-
},
|
|
749
|
-
});
|
|
750
|
-
|
|
751
|
-
// 12. Build design context for rich VPAT remarks
|
|
752
|
-
const designContext: DesignContext = buildDesignContext(
|
|
753
|
-
rootNode,
|
|
754
|
-
allNodes,
|
|
755
|
-
textNodes,
|
|
756
|
-
interactiveNodes,
|
|
757
|
-
componentSetNodes,
|
|
758
|
-
frameNodes
|
|
759
|
-
);
|
|
760
|
-
|
|
761
|
-
// 13. Build VPAT report
|
|
762
|
-
let vpatReport: VPATReport | undefined;
|
|
763
|
-
if (reportFormat === "vpat") {
|
|
764
|
-
vpatReport = buildVPATReport(
|
|
765
|
-
wcagLevel,
|
|
766
|
-
nodeId,
|
|
767
|
-
rootNode.name,
|
|
768
|
-
issues,
|
|
769
|
-
designContext
|
|
770
|
-
);
|
|
771
|
-
|
|
772
|
-
// 13. Render VPAT as a structured Figma page
|
|
773
|
-
try {
|
|
774
|
-
await renderVPATPage(bridge, vpatReport);
|
|
775
|
-
} catch (err) {
|
|
776
|
-
// Non-fatal — the markdown report is still available
|
|
777
|
-
console.error("VPAT Figma page render failed:", err);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// In VPAT mode, return the full conformance report as the primary output.
|
|
781
|
-
return {
|
|
782
|
-
nodeId,
|
|
783
|
-
wcagLevel,
|
|
784
|
-
totalChecks: vpatReport.summary.totalCriteria,
|
|
785
|
-
passed: vpatReport.summary.supports,
|
|
786
|
-
failed: vpatReport.summary.doesNotSupport,
|
|
787
|
-
passRate: vpatReport.summary.totalCriteria > 0
|
|
788
|
-
? `${Math.round(
|
|
789
|
-
((vpatReport.summary.supports + vpatReport.summary.partiallySupports) /
|
|
790
|
-
vpatReport.summary.totalCriteria) *
|
|
791
|
-
100
|
|
792
|
-
)}%`
|
|
793
|
-
: "100%",
|
|
794
|
-
issues,
|
|
795
|
-
annotationsAdded,
|
|
796
|
-
vpatReport,
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
return {
|
|
801
|
-
nodeId,
|
|
802
|
-
wcagLevel,
|
|
803
|
-
totalChecks,
|
|
804
|
-
passed,
|
|
805
|
-
failed,
|
|
806
|
-
passRate,
|
|
807
|
-
issues,
|
|
808
|
-
annotationsAdded,
|
|
809
|
-
};
|
|
810
|
-
}
|