Skip to content

Commit c5b6df2

Browse files
Optimize generated CSS output (#14873)
This PR improves the generated CSS by running it through Lightning CSS twice.Right now Lightning CSS merges adjacent at-rules and at the end flattens the nesting. This means that after the nesting is flattened, the at-rules that are adjacent and could be merged together will not be merged. This PR improves our output by running Lightning CSS twice on the generated CSS which will make sure to merge adjacent at-rules after the nesting is flattened. Note: in the diff output you'll notice that some properties are duplicated. These need some fixes in Lightning CSS itself but they don't break anything for us right now. Related PR in Lightning CSS for the double `-webkit-backdrop-filter` can be found here: parcel-bundler/lightningcss#850 --------- Co-authored-by: Philipp Spiess <[email protected]>
1 parent d099f8b commit c5b6df2

File tree

11 files changed

+262
-1403
lines changed

11 files changed

+262
-1403
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
- Ensure instances of the same variant with different values are always sorted deterministically (e.g. `data-focus:flex` and `data-active:flex`) ([#14835](https://github.com/tailwindlabs/tailwindcss/pull/14835))
2727
- Ensure `--inset-ring=*` and `--inset-shadow-*` variables are ignored by `inset-*` utilities ([#14855](https://github.com/tailwindlabs/tailwindcss/pull/14855))
2828
- Ensure `url(…)` containing special characters such as `;` or `{}` end up in one declaration ([#14879](https://github.com/tailwindlabs/tailwindcss/pull/14879))
29+
- Ensure adjacent rules are merged together after handling nesting when generating optimized CSS ([#14873](https://github.com/tailwindlabs/tailwindcss/pull/14873))
2930
- _Upgrade (experimental)_: Install `@tailwindcss/postcss` next to `tailwindcss` ([#14830](https://github.com/tailwindlabs/tailwindcss/pull/14830))
3031
- _Upgrade (experimental)_: Remove whitespace around `,` separator when print arbitrary values ([#14838](https://github.com/tailwindlabs/tailwindcss/pull/14838))
3132

packages/@tailwindcss-cli/src/commands/build/index.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -410,22 +410,28 @@ function optimizeCss(
410410
input: string,
411411
{ file = 'input.css', minify = false }: { file?: string; minify?: boolean } = {},
412412
) {
413-
return transform({
414-
filename: file,
415-
code: Buffer.from(input),
416-
minify,
417-
sourceMap: false,
418-
drafts: {
419-
customMedia: true,
420-
},
421-
nonStandard: {
422-
deepSelectorCombinator: true,
423-
},
424-
include: Features.Nesting,
425-
exclude: Features.LogicalProperties,
426-
targets: {
427-
safari: (16 << 16) | (4 << 8),
428-
},
429-
errorRecovery: true,
430-
}).code.toString()
413+
function optimize(code: Buffer | Uint8Array) {
414+
return transform({
415+
filename: file,
416+
code,
417+
minify,
418+
sourceMap: false,
419+
drafts: {
420+
customMedia: true,
421+
},
422+
nonStandard: {
423+
deepSelectorCombinator: true,
424+
},
425+
include: Features.Nesting,
426+
exclude: Features.LogicalProperties,
427+
targets: {
428+
safari: (16 << 16) | (4 << 8),
429+
},
430+
errorRecovery: true,
431+
}).code
432+
}
433+
434+
// Running Lightning CSS twice to ensure that adjacent rules are merged after
435+
// nesting is applied. This creates a more optimized output.
436+
return optimize(optimize(Buffer.from(input))).toString()
431437
}

packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
421421
color: inherit;
422422
-webkit-text-decoration: inherit;
423423
-webkit-text-decoration: inherit;
424+
-webkit-text-decoration: inherit;
424425
text-decoration: inherit;
425426
}
426427

packages/@tailwindcss-postcss/src/index.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -218,24 +218,30 @@ function optimizeCss(
218218
input: string,
219219
{ file = 'input.css', minify = false }: { file?: string; minify?: boolean } = {},
220220
) {
221-
return transform({
222-
filename: file,
223-
code: Buffer.from(input),
224-
minify,
225-
sourceMap: false,
226-
drafts: {
227-
customMedia: true,
228-
},
229-
nonStandard: {
230-
deepSelectorCombinator: true,
231-
},
232-
include: Features.Nesting,
233-
exclude: Features.LogicalProperties,
234-
targets: {
235-
safari: (16 << 16) | (4 << 8),
236-
},
237-
errorRecovery: true,
238-
}).code.toString()
221+
function optimize(code: Buffer | Uint8Array) {
222+
return transform({
223+
filename: file,
224+
code,
225+
minify,
226+
sourceMap: false,
227+
drafts: {
228+
customMedia: true,
229+
},
230+
nonStandard: {
231+
deepSelectorCombinator: true,
232+
},
233+
include: Features.Nesting,
234+
exclude: Features.LogicalProperties,
235+
targets: {
236+
safari: (16 << 16) | (4 << 8),
237+
},
238+
errorRecovery: true,
239+
}).code
240+
}
241+
242+
// Running Lightning CSS twice to ensure that adjacent rules are merged after
243+
// nesting is applied. This creates a more optimized output.
244+
return optimize(optimize(Buffer.from(input))).toString()
239245
}
240246

241247
export default Object.assign(tailwindcss, { postcss: true }) as PluginCreator<PluginOptions>

packages/@tailwindcss-vite/src/index.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -277,24 +277,30 @@ function optimizeCss(
277277
input: string,
278278
{ file = 'input.css', minify = false }: { file?: string; minify?: boolean } = {},
279279
) {
280-
return transform({
281-
filename: file,
282-
code: Buffer.from(input),
283-
minify,
284-
sourceMap: false,
285-
drafts: {
286-
customMedia: true,
287-
},
288-
nonStandard: {
289-
deepSelectorCombinator: true,
290-
},
291-
include: Features.Nesting,
292-
exclude: Features.LogicalProperties,
293-
targets: {
294-
safari: (16 << 16) | (4 << 8),
295-
},
296-
errorRecovery: true,
297-
}).code.toString()
280+
function optimize(code: Buffer | Uint8Array) {
281+
return transform({
282+
filename: file,
283+
code,
284+
minify,
285+
sourceMap: false,
286+
drafts: {
287+
customMedia: true,
288+
},
289+
nonStandard: {
290+
deepSelectorCombinator: true,
291+
},
292+
include: Features.Nesting,
293+
exclude: Features.LogicalProperties,
294+
targets: {
295+
safari: (16 << 16) | (4 << 8),
296+
},
297+
errorRecovery: true,
298+
}).code
299+
}
300+
301+
// Running Lightning CSS twice to ensure that adjacent rules are merged after
302+
// nesting is applied. This creates a more optimized output.
303+
return optimize(optimize(Buffer.from(input))).toString()
298304
}
299305

300306
function idToPath(id: string) {

packages/tailwindcss/src/compat/plugin-api.test.ts

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,11 +1592,7 @@ describe('addVariant', () => {
15921592
.potato\\:flex:large-potato {
15931593
display: flex;
15941594
}
1595-
}
1596-
}
15971595
1598-
@media (width <= 400px) {
1599-
@supports (font: bold) {
16001596
.potato\\:underline:large-potato {
16011597
text-decoration-line: underline;
16021598
}
@@ -1840,19 +1836,7 @@ describe('matchVariant', () => {
18401836

18411837
expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`
18421838
"@layer utilities {
1843-
.alphabet-d\\:underline[data-order="1"] {
1844-
text-decoration-line: underline;
1845-
}
1846-
1847-
.alphabet-a\\:underline[data-order="2"] {
1848-
text-decoration-line: underline;
1849-
}
1850-
1851-
.alphabet-c\\:underline[data-order="3"] {
1852-
text-decoration-line: underline;
1853-
}
1854-
1855-
.alphabet-b\\:underline[data-order="4"] {
1839+
.alphabet-d\\:underline[data-order="1"], .alphabet-a\\:underline[data-order="2"], .alphabet-c\\:underline[data-order="3"], .alphabet-b\\:underline[data-order="4"] {
18561840
text-decoration-line: underline;
18571841
}
18581842
}"
@@ -2059,9 +2043,7 @@ describe('matchVariant', () => {
20592043
order: 3;
20602044
}
20612045
}
2062-
}
20632046
2064-
@media (width >= 100px) {
20652047
@media (width <= 300px) {
20662048
.testmin-\\[100px\\]\\:testmax-\\[300px\\]\\:order-4 {
20672049
order: 4;
@@ -2116,11 +2098,7 @@ describe('matchVariant', () => {
21162098
text-decoration-line: underline;
21172099
}
21182100
}
2119-
}
2120-
}
21212101
2122-
@media (width >= 100px) {
2123-
@media (width <= 200px) {
21242102
.testmin-\\[100px\\]\\:testmax-\\[200px\\]\\:focus\\:underline:focus {
21252103
text-decoration-line: underline;
21262104
}
@@ -2245,9 +2223,7 @@ describe('matchVariant', () => {
22452223
text-decoration-line: underline;
22462224
}
22472225
}
2248-
}
22492226
2250-
@media (width <= 400px) {
22512227
@media (width >= 200px) {
22522228
.testmax-\\[400px\\]\\:testmin-\\[200px\\]\\:underline {
22532229
text-decoration-line: underline;
@@ -2261,9 +2237,7 @@ describe('matchVariant', () => {
22612237
text-decoration-line: underline;
22622238
}
22632239
}
2264-
}
22652240
2266-
@media (width <= 300px) {
22672241
@media (width >= 200px) {
22682242
.testmax-\\[300px\\]\\:testmin-\\[200px\\]\\:underline {
22692243
text-decoration-line: underline;
@@ -2870,11 +2844,7 @@ describe('addUtilities()', () => {
28702844

28712845
expect(optimizeCss(compiled.build(['form-input', 'lg:form-textarea'])).trim())
28722846
.toMatchInlineSnapshot(`
2873-
".form-input {
2874-
background-color: red;
2875-
}
2876-
2877-
.form-input::placeholder {
2847+
".form-input, .form-input::placeholder {
28782848
background-color: red;
28792849
}
28802850
@@ -3610,9 +3580,7 @@ describe('matchUtilities()', () => {
36103580
--foo: 12px;
36113581
display: flex;
36123582
}
3613-
}
36143583
3615-
@media (width >= 1024px) {
36163584
.lg\\:foo-bar {
36173585
--foo: bar;
36183586
display: flex;

0 commit comments

Comments
 (0)