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; }