Skip to content

Commit 1fc6678

Browse files
authored
Merge pull request #8 from e-simpson/size-pr
feat: Add size-* support
2 parents 432a23c + b26a554 commit 1fc6678

3 files changed

Lines changed: 192 additions & 6 deletions

File tree

docs/src/content/docs/reference/sizing.md

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,80 @@ description: Width and height utilities
55

66
Control element dimensions with width and height utilities.
77

8+
## Size
9+
10+
Sets both width and height to the same value simultaneously.
11+
12+
### Numeric
13+
14+
```tsx
15+
// Before (Tailwind)
16+
<View className="size-0" />
17+
<View className="size-4" />
18+
<View className="size-8" />
19+
<View className="size-16" />
20+
<View className="size-32" />
21+
<View className="size-64" />
22+
<View className="size-96" />
23+
24+
// After (React Native style object)
25+
<View style={{ width: 0, height: 0 }} />
26+
<View style={{ width: 16, height: 16 }} />
27+
<View style={{ width: 32, height: 32 }} />
28+
<View style={{ width: 64, height: 64 }} />
29+
<View style={{ width: 128, height: 128 }} />
30+
<View style={{ width: 256, height: 256 }} />
31+
<View style={{ width: 384, height: 384 }} />
32+
```
33+
34+
### Fractional
35+
36+
```tsx
37+
// Before (Tailwind)
38+
<View className="size-1/2" />
39+
<View className="size-1/3" />
40+
<View className="size-2/3" />
41+
<View className="size-1/4" />
42+
<View className="size-3/4" />
43+
<View className="size-1/5" />
44+
45+
// After (React Native style object)
46+
<View style={{ width: '50%', height: '50%' }} />
47+
<View style={{ width: '33.333333%', height: '33.333333%' }} />
48+
<View style={{ width: '66.666667%', height: '66.666667%' }} />
49+
<View style={{ width: '25%', height: '25%' }} />
50+
<View style={{ width: '75%', height: '75%' }} />
51+
<View style={{ width: '20%', height: '20%' }} />
52+
```
53+
54+
### Special
55+
56+
```tsx
57+
// Before (Tailwind)
58+
<View className="size-full" />
59+
<View className="size-auto" />
60+
61+
// After (React Native style object)
62+
<View style={{ width: '100%', height: '100%' }} />
63+
<View style={{ width: 'auto', height: 'auto' }} />
64+
```
65+
66+
### Arbitrary
67+
68+
```tsx
69+
// Before (Tailwind)
70+
<View className="size-[123px]" />
71+
<View className="size-[200px]" />
72+
<View className="size-[50%]" />
73+
<View className="size-[75%]" />
74+
75+
// After (React Native style object)
76+
<View style={{ width: 123, height: 123 }} />
77+
<View style={{ width: 200, height: 200 }} />
78+
<View style={{ width: '50%', height: '50%' }} />
79+
<View style={{ width: '75%', height: '75%' }} />
80+
```
81+
882
## Width
983

1084
### Numeric
@@ -147,18 +221,15 @@ function FullScreenModal() {
147221

148222
```tsx
149223
// Input
150-
<View className="w-screen h-screen bg-white" />
224+
<View className="w-screen h-screen bg-white" />;
151225

152226
// Generated output
153-
import { useWindowDimensions } from 'react-native';
227+
import { useWindowDimensions } from "react-native";
154228

155229
function Component() {
156230
const _twDimensions = useWindowDimensions();
157231

158-
return <View style={[
159-
styles._bg_white,
160-
{ width: _twDimensions.width, height: _twDimensions.height }
161-
]} />;
232+
return <View style={[styles._bg_white, { width: _twDimensions.width, height: _twDimensions.height }]} />;
162233
}
163234
```
164235

@@ -234,7 +305,11 @@ You can combine screen dimensions with **static utility classes only** (no modif
234305
### Fixed Size Square
235306

236307
```tsx
308+
// Using separate width and height
237309
<View className="w-16 h-16 bg-gray-200 rounded" />
310+
311+
// Using size shorthand (width + height)
312+
<View className="size-16 bg-gray-200 rounded" />
238313
```
239314

240315
### Responsive Grid
@@ -265,6 +340,7 @@ You can combine screen dimensions with **static utility classes only** (no modif
265340
## Note
266341

267342
Arbitrary sizing supports:
343+
268344
- Pixel values: `[123px]` or `[123]`
269345
- Percentages: `[50%]`
270346

src/parser/sizing.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,85 @@ describe("parseSizing - custom spacing", () => {
310310
expect(parseSizing("h-8")).toEqual({ height: 32 }); // Default behavior
311311
});
312312
});
313+
314+
describe("parseSizing - size (width + height)", () => {
315+
it("should parse size with numeric values", () => {
316+
expect(parseSizing("size-0")).toEqual({ width: 0, height: 0 });
317+
expect(parseSizing("size-4")).toEqual({ width: 16, height: 16 });
318+
expect(parseSizing("size-8")).toEqual({ width: 32, height: 32 });
319+
expect(parseSizing("size-96")).toEqual({ width: 384, height: 384 });
320+
});
321+
322+
it("should parse size with fractional values", () => {
323+
expect(parseSizing("size-0.5")).toEqual({ width: 2, height: 2 });
324+
expect(parseSizing("size-1.5")).toEqual({ width: 6, height: 6 });
325+
expect(parseSizing("size-2.5")).toEqual({ width: 10, height: 10 });
326+
});
327+
328+
it("should parse size with percentage values", () => {
329+
expect(parseSizing("size-full")).toEqual({ width: "100%", height: "100%" });
330+
expect(parseSizing("size-1/2")).toEqual({ width: "50%", height: "50%" });
331+
expect(parseSizing("size-1/3")).toEqual({ width: "33.333333%", height: "33.333333%" });
332+
expect(parseSizing("size-2/3")).toEqual({ width: "66.666667%", height: "66.666667%" });
333+
expect(parseSizing("size-1/4")).toEqual({ width: "25%", height: "25%" });
334+
expect(parseSizing("size-3/4")).toEqual({ width: "75%", height: "75%" });
335+
});
336+
337+
it("should parse size with special values", () => {
338+
expect(parseSizing("size-auto")).toEqual({ width: "auto", height: "auto" });
339+
});
340+
341+
it("should parse size with arbitrary pixel values", () => {
342+
expect(parseSizing("size-[123px]")).toEqual({ width: 123, height: 123 });
343+
expect(parseSizing("size-[200]")).toEqual({ width: 200, height: 200 });
344+
expect(parseSizing("size-[50px]")).toEqual({ width: 50, height: 50 });
345+
});
346+
347+
it("should parse size with arbitrary percentage values", () => {
348+
expect(parseSizing("size-[50%]")).toEqual({ width: "50%", height: "50%" });
349+
expect(parseSizing("size-[33.333%]")).toEqual({ width: "33.333%", height: "33.333%" });
350+
expect(parseSizing("size-[85%]")).toEqual({ width: "85%", height: "85%" });
351+
});
352+
353+
it("should support custom spacing values for size", () => {
354+
const customSpacing = { sm: 8, md: 16, lg: 32, xl: 64 };
355+
expect(parseSizing("size-sm", customSpacing)).toEqual({ width: 8, height: 8 });
356+
expect(parseSizing("size-md", customSpacing)).toEqual({ width: 16, height: 16 });
357+
expect(parseSizing("size-lg", customSpacing)).toEqual({ width: 32, height: 32 });
358+
expect(parseSizing("size-xl", customSpacing)).toEqual({ width: 64, height: 64 });
359+
});
360+
361+
it("should allow custom spacing to override preset values", () => {
362+
const customSpacing = { "4": 20 }; // Override default (16)
363+
expect(parseSizing("size-4", customSpacing)).toEqual({ width: 20, height: 20 });
364+
});
365+
366+
it("should prefer arbitrary values over custom spacing", () => {
367+
expect(parseSizing("size-[24px]", { "4": 20 })).toEqual({ width: 24, height: 24 });
368+
expect(parseSizing("size-[50]", { sm: 8 })).toEqual({ width: 50, height: 50 });
369+
});
370+
371+
it("should handle all fractional percentage variants", () => {
372+
expect(parseSizing("size-1/5")).toEqual({ width: "20%", height: "20%" });
373+
expect(parseSizing("size-2/5")).toEqual({ width: "40%", height: "40%" });
374+
expect(parseSizing("size-3/5")).toEqual({ width: "60%", height: "60%" });
375+
expect(parseSizing("size-4/5")).toEqual({ width: "80%", height: "80%" });
376+
expect(parseSizing("size-1/6")).toEqual({ width: "16.666667%", height: "16.666667%" });
377+
expect(parseSizing("size-5/6")).toEqual({ width: "83.333333%", height: "83.333333%" });
378+
});
379+
380+
it("should handle edge case size values", () => {
381+
expect(parseSizing("size-0")).toEqual({ width: 0, height: 0 });
382+
});
383+
384+
it("should return null for invalid size values", () => {
385+
expect(parseSizing("size-invalid")).toBeNull();
386+
expect(parseSizing("size-999")).toBeNull();
387+
});
388+
389+
it("should return null for arbitrary values with unsupported units", () => {
390+
expect(parseSizing("size-[16rem]")).toBeNull();
391+
expect(parseSizing("size-[2em]")).toBeNull();
392+
expect(parseSizing("size-[50vh]")).toBeNull();
393+
});
394+
});

src/parser/sizing.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,34 @@ export function parseSizing(cls: string, customSpacing?: Record<string, number>)
102102
// Merge custom spacing with defaults (custom takes precedence)
103103
const sizeMap = customSpacing ? { ...SIZE_SCALE, ...customSpacing } : SIZE_SCALE;
104104

105+
// Size (both width AND height)
106+
if (cls.startsWith("size-")) {
107+
const sizeKey = cls.substring(5); // "size-".length = 5
108+
109+
// Arbitrary values: size-[123px], size-[50%] (highest priority)
110+
const arbitrarySize = parseArbitrarySize(sizeKey);
111+
if (arbitrarySize !== null) {
112+
return { width: arbitrarySize, height: arbitrarySize };
113+
}
114+
115+
// Percentage sizes: size-full, size-1/2, etc.
116+
const percentage = SIZE_PERCENTAGES[sizeKey];
117+
if (percentage) {
118+
return { width: percentage, height: percentage };
119+
}
120+
121+
// Numeric sizes: size-4, size-8, etc. (includes custom spacing)
122+
const numericSize = sizeMap[sizeKey];
123+
if (numericSize !== undefined) {
124+
return { width: numericSize, height: numericSize };
125+
}
126+
127+
// Special values
128+
if (sizeKey === "auto") {
129+
return { width: "auto", height: "auto" };
130+
}
131+
}
132+
105133
// Width
106134
if (cls.startsWith("w-")) {
107135
const sizeKey = cls.substring(2);

0 commit comments

Comments
 (0)