@sarjallab09/figma-intelligence 1.0.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/LICENSE +26 -0
- package/README.md +327 -0
- package/bin/cli.js +859 -0
- package/design-bridge/.env.example +5 -0
- package/design-bridge/bridge.js +196 -0
- package/design-bridge/lib/assets.js +367 -0
- package/design-bridge/lib/prompt.js +85 -0
- package/design-bridge/lib/server.js +66 -0
- package/design-bridge/lib/stitch.js +37 -0
- package/design-bridge/lib/tokens.js +82 -0
- package/design-bridge/package-lock.json +579 -0
- package/design-bridge/package.json +19 -0
- package/figma-bridge-plugin/README.md +97 -0
- package/figma-bridge-plugin/anthropic-chat-runner.js +192 -0
- package/figma-bridge-plugin/bridge-relay.js +2363 -0
- package/figma-bridge-plugin/chat-runner.js +459 -0
- package/figma-bridge-plugin/code.js +1528 -0
- package/figma-bridge-plugin/codex-runner.js +505 -0
- package/figma-bridge-plugin/component-schemas.js +110 -0
- package/figma-bridge-plugin/content-context.js +869 -0
- package/figma-bridge-plugin/create-button.js +216 -0
- package/figma-bridge-plugin/gemini-cli-runner.js +291 -0
- package/figma-bridge-plugin/gemini-runner.js +187 -0
- package/figma-bridge-plugin/html-to-figma.js +927 -0
- package/figma-bridge-plugin/knowledge-hub/.gitkeep +0 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/anatomy-spec.md +159 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/api-spec.md +162 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/color-spec.md +148 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/full-spec-template.md +314 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/property-spec.md +175 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/screen-reader-spec.md +180 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/structure-spec.md +165 -0
- package/figma-bridge-plugin/manifest.json +21 -0
- package/figma-bridge-plugin/package-lock.json +1936 -0
- package/figma-bridge-plugin/package.json +20 -0
- package/figma-bridge-plugin/perplexity-runner.js +188 -0
- package/figma-bridge-plugin/references/SKILL.md +178 -0
- package/figma-bridge-plugin/references/anatomy-spec.md +159 -0
- package/figma-bridge-plugin/references/api-spec.md +162 -0
- package/figma-bridge-plugin/references/color-spec.md +148 -0
- package/figma-bridge-plugin/references/full-spec-template.md +314 -0
- package/figma-bridge-plugin/references/property-spec.md +175 -0
- package/figma-bridge-plugin/references/screen-reader-spec.md +180 -0
- package/figma-bridge-plugin/references/structure-spec.md +165 -0
- package/figma-bridge-plugin/shared-prompt-config.js +604 -0
- package/figma-bridge-plugin/spec-helpers/build-table.js +269 -0
- package/figma-bridge-plugin/spec-helpers/classify-elements.js +189 -0
- package/figma-bridge-plugin/spec-helpers/index.js +35 -0
- package/figma-bridge-plugin/spec-helpers/parse-figma-link.js +49 -0
- package/figma-bridge-plugin/spec-helpers/position-markers.js +158 -0
- package/figma-bridge-plugin/stitch-auth.js +322 -0
- package/figma-bridge-plugin/stitch-runner.js +1427 -0
- package/figma-bridge-plugin/token-resolver.js +107 -0
- package/figma-bridge-plugin/ui.html +4467 -0
- package/figma-intelligence-layer/.env.example +39 -0
- package/figma-intelligence-layer/docs/local-image-generation.md +60 -0
- package/figma-intelligence-layer/examples/comfyui-workflow-template.example.json +101 -0
- package/figma-intelligence-layer/jest.config.js +14 -0
- package/figma-intelligence-layer/mcp-config.json +19 -0
- package/figma-intelligence-layer/package-lock.json +5892 -0
- package/figma-intelligence-layer/package.json +48 -0
- package/figma-intelligence-layer/scripts/setup-comfyui-local.sh +67 -0
- package/figma-intelligence-layer/scripts/start-comfyui.sh +33 -0
- package/figma-intelligence-layer/src/index.ts +2233 -0
- package/figma-intelligence-layer/src/shared/auto-layout-validator.ts +404 -0
- package/figma-intelligence-layer/src/shared/cache.ts +187 -0
- package/figma-intelligence-layer/src/shared/color-operations.ts +533 -0
- package/figma-intelligence-layer/src/shared/color-utils.ts +138 -0
- package/figma-intelligence-layer/src/shared/component-script-builder.ts +413 -0
- package/figma-intelligence-layer/src/shared/component-templates.ts +2767 -0
- package/figma-intelligence-layer/src/shared/concept-taxonomy.ts +694 -0
- package/figma-intelligence-layer/src/shared/decision-log.ts +128 -0
- package/figma-intelligence-layer/src/shared/design-system-context.ts +568 -0
- package/figma-intelligence-layer/src/shared/design-system-intelligence.ts +131 -0
- package/figma-intelligence-layer/src/shared/design-system-matcher.ts +184 -0
- package/figma-intelligence-layer/src/shared/design-system-normalizers.ts +196 -0
- package/figma-intelligence-layer/src/shared/design-system-tokens.ts +295 -0
- package/figma-intelligence-layer/src/shared/dtcg-validator.ts +530 -0
- package/figma-intelligence-layer/src/shared/enrichment-pipeline.ts +671 -0
- package/figma-intelligence-layer/src/shared/figma-bridge.ts +1408 -0
- package/figma-intelligence-layer/src/shared/font-config.ts +126 -0
- package/figma-intelligence-layer/src/shared/icon-catalog.ts +360 -0
- package/figma-intelligence-layer/src/shared/icon-fetch.ts +80 -0
- package/figma-intelligence-layer/src/shared/prototype-script-builder.ts +162 -0
- package/figma-intelligence-layer/src/shared/response-compression.ts +440 -0
- package/figma-intelligence-layer/src/shared/semantic-token-catalog.ts +324 -0
- package/figma-intelligence-layer/src/shared/token-binder.ts +505 -0
- package/figma-intelligence-layer/src/shared/token-math.ts +427 -0
- package/figma-intelligence-layer/src/shared/token-naming.ts +468 -0
- package/figma-intelligence-layer/src/shared/token-utils.ts +420 -0
- package/figma-intelligence-layer/src/shared/types.ts +346 -0
- package/figma-intelligence-layer/src/shared/typography-presets.ts +94 -0
- package/figma-intelligence-layer/src/shared/unsplash.ts +165 -0
- package/figma-intelligence-layer/src/shared/vision-client.ts +607 -0
- package/figma-intelligence-layer/src/shared/vision-provider-anthropic.ts +334 -0
- package/figma-intelligence-layer/src/shared/vision-provider-openai.ts +446 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotate-handler.ts +782 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotate-renderer.ts +496 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotation-kit.ts +230 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/colorblind-sim.ts +66 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/index.ts +810 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-analyzer.ts +1191 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-figma-page.ts +1346 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-handler.ts +148 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/vpat-figma-page.ts +499 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/vpat-report.ts +910 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/wcag-checker.ts +989 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/wcag-criteria.ts +1160 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/design-from-ref/index.ts +424 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/component-recognizer.ts +38 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/ds-matcher.ts +111 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/font-matcher.ts +114 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/icon-resolver.ts +103 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/index.ts +1060 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/layout-segmenter.ts +18 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/token-inferencer.ts +39 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/vision-pipeline.ts +58 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/sketch-to-design/index.ts +298 -0
- package/figma-intelligence-layer/src/tools/phase1-vision/visual-audit/index.ts +197 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/component-audit/index.ts +494 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/intent-translator/index.ts +356 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/layout-intelligence/container-patterns.ts +123 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/layout-intelligence/index.ts +663 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/built-in-rules.yaml +56 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/index.ts +614 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/rule-engine.ts +113 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/theme-generator/color-theory.ts +178 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/theme-generator/index.ts +470 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/variant-expander/index.ts +429 -0
- package/figma-intelligence-layer/src/tools/phase2-accuracy/variant-expander/token-override-maps.ts +226 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/ai-image-insert/index.ts +535 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/component-archaeologist/index.ts +660 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/component-archaeologist/pattern-fingerprints.ts +209 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/composition-builder/index.ts +540 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/figma-animated-build.ts +391 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/page-architect/index.ts +2019 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/page-architect/screen-templates.ts +131 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/prototype-map/index.ts +381 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/prototype-wire/index.ts +565 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/swarm-build/index.ts +764 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/system-drift/index.ts +535 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/unsplash-search/index.ts +84 -0
- package/figma-intelligence-layer/src/tools/phase3-generation/url-to-frame/index.ts +401 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/css-animations.ts +68 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/framer-motion.ts +78 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/swift-animations.ts +93 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/index.ts +596 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/ci-check/index.ts +462 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/export-tokens/index.ts +1470 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/generate-component-code/index.ts +829 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/handoff-spec/index.ts +702 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/icon-library-sync/index.ts +483 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/sync-from-code/index.ts +501 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/sync-from-code/storybook-parser.ts +106 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/watch-docs/index.ts +676 -0
- package/figma-intelligence-layer/src/tools/phase4-sync/webhook-listener/index.ts +560 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/apg-doc/index.ts +1043 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/component-detection.ts +620 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/anatomy.ts +331 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/color-tokens.ts +77 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/properties.ts +54 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/snapshot.ts +287 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/spacing.ts +71 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/states.ts +43 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/typography.ts +71 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/index.ts +221 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/_default.ts +166 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/accordion.ts +232 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/alert.ts +234 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/avatar-group.ts +270 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/avatar.ts +249 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/badge.ts +231 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/banner.ts +293 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/breadcrumb.ts +240 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/button.ts +243 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/calendar.ts +307 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/card.ts +143 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/checkbox.ts +227 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/chip.ts +233 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/combobox.ts +282 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/datepicker.ts +276 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/divider.ts +223 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/drawer.ts +255 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/dropdown-menu.ts +289 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/empty-state.ts +261 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/file-uploader.ts +290 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/form.ts +265 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/grid.ts +238 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/icon.ts +255 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/index.ts +128 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/inline-edit.ts +286 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/inline-message.ts +255 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/input.ts +330 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/link.ts +247 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/list.ts +250 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/menu.ts +247 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/modal.ts +144 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/navbar.ts +264 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/navigation.ts +251 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/number-input.ts +261 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/pagination.ts +248 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/popover.ts +270 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/progress.ts +251 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/radio.ts +142 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/range-slider.ts +282 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/rating.ts +250 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/search.ts +258 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/segmented-control.ts +265 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/select.ts +319 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/skeleton.ts +256 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/slider.ts +232 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/spinner.ts +239 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/status-dot.ts +252 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/stepper.ts +270 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/table.ts +244 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tabs.ts +143 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tag.ts +243 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/textarea.ts +259 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/time-picker.ts +293 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toast.ts +144 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toggle.ts +289 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toolbar.ts +267 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tooltip.ts +232 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/treeview.ts +257 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/typography.ts +319 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/legacy-compat.ts +121 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/anatomy-diagram.ts +430 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/figma-page.ts +312 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/json.ts +129 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/markdown.ts +78 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/visual-doc.ts +2333 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/accessibility.ts +100 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/anatomy.ts +32 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/color-tokens.ts +59 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/content-guidance.ts +18 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/design-tokens.ts +53 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/interaction-rules.ts +19 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/overview.ts +91 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/properties-api.ts +71 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/qa-criteria.ts +19 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/related-components.ts +110 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/responsive.ts +19 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/size-specs.ts +67 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/spacing-structure.ts +58 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/state-specs.ts +79 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/states.ts +50 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/type-hierarchy.ts +33 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/typography.ts +55 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/usage-guidelines.ts +73 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/variants.ts +81 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/types.ts +409 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/index.ts +198 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/renderer.ts +701 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/types.ts +88 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/decision-log/index.ts +135 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/design-decision-log/index.ts +491 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-primitives/index.ts +416 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-scaffolder/index.ts +722 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-variables/index.ts +449 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/health-report/index.ts +393 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/index.ts +406 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/figma-page.ts +292 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/json.ts +24 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/markdown.ts +172 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/naming-guide.ts +409 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/token-analytics/index.ts +594 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/token-docs/index.ts +710 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/token-migrate/index.ts +458 -0
- package/figma-intelligence-layer/src/tools/phase5-governance/token-naming/index.ts +134 -0
- package/figma-intelligence-layer/tests/apg-doc.test.ts +101 -0
- package/figma-intelligence-layer/tests/design-system-context.test.ts +152 -0
- package/figma-intelligence-layer/tests/design-system-matcher.test.ts +144 -0
- package/figma-intelligence-layer/tests/figma-bridge.test.ts +83 -0
- package/figma-intelligence-layer/tests/generate-image-and-insert.test.ts +56 -0
- package/figma-intelligence-layer/tests/screen-cloner-regression.test.ts +69 -0
- package/figma-intelligence-layer/tests/smoke.test.ts +174 -0
- package/figma-intelligence-layer/tests/spec-generator.test.ts +127 -0
- package/figma-intelligence-layer/tests/token-migrate.test.ts +21 -0
- package/figma-intelligence-layer/tests/token-naming.test.ts +30 -0
- package/figma-intelligence-layer/tsconfig.json +19 -0
- package/package.json +35 -0
- package/scripts/clean-existing-chunks.js +179 -0
- package/scripts/connect-ai-tool.js +490 -0
- package/scripts/convert-hub-pdfs.js +425 -0
- package/scripts/figma-mcp-status.js +349 -0
- package/scripts/register-codex-mcp.js +96 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list.ts — Gold-standard design knowledge for List components
|
|
3
|
+
*/
|
|
4
|
+
import type { ComponentKnowledge } from "../types.js";
|
|
5
|
+
|
|
6
|
+
export const listKnowledge: ComponentKnowledge = {
|
|
7
|
+
description:
|
|
8
|
+
"Ordered or unordered list of items | Supports simple, interactive, two-line, and avatar variants | Core layout primitive for collections",
|
|
9
|
+
|
|
10
|
+
stateSpecifications: [
|
|
11
|
+
{
|
|
12
|
+
state: "Default",
|
|
13
|
+
visualChange: "List items rendered vertically with consistent spacing; no highlight or selection indicator",
|
|
14
|
+
opacity: "1",
|
|
15
|
+
cursorWeb: "default",
|
|
16
|
+
usage: "Resting state — list is displayed with all items in their neutral visual treatment",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
state: "Hover",
|
|
20
|
+
visualChange: "Hovered item background shifts to hover token; subtle highlight row",
|
|
21
|
+
opacity: "1",
|
|
22
|
+
cursorWeb: "pointer",
|
|
23
|
+
usage: "Mouse cursor enters an interactive list item's hit area (interactive/selectable variants only)",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
state: "Selected",
|
|
27
|
+
visualChange: "Selected item has a tinted background and/or leading checkmark icon; primary accent on left edge possible",
|
|
28
|
+
opacity: "1",
|
|
29
|
+
cursorWeb: "pointer",
|
|
30
|
+
usage: "Item has been chosen by the user in a selectable list; visually persists until deselected",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
state: "Disabled",
|
|
34
|
+
visualChange: "Item text and icons use muted/disabled tokens; no hover or click response",
|
|
35
|
+
opacity: "0.4",
|
|
36
|
+
cursorWeb: "not-allowed",
|
|
37
|
+
usage: "Individual item is non-interactive due to permissions, prerequisites, or unavailability",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
state: "Empty",
|
|
41
|
+
visualChange: "Placeholder message or illustration displayed in place of list items",
|
|
42
|
+
opacity: "1",
|
|
43
|
+
cursorWeb: "default",
|
|
44
|
+
usage: "List has zero items — show empty state with guidance on how to populate",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
|
|
48
|
+
propertyDescriptions: {
|
|
49
|
+
items: "Array of item data objects to render; each object contains primary text, optional secondary text, optional icon/avatar",
|
|
50
|
+
type: "Visual layout variant — 'simple' (text only), 'interactive' (clickable rows), 'twoLine' (primary + secondary text), 'avatar' (leading avatar image)",
|
|
51
|
+
dividers: "When true, thin separator lines appear between list items using the $divider token",
|
|
52
|
+
selectable: "Enables selection behavior — 'single' for radio-like selection, 'multi' for checkbox-like, 'none' to disable",
|
|
53
|
+
onSelect: "Callback fired when an item is selected or deselected; receives the item identifier and selection state",
|
|
54
|
+
dense: "When true, reduces vertical padding per item for compact layouts (e.g. menus, sidebars)",
|
|
55
|
+
ordered: "When true, renders as an ordered list with numeric or alphabetic markers; false renders unordered",
|
|
56
|
+
maxHeight: "Maximum height before the list becomes scrollable; supports virtualisation for large datasets",
|
|
57
|
+
emptyMessage: "Custom string or component displayed when the items array is empty",
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
sizeSpecifications: [
|
|
61
|
+
{
|
|
62
|
+
size: "Small",
|
|
63
|
+
height: "36px per item",
|
|
64
|
+
paddingLR: "12px",
|
|
65
|
+
fontSize: "13px",
|
|
66
|
+
iconSize: "16px",
|
|
67
|
+
borderRadius: "4px",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
size: "Medium",
|
|
71
|
+
height: "48px per item",
|
|
72
|
+
paddingLR: "16px",
|
|
73
|
+
fontSize: "14px",
|
|
74
|
+
iconSize: "20px",
|
|
75
|
+
borderRadius: "6px",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
size: "Large",
|
|
79
|
+
height: "64px per item",
|
|
80
|
+
paddingLR: "20px",
|
|
81
|
+
fontSize: "16px",
|
|
82
|
+
iconSize: "24px",
|
|
83
|
+
borderRadius: "8px",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
|
|
87
|
+
designTokenBindings: [
|
|
88
|
+
{
|
|
89
|
+
property: "Item Background",
|
|
90
|
+
tokenName: "$list-item-bg",
|
|
91
|
+
role: "Default background for each list item",
|
|
92
|
+
fallback: "transparent",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
property: "Hover Background",
|
|
96
|
+
tokenName: "$list-hover-bg",
|
|
97
|
+
role: "Background applied on hover for interactive items",
|
|
98
|
+
fallback: "#F2F4F7",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
property: "Selected Background",
|
|
102
|
+
tokenName: "$list-selected-bg",
|
|
103
|
+
role: "Background tint for selected items",
|
|
104
|
+
fallback: "#EFF8FF",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
property: "Primary Text",
|
|
108
|
+
tokenName: "$list-primary-text",
|
|
109
|
+
role: "Color for the main text label of each item",
|
|
110
|
+
fallback: "#101828",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
property: "Secondary Text",
|
|
114
|
+
tokenName: "$list-secondary-text",
|
|
115
|
+
role: "Color for the secondary/subtitle line in twoLine variant",
|
|
116
|
+
fallback: "#667085",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
property: "Divider",
|
|
120
|
+
tokenName: "$list-divider",
|
|
121
|
+
role: "Color for the horizontal separator lines between items",
|
|
122
|
+
fallback: "#E4E7EC",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
property: "Selected Accent",
|
|
126
|
+
tokenName: "$list-selected-accent",
|
|
127
|
+
role: "Leading edge accent or checkmark color for selected items",
|
|
128
|
+
fallback: "#2563EB",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
property: "Disabled Text",
|
|
132
|
+
tokenName: "$list-disabled-text",
|
|
133
|
+
role: "Muted text color for disabled items",
|
|
134
|
+
fallback: "#98A2B3",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
property: "Focus Ring",
|
|
138
|
+
tokenName: "$focus-ring",
|
|
139
|
+
role: "Keyboard focus indicator ring on focused list item",
|
|
140
|
+
fallback: "0 0 0 2px #FFFFFF, 0 0 0 4px #2E90FA",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
property: "Font Family",
|
|
144
|
+
tokenName: "$font-family-sans",
|
|
145
|
+
role: "Typeface for all list text",
|
|
146
|
+
fallback: "Inter, system-ui, sans-serif",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
|
|
150
|
+
structureRules: [
|
|
151
|
+
"Container uses vertical Auto Layout with no gap — spacing is handled by item internal padding",
|
|
152
|
+
"Each item is a horizontal Auto Layout row: optional leading element (icon/avatar/checkbox), text content, optional trailing element (action/meta)",
|
|
153
|
+
"Text content area uses vertical Auto Layout for twoLine variant: primary text on top, secondary text below",
|
|
154
|
+
"Divider lines are 1px height, full-width children between items — controlled by the dividers prop",
|
|
155
|
+
"Avatar variant places a circular avatar (32-40px) as the leading element with $spacing-md gap to text",
|
|
156
|
+
"Interactive items have a full-width clickable area — the entire row is the hit target, not just the text",
|
|
157
|
+
"Empty state replaces the item list with a centered message component; maintains container min-height",
|
|
158
|
+
"Scrollable lists clip overflow and show a scrollbar when content exceeds maxHeight",
|
|
159
|
+
],
|
|
160
|
+
|
|
161
|
+
typeHierarchyRules: [
|
|
162
|
+
"Primary text uses Regular (400) weight; font-size matches the selected size preset",
|
|
163
|
+
"Secondary text uses Regular (400) weight at one step smaller than primary (e.g. 12px when primary is 14px)",
|
|
164
|
+
"Primary text uses $list-primary-text color; secondary uses $list-secondary-text for visual hierarchy",
|
|
165
|
+
"Text truncation with ellipsis on single-line items; twoLine primary text may wrap to 2 lines max",
|
|
166
|
+
"Ordered list markers use tabular numerals; unordered markers use bullet characters or none",
|
|
167
|
+
],
|
|
168
|
+
|
|
169
|
+
interactionRules: [
|
|
170
|
+
{ event: "Click Item", trigger: "pointerup on an interactive list item", action: "Fire onSelect with item id; toggle selection state if selectable" },
|
|
171
|
+
{ event: "Hover", trigger: "pointerenter on an interactive item", action: "Apply hover background token; transition smoothly" },
|
|
172
|
+
{ event: "Focus", trigger: "Tab key or arrow key navigation", action: "Show focus ring on the focused item; scroll into view if needed" },
|
|
173
|
+
{ event: "Keydown Enter", trigger: "Enter key while an item is focused", action: "Fire onSelect for the focused item (same as click)" },
|
|
174
|
+
{ event: "Keydown Space", trigger: "Space key while a selectable item is focused", action: "Toggle selection on the focused item" },
|
|
175
|
+
{ event: "Keydown Arrow Up/Down", trigger: "Arrow keys while focus is within the list", action: "Move focus to the previous/next item; wrap or stop at boundaries" },
|
|
176
|
+
{ event: "Multi-select", trigger: "Shift+Click or Ctrl+Click on items", action: "Extend or toggle selection range for multi-selectable lists" },
|
|
177
|
+
],
|
|
178
|
+
|
|
179
|
+
contentGuidance: [
|
|
180
|
+
"Primary text should be concise — one line for simple, up to two lines for twoLine variant",
|
|
181
|
+
"Secondary text provides supporting detail: descriptions, metadata, timestamps, or status",
|
|
182
|
+
"Use consistent content patterns across all items in the same list — do not mix twoLine and simple",
|
|
183
|
+
"Leading icons should be semantically meaningful, not decorative; use the same icon style throughout",
|
|
184
|
+
"Avatar variant is ideal for people lists (contacts, team members, chat participants)",
|
|
185
|
+
"Empty state messages should guide the user on how to add items, not just state that the list is empty",
|
|
186
|
+
"Trailing elements (badges, timestamps, action buttons) should be used sparingly to avoid clutter",
|
|
187
|
+
],
|
|
188
|
+
|
|
189
|
+
responsiveBehaviour: [
|
|
190
|
+
{ breakpoint: "Mobile (<768px)", behavior: "List items span full width; trailing elements may stack below primary text; touch targets minimum 48px height" },
|
|
191
|
+
{ breakpoint: "Tablet (768-1023px)", behavior: "Standard layout maintained; consider reducing to simple variant if space is tight" },
|
|
192
|
+
{ breakpoint: "Desktop (1024-1439px)", behavior: "Full layout with all elements visible; twoLine and avatar variants display optimally" },
|
|
193
|
+
{ breakpoint: "Ultra-wide (>=1440px)", behavior: "List width should be constrained (max-width) to maintain readability — do not stretch to fill" },
|
|
194
|
+
],
|
|
195
|
+
|
|
196
|
+
accessibilitySpec: {
|
|
197
|
+
intro:
|
|
198
|
+
"Lists must convey structure and item count to assistive technologies. Interactive lists require full keyboard support including arrow key navigation and selection announcements.",
|
|
199
|
+
requirements: [
|
|
200
|
+
{ requirement: "Semantic Roles", level: "A", notes: "Non-interactive lists use role='list' with role='listitem' children; interactive/selectable lists use role='listbox' with role='option'" },
|
|
201
|
+
{ requirement: "Keyboard Navigation", level: "A", notes: "Interactive items are focusable via Tab; arrow keys move between items; Enter/Space activate or select" },
|
|
202
|
+
{ requirement: "Selection State", level: "A", notes: "Selected items must have aria-selected='true'; multi-select items use aria-multiselectable on the listbox" },
|
|
203
|
+
{ requirement: "Disabled Items", level: "A", notes: "Disabled items use aria-disabled='true'; they may remain in the focus order with no action on activation" },
|
|
204
|
+
{ requirement: "Contrast Ratio", level: "AA", notes: "Primary text: 4.5:1 minimum; secondary text: 4.5:1; selected indicator: 3:1 non-text contrast" },
|
|
205
|
+
{ requirement: "Touch Target", level: "AA", notes: "Each interactive item row must have at least 44px height for touch accessibility" },
|
|
206
|
+
{ requirement: "Empty State", level: "A", notes: "When the list is empty, provide an accessible label or live region announcing the empty state" },
|
|
207
|
+
],
|
|
208
|
+
outro: [
|
|
209
|
+
"When items are added or removed dynamically, use aria-live='polite' to announce the change to screen readers",
|
|
210
|
+
"Focus should remain stable when the list re-renders — do not reset focus to the first item on data updates",
|
|
211
|
+
"Group related lists with headings using aria-labelledby to provide context for the collection",
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
qaAcceptanceCriteria: [
|
|
216
|
+
{ check: "Visual Regression", platform: "All", expectedResult: "List renders pixel-perfect against baseline for each type and size variant" },
|
|
217
|
+
{ check: "Hover State", platform: "Web", expectedResult: "Interactive items show hover background on mouse enter; non-interactive items do not" },
|
|
218
|
+
{ check: "Selection State", platform: "All", expectedResult: "Selected items display tinted background and/or checkmark; aria-selected is true" },
|
|
219
|
+
{ check: "Multi-select", platform: "Web", expectedResult: "Shift+Click selects range; Ctrl+Click toggles individual items; aria-multiselectable set" },
|
|
220
|
+
{ check: "Disabled Items", platform: "All", expectedResult: "Disabled items have muted visuals; no click/hover response; aria-disabled='true'" },
|
|
221
|
+
{ check: "Focus Navigation", platform: "Web", expectedResult: "Tab enters list; arrow keys move between items; focus ring visible; scroll-into-view works" },
|
|
222
|
+
{ check: "Keyboard Activation", platform: "Web", expectedResult: "Enter/Space on focused item fires onSelect; no double-fire" },
|
|
223
|
+
{ check: "Screen Reader", platform: "Web", expectedResult: "Announces role (list/listbox), item count, selected state, and disabled state" },
|
|
224
|
+
{ check: "Dividers", platform: "All", expectedResult: "Separator lines visible when dividers=true; hidden when false; correct token color" },
|
|
225
|
+
{ check: "Empty State", platform: "All", expectedResult: "Empty message displayed when items array is empty; accessible to screen readers" },
|
|
226
|
+
{ check: "Two-line Variant", platform: "All", expectedResult: "Primary and secondary text render on separate lines with correct hierarchy" },
|
|
227
|
+
{ check: "Avatar Variant", platform: "All", expectedResult: "Circular avatar renders at correct size; fallback initials shown for missing images" },
|
|
228
|
+
{ check: "RTL Support", platform: "Web", expectedResult: "Leading/trailing elements swap sides; text alignment mirrors; dividers remain full-width" },
|
|
229
|
+
],
|
|
230
|
+
|
|
231
|
+
dos: [
|
|
232
|
+
"Use the simple type for static informational lists (feature lists, specifications, settings)",
|
|
233
|
+
"Use the interactive type for action-triggering items (navigation menus, command lists)",
|
|
234
|
+
"Use dividers for long lists to help users visually scan and separate items",
|
|
235
|
+
"Maintain consistent item structure across all items in a single list instance",
|
|
236
|
+
"Provide an empty state message when the list can be dynamically populated",
|
|
237
|
+
"Use the dense prop for sidebar navigation and menus where space is limited",
|
|
238
|
+
"Ensure avatar images have meaningful alt text for accessibility",
|
|
239
|
+
],
|
|
240
|
+
|
|
241
|
+
donts: [
|
|
242
|
+
"Do not nest interactive lists inside other interactive lists — flatten the hierarchy",
|
|
243
|
+
"Do not mix different item types (simple, twoLine, avatar) within the same list instance",
|
|
244
|
+
"Do not use a list component for grid-like layouts — use a grid or card grid instead",
|
|
245
|
+
"Do not hide the selected state indicator — users must always know what is selected",
|
|
246
|
+
"Do not render more than 50-100 items without virtualization to prevent performance degradation",
|
|
247
|
+
"Do not use trailing action buttons on every item — it creates visual noise and reduces scannability",
|
|
248
|
+
"Do not remove disabled items from the DOM; keep them visible but inert for context",
|
|
249
|
+
],
|
|
250
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* menu.ts — Gold-standard design knowledge for Menu / Context Menu / Action Menu components
|
|
3
|
+
*/
|
|
4
|
+
import type { ComponentKnowledge } from "../types.js";
|
|
5
|
+
|
|
6
|
+
export const menuKnowledge: ComponentKnowledge = {
|
|
7
|
+
description:
|
|
8
|
+
"Overlay action panel | Context-triggered item list | Actionable command menu",
|
|
9
|
+
|
|
10
|
+
stateSpecifications: [
|
|
11
|
+
{
|
|
12
|
+
state: "Closed",
|
|
13
|
+
visualChange: "Menu is not rendered or has opacity 0 and pointer-events none; trigger element is in its resting state",
|
|
14
|
+
opacity: "0",
|
|
15
|
+
cursorWeb: "default",
|
|
16
|
+
usage: "Default state — menu is hidden until activated by the trigger element",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
state: "Open",
|
|
20
|
+
visualChange: "Menu panel fades in with elevation shadow; first non-disabled item receives visual focus",
|
|
21
|
+
opacity: "1",
|
|
22
|
+
cursorWeb: "default",
|
|
23
|
+
usage: "User clicks the trigger, right-clicks for context menu, or presses Enter/Space/ArrowDown on trigger",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
state: "Item-hover",
|
|
27
|
+
visualChange: "Hovered menu item receives a highlighted background using $menu-item-hover-bg token",
|
|
28
|
+
opacity: "1",
|
|
29
|
+
cursorWeb: "pointer",
|
|
30
|
+
usage: "Mouse cursor enters a menu item's hit area while the menu is open",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
state: "Item-active",
|
|
34
|
+
visualChange: "Pressed menu item background shifts to $menu-item-active-bg; slight darkening for tactile feedback",
|
|
35
|
+
opacity: "1",
|
|
36
|
+
cursorWeb: "pointer",
|
|
37
|
+
usage: "Mouse button is held down on a menu item or Enter/Space is pressed on a focused item",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
state: "Item-disabled",
|
|
41
|
+
visualChange: "Item text uses $menu-item-disabled-text token; no hover or active state; pointer-events none",
|
|
42
|
+
opacity: "0.4",
|
|
43
|
+
cursorWeb: "not-allowed",
|
|
44
|
+
usage: "Menu item action is unavailable due to missing prerequisites or permissions",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
state: "Item-focus",
|
|
48
|
+
visualChange: "Focused item shows a 2px focus ring inset within the item bounds using $focus-ring token",
|
|
49
|
+
opacity: "1",
|
|
50
|
+
cursorWeb: "default",
|
|
51
|
+
usage: "Keyboard navigation highlights the current item via ArrowUp/ArrowDown or type-ahead",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
|
|
55
|
+
propertyDescriptions: {
|
|
56
|
+
items: "Array of menu item objects, each containing label, optional icon, optional keyboard shortcut hint, disabled flag, and divider flag",
|
|
57
|
+
trigger: "The interactive element (button, icon button, or right-click target) that opens the menu on activation",
|
|
58
|
+
placement: "Preferred position of the menu relative to the trigger — bottom-start, bottom-end, or right-start; auto-flips on collision",
|
|
59
|
+
size: "Dimensional preset controlling item height, padding, font-size, and icon-size (Small, Medium, Large)",
|
|
60
|
+
closeOnSelect: "When true (default), the menu automatically closes after an item is activated; set to false for multi-select menus",
|
|
61
|
+
maxHeight: "Maximum height of the menu panel before internal scrolling activates; prevents the menu from exceeding viewport bounds",
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
sizeSpecifications: [
|
|
65
|
+
{
|
|
66
|
+
size: "Small",
|
|
67
|
+
height: "28px",
|
|
68
|
+
paddingLR: "8px",
|
|
69
|
+
fontSize: "12px",
|
|
70
|
+
iconSize: "14px",
|
|
71
|
+
borderRadius: "6px",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
size: "Medium",
|
|
75
|
+
height: "36px",
|
|
76
|
+
paddingLR: "12px",
|
|
77
|
+
fontSize: "14px",
|
|
78
|
+
iconSize: "16px",
|
|
79
|
+
borderRadius: "8px",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
size: "Large",
|
|
83
|
+
height: "44px",
|
|
84
|
+
paddingLR: "16px",
|
|
85
|
+
fontSize: "16px",
|
|
86
|
+
iconSize: "20px",
|
|
87
|
+
borderRadius: "10px",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
|
|
91
|
+
designTokenBindings: [
|
|
92
|
+
{
|
|
93
|
+
property: "Background",
|
|
94
|
+
tokenName: "$menu-bg",
|
|
95
|
+
role: "Menu panel surface color — typically a white or elevated surface token",
|
|
96
|
+
fallback: "#FFFFFF",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
property: "Border",
|
|
100
|
+
tokenName: "$menu-border",
|
|
101
|
+
role: "Subtle border around the menu panel for definition against the page",
|
|
102
|
+
fallback: "#E4E7EC",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
property: "Shadow",
|
|
106
|
+
tokenName: "$menu-shadow",
|
|
107
|
+
role: "Elevation shadow giving the menu a floating appearance above page content",
|
|
108
|
+
fallback: "0 4px 16px rgba(0,0,0,0.12)",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
property: "Item Text",
|
|
112
|
+
tokenName: "$menu-item-text",
|
|
113
|
+
role: "Default text color for menu item labels",
|
|
114
|
+
fallback: "#344054",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
property: "Item Hover Background",
|
|
118
|
+
tokenName: "$menu-item-hover-bg",
|
|
119
|
+
role: "Background highlight applied to the hovered or focused menu item",
|
|
120
|
+
fallback: "#F2F4F7",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
property: "Item Active Background",
|
|
124
|
+
tokenName: "$menu-item-active-bg",
|
|
125
|
+
role: "Background applied during the pressed/active state of a menu item",
|
|
126
|
+
fallback: "#E4E7EC",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
property: "Disabled Text",
|
|
130
|
+
tokenName: "$menu-item-disabled-text",
|
|
131
|
+
role: "Muted text color for disabled menu items indicating unavailability",
|
|
132
|
+
fallback: "#98A2B3",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
property: "Divider",
|
|
136
|
+
tokenName: "$menu-divider",
|
|
137
|
+
role: "Color of the horizontal separator line between menu item groups",
|
|
138
|
+
fallback: "#E4E7EC",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
property: "Focus Ring",
|
|
142
|
+
tokenName: "$focus-ring",
|
|
143
|
+
role: "Keyboard focus indicator ring for the currently focused menu item",
|
|
144
|
+
fallback: "0 0 0 2px #FFFFFF, 0 0 0 4px #2E90FA",
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
|
|
148
|
+
structureRules: [
|
|
149
|
+
"Menu panel renders in a portal at the document root to avoid clipping by parent overflow",
|
|
150
|
+
"Menu container uses vertical Auto Layout with no gap — items stack flush; dividers are 1px separator elements",
|
|
151
|
+
"Each menu item is a horizontal Auto Layout row: optional leading icon, label text, flexible spacer, optional shortcut hint",
|
|
152
|
+
"Dividers use role='separator' and span the full menu width with vertical margin of $spacing-xs (4px)",
|
|
153
|
+
"Positioning is calculated dynamically based on trigger element rect and viewport boundaries with collision detection",
|
|
154
|
+
"Menu width auto-sizes to the widest item up to a maxWidth (280px default); a minWidth (160px) prevents overly narrow panels",
|
|
155
|
+
"When maxHeight is exceeded, the menu panel becomes internally scrollable with overscroll-behavior: contain",
|
|
156
|
+
],
|
|
157
|
+
|
|
158
|
+
typeHierarchyRules: [
|
|
159
|
+
"Item label font weight is Regular (400) — no bold unless the item represents a currently active selection",
|
|
160
|
+
"Text uses sentence case — capitalize only the first word of each menu item label",
|
|
161
|
+
"Shortcut hint text uses a monospace font token at one step smaller than the item label font size",
|
|
162
|
+
"Destructive items (e.g., 'Delete') use $color-danger for label text color to signal risk",
|
|
163
|
+
"No text truncation within menu items — ensure all labels fit within the maxWidth constraint",
|
|
164
|
+
],
|
|
165
|
+
|
|
166
|
+
interactionRules: [
|
|
167
|
+
{ event: "Click Trigger", trigger: "pointerup on trigger element", action: "Toggle menu open/closed; set aria-expanded accordingly" },
|
|
168
|
+
{ event: "ArrowDown / ArrowUp", trigger: "Keydown while menu is open", action: "Move visual focus to next/previous non-disabled item; wrap at boundaries" },
|
|
169
|
+
{ event: "Enter / Space", trigger: "Keydown on focused item", action: "Activate the item's action; close menu if closeOnSelect is true" },
|
|
170
|
+
{ event: "Escape", trigger: "Keydown while menu is open", action: "Close the menu and return focus to the trigger element" },
|
|
171
|
+
{ event: "Type-ahead", trigger: "Letter key while menu is open", action: "Move focus to the next item whose label starts with the typed character" },
|
|
172
|
+
{ event: "Home / End", trigger: "Home or End key while menu is open", action: "Move focus to the first or last non-disabled menu item" },
|
|
173
|
+
{ event: "Click Outside", trigger: "pointerdown outside menu and trigger bounds", action: "Close the menu without activating any item" },
|
|
174
|
+
],
|
|
175
|
+
|
|
176
|
+
contentGuidance: [
|
|
177
|
+
"Menu item labels should be concise action verbs or short verb phrases: 'Edit', 'Duplicate', 'Move to folder'",
|
|
178
|
+
"Group related items together with dividers — no more than 3-4 groups per menu to avoid cognitive overload",
|
|
179
|
+
"Place destructive actions (Delete, Remove) at the bottom of the menu, separated by a divider",
|
|
180
|
+
"Use leading icons consistently — either all items have icons or none do; avoid mixing",
|
|
181
|
+
"Keyboard shortcut hints should match platform conventions (Cmd on macOS, Ctrl on Windows)",
|
|
182
|
+
"Keep the total number of menu items under 12 — use submenus or a different pattern for longer lists",
|
|
183
|
+
"Disabled items should remain visible to maintain spatial consistency but must indicate their unavailability",
|
|
184
|
+
],
|
|
185
|
+
|
|
186
|
+
responsiveBehaviour: [
|
|
187
|
+
{ breakpoint: "Mobile (<768px)", behavior: "Menu may render as a bottom sheet or action sheet for reachability; items use 48px touch targets" },
|
|
188
|
+
{ breakpoint: "Tablet (768-1023px)", behavior: "Standard dropdown positioning; ensure menu does not overflow the viewport horizontally" },
|
|
189
|
+
{ breakpoint: "Desktop (1024-1439px)", behavior: "Standard floating menu anchored to trigger; preferred placement honored with collision detection" },
|
|
190
|
+
{ breakpoint: "Ultra-wide (>=1440px)", behavior: "Menu positioning unchanged; maxWidth prevents overly wide panels; no proportional scaling" },
|
|
191
|
+
],
|
|
192
|
+
|
|
193
|
+
accessibilitySpec: {
|
|
194
|
+
intro:
|
|
195
|
+
"Menus are critical navigation and action surfaces. They must be fully operable via keyboard and correctly announced by screen readers to meet WCAG 2.1 compliance.",
|
|
196
|
+
requirements: [
|
|
197
|
+
{ requirement: "Container Role", level: "A", notes: "Menu container must use role='menu' for proper semantic identification by assistive technology" },
|
|
198
|
+
{ requirement: "Item Role", level: "A", notes: "Each actionable item must use role='menuitem'; dividers use role='separator'" },
|
|
199
|
+
{ requirement: "Trigger Attributes", level: "A", notes: "Trigger element must set aria-haspopup='menu' and aria-expanded (true when open, false when closed)" },
|
|
200
|
+
{ requirement: "Keyboard Navigation", level: "A", notes: "ArrowUp/ArrowDown navigate items; Enter/Space activate; Escape closes; Home/End jump to boundaries" },
|
|
201
|
+
{ requirement: "Focus Management", level: "A", notes: "Focus moves into the menu on open and returns to the trigger on close; focus must not escape the menu while open" },
|
|
202
|
+
{ requirement: "Contrast", level: "AA", notes: "Item text to background must meet 4.5:1; item hover/focus indicator must meet 3:1 against adjacent colors" },
|
|
203
|
+
{ requirement: "Disabled Items", level: "A", notes: "Disabled items must set aria-disabled='true' and be skipped during keyboard navigation" },
|
|
204
|
+
],
|
|
205
|
+
outro: [
|
|
206
|
+
"Screen readers should announce the menu role, the number of items, and the currently focused item position",
|
|
207
|
+
"Ensure focus is trapped within the menu while open — Tab should not move focus outside the menu panel",
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
qaAcceptanceCriteria: [
|
|
212
|
+
{ check: "Open/Close", platform: "Web", expectedResult: "Menu opens on trigger click and closes on item selection, Escape, or outside click" },
|
|
213
|
+
{ check: "Keyboard Navigation", platform: "Web", expectedResult: "ArrowUp/ArrowDown cycle through items; disabled items are skipped; focus wraps at boundaries" },
|
|
214
|
+
{ check: "Item Activation", platform: "Web", expectedResult: "Enter/Space on focused item fires action callback; click on item fires action callback" },
|
|
215
|
+
{ check: "Focus Return", platform: "Web", expectedResult: "Focus returns to trigger element when menu closes via Escape or item selection" },
|
|
216
|
+
{ check: "Type-ahead", platform: "Web", expectedResult: "Pressing a letter key moves focus to the next matching item by first character" },
|
|
217
|
+
{ check: "Screen Reader", platform: "Web", expectedResult: "Announces role 'menu', item count, current item position, and disabled state" },
|
|
218
|
+
{ check: "Placement", platform: "Web", expectedResult: "Menu renders at specified placement; auto-flips when clipped by viewport" },
|
|
219
|
+
{ check: "Scroll Overflow", platform: "Web", expectedResult: "Menu scrolls internally when items exceed maxHeight; overscroll does not propagate to page" },
|
|
220
|
+
{ check: "Dividers", platform: "All", expectedResult: "Visual separators render between groups; announced as separator by screen readers" },
|
|
221
|
+
{ check: "Disabled Items", platform: "All", expectedResult: "Disabled items show muted visuals; are not activatable; are skipped by keyboard navigation" },
|
|
222
|
+
{ check: "Touch Target", platform: "Mobile", expectedResult: "Menu items have at least 44x44px touch target; bottom-sheet variant used on small screens" },
|
|
223
|
+
{ check: "Reduced Motion", platform: "Web", expectedResult: "Open/close animations disabled when prefers-reduced-motion is set" },
|
|
224
|
+
],
|
|
225
|
+
|
|
226
|
+
dos: [
|
|
227
|
+
"Use role='menu' on the container and role='menuitem' on each actionable item",
|
|
228
|
+
"Return focus to the trigger element when the menu closes",
|
|
229
|
+
"Group related actions with dividers for visual and semantic clarity",
|
|
230
|
+
"Support full keyboard navigation including ArrowUp/Down, Enter, Space, Escape, Home, End, and type-ahead",
|
|
231
|
+
"Place destructive actions at the bottom of the menu, visually separated and color-coded",
|
|
232
|
+
"Ensure the menu auto-repositions when it would overflow the viewport",
|
|
233
|
+
"Use consistent iconography — either all items have leading icons or none do",
|
|
234
|
+
"Close the menu when an item is activated (unless multi-select behavior is required)",
|
|
235
|
+
],
|
|
236
|
+
|
|
237
|
+
donts: [
|
|
238
|
+
"Do not nest menus deeper than one sublevel — deeply nested menus are unusable on mobile and difficult for keyboard users",
|
|
239
|
+
"Do not use a menu for navigation — use a navigation component or link list instead",
|
|
240
|
+
"Do not place more than 12 items in a single menu level — consider a different pattern for longer lists",
|
|
241
|
+
"Do not remove disabled items from the menu — keep them visible but inactive for spatial consistency",
|
|
242
|
+
"Do not auto-close the menu on a timer — the user must control when the menu dismisses",
|
|
243
|
+
"Do not allow focus to escape the menu while it is open — trap focus within the menu panel",
|
|
244
|
+
"Do not use hover-only triggers for opening menus — click/tap and keyboard activation must be supported",
|
|
245
|
+
"Do not override design token colors with hard-coded hex values in menu items",
|
|
246
|
+
],
|
|
247
|
+
};
|
package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/modal.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { ComponentKnowledge } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export const modalKnowledge: ComponentKnowledge = {
|
|
4
|
+
description:
|
|
5
|
+
"Overlay dialog | Focused interaction container | Content interruption layer",
|
|
6
|
+
|
|
7
|
+
stateSpecifications: [
|
|
8
|
+
{ state: "Closed", visualChange: "Modal and backdrop are not rendered in the DOM or are display:none", opacity: "0", cursorWeb: "default", usage: "No modal is active; page is fully interactive" },
|
|
9
|
+
{ state: "Opening", visualChange: "Backdrop fades in, modal slides/scales into view", opacity: "0→1", cursorWeb: "default", usage: "Transition state during entrance animation" },
|
|
10
|
+
{ state: "Open-Default", visualChange: "Backdrop at reduced opacity, modal centered and fully visible", opacity: "1", cursorWeb: "default", usage: "User is interacting with modal content" },
|
|
11
|
+
{ state: "Open-Scrollable", visualChange: "Modal body scrolls independently, header/footer remain fixed", opacity: "1", cursorWeb: "default", usage: "Content exceeds the modal viewport height" },
|
|
12
|
+
{ state: "Closing", visualChange: "Modal fades/slides out, backdrop fades", opacity: "1→0", cursorWeb: "default", usage: "Transition state during exit animation" },
|
|
13
|
+
{ state: "Danger", visualChange: "Primary action button uses danger/destructive color", opacity: "1", cursorWeb: "default", usage: "Confirming an irreversible or destructive action" },
|
|
14
|
+
{ state: "Passive", visualChange: "No action buttons; informational content only with close icon", opacity: "1", cursorWeb: "default", usage: "Displaying read-only information or media" },
|
|
15
|
+
],
|
|
16
|
+
|
|
17
|
+
propertyDescriptions: {
|
|
18
|
+
open: "Controls visibility of the modal; when true the dialog is rendered and focus is trapped",
|
|
19
|
+
size: "Width variant — xs (320px), sm (480px), md (640px), lg (800px), full (100vw–margins)",
|
|
20
|
+
title: "Heading text rendered in the modal header bar",
|
|
21
|
+
subtitle: "Optional secondary text below the title providing additional context",
|
|
22
|
+
hasCloseIcon: "Renders an X button in the top-right corner of the header",
|
|
23
|
+
preventCloseOnClickOutside: "When true, clicking the backdrop does not dismiss the modal",
|
|
24
|
+
danger: "Applies destructive styling to the primary action button",
|
|
25
|
+
primaryButtonText: "Label for the main CTA button in the footer",
|
|
26
|
+
secondaryButtonText: "Label for the secondary/cancel button in the footer",
|
|
27
|
+
selectorPrimaryFocus: "CSS selector for the element that receives initial focus on open",
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
sizeSpecifications: [
|
|
31
|
+
{ size: "xs", height: "auto (max 80vh)", paddingLR: "16px", fontSize: "14px", iconSize: "20px", borderRadius: "4px" },
|
|
32
|
+
{ size: "sm", height: "auto (max 80vh)", paddingLR: "24px", fontSize: "14px", iconSize: "20px", borderRadius: "4px" },
|
|
33
|
+
{ size: "md", height: "auto (max 80vh)", paddingLR: "32px", fontSize: "14px", iconSize: "20px", borderRadius: "8px" },
|
|
34
|
+
{ size: "lg", height: "auto (max 84vh)", paddingLR: "32px", fontSize: "16px", iconSize: "24px", borderRadius: "8px" },
|
|
35
|
+
{ size: "full", height: "100vh – 48px", paddingLR: "48px", fontSize: "16px", iconSize: "24px", borderRadius: "0px" },
|
|
36
|
+
],
|
|
37
|
+
|
|
38
|
+
designTokenBindings: [
|
|
39
|
+
{ property: "backdrop", tokenName: "color/overlay", role: "Semi-transparent background behind modal", fallback: "rgba(22,22,22,0.5)" },
|
|
40
|
+
{ property: "surface", tokenName: "color/layer/01", role: "Modal container background", fallback: "#FFFFFF" },
|
|
41
|
+
{ property: "header-text", tokenName: "color/text/primary", role: "Title and subtitle text", fallback: "#161616" },
|
|
42
|
+
{ property: "border-bottom", tokenName: "color/border/subtle/01", role: "Divider between header and body", fallback: "#E0E0E0" },
|
|
43
|
+
{ property: "close-icon", tokenName: "color/icon/primary", role: "Close button icon fill", fallback: "#161616" },
|
|
44
|
+
{ property: "danger-button-bg", tokenName: "color/support/error", role: "Background of destructive primary action", fallback: "#DA1E28" },
|
|
45
|
+
{ property: "shadow", tokenName: "shadow/raised/04", role: "Elevation shadow around modal container", fallback: "0 8px 24px rgba(0,0,0,0.2)" },
|
|
46
|
+
],
|
|
47
|
+
|
|
48
|
+
structureRules: [
|
|
49
|
+
"Modal must use the native <dialog> element or role='dialog' with aria-modal='true'",
|
|
50
|
+
"Structure follows a three-zone layout: fixed header, scrollable body, fixed footer",
|
|
51
|
+
"Header contains title, optional subtitle, and close icon aligned to the end",
|
|
52
|
+
"Footer contains action buttons right-aligned; primary button comes last in DOM order",
|
|
53
|
+
"Backdrop is a sibling or pseudo-element that spans the entire viewport",
|
|
54
|
+
"Modal must be centered both vertically and horizontally with responsive margins",
|
|
55
|
+
"When content overflows, only the body zone scrolls — header and footer remain pinned",
|
|
56
|
+
],
|
|
57
|
+
|
|
58
|
+
typeHierarchyRules: [
|
|
59
|
+
"Title uses heading-03 (20px/28px) to establish the modal's purpose immediately",
|
|
60
|
+
"Subtitle uses body-01 (14px/20px) in text-secondary color",
|
|
61
|
+
"Body content follows standard body-01 typography with 20px line-height",
|
|
62
|
+
"Footer button labels use body-compact-01 (14px) in uppercase or sentence case per system",
|
|
63
|
+
"Title must not exceed two lines — truncate with ellipsis if necessary",
|
|
64
|
+
"Ensure at least 16px vertical spacing between title and body content",
|
|
65
|
+
"Danger button label should use the same type style as primary but with inverse text color",
|
|
66
|
+
],
|
|
67
|
+
|
|
68
|
+
interactionRules: [
|
|
69
|
+
{ event: "keydown:Escape", trigger: "Modal is open and not preventCloseOnClickOutside", action: "Close the modal and return focus to the trigger element" },
|
|
70
|
+
{ event: "click:backdrop", trigger: "User clicks outside modal surface", action: "Close modal unless preventCloseOnClickOutside is true" },
|
|
71
|
+
{ event: "click:close-icon", trigger: "User clicks the X button", action: "Close modal and return focus to trigger" },
|
|
72
|
+
{ event: "keydown:Tab", trigger: "Focus is on the last focusable element inside modal", action: "Wrap focus back to the first focusable element (focus trap)" },
|
|
73
|
+
{ event: "keydown:Shift+Tab", trigger: "Focus is on the first focusable element", action: "Wrap focus to the last focusable element (reverse trap)" },
|
|
74
|
+
{ event: "open", trigger: "Modal becomes visible", action: "Lock body scroll, move focus to selectorPrimaryFocus or first focusable element" },
|
|
75
|
+
{ event: "close", trigger: "Modal is dismissed by any method", action: "Restore body scroll, return focus to the element that opened the modal" },
|
|
76
|
+
],
|
|
77
|
+
|
|
78
|
+
contentGuidance: [
|
|
79
|
+
"Use a clear, action-oriented title — e.g. 'Delete project?' not 'Are you sure?'",
|
|
80
|
+
"Keep body content concise; modals are for focused tasks, not long-form reading",
|
|
81
|
+
"Primary button label should match the action described in the title — e.g. 'Delete' for 'Delete project?'",
|
|
82
|
+
"Secondary button should be a clear escape — 'Cancel' or 'Go back', never just 'No'",
|
|
83
|
+
"Avoid stacking modals on top of modals; redesign the flow if nesting is needed",
|
|
84
|
+
"For forms inside modals, validate inline and disable the primary button until valid",
|
|
85
|
+
"Danger modals should explicitly name the resource being affected in the body text",
|
|
86
|
+
],
|
|
87
|
+
|
|
88
|
+
responsiveBehaviour: [
|
|
89
|
+
{ breakpoint: "≥ 1056px (lg)", behavior: "Modal renders centered with defined width; backdrop visible" },
|
|
90
|
+
{ breakpoint: "672–1055px (md)", behavior: "Modal width may increase to md or lg size; side margins shrink to 24px" },
|
|
91
|
+
{ breakpoint: "< 672px (sm)", behavior: "Modal becomes full-width sheet docked to bottom or fills viewport" },
|
|
92
|
+
{ breakpoint: "Landscape mobile", behavior: "Modal height capped at 80vh with scrollable body" },
|
|
93
|
+
{ breakpoint: "Reduced motion", behavior: "Skip entrance/exit animations; modal appears/disappears instantly" },
|
|
94
|
+
],
|
|
95
|
+
|
|
96
|
+
accessibilitySpec: {
|
|
97
|
+
intro:
|
|
98
|
+
"Modals must trap focus, lock background scrolling, and announce their purpose to assistive technologies. They represent a critical interruption pattern that must be fully keyboard operable.",
|
|
99
|
+
requirements: [
|
|
100
|
+
{ requirement: "Container must have role='dialog' and aria-modal='true'", level: "A", notes: "Native <dialog> with showModal() provides this automatically" },
|
|
101
|
+
{ requirement: "Must have an accessible name via aria-labelledby pointing to the title element", level: "A", notes: "aria-label is acceptable if no visible title exists" },
|
|
102
|
+
{ requirement: "Focus must be trapped inside the modal while open", level: "A", notes: "Tab and Shift+Tab must cycle within modal boundaries" },
|
|
103
|
+
{ requirement: "Escape key must close the modal", level: "A", notes: "Unless explicitly prevented for critical workflows like unsaved data" },
|
|
104
|
+
{ requirement: "On close, focus must return to the element that triggered the modal", level: "A", notes: "Store a reference to document.activeElement before opening" },
|
|
105
|
+
{ requirement: "Background content must be inert (aria-hidden or inert attribute)", level: "A", notes: "Prevents screen readers from accessing content behind the modal" },
|
|
106
|
+
{ requirement: "Opening transition must respect prefers-reduced-motion", level: "AA", notes: "Disable or reduce animation duration to 0ms" },
|
|
107
|
+
],
|
|
108
|
+
outro: [
|
|
109
|
+
"Test that VoiceOver announces the dialog title on open",
|
|
110
|
+
"Verify tab trapping with both Tab and Shift+Tab at modal boundaries",
|
|
111
|
+
"Confirm background scroll is locked on iOS Safari, which requires special handling",
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
qaAcceptanceCriteria: [
|
|
116
|
+
{ check: "Focus moves into modal on open", platform: "Web", expectedResult: "First focusable element or selectorPrimaryFocus receives focus" },
|
|
117
|
+
{ check: "Tab key is trapped within modal", platform: "Web", expectedResult: "Focus cycles through modal elements only" },
|
|
118
|
+
{ check: "Escape closes the modal", platform: "Web", expectedResult: "Modal dismissed, focus returns to trigger" },
|
|
119
|
+
{ check: "Backdrop click closes modal", platform: "Web", expectedResult: "Modal dismissed unless preventCloseOnClickOutside is set" },
|
|
120
|
+
{ check: "Body scroll is locked", platform: "All", expectedResult: "Page behind modal does not scroll on any device" },
|
|
121
|
+
{ check: "Danger variant shows red primary button", platform: "All", expectedResult: "Button uses danger token, label remains legible" },
|
|
122
|
+
{ check: "Long content scrolls inside body zone", platform: "All", expectedResult: "Header and footer stay fixed; only body scrolls" },
|
|
123
|
+
],
|
|
124
|
+
|
|
125
|
+
dos: [
|
|
126
|
+
"Return focus to the triggering element when the modal closes",
|
|
127
|
+
"Provide a visible close affordance (X icon or cancel button) at all times",
|
|
128
|
+
"Use the danger variant when the action is destructive or irreversible",
|
|
129
|
+
"Keep modal content focused on a single task or decision",
|
|
130
|
+
"Apply inert or aria-hidden to all content behind the backdrop",
|
|
131
|
+
"Animate entrance and exit with a 200–300ms duration for smooth perception",
|
|
132
|
+
"Test with screen readers to confirm the title is announced on open",
|
|
133
|
+
],
|
|
134
|
+
|
|
135
|
+
donts: [
|
|
136
|
+
"Don't open a modal from within another modal — flatten the flow instead",
|
|
137
|
+
"Don't use modals for simple confirmations that could be inline alerts",
|
|
138
|
+
"Don't auto-close modals on a timer — users need time to read and decide",
|
|
139
|
+
"Don't remove the backdrop — it provides critical visual context for layering",
|
|
140
|
+
"Don't place critical page-level navigation inside a modal",
|
|
141
|
+
"Don't allow background scrolling while the modal is open",
|
|
142
|
+
"Don't use a modal when the content would benefit from a full page layout",
|
|
143
|
+
],
|
|
144
|
+
};
|