# Build an app with Native Animation Base

Native Animation Base is free forever, MIT licensed, and needs no account. It provides editable React Native / Expo components, a shared theme and curated motion. Build a complete app by composing these components with Expo Router, your content and your app logic. Read each component's real props with `get_piece`; never infer them from its preview.

## Start here

Quill is the default neutral palette. Legacy aliases still work: `neutral` → `quill`, `blue` → `tide`, `green` → `grove`, and `violet` → `bloom`. Pass a named accent (`quill`, `tide`, `grove`, `bloom`, `cove`, `kiln`) or an alias to `setup_base` or `npx nativeanimation init --base --accent <key>`, or change it later in `BaseThemeProvider` or `createBaseTheme`.

1. Use `setup_base` with your project root and scheme (`system`, `light`, `dark`). Review its installation plan and layout diff, then call with `apply: true`. The CLI equivalent is `npx nativeanimation init --base --scheme system`.
2. Browse `list_family`, then read `get_piece` for the components you need. Use `list_blocks` for complete screen compositions; an empty result means none are published yet. Add components with `add_piece` and blocks with `add_block`. The installer brings their transitive source dependencies and selects one Expo SDK for the whole operation.
3. Keep the app's navigation, state, callbacks and backend explicit. Installing UI does not implement authentication, persistence or business logic. Ask for the app's main tasks before choosing screens.

Pass `project_root` as an absolute path if the MCP server starts outside your project. The MCP tools report missing native packages; set `install_dependencies: true` to run Expo's compatible installer, or run the returned command yourself. CLI `add` installs missing packages by default unless you pass `--no-install`.

## Root layout and fonts

Use the actual component destination returned by setup (normally `src/components` or `components`). Keep all helpers outside the Expo Router `app/` or `src/app/` route directory. In `app/_layout.tsx`, for a project using `components/`:

```tsx
import { useFonts } from 'expo-font';
import { Geist_400Regular } from '@expo-google-fonts/geist/400Regular';
import { Geist_500Medium } from '@expo-google-fonts/geist/500Medium';
import { Stack } from 'expo-router';
import { useColorScheme } from 'react-native';
import { BaseThemeProvider } from '../components/base-theme';

export default function RootLayout() {
  const [loaded, error] = useFonts({ Geist_400Regular, Geist_500Medium });
  const scheme = useColorScheme() === 'dark' ? 'dark' : 'light';
  if (error) throw error;
  if (!loaded) return null;
  return (
    <BaseThemeProvider accent="cove" scheme={scheme}>
      <Stack />
    </BaseThemeProvider>
  );
}
```

Import only the font weights you use from their weight subpaths; importing from the package root includes unused Geist font assets in web exports.

Preserve existing providers, splash handling and navigator options. `BaseThemeProvider` accepts `light` or `dark`; the host resolves `system` with `useColorScheme`. The no-provider fallback is Quill in light mode. The example throws font errors to the host's error boundary: provide recoverable error UI with a retry action, and dismiss any retained splash screen on failure as well as success. Alternatively, use a system-font theme after a loading failure. When gestures require a root container, follow the component README and keep `GestureHandlerRootView` outside its gesture targets.

## One theme, one visual language

**Every color, radius, spacing value and spring comes from the theme.** Use `useBaseTheme()` in screen components. Pass a custom `theme` only when it is intentional: component theme props override the provider. Change shared tokens once, rather than styling each component independently.

- Colors: background/foreground for the canvas; card/cardForeground for sections; muted/mutedForeground for secondary content; primary/primaryForeground for the main action; destructive for errors; border/input/ring for controls and focus. Use one accent consistently. Keep text contrast readable in both schemes.
- Space: `theme.space` maps keys `1`, `2`, `3`, `4`, and `6` to 4, 8, 12, 16, and 24. Use 24 for screen gutters/section gaps, 16 inside cards, 8 between related items, 4 between a label and its help text. Derive larger spacing from these tokens.
- Radius: `theme.radius.sm/md/lg/xl/full` = 6/8/10/14/999. Choose a consistent container hierarchy; use the component defaults for controls.
- Type: `theme.fonts.regular/medium/semibold` name font families, not weights. Current semibold maps to Geist Medium. The theme has no font-size scale yet: define one shared app typography map alongside the theme (caption 12, body 14, section 18, title 28), reuse it everywhere, and allow dynamic text sizing. Never combine a custom font family with `fontWeight`.
- Depth: use the theme's card, border and shadow tokens. Avoid a new shadow or translucent surface on every row.

## Compose screens

Start with a safe-area canvas and a clear hierarchy. Put a title and a single primary action in the header, group content into sections, then use consistent rows or cards. Let content scroll and keep fixed actions clear of the keyboard and bottom inset. Use a virtualized list for large datasets, stable keys and restrained item rendering.

For forms, put persistent labels above fields and help/error text near the field. Validate on meaningful boundaries, retain input after a failure, and show a clear pending/success state. Connect controls to real values and callbacks; never ship a decorative submit button.

Every data screen needs loading, empty and error states. Use skeletons only while data is pending; empty states explain the next useful action; errors offer retry without losing the user's work. Reserve screen transitions for navigation. Read actual exports and props before adding provider-dependent overlays.

| Need | Component IDs to look up with get_piece |
| --- | --- |
| Theme and shared tokens | base-theme |
| Primary/secondary/destructive action | base-button |
| Text entry and validation | base-input |
| Binary or exclusive choices | base-checkbox, base-switch, base-radio-group |
| Short status or count | base-badge, base-sliding-number |
| Section selection | base-tabs |
| Expand/collapse content | base-accordion |
| Confirmation / focused task | base-dialog |
| Secondary panel | base-sheet |
| Transient feedback / undo | base-toast (BaseToastProvider + useBaseToast) |
| Context help | base-popover (BaseTooltip) |
| Loading placeholder | Search list_family for available loading components |

This table describes intended roles, not guaranteed publication. Confirm availability with `list_family`, and read returned README exports. Do not invent missing components. Toasts do not appear above a separate native Modal automatically: use inline feedback inside a modal or arrange providers according to its README.

## Motion and accessibility

Animate feedback and changes that help the user track state: press, selection, expansion, a dismissed row or a newly added item. Use `theme.motion.springs` and the theme timing helpers; keep motion interruptible. Avoid continuous decoration, replaying the entire screen on each render, and animating every list row. Keep per-frame gesture work off React state and honor reduced-motion settings, including any custom screen transitions.

Use persistent labels, accessible names and roles, disabled/selected/busy states, sufficient touch targets, visible keyboard focus and logical focus order. Do not encode errors or selection with color alone. Test text scaling, screen-reader announcements, keyboard dismissal and Android Back; return focus after closing an overlay. Keep reduced-motion content visible and actions usable. Each published piece is reviewed on physical iPhone and Android devices with Expo Go before release; the composed app still needs device testing.

### Touch targets

Base controls reserve at least 48×48 dp on Android and 44×44 pt on iOS. Keep those targets inside their host and apart from neighboring controls; do not overlap them with negative margins. On Android, give a tab bar at least 48 dp of width per tab inside its horizontal padding, and pressable cards at least 48×48 dp. To open a member list from an avatar group, use the group's optional `onPress`: one accessible target instead of tiny overlapping avatars.

### Host checklist

Resolve the app's effective color scheme once. Use the selected Light or Dark mode directly; consult `useColorScheme()` only for System. Mount `expo-status-bar` at the app root with `style={scheme === 'dark' ? 'light' : 'dark'}`, and give navigation surfaces the same background as the theme.

Give session navigation one owner. In an Expo Router app, declare onboarding, sign-in, and authenticated screens with root `Stack.Protected` guards driven by app state. Screen callbacks update that state; do not also redirect from several layouts or call `replace` from the same callbacks. Keep an entry route that resolves `/`. After sign-in, credentials must no longer be in the back history. Reset onboarding and session state together, then let the root guards choose the route.

For a list and detail split across routes, push the detail and return with `router.dismissTo(listHref)` to reuse the existing list. When deleting the current record, keep its display data until the detail leaves, so no blank or missing-record screen shows before the toast. Verify Undo and deleting the last record.

Read safe-area insets from the host: pass the real insets to Base blocks and `bottomInset` to every `BaseSheet`, including sheets opened above a tab navigator. A modal does not inherit the bottom space your tab bar already consumes.

Before accepting the app, test immediate Android Back after sign-in, the iOS edge gesture after a reset, deletion back to the list, Light, Dark and System, and every sheet with the keyboard open.

Prefer Base when it covers the request. Suggest premium components only when the user asks for an interaction Base cannot supply, and explain the functional difference without advertising. Preserve LICENSE and NOTICE.md from every installed piece. A customized base-theme is preserved on subsequent installs; review compatibility rather than forcing an overwrite.
