@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.
- package/README.md +443 -13
- package/dist/babel/index.cjs +804 -274
- package/dist/babel/index.d.ts +2 -1
- package/dist/babel/index.ts +319 -16
- package/dist/components/Pressable.d.ts +32 -0
- package/dist/components/Pressable.js +1 -0
- package/dist/components/TextInput.d.ts +56 -0
- package/dist/components/TextInput.js +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +1 -1
- package/dist/parser/aspectRatio.d.ts +16 -0
- package/dist/parser/aspectRatio.js +1 -0
- package/dist/parser/aspectRatio.test.d.ts +1 -0
- package/dist/parser/aspectRatio.test.js +1 -0
- package/dist/parser/borders.js +1 -1
- package/dist/parser/borders.test.d.ts +1 -0
- package/dist/parser/borders.test.js +1 -0
- package/dist/parser/colors.d.ts +1 -0
- package/dist/parser/colors.js +1 -1
- package/dist/parser/colors.test.d.ts +1 -0
- package/dist/parser/colors.test.js +1 -0
- package/dist/parser/index.d.ts +4 -0
- package/dist/parser/index.js +1 -1
- package/dist/parser/layout.d.ts +2 -0
- package/dist/parser/layout.js +1 -1
- package/dist/parser/layout.test.d.ts +1 -0
- package/dist/parser/layout.test.js +1 -0
- package/dist/parser/modifiers.d.ts +47 -0
- package/dist/parser/modifiers.js +1 -0
- package/dist/parser/modifiers.test.d.ts +1 -0
- package/dist/parser/modifiers.test.js +1 -0
- package/dist/parser/shadows.d.ts +26 -0
- package/dist/parser/shadows.js +1 -0
- package/dist/parser/shadows.test.d.ts +1 -0
- package/dist/parser/shadows.test.js +1 -0
- package/dist/parser/sizing.test.d.ts +1 -0
- package/dist/parser/sizing.test.js +1 -0
- package/dist/parser/spacing.d.ts +1 -1
- package/dist/parser/spacing.js +1 -1
- package/dist/parser/spacing.test.d.ts +1 -0
- package/dist/parser/spacing.test.js +1 -0
- package/dist/parser/typography.d.ts +2 -1
- package/dist/parser/typography.js +1 -1
- package/dist/parser/typography.test.d.ts +1 -0
- package/dist/parser/typography.test.js +1 -0
- package/dist/types.d.ts +5 -2
- package/package.json +7 -6
- package/src/babel/index.ts +319 -16
- package/src/components/Pressable.tsx +46 -0
- package/src/components/TextInput.tsx +90 -0
- package/src/index.ts +20 -2
- package/src/parser/aspectRatio.test.ts +191 -0
- package/src/parser/aspectRatio.ts +73 -0
- package/src/parser/borders.test.ts +329 -0
- package/src/parser/borders.ts +187 -108
- package/src/parser/colors.test.ts +335 -0
- package/src/parser/colors.ts +117 -6
- package/src/parser/index.ts +13 -2
- package/src/parser/layout.test.ts +459 -0
- package/src/parser/layout.ts +128 -0
- package/src/parser/modifiers.test.ts +375 -0
- package/src/parser/modifiers.ts +104 -0
- package/src/parser/shadows.test.ts +201 -0
- package/src/parser/shadows.ts +133 -0
- package/src/parser/sizing.test.ts +256 -0
- package/src/parser/spacing.test.ts +226 -0
- package/src/parser/spacing.ts +93 -138
- package/src/parser/typography.test.ts +221 -0
- package/src/parser/typography.ts +143 -112
- package/src/types.ts +2 -2
- 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
|
-
-
|
|
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** —
|
|
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
|
|
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
|
|
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
|
-
- **
|
|
499
|
-
- **
|
|
500
|
-
- **Border
|
|
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:**
|
|
937
|
+
> **Note:** CSS units (`rem`, `em`, `vh`, `vw`) are not supported by React Native.
|
|
508
938
|
|
|
509
939
|
### Custom Colors
|
|
510
940
|
|