@devo-bmad-custom/agent-orchestration 1.0.2 → 1.0.4
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/lib/installer.js +33 -0
- package/package.json +1 -1
- package/src/.agents/skills/audit-website/README.md +20 -20
- package/src/.agents/skills/audit-website/SKILL.md +470 -470
- package/src/.agents/skills/audit-website/agents/openai.yaml +6 -6
- package/src/.agents/skills/audit-website/assets/icon-small.svg +41 -41
- package/src/.agents/skills/audit-website/references/OUTPUT-FORMAT.md +250 -250
- package/src/.agents/skills/clean-code-standards/SKILL.md +104 -104
- package/src/.agents/skills/excalidraw-dark-standard/SKILL.md +281 -281
- package/src/.agents/skills/frontend-responsive-design-standards/SKILL.md +434 -434
- package/src/.agents/skills/java-fundamentals/SKILL.md +116 -116
- package/src/.agents/skills/java-performance/SKILL.md +119 -119
- package/src/.agents/skills/next-best-practices/SKILL.md +153 -153
- package/src/.agents/skills/next-best-practices/async-patterns.md +87 -87
- package/src/.agents/skills/next-best-practices/bundling.md +180 -180
- package/src/.agents/skills/next-best-practices/data-patterns.md +297 -297
- package/src/.agents/skills/next-best-practices/debug-tricks.md +105 -105
- package/src/.agents/skills/next-best-practices/directives.md +73 -73
- package/src/.agents/skills/next-best-practices/error-handling.md +227 -227
- package/src/.agents/skills/next-best-practices/file-conventions.md +140 -140
- package/src/.agents/skills/next-best-practices/font.md +245 -245
- package/src/.agents/skills/next-best-practices/functions.md +108 -108
- package/src/.agents/skills/next-best-practices/hydration-error.md +91 -91
- package/src/.agents/skills/next-best-practices/image.md +173 -173
- package/src/.agents/skills/next-best-practices/metadata.md +301 -301
- package/src/.agents/skills/next-best-practices/parallel-routes.md +287 -287
- package/src/.agents/skills/next-best-practices/route-handlers.md +146 -146
- package/src/.agents/skills/next-best-practices/rsc-boundaries.md +159 -159
- package/src/.agents/skills/next-best-practices/runtime-selection.md +39 -39
- package/src/.agents/skills/next-best-practices/scripts.md +141 -141
- package/src/.agents/skills/next-best-practices/self-hosting.md +371 -371
- package/src/.agents/skills/next-best-practices/suspense-boundaries.md +67 -67
- package/src/.agents/skills/nextjs-app-router-patterns/SKILL.md +537 -537
- package/src/.agents/skills/postgresql-optimization/SKILL.md +404 -404
- package/src/.agents/skills/python-backend/SKILL.md +153 -153
- package/src/.agents/skills/python-fundamentals/SKILL.md +234 -234
- package/src/.agents/skills/python-performance/SKILL.md +404 -404
- package/src/.agents/skills/react-expert/SKILL.md +335 -335
- package/src/.agents/skills/redis-best-practices/SKILL.md +438 -438
- package/src/.agents/skills/security-best-practices/SKILL.md +288 -288
- package/src/.agents/skills/security-review/LICENSE +22 -22
- package/src/.agents/skills/security-review/SKILL.md +312 -312
- package/src/.agents/skills/security-review/infrastructure/docker.md +432 -432
- package/src/.agents/skills/security-review/languages/javascript.md +388 -388
- package/src/.agents/skills/security-review/languages/python.md +363 -363
- package/src/.agents/skills/security-review/references/api-security.md +519 -519
- package/src/.agents/skills/security-review/references/authentication.md +353 -353
- package/src/.agents/skills/security-review/references/authorization.md +372 -372
- package/src/.agents/skills/security-review/references/business-logic.md +443 -443
- package/src/.agents/skills/security-review/references/cryptography.md +329 -329
- package/src/.agents/skills/security-review/references/csrf.md +398 -398
- package/src/.agents/skills/security-review/references/data-protection.md +378 -378
- package/src/.agents/skills/security-review/references/deserialization.md +410 -410
- package/src/.agents/skills/security-review/references/error-handling.md +436 -436
- package/src/.agents/skills/security-review/references/file-security.md +457 -457
- package/src/.agents/skills/security-review/references/injection.md +259 -259
- package/src/.agents/skills/security-review/references/logging.md +433 -433
- package/src/.agents/skills/security-review/references/misconfiguration.md +435 -435
- package/src/.agents/skills/security-review/references/modern-threats.md +475 -475
- package/src/.agents/skills/security-review/references/ssrf.md +415 -415
- package/src/.agents/skills/security-review/references/supply-chain.md +405 -405
- package/src/.agents/skills/security-review/references/xss.md +336 -336
- package/src/.agents/skills/subagent-driven-development/SKILL.md +275 -275
- package/src/.agents/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -26
- package/src/.agents/skills/subagent-driven-development/implementer-prompt.md +113 -113
- package/src/.agents/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -61
- package/src/.agents/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/src/.agents/skills/systematic-debugging/SKILL.md +296 -296
- package/src/.agents/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/src/.agents/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/src/.agents/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/src/.agents/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/src/.agents/skills/systematic-debugging/test-academic.md +14 -14
- package/src/.agents/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/src/.agents/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/src/.agents/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/src/.agents/skills/typescript-best-practices/SKILL.md +373 -373
- package/src/.agents/skills/ui-ux-pro-custom/SKILL.md +348 -348
- package/src/.agents/skills/ui-ux-pro-custom/data/charts.csv +26 -26
- package/src/.agents/skills/ui-ux-pro-custom/data/colors.csv +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/icons.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/SKILL.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/accessibility.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/animation.md +466 -466
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/composition-locals.md +231 -231
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/deprecated-patterns.md +323 -323
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/lists-scrolling.md +400 -400
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/modifiers.md +331 -331
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/navigation.md +416 -416
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/performance.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/side-effects.md +516 -516
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/foundation-source.md +13327 -13327
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/material3-source.md +19097 -19097
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/navigation-source.md +2947 -2947
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/runtime-source.md +11316 -11316
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/ui-source.md +7896 -7896
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/state-management.md +377 -377
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/styles-experimental.md +470 -470
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/theming-material3.md +349 -349
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/view-composition.md +595 -595
- package/src/.agents/skills/ui-ux-pro-custom/data/landing.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/data/mobile-ui-layout.md +654 -654
- package/src/.agents/skills/ui-ux-pro-custom/data/products.csv +96 -96
- package/src/.agents/skills/ui-ux-pro-custom/data/react-performance.csv +45 -45
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/astro.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/flutter.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/html-tailwind.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/jetpack-compose.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nextjs.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxt-ui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxtjs.csv +59 -59
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react-native.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/shadcn.csv +61 -61
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/svelte.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/swiftui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/vue.csv +50 -50
- package/src/.agents/skills/ui-ux-pro-custom/data/styles.csv +68 -68
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/SKILL.md +438 -438
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/references/alarmkit-patterns.md +584 -584
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-clips/SKILL.md +436 -436
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/SKILL.md +489 -489
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/references/appintents-advanced.md +1076 -1076
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/SKILL.md +340 -340
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/privacy-manifest.md +90 -90
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/review-checklists.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-conversion.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-optimization.md +344 -344
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/foundation-models.md +508 -508
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/mlx-swift.md +285 -285
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/references/keychain-biometric.md +211 -211
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/SKILL.md +499 -499
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/references/background-task-patterns.md +390 -390
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/SKILL.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/references/callkit-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/SKILL.md +492 -492
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/references/cloudkit-patterns.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/codable-patterns/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/SKILL.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/references/contacts-patterns.md +409 -409
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/references/ble-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/SKILL.md +388 -388
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/references/motion-patterns.md +405 -405
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/SKILL.md +495 -495
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/references/nfc-patterns.md +420 -420
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/SKILL.md +459 -459
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/references/coreml-swift-integration.md +765 -765
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/SKILL.md +422 -422
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/instruments-guide.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/lldb-patterns.md +298 -298
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/device-integrity/SKILL.md +477 -477
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/SKILL.md +460 -460
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/references/energykit-patterns.md +541 -541
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/references/eventkit-patterns.md +326 -326
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/references/healthkit-patterns.md +602 -602
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/references/matter-commissioning.md +455 -455
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/SKILL.md +301 -301
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/references/a11y-patterns.md +140 -140
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/SKILL.md +418 -418
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/formatstyle-locale.md +627 -627
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/string-catalogs.md +462 -462
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/SKILL.md +441 -441
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/background-websocket.md +862 -862
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/lightweight-clients.md +93 -93
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/network-framework.md +563 -563
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/urlsession-patterns.md +1116 -1116
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/app-review-guidelines.md +174 -174
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/cryptokit-advanced.md +296 -296
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/file-storage-patterns.md +354 -354
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/privacy-manifest.md +117 -117
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/references/live-activity-patterns.md +868 -868
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/corelocation-patterns.md +730 -730
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/mapkit-patterns.md +748 -748
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/metrickit-diagnostics/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/SKILL.md +395 -395
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/references/musickit-patterns.md +363 -363
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/SKILL.md +412 -412
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/references/translation-patterns.md +311 -311
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/SKILL.md +398 -398
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/references/wallet-passes.md +254 -254
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/SKILL.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/paperkit-integration.md +376 -376
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/pencilkit-patterns.md +302 -302
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/SKILL.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/references/permissionkit-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/av-playback.md +701 -701
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/camera-capture.md +774 -774
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/image-loading-caching.md +869 -869
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/photospicker-patterns.md +597 -597
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/notification-patterns.md +677 -677
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/rich-notifications.md +745 -745
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/references/realitykit-patterns.md +480 -480
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/references/shareplay-patterns.md +544 -544
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/speech-recognition/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/SKILL.md +478 -478
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/app-review-guidelines.md +58 -58
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/storekit-advanced.md +755 -755
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/SKILL.md +487 -487
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/references/charts-patterns.md +895 -895
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/SKILL.md +408 -408
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/approachable-concurrency.md +80 -80
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swift-6-2-concurrency.md +233 -233
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swiftui-concurrency.md +187 -187
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/synchronization-primitives.md +341 -341
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/references/swift-patterns-extended.md +505 -505
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/references/testing-patterns.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/SKILL.md +334 -334
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/core-data-coexistence.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-advanced.md +975 -975
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-queries.md +675 -675
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/SKILL.md +481 -481
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/animation-advanced.md +804 -804
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/core-animation-bridge.md +553 -553
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/SKILL.md +450 -450
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/references/gesture-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/SKILL.md +336 -336
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/form.md +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/grids.md +69 -69
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/list.md +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/scrollview.md +147 -147
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/SKILL.md +325 -325
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/references/liquid-glass.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/SKILL.md +262 -262
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/deeplinks.md +207 -207
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/navigationstack.md +177 -177
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/sheets.md +169 -169
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/tabview.md +178 -178
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/SKILL.md +381 -381
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/architecture-patterns.md +486 -486
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/deprecated-migration.md +1097 -1097
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/design-polish.md +780 -780
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/platform-and-sharing.md +696 -696
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +46 -46
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +29 -29
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-hangs-in-your-app.md +33 -33
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-improving-swiftui-performance.md +52 -52
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/SKILL.md +428 -428
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/hosting-migration.md +534 -534
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/representable-recipes.md +1133 -1133
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/SKILL.md +494 -494
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/references/tipkit-patterns.md +782 -782
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -736
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -738
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -410
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -567
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -497
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -871
- package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +57 -57
- package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -253
- package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -1067
- package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -114
- package/src/.agents/skills/ux-audit/SKILL.md +150 -150
- package/src/.agents/skills/websocket-engineer/SKILL.md +168 -168
- package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -391
- package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -400
- package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -195
- package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -333
- package/src/.agents/skills/websocket-engineer/references/security.md +474 -474
- package/src/.agents/skills/writing-skills/SKILL.md +655 -655
- package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -1150
- package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
- package/src/.agents/skills/writing-skills/graphviz-conventions.dot +171 -171
- package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -187
- package/src/.agents/skills/writing-skills/render-graphs.js +168 -168
- package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -384
- package/src/.claude/commands/master-orchestrator.md +15 -0
- package/src/_memory/config.yaml +11 -11
- package/src/_memory/master-orchestrator-sidecar/instructions.md +97 -47
- package/src/_memory/skills/nimbalyst-tracking/SKILL.md +103 -103
- package/src/_memory/skills/writing-skills/SKILL.md +655 -655
- package/src/bmb/agents/agent-builder.md +59 -59
- package/src/bmb/agents/module-builder.md +60 -60
- package/src/bmb/agents/workflow-builder.md +61 -61
- package/src/bmb/config.yaml +12 -12
- package/src/bmb/module-help.csv +13 -13
- package/src/bmb/workflows/agent/data/agent-architecture.md +258 -258
- package/src/bmb/workflows/agent/data/agent-compilation.md +185 -185
- package/src/bmb/workflows/agent/data/agent-menu-patterns.md +189 -189
- package/src/bmb/workflows/agent/data/agent-metadata.md +133 -133
- package/src/bmb/workflows/agent/data/agent-validation.md +111 -111
- package/src/bmb/workflows/agent/data/brainstorm-context.md +96 -96
- package/src/bmb/workflows/agent/data/communication-presets.csv +61 -61
- package/src/bmb/workflows/agent/data/critical-actions.md +75 -75
- package/src/bmb/workflows/agent/data/persona-properties.md +252 -252
- package/src/bmb/workflows/agent/data/principles-crafting.md +142 -142
- package/src/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -68
- package/src/bmb/workflows/agent/data/reference/with-sidecar/journal-keeper/journal-keeper-sidecar/entries/yy-mm-dd-entry-template.md +16 -16
- package/src/bmb/workflows/agent/data/understanding-agent-types.md +126 -126
- package/src/bmb/workflows/agent/steps-c/step-01-brainstorm.md +129 -129
- package/src/bmb/workflows/agent/steps-c/step-02-discovery.md +170 -170
- package/src/bmb/workflows/agent/steps-c/step-03-sidecar-metadata.md +309 -309
- package/src/bmb/workflows/agent/steps-c/step-04-persona.md +213 -213
- package/src/bmb/workflows/agent/steps-c/step-05-commands-menu.md +179 -179
- package/src/bmb/workflows/agent/steps-c/step-06-activation.md +278 -278
- package/src/bmb/workflows/agent/steps-c/step-07-build-agent.md +316 -316
- package/src/bmb/workflows/agent/steps-c/step-08-celebrate.md +247 -247
- package/src/bmb/workflows/agent/steps-e/e-01-load-existing.md +221 -221
- package/src/bmb/workflows/agent/steps-e/e-02-discover-edits.md +195 -195
- package/src/bmb/workflows/agent/steps-e/e-04-sidecar-metadata.md +126 -126
- package/src/bmb/workflows/agent/steps-e/e-05-persona.md +135 -135
- package/src/bmb/workflows/agent/steps-e/e-06-commands-menu.md +123 -123
- package/src/bmb/workflows/agent/steps-e/e-07-activation.md +124 -124
- package/src/bmb/workflows/agent/steps-e/e-08-edit-agent.md +197 -197
- package/src/bmb/workflows/agent/steps-e/e-09-celebrate.md +155 -155
- package/src/bmb/workflows/agent/steps-v/v-01-load-review.md +137 -137
- package/src/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +116 -116
- package/src/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +124 -124
- package/src/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +127 -127
- package/src/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-03-summary.md +104 -104
- package/src/bmb/workflows/agent/templates/agent-plan.template.md +5 -5
- package/src/bmb/workflows/agent/templates/agent-template.md +89 -89
- package/src/bmb/workflows/agent/workflow-create-agent.md +72 -72
- package/src/bmb/workflows/agent/workflow-edit-agent.md +75 -75
- package/src/bmb/workflows/agent/workflow-validate-agent.md +73 -73
- package/src/bmb/workflows/module/data/agent-architecture.md +179 -179
- package/src/bmb/workflows/module/data/agent-spec-template.md +79 -79
- package/src/bmb/workflows/module/data/module-standards.md +263 -263
- package/src/bmb/workflows/module/data/module-yaml-conventions.md +392 -392
- package/src/bmb/workflows/module/module-help-generate.md +254 -254
- package/src/bmb/workflows/module/steps-b/step-01-welcome.md +148 -148
- package/src/bmb/workflows/module/steps-b/step-02-spark.md +141 -141
- package/src/bmb/workflows/module/steps-b/step-03-module-type.md +149 -149
- package/src/bmb/workflows/module/steps-b/step-04-vision.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-05-identity.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-06-users.md +86 -86
- package/src/bmb/workflows/module/steps-b/step-07-value.md +76 -76
- package/src/bmb/workflows/module/steps-b/step-08-agents.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-09-workflows.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-10-tools.md +91 -91
- package/src/bmb/workflows/module/steps-b/step-11-scenarios.md +84 -84
- package/src/bmb/workflows/module/steps-b/step-12-creative.md +95 -95
- package/src/bmb/workflows/module/steps-b/step-13-review.md +105 -105
- package/src/bmb/workflows/module/steps-b/step-14-finalize.md +117 -117
- package/src/bmb/workflows/module/steps-c/step-01-load-brief.md +179 -179
- package/src/bmb/workflows/module/steps-c/step-01b-continue.md +82 -82
- package/src/bmb/workflows/module/steps-c/step-02-structure.md +105 -105
- package/src/bmb/workflows/module/steps-c/step-03-config.md +119 -119
- package/src/bmb/workflows/module/steps-c/step-04-agents.md +168 -168
- package/src/bmb/workflows/module/steps-c/step-05-workflows.md +184 -184
- package/src/bmb/workflows/module/steps-c/step-06-docs.md +401 -401
- package/src/bmb/workflows/module/steps-c/step-07-complete.md +152 -152
- package/src/bmb/workflows/module/steps-e/step-01-load-target.md +81 -81
- package/src/bmb/workflows/module/steps-e/step-02-select-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-03-apply-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-04-review.md +80 -80
- package/src/bmb/workflows/module/steps-e/step-05-confirm.md +75 -75
- package/src/bmb/workflows/module/steps-v/step-01-load-target.md +96 -96
- package/src/bmb/workflows/module/steps-v/step-02-file-structure.md +93 -93
- package/src/bmb/workflows/module/steps-v/step-03-module-yaml.md +99 -99
- package/src/bmb/workflows/module/steps-v/step-04-agent-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-05-workflow-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-06-documentation.md +143 -143
- package/src/bmb/workflows/module/steps-v/step-07-installation.md +102 -102
- package/src/bmb/workflows/module/steps-v/step-08-report.md +197 -197
- package/src/bmb/workflows/module/templates/brief-template.md +154 -154
- package/src/bmb/workflows/module/templates/workflow-spec-template.md +96 -96
- package/src/bmb/workflows/module/workflow-create-module-brief.md +71 -71
- package/src/bmb/workflows/module/workflow-create-module.md +86 -86
- package/src/bmb/workflows/module/workflow-edit-module.md +66 -66
- package/src/bmb/workflows/module/workflow-validate-module.md +66 -66
- package/src/bmb/workflows/workflow/data/architecture.md +150 -150
- package/src/bmb/workflows/workflow/data/common-workflow-tools.csv +19 -19
- package/src/bmb/workflows/workflow/data/csv-data-file-standards.md +53 -53
- package/src/bmb/workflows/workflow/data/frontmatter-standards.md +184 -184
- package/src/bmb/workflows/workflow/data/input-discovery-standards.md +191 -191
- package/src/bmb/workflows/workflow/data/intent-vs-prescriptive-spectrum.md +44 -44
- package/src/bmb/workflows/workflow/data/menu-handling-standards.md +133 -133
- package/src/bmb/workflows/workflow/data/output-format-standards.md +135 -135
- package/src/bmb/workflows/workflow/data/step-file-rules.md +235 -235
- package/src/bmb/workflows/workflow/data/step-type-patterns.md +257 -257
- package/src/bmb/workflows/workflow/data/subprocess-optimization-patterns.md +188 -188
- package/src/bmb/workflows/workflow/data/trimodal-workflow-structure.md +164 -164
- package/src/bmb/workflows/workflow/data/workflow-chaining-standards.md +222 -222
- package/src/bmb/workflows/workflow/data/workflow-examples.md +232 -232
- package/src/bmb/workflows/workflow/data/workflow-type-criteria.md +134 -134
- package/src/bmb/workflows/workflow/steps-c/step-00-conversion.md +263 -263
- package/src/bmb/workflows/workflow/steps-c/step-01-discovery.md +194 -194
- package/src/bmb/workflows/workflow/steps-c/step-01b-continuation.md +3 -3
- package/src/bmb/workflows/workflow/steps-c/step-02-classification.md +270 -270
- package/src/bmb/workflows/workflow/steps-c/step-03-requirements.md +283 -283
- package/src/bmb/workflows/workflow/steps-c/step-04-tools.md +282 -282
- package/src/bmb/workflows/workflow/steps-c/step-05-plan-review.md +243 -243
- package/src/bmb/workflows/workflow/steps-c/step-06-design.md +330 -330
- package/src/bmb/workflows/workflow/steps-c/step-07-foundation.md +239 -239
- package/src/bmb/workflows/workflow/steps-c/step-08-build-step-01.md +379 -379
- package/src/bmb/workflows/workflow/steps-c/step-09-build-next-step.md +350 -350
- package/src/bmb/workflows/workflow/steps-c/step-10-confirmation.md +322 -322
- package/src/bmb/workflows/workflow/steps-c/step-11-completion.md +191 -191
- package/src/bmb/workflows/workflow/steps-e/step-e-01-assess-workflow.md +237 -237
- package/src/bmb/workflows/workflow/steps-e/step-e-02-discover-edits.md +251 -251
- package/src/bmb/workflows/workflow/steps-e/step-e-03-fix-validation.md +254 -254
- package/src/bmb/workflows/workflow/steps-e/step-e-04-direct-edit.md +277 -277
- package/src/bmb/workflows/workflow/steps-e/step-e-05-apply-edit.md +154 -154
- package/src/bmb/workflows/workflow/steps-e/step-e-06-validate-after.md +190 -190
- package/src/bmb/workflows/workflow/steps-e/step-e-07-complete.md +206 -206
- package/src/bmb/workflows/workflow/steps-v/step-01-validate-max-mode.md +109 -109
- package/src/bmb/workflows/workflow/steps-v/step-01-validate.md +221 -221
- package/src/bmb/workflows/workflow/steps-v/step-01b-structure.md +152 -152
- package/src/bmb/workflows/workflow/steps-v/step-02-frontmatter-validation.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-02b-path-violations.md +265 -265
- package/src/bmb/workflows/workflow/steps-v/step-03-menu-validation.md +164 -164
- package/src/bmb/workflows/workflow/steps-v/step-04-step-type-validation.md +211 -211
- package/src/bmb/workflows/workflow/steps-v/step-05-output-format-validation.md +200 -200
- package/src/bmb/workflows/workflow/steps-v/step-06-validation-design-check.md +195 -195
- package/src/bmb/workflows/workflow/steps-v/step-07-instruction-style-check.md +209 -209
- package/src/bmb/workflows/workflow/steps-v/step-08-collaborative-experience-check.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-08b-subprocess-optimization.md +179 -179
- package/src/bmb/workflows/workflow/steps-v/step-09-cohesive-review.md +186 -186
- package/src/bmb/workflows/workflow/steps-v/step-10-report-complete.md +154 -154
- package/src/bmb/workflows/workflow/steps-v/step-11-plan-validation.md +237 -237
- package/src/bmb/workflows/workflow/templates/minimal-output-template.md +11 -11
- package/src/bmb/workflows/workflow/templates/step-01-init-continuable-template.md +241 -241
- package/src/bmb/workflows/workflow/templates/step-1b-template.md +224 -224
- package/src/bmb/workflows/workflow/templates/step-template.md +294 -294
- package/src/bmb/workflows/workflow/templates/workflow-template.md +102 -102
- package/src/bmb/workflows/workflow/workflow-create-workflow.md +79 -79
- package/src/bmb/workflows/workflow/workflow-edit-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-rework-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-validate-max-parallel-workflow.md +66 -66
- package/src/bmb/workflows/workflow/workflow-validate-workflow.md +65 -65
- package/src/bmm/agents/analyst.md +104 -104
- package/src/bmm/agents/dev.md +100 -100
- package/src/bmm/agents/qa.md +100 -90
- package/src/bmm/agents/tech-writer/tech-writer.md +94 -94
- package/src/bmm/module-help.csv +31 -31
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +115 -115
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +107 -107
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +141 -141
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +144 -144
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +147 -147
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +161 -161
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +99 -99
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -57
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +156 -156
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +165 -165
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +140 -140
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +152 -152
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +345 -345
- package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +92 -92
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +164 -164
- package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +174 -174
- package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +184 -184
- package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +105 -105
- package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +360 -360
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +165 -165
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +174 -174
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +141 -141
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +159 -159
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +387 -387
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -54
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +100 -100
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +160 -160
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +88 -88
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +99 -99
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +169 -169
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +156 -156
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +176 -176
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +184 -184
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +174 -174
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +175 -175
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +189 -189
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +162 -162
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +79 -79
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +183 -183
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +149 -149
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +187 -187
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +108 -108
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +166 -166
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +131 -131
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +150 -150
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +155 -155
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +170 -170
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +158 -158
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +147 -147
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +182 -182
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +202 -202
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +148 -148
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +201 -201
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +179 -179
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +164 -164
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +106 -106
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +111 -111
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +115 -115
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +127 -127
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +167 -167
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +143 -143
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +154 -154
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +165 -165
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +135 -135
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +101 -101
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +45 -45
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +185 -185
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +130 -130
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +93 -93
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +196 -196
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -54
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +82 -82
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +106 -106
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +138 -138
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +129 -129
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +166 -166
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +186 -186
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +163 -163
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +38 -38
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -49
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +124 -124
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +122 -122
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +84 -84
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -58
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -43
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -53
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +159 -159
- package/src/bmm/workflows/4-implementation/create-story/template.md +79 -79
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -20
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -25
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +158 -158
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +122 -122
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +87 -87
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -146
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -50
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +152 -152
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +123 -123
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -201
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -79
- package/src/bmm/workflows/document-project/workflow.yaml +22 -22
- package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -184
- package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +322 -322
- package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +235 -235
- package/src/bmm/workflows/generate-project-context/workflow.md +49 -49
- package/src/bmm/workflows/qa/automate/workflow.yaml +233 -233
- package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -42
- package/src/core/config.yaml +9 -9
- package/src/core/module-help.csv +10 -10
- package/src/core/scripts/generate-loop-report.py +72 -72
- package/src/core/tasks/editorial-review-prose.xml +101 -101
- package/src/core/tasks/editorial-review-structure.xml +207 -207
- package/src/core/tasks/help.md +86 -86
- package/src/core/tasks/index-docs.xml +64 -64
- package/src/core/tasks/review-adversarial-general.xml +66 -66
- package/src/core/tasks/review-adversarial-loop.xml +46 -46
- package/src/core/tasks/review-edge-case-hunter.xml +63 -63
- package/src/core/tasks/review-party-loop.xml +46 -46
- package/src/core/tasks/shard-doc.xml +107 -107
- package/src/core/tasks/workflow.xml +235 -235
- package/src/core/templates/review-loop-report.html +88 -88
- package/src/core/templates/review-loop-report.md +5 -5
- package/src/core/workflows/advanced-elicitation/workflow.xml +117 -117
- package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +212 -212
- package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -122
- package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -225
- package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -237
- package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -209
- package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -264
- package/src/core/workflows/brainstorming/steps/step-02e-deep-dive.md +68 -68
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +403 -403
- package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -303
- package/src/core/workflows/brainstorming/workflow.md +60 -60
- package/src/core/workflows/extract-trackers/workflow.md +45 -45
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +142 -142
- package/src/core/workflows/party-mode/workflow.md +194 -194
- package/src/docs/dev/tmux/actions_popup.py +291 -291
- package/src/docs/dev/tmux/tmux-setup.md +62 -1
|
@@ -1,654 +1,654 @@
|
|
|
1
|
-
# mobile-ui-layout
|
|
2
|
-
|
|
3
|
-
**description:** Generate production-grade mobile-first React/Next.js UI components and page layouts. Use this skill when the user asks to build mobile web pages, app-like interfaces, bottom sheets, tab bars, cards, forms, modals, or any touch-optimized component.
|
|
4
|
-
|
|
5
|
-
## Role
|
|
6
|
-
|
|
7
|
-
You are an expert mobile UI engineer specializing in React, Next.js (App Router), TypeScript, and Tailwind CSS v4. You produce production-ready, accessible, touch-optimized mobile-first components that feel native on iOS and Android browsers.
|
|
8
|
-
|
|
9
|
-
## Before Writing Any Code
|
|
10
|
-
|
|
11
|
-
Identify the layout pattern — is this a full page, a component, a modal/sheet, or a navigation element?
|
|
12
|
-
|
|
13
|
-
Pick an aesthetic direction — state it in 2-3 bullets (e.g., "clean minimal with generous whitespace, rounded corners, subtle shadows").
|
|
14
|
-
|
|
15
|
-
Define the spacing + type scale — use a consistent Tailwind scale (e.g., space-y-4, text-base/text-lg/text-2xl).
|
|
16
|
-
|
|
17
|
-
Define interaction states — every interactive element needs hover:, focus-visible:, active:, and disabled: states.
|
|
18
|
-
|
|
19
|
-
Plan motion — subtle transitions only (transition-colors, transition-transform, scale on press). Respect motion-reduce:transition-none.
|
|
20
|
-
|
|
21
|
-
## Mobile-First Principles
|
|
22
|
-
|
|
23
|
-
All styles are written mobile-first (unprefixed = mobile). Use breakpoint prefixes to enhance for larger screens:
|
|
24
|
-
|
|
25
|
-
| Breakpoint | Prefix | Min-width | Typical device |
|
|
26
|
-
| ---------- | ------ | --------- | -------------- |
|
|
27
|
-
| Default | none | 0px | All phones |
|
|
28
|
-
| sm | sm: | 640px | Large phones |
|
|
29
|
-
| md | md: | 768px | Tablets |
|
|
30
|
-
| lg | lg: | 1024px | Laptops |
|
|
31
|
-
| xl | xl: | 1280px | Desktops |
|
|
32
|
-
|
|
33
|
-
Rule: Write the mobile layout first with no prefix. Then layer on sm:, md:, lg: overrides for progressive enhancement.
|
|
34
|
-
|
|
35
|
-
## Touch Target Requirements
|
|
36
|
-
|
|
37
|
-
All interactive elements MUST meet these minimums:
|
|
38
|
-
|
|
39
|
-
Minimum tap target: 44×44px (use min-h-[44px] min-w-[44px] or padding to reach it)
|
|
40
|
-
|
|
41
|
-
Minimum spacing between targets: 8px (gap-2 or space-x-2)
|
|
42
|
-
|
|
43
|
-
Buttons: py-3 px-6 minimum (≈48px height). Full-width on mobile (w-full), auto-width on larger screens (sm:w-auto).
|
|
44
|
-
|
|
45
|
-
Icon buttons: wrap icon in a container with p-3 to expand the hit area beyond the visual icon
|
|
46
|
-
|
|
47
|
-
List items / rows: py-4 px-4 minimum, use Pressable or `<button>` semantics for tappable rows
|
|
48
|
-
|
|
49
|
-
## Safe Area & Viewport
|
|
50
|
-
|
|
51
|
-
```tsx
|
|
52
|
-
// In your root layout.tsx or _app.tsx
|
|
53
|
-
<meta
|
|
54
|
-
name="viewport"
|
|
55
|
-
content="width=device-width, initial-scale=1, viewport-fit=cover"
|
|
56
|
-
/>
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Use `env(safe-area-inset-top)`, `env(safe-area-inset-bottom)` for notch/home-indicator padding
|
|
60
|
-
|
|
61
|
-
Apply `pb-[env(safe-area-inset-bottom)]` to fixed bottom bars
|
|
62
|
-
|
|
63
|
-
For scroll containers behind fixed headers/footers, use `scroll-padding-top` and `scroll-padding-bottom`
|
|
64
|
-
|
|
65
|
-
## Component Library
|
|
66
|
-
|
|
67
|
-
### Page Shell
|
|
68
|
-
|
|
69
|
-
Every mobile page follows this structure:
|
|
70
|
-
|
|
71
|
-
```tsx
|
|
72
|
-
interface MobilePageProps {
|
|
73
|
-
header?: React.ReactNode;
|
|
74
|
-
children: React.ReactNode;
|
|
75
|
-
footer?: React.ReactNode;
|
|
76
|
-
bottomNav?: React.ReactNode;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function MobilePage({
|
|
80
|
-
header,
|
|
81
|
-
children,
|
|
82
|
-
footer,
|
|
83
|
-
bottomNav,
|
|
84
|
-
}: MobilePageProps) {
|
|
85
|
-
return (
|
|
86
|
-
<div className="flex min-h-dvh flex-col bg-background text-foreground">
|
|
87
|
-
{header && (
|
|
88
|
-
<header className="sticky top-0 z-40 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
89
|
-
<div className="flex h-14 items-center px-4">{header}</div>
|
|
90
|
-
</header>
|
|
91
|
-
)}
|
|
92
|
-
<main className="flex-1 overflow-y-auto px-4 py-4">{children}</main>
|
|
93
|
-
{footer && (
|
|
94
|
-
<footer className="border-t px-4 py-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]">
|
|
95
|
-
{footer}
|
|
96
|
-
</footer>
|
|
97
|
-
)}
|
|
98
|
-
{bottomNav && (
|
|
99
|
-
<nav className="fixed inset-x-0 bottom-0 z-50 border-t bg-background/95 backdrop-blur pb-[env(safe-area-inset-bottom)]">
|
|
100
|
-
{bottomNav}
|
|
101
|
-
</nav>
|
|
102
|
-
)}
|
|
103
|
-
</div>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Bottom Tab Navigation
|
|
109
|
-
|
|
110
|
-
```tsx
|
|
111
|
-
"use client";
|
|
112
|
-
import { usePathname, useRouter } from "next/navigation";
|
|
113
|
-
import { Home, Search, Bell, User } from "lucide-react";
|
|
114
|
-
|
|
115
|
-
const tabs = [
|
|
116
|
-
{ href: "/", icon: Home, label: "Home" },
|
|
117
|
-
{ href: "/search", icon: Search, label: "Search" },
|
|
118
|
-
{ href: "/notifications", icon: Bell, label: "Alerts" },
|
|
119
|
-
{ href: "/profile", icon: User, label: "Profile" },
|
|
120
|
-
] as const;
|
|
121
|
-
|
|
122
|
-
export function BottomTabBar() {
|
|
123
|
-
const pathname = usePathname();
|
|
124
|
-
const router = useRouter();
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<div className="flex items-center justify-around py-2">
|
|
128
|
-
{tabs.map(({ href, icon: Icon, label }) => {
|
|
129
|
-
const active = pathname === href;
|
|
130
|
-
return (
|
|
131
|
-
<button
|
|
132
|
-
key={href}
|
|
133
|
-
onClick={() => router.push(href)}
|
|
134
|
-
className={`flex min-h-[44px] min-w-[44px] flex-col items-center justify-center gap-0.5 rounded-lg px-3 py-1 transition-colors
|
|
135
|
-
${
|
|
136
|
-
active
|
|
137
|
-
? "text-primary"
|
|
138
|
-
: "text-muted-foreground hover:text-foreground"
|
|
139
|
-
}`}
|
|
140
|
-
aria-current={active ? "page" : undefined}
|
|
141
|
-
>
|
|
142
|
-
<Icon className="h-5 w-5" strokeWidth={active ? 2.5 : 2} />
|
|
143
|
-
<span className="text-[10px] font-medium">{label}</span>
|
|
144
|
-
</button>
|
|
145
|
-
);
|
|
146
|
-
})}
|
|
147
|
-
</div>
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Card
|
|
153
|
-
|
|
154
|
-
```tsx
|
|
155
|
-
interface CardProps {
|
|
156
|
-
children: React.ReactNode;
|
|
157
|
-
onClick?: () => void;
|
|
158
|
-
className?: string;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function Card({ children, onClick, className = "" }: CardProps) {
|
|
162
|
-
const Component = onClick ? "button" : "div";
|
|
163
|
-
return (
|
|
164
|
-
<Component
|
|
165
|
-
onClick={onClick}
|
|
166
|
-
className={`w-full rounded-xl border bg-card p-4 text-left shadow-sm
|
|
167
|
-
${onClick ? "active:scale-[0.98] transition-transform" : ""}
|
|
168
|
-
${className}`}
|
|
169
|
-
>
|
|
170
|
-
{children}
|
|
171
|
-
</Component>
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function CardHeader({ children }: { children: React.ReactNode }) {
|
|
176
|
-
return (
|
|
177
|
-
<div className="mb-2 flex items-center justify-between">{children}</div>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export function CardTitle({ children }: { children: React.ReactNode }) {
|
|
182
|
-
return <h3 className="text-base font-semibold leading-tight">{children}</h3>;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export function CardDescription({ children }: { children: React.ReactNode }) {
|
|
186
|
-
return <p className="text-sm text-muted-foreground">{children}</p>;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function CardContent({ children }: { children: React.ReactNode }) {
|
|
190
|
-
return <div className="space-y-2">{children}</div>;
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Bottom Sheet / Drawer
|
|
195
|
-
|
|
196
|
-
```tsx
|
|
197
|
-
"use client";
|
|
198
|
-
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
199
|
-
|
|
200
|
-
interface BottomSheetProps {
|
|
201
|
-
open: boolean;
|
|
202
|
-
onClose: () => void;
|
|
203
|
-
children: ReactNode;
|
|
204
|
-
snapPoints?: number[]; // fractions 0-1 of viewport height
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function BottomSheet({
|
|
208
|
-
open,
|
|
209
|
-
onClose,
|
|
210
|
-
children,
|
|
211
|
-
snapPoints = [0.5, 0.9],
|
|
212
|
-
}: BottomSheetProps) {
|
|
213
|
-
const sheetRef = useRef<HTMLDivElement>(null);
|
|
214
|
-
|
|
215
|
-
useEffect(() => {
|
|
216
|
-
if (open) document.body.style.overflow = "hidden";
|
|
217
|
-
return () => {
|
|
218
|
-
document.body.style.overflow = "";
|
|
219
|
-
};
|
|
220
|
-
}, [open]);
|
|
221
|
-
|
|
222
|
-
if (!open) return null;
|
|
223
|
-
|
|
224
|
-
return (
|
|
225
|
-
<>
|
|
226
|
-
{/* Backdrop */}
|
|
227
|
-
<div
|
|
228
|
-
className="fixed inset-0 z-50 bg-black/40 transition-opacity"
|
|
229
|
-
onClick={onClose}
|
|
230
|
-
aria-hidden="true"
|
|
231
|
-
/>
|
|
232
|
-
{/* Sheet */}
|
|
233
|
-
<div
|
|
234
|
-
ref={sheetRef}
|
|
235
|
-
role="dialog"
|
|
236
|
-
aria-modal="true"
|
|
237
|
-
className="fixed inset-x-0 bottom-0 z-50 rounded-t-2xl bg-background shadow-xl animate-in slide-in-from-bottom duration-300"
|
|
238
|
-
style={{ maxHeight: `${snapPoints[snapPoints.length - 1] * 100}vh` }}
|
|
239
|
-
>
|
|
240
|
-
{/* Drag handle */}
|
|
241
|
-
<div className="flex justify-center py-3">
|
|
242
|
-
<div className="h-1.5 w-12 rounded-full bg-muted" />
|
|
243
|
-
</div>
|
|
244
|
-
<div className="overflow-y-auto px-4 pb-[max(1.5rem,env(safe-area-inset-bottom))]">
|
|
245
|
-
{children}
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
</>
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Button Variants
|
|
254
|
-
|
|
255
|
-
```tsx
|
|
256
|
-
import { forwardRef, type ButtonHTMLAttributes } from "react";
|
|
257
|
-
|
|
258
|
-
type Variant = "primary" | "secondary" | "outline" | "ghost" | "destructive";
|
|
259
|
-
type Size = "sm" | "md" | "lg";
|
|
260
|
-
|
|
261
|
-
const variantClasses: Record<Variant, string> = {
|
|
262
|
-
primary:
|
|
263
|
-
"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80",
|
|
264
|
-
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
265
|
-
outline:
|
|
266
|
-
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
267
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
268
|
-
destructive:
|
|
269
|
-
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const sizeClasses: Record<Size, string> = {
|
|
273
|
-
sm: "h-9 px-3 text-sm rounded-lg",
|
|
274
|
-
md: "h-11 px-5 text-base rounded-xl",
|
|
275
|
-
lg: "h-13 px-8 text-lg rounded-xl",
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
279
|
-
variant?: Variant;
|
|
280
|
-
size?: Size;
|
|
281
|
-
fullWidth?: boolean;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
285
|
-
(
|
|
286
|
-
{
|
|
287
|
-
variant = "primary",
|
|
288
|
-
size = "md",
|
|
289
|
-
fullWidth = true,
|
|
290
|
-
className = "",
|
|
291
|
-
children,
|
|
292
|
-
...props
|
|
293
|
-
},
|
|
294
|
-
ref,
|
|
295
|
-
) => (
|
|
296
|
-
<button
|
|
297
|
-
ref={ref}
|
|
298
|
-
className={`inline-flex items-center justify-center font-medium transition-all
|
|
299
|
-
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
|
|
300
|
-
disabled:pointer-events-none disabled:opacity-50
|
|
301
|
-
active:scale-[0.97] motion-reduce:transition-none
|
|
302
|
-
${variantClasses[variant]} ${sizeClasses[size]}
|
|
303
|
-
${fullWidth ? "w-full sm:w-auto" : ""}
|
|
304
|
-
${className}`}
|
|
305
|
-
{...props}
|
|
306
|
-
>
|
|
307
|
-
{children}
|
|
308
|
-
</button>
|
|
309
|
-
),
|
|
310
|
-
);
|
|
311
|
-
Button.displayName = "Button";
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### Input / Text Field
|
|
315
|
-
|
|
316
|
-
```tsx
|
|
317
|
-
import { forwardRef, type InputHTMLAttributes } from "react";
|
|
318
|
-
|
|
319
|
-
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
320
|
-
label: string;
|
|
321
|
-
error?: string;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
325
|
-
({ label, error, id, className = "", ...props }, ref) => {
|
|
326
|
-
const inputId = id || label.toLowerCase().replace(/\s+/g, "-");
|
|
327
|
-
return (
|
|
328
|
-
<div className="space-y-1.5">
|
|
329
|
-
<label
|
|
330
|
-
htmlFor={inputId}
|
|
331
|
-
className="block text-sm font-medium text-foreground"
|
|
332
|
-
>
|
|
333
|
-
{label}
|
|
334
|
-
</label>
|
|
335
|
-
<input
|
|
336
|
-
ref={ref}
|
|
337
|
-
id={inputId}
|
|
338
|
-
className={`w-full rounded-xl border bg-background px-4 py-3 text-base
|
|
339
|
-
placeholder:text-muted-foreground
|
|
340
|
-
focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent
|
|
341
|
-
disabled:cursor-not-allowed disabled:opacity-50
|
|
342
|
-
${error ? "border-destructive focus:ring-destructive" : "border-input"}
|
|
343
|
-
${className}`}
|
|
344
|
-
aria-invalid={!!error}
|
|
345
|
-
aria-describedby={error ? `${inputId}-error` : undefined}
|
|
346
|
-
{...props}
|
|
347
|
-
/>
|
|
348
|
-
{error && (
|
|
349
|
-
<p
|
|
350
|
-
id={`${inputId}-error`}
|
|
351
|
-
className="text-sm text-destructive"
|
|
352
|
-
role="alert"
|
|
353
|
-
>
|
|
354
|
-
{error}
|
|
355
|
-
</p>
|
|
356
|
-
)}
|
|
357
|
-
</div>
|
|
358
|
-
);
|
|
359
|
-
},
|
|
360
|
-
);
|
|
361
|
-
Input.displayName = "Input";
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### List / Feed Item
|
|
365
|
-
|
|
366
|
-
```tsx
|
|
367
|
-
interface ListItemProps {
|
|
368
|
-
leading?: React.ReactNode;
|
|
369
|
-
title: string;
|
|
370
|
-
subtitle?: string;
|
|
371
|
-
trailing?: React.ReactNode;
|
|
372
|
-
onClick?: () => void;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export function ListItem({
|
|
376
|
-
leading,
|
|
377
|
-
title,
|
|
378
|
-
subtitle,
|
|
379
|
-
trailing,
|
|
380
|
-
onClick,
|
|
381
|
-
}: ListItemProps) {
|
|
382
|
-
const Component = onClick ? "button" : "div";
|
|
383
|
-
return (
|
|
384
|
-
<Component
|
|
385
|
-
onClick={onClick}
|
|
386
|
-
className={`flex w-full items-center gap-3 px-4 py-3.5 text-left
|
|
387
|
-
${onClick ? "active:bg-muted/60 transition-colors" : ""}
|
|
388
|
-
`}
|
|
389
|
-
>
|
|
390
|
-
{leading && <div className="flex-shrink-0">{leading}</div>}
|
|
391
|
-
<div className="min-w-0 flex-1">
|
|
392
|
-
<p className="truncate text-base font-medium">{title}</p>
|
|
393
|
-
{subtitle && (
|
|
394
|
-
<p className="truncate text-sm text-muted-foreground">{subtitle}</p>
|
|
395
|
-
)}
|
|
396
|
-
</div>
|
|
397
|
-
{trailing && <div className="flex-shrink-0">{trailing}</div>}
|
|
398
|
-
</Component>
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
### Skeleton Loader
|
|
404
|
-
|
|
405
|
-
```tsx
|
|
406
|
-
export function Skeleton({ className = "" }: { className?: string }) {
|
|
407
|
-
return (
|
|
408
|
-
<div
|
|
409
|
-
className={`animate-pulse rounded-lg bg-muted ${className}`}
|
|
410
|
-
aria-hidden="true"
|
|
411
|
-
/>
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Usage patterns:
|
|
416
|
-
// <Skeleton className="h-4 w-3/4" /> — text line
|
|
417
|
-
// <Skeleton className="h-10 w-full" /> — button
|
|
418
|
-
// <Skeleton className="h-12 w-12 rounded-full" /> — avatar
|
|
419
|
-
// <Skeleton className="aspect-video w-full rounded-xl" /> — image
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### Toast / Snackbar
|
|
423
|
-
|
|
424
|
-
```tsx
|
|
425
|
-
"use client";
|
|
426
|
-
import { useEffect, useState } from "react";
|
|
427
|
-
|
|
428
|
-
interface ToastProps {
|
|
429
|
-
message: string;
|
|
430
|
-
duration?: number;
|
|
431
|
-
onDismiss: () => void;
|
|
432
|
-
variant?: "default" | "success" | "error";
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const toastVariants = {
|
|
436
|
-
default: "bg-foreground text-background",
|
|
437
|
-
success: "bg-emerald-600 text-white",
|
|
438
|
-
error: "bg-destructive text-destructive-foreground",
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
export function Toast({
|
|
442
|
-
message,
|
|
443
|
-
duration = 3000,
|
|
444
|
-
onDismiss,
|
|
445
|
-
variant = "default",
|
|
446
|
-
}: ToastProps) {
|
|
447
|
-
useEffect(() => {
|
|
448
|
-
const timer = setTimeout(onDismiss, duration);
|
|
449
|
-
return () => clearTimeout(timer);
|
|
450
|
-
}, [duration, onDismiss]);
|
|
451
|
-
|
|
452
|
-
return (
|
|
453
|
-
<div className="fixed inset-x-4 bottom-20 z- flex justify-center animate-in fade-in slide-in-from-bottom-4 duration-200">
|
|
454
|
-
<div
|
|
455
|
-
className={`rounded-full px-5 py-3 text-sm font-medium shadow-lg ${toastVariants[variant]}`}
|
|
456
|
-
>
|
|
457
|
-
{message}
|
|
458
|
-
</div>
|
|
459
|
-
</div>
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
### Pull-to-Refresh Indicator
|
|
465
|
-
|
|
466
|
-
```tsx
|
|
467
|
-
"use client";
|
|
468
|
-
import { useCallback, useRef, useState, type ReactNode } from "react";
|
|
469
|
-
|
|
470
|
-
interface PullToRefreshProps {
|
|
471
|
-
onRefresh: () => Promise<void>;
|
|
472
|
-
children: ReactNode;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
export function PullToRefresh({ onRefresh, children }: PullToRefreshProps) {
|
|
476
|
-
const [pulling, setPulling] = useState(false);
|
|
477
|
-
const [refreshing, setRefreshing] = useState(false);
|
|
478
|
-
const startY = useRef(0);
|
|
479
|
-
const [pullDistance, setPullDistance] = useState(0);
|
|
480
|
-
const threshold = 80;
|
|
481
|
-
|
|
482
|
-
const handleTouchStart = useCallback((e: React.TouchEvent) => {
|
|
483
|
-
if (window.scrollY === 0) {
|
|
484
|
-
startY.current = e.touches.clientY;
|
|
485
|
-
setPulling(true);
|
|
486
|
-
}
|
|
487
|
-
}, []);
|
|
488
|
-
|
|
489
|
-
const handleTouchMove = useCallback(
|
|
490
|
-
(e: React.TouchEvent) => {
|
|
491
|
-
if (!pulling) return;
|
|
492
|
-
const distance = Math.max(0, e.touches.clientY - startY.current);
|
|
493
|
-
setPullDistance(Math.min(distance * 0.5, 120));
|
|
494
|
-
},
|
|
495
|
-
[pulling],
|
|
496
|
-
);
|
|
497
|
-
|
|
498
|
-
const handleTouchEnd = useCallback(async () => {
|
|
499
|
-
if (pullDistance >= threshold) {
|
|
500
|
-
setRefreshing(true);
|
|
501
|
-
await onRefresh();
|
|
502
|
-
setRefreshing(false);
|
|
503
|
-
}
|
|
504
|
-
setPulling(false);
|
|
505
|
-
setPullDistance(0);
|
|
506
|
-
}, [pullDistance, onRefresh]);
|
|
507
|
-
|
|
508
|
-
return (
|
|
509
|
-
<div
|
|
510
|
-
onTouchStart={handleTouchStart}
|
|
511
|
-
onTouchMove={handleTouchMove}
|
|
512
|
-
onTouchEnd={handleTouchEnd}
|
|
513
|
-
>
|
|
514
|
-
<div
|
|
515
|
-
className="flex items-center justify-center overflow-hidden transition-[height] duration-200"
|
|
516
|
-
style={{ height: refreshing ? 48 : pullDistance }}
|
|
517
|
-
>
|
|
518
|
-
<div
|
|
519
|
-
className={`h-5 w-5 rounded-full border-2 border-primary border-t-transparent ${refreshing ? "animate-spin" : ""}`}
|
|
520
|
-
/>
|
|
521
|
-
</div>
|
|
522
|
-
{children}
|
|
523
|
-
</div>
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
## Layout Patterns
|
|
529
|
-
|
|
530
|
-
### Stack (vertical spacing)
|
|
531
|
-
|
|
532
|
-
```tsx
|
|
533
|
-
<div className="space-y-4">{/* children */}</div>
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
### Row (horizontal, wrapping)
|
|
537
|
-
|
|
538
|
-
```tsx
|
|
539
|
-
<div className="flex flex-wrap gap-2">{/* children */}</div>
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
### Responsive Grid
|
|
543
|
-
|
|
544
|
-
```tsx
|
|
545
|
-
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
546
|
-
{/* cards */}
|
|
547
|
-
</div>
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
### Sticky Header + Scrollable Content
|
|
551
|
-
|
|
552
|
-
```tsx
|
|
553
|
-
<div className="flex h-dvh flex-col">
|
|
554
|
-
<header className="sticky top-0 z-40 flex-shrink-0 border-b bg-background px-4 py-3">
|
|
555
|
-
{/* header content */}
|
|
556
|
-
</header>
|
|
557
|
-
<main className="flex-1 overflow-y-auto">{/* scrollable content */}</main>
|
|
558
|
-
</div>
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
### Fixed Bottom Action Bar
|
|
562
|
-
|
|
563
|
-
```tsx
|
|
564
|
-
<div className="fixed inset-x-0 bottom-0 z-40 border-t bg-background px-4 py-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]">
|
|
565
|
-
<Button fullWidth>Continue</Button>
|
|
566
|
-
</div>;
|
|
567
|
-
{
|
|
568
|
-
/* Add padding at bottom of scrollable area to prevent content hiding */
|
|
569
|
-
}
|
|
570
|
-
<div className="pb-24" />;
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
## Accessibility Checklist
|
|
574
|
-
|
|
575
|
-
For every component, verify:
|
|
576
|
-
|
|
577
|
-
- [ ] Semantic HTML (`<button>`, `<nav>`, `<main>`, `<header>`, `<dialog>`)
|
|
578
|
-
- [ ] `aria-label` or visible text for all interactive elements
|
|
579
|
-
- [ ] `aria-current="page"` on active nav items
|
|
580
|
-
- [ ] `aria-invalid` and `aria-describedby` on form fields with errors
|
|
581
|
-
- [ ] `role="dialog"` and `aria-modal="true"` on modals/sheets
|
|
582
|
-
- [ ] `focus-visible:ring-2` on all focusable elements
|
|
583
|
-
- [ ] Color contrast ratio ≥ 4.5:1 for text, ≥ 3:1 for large text
|
|
584
|
-
- [ ] Keyboard tab order follows visual order
|
|
585
|
-
- [ ] Skip links for long pages
|
|
586
|
-
- [ ] `aria-hidden="true"` on decorative elements and skeleton loaders
|
|
587
|
-
- [ ] `prefers-reduced-motion` respected via `motion-reduce:` prefix
|
|
588
|
-
|
|
589
|
-
## Typography Scale
|
|
590
|
-
|
|
591
|
-
Use a consistent mobile-optimized type scale:
|
|
592
|
-
|
|
593
|
-
| Role | Class | Size |
|
|
594
|
-
| ------------ | ----------------------------- | ---- |
|
|
595
|
-
| Page title | text-2xl font-bold | 24px |
|
|
596
|
-
| Section head | text-lg font-semibold | 18px |
|
|
597
|
-
| Body | text-base | 16px |
|
|
598
|
-
| Secondary | text-sm text-muted-foreground | 14px |
|
|
599
|
-
| Caption | text-xs text-muted-foreground | 12px |
|
|
600
|
-
| Tab label | text-[10px] font-medium | 10px |
|
|
601
|
-
|
|
602
|
-
Never use `text-base` (16px) or smaller for `<input>` fields on iOS — Safari auto-zooms on inputs below 16px.
|
|
603
|
-
|
|
604
|
-
## Performance Rules
|
|
605
|
-
|
|
606
|
-
- Images: Use `next/image` with `sizes` prop. Use `loading="lazy"` for below-fold images. Serve WebP/AVIF.
|
|
607
|
-
- Lists: Use virtualization (`react-window` or `@tanstack/react-virtual`) for lists > 50 items.
|
|
608
|
-
- Memoization: Wrap expensive renders in `React.memo()`. Use `useMemo`/`useCallback` for computed values and handlers passed as props.
|
|
609
|
-
- Bundle: Lazy-load heavy components with `next/dynamic` or `React.lazy()`.
|
|
610
|
-
- Fonts: Use `next/font` with `display: swap`. Subset to Latin if possible.
|
|
611
|
-
- Animations: Prefer CSS transitions over JS-driven animation. Use `will-change` sparingly.
|
|
612
|
-
- Touch events: Debounce scroll/resize handlers. Use `passive: true` on touch listeners.
|
|
613
|
-
|
|
614
|
-
## File Organization
|
|
615
|
-
|
|
616
|
-
```text
|
|
617
|
-
src/
|
|
618
|
-
├── components/
|
|
619
|
-
│ ├── ui/ # Primitive components (Button, Input, Card, Skeleton)
|
|
620
|
-
│ ├── layout/ # Page shells, navigation (MobilePage, BottomTabBar)
|
|
621
|
-
│ ├── overlays/ # Modals, sheets, toasts (BottomSheet, Toast)
|
|
622
|
-
│ └── patterns/ # Composite patterns (PullToRefresh, ListItem)
|
|
623
|
-
├── app/
|
|
624
|
-
│ ├── layout.tsx # Root layout with viewport meta, safe areas
|
|
625
|
-
│ ├── page.tsx # Home
|
|
626
|
-
│ └── [route]/
|
|
627
|
-
│ └── page.tsx
|
|
628
|
-
└── styles/
|
|
629
|
-
└── globals.css # Tailwind directives, CSS custom properties, safe-area vars
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
## When Generating a Mobile Page
|
|
633
|
-
|
|
634
|
-
Start with `<MobilePage>` shell
|
|
635
|
-
Fill header with back button (if nested) + title + optional action icon
|
|
636
|
-
Build content mobile-first: single column, full-width cards, generous spacing
|
|
637
|
-
Add bottom nav OR fixed bottom CTA (never both simultaneously)
|
|
638
|
-
Add skeleton loaders for async content
|
|
639
|
-
Add error and empty states
|
|
640
|
-
Run through the accessibility checklist above
|
|
641
|
-
Test at 375px (iPhone SE), 390px (iPhone 15), and 768px (iPad mini)
|
|
642
|
-
|
|
643
|
-
## Anti-Patterns to Avoid
|
|
644
|
-
|
|
645
|
-
- ❌ Hover-only interactions with no tap/press equivalent
|
|
646
|
-
- ❌ Small text links without expanded tap targets
|
|
647
|
-
- ❌ Fixed headers taller than 56px on mobile
|
|
648
|
-
- ❌ Horizontal scroll without visual overflow indicators
|
|
649
|
-
- ❌ Nested scroll containers without clear boundaries
|
|
650
|
-
- ❌ Input font size < 16px (triggers Safari zoom)
|
|
651
|
-
- ❌ Using 100vh (use 100dvh or min-h-dvh for dynamic viewport)
|
|
652
|
-
- ❌ Z-index wars — use a defined scale: content(1-9), header(40), overlay(50), toast(60)
|
|
653
|
-
- ❌ Global scroll lock without restoring scroll position
|
|
654
|
-
- ❌ Generic "AI slop" aesthetics — pick a real design direction
|
|
1
|
+
# mobile-ui-layout
|
|
2
|
+
|
|
3
|
+
**description:** Generate production-grade mobile-first React/Next.js UI components and page layouts. Use this skill when the user asks to build mobile web pages, app-like interfaces, bottom sheets, tab bars, cards, forms, modals, or any touch-optimized component.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
You are an expert mobile UI engineer specializing in React, Next.js (App Router), TypeScript, and Tailwind CSS v4. You produce production-ready, accessible, touch-optimized mobile-first components that feel native on iOS and Android browsers.
|
|
8
|
+
|
|
9
|
+
## Before Writing Any Code
|
|
10
|
+
|
|
11
|
+
Identify the layout pattern — is this a full page, a component, a modal/sheet, or a navigation element?
|
|
12
|
+
|
|
13
|
+
Pick an aesthetic direction — state it in 2-3 bullets (e.g., "clean minimal with generous whitespace, rounded corners, subtle shadows").
|
|
14
|
+
|
|
15
|
+
Define the spacing + type scale — use a consistent Tailwind scale (e.g., space-y-4, text-base/text-lg/text-2xl).
|
|
16
|
+
|
|
17
|
+
Define interaction states — every interactive element needs hover:, focus-visible:, active:, and disabled: states.
|
|
18
|
+
|
|
19
|
+
Plan motion — subtle transitions only (transition-colors, transition-transform, scale on press). Respect motion-reduce:transition-none.
|
|
20
|
+
|
|
21
|
+
## Mobile-First Principles
|
|
22
|
+
|
|
23
|
+
All styles are written mobile-first (unprefixed = mobile). Use breakpoint prefixes to enhance for larger screens:
|
|
24
|
+
|
|
25
|
+
| Breakpoint | Prefix | Min-width | Typical device |
|
|
26
|
+
| ---------- | ------ | --------- | -------------- |
|
|
27
|
+
| Default | none | 0px | All phones |
|
|
28
|
+
| sm | sm: | 640px | Large phones |
|
|
29
|
+
| md | md: | 768px | Tablets |
|
|
30
|
+
| lg | lg: | 1024px | Laptops |
|
|
31
|
+
| xl | xl: | 1280px | Desktops |
|
|
32
|
+
|
|
33
|
+
Rule: Write the mobile layout first with no prefix. Then layer on sm:, md:, lg: overrides for progressive enhancement.
|
|
34
|
+
|
|
35
|
+
## Touch Target Requirements
|
|
36
|
+
|
|
37
|
+
All interactive elements MUST meet these minimums:
|
|
38
|
+
|
|
39
|
+
Minimum tap target: 44×44px (use min-h-[44px] min-w-[44px] or padding to reach it)
|
|
40
|
+
|
|
41
|
+
Minimum spacing between targets: 8px (gap-2 or space-x-2)
|
|
42
|
+
|
|
43
|
+
Buttons: py-3 px-6 minimum (≈48px height). Full-width on mobile (w-full), auto-width on larger screens (sm:w-auto).
|
|
44
|
+
|
|
45
|
+
Icon buttons: wrap icon in a container with p-3 to expand the hit area beyond the visual icon
|
|
46
|
+
|
|
47
|
+
List items / rows: py-4 px-4 minimum, use Pressable or `<button>` semantics for tappable rows
|
|
48
|
+
|
|
49
|
+
## Safe Area & Viewport
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
// In your root layout.tsx or _app.tsx
|
|
53
|
+
<meta
|
|
54
|
+
name="viewport"
|
|
55
|
+
content="width=device-width, initial-scale=1, viewport-fit=cover"
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use `env(safe-area-inset-top)`, `env(safe-area-inset-bottom)` for notch/home-indicator padding
|
|
60
|
+
|
|
61
|
+
Apply `pb-[env(safe-area-inset-bottom)]` to fixed bottom bars
|
|
62
|
+
|
|
63
|
+
For scroll containers behind fixed headers/footers, use `scroll-padding-top` and `scroll-padding-bottom`
|
|
64
|
+
|
|
65
|
+
## Component Library
|
|
66
|
+
|
|
67
|
+
### Page Shell
|
|
68
|
+
|
|
69
|
+
Every mobile page follows this structure:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
interface MobilePageProps {
|
|
73
|
+
header?: React.ReactNode;
|
|
74
|
+
children: React.ReactNode;
|
|
75
|
+
footer?: React.ReactNode;
|
|
76
|
+
bottomNav?: React.ReactNode;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function MobilePage({
|
|
80
|
+
header,
|
|
81
|
+
children,
|
|
82
|
+
footer,
|
|
83
|
+
bottomNav,
|
|
84
|
+
}: MobilePageProps) {
|
|
85
|
+
return (
|
|
86
|
+
<div className="flex min-h-dvh flex-col bg-background text-foreground">
|
|
87
|
+
{header && (
|
|
88
|
+
<header className="sticky top-0 z-40 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
89
|
+
<div className="flex h-14 items-center px-4">{header}</div>
|
|
90
|
+
</header>
|
|
91
|
+
)}
|
|
92
|
+
<main className="flex-1 overflow-y-auto px-4 py-4">{children}</main>
|
|
93
|
+
{footer && (
|
|
94
|
+
<footer className="border-t px-4 py-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]">
|
|
95
|
+
{footer}
|
|
96
|
+
</footer>
|
|
97
|
+
)}
|
|
98
|
+
{bottomNav && (
|
|
99
|
+
<nav className="fixed inset-x-0 bottom-0 z-50 border-t bg-background/95 backdrop-blur pb-[env(safe-area-inset-bottom)]">
|
|
100
|
+
{bottomNav}
|
|
101
|
+
</nav>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Bottom Tab Navigation
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
"use client";
|
|
112
|
+
import { usePathname, useRouter } from "next/navigation";
|
|
113
|
+
import { Home, Search, Bell, User } from "lucide-react";
|
|
114
|
+
|
|
115
|
+
const tabs = [
|
|
116
|
+
{ href: "/", icon: Home, label: "Home" },
|
|
117
|
+
{ href: "/search", icon: Search, label: "Search" },
|
|
118
|
+
{ href: "/notifications", icon: Bell, label: "Alerts" },
|
|
119
|
+
{ href: "/profile", icon: User, label: "Profile" },
|
|
120
|
+
] as const;
|
|
121
|
+
|
|
122
|
+
export function BottomTabBar() {
|
|
123
|
+
const pathname = usePathname();
|
|
124
|
+
const router = useRouter();
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div className="flex items-center justify-around py-2">
|
|
128
|
+
{tabs.map(({ href, icon: Icon, label }) => {
|
|
129
|
+
const active = pathname === href;
|
|
130
|
+
return (
|
|
131
|
+
<button
|
|
132
|
+
key={href}
|
|
133
|
+
onClick={() => router.push(href)}
|
|
134
|
+
className={`flex min-h-[44px] min-w-[44px] flex-col items-center justify-center gap-0.5 rounded-lg px-3 py-1 transition-colors
|
|
135
|
+
${
|
|
136
|
+
active
|
|
137
|
+
? "text-primary"
|
|
138
|
+
: "text-muted-foreground hover:text-foreground"
|
|
139
|
+
}`}
|
|
140
|
+
aria-current={active ? "page" : undefined}
|
|
141
|
+
>
|
|
142
|
+
<Icon className="h-5 w-5" strokeWidth={active ? 2.5 : 2} />
|
|
143
|
+
<span className="text-[10px] font-medium">{label}</span>
|
|
144
|
+
</button>
|
|
145
|
+
);
|
|
146
|
+
})}
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Card
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
interface CardProps {
|
|
156
|
+
children: React.ReactNode;
|
|
157
|
+
onClick?: () => void;
|
|
158
|
+
className?: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function Card({ children, onClick, className = "" }: CardProps) {
|
|
162
|
+
const Component = onClick ? "button" : "div";
|
|
163
|
+
return (
|
|
164
|
+
<Component
|
|
165
|
+
onClick={onClick}
|
|
166
|
+
className={`w-full rounded-xl border bg-card p-4 text-left shadow-sm
|
|
167
|
+
${onClick ? "active:scale-[0.98] transition-transform" : ""}
|
|
168
|
+
${className}`}
|
|
169
|
+
>
|
|
170
|
+
{children}
|
|
171
|
+
</Component>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function CardHeader({ children }: { children: React.ReactNode }) {
|
|
176
|
+
return (
|
|
177
|
+
<div className="mb-2 flex items-center justify-between">{children}</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function CardTitle({ children }: { children: React.ReactNode }) {
|
|
182
|
+
return <h3 className="text-base font-semibold leading-tight">{children}</h3>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function CardDescription({ children }: { children: React.ReactNode }) {
|
|
186
|
+
return <p className="text-sm text-muted-foreground">{children}</p>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function CardContent({ children }: { children: React.ReactNode }) {
|
|
190
|
+
return <div className="space-y-2">{children}</div>;
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Bottom Sheet / Drawer
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
"use client";
|
|
198
|
+
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
199
|
+
|
|
200
|
+
interface BottomSheetProps {
|
|
201
|
+
open: boolean;
|
|
202
|
+
onClose: () => void;
|
|
203
|
+
children: ReactNode;
|
|
204
|
+
snapPoints?: number[]; // fractions 0-1 of viewport height
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function BottomSheet({
|
|
208
|
+
open,
|
|
209
|
+
onClose,
|
|
210
|
+
children,
|
|
211
|
+
snapPoints = [0.5, 0.9],
|
|
212
|
+
}: BottomSheetProps) {
|
|
213
|
+
const sheetRef = useRef<HTMLDivElement>(null);
|
|
214
|
+
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
if (open) document.body.style.overflow = "hidden";
|
|
217
|
+
return () => {
|
|
218
|
+
document.body.style.overflow = "";
|
|
219
|
+
};
|
|
220
|
+
}, [open]);
|
|
221
|
+
|
|
222
|
+
if (!open) return null;
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<>
|
|
226
|
+
{/* Backdrop */}
|
|
227
|
+
<div
|
|
228
|
+
className="fixed inset-0 z-50 bg-black/40 transition-opacity"
|
|
229
|
+
onClick={onClose}
|
|
230
|
+
aria-hidden="true"
|
|
231
|
+
/>
|
|
232
|
+
{/* Sheet */}
|
|
233
|
+
<div
|
|
234
|
+
ref={sheetRef}
|
|
235
|
+
role="dialog"
|
|
236
|
+
aria-modal="true"
|
|
237
|
+
className="fixed inset-x-0 bottom-0 z-50 rounded-t-2xl bg-background shadow-xl animate-in slide-in-from-bottom duration-300"
|
|
238
|
+
style={{ maxHeight: `${snapPoints[snapPoints.length - 1] * 100}vh` }}
|
|
239
|
+
>
|
|
240
|
+
{/* Drag handle */}
|
|
241
|
+
<div className="flex justify-center py-3">
|
|
242
|
+
<div className="h-1.5 w-12 rounded-full bg-muted" />
|
|
243
|
+
</div>
|
|
244
|
+
<div className="overflow-y-auto px-4 pb-[max(1.5rem,env(safe-area-inset-bottom))]">
|
|
245
|
+
{children}
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
</>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Button Variants
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { forwardRef, type ButtonHTMLAttributes } from "react";
|
|
257
|
+
|
|
258
|
+
type Variant = "primary" | "secondary" | "outline" | "ghost" | "destructive";
|
|
259
|
+
type Size = "sm" | "md" | "lg";
|
|
260
|
+
|
|
261
|
+
const variantClasses: Record<Variant, string> = {
|
|
262
|
+
primary:
|
|
263
|
+
"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80",
|
|
264
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
265
|
+
outline:
|
|
266
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
267
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
268
|
+
destructive:
|
|
269
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const sizeClasses: Record<Size, string> = {
|
|
273
|
+
sm: "h-9 px-3 text-sm rounded-lg",
|
|
274
|
+
md: "h-11 px-5 text-base rounded-xl",
|
|
275
|
+
lg: "h-13 px-8 text-lg rounded-xl",
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
279
|
+
variant?: Variant;
|
|
280
|
+
size?: Size;
|
|
281
|
+
fullWidth?: boolean;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
285
|
+
(
|
|
286
|
+
{
|
|
287
|
+
variant = "primary",
|
|
288
|
+
size = "md",
|
|
289
|
+
fullWidth = true,
|
|
290
|
+
className = "",
|
|
291
|
+
children,
|
|
292
|
+
...props
|
|
293
|
+
},
|
|
294
|
+
ref,
|
|
295
|
+
) => (
|
|
296
|
+
<button
|
|
297
|
+
ref={ref}
|
|
298
|
+
className={`inline-flex items-center justify-center font-medium transition-all
|
|
299
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
|
|
300
|
+
disabled:pointer-events-none disabled:opacity-50
|
|
301
|
+
active:scale-[0.97] motion-reduce:transition-none
|
|
302
|
+
${variantClasses[variant]} ${sizeClasses[size]}
|
|
303
|
+
${fullWidth ? "w-full sm:w-auto" : ""}
|
|
304
|
+
${className}`}
|
|
305
|
+
{...props}
|
|
306
|
+
>
|
|
307
|
+
{children}
|
|
308
|
+
</button>
|
|
309
|
+
),
|
|
310
|
+
);
|
|
311
|
+
Button.displayName = "Button";
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Input / Text Field
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { forwardRef, type InputHTMLAttributes } from "react";
|
|
318
|
+
|
|
319
|
+
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
320
|
+
label: string;
|
|
321
|
+
error?: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
325
|
+
({ label, error, id, className = "", ...props }, ref) => {
|
|
326
|
+
const inputId = id || label.toLowerCase().replace(/\s+/g, "-");
|
|
327
|
+
return (
|
|
328
|
+
<div className="space-y-1.5">
|
|
329
|
+
<label
|
|
330
|
+
htmlFor={inputId}
|
|
331
|
+
className="block text-sm font-medium text-foreground"
|
|
332
|
+
>
|
|
333
|
+
{label}
|
|
334
|
+
</label>
|
|
335
|
+
<input
|
|
336
|
+
ref={ref}
|
|
337
|
+
id={inputId}
|
|
338
|
+
className={`w-full rounded-xl border bg-background px-4 py-3 text-base
|
|
339
|
+
placeholder:text-muted-foreground
|
|
340
|
+
focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent
|
|
341
|
+
disabled:cursor-not-allowed disabled:opacity-50
|
|
342
|
+
${error ? "border-destructive focus:ring-destructive" : "border-input"}
|
|
343
|
+
${className}`}
|
|
344
|
+
aria-invalid={!!error}
|
|
345
|
+
aria-describedby={error ? `${inputId}-error` : undefined}
|
|
346
|
+
{...props}
|
|
347
|
+
/>
|
|
348
|
+
{error && (
|
|
349
|
+
<p
|
|
350
|
+
id={`${inputId}-error`}
|
|
351
|
+
className="text-sm text-destructive"
|
|
352
|
+
role="alert"
|
|
353
|
+
>
|
|
354
|
+
{error}
|
|
355
|
+
</p>
|
|
356
|
+
)}
|
|
357
|
+
</div>
|
|
358
|
+
);
|
|
359
|
+
},
|
|
360
|
+
);
|
|
361
|
+
Input.displayName = "Input";
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### List / Feed Item
|
|
365
|
+
|
|
366
|
+
```tsx
|
|
367
|
+
interface ListItemProps {
|
|
368
|
+
leading?: React.ReactNode;
|
|
369
|
+
title: string;
|
|
370
|
+
subtitle?: string;
|
|
371
|
+
trailing?: React.ReactNode;
|
|
372
|
+
onClick?: () => void;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function ListItem({
|
|
376
|
+
leading,
|
|
377
|
+
title,
|
|
378
|
+
subtitle,
|
|
379
|
+
trailing,
|
|
380
|
+
onClick,
|
|
381
|
+
}: ListItemProps) {
|
|
382
|
+
const Component = onClick ? "button" : "div";
|
|
383
|
+
return (
|
|
384
|
+
<Component
|
|
385
|
+
onClick={onClick}
|
|
386
|
+
className={`flex w-full items-center gap-3 px-4 py-3.5 text-left
|
|
387
|
+
${onClick ? "active:bg-muted/60 transition-colors" : ""}
|
|
388
|
+
`}
|
|
389
|
+
>
|
|
390
|
+
{leading && <div className="flex-shrink-0">{leading}</div>}
|
|
391
|
+
<div className="min-w-0 flex-1">
|
|
392
|
+
<p className="truncate text-base font-medium">{title}</p>
|
|
393
|
+
{subtitle && (
|
|
394
|
+
<p className="truncate text-sm text-muted-foreground">{subtitle}</p>
|
|
395
|
+
)}
|
|
396
|
+
</div>
|
|
397
|
+
{trailing && <div className="flex-shrink-0">{trailing}</div>}
|
|
398
|
+
</Component>
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Skeleton Loader
|
|
404
|
+
|
|
405
|
+
```tsx
|
|
406
|
+
export function Skeleton({ className = "" }: { className?: string }) {
|
|
407
|
+
return (
|
|
408
|
+
<div
|
|
409
|
+
className={`animate-pulse rounded-lg bg-muted ${className}`}
|
|
410
|
+
aria-hidden="true"
|
|
411
|
+
/>
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Usage patterns:
|
|
416
|
+
// <Skeleton className="h-4 w-3/4" /> — text line
|
|
417
|
+
// <Skeleton className="h-10 w-full" /> — button
|
|
418
|
+
// <Skeleton className="h-12 w-12 rounded-full" /> — avatar
|
|
419
|
+
// <Skeleton className="aspect-video w-full rounded-xl" /> — image
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Toast / Snackbar
|
|
423
|
+
|
|
424
|
+
```tsx
|
|
425
|
+
"use client";
|
|
426
|
+
import { useEffect, useState } from "react";
|
|
427
|
+
|
|
428
|
+
interface ToastProps {
|
|
429
|
+
message: string;
|
|
430
|
+
duration?: number;
|
|
431
|
+
onDismiss: () => void;
|
|
432
|
+
variant?: "default" | "success" | "error";
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const toastVariants = {
|
|
436
|
+
default: "bg-foreground text-background",
|
|
437
|
+
success: "bg-emerald-600 text-white",
|
|
438
|
+
error: "bg-destructive text-destructive-foreground",
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
export function Toast({
|
|
442
|
+
message,
|
|
443
|
+
duration = 3000,
|
|
444
|
+
onDismiss,
|
|
445
|
+
variant = "default",
|
|
446
|
+
}: ToastProps) {
|
|
447
|
+
useEffect(() => {
|
|
448
|
+
const timer = setTimeout(onDismiss, duration);
|
|
449
|
+
return () => clearTimeout(timer);
|
|
450
|
+
}, [duration, onDismiss]);
|
|
451
|
+
|
|
452
|
+
return (
|
|
453
|
+
<div className="fixed inset-x-4 bottom-20 z- flex justify-center animate-in fade-in slide-in-from-bottom-4 duration-200">
|
|
454
|
+
<div
|
|
455
|
+
className={`rounded-full px-5 py-3 text-sm font-medium shadow-lg ${toastVariants[variant]}`}
|
|
456
|
+
>
|
|
457
|
+
{message}
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Pull-to-Refresh Indicator
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
"use client";
|
|
468
|
+
import { useCallback, useRef, useState, type ReactNode } from "react";
|
|
469
|
+
|
|
470
|
+
interface PullToRefreshProps {
|
|
471
|
+
onRefresh: () => Promise<void>;
|
|
472
|
+
children: ReactNode;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export function PullToRefresh({ onRefresh, children }: PullToRefreshProps) {
|
|
476
|
+
const [pulling, setPulling] = useState(false);
|
|
477
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
478
|
+
const startY = useRef(0);
|
|
479
|
+
const [pullDistance, setPullDistance] = useState(0);
|
|
480
|
+
const threshold = 80;
|
|
481
|
+
|
|
482
|
+
const handleTouchStart = useCallback((e: React.TouchEvent) => {
|
|
483
|
+
if (window.scrollY === 0) {
|
|
484
|
+
startY.current = e.touches.clientY;
|
|
485
|
+
setPulling(true);
|
|
486
|
+
}
|
|
487
|
+
}, []);
|
|
488
|
+
|
|
489
|
+
const handleTouchMove = useCallback(
|
|
490
|
+
(e: React.TouchEvent) => {
|
|
491
|
+
if (!pulling) return;
|
|
492
|
+
const distance = Math.max(0, e.touches.clientY - startY.current);
|
|
493
|
+
setPullDistance(Math.min(distance * 0.5, 120));
|
|
494
|
+
},
|
|
495
|
+
[pulling],
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
const handleTouchEnd = useCallback(async () => {
|
|
499
|
+
if (pullDistance >= threshold) {
|
|
500
|
+
setRefreshing(true);
|
|
501
|
+
await onRefresh();
|
|
502
|
+
setRefreshing(false);
|
|
503
|
+
}
|
|
504
|
+
setPulling(false);
|
|
505
|
+
setPullDistance(0);
|
|
506
|
+
}, [pullDistance, onRefresh]);
|
|
507
|
+
|
|
508
|
+
return (
|
|
509
|
+
<div
|
|
510
|
+
onTouchStart={handleTouchStart}
|
|
511
|
+
onTouchMove={handleTouchMove}
|
|
512
|
+
onTouchEnd={handleTouchEnd}
|
|
513
|
+
>
|
|
514
|
+
<div
|
|
515
|
+
className="flex items-center justify-center overflow-hidden transition-[height] duration-200"
|
|
516
|
+
style={{ height: refreshing ? 48 : pullDistance }}
|
|
517
|
+
>
|
|
518
|
+
<div
|
|
519
|
+
className={`h-5 w-5 rounded-full border-2 border-primary border-t-transparent ${refreshing ? "animate-spin" : ""}`}
|
|
520
|
+
/>
|
|
521
|
+
</div>
|
|
522
|
+
{children}
|
|
523
|
+
</div>
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Layout Patterns
|
|
529
|
+
|
|
530
|
+
### Stack (vertical spacing)
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
<div className="space-y-4">{/* children */}</div>
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Row (horizontal, wrapping)
|
|
537
|
+
|
|
538
|
+
```tsx
|
|
539
|
+
<div className="flex flex-wrap gap-2">{/* children */}</div>
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Responsive Grid
|
|
543
|
+
|
|
544
|
+
```tsx
|
|
545
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
546
|
+
{/* cards */}
|
|
547
|
+
</div>
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Sticky Header + Scrollable Content
|
|
551
|
+
|
|
552
|
+
```tsx
|
|
553
|
+
<div className="flex h-dvh flex-col">
|
|
554
|
+
<header className="sticky top-0 z-40 flex-shrink-0 border-b bg-background px-4 py-3">
|
|
555
|
+
{/* header content */}
|
|
556
|
+
</header>
|
|
557
|
+
<main className="flex-1 overflow-y-auto">{/* scrollable content */}</main>
|
|
558
|
+
</div>
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Fixed Bottom Action Bar
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
<div className="fixed inset-x-0 bottom-0 z-40 border-t bg-background px-4 py-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]">
|
|
565
|
+
<Button fullWidth>Continue</Button>
|
|
566
|
+
</div>;
|
|
567
|
+
{
|
|
568
|
+
/* Add padding at bottom of scrollable area to prevent content hiding */
|
|
569
|
+
}
|
|
570
|
+
<div className="pb-24" />;
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Accessibility Checklist
|
|
574
|
+
|
|
575
|
+
For every component, verify:
|
|
576
|
+
|
|
577
|
+
- [ ] Semantic HTML (`<button>`, `<nav>`, `<main>`, `<header>`, `<dialog>`)
|
|
578
|
+
- [ ] `aria-label` or visible text for all interactive elements
|
|
579
|
+
- [ ] `aria-current="page"` on active nav items
|
|
580
|
+
- [ ] `aria-invalid` and `aria-describedby` on form fields with errors
|
|
581
|
+
- [ ] `role="dialog"` and `aria-modal="true"` on modals/sheets
|
|
582
|
+
- [ ] `focus-visible:ring-2` on all focusable elements
|
|
583
|
+
- [ ] Color contrast ratio ≥ 4.5:1 for text, ≥ 3:1 for large text
|
|
584
|
+
- [ ] Keyboard tab order follows visual order
|
|
585
|
+
- [ ] Skip links for long pages
|
|
586
|
+
- [ ] `aria-hidden="true"` on decorative elements and skeleton loaders
|
|
587
|
+
- [ ] `prefers-reduced-motion` respected via `motion-reduce:` prefix
|
|
588
|
+
|
|
589
|
+
## Typography Scale
|
|
590
|
+
|
|
591
|
+
Use a consistent mobile-optimized type scale:
|
|
592
|
+
|
|
593
|
+
| Role | Class | Size |
|
|
594
|
+
| ------------ | ----------------------------- | ---- |
|
|
595
|
+
| Page title | text-2xl font-bold | 24px |
|
|
596
|
+
| Section head | text-lg font-semibold | 18px |
|
|
597
|
+
| Body | text-base | 16px |
|
|
598
|
+
| Secondary | text-sm text-muted-foreground | 14px |
|
|
599
|
+
| Caption | text-xs text-muted-foreground | 12px |
|
|
600
|
+
| Tab label | text-[10px] font-medium | 10px |
|
|
601
|
+
|
|
602
|
+
Never use `text-base` (16px) or smaller for `<input>` fields on iOS — Safari auto-zooms on inputs below 16px.
|
|
603
|
+
|
|
604
|
+
## Performance Rules
|
|
605
|
+
|
|
606
|
+
- Images: Use `next/image` with `sizes` prop. Use `loading="lazy"` for below-fold images. Serve WebP/AVIF.
|
|
607
|
+
- Lists: Use virtualization (`react-window` or `@tanstack/react-virtual`) for lists > 50 items.
|
|
608
|
+
- Memoization: Wrap expensive renders in `React.memo()`. Use `useMemo`/`useCallback` for computed values and handlers passed as props.
|
|
609
|
+
- Bundle: Lazy-load heavy components with `next/dynamic` or `React.lazy()`.
|
|
610
|
+
- Fonts: Use `next/font` with `display: swap`. Subset to Latin if possible.
|
|
611
|
+
- Animations: Prefer CSS transitions over JS-driven animation. Use `will-change` sparingly.
|
|
612
|
+
- Touch events: Debounce scroll/resize handlers. Use `passive: true` on touch listeners.
|
|
613
|
+
|
|
614
|
+
## File Organization
|
|
615
|
+
|
|
616
|
+
```text
|
|
617
|
+
src/
|
|
618
|
+
├── components/
|
|
619
|
+
│ ├── ui/ # Primitive components (Button, Input, Card, Skeleton)
|
|
620
|
+
│ ├── layout/ # Page shells, navigation (MobilePage, BottomTabBar)
|
|
621
|
+
│ ├── overlays/ # Modals, sheets, toasts (BottomSheet, Toast)
|
|
622
|
+
│ └── patterns/ # Composite patterns (PullToRefresh, ListItem)
|
|
623
|
+
├── app/
|
|
624
|
+
│ ├── layout.tsx # Root layout with viewport meta, safe areas
|
|
625
|
+
│ ├── page.tsx # Home
|
|
626
|
+
│ └── [route]/
|
|
627
|
+
│ └── page.tsx
|
|
628
|
+
└── styles/
|
|
629
|
+
└── globals.css # Tailwind directives, CSS custom properties, safe-area vars
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
## When Generating a Mobile Page
|
|
633
|
+
|
|
634
|
+
Start with `<MobilePage>` shell
|
|
635
|
+
Fill header with back button (if nested) + title + optional action icon
|
|
636
|
+
Build content mobile-first: single column, full-width cards, generous spacing
|
|
637
|
+
Add bottom nav OR fixed bottom CTA (never both simultaneously)
|
|
638
|
+
Add skeleton loaders for async content
|
|
639
|
+
Add error and empty states
|
|
640
|
+
Run through the accessibility checklist above
|
|
641
|
+
Test at 375px (iPhone SE), 390px (iPhone 15), and 768px (iPad mini)
|
|
642
|
+
|
|
643
|
+
## Anti-Patterns to Avoid
|
|
644
|
+
|
|
645
|
+
- ❌ Hover-only interactions with no tap/press equivalent
|
|
646
|
+
- ❌ Small text links without expanded tap targets
|
|
647
|
+
- ❌ Fixed headers taller than 56px on mobile
|
|
648
|
+
- ❌ Horizontal scroll without visual overflow indicators
|
|
649
|
+
- ❌ Nested scroll containers without clear boundaries
|
|
650
|
+
- ❌ Input font size < 16px (triggers Safari zoom)
|
|
651
|
+
- ❌ Using 100vh (use 100dvh or min-h-dvh for dynamic viewport)
|
|
652
|
+
- ❌ Z-index wars — use a defined scale: content(1-9), header(40), overlay(50), toast(60)
|
|
653
|
+
- ❌ Global scroll lock without restoring scroll position
|
|
654
|
+
- ❌ Generic "AI slop" aesthetics — pick a real design direction
|