- Getting Started
- Architecture
- The Basics
- Digging Deeper
-
EDGE Components
- Introduction
- Activity Indicator
- Badge
- Bottom Navigation
- Bottom Sheet
- Button
- Button Group
- Canvas
- Carousel
- Checkbox
- Chip
- Column
- Divider
- Gesture Area
- Icon
- Icons
- Image
- Layout & Styling
- List
- Menus
- Modal
- Pressable
- Progress Bar
- Radio Group
- Row
- Scroll View
- Select
- Shapes
- Side Navigation
- Slider
- Spacer
- Stack
- Tab Row
- Text
- Text Input
- Toggle
- Top Bar
- Virtual List
- Web View
- Plugins
- Testing
- Publishing Your App
You're viewing pre-release documentation — version 4.x is in beta
Features, APIs and behaviour may change before the stable release. View the stable version (3.x)
Theming
Overview#
Every SuperNative app has one visual identity, defined in a single theme. Instead of hard-coding colors on each
element, you name semantic tokens — primary, surface, on-surface — and reference them everywhere. Change
a token once and it updates across every screen, in both light and dark mode.
The theme is provided by the nativephp/native-ui plugin (which ships the components), but it governs the whole
app, so it's the visual contract for everything you build.
Publishing the config#
Publish the theme file to your app to customize it:
php artisan vendor:publish --tag=native-ui-config
That writes config/native-ui.php, which holds a theme array of light and dark token blocks plus radii and
font settings.
Tokens#
Colors come in pairs: a surface token and its on- counterpart — the color of content (text, icons) placed
on that surface. Pairing them this way is what keeps contrast correct across light and dark.
| Token | Used for |
|---|---|
primary / on-primary |
Filled buttons, active states, key accents |
secondary / on-secondary |
Muted/secondary actions |
surface / on-surface |
Cards, sheets, dialogs |
background / on-background |
The page root behind everything |
surface-variant / on-surface-variant |
Filled text fields, muted tonal surfaces / hint text |
outline |
Neutral borders — fields, dividers, cards |
destructive / on-destructive |
Destructive actions (variant="destructive") |
accent / on-accent |
Highlights, badges, emphasis outside primary |
Plus non-color tokens: radius-sm|md|lg|full, font-sm|md|lg|xl, and font-family (System resolves to San
Francisco on iOS / Roboto on Android; set a family name to load a custom font).
Using tokens in a screen#
Reference any token from Blade with the theme-{token} class suffix, on background, text, and border utilities:
<native:column class="bg-theme-surface border border-theme-outline rounded-2xl p-4"> <native:text class="text-theme-on-surface text-lg font-bold">Balance</native:text> <native:text class="text-theme-on-surface-variant">Updated just now</native:text></native:column>
Because the tokens carry the theme, you rarely reach for raw palette classes like bg-slate-800 — and your UI
stays correct when the system switches between light and dark.
Light & dark#
The dark block is auto-derived from light by luminance inversion, so a light-only theme already adapts.
Specify any token under dark to override just that value:
'theme' => [ 'light' => [ 'primary' => '#0F766E', // ... ], 'dark' => [ // Everything else derives from light; only override what you want to tune. 'primary' => '#14B8A6', ],],
Appearance in PHP#
The renderers switch between the light and dark token blocks automatically as the system appearance changes.
When you need the current appearance in PHP — to pick an asset, resolve a token, or branch logic — read it from
the System facade:
use Native\Mobile\Facades\System; System::appearance(); // 'light' | 'dark'System::isDarkMode(); // boolSystem::isLightMode(); // bool
Global helper functions wrap the same calls for terse use in Blade and components:
isDark(); // boolisLight(); // bool
Resolving a token in PHP#
The theme() helper returns a token's value for the current appearance — the PHP-side counterpart to the
bg-theme-* / text-theme-* classes, reading from the same config/native-ui.php theme config:
theme('primary'); // config('native-ui.theme.dark.primary') in dark mode, light otherwisetheme('primary', '#0F766E'); // fall back to a value when the token is unset
Pass a fallback when a setter needs a non-null string — theme() returns your default when the key is missing
(or native-ui isn't installed).
Reacting to changes#
When the OS flips the theme — a Control Center toggle, or the sunset auto-switch — the AppearanceChanged event
fires. React in a component with #[On]; $mode is 'light' or 'dark':
use Native\Mobile\Attributes\On;use Native\Mobile\Events\System\AppearanceChanged; #[On(AppearanceChanged::class)]public function appearanceChanged(string $mode): void{ // re-resolve anything appearance-dependent}
AppearanceChanged also dispatches globally, so code anywhere in the app can listen — not just the active
screen:
use Illuminate\Support\Facades\Event;use Native\Mobile\Events\System\AppearanceChanged; Event::listen(AppearanceChanged::class, function (AppearanceChanged $e) { // $e->mode});
The query side (System::appearance() / isDark()) is kept in sync off this event, so reads stay fresh without
a bridge round-trip.
Runtime theming#
For per-tenant or user-selectable themes, merge tokens at runtime from a service provider with Theme::merge().
It deep-merges over the config values, so you only pass what changes:
use Nativephp\NativeUi\Theme; Theme::merge([ 'light' => ['primary' => $tenant->brandColor],]);
Theme::merge() layers on top of config/native-ui.php; Theme::reset() returns to the config defaults.
in no time