--- name: nativecn-component-authoring description: Write or change nativecn Components so they follow the Component contract. Use when the user asks for a "new component", "custom component", "reusable component", "add a variant", "add a size", "make this pressable", or to edit a Component in src/components of a nativecn app, or when contributing a Component or Primitive to the nativecn repo. license: MIT --- # Author a nativecn Component ## 1. Check before writing - Search the Registry first with `search_items` / `list_items`. If the Component exists, add it (`get_add_command`) instead of writing one. - "Add a Variant" or "add a Size" to an installed Component means editing its file: add a key to its typed Variants object and the matching styles. The user owns the file. - A composition used on one Screen is not a Component; keep it next to that Screen. ## 2. Placement (ADR 0008) - Generic, reusable Components: `src/components/.tsx`. Rendering Primitives: `src/components/primitives/`. Hooks: `src/hooks/`. Helpers: `src/utils/`. - In feature mode, something used by one Feature lives in `src/features//components/`. The moment a second Feature needs it, move it to `src/components/` and update the imports. Reuse global items first. - nativecn Components and Primitives always stay global. ## 3. The Component contract **Files and exports** - kebab-case file names (`price-tag.tsx`); named exports only, never a default export; export the props type (`PriceTagProps`). - Variants as a typed object at the top of the file. Sizes (`sm`, `md`, `lg`) map only to Tokens. - Multi-part Components: named exports from one file (`Card`, `CardHeader`, `CardTitle`, …). No dot syntax. - `ref` is a plain prop (React 19); no `forwardRef`. `testID` and the underlying React Native props pass through. - Overrides: only `style`, merged **last** onto the root, for layout. No per-part style props. - Route files in `src/app/` are the one place a default export is required (by Expo Router); they aren't Components. **States and Status** - `disabled` and `loading` are booleans. - Feedback Components take `status?: 'error' | 'success'`. - Precedence: `disabled` > `loading` > `status`. - A Component never resets `status`; the Screen sets and clears it. **Form controls** - Work controlled (`value` + change handler) and uncontrolled (`defaultValue`), via `useControllableState`. - Names: Checkbox/Switch-like `checked` / `onCheckedChange`; choice controls `value` / `onValueChange`; text inputs `value` / `onChangeText`. - Read `label`, `status`, `disabled` and `required` from `FormFieldContext` so the control works inside `FormField`. **Press feedback and motion** - Every pressable has a pressed look, applied instantly through the `Pressable` Primitive's pressed state. No ripple, no OS-specific feedback. - Animations use the `fast` motion Token through `useMotion`, and are skipped when Reduce Motion is on. - Selection controls (switch, checkbox, radio, segmented, chip, slider steps) give a light haptic tick when `haptics` is on in `config.ts`. Buttons don't. **Text and icons** - All text through `Text` (Variant + text Colour Role), all glyphs through `Icon` with a Lucide component passed in. ## 4. Styling - `createStyles((t) => ({ … }))` from the Theme, with Tokens and Colour Roles only. No hard-coded colours or sizes; `t.scaleValue(n)` only for genuine one-offs. No Tailwind, NativeWind or `className`. - Variant styles are extra keys in the same `createStyles` call. - Test in light and dark. ### Style Slots - **In a user's app** the Style is already baked into each Component as literal values. Match the look of the installed Components (e.g. copy the pressed look and control heights from `button.tsx`) so the new one fits the Preset. - **In the nativecn repo**, base files call `slot('.', t)` (e.g. `slot('button.root', t)`, `slot('button.pressed', t)`), and every Style fills every slot in `packages/ui/styles/