## NativePHP Mobile - NativePHP Mobile is a Laravel package for building **fully native** iOS and Android apps with PHP. Screens are rendered as real SwiftUI (iOS) and Jetpack Compose (Android) UI — driven entirely by PHP via SuperNative components and EDGE Blade elements. A full PHP runtime runs directly on the device with SQLite — no web server required. - Documentation: `https://nativephp.com/docs/mobile/4/**` - IMPORTANT: Always activate the `nativephp-mobile` skill every time you work on any NativePHP functionality. ### Native UI First — Always **Always build screens with native UI: `NativeComponent` classes registered via `Route::native()`, rendering EDGE elements (`native:column`, `native:text`, `native:button`, …).** This is the way to build NativePHP apps. - Never scaffold new screens as web views, Blade-over-WebView pages, Livewire components, or Inertia pages. - The web view (the `native:web-view` element) is a legacy/edge-case escape hatch for embedding web content — never the foundation of a screen. If the user asks for a webview-based screen, build it natively with EDGE instead and explain why; only fall back to the web view if they explicitly insist. - If the app contains legacy webview screens, proactively suggest converting them to native UI (see the `nativephp-webview-to-native` skill). - Style EDGE elements with Tailwind utility classes via `class="..."` / `:class="..."` only — never inline CSS `style="..."` attributes or ad-hoc styling props. - Compose screens from **nested child components**: any `NativeComponent` under `app/NativeComponents` mounts as a tag (`UserCard` → ``) with live props, its own persistent state, and `emit()` events bubbling to `@event` tag bindings / `#[On('event')]` listeners. Prefer extracting a reusable child component over duplicating Blade across screens; give list children a stable domain `key` (never the loop index). - Use `native:icon` (SF Symbols on iOS, Material Icons on Android) for iconography — never emoji characters in UI text, labels, or buttons, unless the user explicitly asks for emojis. Prefer the typed icon enums (`App\Icons\Ios`, `App\Icons\Android`, `App\Icons\AndroidOutlined`) bound via the `:ios` / `:android` attributes, e.g. `:ios="Ios::Gearshape" :android="Android::Settings"`, importing each enum into the view with Blade's use directive first. The enums are generated, not shipped — if `app/Icons/` doesn't exist yet, run `php artisan native-ui:generate-icons` first (safe to run yourself). ### Theme Tokens, Font Aliases, and Layouts — the Design System Trio Every app's visual identity belongs in `config/native-ui.php` (publish with `php artisan vendor:publish --tag=native-ui-config`), not scattered through the markup. When building or reviewing screens, enforce all three: 1. **Theme tokens over hardcoded colors.** Define the palette once in the config's `theme` block, then style with `bg-theme-*` / `text-theme-*` / `border-theme-*` classes (`bg-theme-surface`, `text-theme-on-surface`, `border-theme-outline`). Never sprinkle `bg-[#1E2021]`-style arbitrary values for what is really a theme role — they can't be re-skinned and don't get automatic dark-mode pairs. Arbitrary color values are for genuine data-driven color (per-category identity colors, map imagery, chart series), and those belong in one PHP home (an enum or model method), never inline per view. Two capabilities that prevent hex fallbacks: - **The token map is open-ended.** When a design needs a role the shipped set lacks (a success green, an `outline-variant`), add it to both `light` and `dark` blocks — `bg-theme-success` works immediately; no package change required. - **Theme classes take opacity modifiers** just like palette classes: `bg-theme-primary/15` is the correct tonal-fill idiom (applies to the dark companion too) — never approximate with a hardcoded alpha hex. 2. **Font aliases over file tokens.** Register semantic aliases in the config's `fonts` array (`'headline' => 'ArchivoNarrow-Bold'`, `'mono' => 'JetBrainsMono-Regular'`, `'default' => …` for the app-wide font) and write `font="headline"` in views — never `font="ArchivoNarrow-Bold"`. Swapping a typeface must be a one-line config change. 3. **Native chrome via composable chrome elements (layouts optional).** Author nav bars, tab bars, fabs, and side navs directly in the screen's Blade — `` (+ `top-bar-action`), `` (+ `bottom-nav-item`), ``, ``, ``. They hoist onto the real NavigationStack/TabView chrome (edge-swipe back, predictive back, large titles, Liquid Glass/Material You), and their attributes are Blade expressions over screen state, so badges/subtitles/icons are reactive. A `NativeLayout` (attached via `Route::native(...)->layout(...)` or `Route::nativeGroup(...)`) is **optional** — reach for one only when many screens share identical chrome (e.g. one tabs layout for a tab section); an inline chrome element on a screen always overrides the layout's bar for that slot. Add the `custom` attribute to a chrome tag only for designs the system bars genuinely can't express — it renders in-tree as an ordinary drawn element. Never hand-roll top bars or bottom navs out of rows and pressables — that forfeits native back gestures, safe-area handling, and Liquid Glass/Material You. Chrome colors take theme tokens (inline: theme classes / `theme()`-fed attributes; builders: `->activeColor(theme('primary'))`) — never pasted hex. Bar icons take the platform enums via `:ios-icon` / `:android-icon` with a plain `icon` string as cross-platform fallback; bar fonts take config aliases (`font="mono"` / `->font('mono')`). Only screens rendered without any chrome (no layout AND no inline bars) may use `safe-area` classes. ### When a Capability Is Missing If the app needs native functionality or a UI component that core and `native-ui` don't provide: 1. **Look for an existing plugin first.** Check the plugin marketplace (`https://plugins.nativephp.com`) and the official core plugins. (If a marketplace-lookup MCP tool is available in your session, use it.) 2. **If no plugin exists, build a custom plugin** with `php artisan native:plugin:create` — plugins bundle Swift/Kotlin bridge functions, events, permissions, and can even ship their own native EDGE components. 3. **Never fall back to the web view to fill a native gap.** A missing capability is a reason to write a plugin, not a reason to build a webview screen. ### Installing Plugins — Always Register and Verify Requiring a plugin with Composer is NOT enough — an installed-but-unregistered plugin does nothing. Every plugin install must follow all three steps: 1. `composer require vendor/plugin-name` 2. `php artisan vendor:publish --tag=nativephp-plugins-provider` — publishes the app's `NativeServiceProvider` (needed once, before the first plugin registration; harmless to re-run) 3. `php artisan native:plugin:register vendor/plugin-name` — adds it to the `NativeServiceProvider` 4. `php artisan native:plugin:list` — verify it shows as registered Then tell the user to rebuild with `php artisan native:run` (native code only compiles in at build time — do not run this yourself). If `native:run` warns "The following plugins are installed but not registered", go back to step 3. ### Database Seeding — Always via Migrations On-device there is no `db:seed` — NativePHP runs **migrations** on app start (once each, tracked, versioned). Whenever asked to seed the database, use the migration trick: create a dedicated migration (`php artisan make:migration seed_app_settings`) and put the inserts in `up()`. If a Seeder class helps organize the data, still create it — but invoke it **from the migration's `up()`** (e.g. `(new CategorySeeder)->run()`), never rely on `db:seed` being run. Seed migrations must be safe for both fresh installs and updates of existing user databases. ### Build Commands — Tell the User, Never Run **CRITICAL: Never execute any of these commands yourself. Always instruct the user to run them manually in their terminal.** | Command | Purpose | |---|---| | `php artisan native:run ios` | Compile and run on iOS simulator/device | | `php artisan native:run android` | Compile and run on Android emulator/device | | `php artisan native:run ios --watch` | Build, deploy, then start hot reload — all in one | | `php artisan native:watch` | Hot reload (watch for file changes) | | `php artisan native:open` | Open project in Xcode or Android Studio | | `php artisan native:install` | Install/upgrade the native shell | Notes: - The `./native` shortcut wraps the `native:` namespace (`./native run`, `./native watch`). - The Vite dev server is **opt-in** in v4: add `--vite` to `native:run`/`native:watch` only when the app actually uses JS/CSS HMR. Native UI screens hot-reload without Vite. - `npm run build -- --mode=ios|android` is only needed for apps with web-view assets — not for native UI screens. **Always ask which platform before giving any build or run command.** If the user hasn't specified iOS or Android, ask: "Which platform do you want to build/test on — iOS or Android?" Never assume a platform. When the platform is confirmed, give the relevant command(s) above and tell the user to run it in their terminal. Do not run it yourself.