@kensaurus/skills 0.0.0-stage → 2.1.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/.claude-plugin/marketplace.json +53 -0
- package/.claude-plugin/plugin.json +40 -0
- package/.cursor-plugin/plugin.json +38 -0
- package/.mcp.json +28 -0
- package/CHANGELOG.md +1790 -0
- package/LICENSE +21 -0
- package/LICENSE-APACHE +62 -0
- package/NOTICE +13 -0
- package/README.md +821 -2
- package/SECURITY.md +55 -0
- package/agents/code-reviewer.md +60 -0
- package/agents/completion-judge.md +89 -0
- package/agents/db-migrator.md +125 -0
- package/agents/debugger.md +47 -0
- package/agents/deploy-checker.md +100 -0
- package/agents/perf-monitor.md +74 -0
- package/assets/favicon.png +0 -0
- package/assets/logo-light.png +0 -0
- package/assets/logo.png +0 -0
- package/assets/logo.svg +6 -0
- package/assets/og.png +0 -0
- package/bin/install.mjs +1127 -0
- package/bin/kenji.js +2 -0
- package/commands/adr.md +17 -0
- package/commands/aeo-plan.md +18 -0
- package/commands/arch-boundaries.md +17 -0
- package/commands/aso-plan.md +18 -0
- package/commands/auth-flows.md +19 -0
- package/commands/backup-plan.md +17 -0
- package/commands/burndown-full.md +25 -0
- package/commands/capacitor-plan.md +18 -0
- package/commands/codemod-safety.md +22 -0
- package/commands/commit.md +18 -0
- package/commands/complete-everything.md +40 -0
- package/commands/cost-plan.md +19 -0
- package/commands/deadcode-plan.md +26 -0
- package/commands/deadcode.md +32 -0
- package/commands/debug-issue.md +17 -0
- package/commands/deps-plan.md +18 -0
- package/commands/docs-plan.md +17 -0
- package/commands/doctrine.md +19 -0
- package/commands/error-plan.md +19 -0
- package/commands/feedback-to-closure.md +36 -0
- package/commands/fix-issue.md +76 -0
- package/commands/gate-logic.md +26 -0
- package/commands/green-repo.md +36 -0
- package/commands/grill-me.md +19 -0
- package/commands/gtm-plan.md +21 -0
- package/commands/gtm-weekly.md +17 -0
- package/commands/gtm.md +22 -0
- package/commands/handoff.md +15 -0
- package/commands/housekeep-backlog.md +18 -0
- package/commands/housekeep-files.md +22 -0
- package/commands/housekeep-gates.md +18 -0
- package/commands/instant-nav.md +11 -0
- package/commands/integrity-plan.md +19 -0
- package/commands/launch-kit.md +16 -0
- package/commands/mcp-guide.md +40 -0
- package/commands/mobile-plan.md +19 -0
- package/commands/native-rn-monorepo/README.md +78 -0
- package/commands/native-rn-monorepo/android-build.md +26 -0
- package/commands/native-rn-monorepo/android-install.md +32 -0
- package/commands/native-rn-monorepo/android-logcat.md +37 -0
- package/commands/native-rn-monorepo/ios-ci-logs.md +56 -0
- package/commands/native-rn-monorepo/ios-ci-status.md +52 -0
- package/commands/native-rn-monorepo/ios-ci-trigger.md +59 -0
- package/commands/native-rn-monorepo/rn-reset.md +50 -0
- package/commands/native-rn-monorepo/rn-ship-ios.md +67 -0
- package/commands/native-rn-monorepo/rn-verify.md +53 -0
- package/commands/perf-plan.md +18 -0
- package/commands/plan-mode.md +74 -0
- package/commands/pr.md +16 -0
- package/commands/pricing-plan.md +20 -0
- package/commands/privacy-plan.md +18 -0
- package/commands/readability.md +12 -0
- package/commands/readme.md +15 -0
- package/commands/refactor.md +15 -0
- package/commands/release-prep.md +17 -0
- package/commands/research.md +25 -0
- package/commands/responsive-audit.md +21 -0
- package/commands/review-code.md +18 -0
- package/commands/rls-plan.md +18 -0
- package/commands/secrets-plan.md +18 -0
- package/commands/security-plan.md +19 -0
- package/commands/ship-and-observe.md +36 -0
- package/commands/skill-conflicts.md +19 -0
- package/commands/slop-plan.md +18 -0
- package/commands/stub-plan.md +18 -0
- package/commands/test-mutation.md +16 -0
- package/commands/test-plan.md +17 -0
- package/commands/test.md +29 -0
- package/commands/thirdparty-web-interface-guidelines.md +185 -0
- package/commands/uiux-plan.md +18 -0
- package/commands/uiux.md +45 -0
- package/commands/update-deps.md +21 -0
- package/commands/validation-plan.md +19 -0
- package/commands-portable/fix-issue.md +72 -0
- package/commands-portable/plan-mode.md +92 -0
- package/commands-portable/research.md +91 -0
- package/docs/screenshots/README.md +5 -0
- package/docs/screenshots/audit-dark.png +0 -0
- package/docs/screenshots/build-dark.png +0 -0
- package/docs/screenshots/grill-dark.png +0 -0
- package/docs/screenshots/hero-dark.png +0 -0
- package/docs/screenshots/hero-light.png +0 -0
- package/docs/screenshots/ship-dark.png +0 -0
- package/docs/screenshots/src/showcase.html +320 -0
- package/hooks/completion-gate.mjs +258 -0
- package/hooks/cursor-hooks.json +13 -0
- package/hooks/hooks.json +15 -0
- package/install.sh +21 -0
- package/llms.txt +40 -0
- package/mcp/README.md +266 -0
- package/mcp/VERSIONS.md +41 -0
- package/mcp/mcp-full.json.template +124 -0
- package/mcp/mcp.json.template +29 -0
- package/mcp/pinned-versions.json +27 -0
- package/package.json +94 -4
- package/rules/approved-plan-execution.mdc +65 -0
- package/rules/full-stack-ship-discipline.mdc +37 -0
- package/rules/native-rn-monorepo/README.md +63 -0
- package/rules/native-rn-monorepo/_project.mdc +69 -0
- package/rules/native-rn-monorepo/native-android.mdc +72 -0
- package/rules/native-rn-monorepo/native-ios.mdc +61 -0
- package/rules/native-rn-monorepo/react-native-js.mdc +78 -0
- package/rules/native-rn-monorepo/web.mdc +60 -0
- package/rules/project-starter/components.mdc +54 -0
- package/rules/project-starter/data-fetching.mdc +77 -0
- package/rules/project-starter/git.mdc +41 -0
- package/rules/project-starter/supabase.mdc +37 -0
- package/rules/project-starter/tailwind.mdc +48 -0
- package/rules/project-starter/typescript.mdc +36 -0
- package/rules/project-starter/web-performance.mdc +42 -0
- package/rules/senior-engineer.mdc +30 -0
- package/rules/shell-first-search.mdc +19 -0
- package/rules/skill-workflows.mdc +35 -0
- package/rules/verification-before-completion.mdc +57 -0
- package/skills/audit-accessibility/SKILL.md +441 -0
- package/skills/audit-agent-speed/SKILL.md +181 -0
- package/skills/audit-agent-speed/scripts/stop-typecheck.mjs +151 -0
- package/skills/audit-analytics/SKILL.md +138 -0
- package/skills/audit-auth-flows/SKILL.md +267 -0
- package/skills/audit-backend-architecture/SKILL.md +266 -0
- package/skills/audit-backend-architecture/references/patterns.md +386 -0
- package/skills/audit-bundle-size/SKILL.md +295 -0
- package/skills/audit-cicd/SKILL.md +218 -0
- package/skills/audit-code-quality/SKILL.md +314 -0
- package/skills/audit-code-review/SKILL.md +289 -0
- package/skills/audit-codemod-safety/SKILL.md +159 -0
- package/skills/audit-db-schema/SKILL.md +465 -0
- package/skills/audit-db-schema/references/details.md +110 -0
- package/skills/audit-doctrine/SKILL.md +189 -0
- package/skills/audit-env-parity/SKILL.md +133 -0
- package/skills/audit-fe-api/SKILL.md +458 -0
- package/skills/audit-gate-logic/SKILL.md +219 -0
- package/skills/audit-i18n/SKILL.md +339 -0
- package/skills/audit-infra-cost/SKILL.md +142 -0
- package/skills/audit-langfuse-llm/SKILL.md +468 -0
- package/skills/audit-langfuse-llm/references/details.md +226 -0
- package/skills/audit-llm-security/SKILL.md +147 -0
- package/skills/audit-monetization-iap/SKILL.md +137 -0
- package/skills/audit-payment-system/SKILL.md +268 -0
- package/skills/audit-payment-system/references/checklist.md +283 -0
- package/skills/audit-performance/SKILL.md +383 -0
- package/skills/audit-performance/references/loading-priority-2026.md +81 -0
- package/skills/audit-realworld/SKILL.md +287 -0
- package/skills/audit-registry-listing/SKILL.md +122 -0
- package/skills/audit-resilience/SKILL.md +153 -0
- package/skills/audit-responsive/SKILL.md +221 -0
- package/skills/audit-responsive/references/checklist.md +166 -0
- package/skills/audit-security/SKILL.md +289 -0
- package/skills/audit-skill-conflicts/SKILL.md +178 -0
- package/skills/audit-ui-states/SKILL.md +146 -0
- package/skills/audit-uiux-design-system/SKILL.md +475 -0
- package/skills/audit-uiux-design-system/references/details.md +71 -0
- package/skills/audit-ux/SKILL.md +381 -0
- package/skills/audit-ux/references/details.md +245 -0
- package/skills/audit-ux-journeys/SKILL.md +215 -0
- package/skills/audit-ux-journeys/references/checklist.md +179 -0
- package/skills/backend-db-performance/SKILL.md +441 -0
- package/skills/backend-error-handling/SKILL.md +489 -0
- package/skills/backend-error-handling/references/details.md +58 -0
- package/skills/backend-observability/SKILL.md +88 -0
- package/skills/backend-patterns/SKILL.md +499 -0
- package/skills/backend-patterns/references/architecture-patterns.md +298 -0
- package/skills/backend-realtime/SKILL.md +403 -0
- package/skills/backend-realtime/references/patterns.md +74 -0
- package/skills/burndown-full/SKILL.md +174 -0
- package/skills/complete-everything/SKILL.md +295 -0
- package/skills/data-pipeline/SKILL.md +109 -0
- package/skills/data-visualization/SKILL.md +488 -0
- package/skills/debug-error/SKILL.md +322 -0
- package/skills/debug-fe-be-integration/SKILL.md +459 -0
- package/skills/debug-sentry-monitor/SKILL.md +497 -0
- package/skills/debug-sentry-monitor/references/details.md +165 -0
- package/skills/deploy-npm/SKILL.md +394 -0
- package/skills/deploy-npm/references/example-mushi-mushi.md +52 -0
- package/skills/deploy-verify/SKILL.md +489 -0
- package/skills/design-api/SKILL.md +379 -0
- package/skills/design-canvas/SKILL.md +155 -0
- package/skills/design-email/SKILL.md +370 -0
- package/skills/design-frontend/SKILL.md +143 -0
- package/skills/design-generative-art/SKILL.md +474 -0
- package/skills/design-mobile-first/SKILL.md +506 -0
- package/skills/design-motion/SKILL.md +333 -0
- package/skills/design-motion/references/delight-interactions.md +191 -0
- package/skills/design-prd/SKILL.md +443 -0
- package/skills/design-system/SKILL.md +457 -0
- package/skills/design-theme/SKILL.md +226 -0
- package/skills/design-theme/themes/tsumagoi-ranch.md +150 -0
- package/skills/docs-adr/SKILL.md +168 -0
- package/skills/docs-coauthor/SKILL.md +368 -0
- package/skills/docs-comparison-pages/SKILL.md +117 -0
- package/skills/docs-domain-modeling/SKILL.md +97 -0
- package/skills/docs-launch-kit/SKILL.md +139 -0
- package/skills/docs-writer/SKILL.md +469 -0
- package/skills/enhance-agent-guardrails/SKILL.md +164 -0
- package/skills/enhance-arch-boundaries/SKILL.md +154 -0
- package/skills/enhance-capacitor-ui/SKILL.md +463 -0
- package/skills/enhance-capacitor-ui/references/details.md +750 -0
- package/skills/enhance-email-deliverability/SKILL.md +143 -0
- package/skills/enhance-growth-loops/SKILL.md +122 -0
- package/skills/enhance-lifecycle-email/SKILL.md +130 -0
- package/skills/enhance-motion/SKILL.md +193 -0
- package/skills/enhance-onboarding/SKILL.md +148 -0
- package/skills/enhance-pwa/SKILL.md +303 -0
- package/skills/enhance-readability/SKILL.md +146 -0
- package/skills/enhance-readme/SKILL.md +496 -0
- package/skills/enhance-readme/package-lock.json +187 -0
- package/skills/enhance-readme/package.json +17 -0
- package/skills/enhance-readme/scripts/generate-readme-blocks.mjs +199 -0
- package/skills/enhance-readme/scripts/record-readme-tour.mjs +442 -0
- package/skills/enhance-skill-prompts/SKILL.md +167 -0
- package/skills/enhance-skill-prompts/references/exemplar-audit-auth-flows.md +311 -0
- package/skills/enhance-ux-laws/SKILL.md +409 -0
- package/skills/enhance-web-conversion/SKILL.md +155 -0
- package/skills/enhance-web-forms/SKILL.md +154 -0
- package/skills/enhance-web-instant-nav/SKILL.md +138 -0
- package/skills/enhance-web-instant-nav/references/bfcache-blockers.md +23 -0
- package/skills/enhance-web-instant-nav/references/early-hints.md +33 -0
- package/skills/enhance-web-instant-nav/references/speculation-rules.md +44 -0
- package/skills/enhance-web-landing/SKILL.md +459 -0
- package/skills/enhance-web-landing/references/details.md +773 -0
- package/skills/enhance-web-redesign/SKILL.md +228 -0
- package/skills/enhance-web-seo/SKILL.md +276 -0
- package/skills/enhance-web-ui/SKILL.md +473 -0
- package/skills/enhance-web-ui/references/details.md +674 -0
- package/skills/enhance-web-ux/HEURISTICS.md +248 -0
- package/skills/enhance-web-ux/PATTERNS.md +375 -0
- package/skills/enhance-web-ux/SKILL.md +464 -0
- package/skills/enhance-web-ux/examples.md +222 -0
- package/skills/enhance-web-ux/references/details.md +406 -0
- package/skills/enhance-web-web3d/SKILL.md +397 -0
- package/skills/enhance-web-web3d/references/css-canvas-effects.md +180 -0
- package/skills/handoff/SKILL.md +66 -0
- package/skills/housekeep-backlog/SKILL.md +149 -0
- package/skills/housekeep-dead-code/SKILL.md +387 -0
- package/skills/housekeep-dead-code/references/ratchet-ci.md +205 -0
- package/skills/housekeep-dead-code/references/supabase-hygiene.md +152 -0
- package/skills/housekeep-design/SKILL.md +207 -0
- package/skills/housekeep-files/SKILL.md +220 -0
- package/skills/housekeep-files/references/naming-and-catalog.md +86 -0
- package/skills/housekeep-files/scripts/housekeep-files.ps1 +360 -0
- package/skills/housekeep-files/scripts/housekeep-files.sh +238 -0
- package/skills/housekeep-gates/SKILL.md +174 -0
- package/skills/iterate-agent-harness/SKILL.md +137 -0
- package/skills/iterate-gtm-weekly/SKILL.md +103 -0
- package/skills/iterate-post-launch/SKILL.md +292 -0
- package/skills/meta-mcp-builder/SKILL.md +313 -0
- package/skills/meta-skill-creator/SKILL.md +304 -0
- package/skills/mobile-capacitor-platform/SKILL.md +103 -0
- package/skills/mobile-emulator-start/SKILL.md +296 -0
- package/skills/mobile-emulator-test/SKILL.md +491 -0
- package/skills/mobile-emulator-test/references/details.md +478 -0
- package/skills/mobile-rn-performance/SKILL.md +107 -0
- package/skills/mobile-rn-screen/SKILL.md +476 -0
- package/skills/mobile-rn-screen/references/details.md +785 -0
- package/skills/mushi-health/SKILL.md +206 -0
- package/skills/mushi-integration/SKILL.md +257 -0
- package/skills/plan-aeo-readiness/SKILL.md +166 -0
- package/skills/plan-antislop/SKILL.md +281 -0
- package/skills/plan-aso/SKILL.md +149 -0
- package/skills/plan-backup-dr/SKILL.md +131 -0
- package/skills/plan-capacitor-hardening/SKILL.md +217 -0
- package/skills/plan-data-integrity/SKILL.md +187 -0
- package/skills/plan-dead-code/SKILL.md +386 -0
- package/skills/plan-dead-code/references/knip-config.md +214 -0
- package/skills/plan-dead-code/references/output-templates.md +133 -0
- package/skills/plan-dead-code/references/preservation-contract.md +50 -0
- package/skills/plan-dead-code/references/residue-greps.md +84 -0
- package/skills/plan-dependency-provenance/SKILL.md +200 -0
- package/skills/plan-docs-sync/SKILL.md +143 -0
- package/skills/plan-docs-sync/references/drift-taxonomy.md +43 -0
- package/skills/plan-docs-sync/references/output-templates.md +33 -0
- package/skills/plan-docs-sync/references/preservation-contract.md +17 -0
- package/skills/plan-error-handling/SKILL.md +205 -0
- package/skills/plan-gtm/SKILL.md +276 -0
- package/skills/plan-gtm/references/benchmarks-2026.md +183 -0
- package/skills/plan-input-validation/SKILL.md +179 -0
- package/skills/plan-llm-cost-guardrails/SKILL.md +176 -0
- package/skills/plan-mobile-readiness/SKILL.md +171 -0
- package/skills/plan-perf-audit/SKILL.md +145 -0
- package/skills/plan-perf-audit/references/audit-scope.md +51 -0
- package/skills/plan-perf-audit/references/output-templates.md +33 -0
- package/skills/plan-perf-audit/references/preservation-contract.md +13 -0
- package/skills/plan-pricing/SKILL.md +173 -0
- package/skills/plan-privacy-compliance/SKILL.md +148 -0
- package/skills/plan-rls-audit/SKILL.md +231 -0
- package/skills/plan-secrets-audit/SKILL.md +181 -0
- package/skills/plan-security-audit/SKILL.md +168 -0
- package/skills/plan-security-audit/references/output-templates.md +36 -0
- package/skills/plan-security-audit/references/owasp-supabase-scope.md +55 -0
- package/skills/plan-security-audit/references/preservation-contract.md +18 -0
- package/skills/plan-stub-checker/SKILL.md +216 -0
- package/skills/plan-stub-checker/references/detection-methodology.md +75 -0
- package/skills/plan-stub-checker/references/detection-taxonomy.md +34 -0
- package/skills/plan-stub-checker/references/output-templates.md +63 -0
- package/skills/plan-stub-checker/references/preservation-contract.md +24 -0
- package/skills/plan-test-coverage/SKILL.md +170 -0
- package/skills/plan-test-coverage/references/methodology.md +54 -0
- package/skills/plan-test-coverage/references/output-templates.md +34 -0
- package/skills/plan-test-coverage/references/preservation-contract.md +15 -0
- package/skills/plan-uiux-unification/SKILL.md +230 -0
- package/skills/plan-uiux-unification/references/output-templates.md +67 -0
- package/skills/plan-uiux-unification/references/phase-workbook.md +85 -0
- package/skills/plan-uiux-unification/references/preservation-contract.md +24 -0
- package/skills/protocol-browser-anti-stall/SKILL.md +211 -0
- package/skills/protocol-browser-anti-stall/references/mcp-to-cli-map.md +113 -0
- package/skills/protocol-browser-anti-stall/references/playwright-session-coordination.md +170 -0
- package/skills/research/SKILL.md +422 -0
- package/skills/test-exploratory/SKILL.md +165 -0
- package/skills/test-exploratory/references/charter-template.md +29 -0
- package/skills/test-load/SKILL.md +126 -0
- package/skills/test-mutation/SKILL.md +160 -0
- package/skills/test-playwright/SKILL.md +354 -0
- package/skills/test-qa/SKILL.md +364 -0
- package/skills/test-qa/references/details.md +268 -0
- package/skills/test-red-team/SKILL.md +386 -0
- package/skills/test-red-team/references/owasp-attack-checklist.md +193 -0
- package/skills/test-unit/SKILL.md +259 -0
- package/skills/test-unit/references/details.md +267 -0
- package/skills/test-visual-regression/SKILL.md +132 -0
- package/skills/thirdparty-emil-design-eng/ATTRIBUTION.md +20 -0
- package/skills/thirdparty-emil-design-eng/SKILL.md +21 -0
- package/skills/thirdparty-emil-design-eng/references/emil-design-eng.md +676 -0
- package/skills/thirdparty-ui-ux-pro-max/ATTRIBUTION.md +22 -0
- package/skills/thirdparty-ui-ux-pro-max/SKILL.md +304 -0
- package/skills/thirdparty-ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/thirdparty-ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/thirdparty-ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/thirdparty-ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/thirdparty-ui-ux-pro-max/data/products.csv +97 -0
- package/skills/thirdparty-ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/thirdparty-ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/thirdparty-ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/thirdparty-ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/thirdparty-ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/thirdparty-ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/thirdparty-ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/thirdparty-ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/thirdparty-ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/thirdparty-ui-ux-pro-max/scripts/search.py +114 -0
- package/skills/thirdparty-web-interface-guidelines/ATTRIBUTION.md +23 -0
- package/skills/thirdparty-web-interface-guidelines/SKILL.md +190 -0
- package/skills/workflow-build-feature/SKILL.md +118 -0
- package/skills/workflow-coding-discipline/SKILL.md +140 -0
- package/skills/workflow-environment-ready/SKILL.md +128 -0
- package/skills/workflow-feature-flag/SKILL.md +262 -0
- package/skills/workflow-feedback-to-closure/SKILL.md +165 -0
- package/skills/workflow-fix-and-ship/SKILL.md +136 -0
- package/skills/workflow-git-commit/SKILL.md +200 -0
- package/skills/workflow-green-repo/SKILL.md +166 -0
- package/skills/workflow-grilling/SKILL.md +73 -0
- package/skills/workflow-gtm/SKILL.md +153 -0
- package/skills/workflow-housekeep/SKILL.md +453 -0
- package/skills/workflow-housekeep/references/templates.md +109 -0
- package/skills/workflow-launch-ready/SKILL.md +145 -0
- package/skills/workflow-merge-conflicts/SKILL.md +62 -0
- package/skills/workflow-onboard/SKILL.md +99 -0
- package/skills/workflow-parallel-agents/SKILL.md +164 -0
- package/skills/workflow-pr/SKILL.md +197 -0
- package/skills/workflow-quality-gate/SKILL.md +147 -0
- package/skills/workflow-refactor/SKILL.md +274 -0
- package/skills/workflow-release-prep/SKILL.md +207 -0
- package/skills/workflow-ship-and-observe/SKILL.md +164 -0
- package/skills/workflow-spec-tdd/SKILL.md +141 -0
- package/skills/workflow-spec-tdd/references/spec-template.md +126 -0
- package/skills/workflow-spec-tdd/references/tdd-patterns.md +167 -0
- package/skills-cursor/babysit/SKILL.md +17 -0
- package/skills-cursor/canvas/SKILL.md +142 -0
- package/skills-cursor/canvas/sdk/canvas-tokens.d.ts +235 -0
- package/skills-cursor/canvas/sdk/chart-primitives.d.ts +200 -0
- package/skills-cursor/canvas/sdk/dag-layout.d.ts +102 -0
- package/skills-cursor/canvas/sdk/diff-view.d.ts +130 -0
- package/skills-cursor/canvas/sdk/form-primitives.d.ts +194 -0
- package/skills-cursor/canvas/sdk/hooks.d.ts +117 -0
- package/skills-cursor/canvas/sdk/index.d.ts +47 -0
- package/skills-cursor/canvas/sdk/theme.d.ts +61 -0
- package/skills-cursor/canvas/sdk/todo-list.d.ts +49 -0
- package/skills-cursor/canvas/sdk/ui-primitives.d.ts +549 -0
- package/skills-cursor/canvas/sdk/ui-primitives.test.d.ts +2 -0
- package/skills-cursor/create-hook/SKILL.md +238 -0
- package/skills-cursor/create-rule/SKILL.md +185 -0
- package/skills-cursor/create-skill/SKILL.md +269 -0
- package/skills-cursor/create-skill/references/authoring-guide.md +182 -0
- package/skills-cursor/create-subagent/SKILL.md +228 -0
- package/skills-cursor/migrate-to-skills/SKILL.md +121 -0
- package/skills-cursor/shell/SKILL.md +22 -0
- package/skills-cursor/split-to-prs/SKILL.md +47 -0
- package/skills-cursor/statusline/SKILL.md +193 -0
- package/skills-cursor/update-cli-config/SKILL.md +85 -0
- package/skills-cursor/update-cursor-settings/SKILL.md +137 -0
- package/skills.sh.json +297 -0
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
## Step 7 — Enhancement Plan
|
|
2
|
+
|
|
3
|
+
A single, reviewable table ordered by impact / risk:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
| Pri | File | Change | Heuristic | Risk |
|
|
7
|
+
|-----|------|--------|-----------|------|
|
|
8
|
+
| 1 | components/layout/ScreenContainer.tsx | Audit: confirm it handles all four safe-area edges | #1 | low |
|
|
9
|
+
| 2 | screens/home/HomeScreen.tsx | Remove duplicate streak display in footer | #8 | low |
|
|
10
|
+
| 3 | screens/home/HomeScreen.tsx | Add contentInset={{ bottom: tabBarHeight }} to FlatList | #1 | low |
|
|
11
|
+
| 4 | components/ui/Button.tsx | Add numberOfLines={1} to label; abbreviate if > 18 chars on compact | #4 | low |
|
|
12
|
+
| 5 | components/orchard/ToneCard.tsx | Bump bg tint /8 → /20, add ring-tone/30 | #1, #6 | low |
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Step 8 — Implement (smallest possible diffs)
|
|
18
|
+
|
|
19
|
+
Rules during implementation:
|
|
20
|
+
|
|
21
|
+
1. **Safe area first.** Audit the wrapper chain before any visual work. Getting
|
|
22
|
+
the safe area right unlocks the rest of the layout.
|
|
23
|
+
2. **One concern per edit.** Layout change separate from colour change separate
|
|
24
|
+
from animation change.
|
|
25
|
+
3. **No hex literals.** Use `theme.colors.*`, `withAlpha(theme.colors.X, n)`,
|
|
26
|
+
or named constants from `@org/design-tokens`. The ESLint
|
|
27
|
+
`no-restricted-syntax` rule should catch violations.
|
|
28
|
+
4. **Platform shadows in both props.** Every `View` that should cast a shadow
|
|
29
|
+
needs both the iOS `shadow*` props AND `elevation` for Android:
|
|
30
|
+
```tsx
|
|
31
|
+
style={{
|
|
32
|
+
// iOS
|
|
33
|
+
shadowColor: theme.colors.shadow,
|
|
34
|
+
shadowOffset: { width: 0, height: 2 },
|
|
35
|
+
shadowOpacity: 0.12,
|
|
36
|
+
shadowRadius: 8,
|
|
37
|
+
// Android
|
|
38
|
+
elevation: 4,
|
|
39
|
+
}}
|
|
40
|
+
```
|
|
41
|
+
5. **`hitSlop` not visual inflation.** Touch targets ≥ 44×44pt achieved via
|
|
42
|
+
`hitSlop`, negative-margin wrappers, or a pressable region sized for the
|
|
43
|
+
thumb — never by making the visible element 44×44.
|
|
44
|
+
6. **`useNativeDriver: true` on every Animated call.** Unless the animation
|
|
45
|
+
changes layout properties (width, height, padding — which are JS-only),
|
|
46
|
+
always add `useNativeDriver: true`. This moves the animation to the UI
|
|
47
|
+
thread and prevents jank.
|
|
48
|
+
7. **`numberOfLines` + `ellipsizeMode` on all single-line labels.** Prevents
|
|
49
|
+
wrapping at compact screen widths.
|
|
50
|
+
8. **Match colour saturation to domain tier.** Tier A/B needs `/15–/25` tints
|
|
51
|
+
with `/30–/40` rings to read as semantic categories at a squint. See
|
|
52
|
+
`§1.5 Domain Colour Tier`.
|
|
53
|
+
9. **Collapse conditional slots when empty.** `{slot && <Wrapper>{slot}</Wrapper>}`.
|
|
54
|
+
Never pay vertical real estate for absent content.
|
|
55
|
+
10. **Delete duplicate datums; replace with complementary data.** When the same
|
|
56
|
+
number appears twice on one screen, keep the most actionable placement and
|
|
57
|
+
replace the other with a sibling datum.
|
|
58
|
+
11. **`accessibilityLabel` and `accessibilityRole` on all interactive elements.**
|
|
59
|
+
VoiceOver (iOS) and TalkBack (Android) need explicit labels for non-text
|
|
60
|
+
Pressables. `role="button"` on custom Pressables.
|
|
61
|
+
12. **Respect `ReduceMotion` in every animation.**
|
|
62
|
+
```tsx
|
|
63
|
+
const { reduceMotionEnabled } = useReduceMotion(); // or useAnimatedStyle
|
|
64
|
+
const animValue = withTiming(to, {
|
|
65
|
+
duration: reduceMotionEnabled ? 0 : 300,
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
After every meaningful edit: run the repo's typecheck and lint scripts (e.g. `rn:typecheck` / `rn:lint` in a monorepo) on
|
|
70
|
+
modified files. Fix anything you introduced before moving on.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Step 9 — Verify
|
|
75
|
+
|
|
76
|
+
### 9a. Three-device re-screenshot
|
|
77
|
+
|
|
78
|
+
Re-take the screenshots from Step 3a. Compare side-by-side with the originals.
|
|
79
|
+
Check: no wrapped labels, no clipped content, no overlapping elements,
|
|
80
|
+
safe area correct on notched device.
|
|
81
|
+
|
|
82
|
+
### 9b. Re-run the device forensics gates
|
|
83
|
+
|
|
84
|
+
| Gate | What to re-check | Pass criterion |
|
|
85
|
+
|------|-----------------|----------------|
|
|
86
|
+
| Measurement | `onLayout` widths of every repeated element you fixed | Spread ≤ 2dp per group |
|
|
87
|
+
| Conditional slot | Zero-state render of every slot you touched | No `minHeight` reservation for absent content |
|
|
88
|
+
| Dup-datum | Each screen | Every datum appears exactly once |
|
|
89
|
+
| Category squint | Standard-phone screenshot at 10–15px blur | Tier-A/B: distinct colour blobs. Tier-C/D: near-uniform achromatic |
|
|
90
|
+
| Large text | Enable Accessibility → max font size | No overflow, no clipped labels |
|
|
91
|
+
| Keyboard | Tap every input | Active field stays above keyboard |
|
|
92
|
+
| Safe area | Screenshot at top and bottom edges | No content under status bar or home indicator |
|
|
93
|
+
|
|
94
|
+
### 9c. iOS CI gate
|
|
95
|
+
|
|
96
|
+
For any change that affects a screen's layout, navigation, or safe-area
|
|
97
|
+
handling, trigger the repo's iOS CI workflow
|
|
98
|
+
and install via TestFlight. Do NOT claim "iOS verified" from the Android
|
|
99
|
+
emulator alone.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Hidden Failure Modes — RN-Specific (N1–N15)
|
|
104
|
+
|
|
105
|
+
These are the "difficult-to-spot" patterns for React Native screens. When
|
|
106
|
+
feedback is vague-but-visceral ("button is too far down", "can't reach that",
|
|
107
|
+
"jank", "looks like a web page"), walk this list first.
|
|
108
|
+
|
|
109
|
+
### N1. Safe area inset violation
|
|
110
|
+
|
|
111
|
+
**Symptom:** Content hidden under the status bar, Dynamic Island, or home
|
|
112
|
+
indicator. Users can't tap the top CTA. Text is partially invisible.
|
|
113
|
+
|
|
114
|
+
**Detection:** Take a screenshot on a notched device (iPhone 14 Pro or
|
|
115
|
+
later, any Dynamic Island model) or an Android phone with a status bar.
|
|
116
|
+
Does the top of the content region start *below* the status bar? Does the
|
|
117
|
+
bottom CTA sit *above* the home indicator (iOS) or system navigation bar (Android)?
|
|
118
|
+
|
|
119
|
+
**Fix shape:** Wrap the screen root in `<SafeAreaView edges={['top','bottom']}>` or
|
|
120
|
+
consume `useSafeAreaInsets()` at the layout root. In repos with a
|
|
121
|
+
`ScreenContainer` primitive, verify it applies all four edges. Do NOT add
|
|
122
|
+
insets a second time inside the content — see N13.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### N2. Touch target below platform minimum
|
|
127
|
+
|
|
128
|
+
**Symptom:** Users mis-tap icon buttons, especially on smaller phones. Reported
|
|
129
|
+
as "hard to press" or silently as abandoned actions. Most common on close/dismiss
|
|
130
|
+
icons, navigation arrows, and inline quick-action buttons.
|
|
131
|
+
|
|
132
|
+
**Detection:** Add a temporary `onLayout` to the `Pressable` and log its
|
|
133
|
+
`width × height`. Apple HIG minimum: 44×44pt. M3 minimum: 48×48dp.
|
|
134
|
+
Visual icons are typically 20–28dp — they almost always need `hitSlop`.
|
|
135
|
+
|
|
136
|
+
**Fix shape:**
|
|
137
|
+
```tsx
|
|
138
|
+
// Wrong — visual chrome sized for the touch target
|
|
139
|
+
<Pressable style={{ width: 44, height: 44, borderRadius: 22, backgroundColor: ... }}>
|
|
140
|
+
<Icon size={20} />
|
|
141
|
+
</Pressable>
|
|
142
|
+
|
|
143
|
+
// Correct — icon is visually correct; touch area extended invisibly
|
|
144
|
+
<Pressable
|
|
145
|
+
hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
|
|
146
|
+
style={{ width: 24, height: 24 }}
|
|
147
|
+
>
|
|
148
|
+
<Icon size={20} />
|
|
149
|
+
</Pressable>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Or use a Pressable region sized for the thumb (44×44) with `alignItems/justifyContent: 'center'`
|
|
153
|
+
and a smaller visual indicator inside. The point is: **visual chrome ≠ touch target**.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### N3. Keyboard occlusion on input screens
|
|
158
|
+
|
|
159
|
+
**Symptom:** Tapping a text input on a form screen causes the keyboard to
|
|
160
|
+
slide up and cover the input. User must scroll manually or cannot see what
|
|
161
|
+
they're typing.
|
|
162
|
+
|
|
163
|
+
**Detection:** Open the screen, tap the first input, observe whether the
|
|
164
|
+
active field is above the keyboard.
|
|
165
|
+
|
|
166
|
+
**Fix shape:** Wrap the screen content in `KeyboardAvoidingView`:
|
|
167
|
+
```tsx
|
|
168
|
+
<KeyboardAvoidingView
|
|
169
|
+
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
170
|
+
style={{ flex: 1 }}
|
|
171
|
+
>
|
|
172
|
+
<ScrollView keyboardShouldPersistTaps="handled">
|
|
173
|
+
{/* inputs here */}
|
|
174
|
+
</ScrollView>
|
|
175
|
+
</KeyboardAvoidingView>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
For complex nested scrolls: `react-native-keyboard-controller` (preferred for New
|
|
179
|
+
Architecture) or `react-native-keyboard-aware-scroll-view`. Ensure the
|
|
180
|
+
`ScrollView` has `keyboardShouldPersistTaps="handled"` so tapping a button
|
|
181
|
+
above the keyboard doesn't dismiss it unexpectedly.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### N4. Tab bar obscuring bottom content
|
|
186
|
+
|
|
187
|
+
**Symptom:** The last row of a FlatList or the bottom section of a
|
|
188
|
+
ScrollView is permanently hidden behind the tab bar. Users can't reach
|
|
189
|
+
the last card.
|
|
190
|
+
|
|
191
|
+
**Detection:** Scroll to the very bottom of the list. If the last item
|
|
192
|
+
is only 50% visible, the scroll content is not accounting for the tab
|
|
193
|
+
bar height.
|
|
194
|
+
|
|
195
|
+
**Fix shape:**
|
|
196
|
+
```tsx
|
|
197
|
+
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
|
198
|
+
|
|
199
|
+
function ScreenWithList() {
|
|
200
|
+
const tabBarHeight = useBottomTabBarHeight();
|
|
201
|
+
return (
|
|
202
|
+
<FlatList
|
|
203
|
+
data={items}
|
|
204
|
+
renderItem={renderItem}
|
|
205
|
+
contentInset={{ bottom: tabBarHeight }} // iOS
|
|
206
|
+
contentContainerStyle={{ paddingBottom: tabBarHeight }} // Android
|
|
207
|
+
scrollIndicatorInsets={{ bottom: tabBarHeight }}
|
|
208
|
+
/>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Or apply via `ScreenContainer`'s `scrollInset` prop if your repo abstracts
|
|
214
|
+
this. Fix once at the shared list primitive so all screens benefit.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### N5. Text truncation at large dynamic type sizes
|
|
219
|
+
|
|
220
|
+
**Symptom:** On iOS with Accessibility → Display & Text Size → Larger Text
|
|
221
|
+
enabled (or on Android with a very large system font size), row labels wrap
|
|
222
|
+
to 2 lines, containers clip content, and grid cards overflow their bounds.
|
|
223
|
+
|
|
224
|
+
**Detection:** Enable the largest system font size in Settings (iOS:
|
|
225
|
+
Accessibility → Larger Text → max slider; Android: Settings → Font size
|
|
226
|
+
→ largest). Reload the app. Screenshot every screen.
|
|
227
|
+
|
|
228
|
+
**Fix shape:**
|
|
229
|
+
- Never set a fixed height on a `View` that contains `Text` that users type
|
|
230
|
+
or that comes from data (variable length).
|
|
231
|
+
- For labels that *must* stay on one line: `numberOfLines={1}` +
|
|
232
|
+
`ellipsizeMode="tail"` + `adjustsFontSizeToFit={true}` (iOS) or cap
|
|
233
|
+
with `maxFontSizeMultiplier` on the `Text`:
|
|
234
|
+
```tsx
|
|
235
|
+
<Text numberOfLines={1} maxFontSizeMultiplier={1.5}>
|
|
236
|
+
{label}
|
|
237
|
+
</Text>
|
|
238
|
+
```
|
|
239
|
+
- For icons next to labels: use a `flexShrink: 1` on the text container so
|
|
240
|
+
it yields space to a fixed-width icon, not the other way around.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
### N6. Platform shadow inconsistency (iOS vs Android)
|
|
245
|
+
|
|
246
|
+
**Symptom:** Cards appear lifted on iOS but flat on Android (or vice versa),
|
|
247
|
+
making the visual hierarchy differ between platforms.
|
|
248
|
+
|
|
249
|
+
**Detection:** Compare iOS and Android screenshots of the same screen. If
|
|
250
|
+
card depth / elevation reads differently, the shadow props are incomplete.
|
|
251
|
+
|
|
252
|
+
**Fix shape:** Always apply both the iOS shadow set AND `elevation` for Android:
|
|
253
|
+
```tsx
|
|
254
|
+
const cardShadow = {
|
|
255
|
+
// iOS
|
|
256
|
+
shadowColor: theme.colors.shadow, // use the token, never '#000'
|
|
257
|
+
shadowOffset: { width: 0, height: 2 },
|
|
258
|
+
shadowOpacity: 0.10,
|
|
259
|
+
shadowRadius: 6,
|
|
260
|
+
// Android
|
|
261
|
+
elevation: 3,
|
|
262
|
+
};
|
|
263
|
+
```
|
|
264
|
+
Note: `elevation` on Android also creates a clipping region. If the card has
|
|
265
|
+
rounded corners, add `overflow: 'hidden'` to prevent the elevation shadow
|
|
266
|
+
from leaking past the rounded boundary on older Android versions.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
### N7. Gesture conflict (horizontal swipe vs vertical scroll)
|
|
271
|
+
|
|
272
|
+
**Symptom:** Trying to swipe a row action (delete, archive) also triggers
|
|
273
|
+
vertical scrolling. Or: the React Navigation swipe-back gesture conflicts
|
|
274
|
+
with an in-screen horizontal PanGestureHandler.
|
|
275
|
+
|
|
276
|
+
**Detection:** Wrap a `PanGestureHandler` (or `SwipeableRow`) inside a
|
|
277
|
+
`ScrollView` and test: does swiping horizontally also scroll the list?
|
|
278
|
+
Does the iOS swipe-back gesture (from the left edge) conflict with
|
|
279
|
+
an in-screen swipe?
|
|
280
|
+
|
|
281
|
+
**Fix shape — list row swipe:**
|
|
282
|
+
```tsx
|
|
283
|
+
<PanGestureHandler
|
|
284
|
+
activeOffsetX={[-10, 10]} // must move 10dp horizontally before gesture activates
|
|
285
|
+
failOffsetY={[-5, 5]} // fail if user moves vertically first (yields to scroll)
|
|
286
|
+
>
|
|
287
|
+
<Animated.View>...</Animated.View>
|
|
288
|
+
</PanGestureHandler>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Fix shape — navigation swipe-back conflict:**
|
|
292
|
+
Use `gestureEnabled: false` on the screen that owns the conflicting gesture,
|
|
293
|
+
or use `simultaneousHandlers` to let both fire and filter in the gesture
|
|
294
|
+
handler's `onGestureEvent`.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### N8. Missing press feedback on Android
|
|
299
|
+
|
|
300
|
+
**Symptom:** Tapping a `Pressable` on Android produces no visual feedback.
|
|
301
|
+
The action fires but there's no ripple or press state. Users can't tell if
|
|
302
|
+
the tap registered.
|
|
303
|
+
|
|
304
|
+
**Detection:** Tap any tappable element on the Android emulator with the
|
|
305
|
+
touch indicator overlay on (Developer Options → Show tap locations). Does a
|
|
306
|
+
ripple or press state appear? If not, `android_ripple` is missing or `null`.
|
|
307
|
+
|
|
308
|
+
**Fix shape:**
|
|
309
|
+
```tsx
|
|
310
|
+
<Pressable
|
|
311
|
+
android_ripple={{ color: withAlpha(theme.colors.primary, 0.12), borderless: false }}
|
|
312
|
+
style={({ pressed }) => [
|
|
313
|
+
styles.item,
|
|
314
|
+
pressed && Platform.OS === 'ios' && { opacity: 0.7 }, // iOS press state
|
|
315
|
+
]}
|
|
316
|
+
>
|
|
317
|
+
{children}
|
|
318
|
+
</Pressable>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
If your repo has a `TouchablePrimitive`, add these defaults once there.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
### N9. Back button / swipe-back leaves stale screen state
|
|
326
|
+
|
|
327
|
+
**Symptom:** Navigate to a screen, fill a form, tap Android hardware back.
|
|
328
|
+
Return to the same screen — the form is not reset. Or: a counter that should
|
|
329
|
+
reset on `focus` still shows the old value.
|
|
330
|
+
|
|
331
|
+
**Detection:** Navigate to the screen, mutate state, press back, re-navigate
|
|
332
|
+
to the screen. Does the screen show fresh data?
|
|
333
|
+
|
|
334
|
+
**Fix shape:**
|
|
335
|
+
```tsx
|
|
336
|
+
// Subscribe to focus events to reset or re-fetch
|
|
337
|
+
useFocusEffect(
|
|
338
|
+
useCallback(() => {
|
|
339
|
+
// reset form or refetch data
|
|
340
|
+
return () => { /* cleanup on blur */ };
|
|
341
|
+
}, [])
|
|
342
|
+
);
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Note: `useEffect(() => {}, [])` runs only on mount — it does NOT re-run when
|
|
346
|
+
navigating back to the screen. Always use `useFocusEffect` for screen-scoped
|
|
347
|
+
side effects that should run on every visit.
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
### N10. JS-thread animation jank
|
|
352
|
+
|
|
353
|
+
**Symptom:** Animations stutter or drop frames, especially when they run
|
|
354
|
+
concurrently with a heavy JS operation (list render, API fetch, state update).
|
|
355
|
+
Visible as choppy transitions, spring animations that lag behind touch input.
|
|
356
|
+
|
|
357
|
+
**Detection:** Toggle the Perf Monitor in the developer menu (Shake → Perf
|
|
358
|
+
Monitor or `adb shell input keyevent 82`). Does the JS FPS drop to < 50fps
|
|
359
|
+
during the animation? If yes, the animation is running on the JS thread.
|
|
360
|
+
|
|
361
|
+
**Fix shape:**
|
|
362
|
+
1. Add `useNativeDriver: true` to all `Animated.spring` / `Animated.timing`
|
|
363
|
+
calls that only animate `transform` and `opacity` (not layout props).
|
|
364
|
+
2. For layout animations, use `LayoutAnimation.configureNext()` (runs on UI
|
|
365
|
+
thread for the next single layout pass).
|
|
366
|
+
3. For complex, gesture-driven animations: migrate to Reanimated 3
|
|
367
|
+
worklets — these run entirely on the UI thread and are immune to JS jank.
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
// Avoid
|
|
371
|
+
Animated.spring(anim, { toValue: 1, useNativeDriver: false }); // JS thread
|
|
372
|
+
|
|
373
|
+
// Prefer
|
|
374
|
+
Animated.spring(anim, { toValue: 1, useNativeDriver: true }); // UI thread
|
|
375
|
+
|
|
376
|
+
// Best for gesture-driven
|
|
377
|
+
const anim = useSharedValue(0);
|
|
378
|
+
const style = useAnimatedStyle(() => ({
|
|
379
|
+
transform: [{ translateX: anim.value * 100 }],
|
|
380
|
+
}));
|
|
381
|
+
// anim.value = 100; (worklet, runs on UI thread)
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
### N11. FlatList / SectionList re-rendering every row on scroll
|
|
387
|
+
|
|
388
|
+
**Symptom:** Scrolling a list is janky. Profiling shows every row
|
|
389
|
+
re-rendering on each scroll event, not just the rows entering the viewport.
|
|
390
|
+
|
|
391
|
+
**Detection:** In React DevTools Profiler (or add `console.log('row rendered', item.id)`
|
|
392
|
+
inside the row component), scroll the list. If all rows log on every scroll,
|
|
393
|
+
renderItem is not memoized.
|
|
394
|
+
|
|
395
|
+
**Fix shape:**
|
|
396
|
+
```tsx
|
|
397
|
+
// Memoize the row component
|
|
398
|
+
const Row = React.memo(({ item }) => <View>...</View>);
|
|
399
|
+
|
|
400
|
+
// Memoize renderItem
|
|
401
|
+
const renderItem = useCallback(
|
|
402
|
+
({ item }) => <Row item={item} />,
|
|
403
|
+
[] // stable reference; no deps unless renderItem logic depends on something
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
// Stable keyExtractor
|
|
407
|
+
const keyExtractor = useCallback((item) => item.id.toString(), []);
|
|
408
|
+
|
|
409
|
+
// For very long lists, consider FlashList (Shopify) instead of FlatList —
|
|
410
|
+
// it recycles cells and dramatically reduces re-render cost.
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Also: avoid inline anonymous functions in `renderItem` and `keyExtractor` —
|
|
414
|
+
they create new references on every render and defeat memoization.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
### N12. Skia / GPU component re-renders unnecessarily
|
|
419
|
+
|
|
420
|
+
**Symptom:** An animated or Skia-rendered component (background gradient,
|
|
421
|
+
canvas drawing, particle system) updates on every parent state change,
|
|
422
|
+
causing unnecessary GPU repaints and battery drain.
|
|
423
|
+
|
|
424
|
+
**Detection:** Wrap the Skia component in `React.memo` and add a
|
|
425
|
+
`console.log('skia re-render')` inside. Does it log on every keystroke in
|
|
426
|
+
an input above it? If yes, it's not properly isolated.
|
|
427
|
+
|
|
428
|
+
**Fix shape:**
|
|
429
|
+
- Wrap the component in `React.memo` with a custom comparator if the
|
|
430
|
+
props are complex objects.
|
|
431
|
+
- Drive Skia parameters via Reanimated `SharedValue` — changes to a
|
|
432
|
+
`SharedValue` do NOT cause React re-renders. Only the Skia canvas
|
|
433
|
+
re-paints.
|
|
434
|
+
- Gate expensive GPU effects behind `PerformanceProvider` — only render
|
|
435
|
+
them when the device is capable (check `useDeviceCapabilities()`).
|
|
436
|
+
|
|
437
|
+
```tsx
|
|
438
|
+
// Drive animation without causing React re-render
|
|
439
|
+
const progress = useSharedValue(0);
|
|
440
|
+
// progress.value = 0.8; ← no re-render, just repaints the Skia canvas
|
|
441
|
+
|
|
442
|
+
const SkiaBackground = React.memo(() => (
|
|
443
|
+
<Canvas style={StyleSheet.absoluteFill}>
|
|
444
|
+
<AnimatedRect progress={progress} />
|
|
445
|
+
</Canvas>
|
|
446
|
+
));
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
### N13. Double safe-area inset (the wrapper-chain trap)
|
|
452
|
+
|
|
453
|
+
**Symptom:** Unexpected large gap at the top of the screen. A custom header
|
|
454
|
+
sits too far below the status bar. `ScreenBackButton` appears in the wrong
|
|
455
|
+
position. Reported as "the back button is too low" or "why is there so much
|
|
456
|
+
empty space at the top?"
|
|
457
|
+
|
|
458
|
+
**Detection:** Walk the wrapper chain from the top-level navigator screen
|
|
459
|
+
option down to the child view. Count how many of these are in the chain:
|
|
460
|
+
`useSafeAreaInsets()`, `<SafeAreaView>`, `<SafeAreaProvider>`,
|
|
461
|
+
`useSafeAreaFrame()`, a `ScreenContainer` that applies `paddingTop: insets.top`.
|
|
462
|
+
|
|
463
|
+
If the count is ≥ 2 and they both affect the same edge, you have double-counting.
|
|
464
|
+
|
|
465
|
+
**Fix shape:** Pick ONE place to apply safe-area insets per edge — typically
|
|
466
|
+
the outermost `ScreenContainer` or layout wrapper. Children must not apply
|
|
467
|
+
their own insets unless they explicitly need to handle an edge the parent
|
|
468
|
+
does *not* cover.
|
|
469
|
+
|
|
470
|
+
Concretely: if `ScreenContainer` already pads `top: insets.top`, then
|
|
471
|
+
`ScreenBackButton` must use `top: 8` — NOT `top: 8 + insets.top`. Document
|
|
472
|
+
this in the `ScreenContainer` component header.
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
### N14. Deep link navigates to wrong stack depth
|
|
477
|
+
|
|
478
|
+
**Symptom:** Opening a deep link from cold start lands on the correct screen
|
|
479
|
+
but the back button does nothing (pops to blank), or navigates to an unrelated
|
|
480
|
+
screen. Swiping back from the deep-linked screen exits the app entirely.
|
|
481
|
+
|
|
482
|
+
**Detection:** Kill the app completely. Trigger the deep link via ADB:
|
|
483
|
+
```bash
|
|
484
|
+
adb shell am start -W -a android.intent.action.VIEW \
|
|
485
|
+
-d "yourscheme://screen/id123"
|
|
486
|
+
```
|
|
487
|
+
Press back. Where do you land?
|
|
488
|
+
|
|
489
|
+
**Fix shape:** Deep links must initialise the full navigation stack above
|
|
490
|
+
the target screen. In React Navigation's linking config, use `initialRouteNames`
|
|
491
|
+
or provide the `screens` nesting that mirrors the full stack shape:
|
|
492
|
+
|
|
493
|
+
```ts
|
|
494
|
+
// linking.ts
|
|
495
|
+
const linking = {
|
|
496
|
+
screens: {
|
|
497
|
+
HomeStack: {
|
|
498
|
+
screens: {
|
|
499
|
+
StudyScreen: {
|
|
500
|
+
path: 'study/:lessonId',
|
|
501
|
+
// Deep-linking here initialises HomeStack so back works correctly
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Validate with a test — see `linking-coverage.test.ts` in repos that enforce this.
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
### N15. Tab-switch animation competing with screen entrance animation
|
|
514
|
+
|
|
515
|
+
**Symptom:** Switching tabs shows a flash of blank before the incoming screen
|
|
516
|
+
appears, or two animations run simultaneously producing a double-fade or
|
|
517
|
+
z-index conflict.
|
|
518
|
+
|
|
519
|
+
**Detection:** Rapidly switch between tabs. Does the incoming screen flash
|
|
520
|
+
white before its content appears? Does the transition look "doubled"?
|
|
521
|
+
|
|
522
|
+
**Fix shape:** React Navigation's native stack (`react-native-screens`) handles
|
|
523
|
+
the screen entrance animation at the OS level. Do NOT add a JS-side
|
|
524
|
+
`Animated.View` or Reanimated entrance animation to the screen's root View —
|
|
525
|
+
this creates two simultaneous animations on the same layer.
|
|
526
|
+
|
|
527
|
+
If a screen needs a content-reveal animation (e.g. staggered list items),
|
|
528
|
+
trigger it in `useFocusEffect`, not in a `useEffect([], [])` mount effect.
|
|
529
|
+
Mount effects run once and don't re-trigger when a tab is revisited.
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Pattern Library
|
|
534
|
+
|
|
535
|
+
### Safe Area Wrapper Chain (correct)
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
Navigator (headerShown: false)
|
|
539
|
+
└── ScreenContainer (applies insets.top + insets.bottom)
|
|
540
|
+
├── BlurHeader (top: 0 — ScreenContainer already padded)
|
|
541
|
+
├── ScrollView (contentInsetAdjustmentBehavior: 'automatic' on iOS)
|
|
542
|
+
└── FloatingCTA (bottom: 16 — ScreenContainer already padded)
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
ScreenBackButton: `top: 8` flat. NOT `top: 8 + insets.top`.
|
|
546
|
+
|
|
547
|
+
### Correct Touch Target Shape
|
|
548
|
+
|
|
549
|
+
```tsx
|
|
550
|
+
// Visual: 24×24 icon
|
|
551
|
+
// Touch: 44×44pt via hitSlop
|
|
552
|
+
<Pressable
|
|
553
|
+
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
554
|
+
onPress={onPress}
|
|
555
|
+
android_ripple={{ color: withAlpha(theme.colors.primary, 0.12) }}
|
|
556
|
+
>
|
|
557
|
+
<Icon name="close" size={24} color={theme.colors.foreground} />
|
|
558
|
+
</Pressable>
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### FlatList with Tab Bar Clearance
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
const tabBarHeight = useBottomTabBarHeight();
|
|
565
|
+
|
|
566
|
+
<FlatList
|
|
567
|
+
data={items}
|
|
568
|
+
renderItem={renderItem}
|
|
569
|
+
keyExtractor={keyExtractor}
|
|
570
|
+
contentInset={{ bottom: tabBarHeight }}
|
|
571
|
+
contentContainerStyle={{ paddingBottom: tabBarHeight }}
|
|
572
|
+
scrollIndicatorInsets={{ bottom: tabBarHeight }}
|
|
573
|
+
removeClippedSubviews // Android performance
|
|
574
|
+
maxToRenderPerBatch={10}
|
|
575
|
+
initialNumToRender={12}
|
|
576
|
+
/>
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### Platform-Correct Shadow
|
|
580
|
+
|
|
581
|
+
```ts
|
|
582
|
+
const shadow = StyleSheet.create({
|
|
583
|
+
card: {
|
|
584
|
+
shadowColor: theme.colors.shadow,
|
|
585
|
+
shadowOffset: { width: 0, height: 2 },
|
|
586
|
+
shadowOpacity: 0.10,
|
|
587
|
+
shadowRadius: 8,
|
|
588
|
+
elevation: 3,
|
|
589
|
+
overflow: Platform.OS === 'android' ? 'hidden' : 'visible',
|
|
590
|
+
},
|
|
591
|
+
});
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Reanimated 3 Reduced Motion
|
|
595
|
+
|
|
596
|
+
```tsx
|
|
597
|
+
import { useReducedMotion } from 'react-native-reanimated';
|
|
598
|
+
|
|
599
|
+
function AnimatedChip({ active }) {
|
|
600
|
+
const reduceMotion = useReducedMotion();
|
|
601
|
+
const scale = useSharedValue(1);
|
|
602
|
+
|
|
603
|
+
const style = useAnimatedStyle(() => ({
|
|
604
|
+
transform: [{
|
|
605
|
+
scale: withSpring(active ? 1.04 : 1, {
|
|
606
|
+
duration: reduceMotion ? 0 : 300,
|
|
607
|
+
}),
|
|
608
|
+
}],
|
|
609
|
+
}));
|
|
610
|
+
|
|
611
|
+
return <Animated.View style={[styles.chip, style]} />;
|
|
612
|
+
}
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Motion Rules
|
|
618
|
+
|
|
619
|
+
- **Direct feedback** (tap confirmation, toggle): 100–180ms
|
|
620
|
+
- **Small state change** (counter increment, pill swap): 180–260ms
|
|
621
|
+
- **Screen entrance / large spatial transition**: 220–360ms (or defer to native stack)
|
|
622
|
+
- **Prefer `withSpring`** for anything the user drags (gesture-driven).
|
|
623
|
+
- **Prefer `withTiming` ease-out** for programmatic state changes.
|
|
624
|
+
- **Never animate layout props** (`width`, `height`, `padding`, `margin`) with
|
|
625
|
+
`useNativeDriver: true` — they require the JS thread. Use `LayoutAnimation`
|
|
626
|
+
or Reanimated `useAnimatedStyle` with `transform` equivalents instead.
|
|
627
|
+
- **Always check `useReducedMotion()`** (Reanimated) or
|
|
628
|
+
`AccessibilityInfo.isReduceMotionEnabled()` and set `duration: 0` when true.
|
|
629
|
+
- Motion must answer at least one question:
|
|
630
|
+
- Did my action work? (tap feedback)
|
|
631
|
+
- What changed? (state transition)
|
|
632
|
+
- Where did this come from / go? (spatial relationship)
|
|
633
|
+
- What can I interact with? (affordance signal)
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## Quick Sanity Checks Before You Stop
|
|
638
|
+
|
|
639
|
+
- [ ] Safe-area wrapper chain audited; insets applied exactly once per edge.
|
|
640
|
+
- [ ] Every `Pressable` / `TouchableOpacity` has a minimum 44×44pt touch area
|
|
641
|
+
(via `hitSlop` or explicit size; NOT by inflating visual chrome).
|
|
642
|
+
- [ ] Every text input screen tested with keyboard open; active field visible.
|
|
643
|
+
- [ ] FlatList bottom content not clipped by tab bar (`contentInset` applied).
|
|
644
|
+
- [ ] Large-text (Accessibility max font size) tested; no overflow or clipping.
|
|
645
|
+
- [ ] Platform shadow applied in both iOS `shadow*` props AND Android `elevation`.
|
|
646
|
+
- [ ] Every `Animated.spring/timing` has `useNativeDriver: true` (or justified exception).
|
|
647
|
+
- [ ] Every `Pressable` on Android has `android_ripple` (or justified exception).
|
|
648
|
+
- [ ] `useFocusEffect` used for screen-scoped data fetching / form resets (not `useEffect([], [])`).
|
|
649
|
+
- [ ] `renderItem` memoized with `useCallback`; row component wrapped in `React.memo`.
|
|
650
|
+
- [ ] `useReducedMotion()` / `AccessibilityInfo.isReduceMotionEnabled()` respected.
|
|
651
|
+
- [ ] `accessibilityLabel` and `accessibilityRole` set on all non-text Pressables.
|
|
652
|
+
- [ ] Domain colour tier identified (A/B/C/D) and applied consistently.
|
|
653
|
+
- [ ] Primary content obvious at a hierarchy squint (first eye target).
|
|
654
|
+
- [ ] Tier-A/B: categories distinguishable at a category squint (colour alone).
|
|
655
|
+
- [ ] No datum (number, word, date, status) appears more than once on the same screen.
|
|
656
|
+
- [ ] Every conditional slot tested in zero-state; no dead `minHeight` floors.
|
|
657
|
+
- [ ] At least one piece of trailing/right-anchored metadata per stacked card row.
|
|
658
|
+
- [ ] Repeated-element `onLayout` measurements within 2dp of each other.
|
|
659
|
+
- [ ] Repeating bug patched at the shared primitive, not every consumer screen.
|
|
660
|
+
- [ ] The repo's typecheck and lint scripts (e.g. `rn:typecheck` / `rn:lint` in a monorepo) pass on all modified files.
|
|
661
|
+
- [ ] Android emulator screenshots taken at 360×800, 412×917, 430×932 (or platform equivalent).
|
|
662
|
+
- [ ] iOS CI triggered for any change affecting layout, safe area, or navigation.
|
|
663
|
+
|
|
664
|
+
### Hidden failure-mode audit (RN-specific)
|
|
665
|
+
|
|
666
|
+
- [ ] **N1 safe area**: content starts below status bar; home indicator not covered.
|
|
667
|
+
- [ ] **N2 touch target**: every `Pressable` ≥ 44×44pt via `hitSlop` or size.
|
|
668
|
+
- [ ] **N3 keyboard**: active input visible above keyboard on all input screens.
|
|
669
|
+
- [ ] **N4 tab bar**: FlatList last item scrollable above tab bar.
|
|
670
|
+
- [ ] **N5 large text**: no overflow or clipping at max system font size.
|
|
671
|
+
- [ ] **N6 shadow**: card depth consistent on iOS and Android.
|
|
672
|
+
- [ ] **N7 gesture conflict**: `activeOffsetX` / `failOffsetY` on swipeable rows inside scroll views.
|
|
673
|
+
- [ ] **N8 Android ripple**: every `Pressable` has `android_ripple` or documents why not.
|
|
674
|
+
- [ ] **N9 stale screen state**: `useFocusEffect` used (not `useEffect([])`); data resets on re-visit.
|
|
675
|
+
- [ ] **N10 JS-thread animation**: `useNativeDriver: true` on all `Animated` calls.
|
|
676
|
+
- [ ] **N11 FlatList re-renders**: `renderItem` and row component memoized; logged render count ≤ visible rows.
|
|
677
|
+
- [ ] **N12 Skia / GPU**: Skia components wrapped in `React.memo`; driven by `SharedValue`, not state.
|
|
678
|
+
- [ ] **N13 double safe-area inset**: wrapper chain has insets applied exactly once per edge.
|
|
679
|
+
- [ ] **N14 deep link stack depth**: back after cold-start deep link lands on correct parent screen.
|
|
680
|
+
- [ ] **N15 tab-switch animation**: no JS entrance animation on the screen root competing with native stack transition.
|
|
681
|
+
|
|
682
|
+
### Repo health
|
|
683
|
+
|
|
684
|
+
- [ ] Lint and typecheck pass for all touched files.
|
|
685
|
+
- [ ] Three-config screenshots show no clipping, no overlap, no wrapped labels.
|
|
686
|
+
- [ ] Write-up table maps each pain → heuristic → primitive → file → after behaviour.
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## When Not To Use This Skill
|
|
691
|
+
|
|
692
|
+
- **Web / PWA surface (Next.js, browser)** → use `enhance-web-ui` + `enhance-web-ux`.
|
|
693
|
+
- **Hybrid web app (Capacitor, Ionic, RN-Web)** with web and native surfaces → use
|
|
694
|
+
`enhance-capacitor-ui` first (separates the axis architecture), then this skill
|
|
695
|
+
for the native-specific polish.
|
|
696
|
+
- **Pure UX audit (no code changes)** → use `audit-ux` or `audit-uiux-design-system`.
|
|
697
|
+
- **Brand-new screen from scratch** → use `design-frontend` for the web equivalent;
|
|
698
|
+
there is no dedicated "native greenfield" skill — start from the repo's `HubScreen`
|
|
699
|
+
/ `ScreenContainer` primitive and the design tokens.
|
|
700
|
+
- **Single-line bug fix** (e.g. wrong colour on one element) → just fix it.
|
|
701
|
+
- **Performance profiling and optimisation** beyond the basics in N10–N12 →
|
|
702
|
+
use Flipper / Android Studio Profiler / Xcode Instruments directly.
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
## Companion Skills
|
|
707
|
+
|
|
708
|
+
- [`mobile-emulator-start`](../../mobile-emulator-start/SKILL.md) — bring up the Android emulator
|
|
709
|
+
dev loop cleanly; run this first before taking screenshots.
|
|
710
|
+
- [`mobile-emulator-test`](../../mobile-emulator-test/SKILL.md) — full QA walk on the emulator;
|
|
711
|
+
use after this skill to verify the enhanced screen end-to-end.
|
|
712
|
+
- [`enhance-web-ui`](../../enhance-web-ui/SKILL.md) — web companion for the same
|
|
713
|
+
composition and colour-tier principles. The H1–H16 hidden failure modes there
|
|
714
|
+
catalogue *web-specific* bugs; many of the underlying principles (active-state
|
|
715
|
+
mass, brand-color budget, monochromatic tier mismatch, information duplication,
|
|
716
|
+
left-anchored stacks) apply to RN with adapted detection probes.
|
|
717
|
+
- [`enhance-web-ux`](../../enhance-web-ux/SKILL.md) — web companion for heuristic-
|
|
718
|
+
driven task-flow improvements. The NN/g heuristic framework and content-rank
|
|
719
|
+
methodology apply identically to RN screens.
|
|
720
|
+
- [`enhance-capacitor-ui`](../../enhance-capacitor-ui/SKILL.md) — prerequisite for
|
|
721
|
+
hybrid apps (Capacitor / Ionic / RN-Web) that ship both a web PWA and a native
|
|
722
|
+
shell from the same codebase. Establishes the three-axis architecture (form
|
|
723
|
+
factor / platform / pointer) before per-surface polish.
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
## Research Anchors
|
|
728
|
+
|
|
729
|
+
- **Apple Human Interface Guidelines — Layout and Adaptivity**:
|
|
730
|
+
44×44pt minimum touch target, safe-area handling, Dynamic Island insets,
|
|
731
|
+
adaptive layout patterns for iPhone and iPad.
|
|
732
|
+
<https://developer.apple.com/design/human-interface-guidelines/layout>
|
|
733
|
+
- **Apple Human Interface Guidelines — Accessibility**:
|
|
734
|
+
`accessibilityLabel`, `accessibilityRole`, Dynamic Type, Reduce Motion,
|
|
735
|
+
minimum legibility sizes.
|
|
736
|
+
<https://developer.apple.com/design/human-interface-guidelines/accessibility>
|
|
737
|
+
- **Material Design 3 — NavigationBar spec**: active indicator is a 64×32
|
|
738
|
+
`cornerFull` pill in `SecondaryContainer` colour wrapping the icon — never
|
|
739
|
+
a full-cell fill. Reference for N-S13 active-state mass.
|
|
740
|
+
<https://m3.material.io/components/navigation-bar/overview>
|
|
741
|
+
- **Material Design 3 — Touch Targets**: 48×48dp minimum for all interactive
|
|
742
|
+
controls; use a 48×48dp `box` with padding to achieve target size without
|
|
743
|
+
changing visual size.
|
|
744
|
+
<https://m3.material.io/foundations/interaction/states/overview>
|
|
745
|
+
- **React Native — Reanimated 3 — useReducedMotion**:
|
|
746
|
+
`useReducedMotion()` hook returns `true` when the user has enabled Reduce
|
|
747
|
+
Motion; animations should set `duration: 0` or skip entirely.
|
|
748
|
+
<https://docs.swmansion.com/react-native-reanimated/docs/device/useReducedMotion>
|
|
749
|
+
- **React Native — New Architecture (Fabric) — Layout**:
|
|
750
|
+
`useSafeAreaInsets()` via `react-native-safe-area-context` is the correct
|
|
751
|
+
way to consume safe-area data in Fabric; direct `SafeAreaView` can cause
|
|
752
|
+
double-inset in nested navigators.
|
|
753
|
+
<https://reactnative.dev/docs/the-new-architecture/landing-page>
|
|
754
|
+
- **React Navigation — Linking**:
|
|
755
|
+
Deep-link config must mirror the full nested navigator shape so cold-start
|
|
756
|
+
links initialise the correct parent stack.
|
|
757
|
+
<https://reactnavigation.org/docs/deep-linking>
|
|
758
|
+
- **React Navigation — `useFocusEffect`**:
|
|
759
|
+
Runs on every screen focus (not just mount). Use for data re-fetching,
|
|
760
|
+
form resets, and analytics events that must fire on every visit.
|
|
761
|
+
<https://reactnavigation.org/docs/use-focus-effect>
|
|
762
|
+
- **Shopify — FlashList**:
|
|
763
|
+
`RecyclerListView`-based replacement for `FlatList` with significantly
|
|
764
|
+
reduced re-render count and memory footprint for large lists.
|
|
765
|
+
<https://shopify.github.io/flash-list/>
|
|
766
|
+
- **Shopify — react-native-skia**:
|
|
767
|
+
Skia-based drawing surface that runs on both the JS thread and a dedicated
|
|
768
|
+
GPU thread. Drive parameters via Reanimated `SharedValue` to avoid React
|
|
769
|
+
re-renders. Reference for N12.
|
|
770
|
+
<https://shopify.github.io/react-native-skia/>
|
|
771
|
+
- **NN/g — Visual Hierarchy in UX**:
|
|
772
|
+
Contrast, scale, grouping, and whitespace guide focus; squint with 5–10 px
|
|
773
|
+
blur to verify intended hierarchy. Platform-agnostic; applies to native
|
|
774
|
+
screens exactly as to web pages.
|
|
775
|
+
<https://www.nngroup.com/articles/visual-hierarchy-ux-definition/>
|
|
776
|
+
- **NN/g — Recognition over Recall (#6)**:
|
|
777
|
+
Users should not need to remember information — category colour, icon
|
|
778
|
+
shape, and position should signal meaning without reading labels.
|
|
779
|
+
Primary reason for tier-A vibrant palettes on learning apps.
|
|
780
|
+
<https://www.nngroup.com/articles/ten-usability-heuristics/>
|
|
781
|
+
- **Duolingo — Core Tabs Redesign + Gamification as Design Language**:
|
|
782
|
+
Reference for tier-A vibrant semantic palettes (green=correct, red=hearts,
|
|
783
|
+
orange=streak). Each colour carries system meaning; muted palettes undermine
|
|
784
|
+
the gamification signal. Canonical tier-A reference for §1.5.
|
|
785
|
+
<https://blog.duolingo.com/core-tabs-redesign/>
|