@mgcrea/react-native-tailwind 0.4.0 → 0.5.0

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.
Files changed (71) hide show
  1. package/README.md +443 -13
  2. package/dist/babel/index.cjs +804 -274
  3. package/dist/babel/index.d.ts +2 -1
  4. package/dist/babel/index.ts +319 -16
  5. package/dist/components/Pressable.d.ts +32 -0
  6. package/dist/components/Pressable.js +1 -0
  7. package/dist/components/TextInput.d.ts +56 -0
  8. package/dist/components/TextInput.js +1 -0
  9. package/dist/index.d.ts +9 -2
  10. package/dist/index.js +1 -1
  11. package/dist/parser/aspectRatio.d.ts +16 -0
  12. package/dist/parser/aspectRatio.js +1 -0
  13. package/dist/parser/aspectRatio.test.d.ts +1 -0
  14. package/dist/parser/aspectRatio.test.js +1 -0
  15. package/dist/parser/borders.js +1 -1
  16. package/dist/parser/borders.test.d.ts +1 -0
  17. package/dist/parser/borders.test.js +1 -0
  18. package/dist/parser/colors.d.ts +1 -0
  19. package/dist/parser/colors.js +1 -1
  20. package/dist/parser/colors.test.d.ts +1 -0
  21. package/dist/parser/colors.test.js +1 -0
  22. package/dist/parser/index.d.ts +4 -0
  23. package/dist/parser/index.js +1 -1
  24. package/dist/parser/layout.d.ts +2 -0
  25. package/dist/parser/layout.js +1 -1
  26. package/dist/parser/layout.test.d.ts +1 -0
  27. package/dist/parser/layout.test.js +1 -0
  28. package/dist/parser/modifiers.d.ts +47 -0
  29. package/dist/parser/modifiers.js +1 -0
  30. package/dist/parser/modifiers.test.d.ts +1 -0
  31. package/dist/parser/modifiers.test.js +1 -0
  32. package/dist/parser/shadows.d.ts +26 -0
  33. package/dist/parser/shadows.js +1 -0
  34. package/dist/parser/shadows.test.d.ts +1 -0
  35. package/dist/parser/shadows.test.js +1 -0
  36. package/dist/parser/sizing.test.d.ts +1 -0
  37. package/dist/parser/sizing.test.js +1 -0
  38. package/dist/parser/spacing.d.ts +1 -1
  39. package/dist/parser/spacing.js +1 -1
  40. package/dist/parser/spacing.test.d.ts +1 -0
  41. package/dist/parser/spacing.test.js +1 -0
  42. package/dist/parser/typography.d.ts +2 -1
  43. package/dist/parser/typography.js +1 -1
  44. package/dist/parser/typography.test.d.ts +1 -0
  45. package/dist/parser/typography.test.js +1 -0
  46. package/dist/types.d.ts +5 -2
  47. package/package.json +7 -6
  48. package/src/babel/index.ts +319 -16
  49. package/src/components/Pressable.tsx +46 -0
  50. package/src/components/TextInput.tsx +90 -0
  51. package/src/index.ts +20 -2
  52. package/src/parser/aspectRatio.test.ts +191 -0
  53. package/src/parser/aspectRatio.ts +73 -0
  54. package/src/parser/borders.test.ts +329 -0
  55. package/src/parser/borders.ts +187 -108
  56. package/src/parser/colors.test.ts +335 -0
  57. package/src/parser/colors.ts +117 -6
  58. package/src/parser/index.ts +13 -2
  59. package/src/parser/layout.test.ts +459 -0
  60. package/src/parser/layout.ts +128 -0
  61. package/src/parser/modifiers.test.ts +375 -0
  62. package/src/parser/modifiers.ts +104 -0
  63. package/src/parser/shadows.test.ts +201 -0
  64. package/src/parser/shadows.ts +133 -0
  65. package/src/parser/sizing.test.ts +256 -0
  66. package/src/parser/spacing.test.ts +226 -0
  67. package/src/parser/spacing.ts +93 -138
  68. package/src/parser/typography.test.ts +221 -0
  69. package/src/parser/typography.ts +143 -112
  70. package/src/types.ts +2 -2
  71. package/dist/react-native.d.js +0 -1
package/README.md CHANGED
@@ -15,14 +15,15 @@ Compile-time Tailwind CSS for React Native with zero runtime overhead. Transform
15
15
  ## Features
16
16
 
17
17
  - ⚡ **Zero runtime overhead** — All transformations happen at compile time
18
- - 🎯 **Babel-only setup** — No Metro configuration required (like Reanimated)
18
+ - 🔧 **No dependencies** — Direct-to-React-Native style generation without tailwindcss package
19
+ - 🎯 **Babel-only setup** — No Metro configuration required
19
20
  - 📝 **TypeScript-first** — Full type safety and autocomplete support
20
- - 🚀 **Optimized performance** — Uses `StyleSheet.create` for optimal React Native performance
21
+ - 🚀 **Optimized performance** — Compiles down to `StyleSheet.create` for optimal performance
21
22
  - 📦 **Small bundle size** — Only includes actual styles used in your app
22
- - 🔧 **No dependencies** — Direct-to-React-Native style generation without tailwindcss package
23
23
  - 🎨 **Custom colors** — Extend the default palette via `tailwind.config.*`
24
24
  - 📐 **Arbitrary values** — Use custom sizes and borders: `w-[123px]`, `rounded-[20px]`
25
25
  - 🔀 **Dynamic className** — Conditional styles with hybrid compile-time optimization
26
+ - 🎯 **State modifiers** — `active:`, `hover:`, `focus:`, and `disabled:` modifiers for interactive components
26
27
  - 📜 **Special style props** — Support for `contentContainerClassName`, `columnWrapperClassName`, and more
27
28
 
28
29
  ## Installation
@@ -134,6 +135,8 @@ const _twStyles = StyleSheet.create({
134
135
 
135
136
  **Available sizes:** `0`, `0.5`, `1`, `1.5`, `2`, `2.5`, `3`, `3.5`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `14`, `16`, `20`, `24`, `28`, `32`, `36`, `40`, `44`, `48`, `52`, `56`, `60`, `64`, `72`, `80`, `96`
136
137
 
138
+ **Arbitrary values:** `m-[16px]`, `p-[20]`, `mx-[24px]`, `gap-[12px]` — Custom spacing values (px only)
139
+
137
140
  ### Layout
138
141
 
139
142
  **Flexbox:**
@@ -164,6 +167,48 @@ const _twStyles = StyleSheet.create({
164
167
 
165
168
  > **Note:** You can extend the color palette with custom colors via `tailwind.config.*` — see [Custom Colors](#custom-colors)
166
169
 
170
+ **Opacity Modifiers:**
171
+
172
+ Apply transparency to any color using the `/` operator with a percentage value (0-100):
173
+
174
+ ```tsx
175
+ <View className="bg-black/50 p-4"> {/* 50% opacity black background */}
176
+ <Text className="text-gray-900/80"> {/* 80% opacity gray text */}
177
+ Semi-transparent content
178
+ </Text>
179
+ <View className="border-2 border-red-500/30" /> {/* 30% opacity red border */}
180
+ </View>
181
+ ```
182
+
183
+ - Works with all color types: `bg-*`, `text-*`, `border-*`
184
+ - Supports preset colors: `bg-blue-500/75`, `text-red-600/50`
185
+ - Supports arbitrary colors: `bg-[#ff0000]/40`, `text-[#3B82F6]/90`
186
+ - Supports custom colors: `bg-primary/60`, `text-brand/80`
187
+ - Uses React Native's 8-digit hex format: `#RRGGBBAA`
188
+
189
+ **Examples:**
190
+
191
+ ```tsx
192
+ // Background opacity
193
+ <View className="bg-white/90" /> // #FFFFFFE6
194
+ <View className="bg-blue-500/50" /> // #3B82F680
195
+
196
+ // Text opacity
197
+ <Text className="text-black/70" /> // #000000B3
198
+ <Text className="text-gray-900/60" /> // #11182799
199
+
200
+ // Border opacity
201
+ <View className="border-2 border-purple-500/40" /> // #A855F766
202
+
203
+ // Arbitrary colors with opacity
204
+ <View className="bg-[#ff6b6b]/25" /> // #FF6B6B40
205
+
206
+ // Edge cases
207
+ <View className="bg-black/0" /> // Fully transparent
208
+ <View className="bg-blue-500/100" /> // Fully opaque
209
+ <View className="bg-transparent/50" /> // Remains transparent
210
+ ```
211
+
167
212
  ### Typography
168
213
 
169
214
  **Font Size:**
@@ -201,6 +246,152 @@ const _twStyles = StyleSheet.create({
201
246
 
202
247
  `border-solid`, `border-dotted`, `border-dashed`
203
248
 
249
+ ### Shadows & Elevation
250
+
251
+ Apply platform-specific shadows and elevation to create depth and visual hierarchy. Automatically uses iOS shadow properties or Android elevation based on the platform:
252
+
253
+ **Available shadow sizes:**
254
+
255
+ - `shadow-sm` — Subtle shadow
256
+ - `shadow` — Default shadow
257
+ - `shadow-md` — Medium shadow
258
+ - `shadow-lg` — Large shadow
259
+ - `shadow-xl` — Extra large shadow
260
+ - `shadow-2xl` — Extra extra large shadow
261
+ - `shadow-none` — Remove shadow
262
+
263
+ **Platform Differences:**
264
+
265
+ | Platform | Properties Used | Example Output |
266
+ |----------|----------------|----------------|
267
+ | **iOS** | `shadowColor`, `shadowOffset`, `shadowOpacity`, `shadowRadius` | Native iOS shadow rendering |
268
+ | **Android** | `elevation` | Native Android elevation (Material Design) |
269
+
270
+ **Examples:**
271
+
272
+ ```tsx
273
+ // Card with shadow
274
+ <View className="bg-white rounded-lg shadow-lg p-6 m-4">
275
+ <Text className="text-xl font-bold">Card Title</Text>
276
+ <Text className="text-gray-600">Card with large shadow</Text>
277
+ </View>
278
+
279
+ // Button with subtle shadow
280
+ <Pressable className="bg-blue-500 shadow-sm rounded-lg px-6 py-3">
281
+ <Text className="text-white">Press Me</Text>
282
+ </Pressable>
283
+
284
+ // Different shadow sizes
285
+ <View className="shadow-sm p-4">Subtle</View>
286
+ <View className="shadow p-4">Default</View>
287
+ <View className="shadow-md p-4">Medium</View>
288
+ <View className="shadow-lg p-4">Large</View>
289
+ <View className="shadow-xl p-4">Extra Large</View>
290
+ <View className="shadow-2xl p-4">2X Large</View>
291
+
292
+ // Remove shadow
293
+ <View className="shadow-lg md:shadow-none p-4">
294
+ Conditional shadow removal
295
+ </View>
296
+ ```
297
+
298
+ **iOS Shadow Values:**
299
+
300
+ | Class | shadowOpacity | shadowRadius | shadowOffset |
301
+ |-------|---------------|--------------|--------------|
302
+ | `shadow-sm` | 0.05 | 1 | { width: 0, height: 1 } |
303
+ | `shadow` | 0.1 | 2 | { width: 0, height: 1 } |
304
+ | `shadow-md` | 0.15 | 4 | { width: 0, height: 3 } |
305
+ | `shadow-lg` | 0.2 | 8 | { width: 0, height: 6 } |
306
+ | `shadow-xl` | 0.25 | 12 | { width: 0, height: 10 } |
307
+ | `shadow-2xl` | 0.3 | 24 | { width: 0, height: 20 } |
308
+
309
+ **Android Elevation Values:**
310
+
311
+ | Class | elevation |
312
+ |-------|-----------|
313
+ | `shadow-sm` | 1 |
314
+ | `shadow` | 2 |
315
+ | `shadow-md` | 4 |
316
+ | `shadow-lg` | 8 |
317
+ | `shadow-xl` | 12 |
318
+ | `shadow-2xl` | 16 |
319
+
320
+ > **Note:** All shadow parsing happens at compile-time with zero runtime overhead. The platform detection uses React Native's `Platform.select()` API.
321
+
322
+ ### Aspect Ratio
323
+
324
+ Control the aspect ratio of views using preset or arbitrary values. Requires React Native 0.71+:
325
+
326
+ **Preset values:**
327
+
328
+ - `aspect-auto` — Remove aspect ratio constraint
329
+ - `aspect-square` — 1:1 aspect ratio
330
+ - `aspect-video` — 16:9 aspect ratio
331
+
332
+ **Arbitrary values:**
333
+
334
+ Use `aspect-[width/height]` for custom ratios:
335
+
336
+ ```tsx
337
+ <View className="aspect-[4/3]" /> // 4:3 ratio (1.333...)
338
+ <View className="aspect-[16/9]" /> // 16:9 ratio (1.778...)
339
+ <View className="aspect-[21/9]" /> // 21:9 ultrawide
340
+ <View className="aspect-[9/16]" /> // 9:16 portrait
341
+ <View className="aspect-[3/2]" /> // 3:2 ratio (1.5)
342
+ ```
343
+
344
+ **Examples:**
345
+
346
+ ```tsx
347
+ // Square image container
348
+ <View className="w-full aspect-square bg-gray-200">
349
+ <Image source={avatar} className="w-full h-full" />
350
+ </View>
351
+
352
+ // Video player container (16:9)
353
+ <View className="w-full aspect-video bg-black">
354
+ <VideoPlayer />
355
+ </View>
356
+
357
+ // Instagram-style square grid
358
+ <View className="flex-row flex-wrap gap-2">
359
+ {photos.map((photo) => (
360
+ <View key={photo.id} className="w-[32%] aspect-square">
361
+ <Image source={photo.uri} className="w-full h-full rounded" />
362
+ </View>
363
+ ))}
364
+ </View>
365
+
366
+ // Custom aspect ratio for wide images
367
+ <View className="w-full aspect-[21/9] rounded-lg overflow-hidden">
368
+ <Image source={banner} className="w-full h-full" resizeMode="cover" />
369
+ </View>
370
+
371
+ // Portrait orientation
372
+ <View className="h-full aspect-[9/16]">
373
+ <Story />
374
+ </View>
375
+
376
+ // Remove aspect ratio constraint
377
+ <View className="aspect-square md:aspect-auto">
378
+ Responsive aspect ratio
379
+ </View>
380
+ ```
381
+
382
+ **Common Aspect Ratios:**
383
+
384
+ | Ratio | Class | Decimal | Use Case |
385
+ |-------|-------|---------|----------|
386
+ | 1:1 | `aspect-square` | 1.0 | Profile pictures, thumbnails |
387
+ | 16:9 | `aspect-video` | 1.778 | Videos, landscape photos |
388
+ | 4:3 | `aspect-[4/3]` | 1.333 | Standard photos |
389
+ | 3:2 | `aspect-[3/2]` | 1.5 | Classic photography |
390
+ | 21:9 | `aspect-[21/9]` | 2.333 | Ultrawide/cinematic |
391
+ | 9:16 | `aspect-[9/16]` | 0.5625 | Stories, vertical video |
392
+
393
+ > **Note:** The aspect ratio is calculated as `width / height`. When combined with `w-full`, the height will be automatically calculated to maintain the ratio.
394
+
204
395
  ### Sizing
205
396
 
206
397
  - `w-{size}`, `h-{size}` — Width/height
@@ -235,14 +426,18 @@ export function MyComponent() {
235
426
  ### Card Component
236
427
 
237
428
  ```tsx
238
- import { View, Text, Pressable } from "react-native";
429
+ import { View, Text } from "react-native";
430
+ import { Pressable } from "@mgcrea/react-native-tailwind";
239
431
 
240
432
  export function Card({ title, description, onPress }) {
241
433
  return (
242
434
  <View className="bg-white rounded-lg p-6 mb-4 border border-gray-200">
243
435
  <Text className="text-xl font-semibold text-gray-900 mb-2">{title}</Text>
244
436
  <Text className="text-base text-gray-600 mb-4">{description}</Text>
245
- <Pressable className="bg-blue-500 px-4 py-2 rounded-lg items-center" onPress={onPress}>
437
+ <Pressable
438
+ className="bg-blue-500 active:bg-blue-700 px-4 py-2 rounded-lg items-center"
439
+ onPress={onPress}
440
+ >
246
441
  <Text className="text-white font-semibold">Learn More</Text>
247
442
  </Pressable>
248
443
  </View>
@@ -368,6 +563,179 @@ The Babel plugin will merge them:
368
563
  </View>
369
564
  ```
370
565
 
566
+ ### State Modifiers
567
+
568
+ Apply styles based on component state with zero runtime overhead. The Babel plugin automatically generates optimized style functions.
569
+
570
+ #### Active Modifier (Pressable)
571
+
572
+ Use the `active:` modifier to apply styles when a `Pressable` component is pressed. **Requires using the enhanced `Pressable` component from this package.**
573
+
574
+ **Basic Example:**
575
+
576
+ ```tsx
577
+ import { Text } from "react-native";
578
+ import { Pressable } from "@mgcrea/react-native-tailwind";
579
+
580
+ export function MyButton() {
581
+ return (
582
+ <Pressable className="bg-blue-500 active:bg-blue-700 p-4 rounded-lg">
583
+ <Text className="text-white font-semibold">Press Me</Text>
584
+ </Pressable>
585
+ );
586
+ }
587
+ ```
588
+
589
+ **Transforms to:**
590
+
591
+ ```tsx
592
+ <Pressable
593
+ style={({ pressed }) => [_twStyles._bg_blue_500_p_4_rounded_lg, pressed && _twStyles._active_bg_blue_700]}
594
+ >
595
+ <Text style={_twStyles._font_semibold_text_white}>Press Me</Text>
596
+ </Pressable>;
597
+
598
+ // Generated styles:
599
+ const _twStyles = StyleSheet.create({
600
+ _bg_blue_500_p_4_rounded_lg: { backgroundColor: "#3B82F6", padding: 16, borderRadius: 8 },
601
+ _active_bg_blue_700: { backgroundColor: "#1D4ED8" },
602
+ _font_semibold_text_white: { fontWeight: "600", color: "#FFFFFF" },
603
+ });
604
+ ```
605
+
606
+ **Multiple Active Modifiers:**
607
+
608
+ ```tsx
609
+ <Pressable className="bg-green-500 active:bg-green-700 p-4 active:p-6 rounded-lg">
610
+ <Text className="text-white">Press for darker & larger padding</Text>
611
+ </Pressable>
612
+ ```
613
+
614
+ **Complex Styling:**
615
+
616
+ ```tsx
617
+ <Pressable className="bg-purple-500 active:bg-purple-800 border-2 border-purple-700 active:border-purple-900 p-4 rounded-lg">
618
+ <Text className="text-white">Background + Border Changes</Text>
619
+ </Pressable>
620
+ ```
621
+
622
+ **Key Features:**
623
+
624
+ - ✅ **Zero runtime overhead** — All parsing happens at compile-time
625
+ - ✅ **Native Pressable API** — Uses Pressable's `style={({ pressed }) => ...}` pattern
626
+ - ✅ **Type-safe** — Full TypeScript autocomplete for `active:` classes
627
+ - ✅ **Optimized** — Styles deduplicated via `StyleSheet.create`
628
+ - ✅ **Works with custom colors** — `active:bg-primary`, `active:bg-secondary`, etc.
629
+
630
+ #### Focus Modifier (TextInput)
631
+
632
+ Use the `focus:` modifier to apply styles when a `TextInput` component is focused. **Requires using the enhanced `TextInput` component from this package.**
633
+
634
+ **Basic Example:**
635
+
636
+ ```tsx
637
+ import { TextInput } from "@mgcrea/react-native-tailwind";
638
+
639
+ export function MyInput() {
640
+ return (
641
+ <TextInput
642
+ className="border-2 border-gray-300 focus:border-blue-500 p-3 rounded-lg bg-white"
643
+ placeholder="Email address"
644
+ />
645
+ );
646
+ }
647
+ ```
648
+
649
+ **How it works:**
650
+
651
+ The package exports an enhanced `TextInput` component that:
652
+
653
+ 1. Manages focus state internally using `onFocus`/`onBlur` callbacks
654
+ 2. Passes focus state to the style function: `style={({ focused }) => ...}`
655
+ 3. Works seamlessly with the `focus:` modifier in className
656
+
657
+ **Multiple Focus Modifiers:**
658
+
659
+ ```tsx
660
+ <TextInput className="border-2 border-gray-300 focus:border-green-500 bg-gray-50 focus:bg-white p-3 rounded-lg" />
661
+ ```
662
+
663
+ **Supported Modifiers by Component:**
664
+
665
+ | Component | Supported Modifiers | Notes |
666
+ | ---------------------- | -------------------------------------------- | ------------------------------------------ |
667
+ | `Pressable` (enhanced) | `active:`, `hover:`, `focus:`, `disabled:` | Use `@mgcrea/react-native-tailwind` export |
668
+ | `TextInput` (enhanced) | `focus:`, `disabled:` | Use `@mgcrea/react-native-tailwind` export |
669
+
670
+ #### Disabled Modifier (Pressable & TextInput)
671
+
672
+ Use the `disabled:` modifier to apply styles when a component is disabled. **Requires using the enhanced components from this package.**
673
+
674
+ **Pressable Example:**
675
+
676
+ ```tsx
677
+ import { Pressable, Text } from "@mgcrea/react-native-tailwind";
678
+
679
+ export function SubmitButton({ isLoading }) {
680
+ return (
681
+ <Pressable
682
+ disabled={isLoading}
683
+ className="bg-blue-500 active:bg-blue-700 disabled:bg-gray-400 p-4 rounded-lg"
684
+ >
685
+ <Text className="text-white font-semibold">
686
+ {isLoading ? "Loading..." : "Submit"}
687
+ </Text>
688
+ </Pressable>
689
+ );
690
+ }
691
+ ```
692
+
693
+ **TextInput Example:**
694
+
695
+ ```tsx
696
+ import { TextInput } from "@mgcrea/react-native-tailwind";
697
+
698
+ export function MyInput({ isEditing }) {
699
+ return (
700
+ <TextInput
701
+ disabled={!isEditing}
702
+ className="border-2 border-gray-300 focus:border-blue-500 disabled:bg-gray-100 disabled:border-gray-200 p-3 rounded-lg"
703
+ placeholder="Enter text"
704
+ />
705
+ );
706
+ }
707
+ ```
708
+
709
+ **How it works:**
710
+
711
+ The enhanced components inject the `disabled` prop value into the style function context:
712
+
713
+ - **Pressable**: Extends the existing `{ pressed, hovered, focused }` state with `disabled`
714
+ - **TextInput**: Provides `{ focused, disabled }` state to style functions
715
+
716
+ **TextInput `disabled` Prop:**
717
+
718
+ The enhanced `TextInput` also provides a convenient `disabled` prop that overrides React Native's `editable` prop:
719
+
720
+ ```tsx
721
+ // These are equivalent:
722
+ <TextInput disabled={true} />
723
+ <TextInput editable={false} />
724
+
725
+ // If both are provided, disabled takes precedence:
726
+ <TextInput disabled={true} editable={true} /> // Component is disabled
727
+ ```
728
+
729
+ **Important Notes:**
730
+
731
+ - ⚠️ **Enhanced components required** — State modifiers require using the enhanced components from this package
732
+ - ℹ️ **Component-specific** — Each modifier only works on compatible components
733
+ - ℹ️ **No nested modifiers** — Combinations like `active:focus:bg-blue-500` are not currently supported
734
+ - ✅ **Zero styling overhead** — All className parsing happens at compile-time
735
+ - ✅ **Minimal runtime cost** — Only adds state management (focus tracking, disabled injection)
736
+ - ✅ **Type-safe** — Full TypeScript autocomplete for all modifiers
737
+ - ✅ **Works with custom colors** — `focus:border-primary`, `active:bg-secondary`, `disabled:bg-gray-200`, etc.
738
+
371
739
  ### ScrollView Content Container
372
740
 
373
741
  Use `contentContainerClassName` to style the ScrollView's content container:
@@ -441,6 +809,67 @@ export function ListWithHeaderFooter({ items }) {
441
809
  }
442
810
  ```
443
811
 
812
+ ### Building Reusable Components
813
+
814
+ When building reusable components, use static `className` strings internally. To support `className` props from parent components, you **must** accept the corresponding `style` props (the Babel plugin transforms `className` to `style` before your component receives it):
815
+
816
+ ```tsx
817
+ import { Pressable, Text, View, StyleProp, ViewStyle } from "react-native";
818
+
819
+ type ButtonProps = {
820
+ title: string;
821
+ onPress?: () => void;
822
+ // REQUIRED: Must accept style props for className to work
823
+ style?: StyleProp<ViewStyle>;
824
+ containerStyle?: StyleProp<ViewStyle>;
825
+ // Optional: Include in type for TypeScript compatibility
826
+ className?: string; // compile-time only
827
+ containerClassName?: string; // compile-time only
828
+ };
829
+
830
+ export function Button({ title, onPress, style, containerStyle }: ButtonProps) {
831
+ // Use static className strings - these get optimized at compile-time
832
+ return (
833
+ <View className="p-2 bg-gray-100 rounded-lg" style={containerStyle}>
834
+ <Pressable className="bg-blue-500 px-6 py-4 rounded-lg items-center" onPress={onPress} style={style}>
835
+ <Text className="text-white text-center font-semibold text-base">{title}</Text>
836
+ </Pressable>
837
+ </View>
838
+ );
839
+ }
840
+ ```
841
+
842
+ **Key Points:**
843
+
844
+ - ✅ Use **static className strings** internally for default styling
845
+ - ✅ **Must accept `style` props** - the Babel plugin transforms `className` → `style` before your component receives it
846
+ - ✅ Include `className` props in the type (for TypeScript compatibility)
847
+ - ✅ **Don't destructure** className props - they're already transformed to `style` and will be `undefined` at runtime
848
+ - ✅ Babel plugin optimizes all static strings to `StyleSheet.create` calls
849
+
850
+ **Usage:**
851
+
852
+ ```tsx
853
+ // Default styling (uses internal static classNames)
854
+ <Button title="Click Me" onPress={handlePress} />
855
+
856
+ // Override with runtime styles
857
+ <Button
858
+ title="Custom"
859
+ style={{ backgroundColor: '#10B981' }}
860
+ containerStyle={{ padding: 16 }}
861
+ onPress={handlePress}
862
+ />
863
+
864
+ // className props are accepted but transformed by Babel upstream
865
+ <Button className="bg-red-500 p-8" title="Red Button" />
866
+ // At compile-time, this becomes:
867
+ // <Button style={_twStyles._bg_red_500_p_8} title="Red Button" />
868
+ // At runtime, className is undefined (already transformed to style)
869
+ ```
870
+
871
+ This pattern allows you to build component libraries with optimized default styling while still supporting full customization.
872
+
444
873
  ## Architecture
445
874
 
446
875
  ### Compile-Time Transformation
@@ -487,24 +916,25 @@ For dynamic styling, use the `style` prop alongside `className`.
487
916
 
488
917
  ### Arbitrary Values
489
918
 
490
- Use arbitrary values for custom sizes and borders not in the preset scales:
919
+ Use arbitrary values for custom sizes, spacing, and borders not in the preset scales:
491
920
 
492
921
  ```tsx
493
- <View className="w-[350px] h-[85%] border-[3px] rounded-[20px]" />
922
+ <View className="w-[350px] h-[85%] m-[16px] p-[24px] border-[3px] rounded-[20px]" />
494
923
  ```
495
924
 
496
925
  **Supported:**
497
926
 
498
- - **Sizing:** `w-[...]`, `h-[...]`, `min-w-[...]`, `min-h-[...]`, `max-w-[...]`, `max-h-[...]`
499
- - **Border width:** `border-[...]`, `border-t-[...]`, `border-r-[...]`, `border-b-[...]`, `border-l-[...]`
500
- - **Border radius:** `rounded-[...]`, `rounded-t-[...]`, `rounded-tl-[...]`, etc.
927
+ - **Spacing:** `m-[...]`, `mx-[...]`, `my-[...]`, `mt-[...]`, `p-[...]`, `px-[...]`, `gap-[...]`, etc. (px only)
928
+ - **Sizing:** `w-[...]`, `h-[...]`, `min-w-[...]`, `min-h-[...]`, `max-w-[...]`, `max-h-[...]` (px and %)
929
+ - **Border width:** `border-[...]`, `border-t-[...]`, `border-r-[...]`, `border-b-[...]`, `border-l-[...]` (px only)
930
+ - **Border radius:** `rounded-[...]`, `rounded-t-[...]`, `rounded-tl-[...]`, etc. (px only)
501
931
 
502
932
  **Formats:**
503
933
 
504
- - Pixels: `[123px]` or `[123]`
505
- - Percentages: `[50%]`, `[33.333%]`
934
+ - Pixels: `[123px]` or `[123]` — Supported by all utilities
935
+ - Percentages: `[50%]`, `[33.333%]` — Only supported by sizing utilities (`w-*`, `h-*`, etc.)
506
936
 
507
- > **Note:** Only `px` and `%` units are supported. CSS units (`rem`, `em`, `vh`, `vw`) are not supported by React Native.
937
+ > **Note:** CSS units (`rem`, `em`, `vh`, `vw`) are not supported by React Native.
508
938
 
509
939
  ### Custom Colors
510
940