@butternutbox/pawprint-native 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +30 -0
- package/COMPONENT_GUIDELINES.md +610 -0
- package/README.md +72 -0
- package/dist/ibm-plex-sans-condensed-400-normal-I2XLJNNB.woff2 +0 -0
- package/dist/ibm-plex-sans-condensed-500-normal-IEQBNVGX.woff2 +0 -0
- package/dist/ibm-plex-sans-condensed-600-normal-UX5ZU5T6.woff2 +0 -0
- package/dist/ibm-plex-sans-condensed-700-normal-4PFYFTSO.woff2 +0 -0
- package/dist/ida-narrow-500-normal-C6I2PK4T.woff2 +0 -0
- package/dist/ida-narrow-700-normal-UPHPRIN6.woff2 +0 -0
- package/dist/index.cjs +2686 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +780 -0
- package/dist/index.d.ts +780 -0
- package/dist/index.js +2617 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.js +3 -0
- package/llms.txt +458 -0
- package/package.json +57 -0
- package/src/components/atoms/Avatar/Avatar.stories.tsx +125 -0
- package/src/components/atoms/Avatar/Avatar.tsx +159 -0
- package/src/components/atoms/Avatar/index.ts +7 -0
- package/src/components/atoms/Badge/Badge.stories.tsx +231 -0
- package/src/components/atoms/Badge/Badge.tsx +184 -0
- package/src/components/atoms/Badge/index.ts +2 -0
- package/src/components/atoms/Button/Button.stories.tsx +145 -0
- package/src/components/atoms/Button/Button.tsx +261 -0
- package/src/components/atoms/Button/index.ts +7 -0
- package/src/components/atoms/Hint/Hint.stories.tsx +84 -0
- package/src/components/atoms/Hint/Hint.tsx +59 -0
- package/src/components/atoms/Hint/index.ts +2 -0
- package/src/components/atoms/Icon/Icon.stories.tsx +200 -0
- package/src/components/atoms/Icon/Icon.tsx +112 -0
- package/src/components/atoms/Icon/index.ts +8 -0
- package/src/components/atoms/IconButton/IconButton.stories.tsx +162 -0
- package/src/components/atoms/IconButton/IconButton.tsx +227 -0
- package/src/components/atoms/IconButton/index.ts +7 -0
- package/src/components/atoms/Illustration/Illustration.stories.tsx +167 -0
- package/src/components/atoms/Illustration/Illustration.tsx +81 -0
- package/src/components/atoms/Illustration/index.ts +6 -0
- package/src/components/atoms/Input/Input.stories.tsx +142 -0
- package/src/components/atoms/Input/Input.tsx +110 -0
- package/src/components/atoms/Input/InputDescription.tsx +49 -0
- package/src/components/atoms/Input/InputError.tsx +39 -0
- package/src/components/atoms/Input/InputField.tsx +119 -0
- package/src/components/atoms/Input/InputLabel.tsx +61 -0
- package/src/components/atoms/Input/index.ts +10 -0
- package/src/components/atoms/Link/Link.stories.tsx +119 -0
- package/src/components/atoms/Link/Link.tsx +118 -0
- package/src/components/atoms/Link/index.ts +2 -0
- package/src/components/atoms/Logo/Logo.registry.ts +39 -0
- package/src/components/atoms/Logo/Logo.tsx +68 -0
- package/src/components/atoms/Logo/index.ts +4 -0
- package/src/components/atoms/Spinner/Spinner.stories.tsx +98 -0
- package/src/components/atoms/Spinner/Spinner.tsx +91 -0
- package/src/components/atoms/Spinner/index.ts +2 -0
- package/src/components/atoms/Switch/Switch.stories.tsx +120 -0
- package/src/components/atoms/Switch/Switch.tsx +196 -0
- package/src/components/atoms/Switch/index.ts +2 -0
- package/src/components/atoms/Tag/Tag.stories.tsx +89 -0
- package/src/components/atoms/Tag/Tag.tsx +122 -0
- package/src/components/atoms/Tag/index.ts +2 -0
- package/src/components/atoms/Typography/Typography.stories.tsx +315 -0
- package/src/components/atoms/Typography/Typography.tsx +284 -0
- package/src/components/atoms/Typography/index.ts +2 -0
- package/src/components/atoms/index.ts +14 -0
- package/src/components/index.ts +2 -0
- package/src/components/molecules/ButtonDock/ButtonDock.stories.tsx +95 -0
- package/src/components/molecules/ButtonDock/ButtonDock.tsx +148 -0
- package/src/components/molecules/ButtonDock/index.ts +2 -0
- package/src/components/molecules/ButtonGroup/ButtonGroup.stories.tsx +82 -0
- package/src/components/molecules/ButtonGroup/ButtonGroup.tsx +94 -0
- package/src/components/molecules/ButtonGroup/index.ts +2 -0
- package/src/components/molecules/Checkbox/Checkbox.stories.tsx +148 -0
- package/src/components/molecules/Checkbox/Checkbox.tsx +279 -0
- package/src/components/molecules/Checkbox/CheckboxGroup.tsx +53 -0
- package/src/components/molecules/Checkbox/index.ts +4 -0
- package/src/components/molecules/Radio/Radio.stories.tsx +182 -0
- package/src/components/molecules/Radio/Radio.tsx +249 -0
- package/src/components/molecules/Radio/RadioGroup.tsx +142 -0
- package/src/components/molecules/Radio/index.ts +4 -0
- package/src/components/molecules/SegmentedControl/SegmentedControl.stories.tsx +151 -0
- package/src/components/molecules/SegmentedControl/SegmentedControl.tsx +323 -0
- package/src/components/molecules/SegmentedControl/index.ts +5 -0
- package/src/components/molecules/Slider/Slider.stories.tsx +144 -0
- package/src/components/molecules/Slider/Slider.tsx +303 -0
- package/src/components/molecules/Slider/index.ts +2 -0
- package/src/components/molecules/index.ts +6 -0
- package/src/fonts/ibm-plex-sans-condensed-400-normal.woff2 +0 -0
- package/src/fonts/ibm-plex-sans-condensed-500-normal.woff2 +0 -0
- package/src/fonts/ibm-plex-sans-condensed-600-normal.woff2 +0 -0
- package/src/fonts/ibm-plex-sans-condensed-700-normal.woff2 +0 -0
- package/src/fonts/ida-narrow-500-normal.woff2 +0 -0
- package/src/fonts/ida-narrow-700-normal.woff2 +0 -0
- package/src/fonts/index.ts +49 -0
- package/src/index.ts +9 -0
- package/src/theme/PawprintProvider.tsx +26 -0
- package/src/theme/ThemeProvider.tsx +63 -0
- package/src/theme/index.ts +5 -0
- package/src/theme/theme.ts +3 -0
- package/src/theme/utils.ts +31 -0
- package/src/types/fonts.d.ts +4 -0
- package/src/types/index.ts +1 -0
- package/src/types/theme.ts +24 -0
- package/tsconfig.json +5 -0
- package/tsup.config.ts +11 -0
package/eslint.config.js
ADDED
package/llms.txt
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
# Component Development – LLM Instructions (React Native)
|
|
2
|
+
|
|
3
|
+
Full reference: `packages/pawprint-native/COMPONENT_GUIDELINES.md`
|
|
4
|
+
|
|
5
|
+
Always use the equivalent component in `packages/pawprint-web/src/components` as a reference for API design (props, variants, sizes). Convert it to React Native using `@rn-primitives/*`, `@emotion/native`, and standard React Native primitives (`View`, `Pressable`, `Text`, etc.).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# 🔒 Hard Rules (MUST / MUST NOT)
|
|
10
|
+
|
|
11
|
+
## Code Integrity
|
|
12
|
+
|
|
13
|
+
- **MUST NOT** modify any existing file unless explicitly required by Step 7 (exports only).
|
|
14
|
+
- **MUST NOT** refactor, improve, or clean up unrelated code.
|
|
15
|
+
- **MUST NOT** recreate components that already exist.
|
|
16
|
+
- **MUST** reuse existing components (Typography, Icon, Spinner, etc.) whenever possible.
|
|
17
|
+
- Existing code includes:
|
|
18
|
+
- Any file not newly created for this component
|
|
19
|
+
- Tokens
|
|
20
|
+
- Theme
|
|
21
|
+
- Other components
|
|
22
|
+
- Shared types
|
|
23
|
+
- Utilities and fonts
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Token Usage (Strict Order)
|
|
28
|
+
|
|
29
|
+
You **MUST** follow this exact fallback order:
|
|
30
|
+
|
|
31
|
+
1. Component tokens
|
|
32
|
+
`theme.tokens.components.[component].*`
|
|
33
|
+
|
|
34
|
+
2. Semantic tokens
|
|
35
|
+
`theme.tokens.semantics.*`
|
|
36
|
+
|
|
37
|
+
3. Primitive tokens
|
|
38
|
+
`theme.tokens.primitives.*`
|
|
39
|
+
|
|
40
|
+
4. If no token exists → hardcode value with comment:
|
|
41
|
+
`// TODO: Add token`
|
|
42
|
+
|
|
43
|
+
Rules:
|
|
44
|
+
- **MUST NOT** hardcode values if a token exists.
|
|
45
|
+
- Layout values (gap, padding, margin, etc.) MUST use tokens if available.
|
|
46
|
+
- If no layout token exists, hardcode with `// TODO: Add token`.
|
|
47
|
+
- Token values are strings — use `parseTokenValue()` to convert:
|
|
48
|
+
```tsx
|
|
49
|
+
const parseTokenValue = (value: string): number => parseFloat(value)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Visual Styling Rules
|
|
55
|
+
|
|
56
|
+
- **MUST NOT** add visual styles not defined in:
|
|
57
|
+
- Design tokens
|
|
58
|
+
- Figma design
|
|
59
|
+
|
|
60
|
+
This includes:
|
|
61
|
+
- opacity (unless for disabled states in tokens)
|
|
62
|
+
- letterSpacing
|
|
63
|
+
- shadows
|
|
64
|
+
- extra borders
|
|
65
|
+
- etc.
|
|
66
|
+
|
|
67
|
+
Animations are allowed **ONLY if present in tokens or Figma** — use `Animated` or `react-native-reanimated`.
|
|
68
|
+
|
|
69
|
+
Layout properties (flexDirection, gap, margin, padding, alignItems, justifyContent) are allowed for structure.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Layout Stability
|
|
74
|
+
|
|
75
|
+
- **MUST prevent layout shifts**
|
|
76
|
+
- Use `transparent` borders instead of removing borders.
|
|
77
|
+
- Use fixed `height`/`minWidth` from tokens to maintain consistent sizing.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Scope Restrictions
|
|
82
|
+
|
|
83
|
+
You may only create files inside:
|
|
84
|
+
|
|
85
|
+
`packages/pawprint-native/src/.../<component>`
|
|
86
|
+
|
|
87
|
+
You may only modify:
|
|
88
|
+
- Required export barrel files (Step 7)
|
|
89
|
+
|
|
90
|
+
You **MUST NOT** modify:
|
|
91
|
+
- Tokens
|
|
92
|
+
- Theme
|
|
93
|
+
- Unrelated components
|
|
94
|
+
- Config files
|
|
95
|
+
- Storybook config
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## i18n
|
|
100
|
+
|
|
101
|
+
- The design system is **i18n-agnostic**. Components MUST NOT handle translation, interpolation, or localisation.
|
|
102
|
+
- MUST NOT import or depend on `i18next`, `react-i18next`, or any translation library.
|
|
103
|
+
- Components accept `children` or typed props — never translation keys.
|
|
104
|
+
- Consuming apps are responsible for resolving translations and passing already-rendered strings or React nodes.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
# 🧭 Workflow
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Step 0 – Requirement Gate (MANDATORY)
|
|
113
|
+
|
|
114
|
+
Ask ONE question at a time.
|
|
115
|
+
|
|
116
|
+
If any answer is missing:
|
|
117
|
+
→ Ask only the next missing question.
|
|
118
|
+
→ STOP.
|
|
119
|
+
→ Do NOT proceed.
|
|
120
|
+
|
|
121
|
+
Questions:
|
|
122
|
+
|
|
123
|
+
a. Ask component level using `ask_user_question` tool with options:
|
|
124
|
+
- atoms
|
|
125
|
+
- molecules
|
|
126
|
+
- organisms
|
|
127
|
+
|
|
128
|
+
b. Ask for component name (plain text)
|
|
129
|
+
|
|
130
|
+
c. Ask for Figma URL (plain text)
|
|
131
|
+
|
|
132
|
+
d. Ask for specific requirements or constraints (plain text)
|
|
133
|
+
|
|
134
|
+
Examples:
|
|
135
|
+
- Exclude certain variants
|
|
136
|
+
- Custom prop structure
|
|
137
|
+
- Platform-specific behavior
|
|
138
|
+
- Accessibility constraints
|
|
139
|
+
|
|
140
|
+
⚠️ DO NOT proceed until ALL answers are received.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Step 1 – Mandatory Analysis (Before Implementation)
|
|
145
|
+
|
|
146
|
+
### 1. RN Primitives Check
|
|
147
|
+
|
|
148
|
+
- Check if `@rn-primitives` has a matching component:
|
|
149
|
+
```
|
|
150
|
+
@rn-primitives/checkbox
|
|
151
|
+
@rn-primitives/switch
|
|
152
|
+
@rn-primitives/slider
|
|
153
|
+
@rn-primitives/accordion
|
|
154
|
+
@rn-primitives/dialog
|
|
155
|
+
@rn-primitives/radio-group
|
|
156
|
+
@rn-primitives/select
|
|
157
|
+
@rn-primitives/tabs
|
|
158
|
+
@rn-primitives/toggle
|
|
159
|
+
@rn-primitives/toggle-group
|
|
160
|
+
@rn-primitives/separator
|
|
161
|
+
@rn-primitives/progress
|
|
162
|
+
@rn-primitives/label
|
|
163
|
+
@rn-primitives/collapsible
|
|
164
|
+
@rn-primitives/popover
|
|
165
|
+
@rn-primitives/tooltip
|
|
166
|
+
@rn-primitives/context-menu
|
|
167
|
+
@rn-primitives/dropdown-menu
|
|
168
|
+
@rn-primitives/menubar
|
|
169
|
+
@rn-primitives/navigation-menu
|
|
170
|
+
@rn-primitives/hover-card
|
|
171
|
+
@rn-primitives/alert-dialog
|
|
172
|
+
@rn-primitives/toolbar
|
|
173
|
+
@rn-primitives/toast
|
|
174
|
+
@rn-primitives/aspect-ratio
|
|
175
|
+
@rn-primitives/table
|
|
176
|
+
@rn-primitives/portal
|
|
177
|
+
```
|
|
178
|
+
- If a matching primitive exists → MUST use it as the foundation.
|
|
179
|
+
- MUST NOT recreate accessible behavior manually when a primitive is available.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### 2. Web Component Reference
|
|
184
|
+
|
|
185
|
+
- Use `read_file` on:
|
|
186
|
+
`packages/pawprint-web/src/components/{atoms,molecules}/[ComponentName]/[ComponentName].tsx`
|
|
187
|
+
- Study the web component's:
|
|
188
|
+
- Prop types and variants
|
|
189
|
+
- Token paths used
|
|
190
|
+
- JSDoc documentation
|
|
191
|
+
- API surface
|
|
192
|
+
|
|
193
|
+
You MUST document:
|
|
194
|
+
- Which props and variants the web component has
|
|
195
|
+
- How they will map to the native implementation
|
|
196
|
+
- Any platform-specific differences
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### 3. Existing Native Component Audit
|
|
201
|
+
|
|
202
|
+
- Use `list_dir` and `read_file` on:
|
|
203
|
+
`packages/pawprint-native/src`
|
|
204
|
+
- Identify reusable native components (Typography, Icon, Spinner, Illustration, etc.).
|
|
205
|
+
|
|
206
|
+
You MUST document:
|
|
207
|
+
- Which existing components will be reused
|
|
208
|
+
- How they map to Figma elements
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### 4. Token Audit
|
|
213
|
+
|
|
214
|
+
- Use `read_file` or `grep_search` on:
|
|
215
|
+
`packages/pawprint-tokens/build/tokens.d.ts`
|
|
216
|
+
|
|
217
|
+
You MUST document:
|
|
218
|
+
- Exact token paths available
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
`theme.tokens.components.buttons.button.colour.background.primary`
|
|
222
|
+
|
|
223
|
+
You MUST list exact token paths before coding.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
### 5. Figma Analysis
|
|
228
|
+
|
|
229
|
+
- Use `mcp0_get_screenshot`
|
|
230
|
+
- Document:
|
|
231
|
+
|
|
232
|
+
- All variants
|
|
233
|
+
- All states (default, pressed, disabled, error, selected, checked, etc.)
|
|
234
|
+
- Exact layout order
|
|
235
|
+
- Element positioning
|
|
236
|
+
- Spacing
|
|
237
|
+
- Sizing
|
|
238
|
+
- Typography
|
|
239
|
+
- Colors
|
|
240
|
+
|
|
241
|
+
You MUST NOT invent variants or states not visible in Figma.
|
|
242
|
+
|
|
243
|
+
Note: React Native does not have hover states — skip hover unless the component runs on web via Expo.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Step 2 – Decision Points
|
|
248
|
+
|
|
249
|
+
If multiple valid approaches exist:
|
|
250
|
+
|
|
251
|
+
- Use `ask_user_question`
|
|
252
|
+
- Present:
|
|
253
|
+
- What was detected
|
|
254
|
+
- Architectural recommendation
|
|
255
|
+
- Clear options
|
|
256
|
+
- Allow multi-select if needed
|
|
257
|
+
|
|
258
|
+
Do NOT proceed without user confirmation when structural decisions exist.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Step 3 – Implementation Rules
|
|
263
|
+
|
|
264
|
+
- Only implement what is explicitly requested.
|
|
265
|
+
- Use `@rn-primitives` components if available as the accessible foundation.
|
|
266
|
+
- Use standard React Native primitives for layout:
|
|
267
|
+
|
|
268
|
+
- `View` → generic container
|
|
269
|
+
- `Pressable` → interactive elements (NOT `TouchableOpacity`)
|
|
270
|
+
- `ScrollView` → scrollable content
|
|
271
|
+
- `FlatList` → long lists
|
|
272
|
+
|
|
273
|
+
- Compose existing design system components (Typography, Icon, Spinner).
|
|
274
|
+
- Apply tokens using strict fallback strategy.
|
|
275
|
+
- Use `styled` from `@emotion/native` for token-driven styles.
|
|
276
|
+
- Use `useTheme` from `@emotion/react` for dynamic token access in component body.
|
|
277
|
+
- Use `parseTokenValue()` to convert string tokens to numbers.
|
|
278
|
+
- Prefix internal styled props to avoid clashes with native props:
|
|
279
|
+
```tsx
|
|
280
|
+
// ✅ GOOD: badgeVariant, badgeSize (prefixed)
|
|
281
|
+
// ❌ BAD: variant, size (may clash with native props)
|
|
282
|
+
```
|
|
283
|
+
- Use proper TypeScript types.
|
|
284
|
+
- Use `React.forwardRef<View, Props>` (or `Pressable`, `TextInput` as appropriate).
|
|
285
|
+
- Always use `Typography` for text — NEVER raw `<Text>`.
|
|
286
|
+
- Add comprehensive JSDoc comments on the component export:
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
/**
|
|
290
|
+
* Short description of what the component is and does.
|
|
291
|
+
*
|
|
292
|
+
* @param {"variant-a" | "variant-b"} [variant="variant-a"] - Description.
|
|
293
|
+
* @param {"sm" | "md" | "lg"} [size="md"] - Description.
|
|
294
|
+
* @param {boolean} [disabled=false] - Description.
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```tsx
|
|
298
|
+
* import { Component } from "@butternutbox/pawprint-native"
|
|
299
|
+
*
|
|
300
|
+
* <Component size="md">Content</Component>
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
export const Component = React.forwardRef<View, ComponentProps>(
|
|
304
|
+
(props, ref) => { ... }
|
|
305
|
+
)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
- Do NOT add comments inside TypeScript type definitions.
|
|
309
|
+
- Follow `COMPONENT_GUIDELINES.md`.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Step 4 – Interactive States
|
|
314
|
+
|
|
315
|
+
React Native has no CSS pseudo-classes. Handle states via:
|
|
316
|
+
|
|
317
|
+
- `Pressable` `style` callback for pressed state:
|
|
318
|
+
```tsx
|
|
319
|
+
<Pressable style={({ pressed }) => [
|
|
320
|
+
styles.base,
|
|
321
|
+
pressed && styles.pressed
|
|
322
|
+
]} />
|
|
323
|
+
```
|
|
324
|
+
- Props on styled components for variant/disabled states:
|
|
325
|
+
```tsx
|
|
326
|
+
const StyledButton = styled(Pressable)<{ isDisabled: boolean }>(
|
|
327
|
+
({ theme, isDisabled }) => ({
|
|
328
|
+
backgroundColor: isDisabled
|
|
329
|
+
? tokens.colour.background.disabled
|
|
330
|
+
: tokens.colour.background.default,
|
|
331
|
+
opacity: isDisabled ? 0.5 : 1
|
|
332
|
+
})
|
|
333
|
+
)
|
|
334
|
+
```
|
|
335
|
+
- `Animated` or `react-native-reanimated` for animated transitions.
|
|
336
|
+
|
|
337
|
+
States to implement:
|
|
338
|
+
- default
|
|
339
|
+
- pressed (via Pressable callback)
|
|
340
|
+
- disabled (via `disabled` prop + visual tokens)
|
|
341
|
+
- error / success (via `state` or `variant` prop)
|
|
342
|
+
- checked / selected (for toggles, checkboxes, radios)
|
|
343
|
+
|
|
344
|
+
Do NOT implement hover states (not available on mobile).
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Step 5 – Accessibility
|
|
349
|
+
|
|
350
|
+
- Use `accessible={true}` on interactive elements.
|
|
351
|
+
- Set `accessibilityRole`:
|
|
352
|
+
- `"button"` for buttons
|
|
353
|
+
- `"checkbox"` for checkboxes
|
|
354
|
+
- `"switch"` for switches
|
|
355
|
+
- `"image"` for icons/illustrations with labels
|
|
356
|
+
- `"link"` for links
|
|
357
|
+
- `"adjustable"` for sliders
|
|
358
|
+
- `"radio"` for radio buttons
|
|
359
|
+
- Set `accessibilityLabel` for icon-only or image components.
|
|
360
|
+
- Set `accessibilityState` for stateful components:
|
|
361
|
+
```tsx
|
|
362
|
+
accessibilityState={{ disabled, checked, selected }}
|
|
363
|
+
```
|
|
364
|
+
- Set `accessibilityHint` when the action is not obvious from the label.
|
|
365
|
+
- Ensure sufficient color contrast (use semantic tokens).
|
|
366
|
+
- Dev-mode `console.warn` when `aria-label` is missing on meaningful icons.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Step 6 – Storybook Requirements
|
|
371
|
+
|
|
372
|
+
- Co-locate stories in the component folder: `[ComponentName].stories.tsx`
|
|
373
|
+
- Use CSF format with `export default { title, component, argTypes }`.
|
|
374
|
+
- Every argType MUST have a `description` field.
|
|
375
|
+
- Create stories for:
|
|
376
|
+
- ALL variants
|
|
377
|
+
- ALL states
|
|
378
|
+
- Include:
|
|
379
|
+
- `Playground` story with args for every controllable prop
|
|
380
|
+
- `AllVariants` story showcasing all variant/size combinations
|
|
381
|
+
- State-specific stories (disabled, loading, error, etc.)
|
|
382
|
+
- Use `StyleSheet.create` for story layout styles.
|
|
383
|
+
- For interactive controlled components, use `React.useState` in the story render.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Step 7 – Export Requirements
|
|
388
|
+
|
|
389
|
+
You MUST:
|
|
390
|
+
|
|
391
|
+
1. Create `index.ts` in the component folder:
|
|
392
|
+
```tsx
|
|
393
|
+
export { [Component] } from "./[Component]"
|
|
394
|
+
export type { [Component]Props } from "./[Component]"
|
|
395
|
+
```
|
|
396
|
+
2. Export from:
|
|
397
|
+
- `atoms/index.ts`
|
|
398
|
+
- OR `molecules/index.ts`
|
|
399
|
+
- OR `organisms/index.ts`
|
|
400
|
+
3. Export from main:
|
|
401
|
+
`components/index.ts`
|
|
402
|
+
4. Verify the export chain reaches `src/index.ts`.
|
|
403
|
+
|
|
404
|
+
No other files may be modified.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Step 8 – Visual Verification
|
|
409
|
+
|
|
410
|
+
- Compare Storybook rendering with Figma screenshot.
|
|
411
|
+
- Verify:
|
|
412
|
+
- Spacing
|
|
413
|
+
- Sizing
|
|
414
|
+
- Colors
|
|
415
|
+
- Typography
|
|
416
|
+
- Alignment
|
|
417
|
+
- Test all interactive states visually.
|
|
418
|
+
- Test on both iOS and Android (or Expo web) if possible.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Step 9 – Parity Check
|
|
423
|
+
|
|
424
|
+
- Compare the native component API with the web version:
|
|
425
|
+
- Same prop names where possible
|
|
426
|
+
- Same variants and sizes
|
|
427
|
+
- Same token paths
|
|
428
|
+
- Document any platform-specific differences in JSDoc:
|
|
429
|
+
```tsx
|
|
430
|
+
/**
|
|
431
|
+
* ...
|
|
432
|
+
* Note: Unlike the web version, `top` and `bottom` accept numbers only (not strings).
|
|
433
|
+
*/
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
# 📦 Required Output Format
|
|
439
|
+
|
|
440
|
+
Before coding, you MUST provide:
|
|
441
|
+
|
|
442
|
+
1. Web component reference (props, variants from web version)
|
|
443
|
+
2. RN Primitives check result (available primitive or "none — building custom")
|
|
444
|
+
3. Reuse mapping (existing native components → Figma elements)
|
|
445
|
+
4. Token paths that will be used (exact strings)
|
|
446
|
+
5. Proposed component API (props + variants)
|
|
447
|
+
6. Platform differences from web version (if any)
|
|
448
|
+
7. File plan (new files + modified export files)
|
|
449
|
+
|
|
450
|
+
After coding:
|
|
451
|
+
|
|
452
|
+
- Provide code grouped by file
|
|
453
|
+
- Clearly label each file path
|
|
454
|
+
- List exactly which files were:
|
|
455
|
+
- Created
|
|
456
|
+
- Modified
|
|
457
|
+
|
|
458
|
+
You MUST NOT output anything outside this structure.
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@butternutbox/pawprint-native",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "ButternutBox Pawprint Design System - React Native Components",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"dev": "tsup --watch",
|
|
19
|
+
"clean": "rm -rf .turbo node_modules dist",
|
|
20
|
+
"lint:check": "eslint src --max-warnings 0",
|
|
21
|
+
"type:check": "tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@butternutbox/pawprint-tokens": "^0.0.1",
|
|
25
|
+
"@emotion/native": "^11.11.0",
|
|
26
|
+
"@emotion/react": "^11.14.0",
|
|
27
|
+
"@rn-primitives/avatar": "^1.4.0",
|
|
28
|
+
"@rn-primitives/checkbox": "^1.4.0",
|
|
29
|
+
"@rn-primitives/portal": "^1.0.5",
|
|
30
|
+
"@rn-primitives/slider": "^1.4.0",
|
|
31
|
+
"@rn-primitives/slot": "^1.0.5",
|
|
32
|
+
"@rn-primitives/switch": "^1.4.0",
|
|
33
|
+
"@rn-primitives/toggle": "^1.4.0",
|
|
34
|
+
"@rn-primitives/types": "^1.0.5",
|
|
35
|
+
"expo": "^55.0.9"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": ">=18.0.0",
|
|
39
|
+
"react-native": ">=0.74.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@butternutbox/pawprint-icons": "workspace:*",
|
|
43
|
+
"@butternutbox/pawprint-illustrations": "workspace:*",
|
|
44
|
+
"@repo/eslint-config": "workspace:*",
|
|
45
|
+
"@repo/typescript-config": "workspace:*",
|
|
46
|
+
"@types/react": "~19.1.10",
|
|
47
|
+
"@types/react-native": "^0.73.0",
|
|
48
|
+
"eslint": "^8.57.0",
|
|
49
|
+
"react": "19.1.0",
|
|
50
|
+
"react-native": "0.81.5",
|
|
51
|
+
"tsup": "^8.5.0",
|
|
52
|
+
"typescript": "^5.7.3"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { View, StyleSheet } from "react-native"
|
|
3
|
+
import { Avatar } from "./Avatar"
|
|
4
|
+
import type { AvatarProps } from "./Avatar"
|
|
5
|
+
|
|
6
|
+
const DOG_PHOTO_URL = "https://placedog.net/200/200"
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: "Atoms/Avatar",
|
|
10
|
+
component: Avatar,
|
|
11
|
+
argTypes: {
|
|
12
|
+
src: {
|
|
13
|
+
control: { type: "text" },
|
|
14
|
+
description: "Image source URL"
|
|
15
|
+
},
|
|
16
|
+
alt: {
|
|
17
|
+
control: { type: "text" },
|
|
18
|
+
description: "Accessible label (required)"
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
control: { type: "select" },
|
|
22
|
+
options: ["sm", "md", "lg"],
|
|
23
|
+
description: "Size variant"
|
|
24
|
+
},
|
|
25
|
+
border: {
|
|
26
|
+
control: { type: "select" },
|
|
27
|
+
options: ["none", "sm", "md"],
|
|
28
|
+
description: "Border width variant"
|
|
29
|
+
},
|
|
30
|
+
fallbackType: {
|
|
31
|
+
control: { type: "select" },
|
|
32
|
+
options: ["string", "image"],
|
|
33
|
+
description: "Fallback display type (auto-detected)"
|
|
34
|
+
},
|
|
35
|
+
fallbackString: {
|
|
36
|
+
control: { type: "text" },
|
|
37
|
+
description: "Text to display as fallback (e.g. user initials)"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const Default = (args: AvatarProps) => <Avatar {...args} />
|
|
43
|
+
Default.args = {
|
|
44
|
+
src: DOG_PHOTO_URL,
|
|
45
|
+
alt: "User Photo",
|
|
46
|
+
size: "md",
|
|
47
|
+
border: "none"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const Sizes = () => (
|
|
51
|
+
<View style={styles.row}>
|
|
52
|
+
<Avatar src={DOG_PHOTO_URL} alt="Small avatar" size="sm" />
|
|
53
|
+
<Avatar src={DOG_PHOTO_URL} alt="Medium avatar" size="md" />
|
|
54
|
+
<Avatar src={DOG_PHOTO_URL} alt="Large avatar" size="lg" />
|
|
55
|
+
</View>
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
export const Borders = () => (
|
|
59
|
+
<View style={styles.row}>
|
|
60
|
+
<Avatar src={DOG_PHOTO_URL} alt="No border" border="none" />
|
|
61
|
+
<Avatar src={DOG_PHOTO_URL} alt="Small border" border="sm" />
|
|
62
|
+
<Avatar src={DOG_PHOTO_URL} alt="Medium border" border="md" />
|
|
63
|
+
</View>
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
export const FallbackString = (args: AvatarProps) => <Avatar {...args} />
|
|
67
|
+
FallbackString.args = {
|
|
68
|
+
alt: "User initials",
|
|
69
|
+
fallbackString: "JD",
|
|
70
|
+
size: "md"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const FallbackStringSizes = () => (
|
|
74
|
+
<View style={styles.row}>
|
|
75
|
+
<Avatar alt="Small" fallbackString="AB" size="sm" />
|
|
76
|
+
<Avatar alt="Medium" fallbackString="CD" size="md" />
|
|
77
|
+
<Avatar alt="Large" fallbackString="EF" size="lg" />
|
|
78
|
+
</View>
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
export const FallbackImage = (args: AvatarProps) => <Avatar {...args} />
|
|
82
|
+
FallbackImage.args = {
|
|
83
|
+
alt: "Default avatar icon",
|
|
84
|
+
fallbackType: "image",
|
|
85
|
+
size: "md"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const BrokenImage = () => (
|
|
89
|
+
<View style={styles.row}>
|
|
90
|
+
<Avatar
|
|
91
|
+
src="https://broken-link.com/photo.jpg"
|
|
92
|
+
alt="Broken with string fallback"
|
|
93
|
+
fallbackString="JD"
|
|
94
|
+
fallbackType="string"
|
|
95
|
+
/>
|
|
96
|
+
<Avatar
|
|
97
|
+
src="https://broken-link.com/photo.jpg"
|
|
98
|
+
alt="Broken with image fallback"
|
|
99
|
+
fallbackType="image"
|
|
100
|
+
/>
|
|
101
|
+
</View>
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
export const BordersWithFallback = () => (
|
|
105
|
+
<View style={styles.row}>
|
|
106
|
+
<Avatar alt="No border" fallbackString="AB" border="none" />
|
|
107
|
+
<Avatar alt="Small border" fallbackString="CD" border="sm" />
|
|
108
|
+
<Avatar alt="Medium border" fallbackString="EF" border="md" />
|
|
109
|
+
</View>
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
export const LongInitials = (args: AvatarProps) => <Avatar {...args} />
|
|
113
|
+
LongInitials.args = {
|
|
114
|
+
alt: "Long initials",
|
|
115
|
+
fallbackString: "ABC",
|
|
116
|
+
size: "lg"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const styles = StyleSheet.create({
|
|
120
|
+
row: {
|
|
121
|
+
flexDirection: "row",
|
|
122
|
+
gap: 16,
|
|
123
|
+
alignItems: "center"
|
|
124
|
+
}
|
|
125
|
+
})
|