Skip to content

Commit 74999bb

Browse files
committed
chore: workaround @Property issue in Tailwind 4
1 parent 050b2a4 commit 74999bb

File tree

5 files changed

+100
-1
lines changed

5 files changed

+100
-1
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
"playwright": "1.55.0",
112112
"playwright-test": "14.1.12",
113113
"postcss": "8.5.6",
114+
"postcss-load-config": "6.0.1",
114115
"postcss-loader": "8.1.1",
115116
"prettier": "3.6.2",
116117
"prettier-plugin-organize-attributes": "1.0.0",

pnpm-lock.yaml

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

postcss.config.cjs

Lines changed: 0 additions & 1 deletion
This file was deleted.

postcss.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import tailwindcss from '@tailwindcss/postcss';
2+
3+
import { propertyToCustomProp } from './scripts/postcss-property-to-custom-prop.js';
4+
5+
/** @type {import('postcss-load-config').Config} */
6+
const config = { plugins: [tailwindcss(), propertyToCustomProp()] };
7+
8+
export default config;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Tailwind 4 uses @property but these don't work inside shadow DOM
3+
*
4+
* See: https://github.com/tailwindlabs/tailwindcss/issues/15005
5+
*
6+
* This script converts @property rules to custom properties on :root, :host
7+
*
8+
* Courtesy of https://github.com/tailwindlabs/tailwindcss/discussions/16772#discussioncomment-12309978
9+
*/
10+
export function propertyToCustomProp() {
11+
return {
12+
postcssPlugin: 'postcss-property-to-custom-prop',
13+
prepare() {
14+
// Store all the properties we find
15+
const properties = [];
16+
17+
return {
18+
AtRule: {
19+
property: (rule) => {
20+
// Extract the property name and initial value
21+
const propertyName = rule.params.match(/--[\w-]+/)?.[0];
22+
let initialValue = '';
23+
24+
rule.walkDecls('initial-value', (decl) => {
25+
initialValue = decl.value;
26+
});
27+
28+
if (propertyName && initialValue) {
29+
// Store the property
30+
properties.push({ name: propertyName, value: initialValue });
31+
32+
// Remove the original @property rule
33+
rule.remove();
34+
}
35+
},
36+
},
37+
38+
OnceExit(root, { Rule, Declaration }) {
39+
// If we found properties, add them to :root, :host
40+
if (properties.length > 0) {
41+
// Create the :root, :host rule using the Rule constructor from helpers
42+
const rootRule = new Rule({ selector: ':root, :host' });
43+
44+
// Add all properties as declarations
45+
properties.forEach((prop) => {
46+
// Create a new declaration for each property
47+
const decl = new Declaration({
48+
prop: prop.name,
49+
value: prop.value,
50+
});
51+
rootRule.append(decl);
52+
});
53+
54+
// Add the rule to the beginning of the CSS
55+
root.prepend(rootRule);
56+
}
57+
},
58+
};
59+
},
60+
};
61+
}

0 commit comments

Comments
 (0)