# PhoneInput

International phone field with searchable country picker. Default country is auto-detected from the user's IANA timezone (e.g. KSA for Asia/Riyadh). Numbers always render LTR even inside RTL regions.

[View examples](https://marsam.rahhal.app/components/phone-input) · [Agent index](https://marsam.rahhal.app/llms.txt)

**Import:** `@rahhal/ui/phone-input`

**Category:** `auth`

## Props

| prop | type | purpose |
| --- | --- | --- |
| value | string | current value in E.164 form (e.g. `+9665XXXXXXXX`) or empty string |
| onChange | (value: string) => void | fires with the new E.164 value (or `""` if cleared) |
| disabled | boolean | disable interaction; styled with reduced opacity |
| defaultCountry | Country | override the auto-detected default (ISO 3166-1 alpha-2); falls back to timezone, then `US` |
| placeholder | string | custom placeholder for the number field |
| id | string | forwarded to the inner `<input>` |
| autoFocus | boolean | focus the number field (not the country trigger) on mount — pair with sign-in screens so visitors start typing without an extra tap |
| labels | Labels | localized country names — pass `import('react-phone-number-input/locale/ar.json')` for Arabic, defaults to English |
| searchPlaceholder | string | placeholder inside the country-picker search input — defaults to `"Search country..."` |
| searchEmpty | string | message shown when the search returns no countries — defaults to `"No country found."` |

## Country picker

Clicking the flag trigger opens a Popover containing a searchable list of every country. Each row shows the flag, country name, and dial code (e.g. `🇸🇦 Saudi Arabia +966`). The search input matches **all three** in parallel — typing `966`, `saudi`, or `sa` all surface Saudi Arabia. Keyboard navigation (↑ / ↓ / Enter / Esc) works out of the box via cmdk.

The picker is a UI substitution via the upstream library's documented `countrySelectComponent` extension point — the parsing / validation / formatting layer is untouched. Removing the prop restores the library's native `<select>` for instant rollback.

## Localization

Country names default to English. To render them in Arabic (or any other supported locale), pass the lib's locale JSON:

```tsx
import arLocale from "react-phone-number-input/locale/ar.json";

<PhoneInput
  value={phone}
  onChange={setPhone}
  labels={arLocale}
  searchPlaceholder="ابحث عن دولة..."
  searchEmpty="لا توجد دولة مطابقة."
/>
```

The locale file adds ~6 KB gzip per language — only pull it in apps that actually ship that language.

## Required CSS

Apps that render `<PhoneInput>` directly must import the stylesheet once at their entry point:

```ts
import "@rahhal/ui/phone-input.css";
```

Apps that only render `<PhoneInput>` transitively via `<LoginScreen>` from `@rahhal/auth/react` do **not** need this import — `<LoginScreen>` pulls it in for them.

## Dark mode

The stylesheet uses the same `hsl(var(--background))` / `hsl(var(--foreground))` tokens as `<Input>`, so the field reads correctly in both themes. Browser autofill (`-webkit-autofill`) is overridden so saved-phone autofill keeps the dark-mode surface — without that override Chrome / Safari would re-paint the field pale yellow with near-black text.

## RTL

The surrounding chrome (trigger button, popover, country list) follows the document direction. The number itself always renders LTR via `direction: ltr; text-align: start;` on the inner input — phone numbers are universally read LTR even in Arabic UIs.

## Default-country detection

On mount, the component reads `Intl.DateTimeFormat().resolvedOptions().timeZone` and maps it to a country (e.g. `Asia/Riyadh` → `SA`, `America/New_York` → `US`). Unknown timezones fall back to `US`. Pass `defaultCountry` to override.
