@mgcrea/react-native-tailwind 0.5.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.
- package/README.md +281 -320
- package/dist/babel/index.cjs +36 -73
- package/dist/parser/shadows.d.ts +9 -13
- package/dist/parser/shadows.js +1 -1
- package/dist/parser/shadows.test.js +1 -1
- package/dist/types.d.ts +4 -6
- package/package.json +1 -1
- package/src/parser/shadows.test.ts +20 -29
- package/src/parser/shadows.ts +45 -94
- package/src/types.ts +1 -3
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@mgcrea/react-native-tailwind)
|
|
6
6
|
[](https://www.npmjs.com/package/@mgcrea/react-native-tailwind)
|
|
7
7
|
[](https://github.com/mgcrea/react-native-tailwind/blob/main/LICENSE)
|
|
8
|
+
[](https://github.com/mgcrea/react-native-tailwind/actions/workflows/main.yaml)
|
|
8
9
|
|
|
9
10
|
</div>
|
|
10
11
|
|
|
@@ -121,292 +122,6 @@ const _twStyles = StyleSheet.create({
|
|
|
121
122
|
});
|
|
122
123
|
```
|
|
123
124
|
|
|
124
|
-
## API Reference
|
|
125
|
-
|
|
126
|
-
### Spacing
|
|
127
|
-
|
|
128
|
-
**Margin & Padding:**
|
|
129
|
-
|
|
130
|
-
- `m-{size}`, `p-{size}` — All sides
|
|
131
|
-
- `mx-{size}`, `my-{size}`, `px-{size}`, `py-{size}` — Horizontal/vertical
|
|
132
|
-
- `mt-{size}`, `mr-{size}`, `mb-{size}`, `ml-{size}` — Directional margin
|
|
133
|
-
- `pt-{size}`, `pr-{size}`, `pb-{size}`, `pl-{size}` — Directional padding
|
|
134
|
-
- `gap-{size}` — Gap between flex items
|
|
135
|
-
|
|
136
|
-
**Available sizes:** `0`, `0.5`, `1`, `1.5`, `2`, `2.5`, `3`, `3.5`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `14`, `16`, `20`, `24`, `28`, `32`, `36`, `40`, `44`, `48`, `52`, `56`, `60`, `64`, `72`, `80`, `96`
|
|
137
|
-
|
|
138
|
-
**Arbitrary values:** `m-[16px]`, `p-[20]`, `mx-[24px]`, `gap-[12px]` — Custom spacing values (px only)
|
|
139
|
-
|
|
140
|
-
### Layout
|
|
141
|
-
|
|
142
|
-
**Flexbox:**
|
|
143
|
-
|
|
144
|
-
- `flex`, `flex-1`, `flex-auto`, `flex-none` — Flex sizing
|
|
145
|
-
- `flex-row`, `flex-row-reverse`, `flex-col`, `flex-col-reverse` — Direction
|
|
146
|
-
- `flex-wrap`, `flex-wrap-reverse`, `flex-nowrap` — Wrapping
|
|
147
|
-
- `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch` — Align items
|
|
148
|
-
- `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`, `justify-evenly` — Justify content
|
|
149
|
-
- `self-auto`, `self-start`, `self-end`, `self-center`, `self-stretch`, `self-baseline` — Align self
|
|
150
|
-
- `grow`, `grow-0`, `shrink`, `shrink-0` — Flex grow/shrink
|
|
151
|
-
|
|
152
|
-
**Other:**
|
|
153
|
-
|
|
154
|
-
- `absolute`, `relative` — Position
|
|
155
|
-
- `overflow-hidden`, `overflow-visible`, `overflow-scroll` — Overflow
|
|
156
|
-
- `flex`, `hidden` — Display
|
|
157
|
-
|
|
158
|
-
### Colors
|
|
159
|
-
|
|
160
|
-
- `bg-{color}-{shade}` — Background color
|
|
161
|
-
- `text-{color}-{shade}` — Text color
|
|
162
|
-
- `border-{color}-{shade}` — Border color
|
|
163
|
-
|
|
164
|
-
**Available colors:** `gray`, `red`, `blue`, `green`, `yellow`, `purple`, `pink`, `orange`, `indigo`, `white`, `black`, `transparent`
|
|
165
|
-
|
|
166
|
-
**Available shades:** `50`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`
|
|
167
|
-
|
|
168
|
-
> **Note:** You can extend the color palette with custom colors via `tailwind.config.*` — see [Custom Colors](#custom-colors)
|
|
169
|
-
|
|
170
|
-
**Opacity Modifiers:**
|
|
171
|
-
|
|
172
|
-
Apply transparency to any color using the `/` operator with a percentage value (0-100):
|
|
173
|
-
|
|
174
|
-
```tsx
|
|
175
|
-
<View className="bg-black/50 p-4"> {/* 50% opacity black background */}
|
|
176
|
-
<Text className="text-gray-900/80"> {/* 80% opacity gray text */}
|
|
177
|
-
Semi-transparent content
|
|
178
|
-
</Text>
|
|
179
|
-
<View className="border-2 border-red-500/30" /> {/* 30% opacity red border */}
|
|
180
|
-
</View>
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
- Works with all color types: `bg-*`, `text-*`, `border-*`
|
|
184
|
-
- Supports preset colors: `bg-blue-500/75`, `text-red-600/50`
|
|
185
|
-
- Supports arbitrary colors: `bg-[#ff0000]/40`, `text-[#3B82F6]/90`
|
|
186
|
-
- Supports custom colors: `bg-primary/60`, `text-brand/80`
|
|
187
|
-
- Uses React Native's 8-digit hex format: `#RRGGBBAA`
|
|
188
|
-
|
|
189
|
-
**Examples:**
|
|
190
|
-
|
|
191
|
-
```tsx
|
|
192
|
-
// Background opacity
|
|
193
|
-
<View className="bg-white/90" /> // #FFFFFFE6
|
|
194
|
-
<View className="bg-blue-500/50" /> // #3B82F680
|
|
195
|
-
|
|
196
|
-
// Text opacity
|
|
197
|
-
<Text className="text-black/70" /> // #000000B3
|
|
198
|
-
<Text className="text-gray-900/60" /> // #11182799
|
|
199
|
-
|
|
200
|
-
// Border opacity
|
|
201
|
-
<View className="border-2 border-purple-500/40" /> // #A855F766
|
|
202
|
-
|
|
203
|
-
// Arbitrary colors with opacity
|
|
204
|
-
<View className="bg-[#ff6b6b]/25" /> // #FF6B6B40
|
|
205
|
-
|
|
206
|
-
// Edge cases
|
|
207
|
-
<View className="bg-black/0" /> // Fully transparent
|
|
208
|
-
<View className="bg-blue-500/100" /> // Fully opaque
|
|
209
|
-
<View className="bg-transparent/50" /> // Remains transparent
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Typography
|
|
213
|
-
|
|
214
|
-
**Font Size:**
|
|
215
|
-
|
|
216
|
-
`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`
|
|
217
|
-
|
|
218
|
-
**Font Weight:**
|
|
219
|
-
|
|
220
|
-
`font-thin`, `font-extralight`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black`
|
|
221
|
-
|
|
222
|
-
**Other:**
|
|
223
|
-
|
|
224
|
-
- `italic`, `not-italic` — Font style
|
|
225
|
-
- `text-left`, `text-center`, `text-right`, `text-justify` — Text alignment
|
|
226
|
-
- `underline`, `line-through`, `no-underline` — Text decoration
|
|
227
|
-
- `uppercase`, `lowercase`, `capitalize`, `normal-case` — Text transform
|
|
228
|
-
- `leading-none`, `leading-tight`, `leading-snug`, `leading-normal`, `leading-relaxed`, `leading-loose` — Line height
|
|
229
|
-
|
|
230
|
-
### Borders
|
|
231
|
-
|
|
232
|
-
**Width:**
|
|
233
|
-
|
|
234
|
-
- `border`, `border-0`, `border-2`, `border-4`, `border-8` — All sides
|
|
235
|
-
- `border-t`, `border-r`, `border-b`, `border-l` (with variants `-0`, `-2`, `-4`, `-8`) — Directional
|
|
236
|
-
- `border-[8px]`, `border-t-[12px]` — Arbitrary values
|
|
237
|
-
|
|
238
|
-
**Radius:**
|
|
239
|
-
|
|
240
|
-
- `rounded-none`, `rounded-sm`, `rounded`, `rounded-md`, `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`, `rounded-full` — All corners
|
|
241
|
-
- `rounded-t`, `rounded-r`, `rounded-b`, `rounded-l` (with size variants) — Directional
|
|
242
|
-
- `rounded-tl`, `rounded-tr`, `rounded-bl`, `rounded-br` (with size variants) — Individual corners
|
|
243
|
-
- `rounded-[12px]`, `rounded-t-[8px]`, `rounded-tl-[16px]` — Arbitrary values
|
|
244
|
-
|
|
245
|
-
**Style:**
|
|
246
|
-
|
|
247
|
-
`border-solid`, `border-dotted`, `border-dashed`
|
|
248
|
-
|
|
249
|
-
### Shadows & Elevation
|
|
250
|
-
|
|
251
|
-
Apply platform-specific shadows and elevation to create depth and visual hierarchy. Automatically uses iOS shadow properties or Android elevation based on the platform:
|
|
252
|
-
|
|
253
|
-
**Available shadow sizes:**
|
|
254
|
-
|
|
255
|
-
- `shadow-sm` — Subtle shadow
|
|
256
|
-
- `shadow` — Default shadow
|
|
257
|
-
- `shadow-md` — Medium shadow
|
|
258
|
-
- `shadow-lg` — Large shadow
|
|
259
|
-
- `shadow-xl` — Extra large shadow
|
|
260
|
-
- `shadow-2xl` — Extra extra large shadow
|
|
261
|
-
- `shadow-none` — Remove shadow
|
|
262
|
-
|
|
263
|
-
**Platform Differences:**
|
|
264
|
-
|
|
265
|
-
| Platform | Properties Used | Example Output |
|
|
266
|
-
|----------|----------------|----------------|
|
|
267
|
-
| **iOS** | `shadowColor`, `shadowOffset`, `shadowOpacity`, `shadowRadius` | Native iOS shadow rendering |
|
|
268
|
-
| **Android** | `elevation` | Native Android elevation (Material Design) |
|
|
269
|
-
|
|
270
|
-
**Examples:**
|
|
271
|
-
|
|
272
|
-
```tsx
|
|
273
|
-
// Card with shadow
|
|
274
|
-
<View className="bg-white rounded-lg shadow-lg p-6 m-4">
|
|
275
|
-
<Text className="text-xl font-bold">Card Title</Text>
|
|
276
|
-
<Text className="text-gray-600">Card with large shadow</Text>
|
|
277
|
-
</View>
|
|
278
|
-
|
|
279
|
-
// Button with subtle shadow
|
|
280
|
-
<Pressable className="bg-blue-500 shadow-sm rounded-lg px-6 py-3">
|
|
281
|
-
<Text className="text-white">Press Me</Text>
|
|
282
|
-
</Pressable>
|
|
283
|
-
|
|
284
|
-
// Different shadow sizes
|
|
285
|
-
<View className="shadow-sm p-4">Subtle</View>
|
|
286
|
-
<View className="shadow p-4">Default</View>
|
|
287
|
-
<View className="shadow-md p-4">Medium</View>
|
|
288
|
-
<View className="shadow-lg p-4">Large</View>
|
|
289
|
-
<View className="shadow-xl p-4">Extra Large</View>
|
|
290
|
-
<View className="shadow-2xl p-4">2X Large</View>
|
|
291
|
-
|
|
292
|
-
// Remove shadow
|
|
293
|
-
<View className="shadow-lg md:shadow-none p-4">
|
|
294
|
-
Conditional shadow removal
|
|
295
|
-
</View>
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
**iOS Shadow Values:**
|
|
299
|
-
|
|
300
|
-
| Class | shadowOpacity | shadowRadius | shadowOffset |
|
|
301
|
-
|-------|---------------|--------------|--------------|
|
|
302
|
-
| `shadow-sm` | 0.05 | 1 | { width: 0, height: 1 } |
|
|
303
|
-
| `shadow` | 0.1 | 2 | { width: 0, height: 1 } |
|
|
304
|
-
| `shadow-md` | 0.15 | 4 | { width: 0, height: 3 } |
|
|
305
|
-
| `shadow-lg` | 0.2 | 8 | { width: 0, height: 6 } |
|
|
306
|
-
| `shadow-xl` | 0.25 | 12 | { width: 0, height: 10 } |
|
|
307
|
-
| `shadow-2xl` | 0.3 | 24 | { width: 0, height: 20 } |
|
|
308
|
-
|
|
309
|
-
**Android Elevation Values:**
|
|
310
|
-
|
|
311
|
-
| Class | elevation |
|
|
312
|
-
|-------|-----------|
|
|
313
|
-
| `shadow-sm` | 1 |
|
|
314
|
-
| `shadow` | 2 |
|
|
315
|
-
| `shadow-md` | 4 |
|
|
316
|
-
| `shadow-lg` | 8 |
|
|
317
|
-
| `shadow-xl` | 12 |
|
|
318
|
-
| `shadow-2xl` | 16 |
|
|
319
|
-
|
|
320
|
-
> **Note:** All shadow parsing happens at compile-time with zero runtime overhead. The platform detection uses React Native's `Platform.select()` API.
|
|
321
|
-
|
|
322
|
-
### Aspect Ratio
|
|
323
|
-
|
|
324
|
-
Control the aspect ratio of views using preset or arbitrary values. Requires React Native 0.71+:
|
|
325
|
-
|
|
326
|
-
**Preset values:**
|
|
327
|
-
|
|
328
|
-
- `aspect-auto` — Remove aspect ratio constraint
|
|
329
|
-
- `aspect-square` — 1:1 aspect ratio
|
|
330
|
-
- `aspect-video` — 16:9 aspect ratio
|
|
331
|
-
|
|
332
|
-
**Arbitrary values:**
|
|
333
|
-
|
|
334
|
-
Use `aspect-[width/height]` for custom ratios:
|
|
335
|
-
|
|
336
|
-
```tsx
|
|
337
|
-
<View className="aspect-[4/3]" /> // 4:3 ratio (1.333...)
|
|
338
|
-
<View className="aspect-[16/9]" /> // 16:9 ratio (1.778...)
|
|
339
|
-
<View className="aspect-[21/9]" /> // 21:9 ultrawide
|
|
340
|
-
<View className="aspect-[9/16]" /> // 9:16 portrait
|
|
341
|
-
<View className="aspect-[3/2]" /> // 3:2 ratio (1.5)
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
**Examples:**
|
|
345
|
-
|
|
346
|
-
```tsx
|
|
347
|
-
// Square image container
|
|
348
|
-
<View className="w-full aspect-square bg-gray-200">
|
|
349
|
-
<Image source={avatar} className="w-full h-full" />
|
|
350
|
-
</View>
|
|
351
|
-
|
|
352
|
-
// Video player container (16:9)
|
|
353
|
-
<View className="w-full aspect-video bg-black">
|
|
354
|
-
<VideoPlayer />
|
|
355
|
-
</View>
|
|
356
|
-
|
|
357
|
-
// Instagram-style square grid
|
|
358
|
-
<View className="flex-row flex-wrap gap-2">
|
|
359
|
-
{photos.map((photo) => (
|
|
360
|
-
<View key={photo.id} className="w-[32%] aspect-square">
|
|
361
|
-
<Image source={photo.uri} className="w-full h-full rounded" />
|
|
362
|
-
</View>
|
|
363
|
-
))}
|
|
364
|
-
</View>
|
|
365
|
-
|
|
366
|
-
// Custom aspect ratio for wide images
|
|
367
|
-
<View className="w-full aspect-[21/9] rounded-lg overflow-hidden">
|
|
368
|
-
<Image source={banner} className="w-full h-full" resizeMode="cover" />
|
|
369
|
-
</View>
|
|
370
|
-
|
|
371
|
-
// Portrait orientation
|
|
372
|
-
<View className="h-full aspect-[9/16]">
|
|
373
|
-
<Story />
|
|
374
|
-
</View>
|
|
375
|
-
|
|
376
|
-
// Remove aspect ratio constraint
|
|
377
|
-
<View className="aspect-square md:aspect-auto">
|
|
378
|
-
Responsive aspect ratio
|
|
379
|
-
</View>
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
**Common Aspect Ratios:**
|
|
383
|
-
|
|
384
|
-
| Ratio | Class | Decimal | Use Case |
|
|
385
|
-
|-------|-------|---------|----------|
|
|
386
|
-
| 1:1 | `aspect-square` | 1.0 | Profile pictures, thumbnails |
|
|
387
|
-
| 16:9 | `aspect-video` | 1.778 | Videos, landscape photos |
|
|
388
|
-
| 4:3 | `aspect-[4/3]` | 1.333 | Standard photos |
|
|
389
|
-
| 3:2 | `aspect-[3/2]` | 1.5 | Classic photography |
|
|
390
|
-
| 21:9 | `aspect-[21/9]` | 2.333 | Ultrawide/cinematic |
|
|
391
|
-
| 9:16 | `aspect-[9/16]` | 0.5625 | Stories, vertical video |
|
|
392
|
-
|
|
393
|
-
> **Note:** The aspect ratio is calculated as `width / height`. When combined with `w-full`, the height will be automatically calculated to maintain the ratio.
|
|
394
|
-
|
|
395
|
-
### Sizing
|
|
396
|
-
|
|
397
|
-
- `w-{size}`, `h-{size}` — Width/height
|
|
398
|
-
- `min-w-{size}`, `min-h-{size}` — Min width/height
|
|
399
|
-
- `max-w-{size}`, `max-h-{size}` — Max width/height
|
|
400
|
-
|
|
401
|
-
**Available sizes:**
|
|
402
|
-
|
|
403
|
-
- **Numeric:** `0`-`96` (same as spacing scale)
|
|
404
|
-
- **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`
|
|
405
|
-
- **Special:** `full` (100%), `auto`
|
|
406
|
-
- **Arbitrary:** `w-[123px]`, `h-[50%]`, `min-w-[200px]`, `max-h-[80%]`
|
|
407
|
-
|
|
408
|
-
> **Note:** Arbitrary sizing supports pixel values (`[123px]` or `[123]`) and percentages (`[50%]`). Other units (`rem`, `em`, `vh`, `vw`) are not supported in React Native.
|
|
409
|
-
|
|
410
125
|
## Usage Examples
|
|
411
126
|
|
|
412
127
|
### Basic Example
|
|
@@ -662,10 +377,10 @@ The package exports an enhanced `TextInput` component that:
|
|
|
662
377
|
|
|
663
378
|
**Supported Modifiers by Component:**
|
|
664
379
|
|
|
665
|
-
| Component | Supported Modifiers
|
|
666
|
-
| ---------------------- |
|
|
667
|
-
| `Pressable` (enhanced) | `active:`, `hover:`, `focus:`, `disabled:`
|
|
668
|
-
| `TextInput` (enhanced) | `focus:`, `disabled:`
|
|
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 |
|
|
669
384
|
|
|
670
385
|
#### Disabled Modifier (Pressable & TextInput)
|
|
671
386
|
|
|
@@ -682,9 +397,7 @@ export function SubmitButton({ isLoading }) {
|
|
|
682
397
|
disabled={isLoading}
|
|
683
398
|
className="bg-blue-500 active:bg-blue-700 disabled:bg-gray-400 p-4 rounded-lg"
|
|
684
399
|
>
|
|
685
|
-
<Text className="text-white font-semibold">
|
|
686
|
-
{isLoading ? "Loading..." : "Submit"}
|
|
687
|
-
</Text>
|
|
400
|
+
<Text className="text-white font-semibold">{isLoading ? "Loading..." : "Submit"}</Text>
|
|
688
401
|
</Pressable>
|
|
689
402
|
);
|
|
690
403
|
}
|
|
@@ -870,47 +583,295 @@ export function Button({ title, onPress, style, containerStyle }: ButtonProps) {
|
|
|
870
583
|
|
|
871
584
|
This pattern allows you to build component libraries with optimized default styling while still supporting full customization.
|
|
872
585
|
|
|
873
|
-
##
|
|
586
|
+
## API Reference
|
|
587
|
+
|
|
588
|
+
### Spacing
|
|
874
589
|
|
|
875
|
-
|
|
590
|
+
**Margin & Padding:**
|
|
876
591
|
|
|
877
|
-
|
|
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
|
|
878
597
|
|
|
879
|
-
1.
|
|
880
|
-
2. **Static Analysis** — Only processes string literals (dynamic values produce warnings)
|
|
881
|
-
3. **Style Registry** — Collects all className → style mappings per file
|
|
882
|
-
4. **Code Generation** — Injects `StyleSheet.create()` at end of file
|
|
883
|
-
5. **Import Management** — Adds `StyleSheet` import if needed
|
|
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`
|
|
884
599
|
|
|
885
|
-
|
|
600
|
+
**Arbitrary values:** `m-[16px]`, `p-[20]`, `mx-[24px]`, `gap-[12px]` — Custom spacing values (px only)
|
|
886
601
|
|
|
887
|
-
|
|
888
|
-
| ---------------- | ------------------ |
|
|
889
|
-
| Runtime Overhead | 0ms (compile-time) |
|
|
890
|
-
| Bundle Size | ~4KB typical |
|
|
891
|
-
| Build Time | +50-200ms |
|
|
602
|
+
### Layout
|
|
892
603
|
|
|
893
|
-
**
|
|
604
|
+
**Flexbox:**
|
|
894
605
|
|
|
895
|
-
-
|
|
896
|
-
-
|
|
897
|
-
-
|
|
898
|
-
-
|
|
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
|
|
899
613
|
|
|
900
|
-
|
|
614
|
+
**Other:**
|
|
901
615
|
|
|
902
|
-
|
|
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):
|
|
903
635
|
|
|
904
636
|
```tsx
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
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
|
+
```
|
|
908
648
|
|
|
909
|
-
|
|
910
|
-
|
|
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
|
|
671
|
+
|
|
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
|
|
911
676
|
```
|
|
912
677
|
|
|
913
|
-
|
|
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.
|
|
914
875
|
|
|
915
876
|
## Advanced
|
|
916
877
|
|
package/dist/babel/index.cjs
CHANGED
|
@@ -620,94 +620,57 @@ function parseLayout(cls) {
|
|
|
620
620
|
}
|
|
621
621
|
|
|
622
622
|
// src/parser/shadows.ts
|
|
623
|
-
var
|
|
624
|
-
var SHADOW_DEFINITIONS = {
|
|
623
|
+
var SHADOW_SCALE = {
|
|
625
624
|
"shadow-sm": {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
},
|
|
632
|
-
android: {
|
|
633
|
-
elevation: 1
|
|
634
|
-
}
|
|
625
|
+
shadowColor: "#000000",
|
|
626
|
+
shadowOffset: { width: 0, height: 1 },
|
|
627
|
+
shadowOpacity: 0.05,
|
|
628
|
+
shadowRadius: 1,
|
|
629
|
+
elevation: 1
|
|
635
630
|
},
|
|
636
631
|
shadow: {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
},
|
|
643
|
-
android: {
|
|
644
|
-
elevation: 2
|
|
645
|
-
}
|
|
632
|
+
shadowColor: "#000000",
|
|
633
|
+
shadowOffset: { width: 0, height: 1 },
|
|
634
|
+
shadowOpacity: 0.1,
|
|
635
|
+
shadowRadius: 2,
|
|
636
|
+
elevation: 2
|
|
646
637
|
},
|
|
647
638
|
"shadow-md": {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
},
|
|
654
|
-
android: {
|
|
655
|
-
elevation: 4
|
|
656
|
-
}
|
|
639
|
+
shadowColor: "#000000",
|
|
640
|
+
shadowOffset: { width: 0, height: 3 },
|
|
641
|
+
shadowOpacity: 0.15,
|
|
642
|
+
shadowRadius: 4,
|
|
643
|
+
elevation: 4
|
|
657
644
|
},
|
|
658
645
|
"shadow-lg": {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
},
|
|
665
|
-
android: {
|
|
666
|
-
elevation: 8
|
|
667
|
-
}
|
|
646
|
+
shadowColor: "#000000",
|
|
647
|
+
shadowOffset: { width: 0, height: 6 },
|
|
648
|
+
shadowOpacity: 0.2,
|
|
649
|
+
shadowRadius: 8,
|
|
650
|
+
elevation: 8
|
|
668
651
|
},
|
|
669
652
|
"shadow-xl": {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
},
|
|
676
|
-
android: {
|
|
677
|
-
elevation: 12
|
|
678
|
-
}
|
|
653
|
+
shadowColor: "#000000",
|
|
654
|
+
shadowOffset: { width: 0, height: 10 },
|
|
655
|
+
shadowOpacity: 0.25,
|
|
656
|
+
shadowRadius: 12,
|
|
657
|
+
elevation: 12
|
|
679
658
|
},
|
|
680
659
|
"shadow-2xl": {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
},
|
|
687
|
-
android: {
|
|
688
|
-
elevation: 16
|
|
689
|
-
}
|
|
660
|
+
shadowColor: "#000000",
|
|
661
|
+
shadowOffset: { width: 0, height: 20 },
|
|
662
|
+
shadowOpacity: 0.3,
|
|
663
|
+
shadowRadius: 24,
|
|
664
|
+
elevation: 16
|
|
690
665
|
},
|
|
691
666
|
"shadow-none": {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
},
|
|
698
|
-
android: {
|
|
699
|
-
elevation: 0
|
|
700
|
-
}
|
|
667
|
+
shadowColor: "transparent",
|
|
668
|
+
shadowOffset: { width: 0, height: 0 },
|
|
669
|
+
shadowOpacity: 0,
|
|
670
|
+
shadowRadius: 0,
|
|
671
|
+
elevation: 0
|
|
701
672
|
}
|
|
702
673
|
};
|
|
703
|
-
function buildShadowScale() {
|
|
704
|
-
const scale = {};
|
|
705
|
-
for (const [key, value] of Object.entries(SHADOW_DEFINITIONS)) {
|
|
706
|
-
scale[key] = import_react_native.Platform.select(value);
|
|
707
|
-
}
|
|
708
|
-
return scale;
|
|
709
|
-
}
|
|
710
|
-
var SHADOW_SCALE = buildShadowScale();
|
|
711
674
|
function parseShadow(cls) {
|
|
712
675
|
if (cls in SHADOW_SCALE) {
|
|
713
676
|
return SHADOW_SCALE[cls];
|
package/dist/parser/shadows.d.ts
CHANGED
|
@@ -4,23 +4,19 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { StyleObject } from "../types";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Shadow scale definitions combining iOS and Android properties
|
|
8
|
+
* Based on Tailwind CSS shadow scale, adapted for React Native
|
|
9
|
+
*
|
|
10
|
+
* Note: We include BOTH iOS shadow properties AND Android elevation in each style.
|
|
11
|
+
* React Native will automatically use the appropriate properties for each platform:
|
|
12
|
+
* - iOS uses shadowColor, shadowOffset, shadowOpacity, shadowRadius
|
|
13
|
+
* - Android uses elevation
|
|
9
14
|
*/
|
|
10
|
-
declare
|
|
11
|
-
/**
|
|
12
|
-
* Computed shadow scale using Platform.select()
|
|
13
|
-
* This is evaluated at module load time for production use
|
|
14
|
-
*/
|
|
15
|
-
declare let SHADOW_SCALE: Record<string, StyleObject>;
|
|
16
|
-
/**
|
|
17
|
-
* Rebuild the shadow scale (useful for testing with platform mocks)
|
|
18
|
-
*/
|
|
19
|
-
export declare function rebuildShadowScale(): void;
|
|
15
|
+
declare const SHADOW_SCALE: Record<string, StyleObject>;
|
|
20
16
|
/**
|
|
21
17
|
* Parse shadow classes
|
|
22
18
|
* @param cls - Class name to parse
|
|
23
19
|
* @returns Style object or null if not a shadow class
|
|
24
20
|
*/
|
|
25
21
|
export declare function parseShadow(cls: string): StyleObject | null;
|
|
26
|
-
export {
|
|
22
|
+
export { SHADOW_SCALE };
|
package/dist/parser/shadows.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports,"__esModule",{value:true});exports.SHADOW_SCALE=void 0;exports.parseShadow=parseShadow;var SHADOW_SCALE=exports.SHADOW_SCALE={"shadow-sm":{shadowColor:"#000000",shadowOffset:{width:0,height:1},shadowOpacity:0.05,shadowRadius:1,elevation:1},shadow:{shadowColor:"#000000",shadowOffset:{width:0,height:1},shadowOpacity:0.1,shadowRadius:2,elevation:2},"shadow-md":{shadowColor:"#000000",shadowOffset:{width:0,height:3},shadowOpacity:0.15,shadowRadius:4,elevation:4},"shadow-lg":{shadowColor:"#000000",shadowOffset:{width:0,height:6},shadowOpacity:0.2,shadowRadius:8,elevation:8},"shadow-xl":{shadowColor:"#000000",shadowOffset:{width:0,height:10},shadowOpacity:0.25,shadowRadius:12,elevation:12},"shadow-2xl":{shadowColor:"#000000",shadowOffset:{width:0,height:20},shadowOpacity:0.3,shadowRadius:24,elevation:16},"shadow-none":{shadowColor:"transparent",shadowOffset:{width:0,height:0},shadowOpacity:0,shadowRadius:0,elevation:0}};function parseShadow(cls){if(cls in SHADOW_SCALE){return SHADOW_SCALE[cls];}return null;}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var _vitest=require("vitest");var _shadows=require("./shadows");(0,_vitest.describe)("SHADOW_SCALE",function(){(0,_vitest.it)("should export complete shadow scale",function(){(0,_vitest.expect)(_shadows.SHADOW_SCALE).toMatchSnapshot();});(0,_vitest.it)("should have all shadow variants",function(){(0,_vitest.expect)(_shadows.SHADOW_SCALE).toHaveProperty("shadow-sm");(0,_vitest.expect)(_shadows.SHADOW_SCALE).toHaveProperty("shadow");(0,_vitest.expect)(_shadows.SHADOW_SCALE).toHaveProperty("shadow-md");(0,_vitest.expect)(_shadows.SHADOW_SCALE).toHaveProperty("shadow-lg");(0,_vitest.expect)(_shadows.SHADOW_SCALE).toHaveProperty("shadow-xl");(0,_vitest.expect)(_shadows.SHADOW_SCALE).toHaveProperty("shadow-2xl");(0,_vitest.expect)(_shadows.SHADOW_SCALE).toHaveProperty("shadow-none");});});(0,_vitest.describe)("parseShadow - basic shadows",function(){(0,_vitest.it)("should parse shadow-sm",function(){var result=(0,_shadows.parseShadow)("shadow-sm");(0,_vitest.expect)(result).toBeTruthy();(0,_vitest.expect)(result).toHaveProperty("shadowColor");(0,_vitest.expect)(result).toHaveProperty("shadowOffset");(0,_vitest.expect)(result).toHaveProperty("shadowOpacity");(0,_vitest.expect)(result).toHaveProperty("shadowRadius");});(0,_vitest.it)("should parse default shadow",function(){var result=(0,_shadows.parseShadow)("shadow");(0,_vitest.expect)(result).toBeTruthy();});(0,_vitest.it)("should parse shadow-md",function(){var result=(0,_shadows.parseShadow)("shadow-md");(0,_vitest.expect)(result).toBeTruthy();});(0,_vitest.it)("should parse shadow-lg",function(){var result=(0,_shadows.parseShadow)("shadow-lg");(0,_vitest.expect)(result).toBeTruthy();});(0,_vitest.it)("should parse shadow-xl",function(){var result=(0,_shadows.parseShadow)("shadow-xl");(0,_vitest.expect)(result).toBeTruthy();});(0,_vitest.it)("should parse shadow-2xl",function(){var result=(0,_shadows.parseShadow)("shadow-2xl");(0,_vitest.expect)(result).toBeTruthy();});(0,_vitest.it)("should parse shadow-none",function(){var result=(0,_shadows.parseShadow)("shadow-none");(0,_vitest.expect)(result).toBeTruthy();(0,_vitest.expect)(result).toMatchObject({shadowColor:"transparent",shadowOpacity:0,shadowRadius:0});});});(0,_vitest.describe)("parseShadow - shadow properties (iOS)",function(){(0,_vitest.it)("should have increasing shadow values for larger shadows",function(){var sm=(0,_shadows.parseShadow)("shadow-sm");var md=(0,_shadows.parseShadow)("shadow-md");var lg=(0,_shadows.parseShadow)("shadow-lg");var xl=(0,_shadows.parseShadow)("shadow-xl");var xxl=(0,_shadows.parseShadow)("shadow-2xl");(0,_vitest.expect)(md==null?void 0:md.shadowOpacity).toBeGreaterThan(sm==null?void 0:sm.shadowOpacity);(0,_vitest.expect)(lg==null?void 0:lg.shadowOpacity).toBeGreaterThan(md==null?void 0:md.shadowOpacity);(0,_vitest.expect)(md==null?void 0:md.shadowRadius).toBeGreaterThan(sm==null?void 0:sm.shadowRadius);(0,_vitest.expect)(lg==null?void 0:lg.shadowRadius).toBeGreaterThan(md==null?void 0:md.shadowRadius);(0,_vitest.expect)(xl==null?void 0:xl.shadowRadius).toBeGreaterThan(lg==null?void 0:lg.shadowRadius);(0,_vitest.expect)(xxl==null?void 0:xxl.shadowRadius).toBeGreaterThan(xl==null?void 0:xl.shadowRadius);});(0,_vitest.it)("should use consistent shadow color",function(){var shadows=["shadow-sm","shadow","shadow-md","shadow-lg","shadow-xl","shadow-2xl"];shadows.forEach(function(shadow){var result=(0,_shadows.parseShadow)(shadow);(0,_vitest.expect)(result==null?void 0:result.shadowColor).toBe("#000000");});});(0,_vitest.it)("should have proper shadowOffset structure",function(){var result=(0,_shadows.parseShadow)("shadow");var shadowOffset=result==null?void 0:result.shadowOffset;(0,_vitest.expect)(shadowOffset).toHaveProperty("width");(0,_vitest.expect)(shadowOffset).toHaveProperty("height");if(typeof shadowOffset==="object"&&shadowOffset!==null){(0,_vitest.expect)(typeof shadowOffset.width).toBe("number");(0,_vitest.expect)(typeof shadowOffset.height).toBe("number");}});(0,_vitest.it)("should include both iOS shadow and Android elevation properties",function(){var result=(0,_shadows.parseShadow)("shadow");(0,_vitest.expect)(result).toHaveProperty("shadowColor");(0,_vitest.expect)(result).toHaveProperty("shadowOffset");(0,_vitest.expect)(result).toHaveProperty("shadowOpacity");(0,_vitest.expect)(result).toHaveProperty("shadowRadius");(0,_vitest.expect)(result).toHaveProperty("elevation");});});(0,_vitest.describe)("parseShadow - shadow properties (Android)",function(){(0,_vitest.it)("should have increasing elevation values for larger shadows",function(){var sm=(0,_shadows.parseShadow)("shadow-sm");var md=(0,_shadows.parseShadow)("shadow-md");var lg=(0,_shadows.parseShadow)("shadow-lg");var xl=(0,_shadows.parseShadow)("shadow-xl");var xxl=(0,_shadows.parseShadow)("shadow-2xl");(0,_vitest.expect)(md==null?void 0:md.elevation).toBeGreaterThan(sm==null?void 0:sm.elevation);(0,_vitest.expect)(lg==null?void 0:lg.elevation).toBeGreaterThan(md==null?void 0:md.elevation);(0,_vitest.expect)(xl==null?void 0:xl.elevation).toBeGreaterThan(lg==null?void 0:lg.elevation);(0,_vitest.expect)(xxl==null?void 0:xxl.elevation).toBeGreaterThan(xl==null?void 0:xl.elevation);});(0,_vitest.it)("should include elevation property for Android",function(){var result=(0,_shadows.parseShadow)("shadow");(0,_vitest.expect)(result).toHaveProperty("elevation");(0,_vitest.expect)(typeof(result==null?void 0:result.elevation)).toBe("number");});});(0,_vitest.describe)("parseShadow - invalid classes",function(){(0,_vitest.it)("should return null for invalid shadow classes",function(){(0,_vitest.expect)((0,_shadows.parseShadow)("shadow-invalid")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("shadows")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("shadow-")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("shadow-small")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("shadow-3xl")).toBeNull();});(0,_vitest.it)("should return null for non-shadow classes",function(){(0,_vitest.expect)((0,_shadows.parseShadow)("bg-blue-500")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("p-4")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("text-white")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("border-2")).toBeNull();});(0,_vitest.it)("should return null for empty or invalid input",function(){(0,_vitest.expect)((0,_shadows.parseShadow)("")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("shadow123")).toBeNull();});});(0,_vitest.describe)("parseShadow - comprehensive coverage",function(){(0,_vitest.it)("should parse all shadow variants without errors",function(){var variants=["shadow-sm","shadow","shadow-md","shadow-lg","shadow-xl","shadow-2xl","shadow-none"];variants.forEach(function(variant){var result=(0,_shadows.parseShadow)(variant);(0,_vitest.expect)(result).toBeTruthy();(0,_vitest.expect)(typeof result).toBe("object");});});(0,_vitest.it)("should return consistent results for same input",function(){var result1=(0,_shadows.parseShadow)("shadow-md");var result2=(0,_shadows.parseShadow)("shadow-md");(0,_vitest.expect)(result1).toEqual(result2);});(0,_vitest.it)("should handle case-sensitive class names",function(){(0,_vitest.expect)((0,_shadows.parseShadow)("SHADOW")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("Shadow-md")).toBeNull();(0,_vitest.expect)((0,_shadows.parseShadow)("shadow-MD")).toBeNull();});});
|
package/dist/types.d.ts
CHANGED
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { ImageStyle, TextStyle, ViewStyle } from "react-native";
|
|
5
5
|
export type RNStyle = ViewStyle | TextStyle | ImageStyle;
|
|
6
|
-
export type StyleObject = Record<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
};
|
|
6
|
+
export type StyleObject = Record<string, string | number | {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
} | undefined>;
|
|
12
10
|
export type SpacingValue = number;
|
|
13
11
|
export type ColorValue = string;
|
|
14
12
|
export type Parser = (className: string) => StyleObject | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mgcrea/react-native-tailwind",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Compile-time Tailwind CSS for React Native with zero runtime overhead",
|
|
5
5
|
"author": "Olivier Louvignes <olivier@mgcrea.io> (https://github.com/mgcrea)",
|
|
6
6
|
"homepage": "https://github.com/mgcrea/react-native-tailwind#readme",
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { SHADOW_SCALE, parseShadow, rebuildShadowScale } from "./shadows";
|
|
4
|
-
|
|
5
|
-
// Reset to iOS before each test
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
setPlatform("ios");
|
|
8
|
-
rebuildShadowScale();
|
|
9
|
-
});
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { SHADOW_SCALE, parseShadow } from "./shadows";
|
|
10
3
|
|
|
11
4
|
describe("SHADOW_SCALE", () => {
|
|
12
5
|
it("should export complete shadow scale", () => {
|
|
@@ -71,10 +64,6 @@ describe("parseShadow - basic shadows", () => {
|
|
|
71
64
|
});
|
|
72
65
|
|
|
73
66
|
describe("parseShadow - shadow properties (iOS)", () => {
|
|
74
|
-
beforeEach(() => {
|
|
75
|
-
setPlatform("ios");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
67
|
it("should have increasing shadow values for larger shadows", () => {
|
|
79
68
|
const sm = parseShadow("shadow-sm");
|
|
80
69
|
const md = parseShadow("shadow-md");
|
|
@@ -104,24 +93,28 @@ describe("parseShadow - shadow properties (iOS)", () => {
|
|
|
104
93
|
|
|
105
94
|
it("should have proper shadowOffset structure", () => {
|
|
106
95
|
const result = parseShadow("shadow");
|
|
107
|
-
|
|
108
|
-
expect(
|
|
109
|
-
expect(
|
|
110
|
-
|
|
96
|
+
const shadowOffset = result?.shadowOffset;
|
|
97
|
+
expect(shadowOffset).toHaveProperty("width");
|
|
98
|
+
expect(shadowOffset).toHaveProperty("height");
|
|
99
|
+
if (typeof shadowOffset === "object" && shadowOffset !== null) {
|
|
100
|
+
expect(typeof shadowOffset.width).toBe("number");
|
|
101
|
+
expect(typeof shadowOffset.height).toBe("number");
|
|
102
|
+
}
|
|
111
103
|
});
|
|
112
104
|
|
|
113
|
-
it("should
|
|
105
|
+
it("should include both iOS shadow and Android elevation properties", () => {
|
|
114
106
|
const result = parseShadow("shadow");
|
|
115
|
-
|
|
107
|
+
// iOS properties
|
|
108
|
+
expect(result).toHaveProperty("shadowColor");
|
|
109
|
+
expect(result).toHaveProperty("shadowOffset");
|
|
110
|
+
expect(result).toHaveProperty("shadowOpacity");
|
|
111
|
+
expect(result).toHaveProperty("shadowRadius");
|
|
112
|
+
// Android property
|
|
113
|
+
expect(result).toHaveProperty("elevation");
|
|
116
114
|
});
|
|
117
115
|
});
|
|
118
116
|
|
|
119
117
|
describe("parseShadow - shadow properties (Android)", () => {
|
|
120
|
-
beforeEach(() => {
|
|
121
|
-
setPlatform("android");
|
|
122
|
-
rebuildShadowScale();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
118
|
it("should have increasing elevation values for larger shadows", () => {
|
|
126
119
|
const sm = parseShadow("shadow-sm");
|
|
127
120
|
const md = parseShadow("shadow-md");
|
|
@@ -136,12 +129,10 @@ describe("parseShadow - shadow properties (Android)", () => {
|
|
|
136
129
|
expect(xxl?.elevation).toBeGreaterThan(xl?.elevation as number);
|
|
137
130
|
});
|
|
138
131
|
|
|
139
|
-
it("should
|
|
132
|
+
it("should include elevation property for Android", () => {
|
|
140
133
|
const result = parseShadow("shadow");
|
|
141
|
-
expect(result).
|
|
142
|
-
expect(result).
|
|
143
|
-
expect(result).not.toHaveProperty("shadowOpacity");
|
|
144
|
-
expect(result).not.toHaveProperty("shadowRadius");
|
|
134
|
+
expect(result).toHaveProperty("elevation");
|
|
135
|
+
expect(typeof result?.elevation).toBe("number");
|
|
145
136
|
});
|
|
146
137
|
});
|
|
147
138
|
|
package/src/parser/shadows.ts
CHANGED
|
@@ -3,117 +3,68 @@
|
|
|
3
3
|
* iOS uses shadow* properties, Android uses elevation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Platform } from "react-native";
|
|
7
6
|
import type { StyleObject } from "../types";
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
|
-
* Shadow scale definitions
|
|
9
|
+
* Shadow scale definitions combining iOS and Android properties
|
|
11
10
|
* Based on Tailwind CSS shadow scale, adapted for React Native
|
|
11
|
+
*
|
|
12
|
+
* Note: We include BOTH iOS shadow properties AND Android elevation in each style.
|
|
13
|
+
* React Native will automatically use the appropriate properties for each platform:
|
|
14
|
+
* - iOS uses shadowColor, shadowOffset, shadowOpacity, shadowRadius
|
|
15
|
+
* - Android uses elevation
|
|
12
16
|
*/
|
|
13
|
-
const
|
|
17
|
+
const SHADOW_SCALE: Record<string, StyleObject> = {
|
|
14
18
|
"shadow-sm": {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
android: {
|
|
22
|
-
elevation: 1,
|
|
23
|
-
},
|
|
19
|
+
shadowColor: "#000000",
|
|
20
|
+
shadowOffset: { width: 0, height: 1 },
|
|
21
|
+
shadowOpacity: 0.05,
|
|
22
|
+
shadowRadius: 1,
|
|
23
|
+
elevation: 1,
|
|
24
24
|
},
|
|
25
25
|
shadow: {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
android: {
|
|
33
|
-
elevation: 2,
|
|
34
|
-
},
|
|
26
|
+
shadowColor: "#000000",
|
|
27
|
+
shadowOffset: { width: 0, height: 1 },
|
|
28
|
+
shadowOpacity: 0.1,
|
|
29
|
+
shadowRadius: 2,
|
|
30
|
+
elevation: 2,
|
|
35
31
|
},
|
|
36
32
|
"shadow-md": {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
},
|
|
43
|
-
android: {
|
|
44
|
-
elevation: 4,
|
|
45
|
-
},
|
|
33
|
+
shadowColor: "#000000",
|
|
34
|
+
shadowOffset: { width: 0, height: 3 },
|
|
35
|
+
shadowOpacity: 0.15,
|
|
36
|
+
shadowRadius: 4,
|
|
37
|
+
elevation: 4,
|
|
46
38
|
},
|
|
47
39
|
"shadow-lg": {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
54
|
-
android: {
|
|
55
|
-
elevation: 8,
|
|
56
|
-
},
|
|
40
|
+
shadowColor: "#000000",
|
|
41
|
+
shadowOffset: { width: 0, height: 6 },
|
|
42
|
+
shadowOpacity: 0.2,
|
|
43
|
+
shadowRadius: 8,
|
|
44
|
+
elevation: 8,
|
|
57
45
|
},
|
|
58
46
|
"shadow-xl": {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
},
|
|
65
|
-
android: {
|
|
66
|
-
elevation: 12,
|
|
67
|
-
},
|
|
47
|
+
shadowColor: "#000000",
|
|
48
|
+
shadowOffset: { width: 0, height: 10 },
|
|
49
|
+
shadowOpacity: 0.25,
|
|
50
|
+
shadowRadius: 12,
|
|
51
|
+
elevation: 12,
|
|
68
52
|
},
|
|
69
53
|
"shadow-2xl": {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
},
|
|
76
|
-
android: {
|
|
77
|
-
elevation: 16,
|
|
78
|
-
},
|
|
54
|
+
shadowColor: "#000000",
|
|
55
|
+
shadowOffset: { width: 0, height: 20 },
|
|
56
|
+
shadowOpacity: 0.3,
|
|
57
|
+
shadowRadius: 24,
|
|
58
|
+
elevation: 16,
|
|
79
59
|
},
|
|
80
60
|
"shadow-none": {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
android: {
|
|
88
|
-
elevation: 0,
|
|
89
|
-
},
|
|
61
|
+
shadowColor: "transparent",
|
|
62
|
+
shadowOffset: { width: 0, height: 0 },
|
|
63
|
+
shadowOpacity: 0,
|
|
64
|
+
shadowRadius: 0,
|
|
65
|
+
elevation: 0,
|
|
90
66
|
},
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Helper function to build the shadow scale using Platform.select()
|
|
95
|
-
* This allows tests to rebuild the scale after changing the platform mock
|
|
96
|
-
*/
|
|
97
|
-
function buildShadowScale(): Record<string, StyleObject> {
|
|
98
|
-
const scale: Record<string, StyleObject> = {};
|
|
99
|
-
for (const [key, value] of Object.entries(SHADOW_DEFINITIONS)) {
|
|
100
|
-
scale[key] = Platform.select<StyleObject>(value as never);
|
|
101
|
-
}
|
|
102
|
-
return scale;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Computed shadow scale using Platform.select()
|
|
107
|
-
* This is evaluated at module load time for production use
|
|
108
|
-
*/
|
|
109
|
-
let SHADOW_SCALE = buildShadowScale();
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Rebuild the shadow scale (useful for testing with platform mocks)
|
|
113
|
-
*/
|
|
114
|
-
export function rebuildShadowScale() {
|
|
115
|
-
SHADOW_SCALE = buildShadowScale();
|
|
116
|
-
}
|
|
67
|
+
};
|
|
117
68
|
|
|
118
69
|
/**
|
|
119
70
|
* Parse shadow classes
|
|
@@ -129,5 +80,5 @@ export function parseShadow(cls: string): StyleObject | null {
|
|
|
129
80
|
return null;
|
|
130
81
|
}
|
|
131
82
|
|
|
132
|
-
// Export shadow scale
|
|
133
|
-
export {
|
|
83
|
+
// Export shadow scale for testing/advanced usage
|
|
84
|
+
export { SHADOW_SCALE };
|
package/src/types.ts
CHANGED
|
@@ -6,9 +6,7 @@ import type { ImageStyle, TextStyle, ViewStyle } from "react-native";
|
|
|
6
6
|
|
|
7
7
|
export type RNStyle = ViewStyle | TextStyle | ImageStyle;
|
|
8
8
|
|
|
9
|
-
export type StyleObject = Record<
|
|
10
|
-
shadowOffset?: { width: number; height: number };
|
|
11
|
-
};
|
|
9
|
+
export type StyleObject = Record<string, string | number | { width: number; height: number } | undefined>;
|
|
12
10
|
|
|
13
11
|
export type SpacingValue = number;
|
|
14
12
|
export type ColorValue = string;
|