# Getting Started — orn-ui

## Compatibility

Verified on Expo SDK 54, 55, 56 and 57 — every one of them, not just the
newest. Each SDK gets its own sandbox with that release's exact
react-native and react, and both the type check and the full test suite
run against it in CI. Bare React Native works the same way: nothing in
the library imports Expo.

| Expo SDK | react-native | react |
|---|---|---|
| 54 | `0.81.5` | `19.1.0` |
| 55 | `0.83.10` | `19.2.0` |
| 56 | `0.85.3` | `19.2.3` |
| 57 | `0.86.2` | `19.2.3` |

Declared as peerDependencies: `react-native >=0.81.0` and
`react >=19.1.0`. `react-native-safe-area-context >=5.4.0` is optional —
only `orn-ui/safe-area` imports it.

No native build required: orn-ui ships no native modules, so it runs
inside Expo Go on every SDK listed here.

## 1. Install

```bash
pnpm add orn-ui
# or, per component, no npm dependency:
npx orn-ui add button
```

## 2. Wrap your app in UIProvider

UIProvider is the single entry point of the library. It resolves the
active theme (system or manual override) and injects icons, safe-area
insets and default labels to the whole tree. Mount it once, above your
navigator.

```tsx
import { UIProvider } from 'orn-ui';

export default function App() {
  return (
    <UIProvider>
      {/* your navigator / screens go here */}
    </UIProvider>
  );
}
```

> ⚠️ Every orn-ui component and hook (useColors, useTheme, Button, Input,
> all of them) throws `this hook must be used within a <UIProvider>` if
> rendered outside one. There is no fallback and no silent default — it
> fails loudly at runtime, not at build time.

### UIProvider props

| Name | Type | Default | Description |
|---|---|---|---|
| `children` | `React.ReactNode` | — | Your app — mounted once, above the navigator. |
| `theme?` | `ThemePair` | defaultTheme | Light/dark pair. Build one with createTheme(). |
| `defaultMode?` | `'system' | 'light' | 'dark'` | 'system' | Initial mode, uncontrolled. |
| `mode?` | `'system' | 'light' | 'dark'` | — | Controlled mode — pass with onModeChange to manage it yourself. |
| `onModeChange?` | `(mode: ThemeMode) => void` | — | Called on toggle when mode is controlled. |
| `icons?` | `IconRenderer` | renderDefaultIcon | Icon renderer. Defaults to the zero-dep glyphs bundled with orn-ui. |
| `insets?` | `EdgeInsets` | zeroInsets | Safe-area insets, typically from useSafeAreaInsets(). |
| `labels?` | `Partial<Labels>` | defaultLabels | Override any built-in string (Close, Cancel, Search…). |
| `allowFontScaling?` | `boolean` | false | Off by default, for parity across the library. |

### Recommended: SafeAreaUIProvider

UIProvider defaults insets to {top:0,bottom:0,left:0,right:0} — harmless
until you open a full-screen Modal (slides under the notch) or a
BottomSheet (sits flush against the gesture bar). SafeAreaUIProvider is
UIProvider with useSafeAreaInsets() already wired in.

It lives in its own subpath (orn-ui/safe-area), on purpose: it is the
only file in the library that imports a third-party package
(react-native-safe-area-context). Nothing in the main orn-ui entry
point references it, so a plain `import { Button } from 'orn-ui'`
never pulls it in — the rest of the library stays zero-dependency.

```bash
pnpm add react-native-safe-area-context
```

```tsx
import { SafeAreaUIProvider } from 'orn-ui/safe-area';

export default function App() {
  return (
    <SafeAreaUIProvider>
      {/* your navigator / screens go here */}
    </SafeAreaUIProvider>
  );
}
```

Already mounting a `<SafeAreaProvider>` higher up (some React
Navigation templates do)? Pass `mountSafeAreaProvider={false}` — a
nested provider measures its own View's frame, not the window's, so
insets read zero from inside it.

#### SafeAreaUIProviderProps

Plus every UIProvider prop above except `insets` (still accepted, but
optional — it overrides the measured value instead of being required).

| Name | Type | Default | Description |
|---|---|---|---|
| `insets?` | `EdgeInsets` | measured via useSafeAreaInsets() | Explicit override — wins over the measured value. Useful for tests, Storybook, or a screen that does not fill the window. |
| `mountSafeAreaProvider?` | `boolean` | `true` | Also mount `<SafeAreaProvider>`. Set false if your app already has one above. |

## 3. Use components

Every component page works as shown from here — copy a variant snippet,
it renders. Full catalog: https://orn-ui-docs.vercel.app/llms.txt
