diff --git a/CHANGELOG.md b/CHANGELOG.md index 19ecfd3dec0d..3f7f160d899f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Ensure `color-mix(…)` polyfills do not cause used CSS variables to be removed ([#17555](https://github.com/tailwindlabs/tailwindcss/pull/17555)) ## [4.1.3] - 2025-04-04 diff --git a/packages/tailwindcss/src/ast.ts b/packages/tailwindcss/src/ast.ts index af76b0772faf..f15191118d68 100644 --- a/packages/tailwindcss/src/ast.ts +++ b/packages/tailwindcss/src/ast.ts @@ -266,10 +266,8 @@ export function optimizeAst( ) { let atRoots: AstNode[] = [] let seenAtProperties = new Set() - let cssThemeVariables = new DefaultMap< - Extract['nodes'], - Set - >(() => new Set()) + let cssThemeVariables = new DefaultMap>(() => new Set()) + let colorMixDeclarations = new DefaultMap>(() => new Set()) let keyframes = new Set() let usedKeyframeNames = new Set() @@ -280,7 +278,7 @@ export function optimizeAst( function transform( node: AstNode, - parent: Extract['nodes'], + parent: AstNode[], context: Record = {}, depth = 0, ) { @@ -326,71 +324,7 @@ export function optimizeAst( // Create fallback values for usages of the `color-mix(…)` function that reference variables // found in the theme config. if (polyfills & Polyfills.ColorMix && node.value.includes('color-mix(')) { - let ast = ValueParser.parse(node.value) - - let requiresPolyfill = false - ValueParser.walk(ast, (node, { replaceWith }) => { - if (node.kind !== 'function' || node.value !== 'color-mix') return - - let containsUnresolvableVars = false - let containsCurrentcolor = false - ValueParser.walk(node.nodes, (node, { replaceWith }) => { - if (node.kind == 'word' && node.value.toLowerCase() === 'currentcolor') { - containsCurrentcolor = true - requiresPolyfill = true - return - } - if (node.kind !== 'function' || node.value !== 'var') return - let firstChild = node.nodes[0] - if (!firstChild || firstChild.kind !== 'word') return - - requiresPolyfill = true - - let inlinedColor = designSystem.theme.resolveValue(null, [firstChild.value as any]) - if (!inlinedColor) { - containsUnresolvableVars = true - return - } - - replaceWith({ kind: 'word', value: inlinedColor }) - }) - - if (containsUnresolvableVars || containsCurrentcolor) { - let separatorIndex = node.nodes.findIndex( - (node) => node.kind === 'separator' && node.value.trim().includes(','), - ) - if (separatorIndex === -1) return - let firstColorValue = - node.nodes.length > separatorIndex ? node.nodes[separatorIndex + 1] : null - if (!firstColorValue) return - replaceWith(firstColorValue) - } else if (requiresPolyfill) { - // Change the colorspace to `srgb` since the fallback values should not be represented as - // `oklab(…)` functions again as their support in Safari <16 is very limited. - let colorspace = node.nodes[2] - if ( - colorspace.kind === 'word' && - (colorspace.value === 'oklab' || - colorspace.value === 'oklch' || - colorspace.value === 'lab' || - colorspace.value === 'lch') - ) { - colorspace.value = 'srgb' - } - } - }) - - if (requiresPolyfill) { - let fallback = { - ...node, - value: ValueParser.toCss(ast), - } - let colorMixQuery = rule('@supports (color: color-mix(in lab, red, red))', [node]) - - parent.push(fallback, colorMixQuery) - - return - } + colorMixDeclarations.get(parent).add(node) } parent.push(node) @@ -595,6 +529,74 @@ export function optimizeAst( newAst = newAst.concat(atRoots) // Fallbacks + // Create fallback values for usages of the `color-mix(…)` function that reference variables + // found in the theme config. + if (polyfills & Polyfills.ColorMix) { + for (let [parent, declarations] of colorMixDeclarations) { + for (let declaration of declarations) { + let idx = parent.indexOf(declaration) + // If the declaration is no longer present, we don't need to create a polyfill anymore + if (idx === -1 || declaration.value == null) continue + + let ast = ValueParser.parse(declaration.value) + let requiresPolyfill = false + ValueParser.walk(ast, (node, { replaceWith }) => { + if (node.kind !== 'function' || node.value !== 'color-mix') return + let containsUnresolvableVars = false + let containsCurrentcolor = false + ValueParser.walk(node.nodes, (node, { replaceWith }) => { + if (node.kind == 'word' && node.value.toLowerCase() === 'currentcolor') { + containsCurrentcolor = true + requiresPolyfill = true + return + } + if (node.kind !== 'function' || node.value !== 'var') return + let firstChild = node.nodes[0] + if (!firstChild || firstChild.kind !== 'word') return + requiresPolyfill = true + let inlinedColor = designSystem.theme.resolveValue(null, [firstChild.value as any]) + if (!inlinedColor) { + containsUnresolvableVars = true + return + } + replaceWith({ kind: 'word', value: inlinedColor }) + }) + if (containsUnresolvableVars || containsCurrentcolor) { + let separatorIndex = node.nodes.findIndex( + (node) => node.kind === 'separator' && node.value.trim().includes(','), + ) + if (separatorIndex === -1) return + let firstColorValue = + node.nodes.length > separatorIndex ? node.nodes[separatorIndex + 1] : null + if (!firstColorValue) return + replaceWith(firstColorValue) + } else if (requiresPolyfill) { + // Change the colorspace to `srgb` since the fallback values should not be represented as + // `oklab(…)` functions again as their support in Safari <16 is very limited. + let colorspace = node.nodes[2] + if ( + colorspace.kind === 'word' && + (colorspace.value === 'oklab' || + colorspace.value === 'oklch' || + colorspace.value === 'lab' || + colorspace.value === 'lch') + ) { + colorspace.value = 'srgb' + } + } + }) + if (!requiresPolyfill) continue + + let fallback = { + ...declaration, + value: ValueParser.toCss(ast), + } + let colorMixQuery = rule('@supports (color: color-mix(in lab, red, red))', [declaration]) + parent.splice(idx, 1, fallback, colorMixQuery) + } + } + } + if (polyfills & Polyfills.AtProperty) { let fallbackAst = [] diff --git a/packages/tailwindcss/src/compat/config.test.ts b/packages/tailwindcss/src/compat/config.test.ts index b68e6dcff2bb..6bfccb1e3bc1 100644 --- a/packages/tailwindcss/src/compat/config.test.ts +++ b/packages/tailwindcss/src/compat/config.test.ts @@ -1164,21 +1164,21 @@ test('utilities must be prefixed', async () => { // Prefixed utilities are generated expect(compiler.build(['tw:underline', 'tw:hover:line-through', 'tw:custom'])) .toMatchInlineSnapshot(` - ".tw\\:custom { - color: red; - } - .tw\\:underline { - text-decoration-line: underline; - } - .tw\\:hover\\:line-through { - &:hover { - @media (hover: hover) { - text-decoration-line: line-through; + ".tw\\:custom { + color: red; + } + .tw\\:underline { + text-decoration-line: underline; + } + .tw\\:hover\\:line-through { + &:hover { + @media (hover: hover) { + text-decoration-line: line-through; + } } } - } - " - `) + " + `) // Non-prefixed utilities are ignored compiler = await compile(input, { diff --git a/packages/tailwindcss/src/compat/plugin-api.test.ts b/packages/tailwindcss/src/compat/plugin-api.test.ts index dc415eb1a8c2..ca7d96dee648 100644 --- a/packages/tailwindcss/src/compat/plugin-api.test.ts +++ b/packages/tailwindcss/src/compat/plugin-api.test.ts @@ -243,14 +243,14 @@ describe('theme', async () => { expect(compiler.build(['animate-duration-316', 'animate-duration-slow'])) .toMatchInlineSnapshot(` - ".animate-duration-316 { - animation-duration: 316ms; - } - .animate-duration-slow { - animation-duration: 800ms; - } - " - `) + ".animate-duration-316 { + animation-duration: 316ms; + } + .animate-duration-slow { + animation-duration: 800ms; + } + " + `) }) test('plugin theme can have opacity modifiers', async () => { @@ -3340,16 +3340,16 @@ describe('matchUtilities()', () => { } expect(optimizeCss(await run(['@w-1', 'hover:@w-1'])).trim()).toMatchInlineSnapshot(` - ".\\@w-1 { + ".\\@w-1 { + width: 1px; + } + + @media (hover: hover) { + .hover\\:\\@w-1:hover { width: 1px; } - - @media (hover: hover) { - .hover\\:\\@w-1:hover { - width: 1px; - } - }" - `) + }" + `) }) test('custom functional utilities can return an array of rules', async () => { @@ -4153,30 +4153,30 @@ describe('addComponents()', () => { expect(optimizeCss(compiled.build(['btn', 'btn-blue', 'btn-red'])).trim()) .toMatchInlineSnapshot(` - ".btn { - border-radius: .25rem; - padding: .5rem 1rem; - font-weight: 600; - } + ".btn { + border-radius: .25rem; + padding: .5rem 1rem; + font-weight: 600; + } - .btn-blue { - color: #fff; - background-color: #3490dc; - } + .btn-blue { + color: #fff; + background-color: #3490dc; + } - .btn-blue:hover { - background-color: #2779bd; - } + .btn-blue:hover { + background-color: #2779bd; + } - .btn-red { - color: #fff; - background-color: #e3342f; - } + .btn-red { + color: #fff; + background-color: #e3342f; + } - .btn-red:hover { - background-color: #cc1f1a; - }" - `) + .btn-red:hover { + background-color: #cc1f1a; + }" + `) }) }) @@ -4212,16 +4212,16 @@ describe('matchComponents()', () => { expect(optimizeCss(compiled.build(['prose', 'sm:prose-sm', 'hover:prose-lg'])).trim()) .toMatchInlineSnapshot(` - ".prose { - --container-size: normal; - } - - @media (hover: hover) { - .hover\\:prose-lg:hover { - --container-size: lg; + ".prose { + --container-size: normal; } - }" - `) + + @media (hover: hover) { + .hover\\:prose-lg:hover { + --container-size: lg; + } + }" + `) }) }) diff --git a/packages/tailwindcss/src/css-functions.test.ts b/packages/tailwindcss/src/css-functions.test.ts index 37747e32f751..f63682f63e01 100644 --- a/packages/tailwindcss/src/css-functions.test.ts +++ b/packages/tailwindcss/src/css-functions.test.ts @@ -756,10 +756,10 @@ describe('theme(…)', () => { } `), ).toMatchInlineSnapshot(` - ".fam { - font-family: ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; - }" - `) + ".fam { + font-family: ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + }" + `) }) test('theme(fontFamily.sans) (config)', async () => { @@ -776,10 +776,10 @@ describe('theme(…)', () => { ) expect(optimizeCss(compiled.build([])).trim()).toMatchInlineSnapshot(` - ".fam { - font-family: ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; - }" - `) + ".fam { + font-family: ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + }" + `) }) }) diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index 14aaf18c7c67..707e8f02c875 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -5047,6 +5047,150 @@ describe('`color-mix(…)` polyfill', () => { }" `) }) + + it('does not delete theme variables from the output', async () => { + await expect( + compileCss( + css` + @layer theme { + @theme { + --color-red-500: red; + --shadow-xl: 0 6px 18px 4px color-mix(in oklab, var(--color-red-500) 25%, transparent); + --opacity-disabled: 50%; + } + } + @tailwind utilities; + `, + ['text-red-500', 'shadow-xl', 'opacity-disabled'], + ), + ).resolves.toMatchInlineSnapshot(` + "@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, :before, :after, ::backdrop { + --tw-shadow: 0 0 #0000; + --tw-shadow-color: initial; + --tw-shadow-alpha: 100%; + --tw-inset-shadow: 0 0 #0000; + --tw-inset-shadow-color: initial; + --tw-inset-shadow-alpha: 100%; + --tw-ring-color: initial; + --tw-ring-shadow: 0 0 #0000; + --tw-inset-ring-color: initial; + --tw-inset-ring-shadow: 0 0 #0000; + --tw-ring-inset: initial; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-offset-shadow: 0 0 #0000; + } + } + } + + @layer theme { + :root, :host { + --color-red-500: red; + --opacity-disabled: 50%; + } + } + + .text-red-500 { + color: var(--color-red-500); + } + + .opacity-disabled { + opacity: var(--opacity-disabled); + } + + .shadow-xl { + --tw-shadow: 0 6px 18px 4px var(--tw-shadow-color, #ff000040); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + + @supports (color: color-mix(in lab, red, red)) { + .shadow-xl { + --tw-shadow: 0 6px 18px 4px var(--tw-shadow-color, color-mix(in oklab, var(--color-red-500) 25%, transparent)); + } + } + + @property --tw-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; + } + + @property --tw-shadow-color { + syntax: "*"; + inherits: false + } + + @property --tw-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; + } + + @property --tw-inset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; + } + + @property --tw-inset-shadow-color { + syntax: "*"; + inherits: false + } + + @property --tw-inset-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; + } + + @property --tw-ring-color { + syntax: "*"; + inherits: false + } + + @property --tw-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; + } + + @property --tw-inset-ring-color { + syntax: "*"; + inherits: false + } + + @property --tw-inset-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; + } + + @property --tw-ring-inset { + syntax: "*"; + inherits: false + } + + @property --tw-ring-offset-width { + syntax: ""; + inherits: false; + initial-value: 0; + } + + @property --tw-ring-offset-color { + syntax: "*"; + inherits: false; + initial-value: #fff; + } + + @property --tw-ring-offset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; + }" + `) + }) }) describe('`@property` polyfill', async () => { diff --git a/packages/tailwindcss/src/utilities.test.ts b/packages/tailwindcss/src/utilities.test.ts index c0286428f2d7..c3af45d3b7ef 100644 --- a/packages/tailwindcss/src/utilities.test.ts +++ b/packages/tailwindcss/src/utilities.test.ts @@ -1319,26 +1319,26 @@ test('row-start', async () => { expect( await run(['row-start-auto', 'row-start-4', 'row-start-99', 'row-start-[123]', '-row-start-4']), ).toMatchInlineSnapshot(` - ".-row-start-4 { - grid-row-start: calc(4 * -1); - } + ".-row-start-4 { + grid-row-start: calc(4 * -1); + } - .row-start-4 { - grid-row-start: 4; - } + .row-start-4 { + grid-row-start: 4; + } - .row-start-99 { - grid-row-start: 99; - } + .row-start-99 { + grid-row-start: 99; + } - .row-start-\\[123\\] { - grid-row-start: 123; - } + .row-start-\\[123\\] { + grid-row-start: 123; + } - .row-start-auto { - grid-row-start: auto; - }" - `) + .row-start-auto { + grid-row-start: auto; + }" + `) expect( await run([ 'row-start', @@ -4437,22 +4437,22 @@ test('translate-3d', async () => { test('rotate', async () => { expect(await run(['rotate-45', '-rotate-45', 'rotate-[123deg]', 'rotate-[0.3_0.7_1_45deg]'])) .toMatchInlineSnapshot(` - ".-rotate-45 { - rotate: -45deg; - } + ".-rotate-45 { + rotate: -45deg; + } - .rotate-45 { - rotate: 45deg; - } + .rotate-45 { + rotate: 45deg; + } - .rotate-\\[0\\.3_0\\.7_1_45deg\\] { - rotate: .3 .7 1 45deg; - } + .rotate-\\[0\\.3_0\\.7_1_45deg\\] { + rotate: .3 .7 1 45deg; + } - .rotate-\\[123deg\\] { - rotate: 123deg; - }" - `) + .rotate-\\[123deg\\] { + rotate: 123deg; + }" + `) expect( await run([ 'rotate', @@ -5316,42 +5316,42 @@ test('transform', async () => { 'backface-hidden', ]), ).toMatchInlineSnapshot(` - ".backface-hidden { - backface-visibility: hidden; - } + ".backface-hidden { + backface-visibility: hidden; + } - .backface-visible { - backface-visibility: visible; - } + .backface-visible { + backface-visibility: visible; + } - .transform-3d { - transform-style: preserve-3d; - } + .transform-3d { + transform-style: preserve-3d; + } - .transform-border { - transform-box: border-box; - } + .transform-border { + transform-box: border-box; + } - .transform-content { - transform-box: content-box; - } + .transform-content { + transform-box: content-box; + } - .transform-fill { - transform-box: fill-box; - } + .transform-fill { + transform-box: fill-box; + } - .transform-flat { - transform-style: flat; - } + .transform-flat { + transform-style: flat; + } - .transform-stroke { - transform-box: stroke-box; - } + .transform-stroke { + transform-box: stroke-box; + } - .transform-view { - transform-box: view-box; - }" - `) + .transform-view { + transform-box: view-box; + }" + `) expect( await run([ '-transform', @@ -5862,26 +5862,26 @@ test('touch-pinch-zoom', async () => { test('select', async () => { expect(await run(['select-none', 'select-text', 'select-all', 'select-auto'])) .toMatchInlineSnapshot(` - ".select-all { - -webkit-user-select: all; - user-select: all; - } + ".select-all { + -webkit-user-select: all; + user-select: all; + } - .select-auto { - -webkit-user-select: auto; - user-select: auto; - } + .select-auto { + -webkit-user-select: auto; + user-select: auto; + } - .select-none { - -webkit-user-select: none; - user-select: none; - } + .select-none { + -webkit-user-select: none; + user-select: none; + } - .select-text { - -webkit-user-select: text; - user-select: text; - }" - `) + .select-text { + -webkit-user-select: text; + user-select: text; + }" + `) expect( await run([ '-select-none', @@ -6006,22 +6006,22 @@ test('--tw-scroll-snap-strictness', async () => { test('scroll-snap-align', async () => { expect(await run(['snap-align-none', 'snap-start', 'snap-end', 'snap-center'])) .toMatchInlineSnapshot(` - ".snap-align-none { - scroll-snap-align: none; - } + ".snap-align-none { + scroll-snap-align: none; + } - .snap-center { - scroll-snap-align: center; - } + .snap-center { + scroll-snap-align: center; + } - .snap-end { - scroll-snap-align: end; - } + .snap-end { + scroll-snap-align: end; + } - .snap-start { - scroll-snap-align: start; - }" - `) + .snap-start { + scroll-snap-align: start; + }" + `) expect( await run([ '-snap-align-none', @@ -7375,22 +7375,22 @@ test('grid-rows', async () => { test('flex-direction', async () => { expect(await run(['flex-row', 'flex-row-reverse', 'flex-col', 'flex-col-reverse'])) .toMatchInlineSnapshot(` - ".flex-col { - flex-direction: column; - } + ".flex-col { + flex-direction: column; + } - .flex-col-reverse { - flex-direction: column-reverse; - } + .flex-col-reverse { + flex-direction: column-reverse; + } - .flex-row { - flex-direction: row; - } + .flex-row { + flex-direction: row; + } - .flex-row-reverse { - flex-direction: row-reverse; - }" - `) + .flex-row-reverse { + flex-direction: row-reverse; + }" + `) expect( await run([ '-flex-row', @@ -8363,31 +8363,31 @@ test('divide-style', async () => { expect( await run(['divide-solid', 'divide-dashed', 'divide-dotted', 'divide-double', 'divide-none']), ).toMatchInlineSnapshot(` - ":where(.divide-dashed > :not(:last-child)) { - --tw-border-style: dashed; - border-style: dashed; - } + ":where(.divide-dashed > :not(:last-child)) { + --tw-border-style: dashed; + border-style: dashed; + } - :where(.divide-dotted > :not(:last-child)) { - --tw-border-style: dotted; - border-style: dotted; - } + :where(.divide-dotted > :not(:last-child)) { + --tw-border-style: dotted; + border-style: dotted; + } - :where(.divide-double > :not(:last-child)) { - --tw-border-style: double; - border-style: double; - } + :where(.divide-double > :not(:last-child)) { + --tw-border-style: double; + border-style: double; + } - :where(.divide-none > :not(:last-child)) { - --tw-border-style: none; - border-style: none; - } + :where(.divide-none > :not(:last-child)) { + --tw-border-style: none; + border-style: none; + } - :where(.divide-solid > :not(:last-child)) { - --tw-border-style: solid; - border-style: solid; - }" - `) + :where(.divide-solid > :not(:last-child)) { + --tw-border-style: solid; + border-style: solid; + }" + `) expect( await run([ 'divide', @@ -9274,18 +9274,18 @@ test('overflow-y', async () => { test('overscroll', async () => { expect(await run(['overscroll-auto', 'overscroll-contain', 'overscroll-none'])) .toMatchInlineSnapshot(` - ".overscroll-auto { - overscroll-behavior: auto; - } + ".overscroll-auto { + overscroll-behavior: auto; + } - .overscroll-contain { - overscroll-behavior: contain; - } + .overscroll-contain { + overscroll-behavior: contain; + } - .overscroll-none { - overscroll-behavior: none; - }" - `) + .overscroll-none { + overscroll-behavior: none; + }" + `) expect( await run([ 'overscroll', @@ -9302,18 +9302,18 @@ test('overscroll', async () => { test('overscroll-x', async () => { expect(await run(['overscroll-x-auto', 'overscroll-x-contain', 'overscroll-x-none'])) .toMatchInlineSnapshot(` - ".overscroll-x-auto { - overscroll-behavior-x: auto; - } + ".overscroll-x-auto { + overscroll-behavior-x: auto; + } - .overscroll-x-contain { - overscroll-behavior-x: contain; - } + .overscroll-x-contain { + overscroll-behavior-x: contain; + } - .overscroll-x-none { - overscroll-behavior-x: none; - }" - `) + .overscroll-x-none { + overscroll-behavior-x: none; + }" + `) expect( await run([ 'overscroll-x', @@ -9330,18 +9330,18 @@ test('overscroll-x', async () => { test('overscroll-y', async () => { expect(await run(['overscroll-y-auto', 'overscroll-y-contain', 'overscroll-y-none'])) .toMatchInlineSnapshot(` - ".overscroll-y-auto { - overscroll-behavior-y: auto; - } + ".overscroll-y-auto { + overscroll-behavior-y: auto; + } - .overscroll-y-contain { - overscroll-behavior-y: contain; - } + .overscroll-y-contain { + overscroll-behavior-y: contain; + } - .overscroll-y-none { - overscroll-behavior-y: none; - }" - `) + .overscroll-y-none { + overscroll-behavior-y: none; + }" + `) expect( await run([ 'overscroll-y', @@ -9483,22 +9483,22 @@ test('whitespace', async () => { test('text-wrap', async () => { expect(await run(['text-wrap', 'text-nowrap', 'text-balance', 'text-pretty'])) .toMatchInlineSnapshot(` - ".text-balance { - text-wrap: balance; - } + ".text-balance { + text-wrap: balance; + } - .text-nowrap { - text-wrap: nowrap; - } + .text-nowrap { + text-wrap: nowrap; + } - .text-pretty { - text-wrap: pretty; - } + .text-pretty { + text-wrap: pretty; + } - .text-wrap { - text-wrap: wrap; - }" - `) + .text-wrap { + text-wrap: wrap; + }" + `) expect( await run([ '-text-wrap', @@ -18604,18 +18604,18 @@ test('bg-clip', async () => { test('bg-origin', async () => { expect(await run(['bg-origin-border', 'bg-origin-padding', 'bg-origin-content'])) .toMatchInlineSnapshot(` - ".bg-origin-border { - background-origin: border-box; - } + ".bg-origin-border { + background-origin: border-box; + } - .bg-origin-content { - background-origin: content-box; - } + .bg-origin-content { + background-origin: content-box; + } - .bg-origin-padding { - background-origin: padding-box; - }" - `) + .bg-origin-padding { + background-origin: padding-box; + }" + `) expect( await run([ 'bg-origin', @@ -20307,22 +20307,22 @@ test('font-stretch', async () => { test('text-decoration-line', async () => { expect(await run(['underline', 'overline', 'line-through', 'no-underline'])) .toMatchInlineSnapshot(` - ".line-through { - text-decoration-line: line-through; - } + ".line-through { + text-decoration-line: line-through; + } - .no-underline { - text-decoration-line: none; - } + .no-underline { + text-decoration-line: none; + } - .overline { - text-decoration-line: overline; - } + .overline { + text-decoration-line: overline; + } - .underline { - text-decoration-line: underline; - }" - `) + .underline { + text-decoration-line: underline; + }" + `) expect( await run([ '-underline', @@ -22158,14 +22158,14 @@ test('content', async () => { test('forced-color-adjust', async () => { expect(await run(['forced-color-adjust-none', 'forced-color-adjust-auto'])) .toMatchInlineSnapshot(` - ".forced-color-adjust-auto { - forced-color-adjust: auto; - } + ".forced-color-adjust-auto { + forced-color-adjust: auto; + } - .forced-color-adjust-none { - forced-color-adjust: none; - }" - `) + .forced-color-adjust-none { + forced-color-adjust: none; + }" + `) expect( await run([ 'forced', @@ -26588,22 +26588,22 @@ describe('custom utilities', () => { expect(await compileCss(input, ['example-1', 'example-0.5', 'example-20%', 'example-2/3'])) .toMatchInlineSnapshot(` - ".example-0\\.5 { - --value-as-number: .5; - } + ".example-0\\.5 { + --value-as-number: .5; + } - .example-1 { - --value-as-number: 1; - } + .example-1 { + --value-as-number: 1; + } - .example-2\\/3 { - --value-as-ratio: 2 / 3; - } + .example-2\\/3 { + --value-as-ratio: 2 / 3; + } - .example-20\\% { - --value-as-percentage: 20%; - }" - `) + .example-20\\% { + --value-as-percentage: 20%; + }" + `) expect( await compileCss(input, [ 'example-1.23',