From caa1a95bf7e736a9c3d270df1f20a57b325e9cc1 Mon Sep 17 00:00:00 2001 From: jeongminsang Date: Tue, 20 May 2025 23:16:48 +0900 Subject: [PATCH 1/5] feat: update the useCSSLayers type and related logic --- .../__tests__/transform-process-test.js | 18 +-- packages/@stylexjs/babel-plugin/src/index.js | 23 +-- packages/@stylexjs/cli/src/config.js | 2 +- packages/@stylexjs/cli/src/index.js | 2 +- packages/@stylexjs/cli/src/options.js | 4 +- packages/@stylexjs/cli/src/transform.js | 2 +- packages/@stylexjs/postcss-plugin/README.md | 11 +- .../__fixtures__/constants.stylex.js | 37 +++++ .../transform-postcss-polyfill-test.js | 149 ++++++++++++++++++ .../@stylexjs/postcss-plugin/jest.config.js | 22 +++ .../@stylexjs/postcss-plugin/package.json | 3 + .../@stylexjs/postcss-plugin/src/builder.js | 2 +- .../@stylexjs/postcss-plugin/src/index.js | 30 +++- packages/@stylexjs/rollup-plugin/README.md | 11 +- .../rollup-plugin/__tests__/index-test.js | 2 +- packages/@stylexjs/rollup-plugin/src/index.js | 6 +- .../docs/components/playground-utils/files.js | 2 +- .../docs/api/configuration/postcss-plugin.mdx | 10 +- packages/docs/docs/learn/03-installation.mdx | 2 +- 19 files changed, 284 insertions(+), 54 deletions(-) create mode 100644 packages/@stylexjs/postcss-plugin/__tests__/__fixtures__/constants.stylex.js create mode 100644 packages/@stylexjs/postcss-plugin/__tests__/transform-postcss-polyfill-test.js create mode 100644 packages/@stylexjs/postcss-plugin/jest.config.js diff --git a/packages/@stylexjs/babel-plugin/__tests__/transform-process-test.js b/packages/@stylexjs/babel-plugin/__tests__/transform-process-test.js index ab13b2594..aee668886 100644 --- a/packages/@stylexjs/babel-plugin/__tests__/transform-process-test.js +++ b/packages/@stylexjs/babel-plugin/__tests__/transform-process-test.js @@ -128,15 +128,15 @@ describe('@stylexjs/babel-plugin', () => { expect(stylexPlugin.processStylexRules(metadata)).toMatchInlineSnapshot(` "@keyframes x4ssjuf-B{0%{box-shadow:1px 2px 3px 4px red;color:yellow;}100%{box-shadow:10px 20px 30px 40px green;color:var(--orange);}} @keyframes x4ssjuf-B{0%{box-shadow:-1px 2px 3px 4px red;color:yellow;}100%{box-shadow:-10px 20px 30px 40px green;color:var(--orange);}} - .x1bg2uv5:not(#\\#){border-color:green} - .xdmqw5o:not(#\\#):not(#\\#){animation-name:x4ssjuf-B} - .xrkmrrc:not(#\\#):not(#\\#){background-color:red} - html:not([dir='rtl']) .x1skrh0i:not(#\\#):not(#\\#){text-shadow:1px 2px 3px 4px red} - html[dir='rtl'] .x1skrh0i:not(#\\#):not(#\\#){text-shadow:-1px 2px 3px 4px red} - @media (min-width:320px){html:not([dir='rtl']) .x1cmij7u.x1cmij7u:not(#\\#):not(#\\#){text-shadow:10px 20px 30px 40px green}} - @media (min-width:320px){html[dir='rtl'] .x1cmij7u.x1cmij7u:not(#\\#):not(#\\#){text-shadow:-10px 20px 30px 40px green}} - @media (max-width: 1000px){.x1l0eizu.x1l0eizu:not(#\\#):not(#\\#):not(#\\#){border-color:blue}} - @media (max-width: 500px){@media (max-width: 1000px){.x5i7zo.x5i7zo.x5i7zo:not(#\\#):not(#\\#):not(#\\#):not(#\\#){border-color:yellow}}}" + .x1bg2uv5{border-color:green} + .xdmqw5o{animation-name:x4ssjuf-B} + .xrkmrrc{background-color:red} + html:not([dir='rtl']) .x1skrh0i{text-shadow:1px 2px 3px 4px red} + html[dir='rtl'] .x1skrh0i{text-shadow:-1px 2px 3px 4px red} + @media (min-width:320px){html:not([dir='rtl']) .x1cmij7u.x1cmij7u{text-shadow:10px 20px 30px 40px green}} + @media (min-width:320px){html[dir='rtl'] .x1cmij7u.x1cmij7u{text-shadow:-10px 20px 30px 40px green}} + @media (max-width: 1000px){.x1l0eizu.x1l0eizu{border-color:blue}} + @media (max-width: 500px){@media (max-width: 1000px){.x5i7zo.x5i7zo.x5i7zo{border-color:yellow}}}" `); }); diff --git a/packages/@stylexjs/babel-plugin/src/index.js b/packages/@stylexjs/babel-plugin/src/index.js index 62c37ff0f..7a26d4c33 100644 --- a/packages/@stylexjs/babel-plugin/src/index.js +++ b/packages/@stylexjs/babel-plugin/src/index.js @@ -460,14 +460,9 @@ function processStylexRules( new Map(group.map(([a, b]) => [a, b])).values(), ) .flatMap(({ ltr, rtl }) => { - let ltrRule = ltr, + const ltrRule = ltr, rtlRule = rtl; - if (!useLayers) { - ltrRule = addSpecificityLevel(ltrRule, index); - rtlRule = rtlRule && addSpecificityLevel(rtlRule, index); - } - return rtlRule ? [ addAncestorSelector(ltrRule, "html:not([dir='rtl'])"), @@ -514,22 +509,6 @@ function addAncestorSelector( /** * Adds :not(#\#) to bump up specificity. as a polyfill for @layer */ -function addSpecificityLevel(selector: string, index: number): string { - if (selector.startsWith('@keyframes')) { - return selector; - } - const pseudo = Array.from({ length: index }) - .map(() => ':not(#\\#)') - .join(''); - - const lastOpenCurly = selector.includes('::') - ? selector.indexOf('::') - : selector.lastIndexOf('{'); - const beforeCurly = selector.slice(0, lastOpenCurly); - const afterCurly = selector.slice(lastOpenCurly); - - return `${beforeCurly}${pseudo}${afterCurly}`; -} export type StyleXTransformObj = $ReadOnly<{ (): PluginObj<>, diff --git a/packages/@stylexjs/cli/src/config.js b/packages/@stylexjs/cli/src/config.js index 091551bf5..8fe99b638 100644 --- a/packages/@stylexjs/cli/src/config.js +++ b/packages/@stylexjs/cli/src/config.js @@ -23,7 +23,7 @@ export type CliConfig = { babelPluginsPre?: $ReadOnlyArray, babelPluginsPost?: $ReadOnlyArray, modules_EXPERIMENTAL: $ReadOnlyArray, - useCSSLayers?: boolean, + useCSSLayers?: 'none' | 'native' | 'polyfill', styleXConfig?: StyleXOptions, }; diff --git a/packages/@stylexjs/cli/src/index.js b/packages/@stylexjs/cli/src/index.js index fa68823be..a3676b58b 100755 --- a/packages/@stylexjs/cli/src/index.js +++ b/packages/@stylexjs/cli/src/index.js @@ -71,7 +71,7 @@ const modules_EXPERIMENTAL: $ReadOnlyArray = const babelPresets: $ReadOnlyArray = args.babelPresets; const babelPluginsPre: $ReadOnlyArray = args.babelPluginsPre; const babelPluginsPost: $ReadOnlyArray = args.babelPluginsPost; -const useCSSLayers: boolean = args.useCSSLayers; +const useCSSLayers: 'none' | 'native' | 'polyfill' = args.useCSSLayers; const styleXConfig: StyleXOptions = (config.styleXConfig as $FlowFixMe) ?? {}; const cliArgsConfig: CliConfig = { diff --git a/packages/@stylexjs/cli/src/options.js b/packages/@stylexjs/cli/src/options.js index f54f75056..b6420e839 100644 --- a/packages/@stylexjs/cli/src/options.js +++ b/packages/@stylexjs/cli/src/options.js @@ -35,8 +35,8 @@ const options = { useCSSLayers: { alias: 'l', describe: 'Use CSS layers to optimize CSS rendering', - type: 'boolean', - default: false, + type: 'string', + default: 'none', }, babelPresets: { describe: diff --git a/packages/@stylexjs/cli/src/transform.js b/packages/@stylexjs/cli/src/transform.js index a78fe548d..c772113d2 100644 --- a/packages/@stylexjs/cli/src/transform.js +++ b/packages/@stylexjs/cli/src/transform.js @@ -81,7 +81,7 @@ export async function compileDirectory( const compiledCSS = await styleXPlugin.processStylexRules( Array.from(config.state.styleXRules.values()).flat(), - config.useCSSLayers, + config.useCSSLayers !== 'none', ); const cssBundlePath = path.join(config.output, config.styleXBundleName); diff --git a/packages/@stylexjs/postcss-plugin/README.md b/packages/@stylexjs/postcss-plugin/README.md index 7ac907fee..65b28b2ed 100644 --- a/packages/@stylexjs/postcss-plugin/README.md +++ b/packages/@stylexjs/postcss-plugin/README.md @@ -130,8 +130,13 @@ and specify desired options. Refer to ### useCSSLayers ```js -useCSSLayers: boolean; // Default: false +useCSSLayers: 'none' | 'native' | 'polyfill'; // Default: 'none' ``` -Enabling this option switches Stylex from using `:not(#\#)` to using `@layers` -for handling CSS specificity. +The `useCSSLayers` option controls how StyleX handles CSS specificity and layer +management. It supports three strategies: + +- `'none'` (default): Uses `:not(#\#)` to handle CSS specificity without layers +- `'native'`: Uses native CSS `@layer` for handling CSS specificity +- `'polyfill'`: Uses `@csstools/postcss-cascade-layers` to polyfill CSS layers + for browsers that don't support them diff --git a/packages/@stylexjs/postcss-plugin/__tests__/__fixtures__/constants.stylex.js b/packages/@stylexjs/postcss-plugin/__tests__/__fixtures__/constants.stylex.js new file mode 100644 index 000000000..3a9f5169e --- /dev/null +++ b/packages/@stylexjs/postcss-plugin/__tests__/__fixtures__/constants.stylex.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +import * as stylex from '@stylexjs/stylex'; + +export const styles = stylex.create({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-between', + minHeight: '100vh', + paddingTop: '32px', + paddingBottom: '32px', + }, + main: { + fontSize: '24px', + lineHeight: 1, + fontFamily: 'sans-serif', + fontWeight: 400, + textAlign: 'center', + display: 'flex', + gap: '16px', + whiteSpace: 'nowrap', + flexDirection: 'row', + }, + h1: { + fontWeight: 700, + fontFamily: 'monospace', + }, +}); diff --git a/packages/@stylexjs/postcss-plugin/__tests__/transform-postcss-polyfill-test.js b/packages/@stylexjs/postcss-plugin/__tests__/transform-postcss-polyfill-test.js new file mode 100644 index 000000000..d3f884989 --- /dev/null +++ b/packages/@stylexjs/postcss-plugin/__tests__/transform-postcss-polyfill-test.js @@ -0,0 +1,149 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +jest.autoMockOff(); + +const path = require('path'); +const postcss = require('postcss'); +const stylexPlugin = require('../src/index'); + +describe('@stylexjs/postcss-plugin', () => { + test('should transform @stylex at-rule and apply cascadeLayers polyfill', async () => { + const inputCss = ` + @stylex; + `; + + const pluginOptions = { + cwd: path.resolve(__dirname, '__fixtures__'), + include: ['constants.stylex.js'], + useCSSLayers: 'polyfill', + babelConfig: { + presets: ['@babel/preset-env'], + plugins: ['@stylexjs/babel-plugin'], + }, + }; + + const result = await postcss([stylexPlugin(pluginOptions)]).process( + inputCss, + { from: undefined }, + ); + + expect(result.css).toMatchInlineSnapshot(` + " + .xou54vl{gap:16px} + .x6s0dn4:not(#\\#){align-items:center} + .x78zum5:not(#\\#){display:flex} + .xdt5ytf:not(#\\#){flex-direction:column} + .x1q0g3np:not(#\\#){flex-direction:row} + .x1ey7xld:not(#\\#){font-family:monospace} + .x6icuqf:not(#\\#){font-family:sans-serif} + .x1pvqxga:not(#\\#){font-size:24px} + .xo1l8bm:not(#\\#){font-weight:400} + .x1xlr1w8:not(#\\#){font-weight:700} + .x1qughib:not(#\\#){justify-content:space-between} + .xo5v014:not(#\\#){line-height:1} + .x2b8uid:not(#\\#){text-align:center} + .xuxw1ft:not(#\\#){white-space:nowrap} + .xg6iff7:not(#\\#):not(#\\#){min-height:100vh} + .x1gan7if:not(#\\#):not(#\\#){padding-bottom:32px} + .x1miatn0:not(#\\#):not(#\\#){padding-top:32px} + " + `); + }); + test('should handle useCSSLayers: none', async () => { + const inputCss = ` + @stylex; + `; + + const pluginOptions = { + cwd: path.resolve(__dirname, '__fixtures__'), + include: ['constants.stylex.js'], + useCSSLayers: 'none', + babelConfig: { + presets: ['@babel/preset-env'], + plugins: ['@stylexjs/babel-plugin'], + }, + }; + + const result = await postcss([stylexPlugin(pluginOptions)]).process( + inputCss, + { from: undefined }, + ); + + expect(result.css).toMatchInlineSnapshot(` + ".xou54vl{gap:16px} + .x6s0dn4{align-items:center} + .x78zum5{display:flex} + .xdt5ytf{flex-direction:column} + .x1q0g3np{flex-direction:row} + .x1ey7xld{font-family:monospace} + .x6icuqf{font-family:sans-serif} + .x1pvqxga{font-size:24px} + .xo1l8bm{font-weight:400} + .x1xlr1w8{font-weight:700} + .x1qughib{justify-content:space-between} + .xo5v014{line-height:1} + .x2b8uid{text-align:center} + .xuxw1ft{white-space:nowrap} + .xg6iff7{min-height:100vh} + .x1gan7if{padding-bottom:32px} + .x1miatn0{padding-top:32px} + " + `); + }); + test('should handle useCSSLayers: none', async () => { + const inputCss = ` + @stylex; + `; + + const pluginOptions = { + cwd: path.resolve(__dirname, '__fixtures__'), + include: ['constants.stylex.js'], + useCSSLayers: 'native', + babelConfig: { + presets: ['@babel/preset-env'], + plugins: ['@stylexjs/babel-plugin'], + }, + }; + + const result = await postcss([stylexPlugin(pluginOptions)]).process( + inputCss, + { from: undefined }, + ); + + expect(result.css).toMatchInlineSnapshot(` + " + @layer priority1, priority2, priority3; + @layer priority1{ + .xou54vl{gap:16px} + } + @layer priority2{ + .x6s0dn4{align-items:center} + .x78zum5{display:flex} + .xdt5ytf{flex-direction:column} + .x1q0g3np{flex-direction:row} + .x1ey7xld{font-family:monospace} + .x6icuqf{font-family:sans-serif} + .x1pvqxga{font-size:24px} + .xo1l8bm{font-weight:400} + .x1xlr1w8{font-weight:700} + .x1qughib{justify-content:space-between} + .xo5v014{line-height:1} + .x2b8uid{text-align:center} + .xuxw1ft{white-space:nowrap} + } + @layer priority3{ + .xg6iff7{min-height:100vh} + .x1gan7if{padding-bottom:32px} + .x1miatn0{padding-top:32px} + } + " + `); + }); +}); diff --git a/packages/@stylexjs/postcss-plugin/jest.config.js b/packages/@stylexjs/postcss-plugin/jest.config.js new file mode 100644 index 000000000..9736eede8 --- /dev/null +++ b/packages/@stylexjs/postcss-plugin/jest.config.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +module.exports = { + collectCoverageFrom: ['/src/index.js'], + coverageThreshold: { + global: { + branches: 50, + functions: 60, + lines: 70, + statements: 70, + }, + }, + testPathIgnorePatterns: ['/__fixtures__/'], + verbose: true, +}; diff --git a/packages/@stylexjs/postcss-plugin/package.json b/packages/@stylexjs/postcss-plugin/package.json index a26b16312..ce348df2d 100644 --- a/packages/@stylexjs/postcss-plugin/package.json +++ b/packages/@stylexjs/postcss-plugin/package.json @@ -15,5 +15,8 @@ "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3" + }, + "devDependencies": { + "@csstools/postcss-cascade-layers": "^5.0.1" } } diff --git a/packages/@stylexjs/postcss-plugin/src/builder.js b/packages/@stylexjs/postcss-plugin/src/builder.js index 5a7155ac6..b0da2b111 100644 --- a/packages/@stylexjs/postcss-plugin/src/builder.js +++ b/packages/@stylexjs/postcss-plugin/src/builder.js @@ -150,7 +150,7 @@ function createBuilder() { }), ); - const css = bundler.bundle({ useCSSLayers }); + const css = bundler.bundle({ useCSSLayers: useCSSLayers !== 'none' }); return css; } diff --git a/packages/@stylexjs/postcss-plugin/src/index.js b/packages/@stylexjs/postcss-plugin/src/index.js index 76e131504..b33ca3c00 100644 --- a/packages/@stylexjs/postcss-plugin/src/index.js +++ b/packages/@stylexjs/postcss-plugin/src/index.js @@ -7,9 +7,12 @@ const postcss = require('postcss'); const createBuilder = require('./builder'); +const cascadeLayers = require('@csstools/postcss-cascade-layers'); const PLUGIN_NAME = '@stylexjs/postcss-plugin'; +const VALID_CSS_LAYERS = ['none', 'native', 'polyfill']; + const builder = createBuilder(); const isDev = process.env.NODE_ENV === 'development'; @@ -21,8 +24,15 @@ const plugin = ({ babelConfig = {}, include, exclude, - useCSSLayers = false, + useCSSLayers = 'none', }) => { + if (!VALID_CSS_LAYERS.includes(useCSSLayers)) { + throw new Error( + `Invalid useCSSLayers value: "${useCSSLayers}". ` + + `Valid values are: ${VALID_CSS_LAYERS.join(', ')}`, + ); + } + exclude = [ // Exclude type declaration files by default because it never contains any CSS rules. '**/*.d.ts', @@ -51,7 +61,6 @@ const plugin = ({ useCSSLayers, isDev, }); - // Find the "@stylex" at-rule const styleXAtRule = builder.findStyleXAtRule(root); if (styleXAtRule == null) { @@ -76,7 +85,20 @@ const plugin = ({ const css = await builder.build({ shouldSkipTransformError, }); - const parsed = await postcss.parse(css, { + + let processedCss = css; + if (useCSSLayers === 'polyfill') { + const result = await postcss([ + cascadeLayers({ + onRevertLayerKeyword: 'warn', + onConditionalRulesChangingLayerOrder: 'warn', + onImportLayerRule: 'warn', + }), + ]).process(css); + processedCss = result.css; + } + + const parsed = await postcss.parse(processedCss, { from: fileName, }); @@ -93,7 +115,7 @@ const plugin = ({ ], }; }; - +// console.log('plugin', plugin); plugin.postcss = true; module.exports = plugin; diff --git a/packages/@stylexjs/rollup-plugin/README.md b/packages/@stylexjs/rollup-plugin/README.md index f05dc4b64..7829cf758 100644 --- a/packages/@stylexjs/rollup-plugin/README.md +++ b/packages/@stylexjs/rollup-plugin/README.md @@ -43,9 +43,16 @@ The name of the output css file. --- ### useCSSLayers ```js -useCSSLayers: boolean // Default: false +useCSSLayers: 'none' | 'native' | 'polyfill' // Default: 'none' ``` -Enabling this option switches Stylex from using `:not(#\#)` to using `@layers` for handling CSS specificity. + +The `useCSSLayers` option controls how StyleX handles CSS specificity and layer +management. It supports three strategies: + +- `'none'` (default): Uses `:not(#\#)` to handle CSS specificity without layers +- `'native'`: Uses native CSS `@layer` for handling CSS specificity +- `'polyfill'`: Uses `@csstools/postcss-cascade-layers` to polyfill CSS layers + for browsers that don't support them --- ### babelConfig diff --git a/packages/@stylexjs/rollup-plugin/__tests__/index-test.js b/packages/@stylexjs/rollup-plugin/__tests__/index-test.js index 03ff84aff..49281b653 100644 --- a/packages/@stylexjs/rollup-plugin/__tests__/index-test.js +++ b/packages/@stylexjs/rollup-plugin/__tests__/index-test.js @@ -34,7 +34,7 @@ describe('rollup-plugin-stylex', () => { exclude: [/npmStyles\.js/], }), stylexPlugin({ - useCSSLayers: true, + useCSSLayers: 'native', ...options, lightningcssOptions: { minify: false }, }), diff --git a/packages/@stylexjs/rollup-plugin/src/index.js b/packages/@stylexjs/rollup-plugin/src/index.js index 3af23053a..fc2ba4756 100644 --- a/packages/@stylexjs/rollup-plugin/src/index.js +++ b/packages/@stylexjs/rollup-plugin/src/index.js @@ -37,7 +37,7 @@ export type PluginOptions = $ReadOnly<{ plugins?: $ReadOnlyArray, presets?: $ReadOnlyArray, }>, - useCSSLayers?: boolean, + useCSSLayers?: 'none' | 'native' | 'polyfill', lightningcssOptions?: Omit< TransformOptions<{}>, 'code' | 'filename' | 'visitor', @@ -63,7 +63,7 @@ export default function stylexPlugin({ fileName = 'stylex.css', babelConfig: { plugins = [], presets = [] } = {}, importSources = ['stylex', '@stylexjs/stylex'], - useCSSLayers = false, + useCSSLayers = 'none', lightningcssOptions, ...options }: PluginOptions = {}): Plugin<> { @@ -78,7 +78,7 @@ export default function stylexPlugin({ if (rules.length > 0) { const collectedCSS = stylexBabelPlugin.processStylexRules( rules, - useCSSLayers, + useCSSLayers !== 'none', ); // Process the CSS using lightningcss diff --git a/packages/docs/components/playground-utils/files.js b/packages/docs/components/playground-utils/files.js index bd001eab2..a13229556 100644 --- a/packages/docs/components/playground-utils/files.js +++ b/packages/docs/components/playground-utils/files.js @@ -101,7 +101,7 @@ module.exports = { plugins: { '@stylexjs/postcss-plugin': { include: ['src/**/*.{js,jsx}'], - useCSSLayers: true, + useCSSLayers: 'native', babelConfig, }, }, diff --git a/packages/docs/docs/api/configuration/postcss-plugin.mdx b/packages/docs/docs/api/configuration/postcss-plugin.mdx index a1ed815b0..d89d517fa 100644 --- a/packages/docs/docs/api/configuration/postcss-plugin.mdx +++ b/packages/docs/docs/api/configuration/postcss-plugin.mdx @@ -54,7 +54,13 @@ Array of paths or glob patterns to compile. ### useCSSLayers ```js -useCSSLayers: boolean; // Default: false +useCSSLayers: 'none' | 'native' | 'polyfill'; // Default: 'none' ``` -Enabling this option switches Stylex from using `:not(#\#)` to using `@layers` for handling CSS specificity. +The `useCSSLayers` option controls how StyleX handles CSS specificity and layer +management. It supports three strategies: + +- `'none'` (default): Uses `:not(#\#)` to handle CSS specificity without layers +- `'native'`: Uses native CSS `@layer` for handling CSS specificity +- `'polyfill'`: Uses `@csstools/postcss-cascade-layers` to polyfill CSS layers + for browsers that don't support them diff --git a/packages/docs/docs/learn/03-installation.mdx b/packages/docs/docs/learn/03-installation.mdx index 738ca5d1b..793f664ff 100755 --- a/packages/docs/docs/learn/03-installation.mdx +++ b/packages/docs/docs/learn/03-installation.mdx @@ -74,7 +74,7 @@ module.exports = { // any other files that should be included // this should include NPM dependencies that use StyleX ], - useCSSLayers: true, + useCSSLayers: 'none', }, autoprefixer: {}, }, From d4415cd9e3bad2722740ce5f99f1098fea7a0013 Mon Sep 17 00:00:00 2001 From: jeongminsang Date: Wed, 21 May 2025 14:53:07 +0900 Subject: [PATCH 2/5] fix: reinstall dependency postcss-cascade-layers --- packages/@stylexjs/postcss-plugin/package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@stylexjs/postcss-plugin/package.json b/packages/@stylexjs/postcss-plugin/package.json index ce348df2d..be70598fe 100644 --- a/packages/@stylexjs/postcss-plugin/package.json +++ b/packages/@stylexjs/postcss-plugin/package.json @@ -11,12 +11,10 @@ "dependencies": { "@babel/core": "^7.26.8", "@stylexjs/babel-plugin": "0.12.0", + "@csstools/postcss-cascade-layers": "^5.0.1", "postcss": "^8.4.49", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3" - }, - "devDependencies": { - "@csstools/postcss-cascade-layers": "^5.0.1" } } From 4bb3f3ef5059fb008b95495da4ab1ca05625d4cd Mon Sep 17 00:00:00 2001 From: jeongminsang Date: Fri, 6 Jun 2025 15:41:00 +0900 Subject: [PATCH 3/5] feat: change useCSSLayers to boolean in postcss-plugin and update related logic. --- .../postcss-cascade-layers_v5.x.x.js | 21 +++ flow-typed/npm/postcss_v8.x.x.js | 128 ++++++++++++++++++ package.json | 3 +- .../__tests__/transform-process-test.js | 17 +-- packages/@stylexjs/babel-plugin/package.json | 2 + packages/@stylexjs/babel-plugin/src/index.js | 17 ++- packages/@stylexjs/cli/src/config.js | 2 +- packages/@stylexjs/cli/src/index.js | 2 +- packages/@stylexjs/cli/src/transform.js | 2 +- packages/@stylexjs/postcss-plugin/README.md | 11 +- .../__tests__/__fixtures__/styles-third.js | 16 --- .../postcss-plugin/__tests__/index-test.js | 36 +---- .../@stylexjs/postcss-plugin/package.json | 1 - .../@stylexjs/postcss-plugin/src/builder.js | 2 +- .../@stylexjs/postcss-plugin/src/plugin.js | 24 +--- packages/@stylexjs/rollup-plugin/README.md | 11 +- .../rollup-plugin/__tests__/index-test.js | 2 +- packages/@stylexjs/rollup-plugin/src/index.js | 10 +- .../docs/components/playground-utils/files.js | 2 +- .../docs/api/configuration/postcss-plugin.mdx | 10 +- packages/docs/docs/learn/03-installation.mdx | 2 +- packages/docs/scripts/make-stylex-sheet.js | 4 +- 22 files changed, 205 insertions(+), 120 deletions(-) create mode 100644 flow-typed/npm/@csstools/postcss-cascade-layers_v5.x.x.js create mode 100644 flow-typed/npm/postcss_v8.x.x.js delete mode 100644 packages/@stylexjs/postcss-plugin/__tests__/__fixtures__/styles-third.js diff --git a/flow-typed/npm/@csstools/postcss-cascade-layers_v5.x.x.js b/flow-typed/npm/@csstools/postcss-cascade-layers_v5.x.x.js new file mode 100644 index 000000000..de2fed84f --- /dev/null +++ b/flow-typed/npm/@csstools/postcss-cascade-layers_v5.x.x.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +declare module '@csstools/postcss-cascade-layers' { + import type { Plugin } from 'postcss'; + declare type PluginCreator = (opts?: T) => Plugin; + declare type PluginOptions = { + /** Emit a warning when the "revert" keyword is found in your CSS. default: "warn" */ + onRevertLayerKeyword?: 'warn' | false, + /** Emit a warning when conditional rules could change the layer order. default: "warn" */ + onConditionalRulesChangingLayerOrder?: 'warn' | false, + /** Emit a warning when "layer" is used in "@import". default: "warn" */ + onImportLayerRule?: 'warn' | false, + }; + + declare module.exports: PluginCreator; +} diff --git a/flow-typed/npm/postcss_v8.x.x.js b/flow-typed/npm/postcss_v8.x.x.js new file mode 100644 index 000000000..3e99d3eb3 --- /dev/null +++ b/flow-typed/npm/postcss_v8.x.x.js @@ -0,0 +1,128 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +declare module 'postcss' { + declare export type PluginCreator = (opts?: T) => Plugin; + + declare export interface Plugin { + postcssPlugin: string; + Once?: (root: Root, postcss: Postcss) => void; + OnceExit?: (root: Root, postcss: Postcss) => void; + Root?: (root: Root, postcss: Postcss) => void; + RootExit?: (root: Root, postcss: Postcss) => void; + AtRule?: (atRule: AtRule, postcss: Postcss) => void; + AtRuleExit?: (atRule: AtRule, postcss: Postcss) => void; + Rule?: (rule: Rule, postcss: Postcss) => void; + RuleExit?: (rule: Rule, postcss: Postcss) => void; + Declaration?: (decl: Declaration, postcss: Postcss) => void; + DeclarationExit?: (decl: Declaration, postcss: Postcss) => void; + Comment?: (comment: Comment, postcss: Postcss) => void; + CommentExit?: (comment: Comment, postcss: Postcss) => void; + } + + declare export interface Postcss { + version: string; + plugins: Array; + process: ( + css: string | { toString(): string }, + opts?: ProcessOptions, + ) => Promise; + (plugins?: Array): Postcss; + } + + declare export interface ProcessOptions { + from?: string; + to?: string; + map?: boolean | { inline: boolean, annotation: boolean }; + parser?: any; + stringifier?: any; + syntax?: any; + } + + declare export interface Result { + css: string; + map: any; + root: Root; + messages: Array; + processor: Postcss; + opts: ProcessOptions; + warnings(): Array; + toString(): string; + } + + declare export interface Root { + type: 'root'; + nodes: Array; + source: Source; + raws: any; + parent: null; + lastEach: number; + indexes: { [key: string]: number }; + rawCache: { [key: string]: string }; + [key: string]: any; + } + + declare export interface AtRule { + type: 'atrule'; + name: string; + params: string; + nodes?: Array; + parent: Container; + source: Source; + raws: any; + [key: string]: any; + } + + declare export interface Rule { + type: 'rule'; + selector: string; + nodes: Array; + parent: Container; + source: Source; + raws: any; + [key: string]: any; + } + + declare export interface Declaration { + type: 'decl'; + prop: string; + value: string; + parent: Container; + source: Source; + raws: any; + [key: string]: any; + } + + declare export interface Comment { + type: 'comment'; + text: string; + parent: Container; + source: Source; + raws: any; + [key: string]: any; + } + + declare export interface Container { + type: string; + nodes: Array; + parent: Container | null; + source: Source; + raws: any; + [key: string]: any; + } + + declare export type ChildNode = AtRule | Rule | Declaration | Comment; + + declare export interface Source { + start?: { line: number, column: number }; + end?: { line: number, column: number }; + input: { css: string, id?: string }; + } + + declare const postcss: Postcss; + declare export default typeof postcss; +} diff --git a/package.json b/package.json index 5b86e9d82..07b20c96a 100644 --- a/package.json +++ b/package.json @@ -70,5 +70,6 @@ "git update-index --again", "npm run lint" ] - } + }, + "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72" } diff --git a/packages/@stylexjs/babel-plugin/__tests__/transform-process-test.js b/packages/@stylexjs/babel-plugin/__tests__/transform-process-test.js index aee668886..37c603de4 100644 --- a/packages/@stylexjs/babel-plugin/__tests__/transform-process-test.js +++ b/packages/@stylexjs/babel-plugin/__tests__/transform-process-test.js @@ -90,19 +90,19 @@ export const styles = stylex.create({ describe('@stylexjs/babel-plugin', () => { describe('[transform] stylexPlugin.processStylexRules', () => { - test('no rules', () => { + test('no rules', async () => { const { code, metadata } = transform(` import * as stylex from '@stylexjs/stylex'; `); expect(code).toMatchInlineSnapshot( '"import * as stylex from \'@stylexjs/stylex\';"', ); - expect(stylexPlugin.processStylexRules(metadata)).toMatchInlineSnapshot( - '""', - ); + expect( + await stylexPlugin.processStylexRules(metadata), + ).toMatchInlineSnapshot('""'); }); - test('all rules (useLayers:false)', () => { + test('all rules (useLayers:false)', async () => { const { code, metadata } = transform(fixture); expect(code).toMatchInlineSnapshot(` "import * as stylex from '@stylexjs/stylex'; @@ -125,7 +125,8 @@ describe('@stylexjs/babel-plugin', () => { } };" `); - expect(stylexPlugin.processStylexRules(metadata)).toMatchInlineSnapshot(` + expect(await stylexPlugin.processStylexRules(metadata)) + .toMatchInlineSnapshot(` "@keyframes x4ssjuf-B{0%{box-shadow:1px 2px 3px 4px red;color:yellow;}100%{box-shadow:10px 20px 30px 40px green;color:var(--orange);}} @keyframes x4ssjuf-B{0%{box-shadow:-1px 2px 3px 4px red;color:yellow;}100%{box-shadow:-10px 20px 30px 40px green;color:var(--orange);}} .x1bg2uv5{border-color:green} @@ -140,7 +141,7 @@ describe('@stylexjs/babel-plugin', () => { `); }); - test('all rules (useLayers:true)', () => { + test('all rules (useLayers:true)', async () => { const { code, metadata } = transform(fixture, { useLayers: true, }); @@ -165,7 +166,7 @@ describe('@stylexjs/babel-plugin', () => { } };" `); - expect(stylexPlugin.processStylexRules(metadata, true)) + expect(await stylexPlugin.processStylexRules(metadata, true)) .toMatchInlineSnapshot(` " @layer priority1, priority2, priority3, priority4, priority5; diff --git a/packages/@stylexjs/babel-plugin/package.json b/packages/@stylexjs/babel-plugin/package.json index 2c16f8158..7bdbd32d9 100644 --- a/packages/@stylexjs/babel-plugin/package.json +++ b/packages/@stylexjs/babel-plugin/package.json @@ -20,8 +20,10 @@ "@babel/core": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", + "@csstools/postcss-cascade-layers": "^5.0.1", "@dual-bundle/import-meta-resolve": "^4.1.0", "@stylexjs/stylex": "0.13.0", + "postcss": "^8.5.3", "postcss-value-parser": "^4.1.0" }, "devDependencies": { diff --git a/packages/@stylexjs/babel-plugin/src/index.js b/packages/@stylexjs/babel-plugin/src/index.js index 7a26d4c33..b44dcf1a0 100644 --- a/packages/@stylexjs/babel-plugin/src/index.js +++ b/packages/@stylexjs/babel-plugin/src/index.js @@ -31,6 +31,8 @@ import transformStylexCall, { } from './visitors/stylex-merge'; import transformStylexProps from './visitors/stylex-props'; import { skipStylexPropsChildren } from './visitors/stylex-props'; +import postcss from 'postcss'; +import cascadeLayers from '@csstools/postcss-cascade-layers'; const NAME = 'stylex'; @@ -348,12 +350,12 @@ export type Rule = [ }, number, ]; -function processStylexRules( +async function processStylexRules( rules: Array, useLayers: boolean = false, -): string { +): Promise { if (rules.length === 0) { - return ''; + return Promise.resolve(''); } const constantRules = rules.filter( @@ -478,7 +480,10 @@ function processStylexRules( }) .join('\n'); - return header + collectedCSS; + if (!useLayers) { + return transformCollectedCSS(collectedCSS); + } + return Promise.resolve(header + collectedCSS); } styleXTransform.processStylexRules = processStylexRules; @@ -510,6 +515,10 @@ function addAncestorSelector( * Adds :not(#\#) to bump up specificity. as a polyfill for @layer */ +async function transformCollectedCSS(collectedCSS: string): Promise { + return (await postcss([cascadeLayers()]).process(collectedCSS)).css; +} + export type StyleXTransformObj = $ReadOnly<{ (): PluginObj<>, withOptions: typeof stylexPluginWithOptions, diff --git a/packages/@stylexjs/cli/src/config.js b/packages/@stylexjs/cli/src/config.js index 8fe99b638..091551bf5 100644 --- a/packages/@stylexjs/cli/src/config.js +++ b/packages/@stylexjs/cli/src/config.js @@ -23,7 +23,7 @@ export type CliConfig = { babelPluginsPre?: $ReadOnlyArray, babelPluginsPost?: $ReadOnlyArray, modules_EXPERIMENTAL: $ReadOnlyArray, - useCSSLayers?: 'none' | 'native' | 'polyfill', + useCSSLayers?: boolean, styleXConfig?: StyleXOptions, }; diff --git a/packages/@stylexjs/cli/src/index.js b/packages/@stylexjs/cli/src/index.js index a3676b58b..738ce6e43 100755 --- a/packages/@stylexjs/cli/src/index.js +++ b/packages/@stylexjs/cli/src/index.js @@ -71,7 +71,7 @@ const modules_EXPERIMENTAL: $ReadOnlyArray = const babelPresets: $ReadOnlyArray = args.babelPresets; const babelPluginsPre: $ReadOnlyArray = args.babelPluginsPre; const babelPluginsPost: $ReadOnlyArray = args.babelPluginsPost; -const useCSSLayers: 'none' | 'native' | 'polyfill' = args.useCSSLayers; +const useCSSLayers: boolean = args.layers; const styleXConfig: StyleXOptions = (config.styleXConfig as $FlowFixMe) ?? {}; const cliArgsConfig: CliConfig = { diff --git a/packages/@stylexjs/cli/src/transform.js b/packages/@stylexjs/cli/src/transform.js index c772113d2..a78fe548d 100644 --- a/packages/@stylexjs/cli/src/transform.js +++ b/packages/@stylexjs/cli/src/transform.js @@ -81,7 +81,7 @@ export async function compileDirectory( const compiledCSS = await styleXPlugin.processStylexRules( Array.from(config.state.styleXRules.values()).flat(), - config.useCSSLayers !== 'none', + config.useCSSLayers, ); const cssBundlePath = path.join(config.output, config.styleXBundleName); diff --git a/packages/@stylexjs/postcss-plugin/README.md b/packages/@stylexjs/postcss-plugin/README.md index 3245eb52d..f8743603d 100644 --- a/packages/@stylexjs/postcss-plugin/README.md +++ b/packages/@stylexjs/postcss-plugin/README.md @@ -130,16 +130,11 @@ and specify desired options. Refer to ### useCSSLayers ```js -useCSSLayers: 'none' | 'native' | 'polyfill'; // Default: 'none' +useCSSLayers: boolean; // Default: false ``` -The `useCSSLayers` option controls how StyleX handles CSS specificity and layer -management. It supports three strategies: - -- `'none'` (default): Uses `:not(#\#)` to handle CSS specificity without layers -- `'native'`: Uses native CSS `@layer` for handling CSS specificity -- `'polyfill'`: Uses `@csstools/postcss-cascade-layers` to polyfill CSS layers - for browsers that don't support them +Enabling this option switches Stylex from using `:not(#\#)` to using `@layers` +for handling CSS specificity. --- diff --git a/packages/@stylexjs/postcss-plugin/__tests__/__fixtures__/styles-third.js b/packages/@stylexjs/postcss-plugin/__tests__/__fixtures__/styles-third.js deleted file mode 100644 index 62ff76829..000000000 --- a/packages/@stylexjs/postcss-plugin/__tests__/__fixtures__/styles-third.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import * as stylex from '@stylexjs/stylex'; - -export const styles = stylex.create({ - third: { - minHeight: '100vh', - paddingTop: '32px', - paddingBottom: '32px', - }, -}); diff --git a/packages/@stylexjs/postcss-plugin/__tests__/index-test.js b/packages/@stylexjs/postcss-plugin/__tests__/index-test.js index db8344382..081201b26 100644 --- a/packages/@stylexjs/postcss-plugin/__tests__/index-test.js +++ b/packages/@stylexjs/postcss-plugin/__tests__/index-test.js @@ -40,10 +40,7 @@ describe('@stylexjs/postcss-plugin', () => { expect(result.css).toMatchInlineSnapshot(` ".x1u857p9{background-color:green} - .xrkmrrc{background-color:red} - .xg6iff7{min-height:100vh} - .x1gan7if{padding-bottom:32px} - .x1miatn0{padding-top:32px}" + .xrkmrrc{background-color:red}" `); // Check that messages contain dependency information @@ -59,37 +56,19 @@ describe('@stylexjs/postcss-plugin', () => { }); test('supports CSS layers', async () => { - const result = await runStylexPostcss({ useCSSLayers: 'native' }); + const result = await runStylexPostcss({ useCSSLayers: true }); expect(result.css).toContain('@layer'); expect(result.css).toMatchInlineSnapshot(` " - @layer priority1, priority2; + @layer priority1; @layer priority1{ .x1u857p9{background-color:green} .xrkmrrc{background-color:red} - } - @layer priority2{ - .xg6iff7{min-height:100vh} - .x1gan7if{padding-bottom:32px} - .x1miatn0{padding-top:32px} }" `); }); - test('supports polyfill for CSS layers', async () => { - const result = await runStylexPostcss({ useCSSLayers: 'polyfill' }); - - expect(result.css).toMatchInlineSnapshot(` - " - .x1u857p9{background-color:green} - .xrkmrrc{background-color:red} - .xg6iff7:not(#\\#){min-height:100vh} - .x1gan7if:not(#\\#){padding-bottom:32px} - .x1miatn0:not(#\\#){padding-top:32px}" - `); - }); - test('handles exclude patterns', async () => { const result = await runStylexPostcss({ exclude: ['**/styles-second.js'], @@ -98,12 +77,9 @@ describe('@stylexjs/postcss-plugin', () => { // Should not contain styles-second.js styles expect(result.css).not.toContain('green'); - expect(result.css).toMatchInlineSnapshot(` - ".xrkmrrc{background-color:red} - .xg6iff7{min-height:100vh} - .x1gan7if{padding-bottom:32px} - .x1miatn0{padding-top:32px}" - `); + expect(result.css).toMatchInlineSnapshot( + '".xrkmrrc{background-color:red}"', + ); }); test('respects string syntax for importSources', async () => { diff --git a/packages/@stylexjs/postcss-plugin/package.json b/packages/@stylexjs/postcss-plugin/package.json index 5a3f8636f..0e78d4e0f 100644 --- a/packages/@stylexjs/postcss-plugin/package.json +++ b/packages/@stylexjs/postcss-plugin/package.json @@ -14,7 +14,6 @@ "dependencies": { "@babel/core": "^7.26.8", "@stylexjs/babel-plugin": "0.13.0", - "@csstools/postcss-cascade-layers": "^5.0.1", "postcss": "^8.4.49", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", diff --git a/packages/@stylexjs/postcss-plugin/src/builder.js b/packages/@stylexjs/postcss-plugin/src/builder.js index 2331b4a85..b2eee9365 100644 --- a/packages/@stylexjs/postcss-plugin/src/builder.js +++ b/packages/@stylexjs/postcss-plugin/src/builder.js @@ -151,7 +151,7 @@ function createBuilder() { }), ); - const css = bundler.bundle({ useCSSLayers: useCSSLayers !== 'none' }); + const css = bundler.bundle({ useCSSLayers }); return css; } diff --git a/packages/@stylexjs/postcss-plugin/src/plugin.js b/packages/@stylexjs/postcss-plugin/src/plugin.js index 15c0a8566..b6a5aac1d 100644 --- a/packages/@stylexjs/postcss-plugin/src/plugin.js +++ b/packages/@stylexjs/postcss-plugin/src/plugin.js @@ -6,13 +6,10 @@ */ const postcss = require('postcss'); const createBuilder = require('./builder'); -const cascadeLayers = require('@csstools/postcss-cascade-layers'); module.exports = function createPlugin() { const PLUGIN_NAME = '@stylexjs/postcss-plugin'; - const VALID_CSS_LAYERS = ['none', 'native', 'polyfill']; - const builder = createBuilder(); const isDev = process.env.NODE_ENV === 'development'; @@ -24,7 +21,7 @@ module.exports = function createPlugin() { babelConfig = {}, include, exclude, - useCSSLayers = 'none', + useCSSLayers = false, importSources = ['@stylexjs/stylex', 'stylex'], }) => { exclude = [ @@ -34,12 +31,6 @@ module.exports = function createPlugin() { ...(exclude ?? []), ]; - if (!VALID_CSS_LAYERS.includes(useCSSLayers)) { - throw new Error( - `Invalid useCSSLayers value: "${useCSSLayers}". Valid values are: ${VALID_CSS_LAYERS.join(', ')}`, - ); - } - // Whether to skip the error when transforming StyleX rules. // Useful in watch mode where Fast Refresh can recover from errors. // Initial transform will still throw errors in watch mode to surface issues early. @@ -87,18 +78,7 @@ module.exports = function createPlugin() { const css = await builder.build({ shouldSkipTransformError, }); - let processedCss = css; - if (useCSSLayers === 'polyfill') { - const result = await postcss([ - cascadeLayers({ - onRevertLayerKeyword: 'warn', - onConditionalRulesChangingLayerOrder: 'warn', - onImportLayerRule: 'warn', - }), - ]).process(css); - processedCss = result.css; - } - const parsed = await postcss.parse(processedCss, { + const parsed = await postcss.parse(css, { from: fileName, }); diff --git a/packages/@stylexjs/rollup-plugin/README.md b/packages/@stylexjs/rollup-plugin/README.md index 7829cf758..15f9125a6 100644 --- a/packages/@stylexjs/rollup-plugin/README.md +++ b/packages/@stylexjs/rollup-plugin/README.md @@ -43,16 +43,9 @@ The name of the output css file. --- ### useCSSLayers ```js -useCSSLayers: 'none' | 'native' | 'polyfill' // Default: 'none' +useCSSLayers: boolean // Default: true ``` - -The `useCSSLayers` option controls how StyleX handles CSS specificity and layer -management. It supports three strategies: - -- `'none'` (default): Uses `:not(#\#)` to handle CSS specificity without layers -- `'native'`: Uses native CSS `@layer` for handling CSS specificity -- `'polyfill'`: Uses `@csstools/postcss-cascade-layers` to polyfill CSS layers - for browsers that don't support them +Enabling this option switches Stylex from using `:not(#\#)` to using `@layers` for handling CSS specificity. --- ### babelConfig diff --git a/packages/@stylexjs/rollup-plugin/__tests__/index-test.js b/packages/@stylexjs/rollup-plugin/__tests__/index-test.js index 49281b653..03ff84aff 100644 --- a/packages/@stylexjs/rollup-plugin/__tests__/index-test.js +++ b/packages/@stylexjs/rollup-plugin/__tests__/index-test.js @@ -34,7 +34,7 @@ describe('rollup-plugin-stylex', () => { exclude: [/npmStyles\.js/], }), stylexPlugin({ - useCSSLayers: 'native', + useCSSLayers: true, ...options, lightningcssOptions: { minify: false }, }), diff --git a/packages/@stylexjs/rollup-plugin/src/index.js b/packages/@stylexjs/rollup-plugin/src/index.js index fc2ba4756..2ca035421 100644 --- a/packages/@stylexjs/rollup-plugin/src/index.js +++ b/packages/@stylexjs/rollup-plugin/src/index.js @@ -37,7 +37,7 @@ export type PluginOptions = $ReadOnly<{ plugins?: $ReadOnlyArray, presets?: $ReadOnlyArray, }>, - useCSSLayers?: 'none' | 'native' | 'polyfill', + useCSSLayers?: boolean, lightningcssOptions?: Omit< TransformOptions<{}>, 'code' | 'filename' | 'visitor', @@ -63,7 +63,7 @@ export default function stylexPlugin({ fileName = 'stylex.css', babelConfig: { plugins = [], presets = [] } = {}, importSources = ['stylex', '@stylexjs/stylex'], - useCSSLayers = 'none', + useCSSLayers = false, lightningcssOptions, ...options }: PluginOptions = {}): Plugin<> { @@ -73,12 +73,12 @@ export default function stylexPlugin({ buildStart() { stylexRules = {}; }, - generateBundle(this: PluginContext) { + async generateBundle(this: PluginContext) { const rules: Array = Object.values(stylexRules).flat(); if (rules.length > 0) { - const collectedCSS = stylexBabelPlugin.processStylexRules( + const collectedCSS = await stylexBabelPlugin.processStylexRules( rules, - useCSSLayers !== 'none', + useCSSLayers, ); // Process the CSS using lightningcss diff --git a/packages/docs/components/playground-utils/files.js b/packages/docs/components/playground-utils/files.js index a13229556..bd001eab2 100644 --- a/packages/docs/components/playground-utils/files.js +++ b/packages/docs/components/playground-utils/files.js @@ -101,7 +101,7 @@ module.exports = { plugins: { '@stylexjs/postcss-plugin': { include: ['src/**/*.{js,jsx}'], - useCSSLayers: 'native', + useCSSLayers: true, babelConfig, }, }, diff --git a/packages/docs/docs/api/configuration/postcss-plugin.mdx b/packages/docs/docs/api/configuration/postcss-plugin.mdx index 391d90db0..aa2458e02 100644 --- a/packages/docs/docs/api/configuration/postcss-plugin.mdx +++ b/packages/docs/docs/api/configuration/postcss-plugin.mdx @@ -65,13 +65,7 @@ Array of paths or glob patterns to compile. ### useCSSLayers ```js -useCSSLayers: 'none' | 'native' | 'polyfill'; // Default: 'none' +useCSSLayers: boolean; // Default: false ``` -The `useCSSLayers` option controls how StyleX handles CSS specificity and layer -management. It supports three strategies: - -- `'none'` (default): Uses `:not(#\#)` to handle CSS specificity without layers -- `'native'`: Uses native CSS `@layer` for handling CSS specificity -- `'polyfill'`: Uses `@csstools/postcss-cascade-layers` to polyfill CSS layers - for browsers that don't support them +Enabling this option switches Stylex from using `:not(#\#)` to using `@layers` for handling CSS specificity. diff --git a/packages/docs/docs/learn/03-installation.mdx b/packages/docs/docs/learn/03-installation.mdx index 793f664ff..738ca5d1b 100755 --- a/packages/docs/docs/learn/03-installation.mdx +++ b/packages/docs/docs/learn/03-installation.mdx @@ -74,7 +74,7 @@ module.exports = { // any other files that should be included // this should include NPM dependencies that use StyleX ], - useCSSLayers: 'none', + useCSSLayers: true, }, autoprefixer: {}, }, diff --git a/packages/docs/scripts/make-stylex-sheet.js b/packages/docs/scripts/make-stylex-sheet.js index 4383d4735..055f9d98e 100644 --- a/packages/docs/scripts/make-stylex-sheet.js +++ b/packages/docs/scripts/make-stylex-sheet.js @@ -77,7 +77,9 @@ async function genSheet() { const ruleSets = await Promise.all(allFiles.map(transformFile)); - const generatedCSS = stylexBabelPlugin.processStylexRules(ruleSets.flat()); + const generatedCSS = await stylexBabelPlugin.processStylexRules( + ruleSets.flat(), + ); await mkdirp(path.join(__dirname, '../.stylex/')); From 4795233d078c321a8b8b4d7445a484f9302d8dc5 Mon Sep 17 00:00:00 2001 From: jeongminsang Date: Wed, 11 Jun 2025 21:03:34 +0900 Subject: [PATCH 4/5] fix: changed incorrect initial value of useCSSLayers --- packages/@stylexjs/cli/src/index.js | 2 +- packages/@stylexjs/cli/src/options.js | 4 ++-- packages/@stylexjs/rollup-plugin/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@stylexjs/cli/src/index.js b/packages/@stylexjs/cli/src/index.js index 738ce6e43..fa68823be 100755 --- a/packages/@stylexjs/cli/src/index.js +++ b/packages/@stylexjs/cli/src/index.js @@ -71,7 +71,7 @@ const modules_EXPERIMENTAL: $ReadOnlyArray = const babelPresets: $ReadOnlyArray = args.babelPresets; const babelPluginsPre: $ReadOnlyArray = args.babelPluginsPre; const babelPluginsPost: $ReadOnlyArray = args.babelPluginsPost; -const useCSSLayers: boolean = args.layers; +const useCSSLayers: boolean = args.useCSSLayers; const styleXConfig: StyleXOptions = (config.styleXConfig as $FlowFixMe) ?? {}; const cliArgsConfig: CliConfig = { diff --git a/packages/@stylexjs/cli/src/options.js b/packages/@stylexjs/cli/src/options.js index b6420e839..f54f75056 100644 --- a/packages/@stylexjs/cli/src/options.js +++ b/packages/@stylexjs/cli/src/options.js @@ -35,8 +35,8 @@ const options = { useCSSLayers: { alias: 'l', describe: 'Use CSS layers to optimize CSS rendering', - type: 'string', - default: 'none', + type: 'boolean', + default: false, }, babelPresets: { describe: diff --git a/packages/@stylexjs/rollup-plugin/README.md b/packages/@stylexjs/rollup-plugin/README.md index 15f9125a6..f05dc4b64 100644 --- a/packages/@stylexjs/rollup-plugin/README.md +++ b/packages/@stylexjs/rollup-plugin/README.md @@ -43,7 +43,7 @@ The name of the output css file. --- ### useCSSLayers ```js -useCSSLayers: boolean // Default: true +useCSSLayers: boolean // Default: false ``` Enabling this option switches Stylex from using `:not(#\#)` to using `@layers` for handling CSS specificity. From b55950228bef8e070e8834d262791bf57d92515b Mon Sep 17 00:00:00 2001 From: jeongminsang Date: Wed, 11 Jun 2025 21:07:04 +0900 Subject: [PATCH 5/5] chore: remove unnecessary entries --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 07b20c96a..5b86e9d82 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,5 @@ "git update-index --again", "npm run lint" ] - }, - "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72" + } }