Skip to content

Commit a759581

Browse files
committed
chore: wip
1 parent 23c1356 commit a759581

File tree

10 files changed

+1870
-47
lines changed

10 files changed

+1870
-47
lines changed

packages/headwind/src/parser.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ export function parseClass(className: string): ParsedClass {
8686
'grid-flow',
8787
'auto-cols',
8888
'auto-rows',
89+
'col-span',
90+
'row-span',
8991
'col-start',
9092
'col-end',
9193
'row-start',

packages/headwind/src/rules-grid.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,29 @@ export const gridColumnRule: UtilityRule = (parsed) => {
4343
}
4444
return parsed.value ? { 'grid-column': spans[parsed.value] || parsed.value } : undefined
4545
}
46+
if (parsed.utility === 'col-span' && parsed.value) {
47+
const spans: Record<string, string> = {
48+
auto: 'auto',
49+
1: 'span 1 / span 1',
50+
2: 'span 2 / span 2',
51+
3: 'span 3 / span 3',
52+
4: 'span 4 / span 4',
53+
5: 'span 5 / span 5',
54+
6: 'span 6 / span 6',
55+
7: 'span 7 / span 7',
56+
8: 'span 8 / span 8',
57+
9: 'span 9 / span 9',
58+
10: 'span 10 / span 10',
59+
11: 'span 11 / span 11',
60+
12: 'span 12 / span 12',
61+
full: '1 / -1',
62+
}
63+
// Handle arbitrary values: col-span-[15] -> span 15 / span 15
64+
if (parsed.arbitrary) {
65+
return { 'grid-column': `span ${parsed.value} / span ${parsed.value}` }
66+
}
67+
return { 'grid-column': spans[parsed.value] || parsed.value }
68+
}
4669
if (parsed.utility === 'col-start' && parsed.value) {
4770
return { 'grid-column-start': parsed.value }
4871
}
@@ -80,6 +103,23 @@ export const gridRowRule: UtilityRule = (parsed) => {
80103
}
81104
return parsed.value ? { 'grid-row': spans[parsed.value] || parsed.value } : undefined
82105
}
106+
if (parsed.utility === 'row-span' && parsed.value) {
107+
const spans: Record<string, string> = {
108+
auto: 'auto',
109+
1: 'span 1 / span 1',
110+
2: 'span 2 / span 2',
111+
3: 'span 3 / span 3',
112+
4: 'span 4 / span 4',
113+
5: 'span 5 / span 5',
114+
6: 'span 6 / span 6',
115+
full: '1 / -1',
116+
}
117+
// Handle arbitrary values: row-span-[15] -> span 15 / span 15
118+
if (parsed.arbitrary) {
119+
return { 'grid-row': `span ${parsed.value} / span ${parsed.value}` }
120+
}
121+
return { 'grid-row': spans[parsed.value] || parsed.value }
122+
}
83123
if (parsed.utility === 'row-start' && parsed.value) {
84124
return { 'grid-row-start': parsed.value }
85125
}

packages/headwind/src/rules-transforms.ts

Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,109 +14,170 @@ export const transformRule: UtilityRule = (parsed) => {
1414

1515
export const scaleRule: UtilityRule = (parsed) => {
1616
if (parsed.utility === 'scale') {
17-
const scale = parsed.value ? Number(parsed.value) / 100 : 1
17+
if (!parsed.value) return { transform: 'scale(1)' }
18+
// If arbitrary, use value as-is
19+
if (parsed.arbitrary) {
20+
return { transform: `scale(${parsed.value})` }
21+
}
22+
const scale = Number(parsed.value) / 100
1823
return { transform: `scale(${scale})` }
1924
}
2025
if (parsed.utility === 'scale-x') {
21-
const scale = parsed.value ? Number(parsed.value) / 100 : 1
26+
if (!parsed.value) return { transform: 'scaleX(1)' }
27+
if (parsed.arbitrary) {
28+
return { transform: `scaleX(${parsed.value})` }
29+
}
30+
const scale = Number(parsed.value) / 100
2231
return { transform: `scaleX(${scale})` }
2332
}
2433
if (parsed.utility === 'scale-y') {
25-
const scale = parsed.value ? Number(parsed.value) / 100 : 1
34+
if (!parsed.value) return { transform: 'scaleY(1)' }
35+
if (parsed.arbitrary) {
36+
return { transform: `scaleY(${parsed.value})` }
37+
}
38+
const scale = Number(parsed.value) / 100
2639
return { transform: `scaleY(${scale})` }
2740
}
2841
if (parsed.utility === 'scale-z') {
29-
const scale = parsed.value ? Number(parsed.value) / 100 : 1
42+
if (!parsed.value) return { transform: 'scaleZ(1)' }
43+
if (parsed.arbitrary) {
44+
return { transform: `scaleZ(${parsed.value})` }
45+
}
46+
const scale = Number(parsed.value) / 100
3047
return { transform: `scaleZ(${scale})` }
3148
}
3249
}
3350

3451
export const rotateRule: UtilityRule = (parsed) => {
3552
if (parsed.utility === 'rotate' && parsed.value) {
36-
return { transform: `rotate(${parsed.value}deg)` }
53+
// If arbitrary or already has unit, use as-is, otherwise add deg
54+
const value = parsed.arbitrary || parsed.value.includes('deg') || parsed.value.includes('rad') || parsed.value.includes('turn')
55+
? parsed.value
56+
: `${parsed.value}deg`
57+
return { transform: `rotate(${value})` }
3758
}
3859
if (parsed.utility === 'rotate-x' && parsed.value) {
39-
return { transform: `rotateX(${parsed.value}deg)` }
60+
const value = parsed.arbitrary || parsed.value.includes('deg') || parsed.value.includes('rad') || parsed.value.includes('turn')
61+
? parsed.value
62+
: `${parsed.value}deg`
63+
return { transform: `rotateX(${value})` }
4064
}
4165
if (parsed.utility === 'rotate-y' && parsed.value) {
42-
return { transform: `rotateY(${parsed.value}deg)` }
66+
const value = parsed.arbitrary || parsed.value.includes('deg') || parsed.value.includes('rad') || parsed.value.includes('turn')
67+
? parsed.value
68+
: `${parsed.value}deg`
69+
return { transform: `rotateY(${value})` }
4370
}
4471
if (parsed.utility === 'rotate-z' && parsed.value) {
45-
return { transform: `rotateZ(${parsed.value}deg)` }
72+
const value = parsed.arbitrary || parsed.value.includes('deg') || parsed.value.includes('rad') || parsed.value.includes('turn')
73+
? parsed.value
74+
: `${parsed.value}deg`
75+
return { transform: `rotateZ(${value})` }
4676
}
4777
}
4878

4979
export const translateRule: UtilityRule = (parsed, config) => {
80+
const getTranslateValue = (val: string): string => {
81+
// Handle fractions: 1/2 -> 50%, 1/3 -> 33.333333%, etc.
82+
if (val.includes('/')) {
83+
const [num, denom] = val.split('/')
84+
const percentage = (Number(num) / Number(denom)) * 100
85+
return `${percentage}%`
86+
}
87+
// Handle special keywords
88+
if (val === 'full') return '100%'
89+
if (val === 'half') return '50%'
90+
// Check spacing config
91+
return config.theme.spacing[val] || val
92+
}
93+
5094
if (parsed.utility === 'translate-x' && parsed.value) {
5195
let value: string
5296
if (parsed.value.startsWith('-')) {
5397
const positiveValue = parsed.value.slice(1)
54-
const spacing = config.theme.spacing[positiveValue]
55-
value = spacing ? `-${spacing}` : parsed.value
98+
value = `-${getTranslateValue(positiveValue)}`
5699
}
57100
else {
58-
value = config.theme.spacing[parsed.value] || parsed.value
101+
value = getTranslateValue(parsed.value)
59102
}
60103
return { transform: `translateX(${value})` }
61104
}
62105
if (parsed.utility === 'translate-y' && parsed.value) {
63106
let value: string
64107
if (parsed.value.startsWith('-')) {
65108
const positiveValue = parsed.value.slice(1)
66-
const spacing = config.theme.spacing[positiveValue]
67-
value = spacing ? `-${spacing}` : parsed.value
109+
value = `-${getTranslateValue(positiveValue)}`
68110
}
69111
else {
70-
value = config.theme.spacing[parsed.value] || parsed.value
112+
value = getTranslateValue(parsed.value)
71113
}
72114
return { transform: `translateY(${value})` }
73115
}
74116
if (parsed.utility === 'translate-z' && parsed.value) {
75117
let value: string
76118
if (parsed.value.startsWith('-')) {
77119
const positiveValue = parsed.value.slice(1)
78-
const spacing = config.theme.spacing[positiveValue]
79-
value = spacing ? `-${spacing}` : parsed.value
120+
value = `-${getTranslateValue(positiveValue)}`
80121
}
81122
else {
82-
value = config.theme.spacing[parsed.value] || parsed.value
123+
value = getTranslateValue(parsed.value)
83124
}
84125
return { transform: `translateZ(${value})` }
85126
}
86127
}
87128

88129
export const skewRule: UtilityRule = (parsed) => {
89130
if (parsed.utility === 'skew-x' && parsed.value) {
90-
return { transform: `skewX(${parsed.value}deg)` }
131+
const value = parsed.arbitrary || parsed.value.includes('deg') || parsed.value.includes('rad') || parsed.value.includes('turn')
132+
? parsed.value
133+
: `${parsed.value}deg`
134+
return { transform: `skewX(${value})` }
91135
}
92136
if (parsed.utility === 'skew-y' && parsed.value) {
93-
return { transform: `skewY(${parsed.value}deg)` }
137+
const value = parsed.arbitrary || parsed.value.includes('deg') || parsed.value.includes('rad') || parsed.value.includes('turn')
138+
? parsed.value
139+
: `${parsed.value}deg`
140+
return { transform: `skewY(${value})` }
94141
}
95142
}
96143

97144
export const transformOriginRule: UtilityRule = (parsed) => {
98-
const origins: Record<string, string> = {
99-
'origin-center': 'center',
100-
'origin-top': 'top',
101-
'origin-top-right': 'top right',
102-
'origin-right': 'right',
103-
'origin-bottom-right': 'bottom right',
104-
'origin-bottom': 'bottom',
105-
'origin-bottom-left': 'bottom left',
106-
'origin-left': 'left',
107-
'origin-top-left': 'top left',
145+
if (parsed.utility === 'origin' && parsed.value) {
146+
// Handle arbitrary values with underscores as spaces
147+
if (parsed.arbitrary) {
148+
return { 'transform-origin': parsed.value.replace(/_/g, ' ') }
149+
}
150+
// Handle predefined values
151+
const origins: Record<string, string> = {
152+
'center': 'center',
153+
'top': 'top',
154+
'top-right': 'top right',
155+
'right': 'right',
156+
'bottom-right': 'bottom right',
157+
'bottom': 'bottom',
158+
'bottom-left': 'bottom left',
159+
'left': 'left',
160+
'top-left': 'top left',
161+
}
162+
return origins[parsed.value] ? { 'transform-origin': origins[parsed.value] } : undefined
108163
}
109-
return origins[parsed.raw] ? { 'transform-origin': origins[parsed.raw] } : undefined
164+
return undefined
110165
}
111166

112167
export const perspectiveRule: UtilityRule = (parsed) => {
113168
if (parsed.utility === 'perspective' && parsed.value) {
169+
// If value is 'none', use as-is
170+
if (parsed.value === 'none') {
171+
return { perspective: 'none' }
172+
}
173+
// If arbitrary or already has unit, use as-is
174+
if (parsed.arbitrary || parsed.value.includes('px') || parsed.value.includes('rem') || parsed.value.includes('em')) {
175+
return { perspective: parsed.value }
176+
}
177+
// Otherwise add px
114178
return { perspective: `${parsed.value}px` }
115179
}
116-
const values: Record<string, string> = {
117-
'perspective-none': 'none',
118-
}
119-
return values[parsed.raw] ? { perspective: values[parsed.raw] } : undefined
180+
return undefined
120181
}
121182

122183
export const perspectiveOriginRule: UtilityRule = (parsed) => {

packages/headwind/src/rules-typography.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,15 @@ export const letterSpacingRule: UtilityRule = (parsed) => {
6565
wider: '0.05em',
6666
widest: '0.1em',
6767
}
68-
return parsed.value ? { 'letter-spacing': values[parsed.value] || parsed.value } : undefined
68+
if (!parsed.value) {
69+
return undefined
70+
}
71+
// Handle negative values
72+
if (parsed.value.startsWith('-')) {
73+
const positiveValue = parsed.value.slice(1)
74+
return { 'letter-spacing': `-${values[positiveValue] || positiveValue}` }
75+
}
76+
return { 'letter-spacing': values[parsed.value] || parsed.value }
6977
}
7078
}
7179

@@ -133,7 +141,7 @@ export const textDecorationRule: UtilityRule = (parsed, config) => {
133141
// Check if it's a thickness
134142
const thicknesses: Record<string, string> = {
135143
auto: 'auto',
136-
from: 'from-font',
144+
'from-font': 'from-font',
137145
0: '0px',
138146
1: '1px',
139147
2: '2px',
@@ -144,6 +152,11 @@ export const textDecorationRule: UtilityRule = (parsed, config) => {
144152
return { 'text-decoration-thickness': thicknesses[parsed.value] }
145153
}
146154

155+
// Handle arbitrary thickness
156+
if (parsed.arbitrary) {
157+
return { 'text-decoration-thickness': parsed.value }
158+
}
159+
147160
// Otherwise treat it as a color: decoration-blue-500
148161
const parts = parsed.value.split('-')
149162
if (parts.length === 2) {
@@ -216,6 +229,12 @@ export const textWrapRule: UtilityRule = (parsed) => {
216229

217230
export const textIndentRule: UtilityRule = (parsed, config) => {
218231
if (parsed.utility === 'indent' && parsed.value) {
232+
// Handle negative values
233+
if (parsed.value.startsWith('-')) {
234+
const positiveValue = parsed.value.slice(1)
235+
const spacing = config.theme.spacing[positiveValue] || positiveValue
236+
return { 'text-indent': `-${spacing}` }
237+
}
219238
return { 'text-indent': config.theme.spacing[parsed.value] || parsed.value }
220239
}
221240
}
@@ -247,9 +266,16 @@ export const whiteSpaceRule: UtilityRule = (parsed) => {
247266
}
248267

249268
export const wordBreakRule: UtilityRule = (parsed) => {
269+
if (parsed.raw === 'break-normal') {
270+
return {
271+
'overflow-wrap': 'normal',
272+
'word-break': 'normal',
273+
}
274+
}
275+
if (parsed.raw === 'break-words') {
276+
return { 'overflow-wrap': 'break-word' }
277+
}
250278
const breaks: Record<string, string> = {
251-
'break-normal': 'normal',
252-
'break-words': 'break-word',
253279
'break-all': 'break-all',
254280
'break-keep': 'keep-all',
255281
}
@@ -279,7 +305,12 @@ export const contentRule: UtilityRule = (parsed) => {
279305
const values: Record<string, string> = {
280306
none: 'none',
281307
}
282-
return { content: values[parsed.value] || `"${parsed.value}"` }
308+
// If value is already quoted or is a special value, use as-is
309+
if (values[parsed.value] || parsed.value.startsWith('"') || parsed.value.startsWith("'")) {
310+
return { content: values[parsed.value] || parsed.value }
311+
}
312+
// Otherwise wrap in quotes
313+
return { content: `"${parsed.value}"` }
283314
}
284315
}
285316

0 commit comments

Comments
 (0)