diff --git a/packages/react-native-ui-lib/install-skills.js b/packages/react-native-ui-lib/install-skills.js new file mode 100644 index 0000000000..e6082cd3e2 --- /dev/null +++ b/packages/react-native-ui-lib/install-skills.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const SKILL_NAME = 'uilib-codegen'; +const SOURCE_DIR = path.join(__dirname, 'skills', SKILL_NAME); +const TARGET_DIR = path.join(process.cwd(), '.claude', 'skills', SKILL_NAME); + +function copyDir(src, dest) { + fs.mkdirSync(dest, {recursive: true}); + for (const entry of fs.readdirSync(src, {withFileTypes: true})) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + if (entry.isDirectory()) { + copyDir(srcPath, destPath); + } else { + fs.copyFileSync(srcPath, destPath); + } + } +} + +if (!fs.existsSync(SOURCE_DIR)) { + process.stderr.write(`Error: skill source not found at ${SOURCE_DIR}\n`); + process.exit(1); +} + +console.log(`Installing ${SKILL_NAME} Claude Code skill...`); +copyDir(SOURCE_DIR, TARGET_DIR); +console.log(`Done! Skill installed at ${TARGET_DIR}`); +console.log('Restart Claude Code to activate it.'); diff --git a/packages/react-native-ui-lib/package.json b/packages/react-native-ui-lib/package.json index 617a9405bf..2b705302a8 100644 --- a/packages/react-native-ui-lib/package.json +++ b/packages/react-native-ui-lib/package.json @@ -138,6 +138,8 @@ "node": ">=18" }, "files": [ + "skills", + "install-skills.js", "*.js", "*.d.ts", "!scripts", diff --git a/packages/react-native-ui-lib/skills/uilib-codegen/SKILL.md b/packages/react-native-ui-lib/skills/uilib-codegen/SKILL.md new file mode 100644 index 0000000000..c4f31317d1 --- /dev/null +++ b/packages/react-native-ui-lib/skills/uilib-codegen/SKILL.md @@ -0,0 +1,148 @@ +--- +name: uilib-codegen +description: Use when building React Native screens with react-native-ui-lib. Contains component lookup via node_modules API files, layout-to-component mapping, styling conventions (modifiers, design tokens, spacing), and component intent classification. Use when creating screens, looking up component props, choosing the right component for a UI pattern, or styling with the design system. +version: 1.0.0 +--- + +# react-native-ui-lib — React Native UI Component Library + +> **When to use**: Building any React Native screen or component using react-native-ui-lib. Use when creating screens, looking up component APIs, choosing the right component for a UI pattern, styling with modifiers and design tokens, or asking how to use any `react-native-ui-lib` component. + +--- + +## What react-native-ui-lib is + +- **Package**: `react-native-ui-lib` — a comprehensive React Native UI component library with 70+ components, a design token system, spacing presets, typography modifiers, and theming support. +- **Key principle**: Always use library components instead of building custom UI from scratch. If a component exists for a UI pattern, use it. +- **Docs**: https://wix.github.io/react-native-ui-lib/ +- **Figma community file**: https://www.figma.com/community/file/1379775092983284111/rnui-library + +--- + +## How to look up any component + +```bash +# Step 1 — locate the package (monorepo-aware, run once per session) +UILIB_PATH="node_modules/react-native-ui-lib" +[ ! -d "$UILIB_PATH" ] && UILIB_PATH="../../node_modules/react-native-ui-lib" + +# Step 2 — batch-read ALL needed API files in ONE call (fastest approach) +cat "$UILIB_PATH/src/components/button/button.api.json" \ + "$UILIB_PATH/src/components/avatar/avatar.api.json" \ + "$UILIB_PATH/src/components/text/text.api.json" \ + "$UILIB_PATH/src/components/listItem/listItem.api.json" \ + 2>/dev/null +``` + +**Speed rule**: Read all N API files in a **single batched `cat` call** instead of one call per component. Never look up one component at a time. + +### API file path patterns + +Most components follow `/.api.json`. A few use a nested `api/` or `apis/` subdirectory: + +| Component | API file path | +|-----------|--------------| +| Most components | `/.api.json` | +| `modal` | `modal/api/modal.api.json` | +| `picker` | `picker/api/picker.api.json`, `picker/api/pickerItem.api.json` | +| `tabController` | `tabController/apis/tabController.api.json`, `tabController/apis/tabBar.api.json`, `tabController/apis/tabPage.api.json` | +| `card` | `card/card.api.json`, `card/cardSection.api.json`, `card/cardImage.api.json` | +| `dialog` | `dialog/dialog.api.json`, `dialog/dialogHeader.api.json` | +| `wizard` | `wizard/wizard.api.json`, `wizard/wizardStep.api.json` | +| `listItem` | `listItem/listItem.api.json`, `listItem/listItemPart.api.json` | + +**Fallback** — when the path is unknown, find it in one command: +```bash +find "$UILIB_PATH/src/components" -name "*.api.json" 2>/dev/null +``` + +**No `.tsx`/`.ts` in node_modules** — only `.js` and `.d.ts` are compiled into the published package. + +--- + +## Available components + +actionBar, actionSheet, animatedImage, animatedScanner, avatar, badge, baseInput, button, card, carousel, checkbox, chip, chipsInput, colorPalette, colorPicker, colorSwatch, connectionStatusBar, dash, dateTimePicker, dialog, drawer, expandableSection, fadedScrollView, fader, featureHighlight, floatingButton, gradient, gridList, gridListItem, gridView, hint, icon, image, inputs, KeyboardAwareScrollView, listItem, loaderScreen, maskedInput, modal, numberInput, overlay, pageControl, panView, picker, pieChart, progressBar, progressiveImage, radioButton, radioGroup, screenFooter, scrollBar, searchInput, sectionsWheelPicker, segmentedControl, skeletonView, slider, sortableGridList, sortableList, stackAggregator, stateScreen, stepper, svgImage, switch, tabController, text, textArea, textField, timeline, toast, touchableOpacity, view, WheelPicker, wizard + +--- + +## Core conventions + +1. **Modifiers, not StyleSheet** — Layout, spacing, colors, and typography are expressed as component props. Use `StyleSheet` only for transforms and properties without a modifier equivalent. Full reference: [styling.md](./styling.md). + +2. **Design tokens, not hardcoded values** — Use semantic color tokens (`$textDefault`, `$backgroundDefault`, etc.) or palette colors (`Colors.primary`, `Colors.grey30`). Never hardcode hex values. Full reference: [styling.md](./styling.md). + +3. **Import from the library** — Never import `View`, `Text`, or `Button` from `react-native`; use `react-native-ui-lib` equivalents. Use `react-native` only for primitives the library doesn't wrap (`ScrollView`, `FlatList`, `Pressable`). + +4. **Read the API file before using a component** — Always batch-read `.api.json` files. Never guess props. + +5. **Include testID** on all interactive elements. Pattern: `[context].[element]`. + +6. **Discovery loop** — For each component: classify (Capturing / Performing / Displaying / Reporting), read the `.api.json`, extrapolate all relevant props, then triage each prop as Inject (functional necessity) or Propose (contextual enhancement). Full method: [component-intelligence.md](./component-intelligence.md). + +7. **Keyboard handling** — Screens with text inputs must wrap in `KeyboardAwareScrollView` from `react-native-ui-lib`. + +--- + +## Layout-to-component mapping + +**BEFORE writing any layout code**, scan the screen for these patterns and use the mapped component. If the component's default appearance doesn't match the target design, read its API file to find customization props rather than building the layout manually. + +| Layout pattern | Component | Trigger | Notes | +|---|---|---|---| +| Grid of items | `GridView` | N×M layouts, iterating items into rows | Use `numColumns` + `renderCustomItem`. Never build grids with `FlatList numColumns` + manual slicing. Wrap in `ScrollView` when content may exceed viewport. | +| Sectioned list | `SortableList` / `listItem` | Lists with sections, drag-to-reorder | Use `listItem` for rows with leading/trailing elements, subtitles, checkboxes | +| Search with results | `searchInput` | Search bar + filtered list | Has built-in clear button and keyboard handling | +| Bottom floating action | `floatingButton` | Fixed button at bottom of screen | Handles safe area automatically | +| Tabs / screen switcher | `tabController` | Multiple content pages, tab bar | Use with `TabController.TabBar` + `TabController.TabPage` | +| Overlay / bottom sheet | `modal` or `dialog` | Confirmation flows, sheets | `dialog` for simple confirm/cancel; `modal` for custom full content | +| Empty / error state | `stateScreen` | No results, error, offline | Has built-in image + title + CTA slots | +| Loading skeleton | `skeletonView` | Content loading placeholder | Renders shimmer by default | +| Swipeable row | `drawer` | Swipe-to-reveal actions on list items | Wraps any content, exposes action slots | +| Step / multi-page flow | `wizard` | Multi-step onboarding or forms | Manages step state and progress indicator | + +--- + +## DO / DON'T + +- **DO** always read `*.api.json` before using a component (batch multiple lookups in one shell call). +- **DO** use modifiers and design tokens from [styling.md](./styling.md) instead of StyleSheet. +- **DO** use `react-native-ui-lib` components instead of creating complex layouts from scratch. +- **DO** wrap screens that contain text inputs with `KeyboardAwareScrollView` from `react-native-ui-lib`. +- **DO** use `react-native` only for layout primitives the library doesn't wrap (`ScrollView`, `FlatList`, `Pressable`). +- **DON'T** import `View`, `Text`, or `Button` from `react-native` — always use the library equivalents. +- **DON'T** look for `.tsx`/`.ts` in node_modules — only `.js` and `.d.ts` exist. +- **DON'T** guess props — always read the API file. +- **DON'T** manually replicate a layout that the library already handles — check the layout-to-component mapping table first. +- **DON'T** use hardcoded hex colors, font sizes, or spacing values — use tokens and presets. + +--- + +## Where to read more + +- [styling.md](./styling.md) — Modifiers, spacing presets, color tokens, typography presets, border radius, shadows. +- [component-intelligence.md](./component-intelligence.md) — Intent classification, discovery loop, props triage (Inject vs. Propose), keyboard handling. +- [getting-started.md](./getting-started.md) — Installation, peer deps, ThemeManager setup, skill activation. +- [theming.md](./theming.md) — Custom colors, typography, spacings, per-component overrides. +- **Live demos**: Each `*.api.json` has an `"example"` URL pointing to the demo screen on GitHub. + +--- + +## MANDATORY COMPLIANCE AUDIT (Architect's Note) + +After generating code, output a short **Architect's Note** covering these three sections. If the audit reveals a violation, **fix the code before presenting**. + +**1. Injected props** — For each unique component type: `[Component] ([Archetype]): [Props] — [why]`. + +**2. Proposed enhancements** (max 5) — Props that improve UX/performance but aren't required. Number them so the user can say "Apply 1, 3" or "Apply all". + +**3. Compliance checklist** — One line per item, skip if not applicable: +- **Discovery:** APIs verified for [components]. Flag any `⚠️ [AI_UNVERIFIED_API]`. +- **Standards:** 0 hex colors ✅ | `$` tokens preferred over palette ✅ | Modifiers over StyleSheet ✅ +- **testIDs:** `[context].[element]` on all interactive elements. +- **Native imports:** [Justify each, e.g., "ScrollView as layout primitive"]. +- **Keyboard:** [Wrapper used / Not applicable]. + +**4. UX alternatives** (skip if single clear pattern) +- **Chosen: [pattern]** — [why it's the best fit] +- Alternatives: [A] — [why not] / [B] — [why not] diff --git a/packages/react-native-ui-lib/skills/uilib-codegen/component-intelligence.md b/packages/react-native-ui-lib/skills/uilib-codegen/component-intelligence.md new file mode 100644 index 0000000000..c884932304 --- /dev/null +++ b/packages/react-native-ui-lib/skills/uilib-codegen/component-intelligence.md @@ -0,0 +1,73 @@ +# Component Intelligence — Intent Heuristics + +## 1. Discovery Loop (MANDATORY) + +For each component, run these steps (batch all API lookups into a single shell call — see SKILL.md): + +1. **Classify** as **Capturing**, **Performing**, **Displaying**, or **Reporting** — validate against Detection Signals (Section 3). +2. **Lookup** the `.api.json` from `node_modules/react-native-ui-lib/src/components//.api.json`. If not found, try `find . -maxdepth 6 -name ".api.json"`. If still missing, add `// ⚠️ [AI_UNVERIFIED_API]` above the component and flag in the Architect's Note. +3. **Extrapolate** all relevant props from the API file. +4. **Triage** each prop: + - **Inject** — omitting it breaks functionality or a mandatory constraint (`testID`, keyboard handling, validation). Include in code and explain in Architect's Note. + - **Propose** — improves UX/performance but not required. List in Architect's Note (max 5). + - **Boundary rule:** If omitting could cause data loss, broken UX, or inaccessibility → Inject. Otherwise → Propose. + +--- + +## 2. Intent Heuristic Guide + +*Use these as a starting point. Always identify all relevant props from the discovered API file.* + +| Archetype | Functional Goal | Example Props to Find | +|:---|:---|:---| +| **Capturing** | Data collection | `validate`, `validationMessage`, `enableErrors`, `keyboardType`, `onChangeText`, `onChangeValidity` | +| **Performing** | Action triggers | `onPress`, `disabled`, `loading`, `throttleTime`, `activeOpacity` | +| **Displaying** | Persistent content | Design tokens, typography modifiers, `source` (images), `label` | +| **Reporting** | Transient feedback | `visible`, `onDismiss`, `useSafeArea`, `message`, `preset` | + +--- + +## 3. Detection Signals + +| Signal in code | Classification | +|---|---| +| `TextField`, `Picker`, `searchInput`, `Checkbox`, variable starts with `set...`, contains `input`, `form`, `value`, `onChange` | Capturing | +| `Button`, `floatingButton`, `listItem` with `onPress`, function starts with `handle...`, `submit`, `navigate`, `toggle` | Performing | +| `Text`, `Avatar`, `Image`, `badge`, `Divider`, rendering static labels, titles, descriptions, formatted data | Displaying | +| `Toast`, `Dialog`, `modal`, `loaderScreen`, `skeletonView`, `progressBar`, variable starts with `is...`, `show...`, `loading`, `error` | Reporting | + +--- + +## 4. Implementation Constraints + +- **testID:** Every interactive element. Pattern: `[context].[element]` (e.g., `profileScreen.saveButton`). +- **Import hierarchy:** Always prefer the library. NEVER import `View`, `Text`, or `Button` from `react-native`; use `react-native` only for primitives the library doesn't wrap (`ScrollView`, `FlatList`, `Pressable`). +- **Keyboard handling:** When a screen contains **Capturing** components (text fields, pickers, search inputs), **INJECT a keyboard-aware wrapper** — omitting it breaks usability on mobile keyboards. + - Preferred: `KeyboardAwareScrollView` from `react-native-ui-lib` + - Fallback: `KeyboardAvoidingView` from `react-native` if the layout can't accommodate a scroll wrapper + - If a wrapper cannot be applied, add `// TODO: keyboard-aware wrapper needed` to flag it. +- **Design system:** Use tokens and modifiers from [styling.md](./styling.md). Never hardcode colors, spacing, or font sizes. +- **API-first:** If you haven't read the `.api.json` for a component, you are not allowed to use it. Flag missing lookups with `⚠️ [AI_UNVERIFIED_API]`. + +--- + +## 5. UX Pattern Alternatives + +**When to trigger**: Text-prompt generation only. Skip if only one valid pattern exists. + +**Steps:** +1. Identify the pattern family from the screen intent. +2. Pick the best fit using available context (API files, screen type, interaction cost). +3. Report chosen pattern + alternatives in Architect's Note Section 4. + +**Seed patterns:** + +| Pattern family | Default choice | Alternatives | +|:---|:---|:---| +| Multi-select list | `listItem` with `checkbox` | Avatar tap, row `onPress`, trailing icon | +| Destructive action | `Dialog` confirmation | Swipe-to-delete (`drawer`), long-press, `actionSheet` | +| Search / filter | Inline `searchInput` | Header bar, `floatingButton` → modal | +| Single selection | `radioButton` in `listItem` | Row `onPress` + checkmark, `segmentedControl` | +| Form submission | Primary `Button` (bottom) | `floatingButton`, header right action | +| Loading state | `skeletonView` | `loaderScreen`, `progressBar`, spinner overlay | +| Empty state | `stateScreen` | Inline message, illustration + CTA | diff --git a/packages/react-native-ui-lib/skills/uilib-codegen/getting-started.md b/packages/react-native-ui-lib/skills/uilib-codegen/getting-started.md new file mode 100644 index 0000000000..1b5befd7b2 --- /dev/null +++ b/packages/react-native-ui-lib/skills/uilib-codegen/getting-started.md @@ -0,0 +1,97 @@ +# Getting Started + +## Installation + +```bash +npm install react-native-ui-lib +# or +yarn add react-native-ui-lib +``` + +### Peer dependencies + +```bash +npm install react-native-gesture-handler react-native-reanimated react-native-safe-area-context +``` + +Follow the setup guides for each peer dependency: +- [react-native-gesture-handler](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation) +- [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started) +- [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context#getting-started) + +--- + +## Basic setup + +Call your foundation config before rendering any component — typically in your app entry file: + +```typescript +// foundationConfig.ts +import {Colors, Typography, Spacings} from 'react-native-ui-lib'; + +Colors.loadColors({ + primary: '#2364AA', + secondary: '#81C3D7', +}); + +Typography.loadTypographies({ + heading: {fontSize: 28, fontWeight: '700', lineHeight: 36}, + body: {fontSize: 16, fontWeight: '400', lineHeight: 24}, +}); + +Spacings.loadSpacings({ + page: 20, + card: 16, +}); +``` + +```typescript +// App.tsx +import './foundationConfig'; // must be imported before any uilib component +import React from 'react'; +import {View, Text} from 'react-native-ui-lib'; + +export default function App() { + return ( + + Hello + + ); +} +``` + +See [theming.md](./theming.md) for the full ThemeManager API. + +--- + +## Resources + +| Resource | Link | +|----------|------| +| Documentation | https://wix.github.io/react-native-ui-lib/ | +| Figma community file | https://www.figma.com/community/file/1379775092983284111/rnui-library | +| GitHub | https://github.com/wix/react-native-ui-lib | +| Discord | https://discord.gg/2eW4g6Z | +| Demo app | https://expo.dev/@vn.chemgio/rnuilib | + +--- + +## How to activate this skill + +From your project root (requires `react-native-ui-lib` to be installed): + +```bash +node node_modules/react-native-ui-lib/install-skills.js +``` + +This copies the `uilib-codegen` skill into `.claude/skills/uilib-codegen/` in your project. Restart Claude Code to activate it. + +To make it automatic for your team, add to your `package.json`: + +```json +{ + "scripts": { + "postinstall": "node node_modules/react-native-ui-lib/install-skills.js" + } +} +``` diff --git a/packages/react-native-ui-lib/skills/uilib-codegen/styling.md b/packages/react-native-ui-lib/skills/uilib-codegen/styling.md new file mode 100644 index 0000000000..7124c15152 --- /dev/null +++ b/packages/react-native-ui-lib/skills/uilib-codegen/styling.md @@ -0,0 +1,223 @@ +# Styling Guide + +react-native-ui-lib uses **modifiers** (props) instead of StyleSheet. Use StyleSheet only for transforms and properties without a modifier equivalent. + +```typescript +// ✅ Modifiers // ❌ StyleSheet + +``` + +## Imports + +```typescript +import {Colors, Shadows, Spacings, BorderRadiuses, Typography} from 'react-native-ui-lib'; +``` + +--- + +## Layout + +| Modifier | Effect | +|----------|--------| +| `flex` | flex: 1 | +| `flex-N` | flex: N | +| `flexS` | flexShrink | +| `flexG` | flexGrow | +| `center` | center content (both axes) | +| `centerV` | center vertically | +| `centerH` | center horizontally | +| `row` | flexDirection: row | +| `spread` | justifyContent: space-between | +| `left` `right` `top` `bottom` | alignment | +| `abs` | position: absolute | +| `absL` `absT` `absR` `absB` | absolute + align to side | +| `absH` `absV` | absolute + stretch axis | +| `absF` | absolute + fill parent | + +### Modifiers vs StyleSheet — when and where + +- **Modifiers first:** If a layout behavior can be expressed with a modifier (`flex`, `center`, `row`, `spread`, `padding-s*`, `margin-s*`), use the modifier — not StyleSheet. +- **On the component itself:** Apply modifiers directly on the View/component that needs the behavior. +- **StyleSheet only for:** `transform`, `overflow`, fixed `width`/`height`, `gap` — properties that have no modifier equivalent. +- **Never combine spacing values arithmetically** (e.g., `Spacings.s5 + Spacings.s5`) — use a modifier or fixed dimension instead. + +--- + +## Spacing + +`padding-{N}` / `margin-{N}` — literal sizes: 5, 10, 15, 20, 25, 30, 40, 50 +`padding-s{X}` / `margin-s{X}` — preset sizes: s1–s10 + +Sides: `H` (horizontal), `V` (vertical), `T` (top), `B` (bottom), `L` (left), `R` (right) + +For gaps between siblings, use `marginT-s*` / `marginB-s*` on children, or `gap` in `StyleSheet`. + +```typescript + // modifier with presets +style={{ marginTop: Spacings.s2 }} // StyleSheet with Spacings constant +itemSpacing={Spacings.s5} // Spacings as a component prop value +``` + +### Spacing presets + +| Preset | Phone (px) | Tablet (px) | +|--------|------------|-------------| +| s1 | 4 | 6 | +| s2 | 8 | 10 | +| s3 | 12 | 16 | +| s4 | 16 | 20 | +| s5 | 20 | 24 | +| s6 | 24 | 28 | +| s7 | 28 | 32 | +| s8 | 32 | 38 | +| s9 | 36 | 42 | +| s10 | 40 | 48 | + +Custom spacing names (e.g. `page`, `card`) can be added via `Spacings.loadSpacings` in your foundation config — see [theming.md](./theming.md). Once loaded, they work as modifiers: ``. + +--- + +## Colors + +```typescript + // background modifier + // text color modifier + // prop +``` + +### Design tokens — always prefer over palette colors + +All tokens automatically adapt to dark/light mode. Use via modifiers (``) or `Colors.$tokenName`. + +**Rule:** When a `$` token exists for the intent, use it. Fall back to palette colors only when no token fits. + +#### Naming convention + +`$[property][Semantic][Weight]` + +- **Property**: `text`, `background`, `icon`, `outline` +- **Semantic**: `Default`, `Neutral`, `Primary`, `General`, `Success`, `Warning`, `Danger` +- **Weight** (optional): `Light`, `Medium`, `Heavy` + +#### Common tokens + +| Token | Usage | +|-------|-------| +| `$textDefault` | Primary body text | +| `$textNeutral` | Secondary / subtitle text | +| `$textDisabled` | Disabled text | +| `$textPrimary` | Accent / brand text | +| `$backgroundDefault` | Screen background | +| `$backgroundElevated` | Cards, modals | +| `$backgroundNeutral` | Section / secondary background | +| `$backgroundDisabled` | Disabled state | +| `$iconDefault` | Primary icons | +| `$iconNeutral` | Secondary icons | +| `$iconPrimary` | Accent / brand icons | +| `$outlineDefault` | Borders, dividers | +| `$outlineDisabled` | Disabled borders | + +#### Read all available tokens + +```bash +cat "$UILIB_PATH/src/style/colors/colorsBase.d.ts" +``` + +#### Palette colors + +`primary`, `secondary`, `white`, `black`, `grey10`–`grey50`, `green30` (success), `red30` (error), `orange30` (warning), `blue30` (info) + +Note: `Colors.white` / `Colors.black` are literal and do NOT invert in dark mode. Use `$white` / `$black` tokens for dark-mode-aware white/black. + +--- + +## Border Radius + +| Modifier | Pixels | +|----------|--------| +| `br0` | 0 | +| `br10` | 2 | +| `br20` | 4 | +| `br30` | 6 | +| `br40` | 8 | +| `br50` | 10 | +| `br60` | 12 | +| `br70` | 16 | +| `br90` | 24 | +| `br100` | 999 (fully rounded) | + +--- + +## Shadows + +| Preset | Intensity | Elevation (Android) | +|--------|-----------|---------------------| +| `sh10` | Light | 2 | +| `sh20` | Medium | 3 | +| `sh30` | Heavy | 4 | + +Each preset has `.top` and `.bottom` variants. Dark mode is handled automatically. + +```typescript + +``` + +--- + +## Typography + +### fontSize-to-preset mapping + +When you encounter a hardcoded fontSize in a design, map it to the correct preset. If a value is 1px off, round to the nearest preset. + +| fontSize | Preset | Weight suffixes | +|----------|--------|-----------------| +| 64px | `text10` | T, L, R, M, BO, H, BL | +| 48px | `text20` | T, L, R, M, BO, H, BL | +| 36px | `text30` | T, L, R, M, BO, H, BL | +| 28px | `text40` | T, L, R, M, BO, H, BL | +| 24px | `text50` | T, L, R, M, BO, H, BL | +| 20px | `text60` | T, L, R, M, BO, H, BL | +| 16px | `text70` | T, L, R, M, BO, H, BL | +| 14px | `text80` | T, L, R, M, BO, H, BL | +| 12px | `text90` | T, L, R, M, BO, H, BL | +| 10px | `text100` | T, L, R, M, BO, H, BL | + +Weight suffixes: `T` (Thin), `L` (Light), `R` (Regular), `M` (Medium), `BO` (Bold), `H` (Heavy), `BL` (Black). Example: `text70BO` = 16px Bold. + +### Semantic presets (user-defined) + +The `text*` presets above are built-in. Semantic names (`heading`, `body`, `caption`, etc.) are **not pre-loaded** — define them via `Typography.loadTypographies` in your foundation config (see [theming.md](./theming.md)): + +```typescript +Typography.loadTypographies({ + heading: {fontSize: 28, fontWeight: '700', lineHeight: 36}, + body: {fontSize: 16, fontWeight: '400', lineHeight: 24}, + bodySmall: {fontSize: 14, fontWeight: '400', lineHeight: 20}, +}); +``` + +Once loaded, use as modifiers: + +```typescript +Page Title +Description text +``` + +Styles: `italic`, `underline`, `center`, `left`, `right` + +--- + +## DO / DON'T + +- **DO** use `Spacings.s*` for all spacing values (`margin: Spacings.s2`, `gap: Spacings.s4`) +- **DO** use `Shadows.sh*` for all shadows (`style={Shadows.sh20.bottom}`) +- **DO** use `BorderRadiuses.br*` or `br*` modifiers for all border radiuses +- **DO** use typography presets as modifiers on `` instead of hardcoded `fontSize`/`lineHeight` +- **DO** use StyleSheet over inline `style={{}}` when modifiers can't express the property +- **DO** use `$` design tokens for all colors — they cover dark/light mode automatically +- **DON'T** use hardcoded hex colors anywhere — use `$` tokens or palette colors +- **DON'T** use numeric values for spacings — use `Spacings.s*` presets or `margin-s*`/`padding-s*` modifiers +- **DON'T** use hardcoded `fontSize` or `lineHeight` — use the mapping table above +- **DON'T** create custom shadow objects — use `Shadows.sh10/sh20/sh30` +- **DON'T** use hardcoded `borderRadius` — use `BorderRadiuses.br*` or `br*` modifiers diff --git a/packages/react-native-ui-lib/skills/uilib-codegen/theming.md b/packages/react-native-ui-lib/skills/uilib-codegen/theming.md new file mode 100644 index 0000000000..ce8992c5be --- /dev/null +++ b/packages/react-native-ui-lib/skills/uilib-codegen/theming.md @@ -0,0 +1,196 @@ +# Theming Guide + +react-native-ui-lib uses `ThemeManager` plus static loaders (`Colors`, `Typography`, `Spacings`) to build a fully custom design system. Set everything up once in your foundation config before any component renders. + +```typescript +import {Colors, Typography, Spacings, ThemeManager} from 'react-native-ui-lib'; +``` + +--- + +## Colors + +### Load custom palette colors + +```typescript +Colors.loadColors({ + primary: '#2364AA', + secondary: '#81C3D7', + error: '#E63B2E', + success: '#ADC76F', + warning: '#FF963C', +}); +``` + +After loading, use these as modifiers or via `Colors.*`: + +```typescript + + +``` + +### Override design tokens (dark-mode-aware) + +Default `$` tokens are pre-loaded (see `designTokens.ts`). To override them with your brand colors, use `Colors.loadSchemes` for full light/dark control: + +```typescript +Colors.loadSchemes({ + light: { + $textDefault: '#221D23', + $backgroundDefault: '#FFFFFF', + $backgroundNeutral: '#F5F5F5', + $outlineDefault: '#E0E0E0', + }, + dark: { + $textDefault: '#FFFFFF', + $backgroundDefault: '#121212', + $backgroundNeutral: '#1E1E1E', + $outlineDefault: '#333333', + }, +}); +``` + +Alternatively, generate a full token set from a single primary color: + +```typescript +Colors.loadDesignTokens({primaryColor: '#2364AA'}); +``` + +### Dark mode + +```typescript +// Switch the active color scheme +Colors.setScheme('dark'); // or 'light' or 'default' (system) +``` + +Read the current scheme: + +```typescript +Colors.getScheme(); // 'dark' | 'light' | 'default' +``` + +--- + +## Typography + +### Load custom presets + +```typescript +Typography.loadTypographies({ + heading: {fontSize: 28, fontWeight: '700', lineHeight: 36}, + subheading: {fontSize: 22, fontWeight: '600', lineHeight: 30}, + body: {fontSize: 16, fontWeight: '400', lineHeight: 24}, + bodyMedium: {fontSize: 16, fontWeight: '500', lineHeight: 24}, + bodySmall: {fontSize: 14, fontWeight: '400', lineHeight: 20}, + caption: {fontSize: 12, fontWeight: '400', lineHeight: 16}, +}); +``` + +Use as modifiers: + +```typescript +Page Title +Subtitle +``` + +--- + +## Spacings + +### Load custom spacing values + +```typescript +Spacings.loadSpacings({ + page: 20, // horizontal page margins + card: 16, // inner card padding + s1: 4, + s2: 8, + s3: 12, + s4: 16, + s5: 20, +}); +``` + +Use as modifiers or constants: + +```typescript + + +``` + +--- + +## Per-component overrides + +`ThemeManager.setComponentTheme` lets you set default props for any component globally — no need to repeat them at every usage site. + +```typescript +ThemeManager.setComponentTheme('Button', { + borderRadius: 8, + size: 'large', +}); + +ThemeManager.setComponentTheme('Text', { + $textDefault: true, +}); + +ThemeManager.setComponentTheme('Card', { + elevation: 2, + borderRadius: 12, +}); +``` + +### Read a component's current theme + +```typescript +ThemeManager.getComponentTheme('Button'); +``` + +--- + +## Full foundation config example + +```typescript +// foundationConfig.ts — import this before any react-native-ui-lib component + +import {Colors, Typography, Spacings, ThemeManager} from 'react-native-ui-lib'; + +// 1. Palette +Colors.loadColors({ + primary: '#2364AA', + secondary: '#81C3D7', + error: '#E63B2E', + success: '#ADC76F', +}); + +// 2. Override design tokens (optional — defaults are pre-loaded) +Colors.loadSchemes({ + light: { + $textDefault: Colors.dark10, + $backgroundDefault: Colors.white, + $outlineDefault: Colors.grey50, + }, + dark: { + $textDefault: Colors.white, + $backgroundDefault: Colors.dark10, + $outlineDefault: Colors.grey30, + }, +}); + +// 3. Typography +Typography.loadTypographies({ + heading: {fontSize: 28, fontWeight: '700', lineHeight: 36}, + body: {fontSize: 16, fontWeight: '400', lineHeight: 24}, + bodySmall: {fontSize: 14, fontWeight: '400', lineHeight: 20}, + caption: {fontSize: 12, fontWeight: '400', lineHeight: 16}, +}); + +// 4. Spacings +Spacings.loadSpacings({ + page: 20, + card: 16, +}); + +// 5. Component defaults +ThemeManager.setComponentTheme('Button', {borderRadius: 8}); +```