@mgcrea/react-native-tailwind 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +527 -136
  2. package/dist/babel/index.cjs +767 -274
  3. package/dist/babel/index.d.ts +2 -1
  4. package/dist/babel/index.ts +319 -16
  5. package/dist/components/Pressable.d.ts +32 -0
  6. package/dist/components/Pressable.js +1 -0
  7. package/dist/components/TextInput.d.ts +56 -0
  8. package/dist/components/TextInput.js +1 -0
  9. package/dist/index.d.ts +9 -2
  10. package/dist/index.js +1 -1
  11. package/dist/parser/aspectRatio.d.ts +16 -0
  12. package/dist/parser/aspectRatio.js +1 -0
  13. package/dist/parser/aspectRatio.test.d.ts +1 -0
  14. package/dist/parser/aspectRatio.test.js +1 -0
  15. package/dist/parser/borders.js +1 -1
  16. package/dist/parser/borders.test.d.ts +1 -0
  17. package/dist/parser/borders.test.js +1 -0
  18. package/dist/parser/colors.d.ts +1 -0
  19. package/dist/parser/colors.js +1 -1
  20. package/dist/parser/colors.test.d.ts +1 -0
  21. package/dist/parser/colors.test.js +1 -0
  22. package/dist/parser/index.d.ts +4 -0
  23. package/dist/parser/index.js +1 -1
  24. package/dist/parser/layout.d.ts +2 -0
  25. package/dist/parser/layout.js +1 -1
  26. package/dist/parser/layout.test.d.ts +1 -0
  27. package/dist/parser/layout.test.js +1 -0
  28. package/dist/parser/modifiers.d.ts +47 -0
  29. package/dist/parser/modifiers.js +1 -0
  30. package/dist/parser/modifiers.test.d.ts +1 -0
  31. package/dist/parser/modifiers.test.js +1 -0
  32. package/dist/parser/shadows.d.ts +22 -0
  33. package/dist/parser/shadows.js +1 -0
  34. package/dist/parser/shadows.test.d.ts +1 -0
  35. package/dist/parser/shadows.test.js +1 -0
  36. package/dist/parser/sizing.test.d.ts +1 -0
  37. package/dist/parser/sizing.test.js +1 -0
  38. package/dist/parser/spacing.d.ts +1 -1
  39. package/dist/parser/spacing.js +1 -1
  40. package/dist/parser/spacing.test.d.ts +1 -0
  41. package/dist/parser/spacing.test.js +1 -0
  42. package/dist/parser/typography.d.ts +2 -1
  43. package/dist/parser/typography.js +1 -1
  44. package/dist/parser/typography.test.d.ts +1 -0
  45. package/dist/parser/typography.test.js +1 -0
  46. package/dist/types.d.ts +4 -3
  47. package/package.json +7 -6
  48. package/src/babel/index.ts +319 -16
  49. package/src/components/Pressable.tsx +46 -0
  50. package/src/components/TextInput.tsx +90 -0
  51. package/src/index.ts +20 -2
  52. package/src/parser/aspectRatio.test.ts +191 -0
  53. package/src/parser/aspectRatio.ts +73 -0
  54. package/src/parser/borders.test.ts +329 -0
  55. package/src/parser/borders.ts +187 -108
  56. package/src/parser/colors.test.ts +335 -0
  57. package/src/parser/colors.ts +117 -6
  58. package/src/parser/index.ts +13 -2
  59. package/src/parser/layout.test.ts +459 -0
  60. package/src/parser/layout.ts +128 -0
  61. package/src/parser/modifiers.test.ts +375 -0
  62. package/src/parser/modifiers.ts +104 -0
  63. package/src/parser/shadows.test.ts +192 -0
  64. package/src/parser/shadows.ts +84 -0
  65. package/src/parser/sizing.test.ts +256 -0
  66. package/src/parser/spacing.test.ts +226 -0
  67. package/src/parser/spacing.ts +93 -138
  68. package/src/parser/typography.test.ts +221 -0
  69. package/src/parser/typography.ts +143 -112
  70. package/src/types.ts +1 -3
  71. package/dist/react-native.d.js +0 -1
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  [![npm version](https://img.shields.io/npm/v/@mgcrea/react-native-tailwind.svg?style=for-the-badge)](https://www.npmjs.com/package/@mgcrea/react-native-tailwind)
6
6
  [![npm downloads](https://img.shields.io/npm/dt/@mgcrea/react-native-tailwind.svg?style=for-the-badge)](https://www.npmjs.com/package/@mgcrea/react-native-tailwind)
7
7
  [![license](https://img.shields.io/github/license/mgcrea/react-native-tailwind.svg?style=for-the-badge)](https://github.com/mgcrea/react-native-tailwind/blob/main/LICENSE)
8
+ [![build status](https://img.shields.io/github/actions/workflow/status/mgcrea/react-native-tailwind/main.yaml?style=for-the-badge&branch=master)](https://github.com/mgcrea/react-native-tailwind/actions/workflows/main.yaml)
8
9
 
9
10
  </div>
10
11
 
@@ -15,14 +16,15 @@ Compile-time Tailwind CSS for React Native with zero runtime overhead. Transform
15
16
  ## Features
16
17
 
17
18
  - ⚡ **Zero runtime overhead** — All transformations happen at compile time
18
- - 🎯 **Babel-only setup** — No Metro configuration required (like Reanimated)
19
+ - 🔧 **No dependencies** — Direct-to-React-Native style generation without tailwindcss package
20
+ - 🎯 **Babel-only setup** — No Metro configuration required
19
21
  - 📝 **TypeScript-first** — Full type safety and autocomplete support
20
- - 🚀 **Optimized performance** — Uses `StyleSheet.create` for optimal React Native performance
22
+ - 🚀 **Optimized performance** — Compiles down to `StyleSheet.create` for optimal performance
21
23
  - 📦 **Small bundle size** — Only includes actual styles used in your app
22
- - 🔧 **No dependencies** — Direct-to-React-Native style generation without tailwindcss package
23
24
  - 🎨 **Custom colors** — Extend the default palette via `tailwind.config.*`
24
25
  - 📐 **Arbitrary values** — Use custom sizes and borders: `w-[123px]`, `rounded-[20px]`
25
26
  - 🔀 **Dynamic className** — Conditional styles with hybrid compile-time optimization
27
+ - 🎯 **State modifiers** — `active:`, `hover:`, `focus:`, and `disabled:` modifiers for interactive components
26
28
  - 📜 **Special style props** — Support for `contentContainerClassName`, `columnWrapperClassName`, and more
27
29
 
28
30
  ## Installation
@@ -120,102 +122,6 @@ const _twStyles = StyleSheet.create({
120
122
  });
121
123
  ```
122
124
 
123
- ## API Reference
124
-
125
- ### Spacing
126
-
127
- **Margin & Padding:**
128
-
129
- - `m-{size}`, `p-{size}` — All sides
130
- - `mx-{size}`, `my-{size}`, `px-{size}`, `py-{size}` — Horizontal/vertical
131
- - `mt-{size}`, `mr-{size}`, `mb-{size}`, `ml-{size}` — Directional margin
132
- - `pt-{size}`, `pr-{size}`, `pb-{size}`, `pl-{size}` — Directional padding
133
- - `gap-{size}` — Gap between flex items
134
-
135
- **Available sizes:** `0`, `0.5`, `1`, `1.5`, `2`, `2.5`, `3`, `3.5`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `14`, `16`, `20`, `24`, `28`, `32`, `36`, `40`, `44`, `48`, `52`, `56`, `60`, `64`, `72`, `80`, `96`
136
-
137
- ### Layout
138
-
139
- **Flexbox:**
140
-
141
- - `flex`, `flex-1`, `flex-auto`, `flex-none` — Flex sizing
142
- - `flex-row`, `flex-row-reverse`, `flex-col`, `flex-col-reverse` — Direction
143
- - `flex-wrap`, `flex-wrap-reverse`, `flex-nowrap` — Wrapping
144
- - `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch` — Align items
145
- - `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`, `justify-evenly` — Justify content
146
- - `self-auto`, `self-start`, `self-end`, `self-center`, `self-stretch`, `self-baseline` — Align self
147
- - `grow`, `grow-0`, `shrink`, `shrink-0` — Flex grow/shrink
148
-
149
- **Other:**
150
-
151
- - `absolute`, `relative` — Position
152
- - `overflow-hidden`, `overflow-visible`, `overflow-scroll` — Overflow
153
- - `flex`, `hidden` — Display
154
-
155
- ### Colors
156
-
157
- - `bg-{color}-{shade}` — Background color
158
- - `text-{color}-{shade}` — Text color
159
- - `border-{color}-{shade}` — Border color
160
-
161
- **Available colors:** `gray`, `red`, `blue`, `green`, `yellow`, `purple`, `pink`, `orange`, `indigo`, `white`, `black`, `transparent`
162
-
163
- **Available shades:** `50`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`
164
-
165
- > **Note:** You can extend the color palette with custom colors via `tailwind.config.*` — see [Custom Colors](#custom-colors)
166
-
167
- ### Typography
168
-
169
- **Font Size:**
170
-
171
- `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`
172
-
173
- **Font Weight:**
174
-
175
- `font-thin`, `font-extralight`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black`
176
-
177
- **Other:**
178
-
179
- - `italic`, `not-italic` — Font style
180
- - `text-left`, `text-center`, `text-right`, `text-justify` — Text alignment
181
- - `underline`, `line-through`, `no-underline` — Text decoration
182
- - `uppercase`, `lowercase`, `capitalize`, `normal-case` — Text transform
183
- - `leading-none`, `leading-tight`, `leading-snug`, `leading-normal`, `leading-relaxed`, `leading-loose` — Line height
184
-
185
- ### Borders
186
-
187
- **Width:**
188
-
189
- - `border`, `border-0`, `border-2`, `border-4`, `border-8` — All sides
190
- - `border-t`, `border-r`, `border-b`, `border-l` (with variants `-0`, `-2`, `-4`, `-8`) — Directional
191
- - `border-[8px]`, `border-t-[12px]` — Arbitrary values
192
-
193
- **Radius:**
194
-
195
- - `rounded-none`, `rounded-sm`, `rounded`, `rounded-md`, `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`, `rounded-full` — All corners
196
- - `rounded-t`, `rounded-r`, `rounded-b`, `rounded-l` (with size variants) — Directional
197
- - `rounded-tl`, `rounded-tr`, `rounded-bl`, `rounded-br` (with size variants) — Individual corners
198
- - `rounded-[12px]`, `rounded-t-[8px]`, `rounded-tl-[16px]` — Arbitrary values
199
-
200
- **Style:**
201
-
202
- `border-solid`, `border-dotted`, `border-dashed`
203
-
204
- ### Sizing
205
-
206
- - `w-{size}`, `h-{size}` — Width/height
207
- - `min-w-{size}`, `min-h-{size}` — Min width/height
208
- - `max-w-{size}`, `max-h-{size}` — Max width/height
209
-
210
- **Available sizes:**
211
-
212
- - **Numeric:** `0`-`96` (same as spacing scale)
213
- - **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`
214
- - **Special:** `full` (100%), `auto`
215
- - **Arbitrary:** `w-[123px]`, `h-[50%]`, `min-w-[200px]`, `max-h-[80%]`
216
-
217
- > **Note:** Arbitrary sizing supports pixel values (`[123px]` or `[123]`) and percentages (`[50%]`). Other units (`rem`, `em`, `vh`, `vw`) are not supported in React Native.
218
-
219
125
  ## Usage Examples
220
126
 
221
127
  ### Basic Example
@@ -235,14 +141,18 @@ export function MyComponent() {
235
141
  ### Card Component
236
142
 
237
143
  ```tsx
238
- import { View, Text, Pressable } from "react-native";
144
+ import { View, Text } from "react-native";
145
+ import { Pressable } from "@mgcrea/react-native-tailwind";
239
146
 
240
147
  export function Card({ title, description, onPress }) {
241
148
  return (
242
149
  <View className="bg-white rounded-lg p-6 mb-4 border border-gray-200">
243
150
  <Text className="text-xl font-semibold text-gray-900 mb-2">{title}</Text>
244
151
  <Text className="text-base text-gray-600 mb-4">{description}</Text>
245
- <Pressable className="bg-blue-500 px-4 py-2 rounded-lg items-center" onPress={onPress}>
152
+ <Pressable
153
+ className="bg-blue-500 active:bg-blue-700 px-4 py-2 rounded-lg items-center"
154
+ onPress={onPress}
155
+ >
246
156
  <Text className="text-white font-semibold">Learn More</Text>
247
157
  </Pressable>
248
158
  </View>
@@ -368,6 +278,177 @@ The Babel plugin will merge them:
368
278
  </View>
369
279
  ```
370
280
 
281
+ ### State Modifiers
282
+
283
+ Apply styles based on component state with zero runtime overhead. The Babel plugin automatically generates optimized style functions.
284
+
285
+ #### Active Modifier (Pressable)
286
+
287
+ Use the `active:` modifier to apply styles when a `Pressable` component is pressed. **Requires using the enhanced `Pressable` component from this package.**
288
+
289
+ **Basic Example:**
290
+
291
+ ```tsx
292
+ import { Text } from "react-native";
293
+ import { Pressable } from "@mgcrea/react-native-tailwind";
294
+
295
+ export function MyButton() {
296
+ return (
297
+ <Pressable className="bg-blue-500 active:bg-blue-700 p-4 rounded-lg">
298
+ <Text className="text-white font-semibold">Press Me</Text>
299
+ </Pressable>
300
+ );
301
+ }
302
+ ```
303
+
304
+ **Transforms to:**
305
+
306
+ ```tsx
307
+ <Pressable
308
+ style={({ pressed }) => [_twStyles._bg_blue_500_p_4_rounded_lg, pressed && _twStyles._active_bg_blue_700]}
309
+ >
310
+ <Text style={_twStyles._font_semibold_text_white}>Press Me</Text>
311
+ </Pressable>;
312
+
313
+ // Generated styles:
314
+ const _twStyles = StyleSheet.create({
315
+ _bg_blue_500_p_4_rounded_lg: { backgroundColor: "#3B82F6", padding: 16, borderRadius: 8 },
316
+ _active_bg_blue_700: { backgroundColor: "#1D4ED8" },
317
+ _font_semibold_text_white: { fontWeight: "600", color: "#FFFFFF" },
318
+ });
319
+ ```
320
+
321
+ **Multiple Active Modifiers:**
322
+
323
+ ```tsx
324
+ <Pressable className="bg-green-500 active:bg-green-700 p-4 active:p-6 rounded-lg">
325
+ <Text className="text-white">Press for darker & larger padding</Text>
326
+ </Pressable>
327
+ ```
328
+
329
+ **Complex Styling:**
330
+
331
+ ```tsx
332
+ <Pressable className="bg-purple-500 active:bg-purple-800 border-2 border-purple-700 active:border-purple-900 p-4 rounded-lg">
333
+ <Text className="text-white">Background + Border Changes</Text>
334
+ </Pressable>
335
+ ```
336
+
337
+ **Key Features:**
338
+
339
+ - ✅ **Zero runtime overhead** — All parsing happens at compile-time
340
+ - ✅ **Native Pressable API** — Uses Pressable's `style={({ pressed }) => ...}` pattern
341
+ - ✅ **Type-safe** — Full TypeScript autocomplete for `active:` classes
342
+ - ✅ **Optimized** — Styles deduplicated via `StyleSheet.create`
343
+ - ✅ **Works with custom colors** — `active:bg-primary`, `active:bg-secondary`, etc.
344
+
345
+ #### Focus Modifier (TextInput)
346
+
347
+ Use the `focus:` modifier to apply styles when a `TextInput` component is focused. **Requires using the enhanced `TextInput` component from this package.**
348
+
349
+ **Basic Example:**
350
+
351
+ ```tsx
352
+ import { TextInput } from "@mgcrea/react-native-tailwind";
353
+
354
+ export function MyInput() {
355
+ return (
356
+ <TextInput
357
+ className="border-2 border-gray-300 focus:border-blue-500 p-3 rounded-lg bg-white"
358
+ placeholder="Email address"
359
+ />
360
+ );
361
+ }
362
+ ```
363
+
364
+ **How it works:**
365
+
366
+ The package exports an enhanced `TextInput` component that:
367
+
368
+ 1. Manages focus state internally using `onFocus`/`onBlur` callbacks
369
+ 2. Passes focus state to the style function: `style={({ focused }) => ...}`
370
+ 3. Works seamlessly with the `focus:` modifier in className
371
+
372
+ **Multiple Focus Modifiers:**
373
+
374
+ ```tsx
375
+ <TextInput className="border-2 border-gray-300 focus:border-green-500 bg-gray-50 focus:bg-white p-3 rounded-lg" />
376
+ ```
377
+
378
+ **Supported Modifiers by Component:**
379
+
380
+ | Component | Supported Modifiers | Notes |
381
+ | ---------------------- | ------------------------------------------ | ------------------------------------------ |
382
+ | `Pressable` (enhanced) | `active:`, `hover:`, `focus:`, `disabled:` | Use `@mgcrea/react-native-tailwind` export |
383
+ | `TextInput` (enhanced) | `focus:`, `disabled:` | Use `@mgcrea/react-native-tailwind` export |
384
+
385
+ #### Disabled Modifier (Pressable & TextInput)
386
+
387
+ Use the `disabled:` modifier to apply styles when a component is disabled. **Requires using the enhanced components from this package.**
388
+
389
+ **Pressable Example:**
390
+
391
+ ```tsx
392
+ import { Pressable, Text } from "@mgcrea/react-native-tailwind";
393
+
394
+ export function SubmitButton({ isLoading }) {
395
+ return (
396
+ <Pressable
397
+ disabled={isLoading}
398
+ className="bg-blue-500 active:bg-blue-700 disabled:bg-gray-400 p-4 rounded-lg"
399
+ >
400
+ <Text className="text-white font-semibold">{isLoading ? "Loading..." : "Submit"}</Text>
401
+ </Pressable>
402
+ );
403
+ }
404
+ ```
405
+
406
+ **TextInput Example:**
407
+
408
+ ```tsx
409
+ import { TextInput } from "@mgcrea/react-native-tailwind";
410
+
411
+ export function MyInput({ isEditing }) {
412
+ return (
413
+ <TextInput
414
+ disabled={!isEditing}
415
+ className="border-2 border-gray-300 focus:border-blue-500 disabled:bg-gray-100 disabled:border-gray-200 p-3 rounded-lg"
416
+ placeholder="Enter text"
417
+ />
418
+ );
419
+ }
420
+ ```
421
+
422
+ **How it works:**
423
+
424
+ The enhanced components inject the `disabled` prop value into the style function context:
425
+
426
+ - **Pressable**: Extends the existing `{ pressed, hovered, focused }` state with `disabled`
427
+ - **TextInput**: Provides `{ focused, disabled }` state to style functions
428
+
429
+ **TextInput `disabled` Prop:**
430
+
431
+ The enhanced `TextInput` also provides a convenient `disabled` prop that overrides React Native's `editable` prop:
432
+
433
+ ```tsx
434
+ // These are equivalent:
435
+ <TextInput disabled={true} />
436
+ <TextInput editable={false} />
437
+
438
+ // If both are provided, disabled takes precedence:
439
+ <TextInput disabled={true} editable={true} /> // Component is disabled
440
+ ```
441
+
442
+ **Important Notes:**
443
+
444
+ - ⚠️ **Enhanced components required** — State modifiers require using the enhanced components from this package
445
+ - ℹ️ **Component-specific** — Each modifier only works on compatible components
446
+ - ℹ️ **No nested modifiers** — Combinations like `active:focus:bg-blue-500` are not currently supported
447
+ - ✅ **Zero styling overhead** — All className parsing happens at compile-time
448
+ - ✅ **Minimal runtime cost** — Only adds state management (focus tracking, disabled injection)
449
+ - ✅ **Type-safe** — Full TypeScript autocomplete for all modifiers
450
+ - ✅ **Works with custom colors** — `focus:border-primary`, `active:bg-secondary`, `disabled:bg-gray-200`, etc.
451
+
371
452
  ### ScrollView Content Container
372
453
 
373
454
  Use `contentContainerClassName` to style the ScrollView's content container:
@@ -441,70 +522,380 @@ export function ListWithHeaderFooter({ items }) {
441
522
  }
442
523
  ```
443
524
 
444
- ## Architecture
525
+ ### Building Reusable Components
445
526
 
446
- ### Compile-Time Transformation
527
+ 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):
447
528
 
448
- The Babel plugin performs all transformations during build time, ensuring zero runtime overhead:
529
+ ```tsx
530
+ import { Pressable, Text, View, StyleProp, ViewStyle } from "react-native";
531
+
532
+ type ButtonProps = {
533
+ title: string;
534
+ onPress?: () => void;
535
+ // REQUIRED: Must accept style props for className to work
536
+ style?: StyleProp<ViewStyle>;
537
+ containerStyle?: StyleProp<ViewStyle>;
538
+ // Optional: Include in type for TypeScript compatibility
539
+ className?: string; // compile-time only
540
+ containerClassName?: string; // compile-time only
541
+ };
449
542
 
450
- 1. **AST Transformation** Visits JSX elements and finds `className` attributes
451
- 2. **Static Analysis** Only processes string literals (dynamic values produce warnings)
452
- 3. **Style Registry** — Collects all className → style mappings per file
453
- 4. **Code Generation** Injects `StyleSheet.create()` at end of file
454
- 5. **Import Management** Adds `StyleSheet` import if needed
543
+ export function Button({ title, onPress, style, containerStyle }: ButtonProps) {
544
+ // Use static className strings - these get optimized at compile-time
545
+ return (
546
+ <View className="p-2 bg-gray-100 rounded-lg" style={containerStyle}>
547
+ <Pressable className="bg-blue-500 px-6 py-4 rounded-lg items-center" onPress={onPress} style={style}>
548
+ <Text className="text-white text-center font-semibold text-base">{title}</Text>
549
+ </Pressable>
550
+ </View>
551
+ );
552
+ }
553
+ ```
455
554
 
456
- ### Performance Characteristics
555
+ **Key Points:**
457
556
 
458
- | Metric | Value |
459
- | ---------------- | ------------------ |
460
- | Runtime Overhead | 0ms (compile-time) |
461
- | Bundle Size | ~4KB typical |
462
- | Build Time | +50-200ms |
557
+ - Use **static className strings** internally for default styling
558
+ - **Must accept `style` props** - the Babel plugin transforms `className` → `style` before your component receives it
559
+ - Include `className` props in the type (for TypeScript compatibility)
560
+ - **Don't destructure** className props - they're already transformed to `style` and will be `undefined` at runtime
561
+ - Babel plugin optimizes all static strings to `StyleSheet.create` calls
463
562
 
464
- **Why it's fast:**
563
+ **Usage:**
465
564
 
466
- - ⚡ All className parsing happens at build time
467
- - 🎯 Uses React Native's optimized `StyleSheet.create` API
468
- - 📦 Tree shaking — only includes styles actually used
469
- - 🔄 Deduplication — identical styles reused across components
565
+ ```tsx
566
+ // Default styling (uses internal static classNames)
567
+ <Button title="Click Me" onPress={handlePress} />
568
+
569
+ // Override with runtime styles
570
+ <Button
571
+ title="Custom"
572
+ style={{ backgroundColor: '#10B981' }}
573
+ containerStyle={{ padding: 16 }}
574
+ onPress={handlePress}
575
+ />
576
+
577
+ // className props are accepted but transformed by Babel upstream
578
+ <Button className="bg-red-500 p-8" title="Red Button" />
579
+ // At compile-time, this becomes:
580
+ // <Button style={_twStyles._bg_red_500_p_8} title="Red Button" />
581
+ // At runtime, className is undefined (already transformed to style)
582
+ ```
470
583
 
471
- ## Limitations
584
+ This pattern allows you to build component libraries with optimized default styling while still supporting full customization.
472
585
 
473
- **Dynamic class names are not supported** — The Babel plugin can only transform static string literals:
586
+ ## API Reference
587
+
588
+ ### Spacing
589
+
590
+ **Margin & Padding:**
591
+
592
+ - `m-{size}`, `p-{size}` — All sides
593
+ - `mx-{size}`, `my-{size}`, `px-{size}`, `py-{size}` — Horizontal/vertical
594
+ - `mt-{size}`, `mr-{size}`, `mb-{size}`, `ml-{size}` — Directional margin
595
+ - `pt-{size}`, `pr-{size}`, `pb-{size}`, `pl-{size}` — Directional padding
596
+ - `gap-{size}` — Gap between flex items
597
+
598
+ **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`
599
+
600
+ **Arbitrary values:** `m-[16px]`, `p-[20]`, `mx-[24px]`, `gap-[12px]` — Custom spacing values (px only)
601
+
602
+ ### Layout
603
+
604
+ **Flexbox:**
605
+
606
+ - `flex`, `flex-1`, `flex-auto`, `flex-none` — Flex sizing
607
+ - `flex-row`, `flex-row-reverse`, `flex-col`, `flex-col-reverse` — Direction
608
+ - `flex-wrap`, `flex-wrap-reverse`, `flex-nowrap` — Wrapping
609
+ - `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch` — Align items
610
+ - `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`, `justify-evenly` — Justify content
611
+ - `self-auto`, `self-start`, `self-end`, `self-center`, `self-stretch`, `self-baseline` — Align self
612
+ - `grow`, `grow-0`, `shrink`, `shrink-0` — Flex grow/shrink
613
+
614
+ **Other:**
615
+
616
+ - `absolute`, `relative` — Position
617
+ - `overflow-hidden`, `overflow-visible`, `overflow-scroll` — Overflow
618
+ - `flex`, `hidden` — Display
619
+
620
+ ### Colors
621
+
622
+ - `bg-{color}-{shade}` — Background color
623
+ - `text-{color}-{shade}` — Text color
624
+ - `border-{color}-{shade}` — Border color
625
+
626
+ **Available colors:** `gray`, `red`, `blue`, `green`, `yellow`, `purple`, `pink`, `orange`, `indigo`, `white`, `black`, `transparent`
627
+
628
+ **Available shades:** `50`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`
629
+
630
+ > **Note:** You can extend the color palette with custom colors via `tailwind.config.*` — see [Custom Colors](#custom-colors)
631
+
632
+ **Opacity Modifiers:**
633
+
634
+ Apply transparency to any color using the `/` operator with a percentage value (0-100):
474
635
 
475
636
  ```tsx
476
- // This will NOT work
477
- const spacing = 4;
478
- <View className={`m-${spacing} p-2`} />
637
+ <View className="bg-black/50 p-4">
638
+ {" "}
639
+ {/* 50% opacity black background */}
640
+ <Text className="text-gray-900/80">
641
+ {" "}
642
+ {/* 80% opacity gray text */}
643
+ Semi-transparent content
644
+ </Text>
645
+ <View className="border-2 border-red-500/30" /> {/* 30% opacity red border */}
646
+ </View>
647
+ ```
648
+
649
+ - Works with all color types: `bg-*`, `text-*`, `border-*`
650
+ - Supports preset colors: `bg-blue-500/75`, `text-red-600/50`
651
+ - Supports arbitrary colors: `bg-[#ff0000]/40`, `text-[#3B82F6]/90`
652
+ - Supports custom colors: `bg-primary/60`, `text-brand/80`
653
+ - Uses React Native's 8-digit hex format: `#RRGGBBAA`
654
+
655
+ **Examples:**
656
+
657
+ ```tsx
658
+ // Background opacity
659
+ <View className="bg-white/90" /> // #FFFFFFE6
660
+ <View className="bg-blue-500/50" /> // #3B82F680
661
+
662
+ // Text opacity
663
+ <Text className="text-black/70" /> // #000000B3
664
+ <Text className="text-gray-900/60" /> // #11182799
665
+
666
+ // Border opacity
667
+ <View className="border-2 border-purple-500/40" /> // #A855F766
668
+
669
+ // Arbitrary colors with opacity
670
+ <View className="bg-[#ff6b6b]/25" /> // #FF6B6B40
479
671
 
480
- // Use inline styles for dynamic values
481
- <View className="p-2" style={{ margin: spacing * 4 }} />
672
+ // Edge cases
673
+ <View className="bg-black/0" /> // Fully transparent
674
+ <View className="bg-blue-500/100" /> // Fully opaque
675
+ <View className="bg-transparent/50" /> // Remains transparent
482
676
  ```
483
677
 
484
- For dynamic styling, use the `style` prop alongside `className`.
678
+ ### Typography
679
+
680
+ **Font Size:**
681
+
682
+ `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`
683
+
684
+ **Font Weight:**
685
+
686
+ `font-thin`, `font-extralight`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black`
687
+
688
+ **Other:**
689
+
690
+ - `italic`, `not-italic` — Font style
691
+ - `text-left`, `text-center`, `text-right`, `text-justify` — Text alignment
692
+ - `underline`, `line-through`, `no-underline` — Text decoration
693
+ - `uppercase`, `lowercase`, `capitalize`, `normal-case` — Text transform
694
+ - `leading-none`, `leading-tight`, `leading-snug`, `leading-normal`, `leading-relaxed`, `leading-loose` — Line height
695
+
696
+ ### Borders
697
+
698
+ **Width:**
699
+
700
+ - `border`, `border-0`, `border-2`, `border-4`, `border-8` — All sides
701
+ - `border-t`, `border-r`, `border-b`, `border-l` (with variants `-0`, `-2`, `-4`, `-8`) — Directional
702
+ - `border-[8px]`, `border-t-[12px]` — Arbitrary values
703
+
704
+ **Radius:**
705
+
706
+ - `rounded-none`, `rounded-sm`, `rounded`, `rounded-md`, `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`, `rounded-full` — All corners
707
+ - `rounded-t`, `rounded-r`, `rounded-b`, `rounded-l` (with size variants) — Directional
708
+ - `rounded-tl`, `rounded-tr`, `rounded-bl`, `rounded-br` (with size variants) — Individual corners
709
+ - `rounded-[12px]`, `rounded-t-[8px]`, `rounded-tl-[16px]` — Arbitrary values
710
+
711
+ **Style:**
712
+
713
+ `border-solid`, `border-dotted`, `border-dashed`
714
+
715
+ ### Shadows & Elevation
716
+
717
+ Apply platform-specific shadows and elevation to create depth and visual hierarchy. Automatically uses iOS shadow properties or Android elevation based on the platform:
718
+
719
+ **Available shadow sizes:**
720
+
721
+ - `shadow-sm` — Subtle shadow
722
+ - `shadow` — Default shadow
723
+ - `shadow-md` — Medium shadow
724
+ - `shadow-lg` — Large shadow
725
+ - `shadow-xl` — Extra large shadow
726
+ - `shadow-2xl` — Extra extra large shadow
727
+ - `shadow-none` — Remove shadow
728
+
729
+ **Platform Differences:**
730
+
731
+ | Platform | Properties Used | Example Output |
732
+ | ----------- | -------------------------------------------------------------- | ------------------------------------------ |
733
+ | **iOS** | `shadowColor`, `shadowOffset`, `shadowOpacity`, `shadowRadius` | Native iOS shadow rendering |
734
+ | **Android** | `elevation` | Native Android elevation (Material Design) |
735
+
736
+ **Examples:**
737
+
738
+ ```tsx
739
+ // Card with shadow
740
+ <View className="bg-white rounded-lg shadow-lg p-6 m-4">
741
+ <Text className="text-xl font-bold">Card Title</Text>
742
+ <Text className="text-gray-600">Card with large shadow</Text>
743
+ </View>
744
+
745
+ // Button with subtle shadow
746
+ <Pressable className="bg-blue-500 shadow-sm rounded-lg px-6 py-3">
747
+ <Text className="text-white">Press Me</Text>
748
+ </Pressable>
749
+
750
+ // Different shadow sizes
751
+ <View className="shadow-sm p-4">Subtle</View>
752
+ <View className="shadow p-4">Default</View>
753
+ <View className="shadow-md p-4">Medium</View>
754
+ <View className="shadow-lg p-4">Large</View>
755
+ <View className="shadow-xl p-4">Extra Large</View>
756
+ <View className="shadow-2xl p-4">2X Large</View>
757
+
758
+ // Remove shadow
759
+ <View className="shadow-lg md:shadow-none p-4">
760
+ Conditional shadow removal
761
+ </View>
762
+ ```
763
+
764
+ **iOS Shadow Values:**
765
+
766
+ | Class | shadowOpacity | shadowRadius | shadowOffset |
767
+ | ------------ | ------------- | ------------ | ------------------------ |
768
+ | `shadow-sm` | 0.05 | 1 | { width: 0, height: 1 } |
769
+ | `shadow` | 0.1 | 2 | { width: 0, height: 1 } |
770
+ | `shadow-md` | 0.15 | 4 | { width: 0, height: 3 } |
771
+ | `shadow-lg` | 0.2 | 8 | { width: 0, height: 6 } |
772
+ | `shadow-xl` | 0.25 | 12 | { width: 0, height: 10 } |
773
+ | `shadow-2xl` | 0.3 | 24 | { width: 0, height: 20 } |
774
+
775
+ **Android Elevation Values:**
776
+
777
+ | Class | elevation |
778
+ | ------------ | --------- |
779
+ | `shadow-sm` | 1 |
780
+ | `shadow` | 2 |
781
+ | `shadow-md` | 4 |
782
+ | `shadow-lg` | 8 |
783
+ | `shadow-xl` | 12 |
784
+ | `shadow-2xl` | 16 |
785
+
786
+ > **Note:** All shadow parsing happens at compile-time with zero runtime overhead. The platform detection uses React Native's `Platform.select()` API.
787
+
788
+ ### Aspect Ratio
789
+
790
+ Control the aspect ratio of views using preset or arbitrary values. Requires React Native 0.71+:
791
+
792
+ **Preset values:**
793
+
794
+ - `aspect-auto` — Remove aspect ratio constraint
795
+ - `aspect-square` — 1:1 aspect ratio
796
+ - `aspect-video` — 16:9 aspect ratio
797
+
798
+ **Arbitrary values:**
799
+
800
+ Use `aspect-[width/height]` for custom ratios:
801
+
802
+ ```tsx
803
+ <View className="aspect-[4/3]" /> // 4:3 ratio (1.333...)
804
+ <View className="aspect-[16/9]" /> // 16:9 ratio (1.778...)
805
+ <View className="aspect-[21/9]" /> // 21:9 ultrawide
806
+ <View className="aspect-[9/16]" /> // 9:16 portrait
807
+ <View className="aspect-[3/2]" /> // 3:2 ratio (1.5)
808
+ ```
809
+
810
+ **Examples:**
811
+
812
+ ```tsx
813
+ // Square image container
814
+ <View className="w-full aspect-square bg-gray-200">
815
+ <Image source={avatar} className="w-full h-full" />
816
+ </View>
817
+
818
+ // Video player container (16:9)
819
+ <View className="w-full aspect-video bg-black">
820
+ <VideoPlayer />
821
+ </View>
822
+
823
+ // Instagram-style square grid
824
+ <View className="flex-row flex-wrap gap-2">
825
+ {photos.map((photo) => (
826
+ <View key={photo.id} className="w-[32%] aspect-square">
827
+ <Image source={photo.uri} className="w-full h-full rounded" />
828
+ </View>
829
+ ))}
830
+ </View>
831
+
832
+ // Custom aspect ratio for wide images
833
+ <View className="w-full aspect-[21/9] rounded-lg overflow-hidden">
834
+ <Image source={banner} className="w-full h-full" resizeMode="cover" />
835
+ </View>
836
+
837
+ // Portrait orientation
838
+ <View className="h-full aspect-[9/16]">
839
+ <Story />
840
+ </View>
841
+
842
+ // Remove aspect ratio constraint
843
+ <View className="aspect-square md:aspect-auto">
844
+ Responsive aspect ratio
845
+ </View>
846
+ ```
847
+
848
+ **Common Aspect Ratios:**
849
+
850
+ | Ratio | Class | Decimal | Use Case |
851
+ | ----- | --------------- | ------- | ---------------------------- |
852
+ | 1:1 | `aspect-square` | 1.0 | Profile pictures, thumbnails |
853
+ | 16:9 | `aspect-video` | 1.778 | Videos, landscape photos |
854
+ | 4:3 | `aspect-[4/3]` | 1.333 | Standard photos |
855
+ | 3:2 | `aspect-[3/2]` | 1.5 | Classic photography |
856
+ | 21:9 | `aspect-[21/9]` | 2.333 | Ultrawide/cinematic |
857
+ | 9:16 | `aspect-[9/16]` | 0.5625 | Stories, vertical video |
858
+
859
+ > **Note:** The aspect ratio is calculated as `width / height`. When combined with `w-full`, the height will be automatically calculated to maintain the ratio.
860
+
861
+ ### Sizing
862
+
863
+ - `w-{size}`, `h-{size}` — Width/height
864
+ - `min-w-{size}`, `min-h-{size}` — Min width/height
865
+ - `max-w-{size}`, `max-h-{size}` — Max width/height
866
+
867
+ **Available sizes:**
868
+
869
+ - **Numeric:** `0`-`96` (same as spacing scale)
870
+ - **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`
871
+ - **Special:** `full` (100%), `auto`
872
+ - **Arbitrary:** `w-[123px]`, `h-[50%]`, `min-w-[200px]`, `max-h-[80%]`
873
+
874
+ > **Note:** Arbitrary sizing supports pixel values (`[123px]` or `[123]`) and percentages (`[50%]`). Other units (`rem`, `em`, `vh`, `vw`) are not supported in React Native.
485
875
 
486
876
  ## Advanced
487
877
 
488
878
  ### Arbitrary Values
489
879
 
490
- Use arbitrary values for custom sizes and borders not in the preset scales:
880
+ Use arbitrary values for custom sizes, spacing, and borders not in the preset scales:
491
881
 
492
882
  ```tsx
493
- <View className="w-[350px] h-[85%] border-[3px] rounded-[20px]" />
883
+ <View className="w-[350px] h-[85%] m-[16px] p-[24px] border-[3px] rounded-[20px]" />
494
884
  ```
495
885
 
496
886
  **Supported:**
497
887
 
498
- - **Sizing:** `w-[...]`, `h-[...]`, `min-w-[...]`, `min-h-[...]`, `max-w-[...]`, `max-h-[...]`
499
- - **Border width:** `border-[...]`, `border-t-[...]`, `border-r-[...]`, `border-b-[...]`, `border-l-[...]`
500
- - **Border radius:** `rounded-[...]`, `rounded-t-[...]`, `rounded-tl-[...]`, etc.
888
+ - **Spacing:** `m-[...]`, `mx-[...]`, `my-[...]`, `mt-[...]`, `p-[...]`, `px-[...]`, `gap-[...]`, etc. (px only)
889
+ - **Sizing:** `w-[...]`, `h-[...]`, `min-w-[...]`, `min-h-[...]`, `max-w-[...]`, `max-h-[...]` (px and %)
890
+ - **Border width:** `border-[...]`, `border-t-[...]`, `border-r-[...]`, `border-b-[...]`, `border-l-[...]` (px only)
891
+ - **Border radius:** `rounded-[...]`, `rounded-t-[...]`, `rounded-tl-[...]`, etc. (px only)
501
892
 
502
893
  **Formats:**
503
894
 
504
- - Pixels: `[123px]` or `[123]`
505
- - Percentages: `[50%]`, `[33.333%]`
895
+ - Pixels: `[123px]` or `[123]` — Supported by all utilities
896
+ - Percentages: `[50%]`, `[33.333%]` — Only supported by sizing utilities (`w-*`, `h-*`, etc.)
506
897
 
507
- > **Note:** Only `px` and `%` units are supported. CSS units (`rem`, `em`, `vh`, `vw`) are not supported by React Native.
898
+ > **Note:** CSS units (`rem`, `em`, `vh`, `vw`) are not supported by React Native.
508
899
 
509
900
  ### Custom Colors
510
901