@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.
- package/README.md +459 -39
- package/dist/babel/index.cjs +810 -279
- package/dist/babel/index.d.ts +2 -1
- package/dist/babel/index.ts +328 -22
- 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 +328 -22
- 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
|
|
@@ -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={
|
|
99
|
-
<ScrollView contentContainerStyle={
|
|
100
|
-
<FlatList columnWrapperStyle={
|
|
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
|
|
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
|
|
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>
|
|
@@ -279,18 +474,16 @@ export function ToggleButton() {
|
|
|
279
474
|
```tsx
|
|
280
475
|
<Pressable
|
|
281
476
|
onPress={() => setIsActive(!isActive)}
|
|
282
|
-
style={isActive ?
|
|
477
|
+
style={isActive ? _twStyles._bg_green_500_p_4 : _twStyles._bg_red_500_p_4}
|
|
283
478
|
>
|
|
284
|
-
<Text style={
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
isActive ?
|
|
306
|
-
|
|
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={
|
|
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={[
|
|
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
|
-
- **
|
|
509
|
-
- **
|
|
510
|
-
- **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)
|
|
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:**
|
|
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
|
|
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
|