@mgcrea/react-native-tailwind 0.12.1 → 0.13.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 +29 -2014
- package/dist/babel/index.cjs +1462 -1155
- package/dist/babel/plugin/componentScope.d.ts +26 -0
- package/dist/babel/plugin/componentScope.ts +87 -0
- package/dist/babel/plugin/state.d.ts +119 -0
- package/dist/babel/plugin/state.ts +177 -0
- package/dist/babel/plugin/visitors/className.d.ts +11 -0
- package/{src/babel/plugin.test.ts → dist/babel/plugin/visitors/className.test.ts} +74 -674
- package/dist/babel/plugin/visitors/className.ts +624 -0
- package/dist/babel/plugin/visitors/className.windowDimensions.test.ts +406 -0
- package/dist/babel/plugin/visitors/imports.d.ts +11 -0
- package/dist/babel/plugin/visitors/imports.test.ts +88 -0
- package/dist/babel/plugin/visitors/imports.ts +101 -0
- package/dist/babel/plugin/visitors/program.d.ts +15 -0
- package/dist/babel/plugin/visitors/program.test.ts +325 -0
- package/dist/babel/plugin/visitors/program.ts +99 -0
- package/dist/babel/plugin/visitors/tw.d.ts +16 -0
- package/dist/babel/plugin/visitors/tw.test.ts +620 -0
- package/dist/babel/plugin/visitors/tw.ts +148 -0
- package/dist/babel/plugin.d.ts +3 -96
- package/dist/babel/plugin.test.ts +470 -0
- package/dist/babel/plugin.ts +28 -963
- package/dist/babel/utils/colorSchemeModifierProcessing.ts +11 -0
- package/dist/babel/utils/componentSupport.test.ts +20 -7
- package/dist/babel/utils/componentSupport.ts +2 -0
- package/dist/babel/utils/modifierProcessing.ts +21 -0
- package/dist/babel/utils/platformModifierProcessing.ts +11 -0
- package/dist/babel/utils/styleInjection.d.ts +15 -0
- package/dist/babel/utils/styleInjection.ts +115 -0
- package/dist/babel/utils/twProcessing.ts +11 -0
- package/dist/babel/utils/windowDimensionsProcessing.d.ts +56 -0
- package/dist/babel/utils/windowDimensionsProcessing.ts +121 -0
- package/dist/components/TouchableOpacity.d.ts +35 -0
- package/dist/components/TouchableOpacity.js +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +1 -0
- package/dist/config/markers.d.ts +5 -0
- package/dist/config/markers.js +1 -0
- package/dist/index.d.ts +2 -5
- package/dist/index.js +1 -1
- package/dist/parser/borders.d.ts +3 -1
- package/dist/parser/borders.js +1 -1
- package/dist/parser/borders.test.js +1 -1
- package/dist/parser/colors.js +1 -1
- package/dist/parser/colors.test.js +1 -1
- package/dist/parser/index.js +1 -1
- package/dist/parser/sizing.js +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +4 -4
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +4 -4
- package/package.json +1 -1
- package/src/babel/plugin/componentScope.ts +87 -0
- package/src/babel/plugin/state.ts +177 -0
- package/src/babel/plugin/visitors/className.test.ts +1312 -0
- package/src/babel/plugin/visitors/className.ts +624 -0
- package/src/babel/plugin/visitors/className.windowDimensions.test.ts +406 -0
- package/src/babel/plugin/visitors/imports.test.ts +88 -0
- package/src/babel/plugin/visitors/imports.ts +101 -0
- package/src/babel/plugin/visitors/program.test.ts +325 -0
- package/src/babel/plugin/visitors/program.ts +99 -0
- package/src/babel/plugin/visitors/tw.test.ts +620 -0
- package/src/babel/plugin/visitors/tw.ts +148 -0
- package/src/babel/plugin.ts +28 -963
- package/src/babel/utils/colorSchemeModifierProcessing.ts +11 -0
- package/src/babel/utils/componentSupport.test.ts +20 -7
- package/src/babel/utils/componentSupport.ts +2 -0
- package/src/babel/utils/modifierProcessing.ts +21 -0
- package/src/babel/utils/platformModifierProcessing.ts +11 -0
- package/src/babel/utils/styleInjection.ts +115 -0
- package/src/babel/utils/twProcessing.ts +11 -0
- package/src/babel/utils/windowDimensionsProcessing.ts +121 -0
- package/src/components/TouchableOpacity.tsx +71 -0
- package/src/components/index.ts +3 -0
- package/src/config/markers.ts +5 -0
- package/src/index.ts +4 -5
- package/src/parser/borders.test.ts +58 -0
- package/src/parser/borders.ts +18 -3
- package/src/parser/colors.test.ts +249 -0
- package/src/parser/colors.ts +38 -0
- package/src/parser/index.ts +2 -2
- package/src/parser/sizing.ts +11 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- markdownlint-disable MD033 -->
|
|
4
4
|
<p align="center">
|
|
5
|
-
<a href="https://mgcrea.github.io/react-native-
|
|
5
|
+
<a href="https://mgcrea.github.io/react-native-tailwind">
|
|
6
6
|
<img src="./.github/assets/logo.png" alt="logo" width="480" height="300"/>
|
|
7
7
|
</a>
|
|
8
8
|
</p>
|
|
@@ -58,7 +58,9 @@ Compile-time Tailwind CSS for React Native with zero runtime overhead. Transform
|
|
|
58
58
|
|
|
59
59
|

|
|
60
60
|
|
|
61
|
-
##
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### Installation
|
|
62
64
|
|
|
63
65
|
```bash
|
|
64
66
|
npm install @mgcrea/react-native-tailwind
|
|
@@ -68,8 +70,6 @@ yarn add @mgcrea/react-native-tailwind
|
|
|
68
70
|
pnpm add @mgcrea/react-native-tailwind
|
|
69
71
|
```
|
|
70
72
|
|
|
71
|
-
## Setup
|
|
72
|
-
|
|
73
73
|
### 1. Add Babel Plugin
|
|
74
74
|
|
|
75
75
|
Update your `babel.config.js`:
|
|
@@ -83,20 +83,15 @@ module.exports = {
|
|
|
83
83
|
};
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
### 2. Enable TypeScript Support (
|
|
86
|
+
### 2. Enable TypeScript Support (Optional)
|
|
87
87
|
|
|
88
|
-
Create a type declaration file
|
|
89
|
-
|
|
90
|
-
**Create `src/types/react-native-tailwind.d.ts`:**
|
|
88
|
+
Create a type declaration file to enable `className` prop autocomplete:
|
|
91
89
|
|
|
92
90
|
```typescript
|
|
91
|
+
// src/types/react-native-tailwind.d.ts
|
|
93
92
|
import "@mgcrea/react-native-tailwind/react-native";
|
|
94
93
|
```
|
|
95
94
|
|
|
96
|
-
This file will be automatically picked up by TypeScript and enables autocomplete for the `className` prop on all React Native components.
|
|
97
|
-
|
|
98
|
-
> **Why is this needed?** TypeScript module augmentation requires the declaration file to be part of your project's compilation context. This one-time setup ensures the `className` prop is recognized throughout your codebase.
|
|
99
|
-
|
|
100
95
|
### 3. Start Using className
|
|
101
96
|
|
|
102
97
|
```tsx
|
|
@@ -119,8 +114,6 @@ The Babel plugin transforms your code at compile time:
|
|
|
119
114
|
|
|
120
115
|
```tsx
|
|
121
116
|
<View className="m-4 p-2 bg-blue-500 rounded-lg" />
|
|
122
|
-
<ScrollView contentContainerClassName="items-center gap-4" />
|
|
123
|
-
<FlatList columnWrapperClassName="gap-4" ListHeaderComponentClassName="p-4 bg-gray-100" />
|
|
124
117
|
```
|
|
125
118
|
|
|
126
119
|
**Output** (what Babel generates):
|
|
@@ -129,8 +122,6 @@ The Babel plugin transforms your code at compile time:
|
|
|
129
122
|
import { StyleSheet } from "react-native";
|
|
130
123
|
|
|
131
124
|
<View style={_twStyles._bg_blue_500_m_4_p_2_rounded_lg} />;
|
|
132
|
-
<ScrollView contentContainerStyle={_twStyles._gap_4_items_center} />;
|
|
133
|
-
<FlatList columnWrapperStyle={_twStyles._gap_4} ListHeaderComponentStyle={_twStyles._bg_gray_100_p_4} />;
|
|
134
125
|
|
|
135
126
|
const _twStyles = StyleSheet.create({
|
|
136
127
|
_bg_blue_500_m_4_p_2_rounded_lg: {
|
|
@@ -139,67 +130,25 @@ const _twStyles = StyleSheet.create({
|
|
|
139
130
|
backgroundColor: "#3B82F6",
|
|
140
131
|
borderRadius: 8,
|
|
141
132
|
},
|
|
142
|
-
_gap_4_items_center: {
|
|
143
|
-
gap: 16,
|
|
144
|
-
alignItems: "center",
|
|
145
|
-
},
|
|
146
|
-
_gap_4: {
|
|
147
|
-
gap: 16,
|
|
148
|
-
},
|
|
149
|
-
_bg_gray_100_p_4: {
|
|
150
|
-
backgroundColor: "#F3F4F6",
|
|
151
|
-
padding: 16,
|
|
152
|
-
},
|
|
153
133
|
});
|
|
154
134
|
```
|
|
155
135
|
|
|
156
|
-
##
|
|
157
|
-
|
|
158
|
-
### Basic Example
|
|
159
|
-
|
|
160
|
-
```tsx
|
|
161
|
-
import { View, Text } from "react-native";
|
|
162
|
-
|
|
163
|
-
export function MyComponent() {
|
|
164
|
-
return (
|
|
165
|
-
<View className="flex-1 bg-gray-100 p-4">
|
|
166
|
-
<Text className="text-xl font-bold text-blue-500">Hello, Tailwind!</Text>
|
|
167
|
-
</View>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### Card Component
|
|
173
|
-
|
|
174
|
-
```tsx
|
|
175
|
-
import { View, Text } from "react-native";
|
|
176
|
-
import { Pressable } from "@mgcrea/react-native-tailwind";
|
|
136
|
+
## Documentation
|
|
177
137
|
|
|
178
|
-
|
|
179
|
-
return (
|
|
180
|
-
<View className="bg-white rounded-lg p-6 mb-4 border border-gray-200">
|
|
181
|
-
<Text className="text-xl font-semibold text-gray-900 mb-2">{title}</Text>
|
|
182
|
-
<Text className="text-base text-gray-600 mb-4">{description}</Text>
|
|
183
|
-
<Pressable
|
|
184
|
-
className="bg-blue-500 active:bg-blue-700 px-4 py-2 rounded-lg items-center"
|
|
185
|
-
onPress={onPress}
|
|
186
|
-
>
|
|
187
|
-
<Text className="text-white font-semibold">Learn More</Text>
|
|
188
|
-
</Pressable>
|
|
189
|
-
</View>
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
```
|
|
138
|
+
📚 **[Full Documentation](https://mgcrea.github.io/react-native-tailwind/)**
|
|
193
139
|
|
|
194
|
-
|
|
140
|
+
- **[Getting Started](https://mgcrea.github.io/react-native-tailwind/getting-started/installation/)** - Installation and setup
|
|
141
|
+
- **[Guides](https://mgcrea.github.io/react-native-tailwind/guides/basic-usage/)** - Usage examples and patterns
|
|
142
|
+
- **[API Reference](https://mgcrea.github.io/react-native-tailwind/reference/spacing/)** - Complete utility reference
|
|
143
|
+
- **[Advanced](https://mgcrea.github.io/react-native-tailwind/advanced/custom-colors/)** - Configuration and customization
|
|
195
144
|
|
|
196
|
-
|
|
145
|
+
## Examples
|
|
197
146
|
|
|
198
|
-
|
|
147
|
+
### Dynamic Styles
|
|
199
148
|
|
|
200
149
|
```tsx
|
|
201
150
|
import { useState } from "react";
|
|
202
|
-
import {
|
|
151
|
+
import { Pressable, Text } from "react-native";
|
|
203
152
|
|
|
204
153
|
export function ToggleButton() {
|
|
205
154
|
const [isActive, setIsActive] = useState(false);
|
|
@@ -215,330 +164,10 @@ export function ToggleButton() {
|
|
|
215
164
|
}
|
|
216
165
|
```
|
|
217
166
|
|
|
218
|
-
**Transforms to:**
|
|
219
|
-
|
|
220
|
-
```tsx
|
|
221
|
-
<Pressable
|
|
222
|
-
onPress={() => setIsActive(!isActive)}
|
|
223
|
-
style={isActive ? _twStyles._bg_green_500_p_4 : _twStyles._bg_red_500_p_4}
|
|
224
|
-
>
|
|
225
|
-
<Text style={_twStyles._text_white}>{isActive ? "Active" : "Inactive"}</Text>
|
|
226
|
-
</Pressable>
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
**Template Literal (Static + Dynamic):**
|
|
230
|
-
|
|
231
|
-
```tsx
|
|
232
|
-
<Pressable className={`border-2 rounded-lg ${isActive ? "bg-blue-500" : "bg-gray-300"} p-4`}>
|
|
233
|
-
<Text className="text-white">Click Me</Text>
|
|
234
|
-
</Pressable>
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Transforms to:**
|
|
238
|
-
|
|
239
|
-
```tsx
|
|
240
|
-
<Pressable
|
|
241
|
-
style={[
|
|
242
|
-
_twStyles._border_2,
|
|
243
|
-
_twStyles._rounded_lg,
|
|
244
|
-
isActive ? _twStyles._bg_blue_500 : _twStyles._bg_gray_300,
|
|
245
|
-
_twStyles._p_4,
|
|
246
|
-
]}
|
|
247
|
-
>
|
|
248
|
-
<Text style={_twStyles._text_white}>Click Me</Text>
|
|
249
|
-
</Pressable>
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
**Logical Expression:**
|
|
253
|
-
|
|
254
|
-
```tsx
|
|
255
|
-
<View className={`p-4 bg-gray-100 ${isActive && "border-4 border-purple-500"}`}>
|
|
256
|
-
<Text>Content</Text>
|
|
257
|
-
</View>
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
**Transforms to:**
|
|
261
|
-
|
|
262
|
-
```tsx
|
|
263
|
-
<View style={[_twStyles._p_4, _twStyles._bg_gray_100, isActive && _twStyles._border_4_border_purple_500]}>
|
|
264
|
-
<Text>Content</Text>
|
|
265
|
-
</View>
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
**Multiple Conditionals:**
|
|
269
|
-
|
|
270
|
-
```tsx
|
|
271
|
-
<View className={`${size === "lg" ? "p-8" : "p-4"} ${isActive ? "bg-blue-500" : "bg-gray-400"}`}>
|
|
272
|
-
<Text>Dynamic Size & Color</Text>
|
|
273
|
-
</View>
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
**Key Benefits:**
|
|
277
|
-
|
|
278
|
-
- ✅ All string literals are parsed at compile-time
|
|
279
|
-
- ✅ Only conditional logic remains at runtime (no parser overhead)
|
|
280
|
-
- ✅ Full type-safety and validation for all class names
|
|
281
|
-
- ✅ Optimal performance with pre-compiled styles
|
|
282
|
-
|
|
283
|
-
**What Won't Work:**
|
|
284
|
-
|
|
285
|
-
```tsx
|
|
286
|
-
// ❌ Runtime variables in class names
|
|
287
|
-
const spacing = 4;
|
|
288
|
-
<View className={`p-${spacing}`} /> // Can't parse "p-${spacing}" at compile time
|
|
289
|
-
|
|
290
|
-
// ✅ Use inline style for truly dynamic values:
|
|
291
|
-
<View className="border-2" style={{ padding: spacing * 4 }} />
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Combining with Inline Styles
|
|
295
|
-
|
|
296
|
-
You can use inline `style` prop alongside `className`:
|
|
297
|
-
|
|
298
|
-
```tsx
|
|
299
|
-
<View className="flex-1 p-4 bg-blue-500" style={{ paddingTop: safeAreaInsets.top }}>
|
|
300
|
-
<Text>Content</Text>
|
|
301
|
-
</View>
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
The Babel plugin will merge them:
|
|
305
|
-
|
|
306
|
-
```tsx
|
|
307
|
-
<View style={[_twStyles._className_styles, { paddingTop: safeAreaInsets.top }]}>
|
|
308
|
-
<Text>Content</Text>
|
|
309
|
-
</View>
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### Compile-Time `tw` Template Tag
|
|
313
|
-
|
|
314
|
-
For static or compile-time determinable styles, you can use the `tw` template tag that gets transformed by the Babel plugin. This provides **zero runtime overhead** as all styles are compiled to `StyleSheet.create` calls.
|
|
315
|
-
|
|
316
|
-
#### Import
|
|
317
|
-
|
|
318
|
-
```typescript
|
|
319
|
-
import { tw } from "@mgcrea/react-native-tailwind";
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
#### Basic Usage
|
|
323
|
-
|
|
324
|
-
```tsx
|
|
325
|
-
import { View, Text, Pressable } from "react-native";
|
|
326
|
-
import { tw } from "@mgcrea/react-native-tailwind";
|
|
327
|
-
|
|
328
|
-
// Static styles - transformed at compile time
|
|
329
|
-
const containerStyles = tw`flex-1 p-4 bg-gray-100`;
|
|
330
|
-
const buttonStyles = tw`p-4 rounded-lg bg-blue-500`;
|
|
331
|
-
const textStyles = tw`text-white font-bold text-center`;
|
|
332
|
-
|
|
333
|
-
export function Example() {
|
|
334
|
-
return (
|
|
335
|
-
<View style={containerStyles.style}>
|
|
336
|
-
<Pressable style={buttonStyles.style}>
|
|
337
|
-
<Text style={textStyles.style}>Click me</Text>
|
|
338
|
-
</Pressable>
|
|
339
|
-
</View>
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
#### With State Modifiers
|
|
345
|
-
|
|
346
|
-
The compile-time `tw` supports state modifiers (`active:`, `focus:`, `disabled:`) that return a `TwStyle` object with separate style properties:
|
|
347
|
-
|
|
348
|
-
```tsx
|
|
349
|
-
import { Pressable, Text } from "react-native";
|
|
350
|
-
import { tw } from "@mgcrea/react-native-tailwind";
|
|
351
|
-
|
|
352
|
-
const buttonStyles = tw`bg-blue-500 active:bg-blue-700 disabled:bg-gray-300`;
|
|
353
|
-
|
|
354
|
-
export function Button({ disabled }) {
|
|
355
|
-
return (
|
|
356
|
-
<Pressable
|
|
357
|
-
disabled={disabled}
|
|
358
|
-
style={(state) => [
|
|
359
|
-
buttonStyles.style,
|
|
360
|
-
state.pressed && buttonStyles.activeStyle,
|
|
361
|
-
disabled && buttonStyles.disabledStyle,
|
|
362
|
-
]}
|
|
363
|
-
>
|
|
364
|
-
<Text style={tw`text-white font-bold`.style}>Press me</Text>
|
|
365
|
-
</Pressable>
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
#### Transformation Example
|
|
371
|
-
|
|
372
|
-
The Babel plugin transforms your code at compile time:
|
|
373
|
-
|
|
374
|
-
```tsx
|
|
375
|
-
// Input
|
|
376
|
-
const styles = tw`bg-blue-500 active:bg-blue-700 m-4`;
|
|
377
|
-
|
|
378
|
-
// Compiled Output
|
|
379
|
-
const styles = {
|
|
380
|
-
style: _twStyles._bg_blue_500_m_4,
|
|
381
|
-
activeStyle: _twStyles._active_bg_blue_700,
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
const _twStyles = StyleSheet.create({
|
|
385
|
-
_bg_blue_500_m_4: { backgroundColor: "#2b7fff", margin: 16 },
|
|
386
|
-
_active_bg_blue_700: { backgroundColor: "#1854d6" },
|
|
387
|
-
});
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
### Runtime `tw` Template Tag
|
|
391
|
-
|
|
392
|
-
For cases where you need **fully dynamic styling** (values only known at runtime), you can use the runtime `tw` template tag function. This provides runtime parsing of Tailwind classes with memoization for performance.
|
|
393
|
-
|
|
394
|
-
> **Note:** Use the compile-time `tw` (imported from `@mgcrea/react-native-tailwind`) when possible for zero runtime overhead. Only use the runtime version (imported from `@mgcrea/react-native-tailwind/runtime`) when styles must be determined at runtime.
|
|
395
|
-
|
|
396
|
-
#### Installation
|
|
397
|
-
|
|
398
|
-
The runtime module is already included in the package. Import it separately:
|
|
399
|
-
|
|
400
|
-
```typescript
|
|
401
|
-
import { tw, setConfig } from "@mgcrea/react-native-tailwind/runtime";
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
#### Basic Usage
|
|
405
|
-
|
|
406
|
-
```tsx
|
|
407
|
-
import { View, Text, Pressable } from "react-native";
|
|
408
|
-
import { tw } from "@mgcrea/react-native-tailwind/runtime";
|
|
409
|
-
|
|
410
|
-
export function RuntimeExample() {
|
|
411
|
-
const [isActive, setIsActive] = useState(false);
|
|
412
|
-
|
|
413
|
-
return (
|
|
414
|
-
<View style={tw`flex-1 p-4 bg-gray-100`}>
|
|
415
|
-
<Pressable
|
|
416
|
-
onPress={() => setIsActive(!isActive)}
|
|
417
|
-
style={tw`p-4 rounded-lg ${isActive ? "bg-green-500" : "bg-red-500"}`}
|
|
418
|
-
>
|
|
419
|
-
<Text style={tw`text-white font-bold text-center`}>{isActive ? "Active" : "Inactive"}</Text>
|
|
420
|
-
</Pressable>
|
|
421
|
-
</View>
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
#### Configuration
|
|
427
|
-
|
|
428
|
-
Configure custom colors and font families using `setConfig()`:
|
|
429
|
-
|
|
430
|
-
```typescript
|
|
431
|
-
import { setConfig } from '@mgcrea/react-native-tailwind/runtime';
|
|
432
|
-
|
|
433
|
-
// Match your tailwind.config structure
|
|
434
|
-
setConfig({
|
|
435
|
-
theme: {
|
|
436
|
-
extend: {
|
|
437
|
-
colors: {
|
|
438
|
-
primary: '#007AFF',
|
|
439
|
-
secondary: '#5856D6',
|
|
440
|
-
brand: {
|
|
441
|
-
light: '#FF6B6B',
|
|
442
|
-
dark: '#CC0000',
|
|
443
|
-
},
|
|
444
|
-
},
|
|
445
|
-
fontFamily: {
|
|
446
|
-
sans: ['"SF Pro Rounded"'],
|
|
447
|
-
custom: ['"My Custom Font"'],
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
},
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
// Now you can use custom theme
|
|
454
|
-
<View style={tw`bg-primary p-4`} />
|
|
455
|
-
<Text style={tw`text-brand-light font-custom`}>Custom styling</Text>
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
#### API Reference
|
|
459
|
-
|
|
460
|
-
**`tw` tagged template**
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
function tw(strings: TemplateStringsArray, ...values: unknown[]): TwStyle;
|
|
464
|
-
|
|
465
|
-
type TwStyle = {
|
|
466
|
-
style: ViewStyle | TextStyle | ImageStyle;
|
|
467
|
-
activeStyle?: ViewStyle | TextStyle | ImageStyle;
|
|
468
|
-
focusStyle?: ViewStyle | TextStyle | ImageStyle;
|
|
469
|
-
disabledStyle?: ViewStyle | TextStyle | ImageStyle;
|
|
470
|
-
};
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
Parses Tailwind classes at runtime and returns a `TwStyle` object with separate properties for base styles and state modifiers. Results are automatically memoized for performance.
|
|
474
|
-
|
|
475
|
-
**`twStyle(className: string)`**
|
|
476
|
-
|
|
477
|
-
```typescript
|
|
478
|
-
function twStyle(className: string): TwStyle | undefined;
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
String version for cases where template literals aren't needed. Returns `undefined` for empty strings.
|
|
482
|
-
|
|
483
|
-
**`setConfig(config: RuntimeConfig)`**
|
|
484
|
-
|
|
485
|
-
```typescript
|
|
486
|
-
function setConfig(config: RuntimeConfig): void;
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
Configure runtime theme settings (colors, etc.). Matches `tailwind.config.mjs` structure.
|
|
490
|
-
|
|
491
|
-
**`clearCache()`**
|
|
492
|
-
|
|
493
|
-
```typescript
|
|
494
|
-
function clearCache(): void;
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
Clears the internal memoization cache. Useful for testing.
|
|
498
|
-
|
|
499
|
-
**`getCacheStats()`**
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
function getCacheStats(): { size: number; keys: string[] };
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
Returns cache statistics for debugging/monitoring.
|
|
506
|
-
|
|
507
|
-
#### Performance Considerations
|
|
508
|
-
|
|
509
|
-
- **Bundle Size**: The runtime module adds ~25KB minified (~15-20KB gzipped) to your bundle
|
|
510
|
-
- **Caching**: All parsed styles are automatically memoized, so repeated className strings have minimal overhead
|
|
511
|
-
- **When to Use**:
|
|
512
|
-
- ✅ Truly dynamic values that can't be determined at compile-time
|
|
513
|
-
- ✅ Prototyping and rapid development
|
|
514
|
-
- ❌ Static styles (use compile-time `className` instead for zero overhead)
|
|
515
|
-
- ❌ Performance-critical hot paths (compile-time is faster)
|
|
516
|
-
|
|
517
|
-
#### Runtime vs Compile-Time
|
|
518
|
-
|
|
519
|
-
| Feature | Compile-Time (`className`) | Runtime (`tw` tag) |
|
|
520
|
-
| -------------- | ------------------------------- | ----------------------- |
|
|
521
|
-
| Bundle Size | Only used styles (~4KB typical) | Full parser (~25KB) |
|
|
522
|
-
| Performance | Zero overhead (pre-compiled) | Fast (memoized parsing) |
|
|
523
|
-
| Dynamic Values | Conditional only | Fully dynamic |
|
|
524
|
-
| Custom Colors | Via `tailwind.config.*` | Via `setConfig()` |
|
|
525
|
-
| Type Safety | Full TypeScript support | Full TypeScript support |
|
|
526
|
-
|
|
527
|
-
**Recommendation**: Use compile-time `className` by default for best performance. Use runtime `tw` only when you need fully dynamic styling that can't be expressed with conditional logic.
|
|
528
|
-
|
|
529
167
|
### State Modifiers
|
|
530
168
|
|
|
531
|
-
Apply styles based on component state with zero runtime overhead. The Babel plugin automatically generates optimized style functions.
|
|
532
|
-
|
|
533
|
-
#### Active Modifier (Pressable)
|
|
534
|
-
|
|
535
|
-
Use the `active:` modifier to apply styles when a `Pressable` component is pressed. **Requires using the enhanced `Pressable` component from this package.**
|
|
536
|
-
|
|
537
|
-
**Basic Example:**
|
|
538
|
-
|
|
539
169
|
```tsx
|
|
540
|
-
import { Text } from "react-native";
|
|
541
|
-
import { Pressable } from "@mgcrea/react-native-tailwind";
|
|
170
|
+
import { Pressable, Text } from "@mgcrea/react-native-tailwind";
|
|
542
171
|
|
|
543
172
|
export function MyButton() {
|
|
544
173
|
return (
|
|
@@ -549,159 +178,21 @@ export function MyButton() {
|
|
|
549
178
|
}
|
|
550
179
|
```
|
|
551
180
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
```tsx
|
|
555
|
-
<Pressable
|
|
556
|
-
style={({ pressed }) => [_twStyles._bg_blue_500_p_4_rounded_lg, pressed && _twStyles._active_bg_blue_700]}
|
|
557
|
-
>
|
|
558
|
-
<Text style={_twStyles._font_semibold_text_white}>Press Me</Text>
|
|
559
|
-
</Pressable>;
|
|
560
|
-
|
|
561
|
-
// Generated styles:
|
|
562
|
-
const _twStyles = StyleSheet.create({
|
|
563
|
-
_bg_blue_500_p_4_rounded_lg: { backgroundColor: "#3B82F6", padding: 16, borderRadius: 8 },
|
|
564
|
-
_active_bg_blue_700: { backgroundColor: "#1D4ED8" },
|
|
565
|
-
_font_semibold_text_white: { fontWeight: "600", color: "#FFFFFF" },
|
|
566
|
-
});
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
**Multiple Active Modifiers:**
|
|
570
|
-
|
|
571
|
-
```tsx
|
|
572
|
-
<Pressable className="bg-green-500 active:bg-green-700 p-4 active:p-6 rounded-lg">
|
|
573
|
-
<Text className="text-white">Press for darker & larger padding</Text>
|
|
574
|
-
</Pressable>
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
**Complex Styling:**
|
|
578
|
-
|
|
579
|
-
```tsx
|
|
580
|
-
<Pressable className="bg-purple-500 active:bg-purple-800 border-2 border-purple-700 active:border-purple-900 p-4 rounded-lg">
|
|
581
|
-
<Text className="text-white">Background + Border Changes</Text>
|
|
582
|
-
</Pressable>
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
**Key Features:**
|
|
586
|
-
|
|
587
|
-
- ✅ **Zero runtime overhead** — All parsing happens at compile-time
|
|
588
|
-
- ✅ **Native Pressable API** — Uses Pressable's `style={({ pressed }) => ...}` pattern
|
|
589
|
-
- ✅ **Type-safe** — Full TypeScript autocomplete for `active:` classes
|
|
590
|
-
- ✅ **Optimized** — Styles deduplicated via `StyleSheet.create`
|
|
591
|
-
- ✅ **Works with custom colors** — `active:bg-primary`, `active:bg-secondary`, etc.
|
|
592
|
-
|
|
593
|
-
#### Focus Modifier (TextInput)
|
|
594
|
-
|
|
595
|
-
Use the `focus:` modifier to apply styles when a `TextInput` component is focused. **Requires using the enhanced `TextInput` component from this package.**
|
|
596
|
-
|
|
597
|
-
**Basic Example:**
|
|
598
|
-
|
|
599
|
-
```tsx
|
|
600
|
-
import { TextInput } from "@mgcrea/react-native-tailwind";
|
|
601
|
-
|
|
602
|
-
export function MyInput() {
|
|
603
|
-
return (
|
|
604
|
-
<TextInput
|
|
605
|
-
className="border-2 border-gray-300 focus:border-blue-500 p-3 rounded-lg bg-white"
|
|
606
|
-
placeholder="Email address"
|
|
607
|
-
/>
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
**How it works:**
|
|
613
|
-
|
|
614
|
-
The package exports an enhanced `TextInput` component that:
|
|
615
|
-
|
|
616
|
-
1. Manages focus state internally using `onFocus`/`onBlur` callbacks
|
|
617
|
-
2. Passes focus state to the style function: `style={({ focused }) => ...}`
|
|
618
|
-
3. Works seamlessly with the `focus:` modifier in className
|
|
619
|
-
|
|
620
|
-
**Multiple Focus Modifiers:**
|
|
621
|
-
|
|
622
|
-
```tsx
|
|
623
|
-
<TextInput className="border-2 border-gray-300 focus:border-green-500 bg-gray-50 focus:bg-white p-3 rounded-lg" />
|
|
624
|
-
```
|
|
625
|
-
|
|
626
|
-
**Supported Modifiers by Component:**
|
|
627
|
-
|
|
628
|
-
| Component | Supported Modifiers | Notes |
|
|
629
|
-
| ---------------------- | ------------------------------------------ | ------------------------------------------ |
|
|
630
|
-
| `Pressable` (enhanced) | `active:`, `hover:`, `focus:`, `disabled:` | Use `@mgcrea/react-native-tailwind` export |
|
|
631
|
-
| `TextInput` (enhanced) | `focus:`, `disabled:` | Use `@mgcrea/react-native-tailwind` export |
|
|
632
|
-
|
|
633
|
-
#### Disabled Modifier (Pressable & TextInput)
|
|
634
|
-
|
|
635
|
-
Use the `disabled:` modifier to apply styles when a component is disabled. **Requires using the enhanced components from this package.**
|
|
636
|
-
|
|
637
|
-
**Pressable Example:**
|
|
638
|
-
|
|
639
|
-
```tsx
|
|
640
|
-
import { Pressable, Text } from "@mgcrea/react-native-tailwind";
|
|
641
|
-
|
|
642
|
-
export function SubmitButton({ isLoading }) {
|
|
643
|
-
return (
|
|
644
|
-
<Pressable
|
|
645
|
-
disabled={isLoading}
|
|
646
|
-
className="bg-blue-500 active:bg-blue-700 disabled:bg-gray-400 p-4 rounded-lg"
|
|
647
|
-
>
|
|
648
|
-
<Text className="text-white font-semibold">{isLoading ? "Loading..." : "Submit"}</Text>
|
|
649
|
-
</Pressable>
|
|
650
|
-
);
|
|
651
|
-
}
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
**TextInput Example:**
|
|
181
|
+
### Dark Mode
|
|
655
182
|
|
|
656
183
|
```tsx
|
|
657
|
-
import {
|
|
184
|
+
import { View, Text } from "react-native";
|
|
658
185
|
|
|
659
|
-
export function
|
|
186
|
+
export function ThemeCard() {
|
|
660
187
|
return (
|
|
661
|
-
<
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
placeholder="Enter text"
|
|
665
|
-
/>
|
|
188
|
+
<View className="bg-white dark:bg-gray-900 p-4 rounded-lg">
|
|
189
|
+
<Text className="text-gray-900 dark:text-white">Adapts to device theme</Text>
|
|
190
|
+
</View>
|
|
666
191
|
);
|
|
667
192
|
}
|
|
668
193
|
```
|
|
669
194
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
The enhanced components inject the `disabled` prop value into the style function context:
|
|
673
|
-
|
|
674
|
-
- **Pressable**: Extends the existing `{ pressed, hovered, focused }` state with `disabled`
|
|
675
|
-
- **TextInput**: Provides `{ focused, disabled }` state to style functions
|
|
676
|
-
|
|
677
|
-
**TextInput `disabled` Prop:**
|
|
678
|
-
|
|
679
|
-
The enhanced `TextInput` also provides a convenient `disabled` prop that overrides React Native's `editable` prop:
|
|
680
|
-
|
|
681
|
-
```tsx
|
|
682
|
-
// These are equivalent:
|
|
683
|
-
<TextInput disabled={true} />
|
|
684
|
-
<TextInput editable={false} />
|
|
685
|
-
|
|
686
|
-
// If both are provided, disabled takes precedence:
|
|
687
|
-
<TextInput disabled={true} editable={true} /> // Component is disabled
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
**Important Notes:**
|
|
691
|
-
|
|
692
|
-
- ⚠️ **Enhanced components required** — State modifiers require using the enhanced components from this package
|
|
693
|
-
- ℹ️ **Component-specific** — Each modifier only works on compatible components
|
|
694
|
-
- ℹ️ **No nested modifiers** — Combinations like `active:focus:bg-blue-500` are not currently supported
|
|
695
|
-
- ✅ **Zero styling overhead** — All className parsing happens at compile-time
|
|
696
|
-
- ✅ **Minimal runtime cost** — Only adds state management (focus tracking, disabled injection)
|
|
697
|
-
- ✅ **Type-safe** — Full TypeScript autocomplete for all modifiers
|
|
698
|
-
- ✅ **Works with custom colors** — `focus:border-primary`, `active:bg-secondary`, `disabled:bg-gray-200`, etc.
|
|
699
|
-
|
|
700
|
-
### Platform Modifiers
|
|
701
|
-
|
|
702
|
-
Apply platform-specific styles using `ios:`, `android:`, and `web:` modifiers. These work on **all components** (not just enhanced ones) and compile to `Platform.select()` calls with zero runtime parsing overhead.
|
|
703
|
-
|
|
704
|
-
**Basic Example:**
|
|
195
|
+
### Platform-Specific Styles
|
|
705
196
|
|
|
706
197
|
```tsx
|
|
707
198
|
import { View, Text } from "react-native";
|
|
@@ -709,1493 +200,17 @@ import { View, Text } from "react-native";
|
|
|
709
200
|
export function PlatformCard() {
|
|
710
201
|
return (
|
|
711
202
|
<View className="p-4 ios:p-6 android:p-8 bg-white rounded-lg">
|
|
712
|
-
<Text className="text-base ios:text-blue-600 android:text-green-600">
|
|
203
|
+
<Text className="text-base ios:text-blue-600 android:text-green-600">
|
|
204
|
+
Platform-specific styles
|
|
205
|
+
</Text>
|
|
713
206
|
</View>
|
|
714
207
|
);
|
|
715
208
|
}
|
|
716
209
|
```
|
|
717
210
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
```tsx
|
|
721
|
-
import { Platform, StyleSheet } from "react-native";
|
|
722
|
-
|
|
723
|
-
<View
|
|
724
|
-
style={[
|
|
725
|
-
_twStyles._bg_white_p_4_rounded_lg,
|
|
726
|
-
Platform.select({
|
|
727
|
-
ios: _twStyles._ios_p_6,
|
|
728
|
-
android: _twStyles._android_p_8,
|
|
729
|
-
}),
|
|
730
|
-
]}
|
|
731
|
-
>
|
|
732
|
-
<Text
|
|
733
|
-
style={[
|
|
734
|
-
_twStyles._text_base,
|
|
735
|
-
Platform.select({
|
|
736
|
-
ios: _twStyles._ios_text_blue_600,
|
|
737
|
-
android: _twStyles._android_text_green_600,
|
|
738
|
-
}),
|
|
739
|
-
]}
|
|
740
|
-
>
|
|
741
|
-
Platform-specific styles
|
|
742
|
-
</Text>
|
|
743
|
-
</View>;
|
|
744
|
-
|
|
745
|
-
// Generated styles:
|
|
746
|
-
const _twStyles = StyleSheet.create({
|
|
747
|
-
_bg_white_p_4_rounded_lg: {
|
|
748
|
-
backgroundColor: "#FFFFFF",
|
|
749
|
-
padding: 16,
|
|
750
|
-
borderRadius: 8,
|
|
751
|
-
},
|
|
752
|
-
_ios_p_6: { padding: 24 },
|
|
753
|
-
_android_p_8: { padding: 32 },
|
|
754
|
-
_text_base: { fontSize: 16 },
|
|
755
|
-
_ios_text_blue_600: { color: "#2563EB" },
|
|
756
|
-
_android_text_green_600: { color: "#059669" },
|
|
757
|
-
});
|
|
758
|
-
```
|
|
211
|
+
## Contributing
|
|
759
212
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
**Platform-specific colors:**
|
|
763
|
-
|
|
764
|
-
```tsx
|
|
765
|
-
// Different colors per platform for brand consistency
|
|
766
|
-
<View className="bg-blue-500 ios:bg-blue-600 android:bg-green-600">
|
|
767
|
-
<Text className="text-white">Platform-specific background</Text>
|
|
768
|
-
</View>
|
|
769
|
-
```
|
|
770
|
-
|
|
771
|
-
**Platform-specific spacing:**
|
|
772
|
-
|
|
773
|
-
```tsx
|
|
774
|
-
// More padding on Android due to larger default touch targets
|
|
775
|
-
<View className="p-4 ios:p-6 android:p-8">
|
|
776
|
-
<Text>Platform-specific padding</Text>
|
|
777
|
-
</View>
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
**Combined with base styles:**
|
|
781
|
-
|
|
782
|
-
```tsx
|
|
783
|
-
// Base styles + platform-specific overrides
|
|
784
|
-
<View className="border-2 border-gray-300 ios:border-blue-500 android:border-green-500 rounded-lg p-4">
|
|
785
|
-
<Text className="text-gray-800 ios:text-blue-800 android:text-green-800">
|
|
786
|
-
Base styles with platform overrides
|
|
787
|
-
</Text>
|
|
788
|
-
</View>
|
|
789
|
-
```
|
|
790
|
-
|
|
791
|
-
**Multiple platform modifiers:**
|
|
792
|
-
|
|
793
|
-
```tsx
|
|
794
|
-
// Combine multiple platform-specific styles
|
|
795
|
-
<View className="bg-gray-100 ios:bg-blue-50 android:bg-green-50 p-4 ios:p-6 android:p-8 rounded-lg">
|
|
796
|
-
<Text>Multiple platform styles</Text>
|
|
797
|
-
</View>
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
**Web platform support:**
|
|
801
|
-
|
|
802
|
-
```tsx
|
|
803
|
-
// Different styles for React Native Web
|
|
804
|
-
<View className="p-4 ios:p-6 android:p-8 web:p-2">
|
|
805
|
-
<Text className="text-base web:text-lg">Cross-platform styling</Text>
|
|
806
|
-
</View>
|
|
807
|
-
```
|
|
808
|
-
|
|
809
|
-
**Mixing with state modifiers:**
|
|
810
|
-
|
|
811
|
-
```tsx
|
|
812
|
-
import { Pressable } from "@mgcrea/react-native-tailwind";
|
|
813
|
-
|
|
814
|
-
// Platform modifiers work alongside state modifiers
|
|
815
|
-
<Pressable className="bg-blue-500 active:bg-blue-700 ios:border-2 android:border-0 p-4 rounded-lg">
|
|
816
|
-
<Text className="text-white">Button with platform + state modifiers</Text>
|
|
817
|
-
</Pressable>;
|
|
818
|
-
```
|
|
819
|
-
|
|
820
|
-
**Key Features:**
|
|
821
|
-
|
|
822
|
-
- ✅ **Works on all components** — No need for enhanced components (unlike state modifiers)
|
|
823
|
-
- ✅ **Zero runtime overhead** — All parsing happens at compile-time
|
|
824
|
-
- ✅ **Native Platform API** — Uses React Native's `Platform.select()` under the hood
|
|
825
|
-
- ✅ **Type-safe** — Full TypeScript autocomplete for platform modifiers
|
|
826
|
-
- ✅ **Optimized** — Styles deduplicated via `StyleSheet.create`
|
|
827
|
-
- ✅ **Works with custom colors** — `ios:bg-primary`, `android:bg-secondary`, etc.
|
|
828
|
-
- ✅ **Minimal runtime cost** — Only one `Platform.select()` call per element with platform modifiers
|
|
829
|
-
|
|
830
|
-
**Supported Platforms:**
|
|
831
|
-
|
|
832
|
-
| Modifier | Platform | Description |
|
|
833
|
-
| ---------- | ---------------- | -------------------------- |
|
|
834
|
-
| `ios:` | iOS | Styles specific to iOS |
|
|
835
|
-
| `android:` | Android | Styles specific to Android |
|
|
836
|
-
| `web:` | React Native Web | Styles for web platform |
|
|
837
|
-
|
|
838
|
-
**How it works:**
|
|
839
|
-
|
|
840
|
-
The Babel plugin:
|
|
841
|
-
|
|
842
|
-
1. Detects platform modifiers during compilation
|
|
843
|
-
2. Parses all platform-specific classes at compile-time
|
|
844
|
-
3. Generates `Platform.select()` expressions with references to pre-compiled styles
|
|
845
|
-
4. Auto-imports `Platform` from `react-native` when needed
|
|
846
|
-
5. Merges platform styles with base classes and other modifiers in style arrays
|
|
847
|
-
|
|
848
|
-
This approach provides the best of both worlds: compile-time optimization for all styles, with minimal runtime platform detection only for the conditional selection logic.
|
|
849
|
-
|
|
850
|
-
### Color Scheme Modifiers
|
|
851
|
-
|
|
852
|
-
Apply color scheme-specific styles using `dark:` and `light:` modifiers that automatically react to the device's appearance settings. These work on **all components in functional components** and compile to conditional expressions that use React Native's `useColorScheme()` hook.
|
|
853
|
-
|
|
854
|
-
**Basic Example:**
|
|
855
|
-
|
|
856
|
-
```tsx
|
|
857
|
-
import { View, Text } from "react-native";
|
|
858
|
-
|
|
859
|
-
export function ThemeCard() {
|
|
860
|
-
return (
|
|
861
|
-
<View className="bg-white dark:bg-gray-900 p-4 rounded-lg">
|
|
862
|
-
<Text className="text-gray-900 dark:text-white">Adapts to device theme</Text>
|
|
863
|
-
</View>
|
|
864
|
-
);
|
|
865
|
-
}
|
|
866
|
-
```
|
|
867
|
-
|
|
868
|
-
**Transforms to:**
|
|
869
|
-
|
|
870
|
-
```tsx
|
|
871
|
-
import { useColorScheme, StyleSheet } from "react-native";
|
|
872
|
-
|
|
873
|
-
export function ThemeCard() {
|
|
874
|
-
const _twColorScheme = useColorScheme();
|
|
875
|
-
|
|
876
|
-
return (
|
|
877
|
-
<View
|
|
878
|
-
style={[
|
|
879
|
-
_twStyles._bg_white_p_4_rounded_lg,
|
|
880
|
-
_twColorScheme === "dark" && _twStyles._dark_bg_gray_900,
|
|
881
|
-
]}
|
|
882
|
-
>
|
|
883
|
-
<Text style={[_twStyles._text_gray_900, _twColorScheme === "dark" && _twStyles._dark_text_white]}>
|
|
884
|
-
Adapts to device theme
|
|
885
|
-
</Text>
|
|
886
|
-
</View>
|
|
887
|
-
);
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
// Generated styles:
|
|
891
|
-
const _twStyles = StyleSheet.create({
|
|
892
|
-
_bg_white_p_4_rounded_lg: {
|
|
893
|
-
backgroundColor: "#FFFFFF",
|
|
894
|
-
padding: 16,
|
|
895
|
-
borderRadius: 8,
|
|
896
|
-
},
|
|
897
|
-
_dark_bg_gray_900: { backgroundColor: "#111827" },
|
|
898
|
-
_text_gray_900: { color: "#111827" },
|
|
899
|
-
_dark_text_white: { color: "#FFFFFF" },
|
|
900
|
-
});
|
|
901
|
-
```
|
|
902
|
-
|
|
903
|
-
**Common Use Cases:**
|
|
904
|
-
|
|
905
|
-
**Dark mode support:**
|
|
906
|
-
|
|
907
|
-
```tsx
|
|
908
|
-
// Automatically switches between light and dark themes
|
|
909
|
-
<View className="bg-white dark:bg-gray-900">
|
|
910
|
-
<Text className="text-gray-900 dark:text-white">Theme-aware text</Text>
|
|
911
|
-
</View>
|
|
912
|
-
```
|
|
913
|
-
|
|
914
|
-
**Both light and dark overrides:**
|
|
915
|
-
|
|
916
|
-
```tsx
|
|
917
|
-
// Specify both light and dark mode styles explicitly
|
|
918
|
-
<View className="bg-gray-100 light:bg-white dark:bg-gray-900">
|
|
919
|
-
<Text className="text-gray-600 light:text-gray-900 dark:text-gray-100">Custom light & dark styles</Text>
|
|
920
|
-
</View>
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
**Mixed color scheme and platform modifiers:**
|
|
924
|
-
|
|
925
|
-
```tsx
|
|
926
|
-
// Combine color scheme with platform-specific styles
|
|
927
|
-
<View className="p-4 ios:p-6 dark:bg-gray-900 android:rounded-xl">
|
|
928
|
-
<Text className="text-base dark:text-white ios:text-blue-600">Platform + theme aware</Text>
|
|
929
|
-
</View>
|
|
930
|
-
```
|
|
931
|
-
|
|
932
|
-
**Theme-aware cards:**
|
|
933
|
-
|
|
934
|
-
```tsx
|
|
935
|
-
// Card that looks great in both light and dark mode
|
|
936
|
-
<View className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 p-4 rounded-lg">
|
|
937
|
-
<Text className="text-lg font-bold text-gray-900 dark:text-white mb-2">Card Title</Text>
|
|
938
|
-
<Text className="text-gray-600 dark:text-gray-300">Card description text</Text>
|
|
939
|
-
</View>
|
|
940
|
-
```
|
|
941
|
-
|
|
942
|
-
**Key Features:**
|
|
943
|
-
|
|
944
|
-
- ✅ **Reactive** — Automatically updates when user changes system appearance
|
|
945
|
-
- ✅ **Zero runtime parsing** — All styles compiled at build time
|
|
946
|
-
- ✅ **Auto-injected hook** — `useColorScheme()` automatically added to components
|
|
947
|
-
- ✅ **Works with all modifiers** — Combine with platform and state modifiers
|
|
948
|
-
- ✅ **Type-safe** — Full TypeScript autocomplete
|
|
949
|
-
- ✅ **Optimized** — Minimal runtime overhead (just conditional checks)
|
|
950
|
-
|
|
951
|
-
**Supported Modifiers:**
|
|
952
|
-
|
|
953
|
-
| Modifier | Color Scheme | Description |
|
|
954
|
-
| -------- | ------------ | ------------------------------ |
|
|
955
|
-
| `dark:` | Dark mode | Styles when dark mode is active |
|
|
956
|
-
| `light:` | Light mode | Styles when light mode is active |
|
|
957
|
-
|
|
958
|
-
**How it works:**
|
|
959
|
-
|
|
960
|
-
The Babel plugin:
|
|
961
|
-
|
|
962
|
-
1. Detects color scheme modifiers during compilation
|
|
963
|
-
2. Finds the parent function component
|
|
964
|
-
3. Auto-injects `const _twColorScheme = useColorScheme();` at the top of the component
|
|
965
|
-
4. Parses all color scheme-specific classes at compile-time
|
|
966
|
-
5. Generates conditional expressions: `_twColorScheme === 'dark' && styles._dark_bg_gray_900`
|
|
967
|
-
6. Auto-imports `useColorScheme` from `react-native` when needed
|
|
968
|
-
7. Reuses the same hook variable for multiple elements in the same component
|
|
969
|
-
|
|
970
|
-
**Requirements:**
|
|
971
|
-
|
|
972
|
-
- ⚠️ **Functional components only** — Color scheme modifiers require hooks (React Native's `useColorScheme()`)
|
|
973
|
-
- ✅ Works with function declarations: `function Component() { ... }`
|
|
974
|
-
- ✅ Works with arrow functions: `const Component = () => { ... }`
|
|
975
|
-
- ✅ Works with concise arrow functions: `const Component = () => <View className="dark:..." />`
|
|
976
|
-
- ✅ Works in nested callbacks: Hook injected at component level, not in callbacks
|
|
977
|
-
- ❌ **Not supported in class components** — Will show a warning
|
|
978
|
-
- ⚠️ **React Native 0.62+** — Requires the `useColorScheme` API
|
|
979
|
-
- ✅ **Dynamic expressions supported** — Works in template literals and conditional expressions:
|
|
980
|
-
|
|
981
|
-
```tsx
|
|
982
|
-
<View className={`p-4 ${isActive ? "dark:bg-blue-500" : "dark:bg-gray-900"}`} />
|
|
983
|
-
```
|
|
984
|
-
|
|
985
|
-
**Performance:**
|
|
986
|
-
|
|
987
|
-
- **Compile-time**: All styles parsed and registered during build
|
|
988
|
-
- **Runtime**: One `useColorScheme()` hook call per component + minimal conditional checks
|
|
989
|
-
- **Bundle size**: Only includes styles actually used in your code
|
|
990
|
-
|
|
991
|
-
#### Scheme Modifier (Convenience)
|
|
992
|
-
|
|
993
|
-
The `scheme:` modifier is a convenience feature that automatically expands to both `dark:` and `light:` modifiers for color classes. This is useful when you have custom colors with separate dark and light variants.
|
|
994
|
-
|
|
995
|
-
**Basic Usage:**
|
|
996
|
-
|
|
997
|
-
```tsx
|
|
998
|
-
import { View, Text } from "react-native";
|
|
999
|
-
|
|
1000
|
-
export function ThemedCard() {
|
|
1001
|
-
return (
|
|
1002
|
-
<View className="scheme:bg-systemGray p-4 rounded-lg">
|
|
1003
|
-
<Text className="scheme:text-systemLabel">Adaptive system colors</Text>
|
|
1004
|
-
</View>
|
|
1005
|
-
);
|
|
1006
|
-
}
|
|
1007
|
-
```
|
|
1008
|
-
|
|
1009
|
-
**Transforms to:**
|
|
1010
|
-
|
|
1011
|
-
```tsx
|
|
1012
|
-
// Automatically expands to both dark: and light: modifiers
|
|
1013
|
-
<View className="dark:bg-systemGray-dark light:bg-systemGray-light p-4 rounded-lg">
|
|
1014
|
-
<Text className="dark:text-systemLabel-dark light:text-systemLabel-light">
|
|
1015
|
-
Adaptive system colors
|
|
1016
|
-
</Text>
|
|
1017
|
-
</View>
|
|
1018
|
-
```
|
|
1019
|
-
|
|
1020
|
-
**Requirements:**
|
|
1021
|
-
|
|
1022
|
-
To use the `scheme:` modifier, you must define both color variants in your `tailwind.config.*`:
|
|
1023
|
-
|
|
1024
|
-
```javascript
|
|
1025
|
-
// tailwind.config.mjs
|
|
1026
|
-
export default {
|
|
1027
|
-
theme: {
|
|
1028
|
-
extend: {
|
|
1029
|
-
colors: {
|
|
1030
|
-
// Option 1: Nested structure
|
|
1031
|
-
systemGray: {
|
|
1032
|
-
light: "#8e8e93",
|
|
1033
|
-
dark: "#8e8e93",
|
|
1034
|
-
},
|
|
1035
|
-
systemLabel: {
|
|
1036
|
-
light: "#000000",
|
|
1037
|
-
dark: "#ffffff",
|
|
1038
|
-
},
|
|
1039
|
-
|
|
1040
|
-
// Option 2: Flat structure with suffixes
|
|
1041
|
-
"primary-light": "#bfdbfe",
|
|
1042
|
-
"primary-dark": "#1e40af",
|
|
1043
|
-
},
|
|
1044
|
-
},
|
|
1045
|
-
},
|
|
1046
|
-
};
|
|
1047
|
-
```
|
|
1048
|
-
|
|
1049
|
-
**Configuring Suffixes:**
|
|
1050
|
-
|
|
1051
|
-
By default, the plugin looks for `-dark` and `-light` suffixes. You can customize these in your Babel configuration:
|
|
1052
|
-
|
|
1053
|
-
```javascript
|
|
1054
|
-
// babel.config.js
|
|
1055
|
-
module.exports = {
|
|
1056
|
-
plugins: [
|
|
1057
|
-
[
|
|
1058
|
-
"@mgcrea/react-native-tailwind/babel",
|
|
1059
|
-
{
|
|
1060
|
-
schemeModifier: {
|
|
1061
|
-
darkSuffix: "-dark", // default
|
|
1062
|
-
lightSuffix: "-light", // default
|
|
1063
|
-
},
|
|
1064
|
-
},
|
|
1065
|
-
],
|
|
1066
|
-
],
|
|
1067
|
-
};
|
|
1068
|
-
```
|
|
1069
|
-
|
|
1070
|
-
**Custom Suffixes Example:**
|
|
1071
|
-
|
|
1072
|
-
```javascript
|
|
1073
|
-
// babel.config.js with custom suffixes
|
|
1074
|
-
module.exports = {
|
|
1075
|
-
plugins: [
|
|
1076
|
-
[
|
|
1077
|
-
"@mgcrea/react-native-tailwind/babel",
|
|
1078
|
-
{
|
|
1079
|
-
schemeModifier: {
|
|
1080
|
-
darkSuffix: "Dark",
|
|
1081
|
-
lightSuffix: "Light",
|
|
1082
|
-
},
|
|
1083
|
-
},
|
|
1084
|
-
],
|
|
1085
|
-
],
|
|
1086
|
-
};
|
|
1087
|
-
|
|
1088
|
-
// tailwind.config.mjs
|
|
1089
|
-
export default {
|
|
1090
|
-
theme: {
|
|
1091
|
-
extend: {
|
|
1092
|
-
colors: {
|
|
1093
|
-
systemGrayDark: "#8e8e93",
|
|
1094
|
-
systemGrayLight: "#8e8e93",
|
|
1095
|
-
},
|
|
1096
|
-
},
|
|
1097
|
-
},
|
|
1098
|
-
};
|
|
1099
|
-
|
|
1100
|
-
// Usage (same as before)
|
|
1101
|
-
<View className="scheme:bg-systemGray" />
|
|
1102
|
-
```
|
|
1103
|
-
|
|
1104
|
-
**Validation:**
|
|
1105
|
-
|
|
1106
|
-
The plugin validates that both color variants exist at compile time:
|
|
1107
|
-
|
|
1108
|
-
```tsx
|
|
1109
|
-
// ✅ Works - both variants exist
|
|
1110
|
-
<View className="scheme:bg-systemGray" />
|
|
1111
|
-
// Expands to: dark:bg-systemGray-dark light:bg-systemGray-light
|
|
1112
|
-
|
|
1113
|
-
// ⚠️ Warning (development only) - missing light variant
|
|
1114
|
-
<View className="scheme:bg-incomplete" />
|
|
1115
|
-
// Only has: incomplete-dark
|
|
1116
|
-
// Warning: "Missing: incomplete-light. This modifier will be ignored."
|
|
1117
|
-
|
|
1118
|
-
// ⚠️ Warning - non-color class
|
|
1119
|
-
<View className="scheme:p-4" />
|
|
1120
|
-
// Warning: "scheme: modifier only supports color classes (text-*, bg-*, border-*)"
|
|
1121
|
-
```
|
|
1122
|
-
|
|
1123
|
-
**Supported Color Classes:**
|
|
1124
|
-
|
|
1125
|
-
The `scheme:` modifier only works with color utilities:
|
|
1126
|
-
|
|
1127
|
-
- ✅ `scheme:text-{color}` — Text colors
|
|
1128
|
-
- ✅ `scheme:bg-{color}` — Background colors
|
|
1129
|
-
- ✅ `scheme:border-{color}` — Border colors
|
|
1130
|
-
- ❌ Other utilities — Ignored with development warning
|
|
1131
|
-
|
|
1132
|
-
**Use Cases:**
|
|
1133
|
-
|
|
1134
|
-
**Semantic color names:**
|
|
1135
|
-
|
|
1136
|
-
```tsx
|
|
1137
|
-
// Define semantic system colors that adapt to appearance
|
|
1138
|
-
<View className="scheme:bg-systemBackground p-4">
|
|
1139
|
-
<Text className="scheme:text-systemLabel">System-native appearance</Text>
|
|
1140
|
-
<View className="scheme:border-systemSeparator border-t mt-2 pt-2">
|
|
1141
|
-
<Text className="scheme:text-systemSecondaryLabel">Secondary text</Text>
|
|
1142
|
-
</View>
|
|
1143
|
-
</View>
|
|
1144
|
-
```
|
|
1145
|
-
|
|
1146
|
-
**Brand colors with theme variants:**
|
|
1147
|
-
|
|
1148
|
-
```tsx
|
|
1149
|
-
// Brand colors that look good in both themes
|
|
1150
|
-
<View className="scheme:bg-brand p-6 rounded-xl">
|
|
1151
|
-
<Text className="scheme:text-brandContrast text-xl font-bold">
|
|
1152
|
-
Branded Card
|
|
1153
|
-
</Text>
|
|
1154
|
-
<Text className="scheme:text-brandSubtle mt-2">
|
|
1155
|
-
Automatically adapts to user's theme preference
|
|
1156
|
-
</Text>
|
|
1157
|
-
</View>
|
|
1158
|
-
```
|
|
1159
|
-
|
|
1160
|
-
**Mixed with other modifiers:**
|
|
1161
|
-
|
|
1162
|
-
```tsx
|
|
1163
|
-
import { Pressable } from "@mgcrea/react-native-tailwind";
|
|
1164
|
-
|
|
1165
|
-
// Combine with state and platform modifiers
|
|
1166
|
-
<Pressable className="scheme:bg-interactive active:opacity-80 ios:p-6 android:p-4 rounded-lg">
|
|
1167
|
-
<Text className="scheme:text-interactiveText font-semibold">
|
|
1168
|
-
Press Me
|
|
1169
|
-
</Text>
|
|
1170
|
-
</Pressable>
|
|
1171
|
-
```
|
|
1172
|
-
|
|
1173
|
-
**Key Features:**
|
|
1174
|
-
|
|
1175
|
-
- ✅ **DRY principle** — Define color pairs once, use everywhere with `scheme:`
|
|
1176
|
-
- ✅ **Compile-time expansion** — Expands to `dark:` and `light:` during build
|
|
1177
|
-
- ✅ **Type-safe** — Full TypeScript autocomplete
|
|
1178
|
-
- ✅ **Validates at compile-time** — Ensures both variants exist
|
|
1179
|
-
- ✅ **Zero runtime overhead** — Same performance as writing `dark:` and `light:` manually
|
|
1180
|
-
- ✅ **Configurable suffixes** — Adapt to your naming convention
|
|
1181
|
-
|
|
1182
|
-
**How it works:**
|
|
1183
|
-
|
|
1184
|
-
The Babel plugin:
|
|
1185
|
-
|
|
1186
|
-
1. Detects `scheme:` modifiers during compilation
|
|
1187
|
-
2. Validates that the class is a color utility (`text-*`, `bg-*`, `border-*`)
|
|
1188
|
-
3. Checks that both color variants exist in your custom colors (e.g., `systemGray-dark` and `systemGray-light`)
|
|
1189
|
-
4. Expands to both `dark:` and `light:` modifiers before further processing
|
|
1190
|
-
5. Processes the expanded modifiers using the standard color scheme logic (injects `useColorScheme()` hook)
|
|
1191
|
-
|
|
1192
|
-
This means `scheme:bg-systemGray` is functionally identical to writing `dark:bg-systemGray-dark light:bg-systemGray-light`, but more concise and maintainable.
|
|
1193
|
-
|
|
1194
|
-
### ScrollView Content Container
|
|
1195
|
-
|
|
1196
|
-
Use `contentContainerClassName` to style the ScrollView's content container:
|
|
1197
|
-
|
|
1198
|
-
```tsx
|
|
1199
|
-
import { ScrollView, View, Text } from "react-native";
|
|
1200
|
-
|
|
1201
|
-
export function MyScrollView() {
|
|
1202
|
-
return (
|
|
1203
|
-
<ScrollView className="flex-1 bg-gray-100" contentContainerClassName="items-center p-4 gap-4">
|
|
1204
|
-
<View className="bg-white rounded-lg p-4">
|
|
1205
|
-
<Text className="text-lg">Item 1</Text>
|
|
1206
|
-
</View>
|
|
1207
|
-
<View className="bg-white rounded-lg p-4">
|
|
1208
|
-
<Text className="text-lg">Item 2</Text>
|
|
1209
|
-
</View>
|
|
1210
|
-
</ScrollView>
|
|
1211
|
-
);
|
|
1212
|
-
}
|
|
1213
|
-
```
|
|
1214
|
-
|
|
1215
|
-
### FlatList with Column Wrapper
|
|
1216
|
-
|
|
1217
|
-
Use `columnWrapperClassName` for multi-column FlatLists:
|
|
1218
|
-
|
|
1219
|
-
```tsx
|
|
1220
|
-
import { FlatList, View, Text } from "react-native";
|
|
1221
|
-
|
|
1222
|
-
export function GridList({ items }) {
|
|
1223
|
-
return (
|
|
1224
|
-
<FlatList
|
|
1225
|
-
className="flex-1 bg-gray-100"
|
|
1226
|
-
contentContainerClassName="p-4"
|
|
1227
|
-
columnWrapperClassName="gap-4 mb-4"
|
|
1228
|
-
numColumns={2}
|
|
1229
|
-
data={items}
|
|
1230
|
-
renderItem={({ item }) => (
|
|
1231
|
-
<View className="flex-1 bg-white rounded-lg p-4">
|
|
1232
|
-
<Text className="text-lg">{item.name}</Text>
|
|
1233
|
-
</View>
|
|
1234
|
-
)}
|
|
1235
|
-
/>
|
|
1236
|
-
);
|
|
1237
|
-
}
|
|
1238
|
-
```
|
|
1239
|
-
|
|
1240
|
-
### FlatList with Header and Footer
|
|
1241
|
-
|
|
1242
|
-
Use `ListHeaderComponentClassName` and `ListFooterComponentClassName`:
|
|
1243
|
-
|
|
1244
|
-
```tsx
|
|
1245
|
-
import { FlatList, View, Text } from "react-native";
|
|
1246
|
-
|
|
1247
|
-
export function ListWithHeaderFooter({ items }) {
|
|
1248
|
-
return (
|
|
1249
|
-
<FlatList
|
|
1250
|
-
className="flex-1"
|
|
1251
|
-
contentContainerClassName="p-4"
|
|
1252
|
-
ListHeaderComponentClassName="p-4 bg-blue-500 mb-4 rounded-lg"
|
|
1253
|
-
ListFooterComponentClassName="p-4 bg-gray-200 mt-4 rounded-lg"
|
|
1254
|
-
data={items}
|
|
1255
|
-
ListHeaderComponent={<Text className="text-white font-bold">Header</Text>}
|
|
1256
|
-
ListFooterComponent={<Text className="text-gray-600">End of list</Text>}
|
|
1257
|
-
renderItem={({ item }) => (
|
|
1258
|
-
<View className="bg-white rounded-lg p-4 mb-2">
|
|
1259
|
-
<Text>{item.name}</Text>
|
|
1260
|
-
</View>
|
|
1261
|
-
)}
|
|
1262
|
-
/>
|
|
1263
|
-
);
|
|
1264
|
-
}
|
|
1265
|
-
```
|
|
1266
|
-
|
|
1267
|
-
### Building Reusable Components
|
|
1268
|
-
|
|
1269
|
-
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):
|
|
1270
|
-
|
|
1271
|
-
```tsx
|
|
1272
|
-
import { Pressable, Text, View, StyleProp, ViewStyle } from "react-native";
|
|
1273
|
-
|
|
1274
|
-
type ButtonProps = {
|
|
1275
|
-
title: string;
|
|
1276
|
-
onPress?: () => void;
|
|
1277
|
-
// REQUIRED: Must accept style props for className to work
|
|
1278
|
-
style?: StyleProp<ViewStyle>;
|
|
1279
|
-
containerStyle?: StyleProp<ViewStyle>;
|
|
1280
|
-
// Optional: Include in type for TypeScript compatibility
|
|
1281
|
-
className?: string; // compile-time only
|
|
1282
|
-
containerClassName?: string; // compile-time only
|
|
1283
|
-
};
|
|
1284
|
-
|
|
1285
|
-
export function Button({ title, onPress, style, containerStyle }: ButtonProps) {
|
|
1286
|
-
// Use static className strings - these get optimized at compile-time
|
|
1287
|
-
return (
|
|
1288
|
-
<View className="p-2 bg-gray-100 rounded-lg" style={containerStyle}>
|
|
1289
|
-
<Pressable className="bg-blue-500 px-6 py-4 rounded-lg items-center" onPress={onPress} style={style}>
|
|
1290
|
-
<Text className="text-white text-center font-semibold text-base">{title}</Text>
|
|
1291
|
-
</Pressable>
|
|
1292
|
-
</View>
|
|
1293
|
-
);
|
|
1294
|
-
}
|
|
1295
|
-
```
|
|
1296
|
-
|
|
1297
|
-
**Key Points:**
|
|
1298
|
-
|
|
1299
|
-
- ✅ Use **static className strings** internally for default styling
|
|
1300
|
-
- ✅ **Must accept `style` props** - the Babel plugin transforms `className` → `style` before your component receives it
|
|
1301
|
-
- ✅ Include `className` props in the type (for TypeScript compatibility)
|
|
1302
|
-
- ✅ **Don't destructure** className props - they're already transformed to `style` and will be `undefined` at runtime
|
|
1303
|
-
- ✅ Babel plugin optimizes all static strings to `StyleSheet.create` calls
|
|
1304
|
-
|
|
1305
|
-
**Usage:**
|
|
1306
|
-
|
|
1307
|
-
```tsx
|
|
1308
|
-
// Default styling (uses internal static classNames)
|
|
1309
|
-
<Button title="Click Me" onPress={handlePress} />
|
|
1310
|
-
|
|
1311
|
-
// Override with runtime styles
|
|
1312
|
-
<Button
|
|
1313
|
-
title="Custom"
|
|
1314
|
-
style={{ backgroundColor: '#10B981' }}
|
|
1315
|
-
containerStyle={{ padding: 16 }}
|
|
1316
|
-
onPress={handlePress}
|
|
1317
|
-
/>
|
|
1318
|
-
|
|
1319
|
-
// className props are accepted but transformed by Babel upstream
|
|
1320
|
-
<Button className="bg-red-500 p-8" title="Red Button" />
|
|
1321
|
-
// At compile-time, this becomes:
|
|
1322
|
-
// <Button style={_twStyles._bg_red_500_p_8} title="Red Button" />
|
|
1323
|
-
// At runtime, className is undefined (already transformed to style)
|
|
1324
|
-
```
|
|
1325
|
-
|
|
1326
|
-
This pattern allows you to build component libraries with optimized default styling while still supporting full customization.
|
|
1327
|
-
|
|
1328
|
-
## API Reference
|
|
1329
|
-
|
|
1330
|
-
### Spacing
|
|
1331
|
-
|
|
1332
|
-
**Margin & Padding:**
|
|
1333
|
-
|
|
1334
|
-
- `m-{size}`, `p-{size}` — All sides
|
|
1335
|
-
- `mx-{size}`, `my-{size}`, `px-{size}`, `py-{size}` — Horizontal/vertical
|
|
1336
|
-
- `mt-{size}`, `mr-{size}`, `mb-{size}`, `ml-{size}` — Directional margin
|
|
1337
|
-
- `pt-{size}`, `pr-{size}`, `pb-{size}`, `pl-{size}` — Directional padding
|
|
1338
|
-
- `gap-{size}` — Gap between flex items
|
|
1339
|
-
|
|
1340
|
-
**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`
|
|
1341
|
-
|
|
1342
|
-
**Arbitrary values:** `m-[16px]`, `p-[20]`, `mx-[24px]`, `gap-[12px]` — Custom spacing values (px only)
|
|
1343
|
-
|
|
1344
|
-
### Layout
|
|
1345
|
-
|
|
1346
|
-
**Flexbox:**
|
|
1347
|
-
|
|
1348
|
-
- `flex`, `flex-1`, `flex-auto`, `flex-none` — Flex sizing
|
|
1349
|
-
- `flex-row`, `flex-row-reverse`, `flex-col`, `flex-col-reverse` — Direction
|
|
1350
|
-
- `flex-wrap`, `flex-wrap-reverse`, `flex-nowrap` — Wrapping
|
|
1351
|
-
- `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch` — Align items
|
|
1352
|
-
- `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`, `justify-evenly` — Justify content
|
|
1353
|
-
- `self-auto`, `self-start`, `self-end`, `self-center`, `self-stretch`, `self-baseline` — Align self
|
|
1354
|
-
- `grow`, `grow-0`, `shrink`, `shrink-0` — Flex grow/shrink
|
|
1355
|
-
|
|
1356
|
-
**Other:**
|
|
1357
|
-
|
|
1358
|
-
- `absolute`, `relative` — Position
|
|
1359
|
-
- `overflow-hidden`, `overflow-visible`, `overflow-scroll` — Overflow
|
|
1360
|
-
- `flex`, `hidden` — Display
|
|
1361
|
-
|
|
1362
|
-
### Colors
|
|
1363
|
-
|
|
1364
|
-
- `bg-{color}-{shade}` — Background color
|
|
1365
|
-
- `text-{color}-{shade}` — Text color
|
|
1366
|
-
- `border-{color}-{shade}` — Border color
|
|
1367
|
-
|
|
1368
|
-
**Available colors:** `gray`, `red`, `blue`, `green`, `yellow`, `purple`, `pink`, `orange`, `indigo`, `white`, `black`, `transparent`
|
|
1369
|
-
|
|
1370
|
-
**Available shades:** `50`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`
|
|
1371
|
-
|
|
1372
|
-
> **Note:** You can extend the color palette with custom colors via `tailwind.config.*` — see [Custom Theme Extensions](#custom-theme-extensions)
|
|
1373
|
-
|
|
1374
|
-
**Opacity Modifiers:**
|
|
1375
|
-
|
|
1376
|
-
Apply transparency to any color using the `/` operator with a percentage value (0-100):
|
|
1377
|
-
|
|
1378
|
-
```tsx
|
|
1379
|
-
<View className="bg-black/50 p-4">
|
|
1380
|
-
{" "}
|
|
1381
|
-
{/* 50% opacity black background */}
|
|
1382
|
-
<Text className="text-gray-900/80">
|
|
1383
|
-
{" "}
|
|
1384
|
-
{/* 80% opacity gray text */}
|
|
1385
|
-
Semi-transparent content
|
|
1386
|
-
</Text>
|
|
1387
|
-
<View className="border-2 border-red-500/30" /> {/* 30% opacity red border */}
|
|
1388
|
-
</View>
|
|
1389
|
-
```
|
|
1390
|
-
|
|
1391
|
-
- Works with all color types: `bg-*`, `text-*`, `border-*`
|
|
1392
|
-
- Supports preset colors: `bg-blue-500/75`, `text-red-600/50`
|
|
1393
|
-
- Supports arbitrary colors: `bg-[#ff0000]/40`, `text-[#3B82F6]/90`
|
|
1394
|
-
- Supports custom colors: `bg-primary/60`, `text-brand/80`
|
|
1395
|
-
- Uses React Native's 8-digit hex format: `#RRGGBBAA`
|
|
1396
|
-
|
|
1397
|
-
**Examples:**
|
|
1398
|
-
|
|
1399
|
-
```tsx
|
|
1400
|
-
// Background opacity
|
|
1401
|
-
<View className="bg-white/90" /> // #FFFFFFE6
|
|
1402
|
-
<View className="bg-blue-500/50" /> // #3B82F680
|
|
1403
|
-
|
|
1404
|
-
// Text opacity
|
|
1405
|
-
<Text className="text-black/70" /> // #000000B3
|
|
1406
|
-
<Text className="text-gray-900/60" /> // #11182799
|
|
1407
|
-
|
|
1408
|
-
// Border opacity
|
|
1409
|
-
<View className="border-2 border-purple-500/40" /> // #A855F766
|
|
1410
|
-
|
|
1411
|
-
// Arbitrary colors with opacity
|
|
1412
|
-
<View className="bg-[#ff6b6b]/25" /> // #FF6B6B40
|
|
1413
|
-
|
|
1414
|
-
// Edge cases
|
|
1415
|
-
<View className="bg-black/0" /> // Fully transparent
|
|
1416
|
-
<View className="bg-blue-500/100" /> // Fully opaque
|
|
1417
|
-
<View className="bg-transparent/50" /> // Remains transparent
|
|
1418
|
-
```
|
|
1419
|
-
|
|
1420
|
-
### Typography
|
|
1421
|
-
|
|
1422
|
-
**Font Size:**
|
|
1423
|
-
|
|
1424
|
-
`text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `text-3xl`, `text-4xl`, `text-5xl`, `text-6xl`, `text-7xl`, `text-8xl`, `text-9xl`
|
|
1425
|
-
|
|
1426
|
-
**Font Weight:**
|
|
1427
|
-
|
|
1428
|
-
`font-thin`, `font-extralight`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black`
|
|
1429
|
-
|
|
1430
|
-
**Other:**
|
|
1431
|
-
|
|
1432
|
-
- `italic`, `not-italic` — Font style
|
|
1433
|
-
- `text-left`, `text-center`, `text-right`, `text-justify` — Text alignment
|
|
1434
|
-
- `underline`, `line-through`, `no-underline` — Text decoration
|
|
1435
|
-
- `uppercase`, `lowercase`, `capitalize`, `normal-case` — Text transform
|
|
1436
|
-
- `leading-{3-10}` — Line height numeric scale (12px to 40px)
|
|
1437
|
-
- `leading-none`, `leading-tight`, `leading-snug`, `leading-normal`, `leading-relaxed`, `leading-loose` — Line height named values
|
|
1438
|
-
|
|
1439
|
-
### Borders
|
|
1440
|
-
|
|
1441
|
-
**Width:**
|
|
1442
|
-
|
|
1443
|
-
- `border`, `border-0`, `border-2`, `border-4`, `border-8` — All sides
|
|
1444
|
-
- `border-t`, `border-r`, `border-b`, `border-l` (with variants `-0`, `-2`, `-4`, `-8`) — Directional
|
|
1445
|
-
- `border-[8px]`, `border-t-[12px]` — Arbitrary values
|
|
1446
|
-
|
|
1447
|
-
**Radius:**
|
|
1448
|
-
|
|
1449
|
-
- `rounded-none`, `rounded-sm`, `rounded`, `rounded-md`, `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`, `rounded-full` — All corners
|
|
1450
|
-
- `rounded-t`, `rounded-r`, `rounded-b`, `rounded-l` (with size variants) — Directional
|
|
1451
|
-
- `rounded-tl`, `rounded-tr`, `rounded-bl`, `rounded-br` (with size variants) — Individual corners
|
|
1452
|
-
- `rounded-[12px]`, `rounded-t-[8px]`, `rounded-tl-[16px]` — Arbitrary values
|
|
1453
|
-
|
|
1454
|
-
**Style:**
|
|
1455
|
-
|
|
1456
|
-
`border-solid`, `border-dotted`, `border-dashed`
|
|
1457
|
-
|
|
1458
|
-
### Shadows & Elevation
|
|
1459
|
-
|
|
1460
|
-
Apply platform-specific shadows and elevation to create depth and visual hierarchy. Automatically uses iOS shadow properties or Android elevation based on the platform:
|
|
1461
|
-
|
|
1462
|
-
**Available shadow sizes:**
|
|
1463
|
-
|
|
1464
|
-
- `shadow-sm` — Subtle shadow
|
|
1465
|
-
- `shadow` — Default shadow
|
|
1466
|
-
- `shadow-md` — Medium shadow
|
|
1467
|
-
- `shadow-lg` — Large shadow
|
|
1468
|
-
- `shadow-xl` — Extra large shadow
|
|
1469
|
-
- `shadow-2xl` — Extra extra large shadow
|
|
1470
|
-
- `shadow-none` — Remove shadow
|
|
1471
|
-
|
|
1472
|
-
**Platform Differences:**
|
|
1473
|
-
|
|
1474
|
-
| Platform | Properties Used | Example Output |
|
|
1475
|
-
| ----------- | -------------------------------------------------------------- | ------------------------------------------ |
|
|
1476
|
-
| **iOS** | `shadowColor`, `shadowOffset`, `shadowOpacity`, `shadowRadius` | Native iOS shadow rendering |
|
|
1477
|
-
| **Android** | `elevation` | Native Android elevation (Material Design) |
|
|
1478
|
-
|
|
1479
|
-
**Examples:**
|
|
1480
|
-
|
|
1481
|
-
```tsx
|
|
1482
|
-
// Card with shadow
|
|
1483
|
-
<View className="bg-white rounded-lg shadow-lg p-6 m-4">
|
|
1484
|
-
<Text className="text-xl font-bold">Card Title</Text>
|
|
1485
|
-
<Text className="text-gray-600">Card with large shadow</Text>
|
|
1486
|
-
</View>
|
|
1487
|
-
|
|
1488
|
-
// Button with subtle shadow
|
|
1489
|
-
<Pressable className="bg-blue-500 shadow-sm rounded-lg px-6 py-3">
|
|
1490
|
-
<Text className="text-white">Press Me</Text>
|
|
1491
|
-
</Pressable>
|
|
1492
|
-
|
|
1493
|
-
// Different shadow sizes
|
|
1494
|
-
<View className="shadow-sm p-4">Subtle</View>
|
|
1495
|
-
<View className="shadow p-4">Default</View>
|
|
1496
|
-
<View className="shadow-md p-4">Medium</View>
|
|
1497
|
-
<View className="shadow-lg p-4">Large</View>
|
|
1498
|
-
<View className="shadow-xl p-4">Extra Large</View>
|
|
1499
|
-
<View className="shadow-2xl p-4">2X Large</View>
|
|
1500
|
-
|
|
1501
|
-
// Remove shadow
|
|
1502
|
-
<View className="shadow-lg md:shadow-none p-4">
|
|
1503
|
-
Conditional shadow removal
|
|
1504
|
-
</View>
|
|
1505
|
-
```
|
|
1506
|
-
|
|
1507
|
-
**iOS Shadow Values:**
|
|
1508
|
-
|
|
1509
|
-
| Class | shadowOpacity | shadowRadius | shadowOffset |
|
|
1510
|
-
| ------------ | ------------- | ------------ | ------------------------ |
|
|
1511
|
-
| `shadow-sm` | 0.05 | 1 | { width: 0, height: 1 } |
|
|
1512
|
-
| `shadow` | 0.1 | 2 | { width: 0, height: 1 } |
|
|
1513
|
-
| `shadow-md` | 0.15 | 4 | { width: 0, height: 3 } |
|
|
1514
|
-
| `shadow-lg` | 0.2 | 8 | { width: 0, height: 6 } |
|
|
1515
|
-
| `shadow-xl` | 0.25 | 12 | { width: 0, height: 10 } |
|
|
1516
|
-
| `shadow-2xl` | 0.3 | 24 | { width: 0, height: 20 } |
|
|
1517
|
-
|
|
1518
|
-
**Android Elevation Values:**
|
|
1519
|
-
|
|
1520
|
-
| Class | elevation |
|
|
1521
|
-
| ------------ | --------- |
|
|
1522
|
-
| `shadow-sm` | 1 |
|
|
1523
|
-
| `shadow` | 2 |
|
|
1524
|
-
| `shadow-md` | 4 |
|
|
1525
|
-
| `shadow-lg` | 8 |
|
|
1526
|
-
| `shadow-xl` | 12 |
|
|
1527
|
-
| `shadow-2xl` | 16 |
|
|
1528
|
-
|
|
1529
|
-
> **Note:** All shadow parsing happens at compile-time with zero runtime overhead. The platform detection uses React Native's `Platform.select()` API.
|
|
1530
|
-
|
|
1531
|
-
### Aspect Ratio
|
|
1532
|
-
|
|
1533
|
-
Control the aspect ratio of views using preset or arbitrary values. Requires React Native 0.71+:
|
|
1534
|
-
|
|
1535
|
-
**Preset values:**
|
|
1536
|
-
|
|
1537
|
-
- `aspect-auto` — Remove aspect ratio constraint
|
|
1538
|
-
- `aspect-square` — 1:1 aspect ratio
|
|
1539
|
-
- `aspect-video` — 16:9 aspect ratio
|
|
1540
|
-
|
|
1541
|
-
**Arbitrary values:**
|
|
1542
|
-
|
|
1543
|
-
Use `aspect-[width/height]` for custom ratios:
|
|
1544
|
-
|
|
1545
|
-
```tsx
|
|
1546
|
-
<View className="aspect-[4/3]" /> // 4:3 ratio (1.333...)
|
|
1547
|
-
<View className="aspect-[16/9]" /> // 16:9 ratio (1.778...)
|
|
1548
|
-
<View className="aspect-[21/9]" /> // 21:9 ultrawide
|
|
1549
|
-
<View className="aspect-[9/16]" /> // 9:16 portrait
|
|
1550
|
-
<View className="aspect-[3/2]" /> // 3:2 ratio (1.5)
|
|
1551
|
-
```
|
|
1552
|
-
|
|
1553
|
-
**Examples:**
|
|
1554
|
-
|
|
1555
|
-
```tsx
|
|
1556
|
-
// Square image container
|
|
1557
|
-
<View className="w-full aspect-square bg-gray-200">
|
|
1558
|
-
<Image source={avatar} className="w-full h-full" />
|
|
1559
|
-
</View>
|
|
1560
|
-
|
|
1561
|
-
// Video player container (16:9)
|
|
1562
|
-
<View className="w-full aspect-video bg-black">
|
|
1563
|
-
<VideoPlayer />
|
|
1564
|
-
</View>
|
|
1565
|
-
|
|
1566
|
-
// Instagram-style square grid
|
|
1567
|
-
<View className="flex-row flex-wrap gap-2">
|
|
1568
|
-
{photos.map((photo) => (
|
|
1569
|
-
<View key={photo.id} className="w-[32%] aspect-square">
|
|
1570
|
-
<Image source={photo.uri} className="w-full h-full rounded" />
|
|
1571
|
-
</View>
|
|
1572
|
-
))}
|
|
1573
|
-
</View>
|
|
1574
|
-
|
|
1575
|
-
// Custom aspect ratio for wide images
|
|
1576
|
-
<View className="w-full aspect-[21/9] rounded-lg overflow-hidden">
|
|
1577
|
-
<Image source={banner} className="w-full h-full" resizeMode="cover" />
|
|
1578
|
-
</View>
|
|
1579
|
-
|
|
1580
|
-
// Portrait orientation
|
|
1581
|
-
<View className="h-full aspect-[9/16]">
|
|
1582
|
-
<Story />
|
|
1583
|
-
</View>
|
|
1584
|
-
|
|
1585
|
-
// Remove aspect ratio constraint
|
|
1586
|
-
<View className="aspect-square md:aspect-auto">
|
|
1587
|
-
Responsive aspect ratio
|
|
1588
|
-
</View>
|
|
1589
|
-
```
|
|
1590
|
-
|
|
1591
|
-
**Common Aspect Ratios:**
|
|
1592
|
-
|
|
1593
|
-
| Ratio | Class | Decimal | Use Case |
|
|
1594
|
-
| ----- | --------------- | ------- | ---------------------------- |
|
|
1595
|
-
| 1:1 | `aspect-square` | 1.0 | Profile pictures, thumbnails |
|
|
1596
|
-
| 16:9 | `aspect-video` | 1.778 | Videos, landscape photos |
|
|
1597
|
-
| 4:3 | `aspect-[4/3]` | 1.333 | Standard photos |
|
|
1598
|
-
| 3:2 | `aspect-[3/2]` | 1.5 | Classic photography |
|
|
1599
|
-
| 21:9 | `aspect-[21/9]` | 2.333 | Ultrawide/cinematic |
|
|
1600
|
-
| 9:16 | `aspect-[9/16]` | 0.5625 | Stories, vertical video |
|
|
1601
|
-
|
|
1602
|
-
> **Note:** The aspect ratio is calculated as `width / height`. When combined with `w-full`, the height will be automatically calculated to maintain the ratio.
|
|
1603
|
-
|
|
1604
|
-
### Transforms
|
|
1605
|
-
|
|
1606
|
-
Apply 2D and 3D transformations to views with React Native's transform API. All transforms compile to optimized transform arrays at build time:
|
|
1607
|
-
|
|
1608
|
-
**Scale:**
|
|
1609
|
-
|
|
1610
|
-
- `scale-{value}` — Scale uniformly (both X and Y)
|
|
1611
|
-
- `scale-x-{value}`, `scale-y-{value}` — Scale on specific axis
|
|
1612
|
-
- **Values:** `0`, `50`, `75`, `90`, `95`, `100`, `105`, `110`, `125`, `150`, `200`
|
|
1613
|
-
- **Arbitrary:** `scale-[1.23]`, `scale-x-[0.5]`, `scale-y-[2.5]`
|
|
1614
|
-
|
|
1615
|
-
**Rotate:**
|
|
1616
|
-
|
|
1617
|
-
- `rotate-{degrees}`, `-rotate-{degrees}` — Rotate in 2D
|
|
1618
|
-
- `rotate-x-{degrees}`, `rotate-y-{degrees}`, `rotate-z-{degrees}` — Rotate on specific axis
|
|
1619
|
-
- **Values:** `0`, `1`, `2`, `3`, `6`, `12`, `45`, `90`, `180`
|
|
1620
|
-
- **Arbitrary:** `rotate-[37deg]`, `-rotate-[15deg]`, `rotate-x-[30deg]`
|
|
1621
|
-
|
|
1622
|
-
**Translate:**
|
|
1623
|
-
|
|
1624
|
-
- `translate-x-{spacing}`, `translate-y-{spacing}` — Move on specific axis
|
|
1625
|
-
- `-translate-x-{spacing}`, `-translate-y-{spacing}` — Negative translation
|
|
1626
|
-
- **Values:** Uses spacing scale (same as `m-*`, `p-*`)
|
|
1627
|
-
- **Arbitrary:** `translate-x-[50px]`, `translate-y-[100px]`, `translate-x-[50%]`
|
|
1628
|
-
|
|
1629
|
-
**Skew:**
|
|
1630
|
-
|
|
1631
|
-
- `skew-x-{degrees}`, `skew-y-{degrees}` — Skew on specific axis
|
|
1632
|
-
- `-skew-x-{degrees}`, `-skew-y-{degrees}` — Negative skew
|
|
1633
|
-
- **Values:** `0`, `1`, `2`, `3`, `6`, `12`
|
|
1634
|
-
- **Arbitrary:** `skew-x-[15deg]`, `-skew-y-[8deg]`
|
|
1635
|
-
|
|
1636
|
-
**Perspective:**
|
|
1637
|
-
|
|
1638
|
-
- `perspective-{value}` — Apply perspective transformation
|
|
1639
|
-
- **Values:** `0`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`, `1000`
|
|
1640
|
-
- **Arbitrary:** `perspective-[1500]`, `perspective-[2000]`
|
|
1641
|
-
|
|
1642
|
-
**Examples:**
|
|
1643
|
-
|
|
1644
|
-
```tsx
|
|
1645
|
-
// Scale
|
|
1646
|
-
<View className="scale-110 p-4">
|
|
1647
|
-
{/* 110% scale (1.1x larger) */}
|
|
1648
|
-
<Text>Scaled content</Text>
|
|
1649
|
-
</View>
|
|
1650
|
-
|
|
1651
|
-
// Rotate
|
|
1652
|
-
<View className="rotate-45 w-16 h-16 bg-blue-500" />
|
|
1653
|
-
|
|
1654
|
-
// Translate
|
|
1655
|
-
<View className="translate-x-4 translate-y-2 bg-red-500 p-4">
|
|
1656
|
-
{/* Moved 16px right, 8px down */}
|
|
1657
|
-
</View>
|
|
1658
|
-
|
|
1659
|
-
// Arbitrary values
|
|
1660
|
-
<View className="scale-[1.23] w-16 h-16 bg-green-500" />
|
|
1661
|
-
<View className="rotate-[37deg] w-16 h-16 bg-purple-500" />
|
|
1662
|
-
<View className="translate-x-[50px] bg-orange-500 p-4" />
|
|
1663
|
-
|
|
1664
|
-
// Negative values
|
|
1665
|
-
<View className="-rotate-45 w-16 h-16 bg-pink-500" />
|
|
1666
|
-
<View className="-translate-x-4 -translate-y-2 bg-indigo-500 p-4" />
|
|
1667
|
-
|
|
1668
|
-
// 3D rotation
|
|
1669
|
-
<View className="rotate-x-45 w-16 h-16 bg-yellow-500" />
|
|
1670
|
-
<View className="rotate-y-30 w-16 h-16 bg-teal-500" />
|
|
1671
|
-
|
|
1672
|
-
// Skew
|
|
1673
|
-
<View className="skew-x-6 w-16 h-16 bg-cyan-500" />
|
|
1674
|
-
|
|
1675
|
-
// Perspective
|
|
1676
|
-
<View className="perspective-500">
|
|
1677
|
-
<View className="rotate-x-45 w-16 h-16 bg-blue-500" />
|
|
1678
|
-
</View>
|
|
1679
|
-
```
|
|
1680
|
-
|
|
1681
|
-
**Multiple Transforms Limitation:**
|
|
1682
|
-
|
|
1683
|
-
Due to the current architecture, multiple transform classes on the same element will overwrite each other. For example:
|
|
1684
|
-
|
|
1685
|
-
```tsx
|
|
1686
|
-
// ❌ Only rotate-45 will apply (overwrites scale-110)
|
|
1687
|
-
<View className="scale-110 rotate-45 w-16 h-16 bg-blue-500" />
|
|
1688
|
-
|
|
1689
|
-
// ✅ Workaround: Use nested Views for multiple transforms
|
|
1690
|
-
<View className="scale-110">
|
|
1691
|
-
<View className="rotate-45">
|
|
1692
|
-
<View className="w-16 h-16 bg-blue-500" />
|
|
1693
|
-
</View>
|
|
1694
|
-
</View>
|
|
1695
|
-
```
|
|
1696
|
-
|
|
1697
|
-
This limitation exists because the current parser architecture uses `Object.assign()` which overwrites the `transform` property when multiple transform classes are present. This will be addressed in a future update by modifying the Babel plugin to detect multiple transform classes and generate style arrays.
|
|
1698
|
-
|
|
1699
|
-
**What's Not Supported:**
|
|
1700
|
-
|
|
1701
|
-
- `transform-origin` — Not available in React Native (transforms always use center as origin)
|
|
1702
|
-
- Multiple transforms on one element — Use nested Views (see workaround above)
|
|
1703
|
-
|
|
1704
|
-
> **Note:** All transform parsing happens at compile-time with zero runtime overhead. Each transform compiles to a React Native transform array: `transform: [{ scale: 1.1 }]`, `transform: [{ rotate: '45deg' }]`, etc.
|
|
1705
|
-
|
|
1706
|
-
### Sizing
|
|
1707
|
-
|
|
1708
|
-
- `w-{size}`, `h-{size}` — Width/height
|
|
1709
|
-
- `min-w-{size}`, `min-h-{size}` — Min width/height
|
|
1710
|
-
- `max-w-{size}`, `max-h-{size}` — Max width/height
|
|
1711
|
-
|
|
1712
|
-
**Available sizes:**
|
|
1713
|
-
|
|
1714
|
-
- **Numeric:** `0`-`96` (same as spacing scale)
|
|
1715
|
-
- **Fractional:** `1/2`, `1/3`, `2/3`, `1/4`, `3/4`, `1/5`, `2/5`, `3/5`, `4/5`, `1/6`, `2/6`, `3/6`, `4/6`, `5/6`
|
|
1716
|
-
- **Special:** `full` (100%), `auto`
|
|
1717
|
-
- **Arbitrary:** `w-[123px]`, `h-[50%]`, `min-w-[200px]`, `max-h-[80%]`
|
|
1718
|
-
|
|
1719
|
-
> **Note:** Arbitrary sizing supports pixel values (`[123px]` or `[123]`) and percentages (`[50%]`). Other units (`rem`, `em`, `vh`, `vw`) are not supported in React Native.
|
|
1720
|
-
|
|
1721
|
-
## Advanced
|
|
1722
|
-
|
|
1723
|
-
### Custom Attributes
|
|
1724
|
-
|
|
1725
|
-
By default, the Babel plugin transforms these className-like attributes to their corresponding style props:
|
|
1726
|
-
|
|
1727
|
-
- `className` → `style`
|
|
1728
|
-
- `contentContainerClassName` → `contentContainerStyle` (ScrollView, FlatList)
|
|
1729
|
-
- `columnWrapperClassName` → `columnWrapperStyle` (FlatList)
|
|
1730
|
-
- `ListHeaderComponentClassName` → `ListHeaderComponentStyle` (FlatList)
|
|
1731
|
-
- `ListFooterComponentClassName` → `ListFooterComponentStyle` (FlatList)
|
|
1732
|
-
|
|
1733
|
-
You can customize which attributes are transformed using the `attributes` plugin option:
|
|
1734
|
-
|
|
1735
|
-
**Exact Matches:**
|
|
1736
|
-
|
|
1737
|
-
```javascript
|
|
1738
|
-
// babel.config.js
|
|
1739
|
-
module.exports = {
|
|
1740
|
-
plugins: [
|
|
1741
|
-
[
|
|
1742
|
-
"@mgcrea/react-native-tailwind/babel",
|
|
1743
|
-
{
|
|
1744
|
-
attributes: ["className", "buttonClassName", "containerClassName"],
|
|
1745
|
-
},
|
|
1746
|
-
],
|
|
1747
|
-
],
|
|
1748
|
-
};
|
|
1749
|
-
```
|
|
1750
|
-
|
|
1751
|
-
**Pattern Matching:**
|
|
1752
|
-
|
|
1753
|
-
Use glob patterns to match multiple attributes:
|
|
1754
|
-
|
|
1755
|
-
```javascript
|
|
1756
|
-
// babel.config.js
|
|
1757
|
-
module.exports = {
|
|
1758
|
-
plugins: [
|
|
1759
|
-
[
|
|
1760
|
-
"@mgcrea/react-native-tailwind/babel",
|
|
1761
|
-
{
|
|
1762
|
-
// Matches any attribute ending in 'ClassName'
|
|
1763
|
-
attributes: ["*ClassName"],
|
|
1764
|
-
},
|
|
1765
|
-
],
|
|
1766
|
-
],
|
|
1767
|
-
};
|
|
1768
|
-
```
|
|
1769
|
-
|
|
1770
|
-
**Combined:**
|
|
1771
|
-
|
|
1772
|
-
```javascript
|
|
1773
|
-
// babel.config.js
|
|
1774
|
-
module.exports = {
|
|
1775
|
-
plugins: [
|
|
1776
|
-
[
|
|
1777
|
-
"@mgcrea/react-native-tailwind/babel",
|
|
1778
|
-
{
|
|
1779
|
-
// Mix exact matches and patterns
|
|
1780
|
-
attributes: [
|
|
1781
|
-
"className",
|
|
1782
|
-
"*ClassName", // containerClassName, buttonClassName, etc.
|
|
1783
|
-
"custom*", // customButton, customHeader, etc.
|
|
1784
|
-
],
|
|
1785
|
-
},
|
|
1786
|
-
],
|
|
1787
|
-
],
|
|
1788
|
-
};
|
|
1789
|
-
```
|
|
1790
|
-
|
|
1791
|
-
**Usage Example:**
|
|
1792
|
-
|
|
1793
|
-
```tsx
|
|
1794
|
-
// With custom attributes configured
|
|
1795
|
-
function Button({ title, onPress, buttonClassName, containerClassName }) {
|
|
1796
|
-
return (
|
|
1797
|
-
<View containerClassName="p-2 bg-gray-100">
|
|
1798
|
-
<Pressable buttonClassName="bg-blue-500 px-6 py-4 rounded-lg" onPress={onPress}>
|
|
1799
|
-
<Text className="text-white font-semibold">{title}</Text>
|
|
1800
|
-
</Pressable>
|
|
1801
|
-
</View>
|
|
1802
|
-
);
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1805
|
-
// Transforms to:
|
|
1806
|
-
function Button({ title, onPress, buttonStyle, containerStyle }) {
|
|
1807
|
-
return (
|
|
1808
|
-
<View style={[_twStyles._bg_gray_100_p_2, containerStyle]}>
|
|
1809
|
-
<Pressable style={[_twStyles._bg_blue_500_px_6_py_4_rounded_lg, buttonStyle]} onPress={onPress}>
|
|
1810
|
-
<Text style={_twStyles._font_semibold_text_white}>{title}</Text>
|
|
1811
|
-
</Pressable>
|
|
1812
|
-
</View>
|
|
1813
|
-
);
|
|
1814
|
-
}
|
|
1815
|
-
```
|
|
1816
|
-
|
|
1817
|
-
**Naming Convention:**
|
|
1818
|
-
|
|
1819
|
-
Attributes ending in `ClassName` are automatically converted to their `Style` equivalent:
|
|
1820
|
-
|
|
1821
|
-
- `buttonClassName` → `buttonStyle`
|
|
1822
|
-
- `containerClassName` → `containerStyle`
|
|
1823
|
-
- `headerClassName` → `headerStyle`
|
|
1824
|
-
|
|
1825
|
-
For attributes not ending in `ClassName`, the `style` prop is used.
|
|
1826
|
-
|
|
1827
|
-
**TypeScript Support:**
|
|
1828
|
-
|
|
1829
|
-
When using custom attributes, you'll need to augment the component types to include your custom className props. See the [TypeScript section](#2-enable-typescript-support-typescript) for details on module augmentation.
|
|
1830
|
-
|
|
1831
|
-
### Custom Styles Identifier
|
|
1832
|
-
|
|
1833
|
-
By default, the Babel plugin generates a StyleSheet constant named `_twStyles`. You can customize this identifier to avoid conflicts or match your project's naming conventions:
|
|
1834
|
-
|
|
1835
|
-
```javascript
|
|
1836
|
-
// babel.config.js
|
|
1837
|
-
module.exports = {
|
|
1838
|
-
plugins: [
|
|
1839
|
-
[
|
|
1840
|
-
"@mgcrea/react-native-tailwind/babel",
|
|
1841
|
-
{
|
|
1842
|
-
stylesIdentifier: "styles", // or 'tw', 'tailwind', etc.
|
|
1843
|
-
},
|
|
1844
|
-
],
|
|
1845
|
-
],
|
|
1846
|
-
};
|
|
1847
|
-
```
|
|
1848
|
-
|
|
1849
|
-
**Default behavior:**
|
|
1850
|
-
|
|
1851
|
-
```tsx
|
|
1852
|
-
// Input
|
|
1853
|
-
<View className="p-4 bg-blue-500" />
|
|
1854
|
-
|
|
1855
|
-
// Output
|
|
1856
|
-
<View style={_twStyles._bg_blue_500_p_4} />
|
|
1857
|
-
|
|
1858
|
-
const _twStyles = StyleSheet.create({
|
|
1859
|
-
_bg_blue_500_p_4: { padding: 16, backgroundColor: '#3B82F6' }
|
|
1860
|
-
});
|
|
1861
|
-
```
|
|
1862
|
-
|
|
1863
|
-
**With custom identifier:**
|
|
1864
|
-
|
|
1865
|
-
```tsx
|
|
1866
|
-
// Input (with stylesIdentifier: "styles")
|
|
1867
|
-
<View className="p-4 bg-blue-500" />
|
|
1868
|
-
|
|
1869
|
-
// Output
|
|
1870
|
-
<View style={styles._bg_blue_500_p_4} />
|
|
1871
|
-
|
|
1872
|
-
const styles = StyleSheet.create({
|
|
1873
|
-
_bg_blue_500_p_4: { padding: 16, backgroundColor: '#3B82F6' }
|
|
1874
|
-
});
|
|
1875
|
-
```
|
|
1876
|
-
|
|
1877
|
-
**Use Cases:**
|
|
1878
|
-
|
|
1879
|
-
- **Avoid conflicts:** If you already have a `_twStyles` variable in your code
|
|
1880
|
-
- **Consistency:** Match your existing StyleSheet naming convention (`styles`, `styleSheet`, etc.)
|
|
1881
|
-
- **Shorter names:** Use a shorter identifier like `tw` or `s` for more compact code
|
|
1882
|
-
- **Team conventions:** Align with your team's coding standards
|
|
1883
|
-
|
|
1884
|
-
**Important Notes:**
|
|
1885
|
-
|
|
1886
|
-
- The identifier must be a valid JavaScript variable name
|
|
1887
|
-
- Choose a name that won't conflict with existing variables in your files
|
|
1888
|
-
- The same identifier is used across all files in your project
|
|
1889
|
-
|
|
1890
|
-
### Custom Color Scheme Hook
|
|
1891
|
-
|
|
1892
|
-
By default, the plugin uses React Native's built-in `useColorScheme()` hook for `dark:` and `light:` modifiers. You can configure it to use a custom color scheme hook from theme providers like React Navigation, Expo, or your own implementation.
|
|
1893
|
-
|
|
1894
|
-
**Configuration:**
|
|
1895
|
-
|
|
1896
|
-
```javascript
|
|
1897
|
-
// babel.config.js
|
|
1898
|
-
module.exports = {
|
|
1899
|
-
plugins: [
|
|
1900
|
-
[
|
|
1901
|
-
"@mgcrea/react-native-tailwind/babel",
|
|
1902
|
-
{
|
|
1903
|
-
colorScheme: {
|
|
1904
|
-
importFrom: "@/hooks/useColorScheme", // Module to import from
|
|
1905
|
-
importName: "useColorScheme", // Hook name to import
|
|
1906
|
-
},
|
|
1907
|
-
},
|
|
1908
|
-
],
|
|
1909
|
-
],
|
|
1910
|
-
};
|
|
1911
|
-
```
|
|
1912
|
-
|
|
1913
|
-
**Use Cases:**
|
|
1914
|
-
|
|
1915
|
-
#### 1. Custom Theme Provider
|
|
1916
|
-
|
|
1917
|
-
Override system color scheme with user preferences from a store:
|
|
1918
|
-
|
|
1919
|
-
```typescript
|
|
1920
|
-
// src/hooks/useColorScheme.ts
|
|
1921
|
-
import { useColorScheme as useSystemColorScheme } from "react-native";
|
|
1922
|
-
import { profileStore } from "@/stores/profileStore";
|
|
1923
|
-
import { type ColorSchemeName } from "react-native";
|
|
1924
|
-
|
|
1925
|
-
export const useColorScheme = (): ColorSchemeName => {
|
|
1926
|
-
const systemColorScheme = useSystemColorScheme();
|
|
1927
|
-
const userTheme = profileStore.theme; // 'dark' | 'light' | 'auto'
|
|
1928
|
-
|
|
1929
|
-
// Return user preference, or fall back to system if set to 'auto'
|
|
1930
|
-
return userTheme === 'auto' ? systemColorScheme : userTheme;
|
|
1931
|
-
};
|
|
1932
|
-
```
|
|
1933
|
-
|
|
1934
|
-
```javascript
|
|
1935
|
-
// babel.config.js
|
|
1936
|
-
{
|
|
1937
|
-
colorScheme: {
|
|
1938
|
-
importFrom: "@/hooks/useColorScheme",
|
|
1939
|
-
importName: "useColorScheme"
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
```
|
|
1943
|
-
|
|
1944
|
-
#### 2. React Navigation Theme
|
|
1945
|
-
|
|
1946
|
-
Integrate with React Navigation's theme system:
|
|
1947
|
-
|
|
1948
|
-
```typescript
|
|
1949
|
-
// Wrap React Navigation's useTheme to return ColorSchemeName
|
|
1950
|
-
import { useTheme as useNavTheme } from "@react-navigation/native";
|
|
1951
|
-
import { type ColorSchemeName } from "react-native";
|
|
1952
|
-
|
|
1953
|
-
export const useColorScheme = (): ColorSchemeName => {
|
|
1954
|
-
const { dark } = useNavTheme();
|
|
1955
|
-
return dark ? "dark" : "light";
|
|
1956
|
-
};
|
|
1957
|
-
```
|
|
1958
|
-
|
|
1959
|
-
#### 3. Expo Router Theme
|
|
1960
|
-
|
|
1961
|
-
Use Expo Router's theme hook:
|
|
1962
|
-
|
|
1963
|
-
```javascript
|
|
1964
|
-
// babel.config.js
|
|
1965
|
-
{
|
|
1966
|
-
colorScheme: {
|
|
1967
|
-
importFrom: "expo-router",
|
|
1968
|
-
importName: "useColorScheme"
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
|
-
```
|
|
1972
|
-
|
|
1973
|
-
#### 4. Testing
|
|
1974
|
-
|
|
1975
|
-
Mock color scheme for tests:
|
|
1976
|
-
|
|
1977
|
-
```typescript
|
|
1978
|
-
// test/mocks/useColorScheme.ts
|
|
1979
|
-
export const useColorScheme = () => "light"; // Or "dark" for dark mode tests
|
|
1980
|
-
```
|
|
1981
|
-
|
|
1982
|
-
```javascript
|
|
1983
|
-
// babel.config.js (test environment)
|
|
1984
|
-
{
|
|
1985
|
-
colorScheme: {
|
|
1986
|
-
importFrom: "@/test/mocks/useColorScheme",
|
|
1987
|
-
importName: "useColorScheme"
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
```
|
|
1991
|
-
|
|
1992
|
-
#### How it works
|
|
1993
|
-
|
|
1994
|
-
When you use `dark:` or `light:` modifiers:
|
|
1995
|
-
|
|
1996
|
-
```tsx
|
|
1997
|
-
<View className="bg-white dark:bg-gray-900" />
|
|
1998
|
-
```
|
|
1999
|
-
|
|
2000
|
-
The plugin will:
|
|
2001
|
-
|
|
2002
|
-
1. Import your custom hook: `import { useColorScheme } from "@/hooks/useColorScheme"`
|
|
2003
|
-
2. Inject it in components: `const _twColorScheme = useColorScheme();`
|
|
2004
|
-
3. Generate conditionals: `_twColorScheme === "dark" && styles._dark_bg_gray_900`
|
|
2005
|
-
|
|
2006
|
-
#### Default behavior (no configuration)
|
|
2007
|
-
|
|
2008
|
-
Without custom configuration, the plugin uses React Native's built-in hook:
|
|
2009
|
-
|
|
2010
|
-
- Import: `import { useColorScheme } from "react-native"`
|
|
2011
|
-
- This works out of the box for basic system color scheme detection
|
|
2012
|
-
|
|
2013
|
-
#### Requirements
|
|
2014
|
-
|
|
2015
|
-
- Your custom hook must return `ColorSchemeName` (type from React Native: `"light" | "dark" | null | undefined`)
|
|
2016
|
-
- The hook must be compatible with React's rules of hooks (can only be called in function components)
|
|
2017
|
-
- Import merging works automatically if you already import from the same source
|
|
2018
|
-
|
|
2019
|
-
### Arbitrary Values
|
|
2020
|
-
|
|
2021
|
-
Use arbitrary values for custom sizes, spacing, and borders not in the preset scales:
|
|
2022
|
-
|
|
2023
|
-
```tsx
|
|
2024
|
-
<View className="w-[350px] h-[85%] m-[16px] p-[24px] border-[3px] rounded-[20px]" />
|
|
2025
|
-
```
|
|
2026
|
-
|
|
2027
|
-
**Supported:**
|
|
2028
|
-
|
|
2029
|
-
- **Spacing:** `m-[...]`, `mx-[...]`, `my-[...]`, `mt-[...]`, `p-[...]`, `px-[...]`, `gap-[...]`, etc. (px only)
|
|
2030
|
-
- **Sizing:** `w-[...]`, `h-[...]`, `min-w-[...]`, `min-h-[...]`, `max-w-[...]`, `max-h-[...]` (px and %)
|
|
2031
|
-
- **Border width:** `border-[...]`, `border-t-[...]`, `border-r-[...]`, `border-b-[...]`, `border-l-[...]` (px only)
|
|
2032
|
-
- **Border radius:** `rounded-[...]`, `rounded-t-[...]`, `rounded-tl-[...]`, etc. (px only)
|
|
2033
|
-
- **Transforms:**
|
|
2034
|
-
- **Scale:** `scale-[...]`, `scale-x-[...]`, `scale-y-[...]` (number only, e.g., `[1.23]`)
|
|
2035
|
-
- **Rotate:** `rotate-[...]`, `rotate-x-[...]`, `rotate-y-[...]`, `rotate-z-[...]` (deg only, e.g., `[37deg]`)
|
|
2036
|
-
- **Translate:** `translate-x-[...]`, `translate-y-[...]` (px or %, e.g., `[50px]` or `[50%]`)
|
|
2037
|
-
- **Skew:** `skew-x-[...]`, `skew-y-[...]` (deg only, e.g., `[15deg]`)
|
|
2038
|
-
- **Perspective:** `perspective-[...]` (number only, e.g., `[1500]`)
|
|
2039
|
-
|
|
2040
|
-
**Formats:**
|
|
2041
|
-
|
|
2042
|
-
- Pixels: `[123px]` or `[123]` — Supported by all utilities
|
|
2043
|
-
- Percentages: `[50%]`, `[33.333%]` — Only supported by sizing utilities (`w-*`, `h-*`, etc.)
|
|
2044
|
-
|
|
2045
|
-
> **Note:** CSS units (`rem`, `em`, `vh`, `vw`) are not supported by React Native.
|
|
2046
|
-
|
|
2047
|
-
### Custom Theme Extensions
|
|
2048
|
-
|
|
2049
|
-
Extend the default color palette and font families via `tailwind.config.*` in your project root:
|
|
2050
|
-
|
|
2051
|
-
```javascript
|
|
2052
|
-
// tailwind.config.mjs
|
|
2053
|
-
export default {
|
|
2054
|
-
theme: {
|
|
2055
|
-
extend: {
|
|
2056
|
-
colors: {
|
|
2057
|
-
primary: "#1d4ed8",
|
|
2058
|
-
secondary: "#9333ea",
|
|
2059
|
-
brand: {
|
|
2060
|
-
light: "#f0f9ff",
|
|
2061
|
-
DEFAULT: "#0284c7",
|
|
2062
|
-
dark: "#0c4a6e",
|
|
2063
|
-
},
|
|
2064
|
-
},
|
|
2065
|
-
fontFamily: {
|
|
2066
|
-
sans: ['"SF Pro Rounded"'],
|
|
2067
|
-
custom: ['"My Custom Font"'],
|
|
2068
|
-
},
|
|
2069
|
-
},
|
|
2070
|
-
},
|
|
2071
|
-
};
|
|
2072
|
-
```
|
|
2073
|
-
|
|
2074
|
-
Then use your custom theme:
|
|
2075
|
-
|
|
2076
|
-
```tsx
|
|
2077
|
-
<View className="bg-primary p-4">
|
|
2078
|
-
<Text className="text-brand font-custom">Custom branded text</Text>
|
|
2079
|
-
<Text className="font-sans">SF Pro Rounded text</Text>
|
|
2080
|
-
<View className="bg-brand-light rounded-lg" />
|
|
2081
|
-
</View>
|
|
2082
|
-
```
|
|
2083
|
-
|
|
2084
|
-
**How it works:**
|
|
2085
|
-
|
|
2086
|
-
- Babel plugin discovers config by traversing up from source files
|
|
2087
|
-
- Custom theme merged with defaults at build time (custom takes precedence)
|
|
2088
|
-
- Nested color objects flattened with dash notation: `brand.light` → `brand-light`
|
|
2089
|
-
- Font families use first font in array (React Native doesn't support font stacks)
|
|
2090
|
-
- Zero runtime overhead — all loading happens during compilation
|
|
2091
|
-
|
|
2092
|
-
**Supported formats:** `.js`, `.mjs`, `.cjs`, `.ts`
|
|
2093
|
-
|
|
2094
|
-
> **Tip:** Use `theme.extend.*` to keep defaults. Using `theme.colors` or `theme.fontFamily` directly will override all defaults.
|
|
2095
|
-
|
|
2096
|
-
### Programmatic API
|
|
2097
|
-
|
|
2098
|
-
Access the parser and constants programmatically:
|
|
2099
|
-
|
|
2100
|
-
```typescript
|
|
2101
|
-
import { parseClassName, COLORS, SPACING_SCALE } from "@mgcrea/react-native-tailwind";
|
|
2102
|
-
|
|
2103
|
-
// Parse className strings (no custom theme)
|
|
2104
|
-
const styles = parseClassName("m-4 p-2 bg-blue-500");
|
|
2105
|
-
// Returns: { margin: 16, padding: 8, backgroundColor: '#3B82F6' }
|
|
2106
|
-
|
|
2107
|
-
// Parse with custom theme
|
|
2108
|
-
const customStyles = parseClassName("m-4 bg-primary font-custom", {
|
|
2109
|
-
colors: { primary: "#1d4ed8" },
|
|
2110
|
-
fontFamily: { custom: "My Custom Font" },
|
|
2111
|
-
});
|
|
2112
|
-
// Returns: { margin: 16, backgroundColor: '#1d4ed8', fontFamily: 'My Custom Font' }
|
|
2113
|
-
|
|
2114
|
-
// Access default scales
|
|
2115
|
-
const blueColor = COLORS["blue-500"]; // '#3B82F6'
|
|
2116
|
-
const spacing = SPACING_SCALE[4]; // 16
|
|
2117
|
-
```
|
|
2118
|
-
|
|
2119
|
-
## Troubleshooting
|
|
2120
|
-
|
|
2121
|
-
### TypeScript `className` Errors
|
|
2122
|
-
|
|
2123
|
-
If TypeScript doesn't recognize the `className` prop:
|
|
2124
|
-
|
|
2125
|
-
1. Create the type declaration file:
|
|
2126
|
-
|
|
2127
|
-
```typescript
|
|
2128
|
-
// src/types/react-native-tailwind.d.ts
|
|
2129
|
-
import "@mgcrea/react-native-tailwind/react-native";
|
|
2130
|
-
```
|
|
2131
|
-
|
|
2132
|
-
2. Verify it's covered by your `tsconfig.json` `include` pattern
|
|
2133
|
-
3. Restart TypeScript server (VS Code: Cmd+Shift+P → "TypeScript: Restart TS Server")
|
|
2134
|
-
|
|
2135
|
-
### Babel Plugin Not Working
|
|
2136
|
-
|
|
2137
|
-
**Clear Metro cache:**
|
|
2138
|
-
|
|
2139
|
-
```bash
|
|
2140
|
-
npx react-native start --reset-cache
|
|
2141
|
-
```
|
|
2142
|
-
|
|
2143
|
-
**Verify `babel.config.js`:**
|
|
2144
|
-
|
|
2145
|
-
```javascript
|
|
2146
|
-
plugins: ["@mgcrea/react-native-tailwind/babel"];
|
|
2147
|
-
```
|
|
2148
|
-
|
|
2149
|
-
### Custom Colors Not Recognized
|
|
2150
|
-
|
|
2151
|
-
1. **Config location** — Must be in project root or parent directory
|
|
2152
|
-
2. **Config format** — Verify proper export:
|
|
2153
|
-
|
|
2154
|
-
```javascript
|
|
2155
|
-
// CommonJS
|
|
2156
|
-
module.exports = { theme: { extend: { colors: { ... } } } };
|
|
2157
|
-
|
|
2158
|
-
// ESM
|
|
2159
|
-
export default { theme: { extend: { colors: { ... } } } };
|
|
2160
|
-
```
|
|
2161
|
-
|
|
2162
|
-
3. **Clear cache** — Config changes require Metro cache reset
|
|
2163
|
-
4. **Use `theme.extend.colors`** — Don't use `theme.colors` directly (overrides defaults)
|
|
2164
|
-
|
|
2165
|
-
## Development
|
|
2166
|
-
|
|
2167
|
-
### Project Setup
|
|
2168
|
-
|
|
2169
|
-
```bash
|
|
2170
|
-
git clone https://github.com/mgcrea/react-native-tailwind.git
|
|
2171
|
-
cd react-native-tailwind
|
|
2172
|
-
pnpm install
|
|
2173
|
-
```
|
|
2174
|
-
|
|
2175
|
-
### Build
|
|
2176
|
-
|
|
2177
|
-
```bash
|
|
2178
|
-
pnpm build # Full build
|
|
2179
|
-
pnpm build:babel # Compile TypeScript
|
|
2180
|
-
pnpm build:babel-plugin # Bundle Babel plugin
|
|
2181
|
-
pnpm build:types # Generate type declarations
|
|
2182
|
-
```
|
|
2183
|
-
|
|
2184
|
-
### Testing
|
|
2185
|
-
|
|
2186
|
-
```bash
|
|
2187
|
-
pnpm test # Run all tests
|
|
2188
|
-
pnpm lint # ESLint
|
|
2189
|
-
pnpm check # TypeScript type check
|
|
2190
|
-
pnpm spec # Jest tests
|
|
2191
|
-
```
|
|
2192
|
-
|
|
2193
|
-
### Example App
|
|
2194
|
-
|
|
2195
|
-
```bash
|
|
2196
|
-
pnpm dev # Run example app
|
|
2197
|
-
cd example && npm run dev -- --reset-cache
|
|
2198
|
-
```
|
|
213
|
+
Contributions are welcome! Please read our [Contributing Guide](https://mgcrea.github.io/react-native-tailwind/advanced/contributing/) for details.
|
|
2199
214
|
|
|
2200
215
|
## Authors
|
|
2201
216
|
|