Skip to content

Commit 4484192

Browse files
Use @layer properties for @property polyfills (#17506)
This PR changes how polyfills for `@property` are inserted. The main motivation is to remove the need to rely on the correct placement of `@layer base;`—Something that's not really required right not in Tailwind CSS v4 and we'd like to keep it this way. The idea is that the polyfills are inserted for you automatically. To ensure they always take precedence, we insert an empty `@layer properties;` at the top of the CSS file so that later, when we emit all `@property` rules and their fallback, we can use this new named layer to ensure the rules have a higher order. Unfortunately, just putting `@layer properties;` at the beginning of a file would not work as `lightningcss` incorrectly hoists all content into the first occurrence of a layer name meaning these rules might be inserted _before_ eventual external imports: ![image](https://github.com/user-attachments/assets/c5a1694d-1549-47ed-ad0f-266807be4730) To work around this, we have to insert that layer name after any eventual remaining external `@imports` for now. ## Test plan - Updated snapshot tests - Deployed a new version of the website with the patch applied to ensure it works across browsers: https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/. Tested on: Safari on iOS 15.5, Safari on iOS 16.0, Firefox 127, Firefox 128, Chrome 110, Chrome latest, Safari latest, Firefox latest
1 parent 6a0a3ec commit 4484192

File tree

12 files changed

+320
-301
lines changed

12 files changed

+320
-301
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Fixed
11+
12+
- Don't rely on `@layer base` for the `@property` polyfills ([#17506](https://github.com/tailwindlabs/tailwindcss/pull/17506))
1113

1214
## [4.1.1] - 2025-04-02
1315

integrations/cli/index.test.ts

+45-41
Original file line numberDiff line numberDiff line change
@@ -713,13 +713,7 @@ test(
713713
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
714714
"
715715
--- ./dist/out.css ---
716-
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
717-
@layer base {
718-
*, ::before, ::after, ::backdrop {
719-
--tw-content: "";
720-
}
721-
}
722-
}
716+
@layer properties;
723717
.content-\\[\\"components\\/my-component\\.tsx\\"\\] {
724718
--tw-content: "components/my-component.tsx";
725719
content: var(--tw-content);
@@ -761,6 +755,13 @@ test(
761755
inherits: false;
762756
initial-value: "";
763757
}
758+
@layer properties {
759+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
760+
*, ::before, ::after, ::backdrop {
761+
--tw-content: "";
762+
}
763+
}
764+
}
764765
"
765766
`)
766767
},
@@ -951,13 +952,7 @@ test(
951952
expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(`
952953
"
953954
--- ./project-a/dist/out.css ---
954-
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
955-
@layer base {
956-
*, ::before, ::after, ::backdrop {
957-
--tw-content: "";
958-
}
959-
}
960-
}
955+
@layer properties;
961956
.content-\\[\\'project-a\\/node_modules\\/my-lib-1\\/src\\/index\\.html\\'\\] {
962957
--tw-content: 'project-a/node modules/my-lib-1/src/index.html';
963958
content: var(--tw-content);
@@ -1011,6 +1006,13 @@ test(
10111006
inherits: false;
10121007
initial-value: "";
10131008
}
1009+
@layer properties {
1010+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
1011+
*, ::before, ::after, ::backdrop {
1012+
--tw-content: "";
1013+
}
1014+
}
1015+
}
10141016
"
10151017
`)
10161018

@@ -1195,13 +1197,7 @@ test(
11951197
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
11961198
"
11971199
--- ./dist/out.css ---
1198-
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
1199-
@layer base {
1200-
*, ::before, ::after, ::backdrop {
1201-
--tw-content: "";
1202-
}
1203-
}
1204-
}
1200+
@layer properties;
12051201
.content-\\[\\"pages\\/foo\\.html\\"\\] {
12061202
--tw-content: "pages/foo.html";
12071203
content: var(--tw-content);
@@ -1219,6 +1215,13 @@ test(
12191215
inherits: false;
12201216
initial-value: "";
12211217
}
1218+
@layer properties {
1219+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
1220+
*, ::before, ::after, ::backdrop {
1221+
--tw-content: "";
1222+
}
1223+
}
1224+
}
12221225
"
12231226
`)
12241227
},
@@ -1588,27 +1591,8 @@ test(
15881591
"
15891592
--- ./dist/out.css ---
15901593
@import url('https://fonts.googleapis.com');
1594+
@layer properties;
15911595
@layer theme, base, components, utilities;
1592-
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
1593-
@layer base {
1594-
*, ::before, ::after, ::backdrop {
1595-
--tw-shadow: 0 0 #0000;
1596-
--tw-shadow-color: initial;
1597-
--tw-shadow-alpha: 100%;
1598-
--tw-inset-shadow: 0 0 #0000;
1599-
--tw-inset-shadow-color: initial;
1600-
--tw-inset-shadow-alpha: 100%;
1601-
--tw-ring-color: initial;
1602-
--tw-ring-shadow: 0 0 #0000;
1603-
--tw-inset-ring-color: initial;
1604-
--tw-inset-ring-shadow: 0 0 #0000;
1605-
--tw-ring-inset: initial;
1606-
--tw-ring-offset-width: 0px;
1607-
--tw-ring-offset-color: #fff;
1608-
--tw-ring-offset-shadow: 0 0 #0000;
1609-
}
1610-
}
1611-
}
16121596
@layer theme {
16131597
:root, :host {
16141598
--font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
@@ -1839,6 +1823,26 @@ test(
18391823
inherits: false;
18401824
initial-value: 0 0 #0000;
18411825
}
1826+
@layer properties {
1827+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
1828+
*, ::before, ::after, ::backdrop {
1829+
--tw-shadow: 0 0 #0000;
1830+
--tw-shadow-color: initial;
1831+
--tw-shadow-alpha: 100%;
1832+
--tw-inset-shadow: 0 0 #0000;
1833+
--tw-inset-shadow-color: initial;
1834+
--tw-inset-shadow-alpha: 100%;
1835+
--tw-ring-color: initial;
1836+
--tw-ring-shadow: 0 0 #0000;
1837+
--tw-inset-ring-color: initial;
1838+
--tw-inset-ring-shadow: 0 0 #0000;
1839+
--tw-ring-inset: initial;
1840+
--tw-ring-offset-width: 0px;
1841+
--tw-ring-offset-color: #fff;
1842+
--tw-ring-offset-shadow: 0 0 #0000;
1843+
}
1844+
}
1845+
}
18421846
"
18431847
`)
18441848
},

integrations/postcss/source.test.ts

+32-28
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,7 @@ test(
9494
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
9595
"
9696
--- ./dist/out.css ---
97-
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
98-
@layer base {
99-
*, ::before, ::after, ::backdrop {
100-
--tw-content: "";
101-
}
102-
}
103-
}
97+
@layer properties;
10498
.content-\\[\\"components\\/my-component\\.tsx\\"\\] {
10599
--tw-content: "components/my-component.tsx";
106100
content: var(--tw-content);
@@ -142,6 +136,13 @@ test(
142136
inherits: false;
143137
initial-value: "";
144138
}
139+
@layer properties {
140+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
141+
*, ::before, ::after, ::backdrop {
142+
--tw-content: "";
143+
}
144+
}
145+
}
145146
"
146147
`)
147148
},
@@ -331,13 +332,7 @@ test(
331332
expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(`
332333
"
333334
--- ./project-a/dist/out.css ---
334-
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
335-
@layer base {
336-
*, ::before, ::after, ::backdrop {
337-
--tw-content: "";
338-
}
339-
}
340-
}
335+
@layer properties;
341336
.content-\\[\\'project-a\\/node_modules\\/my-lib-1\\/src\\/index\\.html\\'\\] {
342337
--tw-content: 'project-a/node modules/my-lib-1/src/index.html';
343338
content: var(--tw-content);
@@ -383,6 +378,13 @@ test(
383378
inherits: false;
384379
initial-value: "";
385380
}
381+
@layer properties {
382+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
383+
*, ::before, ::after, ::backdrop {
384+
--tw-content: "";
385+
}
386+
}
387+
}
386388
"
387389
`)
388390

@@ -593,13 +595,7 @@ test(
593595
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
594596
"
595597
--- ./dist/out.css ---
596-
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
597-
@layer base {
598-
*, ::before, ::after, ::backdrop {
599-
--tw-content: "";
600-
}
601-
}
602-
}
598+
@layer properties;
603599
.content-\\[\\"pages\\/foo\\.html\\"\\] {
604600
--tw-content: "pages/foo.html";
605601
content: var(--tw-content);
@@ -617,6 +613,13 @@ test(
617613
inherits: false;
618614
initial-value: "";
619615
}
616+
@layer properties {
617+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
618+
*, ::before, ::after, ::backdrop {
619+
--tw-content: "";
620+
}
621+
}
622+
}
620623
"
621624
`)
622625
},
@@ -704,13 +707,7 @@ test(
704707
expect(await fs.dumpFiles('./project-a/dist/*.css')).toMatchInlineSnapshot(`
705708
"
706709
--- ./project-a/dist/out.css ---
707-
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
708-
@layer base {
709-
*, ::before, ::after, ::backdrop {
710-
--tw-content: "";
711-
}
712-
}
713-
}
710+
@layer properties;
714711
.content-\\[\\'keep-me\\.html\\'\\] {
715712
--tw-content: 'keep-me.html';
716713
content: var(--tw-content);
@@ -724,6 +721,13 @@ test(
724721
inherits: false;
725722
initial-value: "";
726723
}
724+
@layer properties {
725+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
726+
*, ::before, ::after, ::backdrop {
727+
--tw-content: "";
728+
}
729+
}
730+
}
727731
"
728732
`)
729733

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

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

33
exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
4-
"@layer theme {
4+
"@layer properties {
5+
@supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
6+
*, :before, :after, ::backdrop {
7+
--tw-font-weight: initial;
8+
}
9+
}
10+
}
11+
12+
@layer theme {
513
:root, :host {
614
--font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
715
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
@@ -281,14 +289,6 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
281289
}
282290
}
283291
284-
@supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
285-
@layer base {
286-
*, :before, :after, ::backdrop {
287-
--tw-font-weight: initial;
288-
}
289-
}
290-
}
291-
292292
@property --tw-font-weight {
293293
syntax: "*";
294294
inherits: false

packages/tailwindcss/src/__snapshots__/index.test.ts.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

33
exports[`compiling CSS > \`@tailwind utilities\` is replaced by utilities using the default theme 1`] = `
4-
"@supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
5-
@layer base {
4+
"@layer properties {
5+
@supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
66
*, :before, :after, ::backdrop {
77
--tw-shadow: 0 0 #0000;
88
--tw-shadow-color: initial;

0 commit comments

Comments
 (0)