Skip to content

Commit 918977a

Browse files
committed
Add Tailwind 4
1 parent 2465711 commit 918977a

File tree

9 files changed

+90
-17
lines changed

9 files changed

+90
-17
lines changed

apps/debug/pages/index.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ export default function Web() {
99
const testWarnings = ["This is an example of a conversion warning message."];
1010

1111
return (
12-
<div className="flex flex-col p-8 space-y-2">
12+
<div className="flex flex-col p-8 gap-2">
1313
<p className="text-3xl font-bold">Debug Mode</p>
1414
<div className="h-0.5 bg-gray-100 rounded"></div>
1515

16-
<div className="flex w-full space-x-2">
16+
<div className="flex w-full gap-2">
1717
<div className="bg-gray-100 p-2 rounded-md">
1818
<div className="bg-white w-96 shadow-md rounded-md">
1919
<PluginFigmaToolbar variant="(Light)" />
@@ -56,7 +56,7 @@ export default function Web() {
5656
<div>
5757
Plugin dropdown selection (each frame a different breakpoint)
5858
</div>
59-
<div className="flex space-x-4">
59+
<div className="flex gap-4">
6060
<div className="w-24 h-12 bg-red-400"></div>
6161
<div className="w-12 h-12 bg-green-500"></div>
6262
<div className="w-4 h-12 bg-blue-600"></div>
@@ -65,7 +65,7 @@ export default function Web() {
6565

6666
<div>
6767
<div>Outputs from plugin (different screen sizes)</div>
68-
<div className="flex space-x-4">
68+
<div className="flex gap-4">
6969
<div className="w-24 h-12 bg-gray-400"></div>
7070
<div className="w-12 h-12 bg-gray-500"></div>
7171
<div className="w-4 h-12 bg-gray-600"></div>
@@ -74,7 +74,7 @@ export default function Web() {
7474

7575
<div>
7676
<div> - Experiment on dark mode (invert colors on output) </div>
77-
<div className="flex space-x-4">
77+
<div className="flex gap-4">
7878
<div className="w-24 h-12 bg-gray-900"></div>
7979
<div className="w-12 h-12 bg-gray-800"></div>
8080
<div className="w-4 h-12 bg-gray-700"></div>
@@ -106,7 +106,7 @@ export default function Web() {
106106
}
107107

108108
const PluginFigmaToolbar = (props: { variant: string }) => (
109-
<div className="bg-gray-800 w-full h-8 flex items-center text-white space-x-4 px-4">
109+
<div className="bg-gray-800 w-full h-8 flex items-center text-white gap-4 px-4">
110110
<span>Figma to Code {props.variant}</span>
111111
</div>
112112
);

apps/plugin/plugin-src/code.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const defaultPluginSettings: PluginSettings = {
3232
htmlGenerationMode: "html",
3333
tailwindGenerationMode: "jsx",
3434
baseFontSize: 16,
35+
useTailwind4: false,
3536
};
3637

3738
// A helper type guard to ensure the key belongs to the PluginSettings type

packages/backend/src/tailwind/builderImpl/tailwindBorder.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ export const tailwindBorderRadius = (node: SceneNode): string => {
154154
return `-${r}`;
155155
}
156156
return "";
157-
// }
158157
};
159158

160159
const radius = getCommonRadius(node);

packages/backend/src/tailwind/builderImpl/tailwindColor.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
htmlRadialGradient,
1414
} from "../../html/builderImpl/htmlColor";
1515
import { GradientPaint, Paint } from "../../api_types";
16+
import { localTailwindSettings } from "../tailwindMain";
1617

1718
/**
1819
* Get a tailwind color value object
@@ -70,12 +71,23 @@ export const tailwindSolidColor = (
7071
const { colorName } = getColorInfo(fill);
7172
const effectiveOpacity = calculateEffectiveOpacity(fill);
7273

73-
// Only add opacity suffix if it's not 1.0
74-
const opacity =
75-
effectiveOpacity !== 1.0 ? `/${nearestOpacity(effectiveOpacity)}` : "";
76-
77-
// example: text-red-500, text-[#123abc], text-custom-color-700/25
78-
return `${kind}-${colorName}${opacity}`;
74+
// In Tailwind v4, we always use the slash syntax for opacity
75+
// In Tailwind v3, we use opacity utilities for standard colors and slash syntax for arbitrary values
76+
if (localTailwindSettings.useTailwind4 || colorName.startsWith('[')) {
77+
// Only add opacity suffix if it's not 1.0
78+
const opacity = effectiveOpacity !== 1.0
79+
? `/${nearestOpacity(effectiveOpacity)}`
80+
: "";
81+
82+
return `${kind}-${colorName}${opacity}`;
83+
} else {
84+
// Tailwind v3 - use separate opacity utilities for standard colors
85+
if (effectiveOpacity !== 1.0) {
86+
const opacityValue = nearestOpacity(effectiveOpacity);
87+
return `${kind}-${colorName} ${kind}-opacity-${opacityValue}`;
88+
}
89+
return `${kind}-${colorName}`;
90+
}
7991
};
8092

8193
/**

packages/backend/src/tailwind/builderImpl/tailwindShadow.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { localTailwindSettings } from "../tailwindMain";
2+
13
/**
24
* https://tailwindcss.com/docs/box-shadow/
35
* example: shadow
@@ -19,7 +21,8 @@ export const tailwindShadow = (node: BlendMixin): string[] => {
1921
d.color?.b === 0 &&
2022
Math.abs(d.color?.a - 0.05) < EPSILON
2123
) {
22-
return "shadow-sm";
24+
// shadow-sm → shadow-xs in v4
25+
return localTailwindSettings.useTailwind4 ? "shadow-xs" : "shadow-sm";
2326
} else if (
2427
d.offset?.x === 0 &&
2528
d.offset?.y === 1 &&
@@ -30,7 +33,8 @@ export const tailwindShadow = (node: BlendMixin): string[] => {
3033
d.color?.b === 0 &&
3134
Math.abs(d.color?.a - 0.1) < EPSILON
3235
) {
33-
return "shadow";
36+
// shadow → shadow-sm in v4
37+
return localTailwindSettings.useTailwind4 ? "shadow-sm" : "shadow";
3438
} else if (
3539
d.offset?.x === 0 &&
3640
d.offset?.y === 4 &&
@@ -41,6 +45,7 @@ export const tailwindShadow = (node: BlendMixin): string[] => {
4145
d.color?.b === 0 &&
4246
Math.abs(d.color?.a - 0.1) < EPSILON
4347
) {
48+
// shadow-md stays the same
4449
return "shadow-md";
4550
} else if (
4651
d.offset?.x === 0 &&

packages/backend/src/tailwind/builderImpl/tailwindSize.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { pxToLayoutSize } from "../conversionTables";
22
import { nodeSize } from "../../common/nodeWidthHeight";
33
import { numberToFixedString } from "../../common/numToAutoFixed";
44
import { TailwindSettings } from "types";
5+
import { localTailwindSettings } from "../tailwindMain";
56

67
/**
78
* Formats a size value into a Tailwind class
@@ -96,6 +97,16 @@ export const tailwindSizePartial = (
9697
);
9798
}
9899

100+
// Technically size exists since Tailwind 3.4 (December 2023), but to avoid confusion, restrict to 4,
101+
if (localTailwindSettings.useTailwind4) {
102+
const wValue = w.substring(2);
103+
const hValue = h.substring(2);
104+
if (wValue === hValue) {
105+
w = `size-${wValue}`;
106+
h = "";
107+
}
108+
}
109+
99110
return {
100111
width: w,
101112
height: h,

packages/backend/src/tailwind/conversionTables.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ export const pxToFontSize = (value: number): string => {
105105
};
106106

107107
export const pxToBorderRadius = (value: number): string => {
108-
return pxToRemToTailwind(value, config.borderRadius);
108+
const conversionMap = localTailwindSettings.useTailwind4
109+
? config.borderRadiusV4
110+
: config.borderRadius;
111+
return pxToRemToTailwind(value, conversionMap);
109112
};
110113

111114
export const pxToBorderWidth = (value: number): string | null => {
@@ -117,7 +120,10 @@ export const pxToOutline = (value: number): string | null => {
117120
};
118121

119122
export const pxToBlur = (value: number): string | null => {
120-
return pxToTailwind(value, config.blur);
123+
const conversionMap = localTailwindSettings.useTailwind4
124+
? config.blurV4
125+
: config.blur;
126+
return pxToTailwind(value, conversionMap);
121127
};
122128

123129
export const pxToLayoutSize = (value: number): string => {

packages/backend/src/tailwind/tailwindConfig.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ const borderRadius = {
4848
10: "full",
4949
};
5050

51+
// Add a set of v4 mappings for the renamed utilities
52+
const borderRadiusV4 = {
53+
0: "none",
54+
0.125: "xs", // sm -> xs
55+
0.25: "sm", // (default) -> sm
56+
0.375: "md", // unchanged
57+
0.5: "lg", // unchanged
58+
0.75: "xl", // unchanged
59+
1.0: "2xl", // unchanged
60+
1.5: "3xl", // unchanged
61+
10: "full", // unchanged
62+
};
63+
5164
const fontSize = {
5265
0.75: "xs",
5366
0.875: "sm",
@@ -98,6 +111,18 @@ const blur = {
98111
64: "3xl",
99112
};
100113

114+
// Tailwind v4 blur mapping
115+
const blurV4 = {
116+
0: "none",
117+
4: "xs", // sm -> xs
118+
8: "sm", // blur -> sm
119+
12: "md", // unchanged
120+
16: "lg", // unchanged
121+
24: "xl", // unchanged
122+
40: "2xl", // unchanged
123+
64: "3xl", // unchanged
124+
};
125+
101126
const opacity = [0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 95];
102127

103128
// AutoGenerated for Tailwind 2 via [convert_tailwind_colors.js]
@@ -405,13 +430,26 @@ const outline = {
405430
8: "8",
406431
};
407432

433+
// Tailwind v4 shadow mapping
434+
const shadowV4 = {
435+
sm: "xs", // sm -> xs
436+
DEFAULT: "sm", // (default) -> sm
437+
md: "md", // unchanged
438+
lg: "lg", // unchanged
439+
xl: "xl", // unchanged
440+
"2xl": "2xl", // unchanged
441+
};
442+
408443
export const config = {
409444
layoutSize,
410445
borderRadius,
446+
borderRadiusV4,
411447
fontSize,
412448
lineHeight,
413449
letterSpacing,
414450
blur,
451+
blurV4,
452+
shadowV4,
415453
opacity,
416454
color,
417455
fontWeight,

packages/types/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface TailwindSettings extends HTMLSettings {
1616
customTailwindPrefix?: string;
1717
embedVectors: boolean;
1818
baseFontSize: number;
19+
useTailwind4: boolean;
1920
}
2021
export interface FlutterSettings {
2122
flutterGenerationMode: string;

0 commit comments

Comments
 (0)