# RootNative UI — Full API Reference > Design-system agnostic component library for React Native — ships with Material Design 3 > Versions: `@rootnative/core` 0.0.0-alpha.1 · `@rootnative/components` 0.0.0-alpha.1 · `@rootnative/icons` 0.0.0-alpha.1 · `@rootnative/cli` 0.0.0-alpha.1 > Requirements: React Native 0.81+, React 19+, Expo SDK 54+ > Peer deps: `react-native-safe-area-context >=4`, `react-native-reanimated >=4`, `react-native-worklets >=0.5` (Reanimated 4 runtime — also enable the `react-native-worklets/plugin` Babel plugin; bundled in `babel-preset-expo` on Expo SDK 54) > Optional peer deps: `@expo/vector-icons >=14` (only needed for icon props) --- ## Quick Start (new project) ```bash npx rootnative create my-app cd my-app npx expo start ``` The `create` command scaffolds a ready-to-run Expo project with `ThemeProvider`, example components (Button, Card), Expo Router, and all dependencies pre-configured. Interactive prompts: project name, display name (shown on home screen), package manager (npm/yarn/pnpm/bun), install dependencies. Options: - `-y, --yes` — Skip all prompts, use defaults (name: `my-app`, pm: `npm`, auto-install) Pass name directly: `npx rootnative create my-app` ## Installation (existing project) ```bash pnpm add @rootnative/core @rootnative/components @expo/vector-icons react-native-safe-area-context react-native-reanimated react-native-worklets ``` Reanimated 4 runs on `react-native-worklets` (installed above). Expo SDK 54 bundles its Babel plugin — nothing to configure. On bare React Native, add `'react-native-worklets/plugin'` last in `babel.config.js` plugins. --- ## CLI (`rootnative`) ### Commands #### `rootnative create [name]` Create a new project with RootNative UI pre-configured. Fetches the quickstart template, applies your project name to `package.json` and `app.json`, and optionally installs dependencies. ```bash npx rootnative create # Interactive npx rootnative create my-app # With name npx rootnative create my-app -y # Non-interactive, accept defaults ``` Options: - `-y, --yes` — Skip prompts and use defaults - `-t, --template ` — Template to use (`blank`, `with-router`) - `--package-manager ` — Package manager to use (`npm`, `yarn`, `pnpm`, `bun`) #### `rootnative init` Copy-paste workflow — copies component source files into your project. The theme system (`@rootnative/core`) stays as an npm dependency. Initialize project. Detects project type (Expo/RN), package manager, and tsconfig path aliases. ```bash npx rootnative init # Interactive npx rootnative init -y # Non-interactive, accept defaults npx rootnative init -y --components-alias "~/ui" --lib-alias "~/utils" ``` Options: - `-y, --yes` — Skip all prompts, use detected defaults. Overwrites existing config. Auto-installs `@rootnative/core`. - `--components-alias ` — Components install path. Default: `@/components/ui` (or `~/components/ui` if `~/*` alias detected in tsconfig) - `--lib-alias ` — Utility files path. Default: `@/lib` (or `~/lib` if `~/*` alias detected) - `--package-manager ` — Package manager to use (`npm`, `yarn`, `pnpm`, `bun`) Creates `rootnative.json`: ```json { "$schema": "https://rootnative.github.io/ui/schema.json", "aliases": { "components": "@/components/ui", "lib": "@/lib" }, "registryUrl": "https://raw.githubusercontent.com/rootnative/ui", "registryVersion": "main" } ``` #### `rootnative add ` Add components to your project. Resolves dependency graph, copies files with rewritten imports, installs npm deps. ```bash npx rootnative add button npx rootnative add card chip text-field npx rootnative add appbar # auto-adds icon-button + typography ``` Options: - `-f, --force` — Overwrite existing components - `-d, --dry-run` — Preview without writing files - `--package-manager ` — Package manager to use (`npm`, `yarn`, `pnpm`, `bun`) #### `rootnative update [components...]` Update installed components to latest registry version. Options: - `-a, --all` — Update all installed components - `-d, --dry-run` — Show diff without applying #### `rootnative upgrade` Upgrade `@rootnative/core` to the latest published version and install any new peer dependencies. ```bash npx rootnative upgrade # Interactive — shows plan and prompts before installing npx rootnative upgrade -y # Non-interactive — skip confirmation ``` Options: - `-y, --yes` — Skip confirmation prompt - `--package-manager ` — Package manager to use (`npm`, `yarn`, `pnpm`, `bun`) What it does: 1. Reads the installed `@rootnative/core` version from `node_modules` 2. Fetches the latest version from the npm registry 3. Compares peer dependencies between the installed and latest versions 4. Shows a plan: version bump, new required peer deps, changed version ranges, removed deps 5. Upgrades `@rootnative/core` and installs any new required peer dependencies in one step 6. Reports optional peer deps that aren't installed (does not auto-install optional deps) 7. Lists peer deps that are no longer required so you can remove them manually #### `rootnative list` Show available components with install status. #### `rootnative doctor` Check project health: config validity, core installation, RN version, file integrity, peer deps. --- ## @rootnative/utils Shared utilities used by `@rootnative/components` and available for custom component development. ```tsx import { alphaColor, blendColor, elevationStyle, getMaterialCommunityIcons, transformOrigin, selectRTL } from '@rootnative/utils' ``` ### Color helpers - `alphaColor(color: string, alpha: number): string` — Converts hex color to `rgba(...)` with the given alpha (clamped 0–1). Returns the input unchanged if parsing fails. - `blendColor(base: string, overlay: string, overlayAlpha: number): string` — Blends two hex colors by mixing RGB channels at the given overlay opacity. Returns `rgb(...)`. ### Elevation - `elevationStyle(level: ElevationLevel): ViewStyle` — Converts an MD3 elevation level into platform-appropriate shadow styles. Uses `boxShadow` on web, `shadow*` + `elevation` on native. > **Gotcha:** the return shape differs by platform. Both are typed as `ViewStyle`, but spreading the result and overriding individual `shadow*` props silently no-ops on web — the shadow is baked into the `boxShadow` string. To customize, modify the `ElevationLevel` before calling, or branch on `Platform.OS`. ### RTL support - `transformOrigin(vertical?: 'top' | 'center' | 'bottom'): string` — Returns `"left top"` or `"right top"` based on RTL layout direction. Used for label animations. - `selectRTL(ltr: T, rtl: T): T` — Picks a value based on layout direction. ### Icon resolver - `getMaterialCommunityIcons()` — Lazily resolves `MaterialCommunityIcons` from `@expo/vector-icons` at render time. Throws with install instructions if the package is missing. ### Test helper (subpath export) ```tsx import { renderWithTheme } from '@rootnative/utils/test' ``` - `renderWithTheme(ui: ReactElement, options?: RenderOptions)` — Wraps `@testing-library/react-native`'s `render` with `ThemeProvider`. --- ## @rootnative/core ### ThemeProvider Wrap your app root to supply the theme to all components. Works with any design system — Material Design 3 or custom themes. Defaults to the MD3 light theme when no theme is provided. ```tsx import { ThemeProvider, darkTheme } from '@rootnative/core' // Light theme (default) {children} // Dark theme {children} // Custom theme import { lightTheme } from '@rootnative/core' import type { Theme } from '@rootnative/core' const custom: Theme = { ...lightTheme, colors: { ...lightTheme.colors, primary: '#006A6A', onPrimary: '#FFFFFF' }, } {children} ``` Props: - `theme?: BaseTheme` — Theme object. Default: `lightTheme` (MD3) - `children: ReactNode` ### useTheme() Returns the current theme from the nearest `ThemeProvider`. Without a type parameter, returns the Material Design 3 `Theme`. Pass a custom theme type for typed access to your design system's tokens. ```tsx import { useTheme } from '@rootnative/core' // Material Design 3 (default) const theme = useTheme() // theme.colors.primary, theme.typography.bodyMedium, theme.spacing.md, etc. // Custom design system const theme = useTheme() // theme.colors.brand, theme.typography.heading, etc. ``` ### defineTheme(theme) Identity function that validates a custom theme object against `BaseTheme`. Provides type-checking and autocompletion. ```tsx import { defineTheme } from '@rootnative/core' import type { BaseTheme, TextStyle } from '@rootnative/core' interface MyColors { [key: string]: string; brand: string; background: string; text: string } interface MyTypography { [key: string]: TextStyle; heading: TextStyle; body: TextStyle } interface MyTheme extends BaseTheme { colors: MyColors; typography: MyTypography } const myTheme = defineTheme({ colors: { brand: '#FF6B00', background: '#FFFFFF', text: '#1A1A1A' }, typography: { heading: { fontFamily: 'Inter', fontSize: 24, fontWeight: '700', lineHeight: 32, letterSpacing: 0 }, body: { fontFamily: 'Inter', fontSize: 16, fontWeight: '400', lineHeight: 24, letterSpacing: 0.5 }, }, shape: { roundness: 1, cornerNone: 0, cornerExtraSmall: 4, cornerSmall: 8, cornerMedium: 12, cornerLarge: 16, cornerExtraLarge: 28, cornerFull: 9999 }, spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 }, stateLayer: { pressedOpacity: 0.12, focusedOpacity: 0.12, hoveredOpacity: 0.08, disabledOpacity: 0.38 }, elevation: { ... }, motion: { ... }, }) ``` ### createMaterialTheme(seedColor, options?) Generates a complete MD3 light and dark theme from a single seed color. Uses Google's HCT color space via `@material/material-color-utilities` for spec-compliant palette generation. Defaults are byte-identical to the upstream MD3 `material-color-utilities` library — pure spec output. **Spec-aligned options:** - `variant?: 'tonalSpot' | 'neutral' | 'vibrant' | 'expressive' | 'fidelity' | 'content' | 'monochrome' | 'rainbow' | 'fruitSalad'` — MD3 scheme variant (default `'tonalSpot'`, the Material You default on Android 12+). Each is a spec-defined recipe for deriving the palette. Use `'monochrome'` for spec-legal pure-grey themes, `'vibrant'` for high-colorfulness, etc. - `contrastLevel?: 'standard' | 'medium' | 'high'` — MD3 contrast preset (default `'standard'`). Maps to MD3 contrast values `0 / 0.5 / 1.0`. Use `'medium'` or `'high'` for WCAG AAA / low-vision modes. - `fontFamily?: string` — Custom font family applied to all 15 typography styles. When omitted, platform defaults are used (Roboto on Android, System on iOS). - `roundness?: number` — Global corner-radius multiplier. `0` = sharp corners, `1` = default MD3 (default), `2` = double rounding. Does not affect `cornerNone` or `cornerFull`. **Explicit overrides (NOT part of MD3 spec — use only when no built-in `variant` covers your case):** - `surfaceTone?: 'spec' | 'neutral'` — Default `'spec'` keeps the variant's neutral palette as-is. `'neutral'` rebuilds the neutral and neutralVariant palettes with chroma `0` while leaving primary/secondary/tertiary untouched. Use this for a colorful brand + OLED-near-black surfaces. For a fully spec-legal monochrome theme prefer `variant: 'monochrome'`. - `seedAdjustments?: { primary?: number, secondary?: number }` — Per-palette HCT chroma overrides. Same hue, fresh chroma. The variant defaults are spec-defined (TonalSpot uses `primary: 36` / `secondary: 16`); only override when no `variant` matches your brand. Try `variant: 'vibrant'` first. **Separate entry point** — keeps the ~60 kB dependency out of the main bundle: ```tsx import { createMaterialTheme } from '@rootnative/core/create-theme' import { ThemeProvider } from '@rootnative/core' // Pure MD3 default (TonalSpot variant) const { lightTheme, darkTheme } = createMaterialTheme('#006A6A') // Switch MD3 variant createMaterialTheme('#006A6A', { variant: 'vibrant' }) // Spec-legal monochrome theme createMaterialTheme('#006A6A', { variant: 'monochrome' }) // High-contrast accessibility preset createMaterialTheme('#006A6A', { contrastLevel: 'high' }) // Custom font + sharp corners createMaterialTheme('#006A6A', { fontFamily: 'Inter', roundness: 0 }) // Override: keep colorful primary/secondary, flatten surfaces to pure grey createMaterialTheme('#006A6A', { surfaceTone: 'neutral' }) // Override: per-palette chroma createMaterialTheme('#006A6A', { seedAdjustments: { primary: 60, secondary: 32 } }) // Use in provider {children} // Or with dark mode {children} ``` Returns: `{ lightTheme: Theme, darkTheme: Theme }` The generated themes include all 69 MD3 color roles plus shared tokens (typography, shape, spacing, stateLayer, elevation, motion, topAppBar). Requires `@material/material-color-utilities` as a peer dependency: `npm install @material/material-color-utilities` ### applyRoundness(roundness) Scales the MD3 corner radius tokens by a multiplier. Returns a complete `Shape` object. `cornerNone` (0) and `cornerFull` (999) are never affected. - `roundness: 0` — sharp corners (all intermediate tokens become 0) - `roundness: 1` — default MD3 values - `roundness: 2` — double the rounding ```tsx import { applyRoundness, lightTheme } from '@rootnative/core' // Use with spread to override shape on an existing theme const sharpTheme = { ...lightTheme, shape: applyRoundness(0) } // Or with defineTheme import { defineTheme } from '@rootnative/core' const theme = defineTheme({ ...lightTheme, shape: applyRoundness(0.5) }) ``` ### material preset Grouped object containing all Material Design 3 theme values. ```tsx import { material } from '@rootnative/core' material.lightTheme // MD3 light theme material.darkTheme // MD3 dark theme material.defaultTopAppBarTokens ``` ### Theme type hierarchy - `BaseTheme` — Generic base interface. All design systems extend this. Has `colors: Record`, `typography: Record`, plus shape, spacing, stateLayer, elevation, motion. - `Theme` — Material Design 3 theme. Extends `BaseTheme` with 69 MD3 color roles, 15 typography variants, and optional `topAppBar` tokens. `MaterialTheme` is an identical alias (same type) — use it to disambiguate in multi-design-system codebases. ### Theme structure ``` BaseTheme { colors: Record typography: Record shape: Shape — roundness, cornerNone, cornerExtraSmall, cornerSmall, cornerMedium, cornerLarge, cornerExtraLarge, cornerFull spacing: Spacing — xs, sm, md, lg, xl elevation: Elevation — level0..level3 (shadow properties) stateLayer: StateLayer — pressedOpacity, focusedOpacity, hoveredOpacity, disabledOpacity motion: Motion — duration, easing, and spring tokens } Theme extends BaseTheme { colors: Colors — 69 MD3 color roles typography: Typography — 15 type scale variants (displayLarge..labelSmall) topAppBar?: TopAppBarTokens } ``` Colors: primary, onPrimary, primaryContainer, onPrimaryContainer, primaryFixed, onPrimaryFixed, primaryFixedDim, onPrimaryFixedVariant, secondary (same pattern), tertiary (same pattern), error, onError, errorContainer, onErrorContainer, background, onBackground, surface, surfaceDim, surfaceBright, surfaceContainerLowest, surfaceContainerLow, surfaceContainer, surfaceContainerHigh, surfaceContainerHighest, onSurface, surfaceVariant, onSurfaceVariant, outline, outlineVariant, surfaceTint, shadow, scrim, inverseSurface, inverseOnSurface, inversePrimary Typography variants: displayLarge, displayMedium, displaySmall, headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium, titleSmall, bodyLarge, bodyMedium, bodySmall, labelLarge, labelMedium, labelSmall — each with fontFamily, fontSize, fontWeight, lineHeight, letterSpacing ### useBreakpoint() Returns the current MD3 window size class. Reactively updates on resize. ```tsx import { useBreakpoint } from '@rootnative/core' const bp = useBreakpoint() // 'compact' (0-599) | 'medium' (600-839) | 'expanded' (840-1199) | 'large' (1200-1599) | 'extraLarge' (1600+) ``` ### useBreakpointValue(values) Returns a value based on the current breakpoint with cascade fallback. ```tsx import { useBreakpointValue } from '@rootnative/core' const columns = useBreakpointValue({ compact: 1, medium: 2, expanded: 4 }) // compact → 1, medium → 2, expanded/large/extraLarge → 4 ``` Type: `useBreakpointValue(values: Partial> & Record<'compact', T>): T` --- ## @rootnative/components Import via subpath (preferred): `import { X } from '@rootnative/components/x'` Import via root: `import { X } from '@rootnative/components'` ### Component override pattern All interactive components follow a 3-tier override system. Merge order: theme defaults → variant → semantic props → style props (last wins). Standard override props on interactive components: - `containerColor?: string` — Root container background. Hover/press state-layer colors auto-derived. - `contentColor?: string` — Content (label + icons) color. State-layer colors auto-derived. - `labelStyle?: StyleProp` — Text-specific overrides (does not affect icons). - `style` — Root container style. Disabled state always uses standard MD3 disabled treatment (38% onSurface) regardless of overrides. --- ### Typography ```tsx import { Typography } from '@rootnative/components/typography' Hello ``` Props: - `children: ReactNode` — Content to display. Accepts strings, numbers, or nested elements. - `variant?: 'displayLarge' | 'displayMedium' | 'displaySmall' | 'headlineLarge' | 'headlineMedium' | 'headlineSmall' | 'titleLarge' | 'titleMedium' | 'titleSmall' | 'bodyLarge' | 'bodyMedium' | 'bodySmall' | 'labelLarge' | 'labelMedium' | 'labelSmall'` — Default: `'bodyMedium'`. MD3 type scale role. Controls font size, weight, line height, and letter spacing. - `color?: string` — Override the text color. Takes priority over `style.color`. Defaults to the theme's `onSurface` color. - `style?: StyleProp` — Additional text styles. Can override the default theme color via `style.color` when no `color` prop is set. - `as?: ComponentType` — Default: `Text`. Override the underlying text component (e.g. Animated.Text). - Inherits `TextProps` (except `children`, `style`) --- ### Button ```tsx import { Button } from '@rootnative/components/button' ``` Props: - `children: string` — Text label rendered inside the button. - `variant?: 'filled' | 'elevated' | 'outlined' | 'text' | 'tonal'` — Default: `'filled'`. Visual variant. Controls background, border, and text color. - `leadingIcon?: IconSource` — Icon rendered before the label. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. - `trailingIcon?: IconSource` — Icon rendered after the label. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. - `iconSize?: number` — Default: `18`. Size of leading and trailing icons in dp. Used when resolving string icon names or invoking the render-function form. Pre-rendered elements are not resized. - `containerColor?: string` — Override the container (background) color. State-layer colors (hover, press) are derived automatically. - `contentColor?: string` — Override the content (label and icon) color. State-layer colors are derived automatically when no containerColor is set. - `labelStyle?: StyleProp` — Additional style applied to the label text. - `style?: StyleProp` — Style applied to the root container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling. --- ### ButtonGroup ```tsx import { ButtonGroup } from '@rootnative/components/button-group' // Single-select connected group (replaces deprecated MD3 segmented button). // Multi-select standard group. // Action-only group — no selection state. ``` --- ### IconButton ```tsx import { IconButton } from '@rootnative/components/icon-button' ``` Props: - `icon: IconSource` — Icon to display. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. - `selectedIcon?: IconSource` — Icon to display when `selected` is `true` (toggle mode). - `iconColor?: string` — Overrides the automatic icon color derived from the variant and state. - `contentColor?: string` — Override the content (icon) color. Takes precedence over `iconColor` when both are provided. - `containerColor?: string` — Override the container (background) color. State-layer colors (hover, press) are derived automatically. - `style?: StyleProp` — Style applied to the root container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling. - `onPress?: () => void` — Called when the button is pressed. - `disabled?: boolean` — Default: `false`. Disables the button. - `variant?: 'filled' | 'tonal' | 'outlined' | 'standard'` — Default: `'filled'`. Visual style variant. - `selected?: boolean` — Enables toggle mode. The button changes appearance based on selected/unselected state. - `size?: 'small' | 'medium' | 'large'` — Default: `'medium'`. Physical size of the touch target and icon container. - `accessibilityLabel: string` — Required — icon-only buttons must have a label for screen readers. --- ### FAB ```tsx import { FAB } from '@rootnative/components/fab' // Icon-only — accessibilityLabel required // Extended — label doubles as the accessible name ``` Props: - `icon: IconSource` — Icon to display. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. - `label?: string` — Optional text label. When provided, renders an extended FAB (56dp tall) and `size` is ignored. The label is also used as the accessible name unless `accessibilityLabel` is also set. - `variant?: 'primary' | 'secondary' | 'tertiary' | 'surface'` — Default: `'primary'`. Color variant. Controls container and content colors. - `size?: 'small' | 'medium' | 'large'` — Default: `'medium'`. Physical size. Ignored when `label` is set. - `containerColor?: string` — Override the container (background) color. State-layer colors (hover, press) are derived automatically. - `contentColor?: string` — Override the content (icon + label) color. State-layer colors are derived automatically when no `containerColor` is set. - `labelStyle?: StyleProp` — Style applied to the label text. Only used when `label` is set. - `style?: StyleProp` — Style applied to the root container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling. - `onPress?: () => void` — Called when the FAB is pressed. - `disabled?: boolean` — Default: `false`. Disables the FAB. - `accessibilityLabel?: string` — Accessible name for screen readers. Required for icon-only FABs. When `label` is set, defaults to the label and may be omitted (override only if you need a more descriptive name). --- ### AppBar ```tsx import { AppBar } from '@rootnative/components/appbar' ``` Props: - `title: string` — Title text displayed in the bar. - `variant?: 'small' | 'center-aligned' | 'medium' | 'large'` — Default: `'small'`. Layout variant. - `colorScheme?: 'surface' | 'surfaceContainerLowest' | 'surfaceContainerLow' | 'surfaceContainer' | 'surfaceContainerHigh' | 'surfaceContainerHighest' | 'primary' | 'primaryContainer'` — Default: `'surface'`. Color scheme that determines the default container and content colors. `containerColor` and `contentColor` props override these defaults. - `canGoBack?: boolean` — Default: `false`. When `true`, renders a back button in the leading slot. - `onBackPress?: () => void` — Called when the auto-rendered back button is pressed. - `insetTop?: boolean` — Default: `false`. When `true`, wraps the bar in a SafeAreaView that handles the top inset. - `elevated?: boolean` — Default: `false`. When `true`, applies the MD3 on-scroll tonal shift: the container color animates from its resting color to its elevated color (`surface` → `surfaceContainer` for the default `'surface'` color scheme). No shadow is added. Only the `'surface'` scheme defines a distinct elevated color; other schemes keep the same container color. Toggle this from your scroll handler when content scrolls under the bar. When `scrollOffset` is provided on a `'medium'`/`'large'` bar, the tonal shift is driven from the scroll position automatically and this prop only forces the elevated color on. - `scrollOffset?: SharedValue` — Vertical scroll offset (in px) of the content scrolling under the bar, as a Reanimated shared value. Pass `scrollY` from `@rootnative/inertia`'s `useScroll()` and give the matching `onScroll` to your scroll view: ```tsx const { scrollY, onScroll } = useScroll() ``` Drives the MD3 collapse-on-scroll behavior of the `'medium'` and `'large'` variants: the container collapses to the small (64dp) form and the title interpolates to `titleLarge` as the user scrolls, with the on-scroll tonal shift riding the same progress. Ignored by the `'small'` and `'center-aligned'` variants. - `leading?: ReactNode` — Custom leading content. When provided, overrides `canGoBack`. - `trailing?: ReactNode` — Custom trailing content. When provided, overrides `actions`. - `actions?: AppBarAction[]` — Array of actions rendered in the trailing slot. Each entry is either an icon action (`{ icon }`) or a text action (`{ label }`, e.g. "Save"). - `containerColor?: string` — Override the container (background) color. Applied to both normal and elevated states. - `contentColor?: string` — Override the content (title and icon) color. - `titleStyle?: StyleProp` — Additional style applied to the title text. - `style?: StyleProp` — Custom style applied to the root container. --- ### Card ```tsx import { Card } from '@rootnative/components/card' {children} {children} ``` Props: - `children: ReactNode` — Content rendered inside the card surface. - `variant?: 'elevated' | 'filled' | 'outlined'` — Default: `'elevated'`. Surface style variant. - `onPress?: () => void` — When provided, the card becomes interactive (Pressable). Omit to render as a plain View. - `disabled?: boolean` — Default: `false`. Disables the press interaction and reduces opacity. Only effective when `onPress` is provided. - `containerColor?: string` — Override the container (background) color. State-layer colors (hover, press) are derived automatically. - Inherits `ViewProps` --- ### Chip ```tsx import { Chip } from '@rootnative/components/chip' Today Active } onClose={handleRemove}>John Try this ``` `ChipProps` is a discriminated union on `variant`. Each variant exposes only its valid props — `selected` is filter-only, `elevated` is unavailable on `input`, and on `input` `avatar` and `leadingIcon` are mutually exclusive at the type level. Common props (every variant): - `children: string` — Text label rendered inside the chip. - `iconSize?: number` — Default: `18`. Size of the leading icon in dp. - `containerColor?: string` — Override the container (background) color. State-layer colors auto-derived. - `contentColor?: string` — Override the content (label and icon) color. State-layer colors auto-derived when no `containerColor` is set. - `labelStyle?: StyleProp` — Additional style applied to the label text. - Inherits `PressableProps` (except `children`) #### Assist (default) Smart, contextual actions related to the surrounding content. Props: - `variant?: 'assist'` - `elevated?: boolean` — Default: `false`. Whether the chip uses an elevated surface instead of an outline border. - `leadingIcon?: IconSource` — Icon rendered before the label. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. #### Filter Toggleable chip used to refine or narrow content. Supports a `selected` state and an optional close icon while selected. Props: - `variant: 'filter'` - `elevated?: boolean` — Default: `false`. Whether the chip uses an elevated surface instead of an outline border. - `selected?: boolean` — Whether the chip is in a selected (toggled-on) state. - `leadingIcon?: IconSource` — Icon rendered before the label. When the chip is selected and no `leadingIcon` is provided, a checkmark is shown automatically. - `onClose?: () => void` — Callback fired when the close icon is pressed. The close icon only renders when the chip is selected. #### Suggestion Dynamic recommendations or follow-up actions. Props: - `variant: 'suggestion'` - `elevated?: boolean` — Default: `false`. Whether the chip uses an elevated surface instead of an outline border. - `leadingIcon?: IconSource` — Icon rendered before the label. #### Input User-entered information such as a tag or contact. Always outlined; supports either `avatar` or `leadingIcon` (mutually exclusive) plus an optional close icon. Variant-specific props: - `variant: 'input'` - `avatar?: ReactNode` — Custom avatar content (e.g. a small Image or View) before the label. Mutually exclusive with `leadingIcon`. - `leadingIcon?: IconSource` — Icon rendered before the label. Mutually exclusive with `avatar`. - `onClose?: () => void` — Callback fired when the close/remove icon is pressed. When provided, renders a trailing close icon. --- ### Checkbox ```tsx import { Checkbox } from '@rootnative/components/checkbox' ``` Props: - `value?: boolean` — Default: `false`. Whether the checkbox is checked. - `onValueChange?: (value: boolean) => void` — Callback fired when the checkbox is toggled. Receives the new value. - `indeterminate?: boolean` — Default: `false`. Render the MD3 indeterminate ("mixed") state — a dash mark on the selected container colors. Wins over `value` visually, and reports `checked: 'mixed'` to accessibility. - `error?: boolean` — Default: `false`. Render the MD3 error state: `error`-colored outline when unchecked, and an `error` container with `onError` mark when checked or indeterminate. State-layer (hover, focus, press) colors also use `error`. The standard disabled treatment wins over the error state. - `checkIcon?: IconSource` — Default: `'check'`. Icon shown when the checkbox is checked. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. Override this when your `iconResolver` doesn't map the default `'check'` name (e.g. a Lucide-only resolver). - `containerColor?: string` — Override the container (box) color when checked. State-layer colors (hover, press) are derived automatically. - `contentColor?: string` — Override the checkmark icon color. - `style?: StyleProp` — Style applied to the root container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling. --- ### Radio ```tsx import { Radio } from '@rootnative/components/radio' ``` Props: - `value?: boolean` — Default: `false`. Whether the radio button is selected. - `onValueChange?: (value: boolean) => void` — Callback fired when an unselected radio is pressed. Always receives `true` — radios are select-only, so pressing an already-selected radio is a no-op. - `containerColor?: string` — Override the outer ring and inner dot color when selected. State-layer colors (hover, press) are derived automatically. - `contentColor?: string` — Override the outer ring color when unselected. - `style?: StyleProp` — Style applied to the root container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling. - Inherits `PressableProps` (except `children`, `style`) --- ### Switch ```tsx import { Switch } from '@rootnative/components/switch' ``` Props: - `value?: boolean` — Default: `false`. Whether the switch is toggled on. - `onValueChange?: (value: boolean) => void` — Callback fired when the switch is toggled. Receives the new value. - `selectedIcon?: IconSource` — Icon shown on the thumb when selected. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. Opt-in: the MD3 default switch renders no icons — pass `'check'` for the spec's "switch with icon" variant. - `unselectedIcon?: IconSource` — Icon shown on the thumb when unselected. When provided, the thumb renders at the larger selected size. Same accepted forms as `selectedIcon`. - `containerColor?: string` — Override the track color. State-layer colors (hover, press) are derived automatically. - `contentColor?: string` — Override the thumb and icon color. - `style?: StyleProp` — Style applied to the root container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling. --- ### TextField ```tsx import { TextField } from '@rootnative/components/text-field' ``` Props: - `label?: string` — Floating label text. Animates above the input when focused or filled. - `variant?: 'filled' | 'outlined'` — Default: `'filled'`. Container style. - `supportingText?: string` — Helper text displayed below the field. Hidden when `error` or `errorText` is active. - `errorText?: string` — Error message. When provided, implicitly sets `error` to `true` and replaces `supportingText`. - `error?: boolean` — Default: `false`. When `true`, renders the field in error state with error colors. - `disabled?: boolean` — Default: `false`. Disables text input and reduces opacity. - `leadingIcon?: IconSource` — Icon rendered at the start of the field. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. - `trailingIcon?: IconSource` — Icon rendered at the end of the field. Same accepted forms as `leadingIcon`. - `onTrailingIconPress?: () => void` — Called when the trailing icon is pressed. - `trailingIconAccessibilityLabel?: string` — Accessibility label announced by screen readers for the trailing icon pressable (e.g. "Show password"). - `showCharacterCounter?: boolean` — Default: `true (when `maxLength` is set)`. Shows a `{length}/{maxLength}` character counter at the end of the supporting-text row. Only rendered when `maxLength` is set — without a `maxLength` the counter is always hidden. - `containerColor?: string` — Override the container (background) color. Disabled state still uses the standard disabled appearance. - `contentColor?: string` — Override the content (input text and icon) color. Error and disabled states take precedence. - `inputStyle?: StyleProp` — Additional style applied to the text input element. --- ### Layout Components #### Layout Full-screen safe area wrapper. ```tsx import { Layout } from '@rootnative/components/layout' {children} {/* No safe area insets */} {children} ``` Props: - `immersive?: boolean` — Default: `false`. When `true`, removes all safe area insets for full-screen layout. - `edges?: Edge[]` — Explicit set of safe-area edges to apply. Overrides `immersive` when provided. - `style?: StyleProp` — Additional styles applied to the SafeAreaView container. - Inherits `PropsWithChildren` #### Box Base layout primitive with spacing shorthand props. ```tsx import { Box } from '@rootnative/components/layout' {children} {children} ``` Props: - `p?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Padding on all sides - `px?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Horizontal padding (paddingStart + paddingEnd) - `py?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Vertical padding (paddingTop + paddingBottom) - `pt?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Padding top - `pb?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Padding bottom - `ps?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Padding start (left in LTR, right in RTL) - `pe?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Padding end (right in LTR, left in RTL) - `m?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Margin on all sides - `mx?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Horizontal margin (marginStart + marginEnd) - `my?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Vertical margin (marginTop + marginBottom) - `mt?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Margin top - `mb?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Margin bottom - `ms?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Margin start - `me?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Margin end - `gap?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Gap between children - `rowGap?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Row gap between children - `columnGap?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number` — Column gap between children - `flex?: number` — Flex value - `align?: FlexAlignType` — Align items along the cross axis - `bg?: string` — Background color - Inherits `ViewProps` #### Row Horizontal flex container (extends Box). ```tsx import { Row } from '@rootnative/components/layout' {children} {/* Wraps to next line */} ``` Props: - `wrap?: boolean` — Default: `false`. Whether children should wrap to the next line when they overflow. - `inverted?: boolean` — Default: `false`. Reverses the layout direction (`row-reverse`). #### Column Vertical flex container (extends Box). ```tsx import { Column } from '@rootnative/components/layout' {children} ``` Props: - `inverted?: boolean` — Default: `false`. Reverses the layout direction (`column-reverse`). #### Grid Equal-width column grid (extends Row). ```tsx import { Grid } from '@rootnative/components/layout' {children} ``` Props: - `columns: number` — Number of equal-width columns. --- ### List ```tsx import { List, ListItem, ListDivider } from '@rootnative/components/list' } trailingContent={} /> ``` #### List Container for list items. Props: - `children: ReactNode` — Content rendered inside the list container. - `style?: StyleProp` - Inherits `ViewProps` #### ListItem Props: - `headlineText: string` — Primary text displayed on the list item. - `supportingText?: string` — Secondary text displayed below the headline. - `overlineText?: string` — Text displayed above the headline (e.g. category label). - `trailingSupportingText?: string` — Short text displayed at the trailing edge (e.g. "100+", timestamp). - `leadingContent?: ReactNode` — Content rendered before the text block (icon, avatar, image, checkbox). - `trailingContent?: ReactNode` — Content rendered after the text block (icon, switch, checkbox). - `onPress?: () => void` — When provided, the item becomes interactive (Pressable). Omit to render as a plain View. - `disabled?: boolean` — Default: `false`. Disables the press interaction and reduces opacity. Only effective when `onPress` is provided. - `containerColor?: string` — Override the container (background) color. State-layer colors (hover, press) are derived automatically. - `contentColor?: string` — Override the headline text color. Overline, supporting, and trailing-supporting text keep their MD3 muted variants (`onSurfaceVariant`) — those are intentionally distinct from the primary content color. - `supportingTextNumberOfLines?: number` — Default: `1`. Maximum number of lines for supportingText before truncating. - `style?: StyleProp` - Inherits `ViewProps` #### ListDivider Props: - `inset?: boolean` — Default: `false`. When true, adds a leading inset so the divider aligns with text that follows a leading icon (56dp from the start edge). - `style?: StyleProp` - Inherits `ViewProps` --- ### KeyboardAvoidingWrapper ```tsx import { KeyboardAvoidingWrapper } from '@rootnative/components/keyboard-avoiding-wrapper' ``` Props: - `behavior?: 'padding' | 'height' | 'position'` — Default: `'padding'`. Keyboard avoidance strategy. - `keyboardVerticalOffset?: number` — Default: `0`. Extra offset added to the keyboard height calculation. Useful for accounting for headers or tab bars. - `enabled?: boolean` — Default: `true`. Enable or disable the keyboard avoiding behavior. - `scrollViewProps?: ScrollViewProps` — Props forwarded to the inner `ScrollView`. - `onKeyboardShow?: (event: KeyboardEvent) => void` — Called when the keyboard is about to show (iOS) or has shown (Android). - `onKeyboardHide?: (event: KeyboardEvent) => void` — Called when the keyboard is about to hide (iOS) or has hidden (Android). - `style?: StyleProp` — Style applied to the outer `KeyboardAvoidingView`. - `contentContainerStyle?: StyleProp` — Style applied to the inner `ScrollView` contentContainerStyle. - Inherits `PropsWithChildren` --- ### Avatar ```tsx import { Avatar } from '@rootnative/components/avatar' ``` Props: - `imageUri?: string` — URI of the image to display. Takes priority over `icon` and `label`. - `icon?: IconSource` — Icon to display. Accepts a string name (resolved via the theme's `iconResolver`, defaulting to `MaterialCommunityIcons`), a pre-rendered element, or a render function that receives `{ size, color }`. Takes priority over `label` when `imageUri` is not set. - `label?: string` — Text initials to display (1–2 characters recommended). Shown when `imageUri` and `icon` are not set. - `size?: 'xSmall' | 'small' | 'medium' | 'large' | 'xLarge'` — Default: `'medium'`. Size of the avatar. - `containerColor?: string` — Override the container (background) color. State-layer colors (hover, press) are derived automatically when `onPress` is set. - `contentColor?: string` — Override the content (icon / initials) color. - `style?: StyleProp` — Custom style applied to the root container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling. - `onPress?: () => void` — When provided, the avatar becomes interactive (Pressable). - `disabled?: boolean` — Default: `false`. Whether the avatar is disabled. Blocks interaction, renders the content at 38% opacity, and reports `accessibilityState: { disabled: true }`. Only applies when `onPress` is set. - `accessibilityLabel?: string` — Required when `onPress` is provided — labels the button for screen readers. - Inherits `ViewProps` (except `style`) --- ### Slider ```tsx import { Slider } from '@rootnative/components/slider' // Continuous (single thumb) // Discrete (snaps to step, tick marks shown automatically) // Range (pass a tuple for two thumbs) // Centered — active track fills from midpoint to thumb // With start/end icon decorations ``` Props: - `value?: number | [number, number]` — Current value. Pass a number for single-thumb mode or `[low, high]` for range mode. Use `defaultValue` for uncontrolled usage. - `defaultValue?: number | [number, number]` — Initial value for uncontrolled usage. Pass a `[low, high]` tuple to enable range mode. - `onValueChange?: (value: SliderValue) => void` — Called continuously during a drag with the latest value. - `onSlidingComplete?: (value: SliderValue) => void` — Called once when the user releases the thumb. - `minimumValue?: number` — Default: `0`. Lowest allowed value. - `maximumValue?: number` — Default: `1`. Highest allowed value. - `step?: number` — Default: `0`. Step interval. Set to a positive number to enable discrete mode (values snap to multiples of `step` and tick marks are shown by default). Omit or set to `0` for a continuous slider. - `centered?: boolean` — Default: `false`. Centered slider — the active track fills from the midpoint to the thumb instead of from the start. Useful for symmetric values like balance or EQ adjustments. Single-thumb mode only; ignored for range sliders. - `showTickMarks?: boolean` — Whether to render tick marks along the track. Defaults to `true` when `step > 0`, otherwise `false`. Pass an explicit boolean to override. - `showValueLabel?: boolean | 'always'` — Default: `true`. Show the value bubble above the thumb. `true` shows it during drag only, `'always'` keeps it visible, `false` hides it entirely. - `formatValueLabel?: (value: number) => string` — Custom formatter for the value-label text. Defaults to integer rounding for discrete sliders and 2-decimal rounding for continuous sliders. - `startIcon?: IconSource` — Optional icon decoration shown at the start (leading edge) of the track. Accepts a string name (resolved via the theme's `iconResolver`), a pre-rendered element, or a render function. - `endIcon?: IconSource` — Optional icon decoration shown at the end (trailing edge) of the track. - `containerColor?: string` — Override the active track color. Hover/press state-layer colors on the thumb are derived from `contentColor` (or the active track color when unset). - `contentColor?: string` — Override the thumb color. - `inactiveTrackColor?: string` — Override the inactive track color. - `disabled?: boolean` — Default: `false`. Disable interaction. Renders the standard MD3 disabled treatment (38% `onSurface`); `containerColor`/`contentColor` are ignored. - `style?: StyleProp` — Style applied to the root container. - Inherits `ViewProps` (except `children`) --- ### Progress ```tsx import { LinearProgress, CircularProgress } from '@rootnative/components/progress' // Determinate (progress 0..1) // Indeterminate (omit progress) // Custom sizing / colors ``` #### LinearProgress Horizontal progress indicator. Omit `progress` for indeterminate mode. Props: - `progress?: number` — Progress value from 0 to 1. When omitted, the indicator shows the indeterminate animation. - `containerColor?: string` — Override the active track / indicator color. - `trackColor?: string` — Override the inactive track color. - `thickness?: number` — Default: `4`. Track / indicator thickness in dp. - `stopIndicator?: boolean` — Default: `true`. Show the trailing stop indicator dot. Only applies in determinate mode and is hidden when `progress` reaches 1. - Inherits `ViewProps` (except `children`) #### CircularProgress Circular progress indicator. Omit `progress` for indeterminate mode. Props: - `progress?: number` — Progress value from 0 to 1. When omitted, the indicator shows the indeterminate animation. - `containerColor?: string` — Override the active track / indicator color. - `trackColor?: string` — Override the inactive track color. - `size?: number` — Default: `48`. Outer diameter in dp. - `thickness?: number` — Default: `4`. Stroke thickness in dp. - Inherits `ViewProps` (except `children`) --- ## Icons Every icon prop accepts an `IconSource` (`import type { IconSource } from '@rootnative/utils'`) — one of three forms: 1. **String name** — resolved through the theme's `iconResolver`. Defaults to `MaterialCommunityIcons` from `@expo/vector-icons`. Browse names at https://pictogrammers.com/library/mdi/. 2. **ReactElement** — a pre-rendered icon (`leadingIcon={}`). The component does not override size/color. 3. **Render function** — `(props: { size: number; color?: string }) => ReactNode`. Receives the component's resolved icon size and color, so the icon stays consistent with theme/variant state. Per-call elements/functions always take precedence over the resolver. `@expo/vector-icons` is only required if you actually pass string icon names without a custom resolver. ### `@rootnative/icons` adapter package (v0.0.0-alpha.1) Pre-built resolver factories for the most common React Native icon libraries. Install only the icon library you actually use — Lucide / Phosphor / `@expo/vector-icons` are declared as optional peer deps. ```bash pnpm add @rootnative/icons ``` | Helper | For | |--------|-----| | `createLucideResolver({ icons })` | [Lucide](https://lucide.dev) (`lucide-react-native`) | | `createPhosphorResolver({ icons })` | [Phosphor](https://phosphoricons.com) (`phosphor-react-native`) | | `createVectorIconsResolver({ IconSet })` | Any `@expo/vector-icons` set (`Ionicons`, `FontAwesome`, …) | | `withLegacyMdiFallback(resolver)` | Wrap any custom resolver to add MDI-name compatibility | #### Lucide ```tsx import { ThemeProvider } from '@rootnative/core' import { createLucideResolver } from '@rootnative/icons' import { Check, Search, ArrowRight } from 'lucide-react-native' const resolver = createLucideResolver({ icons: { check: Check, search: Search, 'arrow-right': ArrowRight }, mdiCompat: true, // accept legacy MDI names like "magnify" strokeWidth: 1.75, // optional — Lucide default is 2 }) {children} ``` #### Phosphor ```tsx import { ThemeProvider } from '@rootnative/core' import { createPhosphorResolver } from '@rootnative/icons' import { Check, MagnifyingGlass } from 'phosphor-react-native' const resolver = createPhosphorResolver({ icons: { Check, MagnifyingGlass }, weight: 'regular', // 'thin' | 'light' | 'regular' | 'bold' | 'fill' | 'duotone' mdiCompat: true, }) ``` #### `@expo/vector-icons` ```tsx import { Ionicons } from '@expo/vector-icons' import { createVectorIconsResolver } from '@rootnative/icons' const resolver = createVectorIconsResolver({ IconSet: Ionicons, aliases: { check: 'checkmark', close: 'close', 'arrow-right': 'arrow-forward' }, }) ``` #### Custom resolver + MDI compatibility `withLegacyMdiFallback` wraps any `IconResolver` so that legacy MaterialCommunityIcons names (`magnify`, `pencil`, `dots-vertical`, …) are rewritten to the wrapped resolver's vocabulary. The base resolver is always tried first; the alias map is consulted only when the base returns `null`. ```tsx import { withLegacyMdiFallback } from '@rootnative/icons' import type { IconResolver } from '@rootnative/core' const baseResolver: IconResolver = (name, { size, color }) => { const Svg = mySvgIcons[name] return Svg ? : null } // target: 'lucide' | 'phosphor' | Record const resolver = withLegacyMdiFallback(baseResolver, { target: 'lucide' }) ``` The first call with each legacy name emits a one-time `console.warn` so you know which call sites still need migrating. Pass `warn: false` to suppress. #### Adapter options reference All three name-mapped adapters (Lucide / Phosphor) accept: - `icons: Record` — required. Names you'll pass to component props. - `mdiCompat?: boolean | Record` — `true` enables the built-in MDI alias map; an object merges/overrides entries (`null` to suppress). - `onMissing?: 'warn' | 'silent' | IconResolver` — what to do when a name isn't registered. Defaults to `'warn'` (one-time `console.warn` per missing name). Pass another resolver to delegate. Lucide-specific: `strokeWidth?: number`. Phosphor-specific: `weight?: 'thin' | 'light' | 'regular' | 'bold' | 'fill' | 'duotone'`. #### Manual resolver (no adapter package) You don't need `@rootnative/icons` at all — `iconResolver` accepts any `(name, { size, color }) => ReactNode` function: ```tsx import { ThemeProvider } from '@rootnative/core' import type { IconResolver } from '@rootnative/core' import { Check, Heart } from 'lucide-react-native' const icons = { check: Check, heart: Heart } const lucide: IconResolver = (name, { size, color }) => { const Icon = icons[name as keyof typeof icons] return Icon ? : null } {/* string icon names route to Lucide */} ``` Reach for the adapter package when you want `mdiCompat`, `onMissing` warning behavior, or a typed options surface; reach for the manual form for one-offs and SF Symbols / SVG sprite sheets. --- ## Code style - No semicolons, single quotes, trailing commas - TypeScript strict mode - Subpath imports preferred: `@rootnative/components/button` over `@rootnative/components`