diff --git a/apps/builder/app/shared/tailwind/tailwind.test.tsx b/apps/builder/app/shared/tailwind/tailwind.test.tsx
index 19cc8e3df0c8..5d2b0c7adb3b 100644
--- a/apps/builder/app/shared/tailwind/tailwind.test.tsx
+++ b/apps/builder/app/shared/tailwind/tailwind.test.tsx
@@ -511,6 +511,65 @@ describe("extract breakpoints", () => {
)
);
});
+
+ test("adapt max-* breakpoints", async () => {
+ expect(
+ await generateFragmentFromTailwind(
+ renderTemplate(
+
+ )
+ )
+ ).toEqual(
+ renderTemplate(
+
+ )
+ );
+ });
+
+ test("ignore composite breakpoints", async () => {
+ expect(
+ await generateFragmentFromTailwind(
+ renderTemplate(
+
+ )
+ )
+ ).toEqual(
+ renderTemplate(
+
+ )
+ );
+ });
});
test("generate space without display property", async () => {
diff --git a/apps/builder/app/shared/tailwind/tailwind.ts b/apps/builder/app/shared/tailwind/tailwind.ts
index 2844faa68c42..834446472262 100644
--- a/apps/builder/app/shared/tailwind/tailwind.ts
+++ b/apps/builder/app/shared/tailwind/tailwind.ts
@@ -27,11 +27,13 @@ const availableBreakpoints = [
];
const tailwindToWebstudioMappings: Record = {
- 639: 479,
+ 639.9: 479,
640: 480,
- 1023: 991,
+ 767.9: 767,
+ 1023.9: 991,
1024: 992,
- 1535: 1439,
+ 1279.9: 1279,
+ 1535.9: 1439,
1536: 1440,
};
@@ -120,8 +122,10 @@ const rangesToBreakpoints = (ranges: Range[]) => {
const adaptBreakpoints = (
parsedStyles: Omit[]
) => {
+ const newStyles: typeof parsedStyles = [];
const breakpointGroups = new Map();
for (const styleDecl of parsedStyles) {
+ newStyles.push(styleDecl);
const mediaQuery = styleDecl.breakpoint
? parseMediaQuery(styleDecl.breakpoint)
: undefined;
@@ -155,13 +159,14 @@ const adaptBreakpoints = (
breakpointsByKey.set(breakpoint.key, breakpoint);
}
}
- for (const styleDecl of parsedStyles) {
+ for (const styleDecl of newStyles) {
const styleDeclKey = `${styleDecl.breakpoint ?? ""}:${styleDecl.property}:${styleDecl.state ?? ""}`;
const breakpoint = breakpointsByKey.get(styleDeclKey);
if (breakpoint) {
styleDecl.breakpoint = serializeBreakpoint(breakpoint);
}
}
+ return newStyles;
};
const createUnoGenerator = async () => {
diff --git a/packages/css-data/src/parse-css.test.ts b/packages/css-data/src/parse-css.test.ts
index f9191ffa2e46..50b1b00d0442 100644
--- a/packages/css-data/src/parse-css.test.ts
+++ b/packages/css-data/src/parse-css.test.ts
@@ -714,6 +714,30 @@ test("ignore unsupported media queries", () => {
]);
});
+test("ignore nested media queries", () => {
+ expect(
+ parseCss(`
+ @media (min-width: 768px) {
+ a {
+ color: green;
+ }
+ @media (max-width: 1024px) {
+ a {
+ color: red;
+ }
+ }
+ }
+ `)
+ ).toEqual([
+ {
+ breakpoint: "(min-width:768px)",
+ selector: "a",
+ property: "color",
+ value: { type: "keyword", value: "green" },
+ },
+ ]);
+});
+
test("ignore unsupported at rules", () => {
expect(
parseCss(`
diff --git a/packages/css-data/src/parse-css.ts b/packages/css-data/src/parse-css.ts
index ce6a0a0692c7..5af9cf0ed6e1 100644
--- a/packages/css-data/src/parse-css.ts
+++ b/packages/css-data/src/parse-css.ts
@@ -103,6 +103,12 @@ export const parseCss = (css: string): ParsedStyleDecl[] => {
}
csstree.walk(ast, function (node) {
+ // forbid nested at rules
+ if (node.type === "Atrule" && this.atrule) {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore https://github.com/csstree/csstree/blob/v2.3.1/docs/traversal.md
+ return this.break;
+ }
if (node.type !== "Declaration" || this.rule?.prelude.type === undefined) {
return;
}