Skip to content

Text, input, and controls

Text editing, selection, cursor movement, keyboard interaction and press feedback stay in native controls. PHP receives semantic events instead of driving each platform frame.

use Pam\Native\TextEllipsizeMode;
use Pam\Native\UI\Text;
$title = Text::make('A long native title')
->numberOfLines(2)
->ellipsize(TextEllipsizeMode::Tail)
->allowFontScaling()
->maxFontSizeMultiplier(1.6)
->selectable();

Text also supports automatic fit, platform break strategy, hyphenation and data detection. Keep font scaling enabled unless a tightly constrained decorative label has an accessible alternative.

Place TTF or OTF files in the application source, for example assets/fonts/Brand-Bold.ttf, and declare the application type system once:

src/app.css
@font-face {
font-family: "Brand";
src: url("asset://assets/fonts/Brand-Bold.ttf");
font-weight: 700;
}
.brand-title {
font-weight: 700;
font-size: 20px;
}
<template>
<Text class="brand-title">Brand title</Text>
</template>

PAM Native resolves @font-face aliases before the typed tree crosses into native code and loads the selected file below the packaged pam/ asset root. Add one face per numeric weight and optional italic variant; ordinary font-family, font-weight, and font-style declarations select the closest match. Since 0.5.71, the logical family inherits through layout containers, so a descendant may change only its weight and still select the correct face. Paths with traversal, empty segments, or unsupported extensions are rejected. A normal family name such as sans-serif continues to resolve through Android.

Since 0.5.84, the Rust layout engine reads each selected TTF/OTF once and caches normalized glyph advances shared by every text node using that face. Intrinsic width, wrapping, accessibility font scaling, and flex centering use the same packaged font the platform renders before the first mount. This avoids clipped labels and font-specific centering drift without a UI-thread measure or post-render correction.

An inline fontFamily="asset://…" attribute remains valid for a one-off element. A scoped @font-face and semantic class are preferable when a family is reused because paths stay out of the template structure. The alias compiles to a typed native property and adds no font registry or runtime selector work.

PAM Native 0.5.54 prepends src/app.css while compiling every component. Local <style scoped> rules may override it, and either sheet may split files with relative @import. Nothing creates a runtime CSS cascade, selector pass, or WebView.

letterSpacing uses logical points, matching the rest of the text contract. PAM Native 0.5.43 converts that value to Android’s font-relative em unit using the authored fontSize, so letterSpacing="0.6" remains a subtle tracking adjustment rather than 60% of a glyph width.

Packaged fonts use their cached face metrics; installed and missing families fall back to PAM’s allocation-free estimator. A centered Row containing Text, gap, and an icon therefore centers the visible control content, including when the Android font scale changes. PAM Native 0.5.85 also rounds the absolute start and end edges of Android frames before deriving pixel extents. Text and icon children that share a logical center therefore retain the same physical center even on fractional density screens.

use Pam\Native\KeyboardType;
use Pam\Native\ReturnKeyType;
use Pam\Native\UI\Input;
$email = Input::make($this->state->email)
->placeholder('you@example.com')
->keyboard(KeyboardType::Email)
->returnKey(ReturnKeyType::Done)
->maxLength(254)
->onChange(fn (string $value) => $this->state->email = $value)
->onSubmit(fn () => $this->submit());
<Input
bind:value="$email"
keyboardType="email"
returnKeyType="done"
sync="debounced"
placeholder="you@example.com"
on:submit="submit"
/>

TextInput is the compatibility entry point for Input. Inputs additionally support multiline and secure entry, autofill hints, capitalization, autocorrect, selection, content-size/key events and native-owned immediate or debounced state.

keyboardType accepts PAM’s concise text, email, number, phone, decimal, and url values. PAM Native 0.5.79 also accepts the familiar React Native aliases default, email-address, number-pad, numeric, phone-pad, decimal-pad, ascii-capable, numbers-and-punctuation, ascii-capable-number-pad, name-phone-pad, twitter, web-search, and visible-password.

$save = Button::make('Save')
->loading($this->state->saving)
->onPress(fn () => $this->save());
$card = Pressable::make(Text::make('Open project'))
->hitSlop(12)
->pressRetentionOffset(20)
->delayLongPress(450)
->pressedOpacity(0.72)
->onPress($open)
->onLongPress($showMenu);
$enabled = Toggle::make($this->state->enabled)
->trackColors(0xFFCBD5E1, 0xFF2563EB)
->thumbColor(0xFFFFFFFF)
->onToggle(fn (bool $value) => $this->state->enabled = $value);

NativeSwitch exposes the same native switch family. TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, and TouchableNativeFeedback exist for authoring compatibility; new code should prefer Pressable when it needs the complete press lifecycle.