@mgcrea/react-native-tailwind 0.3.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 +459 -39
  2. package/dist/babel/index.cjs +810 -279
  3. package/dist/babel/index.d.ts +2 -1
  4. package/dist/babel/index.ts +328 -22
  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 +328 -22
  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
@@ -95,11 +96,11 @@ The Babel plugin transforms your code at compile time:
95
96
  ```tsx
96
97
  import { StyleSheet } from "react-native";
97
98
 
98
- <View style={styles._bg_blue_500_m_4_p_2_rounded_lg} />;
99
- <ScrollView contentContainerStyle={styles._gap_4_items_center} />;
100
- <FlatList columnWrapperStyle={styles._gap_4} ListHeaderComponentStyle={styles._bg_gray_100_p_4} />;
99
+ <View style={_twStyles._bg_blue_500_m_4_p_2_rounded_lg} />;
100
+ <ScrollView contentContainerStyle={_twStyles._gap_4_items_center} />;
101
+ <FlatList columnWrapperStyle={_twStyles._gap_4} ListHeaderComponentStyle={_twStyles._bg_gray_100_p_4} />;
101
102
 
102
- const styles = StyleSheet.create({
103
+ const _twStyles = StyleSheet.create({
103
104
  _bg_blue_500_m_4_p_2_rounded_lg: {
104
105
  margin: 16,
105
106
  padding: 8,
@@ -134,6 +135,8 @@ const styles = 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 styles = 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 styles = 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>
@@ -279,18 +474,16 @@ export function ToggleButton() {
279
474
  ```tsx
280
475
  <Pressable
281
476
  onPress={() => setIsActive(!isActive)}
282
- style={isActive ? styles._bg_green_500_p_4 : styles._bg_red_500_p_4}
477
+ style={isActive ? _twStyles._bg_green_500_p_4 : _twStyles._bg_red_500_p_4}
283
478
  >
284
- <Text style={styles._text_white}>{isActive ? "Active" : "Inactive"}</Text>
479
+ <Text style={_twStyles._text_white}>{isActive ? "Active" : "Inactive"}</Text>
285
480
  </Pressable>
286
481
  ```
287
482
 
288
483
  **Template Literal (Static + Dynamic):**
289
484
 
290
485
  ```tsx
291
- <Pressable
292
- className={`border-2 rounded-lg ${isActive ? "bg-blue-500" : "bg-gray-300"} p-4`}
293
- >
486
+ <Pressable className={`border-2 rounded-lg ${isActive ? "bg-blue-500" : "bg-gray-300"} p-4`}>
294
487
  <Text className="text-white">Click Me</Text>
295
488
  </Pressable>
296
489
  ```
@@ -300,13 +493,13 @@ export function ToggleButton() {
300
493
  ```tsx
301
494
  <Pressable
302
495
  style={[
303
- styles._border_2,
304
- styles._rounded_lg,
305
- isActive ? styles._bg_blue_500 : styles._bg_gray_300,
306
- styles._p_4
496
+ _twStyles._border_2,
497
+ _twStyles._rounded_lg,
498
+ isActive ? _twStyles._bg_blue_500 : _twStyles._bg_gray_300,
499
+ _twStyles._p_4,
307
500
  ]}
308
501
  >
309
- <Text style={styles._text_white}>Click Me</Text>
502
+ <Text style={_twStyles._text_white}>Click Me</Text>
310
503
  </Pressable>
311
504
  ```
312
505
 
@@ -321,13 +514,7 @@ export function ToggleButton() {
321
514
  **Transforms to:**
322
515
 
323
516
  ```tsx
324
- <View
325
- style={[
326
- styles._p_4,
327
- styles._bg_gray_100,
328
- isActive && styles._border_4_border_purple_500
329
- ]}
330
- >
517
+ <View style={[_twStyles._p_4, _twStyles._bg_gray_100, isActive && _twStyles._border_4_border_purple_500]}>
331
518
  <Text>Content</Text>
332
519
  </View>
333
520
  ```
@@ -335,9 +522,7 @@ export function ToggleButton() {
335
522
  **Multiple Conditionals:**
336
523
 
337
524
  ```tsx
338
- <View
339
- className={`${size === "lg" ? "p-8" : "p-4"} ${isActive ? "bg-blue-500" : "bg-gray-400"}`}
340
- >
525
+ <View className={`${size === "lg" ? "p-8" : "p-4"} ${isActive ? "bg-blue-500" : "bg-gray-400"}`}>
341
526
  <Text>Dynamic Size & Color</Text>
342
527
  </View>
343
528
  ```
@@ -373,11 +558,184 @@ You can use inline `style` prop alongside `className`:
373
558
  The Babel plugin will merge them:
374
559
 
375
560
  ```tsx
376
- <View style={[styles._className_styles, { paddingTop: safeAreaInsets.top }]}>
561
+ <View style={[_twStyles._className_styles, { paddingTop: safeAreaInsets.top }]}>
377
562
  <Text>Content</Text>
378
563
  </View>
379
564
  ```
380
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
+
381
739
  ### ScrollView Content Container
382
740
 
383
741
  Use `contentContainerClassName` to style the ScrollView's content container:
@@ -451,6 +809,67 @@ export function ListWithHeaderFooter({ items }) {
451
809
  }
452
810
  ```
453
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
+
454
873
  ## Architecture
455
874
 
456
875
  ### Compile-Time Transformation
@@ -497,24 +916,25 @@ For dynamic styling, use the `style` prop alongside `className`.
497
916
 
498
917
  ### Arbitrary Values
499
918
 
500
- 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:
501
920
 
502
921
  ```tsx
503
- <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]" />
504
923
  ```
505
924
 
506
925
  **Supported:**
507
926
 
508
- - **Sizing:** `w-[...]`, `h-[...]`, `min-w-[...]`, `min-h-[...]`, `max-w-[...]`, `max-h-[...]`
509
- - **Border width:** `border-[...]`, `border-t-[...]`, `border-r-[...]`, `border-b-[...]`, `border-l-[...]`
510
- - **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)
511
931
 
512
932
  **Formats:**
513
933
 
514
- - Pixels: `[123px]` or `[123]`
515
- - 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.)
516
936
 
517
- > **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.
518
938
 
519
939
  ### Custom Colors
520
940
 
@@ -567,7 +987,7 @@ Access the parser and constants programmatically:
567
987
  import { parseClassName, COLORS, SPACING_SCALE } from "@mgcrea/react-native-tailwind";
568
988
 
569
989
  // Parse className strings
570
- const styles = parseClassName("m-4 p-2 bg-blue-500");
990
+ const _twStyles = parseClassName("m-4 p-2 bg-blue-500");
571
991
  // Returns: { margin: 16, padding: 8, backgroundColor: '#3B82F6' }
572
992
 
573
993
  // Access default scales