@fprad0/skill-master-mcp 0.0.12 → 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/CHANGELOG.md +96 -90
- package/README.md +472 -472
- package/VERSION.md +9 -9
- package/bin/lib/bootstrap-global-core.mjs +34 -0
- package/bin/lib/client-config.mjs +293 -293
- package/bin/lib/doctor-core.mjs +202 -0
- package/bin/lib/menu-core.mjs +1629 -1522
- package/bin/lib/operation-result.mjs +59 -0
- package/bin/lib/register-clients-core.mjs +247 -0
- package/bin/lib/skill-installation.mjs +215 -215
- package/bin/lib/update-cli-core.mjs +117 -0
- package/bin/skill-master-activation.mjs +163 -163
- package/bin/skill-master-bootstrap-global.mjs +61 -49
- package/bin/skill-master-configure-private-registry.mjs +3 -3
- package/bin/skill-master-doctor.mjs +239 -228
- package/bin/skill-master-eval-activation.mjs +32 -32
- package/bin/skill-master-install-global-skills.mjs +59 -59
- package/bin/skill-master-install-project-skills.mjs +97 -97
- package/bin/skill-master-menu.mjs +406 -405
- package/bin/skill-master-register-clients.mjs +232 -153
- package/bin/skill-master-success-skills.mjs +307 -307
- package/bin/skill-master-update.mjs +121 -72
- package/bin/skill-master.mjs +3 -3
- package/dist/activation.d.ts.map +1 -1
- package/dist/activation.js +12 -0
- package/dist/activation.js.map +1 -1
- package/dist/prompt-router.d.ts.map +1 -1
- package/dist/prompt-router.js +19 -0
- package/dist/prompt-router.js.map +1 -1
- package/dist/recommender.d.ts.map +1 -1
- package/dist/recommender.js +4 -1
- package/dist/recommender.js.map +1 -1
- package/docs/architecture/APRENDIZADO_DE_IMPLEMENTACOES_BEM_SUCEDIDAS.md +125 -125
- package/docs/architecture/ARQUITETURA_AUTO_UPDATE.md +9 -9
- package/docs/architecture/PLANO_MASTER_ACIONAMENTO_AUTOMATICO_E_APRENDIZADO.md +341 -341
- package/docs/architecture/REDE_SEGURA_DE_SKILLS.md +148 -148
- package/docs/operations/GUIA_MULTI_COMPUTADOR.md +262 -262
- package/docs/operations/GUIA_NPM_PRIVADO.md +294 -294
- package/docs/operations/GUIA_NPM_PUBLICO.md +147 -147
- package/docs/operations/MENU_VISUAL_EVIDENCE_2026-06-28.md +66 -66
- package/docs/operations/assets/menu-frame-compact.html +75 -75
- package/docs/operations/assets/menu-frame-large.html +83 -83
- package/docs/operations/assets/menu-frame-running.html +79 -79
- package/docs/operations/cross-platform-auth-transfer/ANALISE_COMPATIBILIDADE_MCP_2026-06-28.md +140 -140
- package/docs/operations/cross-platform-auth-transfer/README_TRANSFERENCIA.md +85 -85
- package/docs/operations/reborn-menu-cyberpunk-transfer/ANALISE_MENU_REBORN_CYBERPUNK_2026-06-28.md +174 -174
- package/docs/operations/reborn-menu-cyberpunk-transfer/HANDOFF_IMPLEMENTACAO_REBORN_CYBERPUNK_2026-06-28.md +119 -119
- package/docs/operations/reborn-menu-cyberpunk-transfer/ORDEM_DE_EXECUCAO_MENU_REBORN_CYBERPUNK.md +134 -134
- package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA.md +84 -84
- package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA_REBORN_PACKAGE.md +56 -56
- package/docs/operations/token-economy-transfer/ANALISE_AVANCADA_ECONOMIA_TOKENS_2026-06-30.md +141 -0
- package/docs/operations/token-economy-transfer/PLANO_DEV_SENIOR_MASTER_TOKEN_ECONOMY_2026-06-30.md +171 -0
- package/docs/operations/token-economy-transfer/README_TRANSFERENCIA_TOKEN_ECONOMY.md +31 -0
- package/docs/planning/MENU_RUNTIME_CORRECTION_PLAN_2026-06-30.md +551 -0
- package/docs/planning/V0_0_9_APROVACAO_CRITICA_MENSAGENS_DE_VENDA.md +85 -85
- package/docs/planning/V0_0_9_FONTES_E_CRITERIOS_DE_AUTORIDADE.md +139 -139
- package/docs/planning/V0_0_9_MATRIZ_SKILLS_MULTIDISCIPLINARES.md +105 -105
- package/docs/planning/V0_0_9_POLITICA_MORAL_CATOLICA_PARA_IA.md +181 -181
- package/docs/planning/V0_0_9_PROMPTS_EXECUCAO.md +59 -59
- package/docs/planning/V0_0_9_ROADMAP_DISCERNIMENTO_E_CONHECIMENTO_AMPLO.md +181 -181
- package/docs/prompt-tasks/PROMPT_TASK_001_BOOTSTRAP_SKILL_MASTER_MCP.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_002_AUTO_UPDATE_LAUNCHER.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_003_REMOTE_MANIFEST_AND_RELEASES.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_004_MULTI_USER_DISTRIBUTION.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_005_SECURITY_AND_QUALITY_GATE.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_006_MASTER_ACIONAMENTO_APRENDIZADO.md +83 -83
- package/docs/prompt-tasks/PROMPT_TASK_007_PERSONA_ORQUESTRADORA.md +88 -88
- package/docs/prompt-tasks/PROMPT_TASK_008_PROMPT_ROUTER_MODOS_ATIVACAO.md +156 -156
- package/docs/prompt-tasks/PROMPT_TASK_009_PIPELINE_APRENDIZADO_SUCESSO.md +105 -105
- package/docs/prompt-tasks/PROMPT_TASK_010_EVALS_GOVERNANCA_ATIVACAO.md +119 -119
- package/docs/prompt-tasks/PROMPT_TASK_011_MENU_NOTIFICACOES_NOTION.md +120 -120
- package/docs/prompt-tasks/PROMPT_TASK_012_MENU_CYBERPUNK_PIXEL_FRAME.md +123 -123
- package/docs/prompt-tasks/PROMPT_TASK_013_MENU_FLUID_DNA_ANIMATION.md +114 -114
- package/docs/prompt-tasks/PROMPT_TASK_014_MENU_FUNCTIONAL_PARITY_QA.md +157 -157
- package/docs/prompt-tasks/PROMPT_TASK_015_TRANSFER_RELEASE_HANDOFF.md +127 -127
- package/docs/prompt-tasks/PROMPT_TASK_016_CROSS_PLATFORM_MCP_AUTH_REGISTRATION.md +107 -107
- package/docs/prompt-tasks/PROMPT_TASK_018_NPM_PUBLISH_2FA_SETUP.md +80 -80
- package/docs/prompt-tasks/PROMPT_TASK_019_TOKEN_ECONOMY_GLOBAL_SKILLS.md +56 -0
- package/docs/prompt-tasks/PROMPT_TASK_MASTER_EXECUTOR.md +6 -6
- package/docs/skill-candidates/v0.0.10/cli-creator/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/cli-creator/SKILL.md +160 -160
- package/docs/skill-candidates/v0.0.10/cli-creator/agents/openai.yaml +4 -4
- package/docs/skill-candidates/v0.0.10/cli-creator/references/agent-cli-patterns.md +154 -154
- package/docs/skill-candidates/v0.0.10/developer-workstation-ops/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.10/figma/LICENSE.txt +1 -1
- package/docs/skill-candidates/v0.0.10/figma/SKILL.md +42 -42
- package/docs/skill-candidates/v0.0.10/figma/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma/references/figma-mcp-config.md +35 -35
- package/docs/skill-candidates/v0.0.10/figma/references/figma-tools-and-prompts.md +34 -34
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/SKILL.md +349 -349
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/references/mapping-checklist.md +7 -7
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/scripts/normalize_node_id.py +25 -25
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/SKILL.md +537 -537
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/references/rule-template.md +15 -15
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/scripts/check_agents_md.sh +9 -9
- package/docs/skill-candidates/v0.0.10/figma-generate-design/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-generate-design/SKILL.md +341 -341
- package/docs/skill-candidates/v0.0.10/figma-generate-design/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-generate-design/maintainers.yml +1 -1
- package/docs/skill-candidates/v0.0.10/figma-generate-library/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-generate-library/SKILL.md +314 -314
- package/docs/skill-candidates/v0.0.10/figma-generate-library/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-generate-library/maintainers.yml +3 -3
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/code-connect-setup.md +260 -260
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/component-creation.md +1014 -1014
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/discovery-phase.md +518 -518
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/documentation-creation.md +834 -834
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/error-recovery.md +540 -540
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/naming-conventions.md +527 -527
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/token-creation.md +962 -962
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/bindVariablesToComponent.js +110 -110
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/cleanupOrphans.js +127 -127
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createComponentWithVariants.js +148 -148
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createDocumentationPage.js +139 -139
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createSemanticTokens.js +108 -108
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createVariableCollection.js +49 -49
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/inspectFileStructure.js +121 -121
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/rehydrateState.js +92 -92
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/validateCreation.js +83 -83
- package/docs/skill-candidates/v0.0.10/figma-implement-design/LICENSE.txt +1 -1
- package/docs/skill-candidates/v0.0.10/figma-implement-design/SKILL.md +258 -258
- package/docs/skill-candidates/v0.0.10/figma-implement-design/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-use/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-use/SKILL.md +233 -233
- package/docs/skill-candidates/v0.0.10/figma-use/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-use/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-use/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-use/maintainers.yml +1 -1
- package/docs/skill-candidates/v0.0.10/figma-use/references/api-reference.md +301 -301
- package/docs/skill-candidates/v0.0.10/figma-use/references/common-patterns.md +512 -512
- package/docs/skill-candidates/v0.0.10/figma-use/references/component-patterns.md +488 -488
- package/docs/skill-candidates/v0.0.10/figma-use/references/effect-style-patterns.md +123 -123
- package/docs/skill-candidates/v0.0.10/figma-use/references/gotchas.md +599 -599
- package/docs/skill-candidates/v0.0.10/figma-use/references/maintainers.yml +12 -12
- package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-patterns.md +513 -513
- package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-standalone.d.ts +11293 -11293
- package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-standalone.index.md +441 -441
- package/docs/skill-candidates/v0.0.10/figma-use/references/text-style-patterns.md +203 -203
- package/docs/skill-candidates/v0.0.10/figma-use/references/validation-and-recovery.md +109 -109
- package/docs/skill-candidates/v0.0.10/figma-use/references/variable-patterns.md +354 -354
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/maintainers.yml +9 -9
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components--creating.md +17 -17
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components--using.md +17 -17
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components.md +50 -50
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-effect-styles.md +52 -52
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-text-styles.md +90 -90
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables--creating.md +13 -13
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables--using.md +13 -13
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables.md +64 -64
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds.md +41 -41
- package/docs/skill-candidates/v0.0.10/frontend-design/LICENSE.txt +177 -177
- package/docs/skill-candidates/v0.0.10/frontend-design/SKILL.md +55 -55
- package/docs/skill-candidates/v0.0.10/frontend-ui-ux-systems/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.10/github/SKILL.md +74 -74
- package/docs/skill-candidates/v0.0.10/github/agents/openai.yaml +6 -6
- package/docs/skill-candidates/v0.0.10/github/assets/github-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/image-graphic-design-rendering/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.10/language-quality-pt-en-fr-it-ru/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.10/math-physics-reasoning/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.10/mcp-builder/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/mcp-builder/SKILL.md +236 -236
- package/docs/skill-candidates/v0.0.10/mcp-builder/reference/evaluation.md +601 -601
- package/docs/skill-candidates/v0.0.10/mcp-builder/reference/mcp_best_practices.md +249 -249
- package/docs/skill-candidates/v0.0.10/mcp-builder/reference/node_mcp_server.md +969 -969
- package/docs/skill-candidates/v0.0.10/mcp-builder/reference/python_mcp_server.md +718 -718
- package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/connections.py +151 -151
- package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/evaluation.py +373 -373
- package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/example_evaluation.xml +22 -22
- package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/requirements.txt +2 -2
- package/docs/skill-candidates/v0.0.10/mcp-client-readiness/SKILL.md +31 -31
- package/docs/skill-candidates/v0.0.10/openai-docs/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/openai-docs/SKILL.md +161 -161
- package/docs/skill-candidates/v0.0.10/openai-docs/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/openai-docs/assets/openai-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/openai-docs/references/latest-model.md +37 -37
- package/docs/skill-candidates/v0.0.10/openai-docs/references/prompting-guide.md +244 -244
- package/docs/skill-candidates/v0.0.10/openai-docs/references/upgrade-guide.md +181 -181
- package/docs/skill-candidates/v0.0.10/openai-docs/scripts/fetch-codex-manual.mjs +598 -598
- package/docs/skill-candidates/v0.0.10/openai-docs/scripts/resolve-latest-model-info.js +147 -147
- package/docs/skill-candidates/v0.0.10/playwright/NOTICE.txt +14 -14
- package/docs/skill-candidates/v0.0.10/playwright/SKILL.md +147 -147
- package/docs/skill-candidates/v0.0.10/playwright/agents/openai.yaml +6 -6
- package/docs/skill-candidates/v0.0.10/playwright/assets/playwright-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/playwright/references/cli.md +116 -116
- package/docs/skill-candidates/v0.0.10/playwright/references/workflows.md +95 -95
- package/docs/skill-candidates/v0.0.10/playwright/scripts/playwright_cli.sh +25 -25
- package/docs/skill-candidates/v0.0.10/polyglot-backend-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.10/screenshot/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/screenshot/SKILL.md +267 -267
- package/docs/skill-candidates/v0.0.10/screenshot/agents/openai.yaml +6 -6
- package/docs/skill-candidates/v0.0.10/screenshot/assets/screenshot-small.svg +5 -5
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/ensure_macos_permissions.sh +54 -54
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_display_info.swift +22 -22
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_permissions.swift +40 -40
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_window_info.swift +126 -126
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/take_screenshot.ps1 +163 -163
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/take_screenshot.py +585 -585
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/SKILL.md +62 -62
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/agents/openai.yaml +4 -4
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/activation-policy.md +77 -77
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/human-approval-policy.md +83 -83
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/persona-dev-senior-master.md +46 -46
- package/docs/skill-candidates/v0.0.10/terminal-menu-operations/SKILL.md +30 -30
- package/docs/skill-candidates/v0.0.10/terminal-pixel-art-tui/SKILL.md +43 -43
- package/docs/skill-candidates/v0.0.10/webapp-testing/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/webapp-testing/SKILL.md +95 -95
- package/docs/skill-candidates/v0.0.10/webapp-testing/examples/console_logging.py +34 -34
- package/docs/skill-candidates/v0.0.10/webapp-testing/examples/element_discovery.py +39 -39
- package/docs/skill-candidates/v0.0.10/webapp-testing/examples/static_html_automation.py +32 -32
- package/docs/skill-candidates/v0.0.10/webapp-testing/scripts/with_server.py +105 -105
- package/docs/skill-candidates/v0.0.10/winui-app/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/winui-app/SKILL.md +94 -94
- package/docs/skill-candidates/v0.0.10/winui-app/agents/openai.yaml +5 -5
- package/docs/skill-candidates/v0.0.10/winui-app/config.yaml +50 -50
- package/docs/skill-candidates/v0.0.10/winui-app/references/_sections.md +96 -96
- package/docs/skill-candidates/v0.0.10/winui-app/references/accessibility-input-and-localization.md +51 -51
- package/docs/skill-candidates/v0.0.10/winui-app/references/build-run-and-launch-verification.md +72 -72
- package/docs/skill-candidates/v0.0.10/winui-app/references/community-toolkit-controls-and-helpers.md +57 -57
- package/docs/skill-candidates/v0.0.10/winui-app/references/controls-layout-and-adaptive-ui.md +84 -84
- package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-environment-audit-and-remediation.md +82 -82
- package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-setup-and-project-selection.md +67 -67
- package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-template-first-recovery.md +62 -62
- package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-winui-app-structure.md +62 -62
- package/docs/skill-candidates/v0.0.10/winui-app/references/motion-animations-and-polish.md +45 -45
- package/docs/skill-candidates/v0.0.10/winui-app/references/performance-diagnostics-and-responsiveness.md +46 -46
- package/docs/skill-candidates/v0.0.10/winui-app/references/sample-source-map.md +37 -37
- package/docs/skill-candidates/v0.0.10/winui-app/references/shell-navigation-and-windowing.md +67 -67
- package/docs/skill-candidates/v0.0.10/winui-app/references/styling-theming-materials-and-icons.md +71 -71
- package/docs/skill-candidates/v0.0.10/winui-app/references/testing-debugging-and-review-checklists.md +77 -77
- package/docs/skill-candidates/v0.0.10/winui-app/references/windows-app-sdk-lifecycle-notifications-and-deployment.md +52 -52
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/SKILL.md +398 -398
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/common-patterns.md +330 -330
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/complete-examples.md +871 -871
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/component-patterns.md +501 -501
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/data-fetching.md +766 -766
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/file-organization.md +501 -501
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/loading-and-error-states.md +500 -500
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/performance.md +405 -405
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/routing-guide.md +363 -363
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/styling-guide.md +427 -427
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/typescript-standards.md +417 -417
- package/docs/skill-candidates/v0.0.11/git-version-control-ops/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/go-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/java-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/javascript-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/json-contract-design/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/multi-client-mcp-ops/SKILL.md +36 -36
- package/docs/skill-candidates/v0.0.11/nextjs/SKILL.md +745 -745
- package/docs/skill-candidates/v0.0.11/nextjs/agents/openai.yaml +3 -3
- package/docs/skill-candidates/v0.0.11/nextjs/references/app-router-files.md +94 -94
- package/docs/skill-candidates/v0.0.11/python-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/ruby-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/senior-fullstack/SKILL.md +209 -209
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/architecture_patterns.md +103 -103
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/development_workflows.md +103 -103
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/tech_stack_guide.md +103 -103
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/code_quality_analyzer.py +114 -114
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/fullstack_scaffolder.py +114 -114
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/project_scaffolder.py +114 -114
- package/docs/skill-candidates/v0.0.11/shadcn/SKILL.md +573 -573
- package/docs/skill-candidates/v0.0.11/shadcn/agents/openai.yaml +3 -3
- package/docs/skill-candidates/v0.0.11/sql-postgresql-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/terminal-shell-ops/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/typescript-expert/SKILL.md +429 -429
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/tsconfig-strict.json +91 -91
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/typescript-cheatsheet.md +383 -383
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/utility-types.ts +335 -335
- package/docs/skill-candidates/v0.0.11/typescript-expert/scripts/ts_diagnostic.py +203 -203
- package/docs/skill-candidates/v0.0.11/ui-component-primitives/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/web-mobile-design-systems/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/windows-linux-platform-ops/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.12/context-compression-handoff/SKILL.md +47 -0
- package/docs/skill-candidates/v0.0.12/csharp-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/css-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/go-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/html-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/javascript-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/json-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/prompt-budget-gate/SKILL.md +46 -0
- package/docs/skill-candidates/v0.0.12/python-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/react-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/ruby-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/senior-master-code-optimizer/SKILL.md +48 -48
- package/docs/skill-candidates/v0.0.12/sql-senior-master-engineering/SKILL.md +31 -31
- package/docs/skill-candidates/v0.0.12/token-economy-orchestrator/SKILL.md +38 -0
- package/docs/skill-candidates/v0.0.12/typescript-senior-master-engineering/SKILL.md +35 -35
- package/docs/skill-candidates/v0.0.9/ai-ethics-human-dignity/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.9/broad-domain-router/SKILL.md +41 -41
- package/docs/skill-candidates/v0.0.9/catholic-moral-discernment/SKILL.md +31 -31
- package/docs/skill-candidates/v0.0.9/engineering-systems-master/SKILL.md +31 -31
- package/docs/skill-candidates/v0.0.9/language-quality-pt-en-fr/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.9/math-science-reasoning/SKILL.md +29 -29
- package/docs/skill-candidates/v0.0.9/philosophy-sociology-discernment/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.9/professional-boundary-triage/SKILL.md +40 -40
- package/docs/skill-candidates/v0.0.9/release-ethics-gate/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.9/source-authority-reviewer/SKILL.md +31 -31
- package/examples/client-configs/claude-code.commands.md +21 -21
- package/examples/client-configs/claude-code.project.mcp.json +18 -18
- package/examples/client-configs/claude-desktop.macos.json +18 -18
- package/examples/client-configs/claude-desktop.windows.json +20 -20
- package/examples/client-configs/codex.windows.toml +11 -11
- package/examples/client-configs/gemini-code-assist.intellij.mcp.json +18 -18
- package/examples/client-configs/gemini.linux.settings.json +21 -21
- package/examples/client-configs/gemini.windows.settings.json +23 -23
- package/examples/client-configs/generic-stdio.json +16 -16
- package/manifests/channels/beta.json +26 -26
- package/manifests/channels/stable.json +27 -27
- package/network/approved-skills.json +54 -54
- package/network/unapproved-skill-candidates.json +110 -110
- package/package.json +87 -86
- package/scripts/configure-private-registry.mjs +208 -208
- package/scripts/lib/private-registry.mjs +97 -97
- package/scripts/render-menu-evidence.mjs +130 -130
- package/scripts/verify-menu-actions.mjs +117 -117
- package/sources.json +11 -11
|
@@ -1,599 +1,599 @@
|
|
|
1
|
-
# Gotchas & Common Mistakes
|
|
2
|
-
|
|
3
|
-
> Part of the [use_figma skill](../SKILL.md). Every known pitfall with WRONG/CORRECT code examples.
|
|
4
|
-
|
|
5
|
-
## Contents
|
|
6
|
-
|
|
7
|
-
- Component properties and variant creation pitfalls
|
|
8
|
-
- Paint, color, and variable binding pitfalls
|
|
9
|
-
- Page context and plugin lifecycle pitfalls
|
|
10
|
-
- Auto Layout and sizing order pitfalls (including HUG/FILL interactions)
|
|
11
|
-
- Variant layout and geometry pitfalls
|
|
12
|
-
- Variable scopes and mode pitfalls
|
|
13
|
-
- Node cleanup and empty-fill pitfalls
|
|
14
|
-
- detachInstance() and node ID invalidation
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## New nodes default to (0,0) and overlap existing content
|
|
18
|
-
|
|
19
|
-
Every `figma.create*()` call places the node at position (0,0). If you append multiple nodes directly to the page, they all stack on top of each other and on top of any existing content.
|
|
20
|
-
|
|
21
|
-
**This only matters for nodes appended directly to the page** (i.e., top-level nodes). Nodes appended as children of other frames, components, or auto-layout containers are positioned by their parent — don't scan for overlaps when nesting nodes.
|
|
22
|
-
|
|
23
|
-
```js
|
|
24
|
-
// WRONG — top-level node lands at (0,0), overlapping existing page content
|
|
25
|
-
const frame = figma.createFrame()
|
|
26
|
-
frame.name = "My New Frame"
|
|
27
|
-
frame.resize(400, 300)
|
|
28
|
-
figma.currentPage.appendChild(frame)
|
|
29
|
-
|
|
30
|
-
// CORRECT — find existing content bounds and place the new top-level node to the right
|
|
31
|
-
const page = figma.currentPage
|
|
32
|
-
let maxX = 0
|
|
33
|
-
for (const child of page.children) {
|
|
34
|
-
const right = child.x + child.width
|
|
35
|
-
if (right > maxX) maxX = right
|
|
36
|
-
}
|
|
37
|
-
const frame = figma.createFrame()
|
|
38
|
-
frame.name = "My New Frame"
|
|
39
|
-
frame.resize(400, 300)
|
|
40
|
-
figma.currentPage.appendChild(frame)
|
|
41
|
-
frame.x = maxX + 100 // 100px gap from rightmost existing content
|
|
42
|
-
frame.y = 0
|
|
43
|
-
|
|
44
|
-
// NOT NEEDED — child nodes inside a parent don't need overlap scanning
|
|
45
|
-
const card = figma.createFrame()
|
|
46
|
-
card.layoutMode = 'VERTICAL'
|
|
47
|
-
const label = figma.createText()
|
|
48
|
-
card.appendChild(label) // positioned by auto-layout, no x/y needed
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## `addComponentProperty` returns a string key, not an object — never hardcode or guess it
|
|
52
|
-
|
|
53
|
-
Figma generates the property key dynamically (e.g. `"label#4:0"`). The suffix is unpredictable. Always capture and use the return value directly.
|
|
54
|
-
|
|
55
|
-
```js
|
|
56
|
-
// WRONG — guessing / hardcoding the key
|
|
57
|
-
comp.addComponentProperty('label', 'TEXT', 'Button')
|
|
58
|
-
labelNode.componentPropertyReferences = { characters: 'label#0:1' } // Error: key not found
|
|
59
|
-
|
|
60
|
-
// WRONG — treating the return value as an object
|
|
61
|
-
const result = comp.addComponentProperty('Label', 'TEXT', 'Button')
|
|
62
|
-
const propKey = Object.keys(result)[0] // BUG: returns '0' (first char index of string!)
|
|
63
|
-
labelNode.componentPropertyReferences = { characters: propKey } // Error: property '0' not found
|
|
64
|
-
|
|
65
|
-
// CORRECT — the return value IS the key string, use it directly
|
|
66
|
-
const propKey = comp.addComponentProperty('Label', 'TEXT', 'Button')
|
|
67
|
-
// propKey === "label#4:0" (exact value varies; never assume it)
|
|
68
|
-
labelNode.componentPropertyReferences = { characters: propKey }
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
The same applies to `COMPONENT_SET` nodes — `addComponentProperty` always returns the property key as a string.
|
|
72
|
-
|
|
73
|
-
## MUST return ALL created/mutated node IDs
|
|
74
|
-
|
|
75
|
-
Every script that creates or mutates nodes on the canvas must track and return all affected node IDs in the `figma.closePlugin()` response. Without these IDs, subsequent calls cannot reference, validate, or clean up those nodes.
|
|
76
|
-
|
|
77
|
-
```js
|
|
78
|
-
// WRONG — only returns the parent frame ID, loses track of children
|
|
79
|
-
const frame = figma.createFrame()
|
|
80
|
-
const rect = figma.createRectangle()
|
|
81
|
-
const text = figma.createText()
|
|
82
|
-
frame.appendChild(rect)
|
|
83
|
-
frame.appendChild(text)
|
|
84
|
-
figma.closePlugin(JSON.stringify({ nodeId: frame.id }))
|
|
85
|
-
|
|
86
|
-
// CORRECT — returns all created node IDs in a structured response
|
|
87
|
-
const frame = figma.createFrame()
|
|
88
|
-
const rect = figma.createRectangle()
|
|
89
|
-
const text = figma.createText()
|
|
90
|
-
frame.appendChild(rect)
|
|
91
|
-
frame.appendChild(text)
|
|
92
|
-
figma.closePlugin(JSON.stringify({
|
|
93
|
-
createdNodeIds: [frame.id, rect.id, text.id],
|
|
94
|
-
rootNodeId: frame.id
|
|
95
|
-
}))
|
|
96
|
-
|
|
97
|
-
// CORRECT — when mutating existing nodes, return those IDs too
|
|
98
|
-
const nodes = figma.currentPage.findAll(n => n.name === 'Card')
|
|
99
|
-
for (const n of nodes) {
|
|
100
|
-
n.fills = [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 } }]
|
|
101
|
-
}
|
|
102
|
-
figma.closePlugin(JSON.stringify({
|
|
103
|
-
mutatedNodeIds: nodes.map(n => n.id),
|
|
104
|
-
count: nodes.length
|
|
105
|
-
}))
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Colors are 0–1 range
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
// WRONG — will throw validation error (ZeroToOne enforced)
|
|
112
|
-
node.fills = [{ type: 'SOLID', color: { r: 255, g: 0, b: 0 } }]
|
|
113
|
-
|
|
114
|
-
// CORRECT
|
|
115
|
-
node.fills = [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 } }]
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## Fills/strokes are immutable arrays
|
|
119
|
-
|
|
120
|
-
```js
|
|
121
|
-
// WRONG — modifying in place does nothing
|
|
122
|
-
node.fills[0].color = { r: 1, g: 0, b: 0 }
|
|
123
|
-
|
|
124
|
-
// CORRECT — clone, modify, reassign
|
|
125
|
-
const fills = JSON.parse(JSON.stringify(node.fills))
|
|
126
|
-
fills[0].color = { r: 1, g: 0, b: 0 }
|
|
127
|
-
node.fills = fills
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## setBoundVariableForPaint returns a NEW paint
|
|
131
|
-
|
|
132
|
-
```js
|
|
133
|
-
// WRONG — ignoring return value
|
|
134
|
-
figma.variables.setBoundVariableForPaint(paint, "color", colorVar)
|
|
135
|
-
node.fills = [paint] // paint is unchanged!
|
|
136
|
-
|
|
137
|
-
// CORRECT — capture the returned new paint
|
|
138
|
-
const boundPaint = figma.variables.setBoundVariableForPaint(paint, "color", colorVar)
|
|
139
|
-
node.fills = [boundPaint]
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Variable collection starts with 1 mode
|
|
143
|
-
|
|
144
|
-
```js
|
|
145
|
-
// A new collection already has one mode — rename it, don't try to add first
|
|
146
|
-
const collection = figma.variables.createVariableCollection("Colors")
|
|
147
|
-
// collection.modes = [{ modeId: "...", name: "Mode 1" }]
|
|
148
|
-
collection.renameMode(collection.modes[0].modeId, "Light")
|
|
149
|
-
const darkModeId = collection.addMode("Dark")
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## combineAsVariants requires ComponentNodes
|
|
153
|
-
|
|
154
|
-
```js
|
|
155
|
-
// WRONG — passing frames
|
|
156
|
-
const f1 = figma.createFrame()
|
|
157
|
-
figma.combineAsVariants([f1], figma.currentPage) // Error!
|
|
158
|
-
|
|
159
|
-
// CORRECT — passing components
|
|
160
|
-
const c1 = figma.createComponent()
|
|
161
|
-
c1.name = "variant=primary, size=md"
|
|
162
|
-
const c2 = figma.createComponent()
|
|
163
|
-
c2.name = "variant=secondary, size=md"
|
|
164
|
-
figma.combineAsVariants([c1, c2], figma.currentPage)
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## Page switching: sync setter throws
|
|
168
|
-
|
|
169
|
-
The sync setter `figma.currentPage = page` **throws an error** in `use_figma` runtimes (MCP, evals, assistant). Use `await figma.setCurrentPageAsync(page)` instead — it switches the page and loads its content.
|
|
170
|
-
|
|
171
|
-
```js
|
|
172
|
-
// WRONG — throws "Setting figma.currentPage is not supported in this runtime"
|
|
173
|
-
figma.currentPage = targetPage
|
|
174
|
-
|
|
175
|
-
// CORRECT — async method switches and loads content
|
|
176
|
-
await figma.setCurrentPageAsync(targetPage)
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## `get_metadata` only sees one page — use `use_figma` to discover all pages
|
|
180
|
-
|
|
181
|
-
A Figma file can have multiple pages (canvas nodes). `get_metadata` operates on a single node/page — it cannot scan the entire document. To discover all pages and their top-level contents, use `use_figma`:
|
|
182
|
-
|
|
183
|
-
```js
|
|
184
|
-
// WRONG — calling get_metadata with the file root or expecting it to list all pages
|
|
185
|
-
// get_metadata only returns the subtree of the node you pass it
|
|
186
|
-
|
|
187
|
-
// CORRECT — use use_figma to list pages, then inspect each one
|
|
188
|
-
const pages = figma.root.children.map(p => `${p.name} id=${p.id} children=${p.children.length}`);
|
|
189
|
-
figma.closePlugin(pages.join('\n'));
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
Icons, variables, and components may live on pages other than the first. Always enumerate all pages before concluding that the file has no existing assets.
|
|
193
|
-
|
|
194
|
-
## Never use figma.notify()
|
|
195
|
-
|
|
196
|
-
```js
|
|
197
|
-
// WRONG — throws "not implemented" error
|
|
198
|
-
figma.notify("Done!")
|
|
199
|
-
|
|
200
|
-
// CORRECT — use closePlugin for messaging
|
|
201
|
-
figma.closePlugin("Done!")
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Script must always terminate
|
|
205
|
-
|
|
206
|
-
```js
|
|
207
|
-
// WRONG — no closePlugin call, script hangs
|
|
208
|
-
(async () => {
|
|
209
|
-
figma.createRectangle()
|
|
210
|
-
})()
|
|
211
|
-
|
|
212
|
-
// CORRECT — always close
|
|
213
|
-
(async () => {
|
|
214
|
-
try {
|
|
215
|
-
figma.createRectangle()
|
|
216
|
-
figma.closePlugin("created")
|
|
217
|
-
} catch(e) {
|
|
218
|
-
figma.closePluginWithFailure(e.toString())
|
|
219
|
-
}
|
|
220
|
-
})()
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## setBoundVariable for paint fields only works on SOLID paints
|
|
224
|
-
|
|
225
|
-
```js
|
|
226
|
-
// Only SOLID paint type supports color variable binding
|
|
227
|
-
// Gradient paints, image paints, etc. will throw
|
|
228
|
-
const solidPaint = { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }
|
|
229
|
-
const bound = figma.variables.setBoundVariableForPaint(solidPaint, "color", colorVar)
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
## Explicit variable modes must be set per component
|
|
233
|
-
|
|
234
|
-
```js
|
|
235
|
-
// WRONG — all variants render with the default (first) mode
|
|
236
|
-
const colorCollection = figma.variables.createVariableCollection("Colors")
|
|
237
|
-
// ... create variables and modes ...
|
|
238
|
-
// Components all show the first mode's values by default!
|
|
239
|
-
|
|
240
|
-
// CORRECT — set explicit mode on each component to get variant-specific values
|
|
241
|
-
component.setExplicitVariableModeForCollection(colorCollection.id, targetModeId)
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## `TextStyle.setBoundVariable` is not available in headless use_figma
|
|
245
|
-
|
|
246
|
-
`setBoundVariable` exists on `TextStyle` in the typed API but is **not available** when running scripts through `use_figma` (MCP, headless assistant mode). Calling it will throw `"not a function"`.
|
|
247
|
-
|
|
248
|
-
```js
|
|
249
|
-
// WRONG — throws "not a function" in use_figma / headless
|
|
250
|
-
const ts = figma.createTextStyle()
|
|
251
|
-
ts.setBoundVariable("fontSize", fontSizeVar)
|
|
252
|
-
|
|
253
|
-
// CORRECT (headless) — set raw values; bind variables interactively in Figma later
|
|
254
|
-
const ts = figma.createTextStyle()
|
|
255
|
-
ts.fontSize = 24
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
This only affects `TextStyle`. Variable binding on **nodes** (`node.setBoundVariable(...)`) and on **paint objects** (`figma.variables.setBoundVariableForPaint(...)`) still works in headless mode as expected.
|
|
259
|
-
|
|
260
|
-
If live variable binding on text styles is required, create the styles with raw values via `use_figma`, then bind variables interactively through the Figma Styles panel or a full interactive plugin.
|
|
261
|
-
|
|
262
|
-
## `lineHeight` and `letterSpacing` must be objects, not bare numbers
|
|
263
|
-
|
|
264
|
-
```js
|
|
265
|
-
// WRONG — throws or silently does nothing
|
|
266
|
-
style.lineHeight = 1.5
|
|
267
|
-
style.lineHeight = 24
|
|
268
|
-
style.letterSpacing = 0
|
|
269
|
-
|
|
270
|
-
// CORRECT
|
|
271
|
-
style.lineHeight = { unit: "AUTO" } // auto/intrinsic
|
|
272
|
-
style.lineHeight = { value: 24, unit: "PIXELS" } // fixed pixel height
|
|
273
|
-
style.lineHeight = { value: 150, unit: "PERCENT" } // percentage of font size
|
|
274
|
-
|
|
275
|
-
style.letterSpacing = { value: 0, unit: "PIXELS" } // no tracking
|
|
276
|
-
style.letterSpacing = { value: -0.5, unit: "PIXELS" } // tight
|
|
277
|
-
style.letterSpacing = { value: 5, unit: "PERCENT" } // percent-based
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
This applies to both `TextStyle` and `TextNode` properties. The same rule applies inside `use_figma`, interactive plugins, and any other plugin API context.
|
|
281
|
-
|
|
282
|
-
## Font style names are file-dependent — probe before assuming
|
|
283
|
-
|
|
284
|
-
Font style names vary per provider and per Figma file. `"SemiBold"` and `"Semi Bold"` are different strings. Loading a font with the wrong style string **throws silently or errors** — there is no canonical list.
|
|
285
|
-
|
|
286
|
-
```js
|
|
287
|
-
// WRONG — guessing style names
|
|
288
|
-
await figma.loadFontAsync({ family: "Inter", style: "SemiBold" }) // may throw
|
|
289
|
-
|
|
290
|
-
// CORRECT — probe which style names are available
|
|
291
|
-
const candidates = ["SemiBold", "Semi Bold", "Semibold"]
|
|
292
|
-
for (const style of candidates) {
|
|
293
|
-
try {
|
|
294
|
-
await figma.loadFontAsync({ family: "Inter", style })
|
|
295
|
-
// capture the one that works
|
|
296
|
-
break
|
|
297
|
-
} catch (_) {}
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
When building a type ramp script, always verify font styles against the target file before hardcoding them.
|
|
302
|
-
|
|
303
|
-
## combineAsVariants does NOT auto-layout in headless mode
|
|
304
|
-
|
|
305
|
-
```js
|
|
306
|
-
// WRONG — all variants stack at position (0, 0), resulting in a tiny ComponentSet
|
|
307
|
-
const components = [comp1, comp2, comp3]
|
|
308
|
-
const cs = figma.combineAsVariants(components, figma.currentPage)
|
|
309
|
-
// cs.width/height will be the size of a SINGLE variant!
|
|
310
|
-
|
|
311
|
-
// CORRECT — manually layout children in a grid after combining
|
|
312
|
-
const cs = figma.combineAsVariants(components, figma.currentPage)
|
|
313
|
-
const colWidth = 120
|
|
314
|
-
const rowHeight = 56
|
|
315
|
-
cs.children.forEach((child, i) => {
|
|
316
|
-
const col = i % numCols
|
|
317
|
-
const row = Math.floor(i / numCols)
|
|
318
|
-
child.x = col * colWidth
|
|
319
|
-
child.y = row * rowHeight
|
|
320
|
-
})
|
|
321
|
-
// CRITICAL: resize from actual child bounds, not formula — formula errors leave variants outside the boundary
|
|
322
|
-
let maxX = 0, maxY = 0
|
|
323
|
-
for (const child of cs.children) {
|
|
324
|
-
maxX = Math.max(maxX, child.x + child.width)
|
|
325
|
-
maxY = Math.max(maxY, child.y + child.height)
|
|
326
|
-
}
|
|
327
|
-
cs.resizeWithoutConstraints(maxX + 40, maxY + 40)
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
## COLOR variable values use {r, g, b, a} (with alpha)
|
|
331
|
-
|
|
332
|
-
```js
|
|
333
|
-
// Paint colors use {r, g, b} (no alpha — opacity is a separate paint property)
|
|
334
|
-
node.fills = [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 } }]
|
|
335
|
-
|
|
336
|
-
// But COLOR variable values use {r, g, b, a} — alpha maps to paint opacity
|
|
337
|
-
const colorVar = figma.variables.createVariable("bg", collection, "COLOR")
|
|
338
|
-
colorVar.setValueForMode(modeId, { r: 1, g: 0, b: 0, a: 1 }) // opaque red
|
|
339
|
-
colorVar.setValueForMode(modeId, { r: 0, g: 0, b: 0, a: 0 }) // fully transparent
|
|
340
|
-
|
|
341
|
-
// ⚠️ Don't confuse: {r, g, b} for paint colors vs {r, g, b, a} for variable values
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
## `layoutSizingVertical`/`layoutSizingHorizontal` = `'FILL'` requires auto-layout parent FIRST
|
|
345
|
-
|
|
346
|
-
```js
|
|
347
|
-
// WRONG — setting FILL before the node is a child of an auto-layout frame
|
|
348
|
-
const child = figma.createFrame()
|
|
349
|
-
child.layoutSizingVertical = 'FILL' // ERROR: "FILL can only be set on children of auto-layout frames"
|
|
350
|
-
parent.appendChild(child)
|
|
351
|
-
|
|
352
|
-
// CORRECT — append to auto-layout parent FIRST, then set FILL
|
|
353
|
-
const child = figma.createFrame()
|
|
354
|
-
parent.appendChild(child) // parent must have layoutMode set
|
|
355
|
-
child.layoutSizingVertical = 'FILL' // Works!
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
## HUG parents collapse FILL children
|
|
359
|
-
|
|
360
|
-
A `HUG` parent cannot give `FILL` children meaningful size. If children have `layoutSizingHorizontal = "FILL"` but the parent is `"HUG"`, the children collapse to minimum size. The parent must be `"FILL"` or `"FIXED"` for FILL children to expand. This is a common cause of truncated text in select fields, inputs, and action rows.
|
|
361
|
-
|
|
362
|
-
```js
|
|
363
|
-
// WRONG — parent hugs, so FILL children get zero extra space
|
|
364
|
-
const parent = figma.createFrame()
|
|
365
|
-
parent.layoutMode = 'HORIZONTAL'
|
|
366
|
-
parent.layoutSizingHorizontal = 'HUG'
|
|
367
|
-
const child = figma.createFrame()
|
|
368
|
-
parent.appendChild(child)
|
|
369
|
-
child.layoutSizingHorizontal = 'FILL' // collapses to min size!
|
|
370
|
-
|
|
371
|
-
// CORRECT — parent must be FIXED or FILL for FILL children to expand
|
|
372
|
-
const parent = figma.createFrame()
|
|
373
|
-
parent.layoutMode = 'HORIZONTAL'
|
|
374
|
-
parent.resize(400, 50)
|
|
375
|
-
parent.layoutSizingHorizontal = 'FIXED' // or 'FILL' if inside another auto-layout
|
|
376
|
-
const child = figma.createFrame()
|
|
377
|
-
parent.appendChild(child)
|
|
378
|
-
child.layoutSizingHorizontal = 'FILL' // expands to fill remaining 400px
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
## `layoutGrow` with a hugging parent causes content compression
|
|
382
|
-
|
|
383
|
-
```js
|
|
384
|
-
// WRONG — layoutGrow on a child when parent has primaryAxisSizingMode='AUTO' (hug)
|
|
385
|
-
// causes the child to SHRINK below its natural size instead of expanding
|
|
386
|
-
const parent = figma.createComponent()
|
|
387
|
-
parent.layoutMode = 'VERTICAL'
|
|
388
|
-
parent.primaryAxisSizingMode = 'AUTO' // hug contents
|
|
389
|
-
const content = figma.createFrame()
|
|
390
|
-
content.layoutMode = 'VERTICAL'
|
|
391
|
-
content.primaryAxisSizingMode = 'AUTO'
|
|
392
|
-
parent.appendChild(content)
|
|
393
|
-
content.layoutGrow = 1 // BUG: content compresses, children hidden!
|
|
394
|
-
|
|
395
|
-
// CORRECT — only use layoutGrow when parent has FIXED sizing with extra space
|
|
396
|
-
content.layoutGrow = 0 // let content take its natural size
|
|
397
|
-
// OR: set parent to FIXED sizing first
|
|
398
|
-
parent.primaryAxisSizingMode = 'FIXED'
|
|
399
|
-
parent.resizeWithoutConstraints(300, 500)
|
|
400
|
-
content.layoutGrow = 1 // NOW it correctly fills remaining space
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
## `resize()` resets `primaryAxisSizingMode` and `counterAxisSizingMode` to FIXED
|
|
404
|
-
|
|
405
|
-
```js
|
|
406
|
-
// WRONG — resize() after setting sizing mode overwrites it back to FIXED
|
|
407
|
-
const frame = figma.createComponent()
|
|
408
|
-
frame.layoutMode = 'VERTICAL'
|
|
409
|
-
frame.primaryAxisSizingMode = 'AUTO' // hug height
|
|
410
|
-
frame.counterAxisSizingMode = 'FIXED'
|
|
411
|
-
frame.resize(300, 10) // BUG: resets BOTH axes to 'FIXED'! Height stays at 10px forever.
|
|
412
|
-
|
|
413
|
-
// CORRECT — call resize() FIRST, then set sizing modes
|
|
414
|
-
const frame = figma.createComponent()
|
|
415
|
-
frame.layoutMode = 'VERTICAL'
|
|
416
|
-
frame.resize(300, 10) // set initial dimensions first
|
|
417
|
-
frame.counterAxisSizingMode = 'FIXED' // keep width fixed at 300
|
|
418
|
-
frame.primaryAxisSizingMode = 'AUTO' // NOW set height to hug — this sticks!
|
|
419
|
-
// Or use the modern shorthand (equivalent):
|
|
420
|
-
// frame.layoutSizingHorizontal = 'FIXED'
|
|
421
|
-
// frame.layoutSizingVertical = 'HUG'
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
## Node positions don't auto-reset after reparenting
|
|
425
|
-
|
|
426
|
-
```js
|
|
427
|
-
// WRONG — assuming positions reset when moving a node into a new parent
|
|
428
|
-
const node = figma.createRectangle()
|
|
429
|
-
node.x = 500; node.y = 500;
|
|
430
|
-
figma.currentPage.appendChild(node)
|
|
431
|
-
section.appendChild(node) // node still at (500, 500) relative to section!
|
|
432
|
-
|
|
433
|
-
// CORRECT — explicitly set x/y after ANY reparenting operation
|
|
434
|
-
section.appendChild(node)
|
|
435
|
-
node.x = 80; node.y = 80; // reset to desired position within section
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
## Grid layout with mixed-width rows causes overlaps
|
|
439
|
-
|
|
440
|
-
```js
|
|
441
|
-
// WRONG — using a single column offset for rows with different-width items
|
|
442
|
-
// e.g. vertical cards (320px) and horizontal cards (500px) in a 2-row grid
|
|
443
|
-
for (let i = 0; i < allCards.length; i++) {
|
|
444
|
-
allCards[i].x = (i % 4) * 370 // 370 works for 320px cards but NOT 500px cards!
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// CORRECT — compute each row's spacing independently based on actual child widths
|
|
448
|
-
const gap = 50
|
|
449
|
-
let x = 0
|
|
450
|
-
for (const card of horizontalCards) {
|
|
451
|
-
card.x = x
|
|
452
|
-
x += card.width + gap // use actual width, not a fixed column size
|
|
453
|
-
}
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
## Sections don't auto-resize to fit content
|
|
457
|
-
|
|
458
|
-
```js
|
|
459
|
-
// WRONG — section stays at default size, content overflows
|
|
460
|
-
const section = figma.createSection()
|
|
461
|
-
section.name = "My Section"
|
|
462
|
-
section.appendChild(someNode) // node may be outside section bounds
|
|
463
|
-
|
|
464
|
-
// CORRECT — explicitly resize after adding content
|
|
465
|
-
const section = figma.createSection()
|
|
466
|
-
section.name = "My Section"
|
|
467
|
-
section.appendChild(someNode)
|
|
468
|
-
section.resizeWithoutConstraints(
|
|
469
|
-
Math.max(someNode.width + 100, 800),
|
|
470
|
-
Math.max(someNode.height + 100, 600)
|
|
471
|
-
)
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
## `counterAxisAlignItems` does NOT support `'STRETCH'`
|
|
475
|
-
|
|
476
|
-
```js
|
|
477
|
-
// WRONG — 'STRETCH' is not a valid enum value
|
|
478
|
-
comp.counterAxisAlignItems = 'STRETCH'
|
|
479
|
-
// Error: Invalid enum value. Expected 'MIN' | 'MAX' | 'CENTER' | 'BASELINE', received 'STRETCH'
|
|
480
|
-
|
|
481
|
-
// CORRECT — use 'MIN' on the parent, then set children to FILL on the cross axis
|
|
482
|
-
comp.counterAxisAlignItems = 'MIN'
|
|
483
|
-
comp.appendChild(child)
|
|
484
|
-
// For vertical layout, stretch width:
|
|
485
|
-
child.layoutSizingHorizontal = 'FILL'
|
|
486
|
-
// For horizontal layout, stretch height:
|
|
487
|
-
child.layoutSizingVertical = 'FILL'
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
## Variable collection mode limits are plan-dependent
|
|
491
|
-
|
|
492
|
-
```js
|
|
493
|
-
// Figma limits modes per collection based on the team/org plan:
|
|
494
|
-
// Free: 1 mode only (no addMode)
|
|
495
|
-
// Professional: up to 4 modes
|
|
496
|
-
// Organization/Enterprise: up to 40+ modes
|
|
497
|
-
//
|
|
498
|
-
// WRONG — creating 20 modes on a Professional plan will fail silently or throw
|
|
499
|
-
const coll = figma.variables.createVariableCollection("Variants")
|
|
500
|
-
for (let i = 0; i < 20; i++) coll.addMode("mode" + i) // May fail!
|
|
501
|
-
|
|
502
|
-
// CORRECT — if you need many modes, split across multiple collections
|
|
503
|
-
// E.g., instead of 1 collection with 20 modes (variant×color):
|
|
504
|
-
// Collection A: 4 modes (variant: plain/outlined/soft/solid)
|
|
505
|
-
// Collection B: 5 modes (color: neutral/primary/danger/success/warning)
|
|
506
|
-
// Then use setExplicitVariableModeForCollection for BOTH on each component
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
## Variables default to `ALL_SCOPES` — always set scopes explicitly
|
|
510
|
-
|
|
511
|
-
```js
|
|
512
|
-
// WRONG — variable appears in every property picker (fills, text, strokes, spacing, etc.)
|
|
513
|
-
const bgColor = figma.variables.createVariable("Background/Default", coll, "COLOR")
|
|
514
|
-
// bgColor.scopes defaults to ["ALL_SCOPES"] — pollutes all dropdowns
|
|
515
|
-
|
|
516
|
-
// CORRECT — restrict to relevant property pickers
|
|
517
|
-
const bgColor = figma.variables.createVariable("Background/Default", coll, "COLOR")
|
|
518
|
-
bgColor.scopes = ["FRAME_FILL", "SHAPE_FILL", "EFFECT_COLOR"] // fill pickers only
|
|
519
|
-
|
|
520
|
-
const textColor = figma.variables.createVariable("Text/Default", coll, "COLOR")
|
|
521
|
-
textColor.scopes = ["TEXT_FILL"] // text color picker only
|
|
522
|
-
|
|
523
|
-
const borderColor = figma.variables.createVariable("Border/Default", coll, "COLOR")
|
|
524
|
-
borderColor.scopes = ["STROKE_COLOR"] // stroke picker only
|
|
525
|
-
|
|
526
|
-
const spacing = figma.variables.createVariable("Space/400", coll, "FLOAT")
|
|
527
|
-
spacing.scopes = ["GAP"] // gap/spacing pickers only
|
|
528
|
-
|
|
529
|
-
// Hide primitives that are only referenced via aliases
|
|
530
|
-
const primitive = figma.variables.createVariable("Brand/500", coll, "COLOR")
|
|
531
|
-
primitive.scopes = [] // hidden from all pickers
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
## Binding fills on nodes with empty fills
|
|
535
|
-
|
|
536
|
-
```js
|
|
537
|
-
// WRONG — binding to a node with no fills does nothing
|
|
538
|
-
const comp = figma.createComponent()
|
|
539
|
-
comp.fills = [] // transparent
|
|
540
|
-
// Can't bind a color variable to fills that don't exist
|
|
541
|
-
|
|
542
|
-
// CORRECT — add a placeholder SOLID fill, then bind the variable
|
|
543
|
-
const comp = figma.createComponent()
|
|
544
|
-
const basePaint = { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }
|
|
545
|
-
const boundPaint = figma.variables.setBoundVariableForPaint(basePaint, "color", colorVar)
|
|
546
|
-
comp.fills = [boundPaint]
|
|
547
|
-
// The variable's resolved value (which may be transparent) will control the actual color
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
## Mode names must be descriptive — never leave 'Mode 1'
|
|
551
|
-
|
|
552
|
-
Every new `VariableCollection` starts with one mode named `'Mode 1'`. Always rename it immediately. For single-mode collections use `'Default'`; for multi-mode collections use names from the source (e.g. `'Light'`/`'Dark'`, `'Desktop'`/`'Tablet'`/`'Mobile'`).
|
|
553
|
-
|
|
554
|
-
// WRONG — generic names give no semantic meaning
|
|
555
|
-
const coll = figma.variables.createVariableCollection('Colors')
|
|
556
|
-
// coll.modes[0].name === 'Mode 1' — left as-is
|
|
557
|
-
const darkId = coll.addMode('Mode 2')
|
|
558
|
-
|
|
559
|
-
// CORRECT — rename immediately to match the source
|
|
560
|
-
const coll = figma.variables.createVariableCollection('Colors')
|
|
561
|
-
coll.renameMode(coll.modes[0].modeId, 'Light') // was 'Mode 1'
|
|
562
|
-
const darkId = coll.addMode('Dark')
|
|
563
|
-
|
|
564
|
-
// For single-mode collections (primitives, spacing, etc.)
|
|
565
|
-
const spacing = figma.variables.createVariableCollection('Spacing')
|
|
566
|
-
spacing.renameMode(spacing.modes[0].modeId, 'Default') // was 'Mode 1'
|
|
567
|
-
|
|
568
|
-
## CSS variable names must not contain spaces
|
|
569
|
-
|
|
570
|
-
When constructing a `var(--name)` string from a Figma variable name, replace BOTH slashes AND spaces with hyphens and convert to lowercase.
|
|
571
|
-
|
|
572
|
-
// WRONG — only replacing slashes leaves spaces like 'var(--color-bg-brand secondary hover)'
|
|
573
|
-
v.setVariableCodeSyntax('WEB', `var(--${figmaName.replace(/\//g, '-').toLowerCase()})`)
|
|
574
|
-
|
|
575
|
-
// CORRECT — replace all whitespace and slashes in one pass
|
|
576
|
-
v.setVariableCodeSyntax('WEB', `var(--${figmaName.replace(/[\s\/]+/g, '-').toLowerCase()})`)
|
|
577
|
-
|
|
578
|
-
**Best practice**: Preserve the original CSS variable name from the source token file rather than deriving it from the Figma name.
|
|
579
|
-
|
|
580
|
-
// Preferred — use the source CSS name directly
|
|
581
|
-
v.setVariableCodeSyntax('WEB', `var(${token.cssVar})`) // e.g. '--color-bg-brand-secondary-hover'
|
|
582
|
-
|
|
583
|
-
## `detachInstance()` invalidates ancestor node IDs
|
|
584
|
-
|
|
585
|
-
When `detachInstance()` is called on a nested instance inside a library component instance, the parent instance may also get implicitly detached (converted from INSTANCE to FRAME with a **new ID**). Any previously cached ID for the parent becomes invalid.
|
|
586
|
-
|
|
587
|
-
```js
|
|
588
|
-
// WRONG — using cached parent ID after child detach
|
|
589
|
-
const parentId = parentInstance.id;
|
|
590
|
-
nestedChild.detachInstance();
|
|
591
|
-
const parent = await figma.getNodeByIdAsync(parentId); // null! ID changed.
|
|
592
|
-
|
|
593
|
-
// CORRECT — re-discover by traversal from a stable (non-instance) frame
|
|
594
|
-
const stableFrame = await figma.getNodeByIdAsync(manualFrameId);
|
|
595
|
-
nestedChild.detachInstance();
|
|
596
|
-
const parent = stableFrame.findOne(n => n.name === "ParentName");
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
If detaching multiple nested instances across siblings, do it in a **single** `use_figma` call — discover all targets by traversal before any detachment mutates the tree.
|
|
1
|
+
# Gotchas & Common Mistakes
|
|
2
|
+
|
|
3
|
+
> Part of the [use_figma skill](../SKILL.md). Every known pitfall with WRONG/CORRECT code examples.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- Component properties and variant creation pitfalls
|
|
8
|
+
- Paint, color, and variable binding pitfalls
|
|
9
|
+
- Page context and plugin lifecycle pitfalls
|
|
10
|
+
- Auto Layout and sizing order pitfalls (including HUG/FILL interactions)
|
|
11
|
+
- Variant layout and geometry pitfalls
|
|
12
|
+
- Variable scopes and mode pitfalls
|
|
13
|
+
- Node cleanup and empty-fill pitfalls
|
|
14
|
+
- detachInstance() and node ID invalidation
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## New nodes default to (0,0) and overlap existing content
|
|
18
|
+
|
|
19
|
+
Every `figma.create*()` call places the node at position (0,0). If you append multiple nodes directly to the page, they all stack on top of each other and on top of any existing content.
|
|
20
|
+
|
|
21
|
+
**This only matters for nodes appended directly to the page** (i.e., top-level nodes). Nodes appended as children of other frames, components, or auto-layout containers are positioned by their parent — don't scan for overlaps when nesting nodes.
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
// WRONG — top-level node lands at (0,0), overlapping existing page content
|
|
25
|
+
const frame = figma.createFrame()
|
|
26
|
+
frame.name = "My New Frame"
|
|
27
|
+
frame.resize(400, 300)
|
|
28
|
+
figma.currentPage.appendChild(frame)
|
|
29
|
+
|
|
30
|
+
// CORRECT — find existing content bounds and place the new top-level node to the right
|
|
31
|
+
const page = figma.currentPage
|
|
32
|
+
let maxX = 0
|
|
33
|
+
for (const child of page.children) {
|
|
34
|
+
const right = child.x + child.width
|
|
35
|
+
if (right > maxX) maxX = right
|
|
36
|
+
}
|
|
37
|
+
const frame = figma.createFrame()
|
|
38
|
+
frame.name = "My New Frame"
|
|
39
|
+
frame.resize(400, 300)
|
|
40
|
+
figma.currentPage.appendChild(frame)
|
|
41
|
+
frame.x = maxX + 100 // 100px gap from rightmost existing content
|
|
42
|
+
frame.y = 0
|
|
43
|
+
|
|
44
|
+
// NOT NEEDED — child nodes inside a parent don't need overlap scanning
|
|
45
|
+
const card = figma.createFrame()
|
|
46
|
+
card.layoutMode = 'VERTICAL'
|
|
47
|
+
const label = figma.createText()
|
|
48
|
+
card.appendChild(label) // positioned by auto-layout, no x/y needed
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## `addComponentProperty` returns a string key, not an object — never hardcode or guess it
|
|
52
|
+
|
|
53
|
+
Figma generates the property key dynamically (e.g. `"label#4:0"`). The suffix is unpredictable. Always capture and use the return value directly.
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
// WRONG — guessing / hardcoding the key
|
|
57
|
+
comp.addComponentProperty('label', 'TEXT', 'Button')
|
|
58
|
+
labelNode.componentPropertyReferences = { characters: 'label#0:1' } // Error: key not found
|
|
59
|
+
|
|
60
|
+
// WRONG — treating the return value as an object
|
|
61
|
+
const result = comp.addComponentProperty('Label', 'TEXT', 'Button')
|
|
62
|
+
const propKey = Object.keys(result)[0] // BUG: returns '0' (first char index of string!)
|
|
63
|
+
labelNode.componentPropertyReferences = { characters: propKey } // Error: property '0' not found
|
|
64
|
+
|
|
65
|
+
// CORRECT — the return value IS the key string, use it directly
|
|
66
|
+
const propKey = comp.addComponentProperty('Label', 'TEXT', 'Button')
|
|
67
|
+
// propKey === "label#4:0" (exact value varies; never assume it)
|
|
68
|
+
labelNode.componentPropertyReferences = { characters: propKey }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The same applies to `COMPONENT_SET` nodes — `addComponentProperty` always returns the property key as a string.
|
|
72
|
+
|
|
73
|
+
## MUST return ALL created/mutated node IDs
|
|
74
|
+
|
|
75
|
+
Every script that creates or mutates nodes on the canvas must track and return all affected node IDs in the `figma.closePlugin()` response. Without these IDs, subsequent calls cannot reference, validate, or clean up those nodes.
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// WRONG — only returns the parent frame ID, loses track of children
|
|
79
|
+
const frame = figma.createFrame()
|
|
80
|
+
const rect = figma.createRectangle()
|
|
81
|
+
const text = figma.createText()
|
|
82
|
+
frame.appendChild(rect)
|
|
83
|
+
frame.appendChild(text)
|
|
84
|
+
figma.closePlugin(JSON.stringify({ nodeId: frame.id }))
|
|
85
|
+
|
|
86
|
+
// CORRECT — returns all created node IDs in a structured response
|
|
87
|
+
const frame = figma.createFrame()
|
|
88
|
+
const rect = figma.createRectangle()
|
|
89
|
+
const text = figma.createText()
|
|
90
|
+
frame.appendChild(rect)
|
|
91
|
+
frame.appendChild(text)
|
|
92
|
+
figma.closePlugin(JSON.stringify({
|
|
93
|
+
createdNodeIds: [frame.id, rect.id, text.id],
|
|
94
|
+
rootNodeId: frame.id
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
// CORRECT — when mutating existing nodes, return those IDs too
|
|
98
|
+
const nodes = figma.currentPage.findAll(n => n.name === 'Card')
|
|
99
|
+
for (const n of nodes) {
|
|
100
|
+
n.fills = [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 } }]
|
|
101
|
+
}
|
|
102
|
+
figma.closePlugin(JSON.stringify({
|
|
103
|
+
mutatedNodeIds: nodes.map(n => n.id),
|
|
104
|
+
count: nodes.length
|
|
105
|
+
}))
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Colors are 0–1 range
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
// WRONG — will throw validation error (ZeroToOne enforced)
|
|
112
|
+
node.fills = [{ type: 'SOLID', color: { r: 255, g: 0, b: 0 } }]
|
|
113
|
+
|
|
114
|
+
// CORRECT
|
|
115
|
+
node.fills = [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 } }]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Fills/strokes are immutable arrays
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
// WRONG — modifying in place does nothing
|
|
122
|
+
node.fills[0].color = { r: 1, g: 0, b: 0 }
|
|
123
|
+
|
|
124
|
+
// CORRECT — clone, modify, reassign
|
|
125
|
+
const fills = JSON.parse(JSON.stringify(node.fills))
|
|
126
|
+
fills[0].color = { r: 1, g: 0, b: 0 }
|
|
127
|
+
node.fills = fills
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## setBoundVariableForPaint returns a NEW paint
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
// WRONG — ignoring return value
|
|
134
|
+
figma.variables.setBoundVariableForPaint(paint, "color", colorVar)
|
|
135
|
+
node.fills = [paint] // paint is unchanged!
|
|
136
|
+
|
|
137
|
+
// CORRECT — capture the returned new paint
|
|
138
|
+
const boundPaint = figma.variables.setBoundVariableForPaint(paint, "color", colorVar)
|
|
139
|
+
node.fills = [boundPaint]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Variable collection starts with 1 mode
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
// A new collection already has one mode — rename it, don't try to add first
|
|
146
|
+
const collection = figma.variables.createVariableCollection("Colors")
|
|
147
|
+
// collection.modes = [{ modeId: "...", name: "Mode 1" }]
|
|
148
|
+
collection.renameMode(collection.modes[0].modeId, "Light")
|
|
149
|
+
const darkModeId = collection.addMode("Dark")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## combineAsVariants requires ComponentNodes
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
// WRONG — passing frames
|
|
156
|
+
const f1 = figma.createFrame()
|
|
157
|
+
figma.combineAsVariants([f1], figma.currentPage) // Error!
|
|
158
|
+
|
|
159
|
+
// CORRECT — passing components
|
|
160
|
+
const c1 = figma.createComponent()
|
|
161
|
+
c1.name = "variant=primary, size=md"
|
|
162
|
+
const c2 = figma.createComponent()
|
|
163
|
+
c2.name = "variant=secondary, size=md"
|
|
164
|
+
figma.combineAsVariants([c1, c2], figma.currentPage)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Page switching: sync setter throws
|
|
168
|
+
|
|
169
|
+
The sync setter `figma.currentPage = page` **throws an error** in `use_figma` runtimes (MCP, evals, assistant). Use `await figma.setCurrentPageAsync(page)` instead — it switches the page and loads its content.
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
// WRONG — throws "Setting figma.currentPage is not supported in this runtime"
|
|
173
|
+
figma.currentPage = targetPage
|
|
174
|
+
|
|
175
|
+
// CORRECT — async method switches and loads content
|
|
176
|
+
await figma.setCurrentPageAsync(targetPage)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## `get_metadata` only sees one page — use `use_figma` to discover all pages
|
|
180
|
+
|
|
181
|
+
A Figma file can have multiple pages (canvas nodes). `get_metadata` operates on a single node/page — it cannot scan the entire document. To discover all pages and their top-level contents, use `use_figma`:
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
// WRONG — calling get_metadata with the file root or expecting it to list all pages
|
|
185
|
+
// get_metadata only returns the subtree of the node you pass it
|
|
186
|
+
|
|
187
|
+
// CORRECT — use use_figma to list pages, then inspect each one
|
|
188
|
+
const pages = figma.root.children.map(p => `${p.name} id=${p.id} children=${p.children.length}`);
|
|
189
|
+
figma.closePlugin(pages.join('\n'));
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Icons, variables, and components may live on pages other than the first. Always enumerate all pages before concluding that the file has no existing assets.
|
|
193
|
+
|
|
194
|
+
## Never use figma.notify()
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
// WRONG — throws "not implemented" error
|
|
198
|
+
figma.notify("Done!")
|
|
199
|
+
|
|
200
|
+
// CORRECT — use closePlugin for messaging
|
|
201
|
+
figma.closePlugin("Done!")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Script must always terminate
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
// WRONG — no closePlugin call, script hangs
|
|
208
|
+
(async () => {
|
|
209
|
+
figma.createRectangle()
|
|
210
|
+
})()
|
|
211
|
+
|
|
212
|
+
// CORRECT — always close
|
|
213
|
+
(async () => {
|
|
214
|
+
try {
|
|
215
|
+
figma.createRectangle()
|
|
216
|
+
figma.closePlugin("created")
|
|
217
|
+
} catch(e) {
|
|
218
|
+
figma.closePluginWithFailure(e.toString())
|
|
219
|
+
}
|
|
220
|
+
})()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## setBoundVariable for paint fields only works on SOLID paints
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
// Only SOLID paint type supports color variable binding
|
|
227
|
+
// Gradient paints, image paints, etc. will throw
|
|
228
|
+
const solidPaint = { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }
|
|
229
|
+
const bound = figma.variables.setBoundVariableForPaint(solidPaint, "color", colorVar)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Explicit variable modes must be set per component
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
// WRONG — all variants render with the default (first) mode
|
|
236
|
+
const colorCollection = figma.variables.createVariableCollection("Colors")
|
|
237
|
+
// ... create variables and modes ...
|
|
238
|
+
// Components all show the first mode's values by default!
|
|
239
|
+
|
|
240
|
+
// CORRECT — set explicit mode on each component to get variant-specific values
|
|
241
|
+
component.setExplicitVariableModeForCollection(colorCollection.id, targetModeId)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## `TextStyle.setBoundVariable` is not available in headless use_figma
|
|
245
|
+
|
|
246
|
+
`setBoundVariable` exists on `TextStyle` in the typed API but is **not available** when running scripts through `use_figma` (MCP, headless assistant mode). Calling it will throw `"not a function"`.
|
|
247
|
+
|
|
248
|
+
```js
|
|
249
|
+
// WRONG — throws "not a function" in use_figma / headless
|
|
250
|
+
const ts = figma.createTextStyle()
|
|
251
|
+
ts.setBoundVariable("fontSize", fontSizeVar)
|
|
252
|
+
|
|
253
|
+
// CORRECT (headless) — set raw values; bind variables interactively in Figma later
|
|
254
|
+
const ts = figma.createTextStyle()
|
|
255
|
+
ts.fontSize = 24
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
This only affects `TextStyle`. Variable binding on **nodes** (`node.setBoundVariable(...)`) and on **paint objects** (`figma.variables.setBoundVariableForPaint(...)`) still works in headless mode as expected.
|
|
259
|
+
|
|
260
|
+
If live variable binding on text styles is required, create the styles with raw values via `use_figma`, then bind variables interactively through the Figma Styles panel or a full interactive plugin.
|
|
261
|
+
|
|
262
|
+
## `lineHeight` and `letterSpacing` must be objects, not bare numbers
|
|
263
|
+
|
|
264
|
+
```js
|
|
265
|
+
// WRONG — throws or silently does nothing
|
|
266
|
+
style.lineHeight = 1.5
|
|
267
|
+
style.lineHeight = 24
|
|
268
|
+
style.letterSpacing = 0
|
|
269
|
+
|
|
270
|
+
// CORRECT
|
|
271
|
+
style.lineHeight = { unit: "AUTO" } // auto/intrinsic
|
|
272
|
+
style.lineHeight = { value: 24, unit: "PIXELS" } // fixed pixel height
|
|
273
|
+
style.lineHeight = { value: 150, unit: "PERCENT" } // percentage of font size
|
|
274
|
+
|
|
275
|
+
style.letterSpacing = { value: 0, unit: "PIXELS" } // no tracking
|
|
276
|
+
style.letterSpacing = { value: -0.5, unit: "PIXELS" } // tight
|
|
277
|
+
style.letterSpacing = { value: 5, unit: "PERCENT" } // percent-based
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
This applies to both `TextStyle` and `TextNode` properties. The same rule applies inside `use_figma`, interactive plugins, and any other plugin API context.
|
|
281
|
+
|
|
282
|
+
## Font style names are file-dependent — probe before assuming
|
|
283
|
+
|
|
284
|
+
Font style names vary per provider and per Figma file. `"SemiBold"` and `"Semi Bold"` are different strings. Loading a font with the wrong style string **throws silently or errors** — there is no canonical list.
|
|
285
|
+
|
|
286
|
+
```js
|
|
287
|
+
// WRONG — guessing style names
|
|
288
|
+
await figma.loadFontAsync({ family: "Inter", style: "SemiBold" }) // may throw
|
|
289
|
+
|
|
290
|
+
// CORRECT — probe which style names are available
|
|
291
|
+
const candidates = ["SemiBold", "Semi Bold", "Semibold"]
|
|
292
|
+
for (const style of candidates) {
|
|
293
|
+
try {
|
|
294
|
+
await figma.loadFontAsync({ family: "Inter", style })
|
|
295
|
+
// capture the one that works
|
|
296
|
+
break
|
|
297
|
+
} catch (_) {}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
When building a type ramp script, always verify font styles against the target file before hardcoding them.
|
|
302
|
+
|
|
303
|
+
## combineAsVariants does NOT auto-layout in headless mode
|
|
304
|
+
|
|
305
|
+
```js
|
|
306
|
+
// WRONG — all variants stack at position (0, 0), resulting in a tiny ComponentSet
|
|
307
|
+
const components = [comp1, comp2, comp3]
|
|
308
|
+
const cs = figma.combineAsVariants(components, figma.currentPage)
|
|
309
|
+
// cs.width/height will be the size of a SINGLE variant!
|
|
310
|
+
|
|
311
|
+
// CORRECT — manually layout children in a grid after combining
|
|
312
|
+
const cs = figma.combineAsVariants(components, figma.currentPage)
|
|
313
|
+
const colWidth = 120
|
|
314
|
+
const rowHeight = 56
|
|
315
|
+
cs.children.forEach((child, i) => {
|
|
316
|
+
const col = i % numCols
|
|
317
|
+
const row = Math.floor(i / numCols)
|
|
318
|
+
child.x = col * colWidth
|
|
319
|
+
child.y = row * rowHeight
|
|
320
|
+
})
|
|
321
|
+
// CRITICAL: resize from actual child bounds, not formula — formula errors leave variants outside the boundary
|
|
322
|
+
let maxX = 0, maxY = 0
|
|
323
|
+
for (const child of cs.children) {
|
|
324
|
+
maxX = Math.max(maxX, child.x + child.width)
|
|
325
|
+
maxY = Math.max(maxY, child.y + child.height)
|
|
326
|
+
}
|
|
327
|
+
cs.resizeWithoutConstraints(maxX + 40, maxY + 40)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## COLOR variable values use {r, g, b, a} (with alpha)
|
|
331
|
+
|
|
332
|
+
```js
|
|
333
|
+
// Paint colors use {r, g, b} (no alpha — opacity is a separate paint property)
|
|
334
|
+
node.fills = [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 } }]
|
|
335
|
+
|
|
336
|
+
// But COLOR variable values use {r, g, b, a} — alpha maps to paint opacity
|
|
337
|
+
const colorVar = figma.variables.createVariable("bg", collection, "COLOR")
|
|
338
|
+
colorVar.setValueForMode(modeId, { r: 1, g: 0, b: 0, a: 1 }) // opaque red
|
|
339
|
+
colorVar.setValueForMode(modeId, { r: 0, g: 0, b: 0, a: 0 }) // fully transparent
|
|
340
|
+
|
|
341
|
+
// ⚠️ Don't confuse: {r, g, b} for paint colors vs {r, g, b, a} for variable values
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## `layoutSizingVertical`/`layoutSizingHorizontal` = `'FILL'` requires auto-layout parent FIRST
|
|
345
|
+
|
|
346
|
+
```js
|
|
347
|
+
// WRONG — setting FILL before the node is a child of an auto-layout frame
|
|
348
|
+
const child = figma.createFrame()
|
|
349
|
+
child.layoutSizingVertical = 'FILL' // ERROR: "FILL can only be set on children of auto-layout frames"
|
|
350
|
+
parent.appendChild(child)
|
|
351
|
+
|
|
352
|
+
// CORRECT — append to auto-layout parent FIRST, then set FILL
|
|
353
|
+
const child = figma.createFrame()
|
|
354
|
+
parent.appendChild(child) // parent must have layoutMode set
|
|
355
|
+
child.layoutSizingVertical = 'FILL' // Works!
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## HUG parents collapse FILL children
|
|
359
|
+
|
|
360
|
+
A `HUG` parent cannot give `FILL` children meaningful size. If children have `layoutSizingHorizontal = "FILL"` but the parent is `"HUG"`, the children collapse to minimum size. The parent must be `"FILL"` or `"FIXED"` for FILL children to expand. This is a common cause of truncated text in select fields, inputs, and action rows.
|
|
361
|
+
|
|
362
|
+
```js
|
|
363
|
+
// WRONG — parent hugs, so FILL children get zero extra space
|
|
364
|
+
const parent = figma.createFrame()
|
|
365
|
+
parent.layoutMode = 'HORIZONTAL'
|
|
366
|
+
parent.layoutSizingHorizontal = 'HUG'
|
|
367
|
+
const child = figma.createFrame()
|
|
368
|
+
parent.appendChild(child)
|
|
369
|
+
child.layoutSizingHorizontal = 'FILL' // collapses to min size!
|
|
370
|
+
|
|
371
|
+
// CORRECT — parent must be FIXED or FILL for FILL children to expand
|
|
372
|
+
const parent = figma.createFrame()
|
|
373
|
+
parent.layoutMode = 'HORIZONTAL'
|
|
374
|
+
parent.resize(400, 50)
|
|
375
|
+
parent.layoutSizingHorizontal = 'FIXED' // or 'FILL' if inside another auto-layout
|
|
376
|
+
const child = figma.createFrame()
|
|
377
|
+
parent.appendChild(child)
|
|
378
|
+
child.layoutSizingHorizontal = 'FILL' // expands to fill remaining 400px
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## `layoutGrow` with a hugging parent causes content compression
|
|
382
|
+
|
|
383
|
+
```js
|
|
384
|
+
// WRONG — layoutGrow on a child when parent has primaryAxisSizingMode='AUTO' (hug)
|
|
385
|
+
// causes the child to SHRINK below its natural size instead of expanding
|
|
386
|
+
const parent = figma.createComponent()
|
|
387
|
+
parent.layoutMode = 'VERTICAL'
|
|
388
|
+
parent.primaryAxisSizingMode = 'AUTO' // hug contents
|
|
389
|
+
const content = figma.createFrame()
|
|
390
|
+
content.layoutMode = 'VERTICAL'
|
|
391
|
+
content.primaryAxisSizingMode = 'AUTO'
|
|
392
|
+
parent.appendChild(content)
|
|
393
|
+
content.layoutGrow = 1 // BUG: content compresses, children hidden!
|
|
394
|
+
|
|
395
|
+
// CORRECT — only use layoutGrow when parent has FIXED sizing with extra space
|
|
396
|
+
content.layoutGrow = 0 // let content take its natural size
|
|
397
|
+
// OR: set parent to FIXED sizing first
|
|
398
|
+
parent.primaryAxisSizingMode = 'FIXED'
|
|
399
|
+
parent.resizeWithoutConstraints(300, 500)
|
|
400
|
+
content.layoutGrow = 1 // NOW it correctly fills remaining space
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## `resize()` resets `primaryAxisSizingMode` and `counterAxisSizingMode` to FIXED
|
|
404
|
+
|
|
405
|
+
```js
|
|
406
|
+
// WRONG — resize() after setting sizing mode overwrites it back to FIXED
|
|
407
|
+
const frame = figma.createComponent()
|
|
408
|
+
frame.layoutMode = 'VERTICAL'
|
|
409
|
+
frame.primaryAxisSizingMode = 'AUTO' // hug height
|
|
410
|
+
frame.counterAxisSizingMode = 'FIXED'
|
|
411
|
+
frame.resize(300, 10) // BUG: resets BOTH axes to 'FIXED'! Height stays at 10px forever.
|
|
412
|
+
|
|
413
|
+
// CORRECT — call resize() FIRST, then set sizing modes
|
|
414
|
+
const frame = figma.createComponent()
|
|
415
|
+
frame.layoutMode = 'VERTICAL'
|
|
416
|
+
frame.resize(300, 10) // set initial dimensions first
|
|
417
|
+
frame.counterAxisSizingMode = 'FIXED' // keep width fixed at 300
|
|
418
|
+
frame.primaryAxisSizingMode = 'AUTO' // NOW set height to hug — this sticks!
|
|
419
|
+
// Or use the modern shorthand (equivalent):
|
|
420
|
+
// frame.layoutSizingHorizontal = 'FIXED'
|
|
421
|
+
// frame.layoutSizingVertical = 'HUG'
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Node positions don't auto-reset after reparenting
|
|
425
|
+
|
|
426
|
+
```js
|
|
427
|
+
// WRONG — assuming positions reset when moving a node into a new parent
|
|
428
|
+
const node = figma.createRectangle()
|
|
429
|
+
node.x = 500; node.y = 500;
|
|
430
|
+
figma.currentPage.appendChild(node)
|
|
431
|
+
section.appendChild(node) // node still at (500, 500) relative to section!
|
|
432
|
+
|
|
433
|
+
// CORRECT — explicitly set x/y after ANY reparenting operation
|
|
434
|
+
section.appendChild(node)
|
|
435
|
+
node.x = 80; node.y = 80; // reset to desired position within section
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## Grid layout with mixed-width rows causes overlaps
|
|
439
|
+
|
|
440
|
+
```js
|
|
441
|
+
// WRONG — using a single column offset for rows with different-width items
|
|
442
|
+
// e.g. vertical cards (320px) and horizontal cards (500px) in a 2-row grid
|
|
443
|
+
for (let i = 0; i < allCards.length; i++) {
|
|
444
|
+
allCards[i].x = (i % 4) * 370 // 370 works for 320px cards but NOT 500px cards!
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// CORRECT — compute each row's spacing independently based on actual child widths
|
|
448
|
+
const gap = 50
|
|
449
|
+
let x = 0
|
|
450
|
+
for (const card of horizontalCards) {
|
|
451
|
+
card.x = x
|
|
452
|
+
x += card.width + gap // use actual width, not a fixed column size
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## Sections don't auto-resize to fit content
|
|
457
|
+
|
|
458
|
+
```js
|
|
459
|
+
// WRONG — section stays at default size, content overflows
|
|
460
|
+
const section = figma.createSection()
|
|
461
|
+
section.name = "My Section"
|
|
462
|
+
section.appendChild(someNode) // node may be outside section bounds
|
|
463
|
+
|
|
464
|
+
// CORRECT — explicitly resize after adding content
|
|
465
|
+
const section = figma.createSection()
|
|
466
|
+
section.name = "My Section"
|
|
467
|
+
section.appendChild(someNode)
|
|
468
|
+
section.resizeWithoutConstraints(
|
|
469
|
+
Math.max(someNode.width + 100, 800),
|
|
470
|
+
Math.max(someNode.height + 100, 600)
|
|
471
|
+
)
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
## `counterAxisAlignItems` does NOT support `'STRETCH'`
|
|
475
|
+
|
|
476
|
+
```js
|
|
477
|
+
// WRONG — 'STRETCH' is not a valid enum value
|
|
478
|
+
comp.counterAxisAlignItems = 'STRETCH'
|
|
479
|
+
// Error: Invalid enum value. Expected 'MIN' | 'MAX' | 'CENTER' | 'BASELINE', received 'STRETCH'
|
|
480
|
+
|
|
481
|
+
// CORRECT — use 'MIN' on the parent, then set children to FILL on the cross axis
|
|
482
|
+
comp.counterAxisAlignItems = 'MIN'
|
|
483
|
+
comp.appendChild(child)
|
|
484
|
+
// For vertical layout, stretch width:
|
|
485
|
+
child.layoutSizingHorizontal = 'FILL'
|
|
486
|
+
// For horizontal layout, stretch height:
|
|
487
|
+
child.layoutSizingVertical = 'FILL'
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## Variable collection mode limits are plan-dependent
|
|
491
|
+
|
|
492
|
+
```js
|
|
493
|
+
// Figma limits modes per collection based on the team/org plan:
|
|
494
|
+
// Free: 1 mode only (no addMode)
|
|
495
|
+
// Professional: up to 4 modes
|
|
496
|
+
// Organization/Enterprise: up to 40+ modes
|
|
497
|
+
//
|
|
498
|
+
// WRONG — creating 20 modes on a Professional plan will fail silently or throw
|
|
499
|
+
const coll = figma.variables.createVariableCollection("Variants")
|
|
500
|
+
for (let i = 0; i < 20; i++) coll.addMode("mode" + i) // May fail!
|
|
501
|
+
|
|
502
|
+
// CORRECT — if you need many modes, split across multiple collections
|
|
503
|
+
// E.g., instead of 1 collection with 20 modes (variant×color):
|
|
504
|
+
// Collection A: 4 modes (variant: plain/outlined/soft/solid)
|
|
505
|
+
// Collection B: 5 modes (color: neutral/primary/danger/success/warning)
|
|
506
|
+
// Then use setExplicitVariableModeForCollection for BOTH on each component
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Variables default to `ALL_SCOPES` — always set scopes explicitly
|
|
510
|
+
|
|
511
|
+
```js
|
|
512
|
+
// WRONG — variable appears in every property picker (fills, text, strokes, spacing, etc.)
|
|
513
|
+
const bgColor = figma.variables.createVariable("Background/Default", coll, "COLOR")
|
|
514
|
+
// bgColor.scopes defaults to ["ALL_SCOPES"] — pollutes all dropdowns
|
|
515
|
+
|
|
516
|
+
// CORRECT — restrict to relevant property pickers
|
|
517
|
+
const bgColor = figma.variables.createVariable("Background/Default", coll, "COLOR")
|
|
518
|
+
bgColor.scopes = ["FRAME_FILL", "SHAPE_FILL", "EFFECT_COLOR"] // fill pickers only
|
|
519
|
+
|
|
520
|
+
const textColor = figma.variables.createVariable("Text/Default", coll, "COLOR")
|
|
521
|
+
textColor.scopes = ["TEXT_FILL"] // text color picker only
|
|
522
|
+
|
|
523
|
+
const borderColor = figma.variables.createVariable("Border/Default", coll, "COLOR")
|
|
524
|
+
borderColor.scopes = ["STROKE_COLOR"] // stroke picker only
|
|
525
|
+
|
|
526
|
+
const spacing = figma.variables.createVariable("Space/400", coll, "FLOAT")
|
|
527
|
+
spacing.scopes = ["GAP"] // gap/spacing pickers only
|
|
528
|
+
|
|
529
|
+
// Hide primitives that are only referenced via aliases
|
|
530
|
+
const primitive = figma.variables.createVariable("Brand/500", coll, "COLOR")
|
|
531
|
+
primitive.scopes = [] // hidden from all pickers
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
## Binding fills on nodes with empty fills
|
|
535
|
+
|
|
536
|
+
```js
|
|
537
|
+
// WRONG — binding to a node with no fills does nothing
|
|
538
|
+
const comp = figma.createComponent()
|
|
539
|
+
comp.fills = [] // transparent
|
|
540
|
+
// Can't bind a color variable to fills that don't exist
|
|
541
|
+
|
|
542
|
+
// CORRECT — add a placeholder SOLID fill, then bind the variable
|
|
543
|
+
const comp = figma.createComponent()
|
|
544
|
+
const basePaint = { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }
|
|
545
|
+
const boundPaint = figma.variables.setBoundVariableForPaint(basePaint, "color", colorVar)
|
|
546
|
+
comp.fills = [boundPaint]
|
|
547
|
+
// The variable's resolved value (which may be transparent) will control the actual color
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Mode names must be descriptive — never leave 'Mode 1'
|
|
551
|
+
|
|
552
|
+
Every new `VariableCollection` starts with one mode named `'Mode 1'`. Always rename it immediately. For single-mode collections use `'Default'`; for multi-mode collections use names from the source (e.g. `'Light'`/`'Dark'`, `'Desktop'`/`'Tablet'`/`'Mobile'`).
|
|
553
|
+
|
|
554
|
+
// WRONG — generic names give no semantic meaning
|
|
555
|
+
const coll = figma.variables.createVariableCollection('Colors')
|
|
556
|
+
// coll.modes[0].name === 'Mode 1' — left as-is
|
|
557
|
+
const darkId = coll.addMode('Mode 2')
|
|
558
|
+
|
|
559
|
+
// CORRECT — rename immediately to match the source
|
|
560
|
+
const coll = figma.variables.createVariableCollection('Colors')
|
|
561
|
+
coll.renameMode(coll.modes[0].modeId, 'Light') // was 'Mode 1'
|
|
562
|
+
const darkId = coll.addMode('Dark')
|
|
563
|
+
|
|
564
|
+
// For single-mode collections (primitives, spacing, etc.)
|
|
565
|
+
const spacing = figma.variables.createVariableCollection('Spacing')
|
|
566
|
+
spacing.renameMode(spacing.modes[0].modeId, 'Default') // was 'Mode 1'
|
|
567
|
+
|
|
568
|
+
## CSS variable names must not contain spaces
|
|
569
|
+
|
|
570
|
+
When constructing a `var(--name)` string from a Figma variable name, replace BOTH slashes AND spaces with hyphens and convert to lowercase.
|
|
571
|
+
|
|
572
|
+
// WRONG — only replacing slashes leaves spaces like 'var(--color-bg-brand secondary hover)'
|
|
573
|
+
v.setVariableCodeSyntax('WEB', `var(--${figmaName.replace(/\//g, '-').toLowerCase()})`)
|
|
574
|
+
|
|
575
|
+
// CORRECT — replace all whitespace and slashes in one pass
|
|
576
|
+
v.setVariableCodeSyntax('WEB', `var(--${figmaName.replace(/[\s\/]+/g, '-').toLowerCase()})`)
|
|
577
|
+
|
|
578
|
+
**Best practice**: Preserve the original CSS variable name from the source token file rather than deriving it from the Figma name.
|
|
579
|
+
|
|
580
|
+
// Preferred — use the source CSS name directly
|
|
581
|
+
v.setVariableCodeSyntax('WEB', `var(${token.cssVar})`) // e.g. '--color-bg-brand-secondary-hover'
|
|
582
|
+
|
|
583
|
+
## `detachInstance()` invalidates ancestor node IDs
|
|
584
|
+
|
|
585
|
+
When `detachInstance()` is called on a nested instance inside a library component instance, the parent instance may also get implicitly detached (converted from INSTANCE to FRAME with a **new ID**). Any previously cached ID for the parent becomes invalid.
|
|
586
|
+
|
|
587
|
+
```js
|
|
588
|
+
// WRONG — using cached parent ID after child detach
|
|
589
|
+
const parentId = parentInstance.id;
|
|
590
|
+
nestedChild.detachInstance();
|
|
591
|
+
const parent = await figma.getNodeByIdAsync(parentId); // null! ID changed.
|
|
592
|
+
|
|
593
|
+
// CORRECT — re-discover by traversal from a stable (non-instance) frame
|
|
594
|
+
const stableFrame = await figma.getNodeByIdAsync(manualFrameId);
|
|
595
|
+
nestedChild.detachInstance();
|
|
596
|
+
const parent = stableFrame.findOne(n => n.name === "ParentName");
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
If detaching multiple nested instances across siblings, do it in a **single** `use_figma` call — discover all targets by traversal before any detachment mutates the tree.
|