├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .rollup.js ├── .tape.js ├── CHANGELOG.md ├── CONTRIBUTING.md ├── INSTALL.md ├── LICENSE.md ├── README.md ├── package.json ├── src ├── cli.js ├── lib │ ├── get-transformed-insertions.js │ ├── get-unsupported-browsers-by-feature.js │ ├── ids-by-execution-order.js │ ├── plugins-by-id.js │ └── write-to-exports.js ├── patch │ └── postcss-system-ui-font-family.js └── postcss.js └── test ├── basic.autoprefixer.expect.css ├── basic.autoprefixer.false.expect.css ├── basic.ch38.expect.css ├── basic.css ├── basic.expect.css ├── basic.ff49.expect.css ├── basic.nesting.expect.css ├── basic.stage0-ff49.expect.css ├── basic.stage0.expect.css ├── custom-properties.css ├── custom-properties.disabled.expect.css ├── custom-properties.enabled.expect.css ├── custom-properties.expect.css ├── generated-custom-exports.css ├── generated-custom-exports.js ├── generated-custom-exports.json ├── generated-custom-exports.mjs ├── import.css ├── import.expect.css ├── insert.after.expect.css ├── insert.before.expect.css └── insert.css /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{json,md,yml}] 14 | indent_size = 2 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node: [12, 14, 16] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: ${{ matrix.node }} 16 | - run: npm install --ignore-scripts 17 | - run: npm run build 18 | - run: npm run test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | package-lock.json 4 | yarn.lock 5 | *.log* 6 | *.result.css 7 | .* 8 | !.editorconfig 9 | !.gitattributes 10 | !.github 11 | !.gitignore 12 | !.rollup.js 13 | !.tape.js 14 | -------------------------------------------------------------------------------- /.rollup.js: -------------------------------------------------------------------------------- 1 | import pkg from './package.json' 2 | 3 | export default { 4 | ...pkg.rollup, 5 | plugins: pkg.rollup.plugins.map(plugin => require(plugin)()), 6 | onwarn(warning, warn) { 7 | if (warning.code !== 'UNRESOLVED_IMPORT') warn(warning) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.tape.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'basic': { 3 | message: 'supports basic usage' 4 | }, 5 | 'basic:ff49': { 6 | message: 'supports { browsers: "ff >= 49" } usage', 7 | options: { 8 | browsers: 'ff >= 49' 9 | } 10 | }, 11 | 'basic:ch38': { 12 | message: 'supports { browsers: "chrome >= 38" } usage', 13 | options: { 14 | browsers: 'chrome >= 38' 15 | } 16 | }, 17 | 'basic:stage0': { 18 | message: 'supports { stage: 0 } usage', 19 | options: { 20 | stage: 0 21 | } 22 | }, 23 | 'basic:stage0-ff49': { 24 | message: 'supports { browsers: "ff >= 49", stage: 0 } usage', 25 | options: { 26 | browsers: 'ff >= 49', 27 | stage: 0 28 | } 29 | }, 30 | 'basic:nesting': { 31 | message: 'supports { stage: false, features: { "nesting-rules": true } } usage', 32 | options: { 33 | stage: false, 34 | features: { 35 | 'nesting-rules': true 36 | } 37 | } 38 | }, 39 | 'basic:autoprefixer': { 40 | message: 'supports { autoprefixer: { add: false } } usage', 41 | options: { 42 | autoprefixer: { 43 | add: false 44 | } 45 | } 46 | }, 47 | 'basic:autoprefixer:false': { 48 | message: 'supports { autoprefixer: false } usage', 49 | options: { 50 | autoprefixer: false 51 | } 52 | }, 53 | 'custom-properties': { 54 | message: 'supports { browsers: "ie >= 10" } usage', 55 | options: { 56 | browsers: 'ie >= 10' 57 | } 58 | }, 59 | 'custom-properties:disabled': { 60 | message: 'supports { browsers: "ie >= 10", features: { "custom-properties": false } } usage', 61 | options: { 62 | browsers: 'ie >= 10', 63 | features: { 64 | 'custom-properties': false 65 | } 66 | } 67 | }, 68 | 'custom-properties:enabled': { 69 | message: 'supports { browsers: "chrome >= 60", features: { "custom-properties": true } } usage', 70 | options: { 71 | browsers: 'chrome >= 60', 72 | features: { 73 | 'custom-properties': true 74 | } 75 | } 76 | }, 77 | 'insert:before': { 78 | message: 'supports { stage: 1, features: { "color-mod-function": true }, insertBefore: { "color-mod-function": [ require("postcss-simple-vars") ] } } usage', 79 | options: { 80 | stage: 1, 81 | features: { 82 | 'color-mod-function': true 83 | }, 84 | insertBefore: { 85 | 'color-mod-function': [ 86 | require('postcss-simple-vars') 87 | ] 88 | } 89 | } 90 | }, 91 | 'insert:after': { 92 | message: 'supports { stage: 1, insertAfter: { "color-mod-function": [ require("postcss-simple-vars")() ] } } usage', 93 | options: { 94 | stage: 1, 95 | insertAfter: { 96 | 'color-mod-function': require('postcss-simple-vars') 97 | } 98 | }, 99 | }, 100 | 'insert:after:exec': { 101 | message: 'supports { stage: 2, features: { "color-mod-function": { unresolved: "ignore" } }, insertAfter: { "color-mod-function": require("postcss-simple-vars")() } } usage', 102 | options: { 103 | stage: 2, 104 | insertAfter: { 105 | 'color-mod-function': require('postcss-simple-vars')() 106 | } 107 | }, 108 | expect: 'insert.after.expect.css' 109 | }, 110 | 'insert:after:array': { 111 | message: 'supports { stage: 1, after: { "color-mod-function": [ require("postcss-simple-vars") ] } } usage', 112 | options: { 113 | stage: 1, 114 | insertAfter: { 115 | 'color-mod-function': [ 116 | require('postcss-simple-vars') 117 | ] 118 | }, 119 | features: { 120 | 'color-mod-function': { 121 | unresolved: 'ignore' 122 | } 123 | } 124 | }, 125 | expect: 'insert.after.expect.css' 126 | }, 127 | 'import': { 128 | message: 'supports { importFrom: { customMedia, customProperties, customSelectors, environmentVariables } } usage', 129 | options: { 130 | importFrom: { 131 | customMedia: { 132 | '--narrow-window': '(max-width: env(--sm))' 133 | }, 134 | customProperties: { 135 | '--order': '1' 136 | }, 137 | customSelectors: { 138 | ':--heading': 'h1, h2, h3, h4, h5, h6' 139 | }, 140 | environmentVariables: { 141 | '--sm': '40rem' 142 | } 143 | }, 144 | stage: 0 145 | } 146 | }, 147 | 'basic:export': { 148 | message: 'supports { stage: 0 } usage', 149 | options: { 150 | stage: 0, 151 | exportTo: [ 152 | 'test/generated-custom-exports.css', 153 | 'test/generated-custom-exports.js', 154 | 'test/generated-custom-exports.json', 155 | 'test/generated-custom-exports.mjs' 156 | ] 157 | }, 158 | expect: 'basic.stage0.expect.css', 159 | result: 'basic.stage0.result.css', 160 | before() { 161 | global.__exportTo = { 162 | css: require('fs').readFileSync('test/generated-custom-exports.css', 'utf8'), 163 | js: require('fs').readFileSync('test/generated-custom-exports.js', 'utf8'), 164 | json: require('fs').readFileSync('test/generated-custom-exports.json', 'utf8'), 165 | mjs: require('fs').readFileSync('test/generated-custom-exports.mjs', 'utf8') 166 | }; 167 | }, 168 | after() { 169 | global.__exportAs = { 170 | css: require('fs').readFileSync('test/generated-custom-exports.css', 'utf8'), 171 | js: require('fs').readFileSync('test/generated-custom-exports.js', 'utf8'), 172 | json: require('fs').readFileSync('test/generated-custom-exports.json', 'utf8'), 173 | mjs: require('fs').readFileSync('test/generated-custom-exports.mjs', 'utf8') 174 | }; 175 | 176 | Object.keys(global.__exportTo).forEach(key => { 177 | if (global.__exportTo[key] !== global.__exportAs[key]) { 178 | throw new Error(`The original ${key} file did not match the freshly exported copy`); 179 | } 180 | }); 181 | } 182 | } 183 | }; 184 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changes to PostCSS Preset Env 2 | 3 | ### 7.0.1 (November 19, 2021) 4 | 5 | - Fixed infinite loop in double-position-gradients [223](https://github.com/csstools/postcss-preset-env/issues/223) 6 | - Fixed "Unknown word" errors in when parsing CSS values [224](https://github.com/csstools/postcss-preset-env/issues/224) 7 | - Fixed "undefined" CSS values after transforms with postcss-place [225](https://github.com/csstools/postcss-preset-env/issues/225) 8 | - Updated `postcss-color-functional-notation` to 4.0.1 (patch) 9 | - Updated `postcss-double-position-gradients` to 3.0.1 (patch) 10 | - Updated `postcss-env-function` to 4.0.2 (patch) 11 | - Updated `postcss-image-set-function` to 4.0.2 (patch) 12 | - Updated `postcss-lab-function` to 4.0.1 (patch) 13 | - Updated `postcss-nesting` to 10.0.2 (patch) 14 | - Updated `postcss-place` to 7.0.1 (patch) 15 | 16 | ### 7.0.0 (November 16, 2021) 17 | 18 | - Updated `autoprefixer` to 10.4.0 (major) 19 | - Updated `browserslist` to 4.17.5 (minor) 20 | - Updated `caniuse-lite` to 1.0.30001272 (patch) 21 | - Updated `css-blank-pseudo` to 2.0.0 (major) 22 | - Updated `css-has-pseudo` to 2.0.0 (major) 23 | - Updated `css-prefers-color-scheme` to 5.0.0 (major) 24 | - Updated `cssdb` to 5.0.0 (major) 25 | - Updated `postcss` to 8.3.0 (major) 26 | - Updated `postcss-attribute-case-insensitive` to 5.0.0 (major) 27 | - Updated `postcss-color-functional-notation` to 4.0.0 (major) 28 | - Updated `postcss-color-hex-alpha` to 8.0.0 (major) 29 | - Updated `postcss-color-rebeccapurple` to 7.0.0 (major) 30 | - Updated `postcss-custom-media` to 8.0.0 (major) 31 | - Updated `postcss-custom-properties` to 12.0.0 (major) 32 | - Updated `postcss-custom-selectors` to 6.0.0 (major) 33 | - Updated `postcss-dir-pseudo-class` to 6.0.0 (major) 34 | - Updated `postcss-double-position-gradients` to 3.0.0 (major) 35 | - Updated `postcss-env-function` to 4.0.1 (major) 36 | - Updated `postcss-focus-visible` to 6.0.1 (major) 37 | - Updated `postcss-focus-within` to 5.0.1 (major) 38 | - Updated `postcss-font-variant` to 5.0.0 (major) 39 | - Updated `postcss-gap-properties` to 3.0.0 (major) 40 | - Updated `postcss-image-set-function` to 4.0.0 (major) 41 | - Updated `postcss-initial` to 3.0.4 (patch) 42 | - Updated `postcss-lab-function` to 4.0.0 (major) 43 | - Updated `postcss-logical` to 5.0.0 (major) 44 | - Updated `postcss-media-minmax` to 5.0.0 (major) 45 | - Updated `postcss-nesting` to 10.0.0 (major) 46 | - Updated `postcss-overflow-shorthand` to 3.0.0 (major) 47 | - Updated `postcss-page-break` to 3.0.4 (major) 48 | - Updated `postcss-place` to 7.0.0 (major) 49 | - Updated `postcss-pseudo-class-any-link` to 7.0.0 (major) 50 | - Updated `postcss-replace-overflow-wrap` to 4.0.0 (major) 51 | - Removed `postcss-selector-matches` 52 | - Removed `postcss-color-gray` 53 | - Updated support for Node 12+ (major) 54 | 55 | ### 6.7.0 (July 8, 2019) 56 | 57 | - Fixed the issue of autoprefixer alerting an upcoming change to the API 58 | - Updated `autoprefixer` to 9.6.1 (minor) 59 | - Updated `browserslist` to 4.6.4 (minor) 60 | - Updated `cssdb` to 4.4.0 (minor) 61 | - Updated `caniuse-lite` to 1.0.30000981 (patch) 62 | - Updated `postcss` to 7.0.17 (patch) 63 | - Updated `postcss-color-hex-alpha` to 5.0.3 (patch) 64 | - Updated `postcss-custom-media` to 7.0.8 (patch) 65 | - Updated `postcss-custom-properties` to 8.0.11 (patch) 66 | 67 | ### 6.6.0 (February 28, 2019) 68 | 69 | - Moved browserslist detection from using each input file per process to using 70 | the working directory on intialization, as was implied by the documentation. 71 | If fixing this previously undocumented behavior causes any harm to existing 72 | projects, it can be easily rolled back in a subsequent patch. For the 73 | majority of projects — those with a singular browserslist configuration and 74 | potentially many individually processed CSS files — we should expect reported 75 | build times around 35 seconds to drop to less than 2 seconds. 76 | - Updated `browserslist` to 4.4.2 (minor) 77 | - Updated `autoprefixer` to 9.4.9 (patch) 78 | - Updated `caniuse-lite` to 1.0.30000939 (patch) 79 | - Updated `postcss` to 7.0.14 (patch) 80 | - Updated `postcss-attribute-case-insensitive` to 4.0.1 (patch) 81 | 82 | ### 6.5.0 (December 12, 2018) 83 | 84 | - Added `css-blank-pseudo` polyfill 85 | - Added `css-has-pseudo` polyfill 86 | - Updated `autoprefixer` to 9.4.2 (minor) 87 | - Updated `browserslist` to 4.3.5 (minor) 88 | - Updated `caniuse-lite` to 1.0.30000918 (patch) 89 | - Updated `css-prefers-color-scheme` to 3.1.1 (minor, patch for this project) 90 | - Updated `cssdb` to 4.3.0 (minor) 91 | - Updated `postcss` to 7.0.6 (patch) 92 | 93 | ### 6.4.0 (November 6, 2018) 94 | 95 | - Fixed `exportTo` option to export Custom Media, Custom Properties, and Custom 96 | Selectors all to the same function, object, or file 97 | - Added `css-prefers-color-scheme` 3.0.0 (major, non-breaking for this project) 98 | - Updated `cssdb` to 4.2.0 (minor) 99 | 100 | ### 6.3.1 (November 5, 2018) 101 | 102 | - Updated `caniuse-lite` to 1.0.30000905 (patch) 103 | - Updated `postcss-custom-properties` to 8.0.9 (patch) 104 | 105 | ### 6.3.0 (October 28, 2018) 106 | 107 | - Added `postcss-double-position-gradients` 1.0.0 (major, non-breaking for this project) 108 | - Updated `autoprefixer` to 9.3.1 (minor) 109 | - Updated `browserslist` to 4.3.4 (patch) 110 | - Updated `caniuse-lite` to 1.0.30000899 (patch) 111 | - Updated `cssdb` to 4.1.0 (major, non-breaking for this project) 112 | 113 | ### 6.2.0 (October 22, 2018) 114 | 115 | - Updated `autoprefixer` to 9.2.1 (minor) 116 | - Updated `browserslist` to 4.3.1 (minor) 117 | 118 | ### 6.1.2 (October 19, 2018) 119 | 120 | - Updated `browserslist` to 4.2.1 (patch) 121 | - Updated `caniuse-lite` to 1.0.30000893 (patch) 122 | - Updated `postcss-custom-media` to 7.0.7 (patch) 123 | 124 | ### 6.1.1 (October 12, 2018) 125 | 126 | - Updated: `postcss-custom-media` to 7.0.6 (patch) 127 | 128 | ### 6.1.0 (October 10, 2018) 129 | 130 | - Added: `postcss-color-gray` 131 | - Added: Passing `autoprefixer: false` disables autoprefixer 132 | - Updated: `browserslist` to 4.2.0 (minor) 133 | - Updated: `caniuse-lite` to 1.0.30000890 (patch) 134 | 135 | ### 6.0.10 (October 2, 2018) 136 | 137 | - Updated: `postcss-custom-properties` to 8.0.8 (patch) 138 | 139 | ### 6.0.9 (October 2, 2018) 140 | 141 | - Updated: `browserslist` to 4.1.2 (patch) 142 | - Updated: `postcss` to 7.0.5 (patch) 143 | - Updated: `postcss-custom-properties` to 8.0.7 (patch) 144 | 145 | ### 6.0.8 (October 1, 2018) 146 | 147 | - Updated: `caniuse-lite` to 1.0.30000888 (patch) 148 | - Updated: `postcss` to 7.0.4 (patch) 149 | 150 | **Did you hear? PostCSS Preset Env is now part of Create React App!** 🎉 151 | 152 | ### 6.0.7 (September 23, 2018) 153 | 154 | - Updated: `postcss` to 7.0.3 (patch) 155 | - Updated: `postcss-custom-properties` to 8.0.6 (patch) 156 | 157 | ### 6.0.6 (September 23, 2018) 158 | 159 | - Updated: `postcss-custom-media` to 7.0.4 (patch) 160 | 161 | ### 6.0.5 (September 23, 2018) 162 | 163 | - Updated: `postcss-color-mod-function` to 3.0.3 (patch) 164 | 165 | ### 6.0.4 (September 23, 2018) 166 | 167 | - Updated: `caniuse-lite` to 1.0.30000887 (patch) 168 | - Updated: `postcss-color-mod-function` to 3.0.2 (patch) 169 | 170 | ### 6.0.3 (September 21, 2018) 171 | 172 | - Updated: `caniuse-lite` to 1.0.30000885 (patch) 173 | - Updated: `postcss-custom-properties` to 8.0.5 (patch) 174 | 175 | ### 6.0.2 (September 20, 2018) 176 | 177 | - Fixed: Do not break on an empty `importFrom` object 178 | - Fixed: Actually run `postcss-env-function` 179 | 180 | ### 6.0.1 (September 20, 2018) 181 | 182 | - Fixed: Issue with the `system-ui` font family polyfill by replacing 183 | `postcss-font-family-system-ui` with an internal polyfill, at least until the 184 | problem with the original plugin is resolved. 185 | 186 | ### 6.0.0 (September 20, 2018) 187 | 188 | - Added: Support for PostCSS 7+ 189 | - Added: Support for PostCSS Values Parser 2+ 190 | - Added: Support for PostCSS Selector Parser 5+ 191 | - Added: Support for Node 6+ 192 | - Updated: All 28 plugins 193 | 194 | ### 5.4.0 (July 25, 2018) 195 | 196 | - Added: `toggle` option to override which features are enabled or disabled 197 | - Deprecated: toggle features with `toggle`, not `features` 198 | 199 | ### 5.3.0 (July 24, 2018) 200 | 201 | - Updated: `postcss-lab-function` to v1.1.0 (minor update) 202 | 203 | ### 5.2.3 (July 21, 2018) 204 | 205 | - Updated: `postcss-color-mod-function` to v2.4.3 (patch update) 206 | 207 | ### 5.2.2 (July 13, 2018) 208 | 209 | - Updated: `autoprefixer` to v8.6.5 (patch update) 210 | - Updated: `caniuse-lite` to v1.0.30000865 (patch update) 211 | - Updated: `postcss-color-functional-notation` to v1.0.2 (patch update) 212 | 213 | ### 5.2.1 (June 26, 2018) 214 | 215 | - Updated: `caniuse-lite` to v1.0.30000859 (patch update) 216 | - Updated: `postcss-attribute-case-insensitive` to v3.0.1 (patch update) 217 | 218 | ### 5.2.0 (June 25, 2018) 219 | 220 | - Updated: `autoprefixer` to v8.6.3 (minor update) 221 | - Updated: `caniuse-lite` to v1.0.30000858 (patch update) 222 | - Updated: `postcss` to 6.0.23 (patch update) 223 | - Updated: `postcss-nesting` to v6.0.0 (major internal update, non-breaking for this project) 224 | 225 | ### 5.1.0 (May 21, 2018) 226 | 227 | - Added: `autoprefixer` option to pass options into autoprefixer 228 | - Updated: `autoprefixer` to v8.5.0 (minor update) 229 | - Updated: `browserslist` to v3.2.8 (patch update) 230 | - Updated: `caniuse-lite` to v1.0.30000844 (patch update) 231 | - Updated: `postcss-color-functional-notation` to v1.0.1 (patch update) 232 | 233 | ### 5.0.0 (May 11, 2018) 234 | 235 | - Added: `autoprefixer` 236 | - Added: `postcss-color-functional-notation` 237 | - Added: `postcss-env-function` 238 | - Added: `postcss-lab-function` 239 | - Added: `postcss-place` 240 | - Added: `postcss-gap-properties` 241 | - Added: `postcss-overflow-shorthand` 242 | - Updated: `cssdb` to v3.1.0 (major update) 243 | - Updated: In conformance with cssdb v3, the default stage is now 2 244 | - Updated: `postcss-attribute-case-insensitive` to v3.0.0 (major update) 245 | - Updated: `postcss-pseudo-class-any-link` to v5.0.0 (major update) 246 | - Updated: `postcss-image-set-function` to v2.0.0 (major update) 247 | - Updated: `postcss-dir-pseudo-class` to v4.0.0 (major update) 248 | - Updated: `postcss-color-rebeccapurple` to v3.1.0 (minor update) 249 | - Updated: `postcss` to v6.0.22 (patch update) 250 | - Updated: `browserslist` to v3.2.7 (patch update) 251 | - Updated: `caniuse-lite` to v1.0.30000839 (patch update) 252 | 253 | All plugins now conform to the latest stable releases of `postcss-value-parser` 254 | v1.5.0 and `postcss-selector-parser` v4.0.0. 255 | 256 | ### 4.1.0 (April 23, 2018) 257 | 258 | - Updated: `browserslist` to v3.2.5 (patch update) 259 | - Updated: `caniuse-lite` to v1.0.30000830 (patch update) 260 | - Updated: `postcss-apply` to v0.10.0 (minor update) 261 | - Updated: `postcss-nesting` to v5.0.0 (major update, non-breaking for this project) 262 | 263 | ### 4.0.0 (April 7, 2018) 264 | 265 | - Added: `postcss-focus-within` 266 | - Updated: `postcss-focus-visible` to v3.0.0 (major update) 267 | - Updated: `caniuse-lite` to v1.0.30000824 (patch update) 268 | - Updated: `cssdb` to v2.0.0 (major update) 269 | - Changed: All `specificationId` names to new `id` names for the `cssdb` update. 270 | 271 | ### 3.5.0 (April 5, 2018) 272 | 273 | - Fixed: `selectors-matches-pseudo` mapping to allow `:matches` polyfilling 274 | - Updated: `postcss-dir-pseudo-class` to v3.0.0 (major update, non-breaking for this project) 275 | - Updated: `postcss-logical` to v1.1.1 (minor update) 276 | - Updated: `postcss` to v6.0.21 (patch update) 277 | - Updated: `browserslist` to v3.2.4 (patch update) 278 | - Updated: `caniuse-lite` to v1.0.30000823 (patch update) 279 | 280 | ### 3.4.0 (March 18, 2018) 281 | 282 | - Updated: `browserslist` to v3.2.0 (minor update) 283 | - Updated: `postcss` to v6.0.20 (patch update) 284 | - Updated: `postcss-image-set-polyfill` to `@csstools/postcss-image-set-function` (hopefully temporarily) 285 | 286 | ### 3.3.0 (March 16, 2018) 287 | 288 | - Updated: `postcss-apply` to v0.9.0 (minor update) 289 | - Updated: `browserslist` to v3.1.2 (patch update) 290 | - Updated: `caniuse-lite` to v1.0.30000815 (patch update) 291 | - Updated: distribution to cjs and es bundles 292 | 293 | ### 3.2.2 (February 27, 2018) 294 | 295 | - Updated: `postcss-color-mod-function` to v2.4.2 (patch update) 296 | 297 | ### 3.2.1 (February 21, 2018) 298 | 299 | - Updated: Use the latest tested version of all dependencies 300 | 301 | ### 3.2.0 (February 18, 2018) 302 | 303 | - Added: `postcss-page-break` which has moved here from Autoprefixer 304 | 305 | ### 3.1.0 (February 17, 2018) 306 | 307 | - Added: `postcss-focus-visible` 308 | 309 | ### 3.0.0 (February 16, 2018) 310 | 311 | - Updated: `postcss-color-mod-function` to v2.4 (minor update) 312 | - Updated: `postcss-custom-properties` to v7.0 (major update) 313 | 314 | ### 2.2.0 (February 14, 2018) 315 | 316 | - Updated: `browserslist` to v3.1 (major update) 317 | - Updated: `postcss-color-mod-function` to v2.3 (minor update) 318 | - Improved: cleaned up one reusable variable and added a few tests 319 | 320 | ### 2.1.0 (January 22, 2018) 321 | 322 | - Updated: `cssdb` to v1.5 (minor update) 323 | - Updated: `postcss-color-mod-function` to v2.2 (major update) 324 | - Updated: `postcss-font-family-system-ui` to v3.0 (repo update) 325 | 326 | ### 2.0.0 (January 16, 2018) 327 | 328 | - Initial version 329 | 330 | ### 1.0.0 (December 20, 2017) 331 | 332 | - Unsupported version accidentally published by a member of the community 333 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PostCSS Preset Env 2 | 3 | You want to help? You rock! Now, take a moment to be sure your contributions 4 | make sense to everyone else. 5 | 6 | ## Reporting Issues 7 | 8 | Found a problem? Want a new feature? 9 | 10 | - See if your issue or idea has [already been reported]. 11 | - Provide a [reduced test case] or a [live example]. 12 | 13 | Remember, a bug is a _demonstrable problem_ caused by _our_ code. 14 | 15 | ## Submitting Pull Requests 16 | 17 | Pull requests are the greatest contributions, so be sure they are focused in 18 | scope and avoid unrelated commits. 19 | 20 | 1. To begin; [fork this project], clone your fork, and add our upstream. 21 | ```bash 22 | # Clone your fork of the repo into the current directory 23 | git clone git@github.com:YOUR_USER/postcss-preset-env.git 24 | 25 | # Navigate to the newly cloned directory 26 | cd postcss-preset-env 27 | 28 | # Assign the original repo to a remote called "upstream" 29 | git remote add upstream git@github.com:csstools/postcss-preset-env.git 30 | 31 | # Install the tools necessary for testing 32 | npm install 33 | ``` 34 | 35 | 2. Create a branch for your feature or fix: 36 | ```bash 37 | # Move into a new branch for your feature 38 | git checkout -b feature/thing 39 | ``` 40 | ```bash 41 | # Move into a new branch for your fix 42 | git checkout -b fix/something 43 | ``` 44 | 45 | 3. If your code follows our practices, then push your feature branch: 46 | ```bash 47 | # Test current code 48 | npm test 49 | ``` 50 | ```bash 51 | # Push the branch for your new feature 52 | git push origin feature/thing 53 | ``` 54 | ```bash 55 | # Or, push the branch for your update 56 | git push origin update/something 57 | ``` 58 | 59 | That’s it! Now [open a pull request] with a clear title and description. 60 | 61 | ## Adding a new plugin to Postcss Preset Env 62 | 63 | If you want to add a new plugin, follow the 64 | [pull request guidelines](#submitting-pull-requests) while making these changes: 65 | 66 | - Ensure the feature exists in [cssdb](https://cssdb.org/). 67 | - Add the plugin to the `dependencies` list in [`package.json`](package.json). 68 | - Add the plugin to 69 | [`lib/plugins-by-id.js`](lib/plugins-by-id.js). 70 | 71 | [already been reported]: issues 72 | [fork this project]: fork 73 | [live example]: https://codepen.io/pen 74 | [open a pull request]: https://help.github.com/articles/using-pull-requests/ 75 | [reduced test case]: https://css-tricks.com/reduced-test-cases/ 76 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installing PostCSS Preset Env 2 | 3 | [PostCSS Preset Env] runs in all Node environments, with special instructions for: 4 | 5 | | [Node](#node) | [PostCSS CLI](#postcss-cli) | [Webpack](#webpack) | [Create React App](#create-react-app) | [Gulp](#gulp) | [Grunt](#grunt) | [Rollup](#rollup) | 6 | | --- | --- | --- | --- | --- | --- | --- | 7 | 8 | ## Node 9 | 10 | Add [PostCSS Preset Env] to your project: 11 | 12 | ```bash 13 | npm install postcss-preset-env --save-dev 14 | ``` 15 | 16 | Use [PostCSS Preset Env] to process your CSS: 17 | 18 | ```js 19 | const postcssPresetEnv = require('postcss-preset-env'); 20 | 21 | postcssPresetEnv.process(YOUR_CSS /*, processOptions, pluginOptions */); 22 | ``` 23 | 24 | Or use it as a [PostCSS] plugin: 25 | 26 | ```js 27 | const postcss = require('postcss'); 28 | const postcssPresetEnv = require('postcss-preset-env'); 29 | 30 | postcss([ 31 | postcssPresetEnv(/* pluginOptions */) 32 | ]).process(YOUR_CSS /*, processOptions */); 33 | ``` 34 | 35 | ## PostCSS CLI 36 | 37 | Add [PostCSS CLI] to your project: 38 | 39 | ```bash 40 | npm install postcss-cli --save-dev 41 | ``` 42 | 43 | Use [PostCSS Preset Env] in your `postcss.config.js` configuration file: 44 | 45 | ```js 46 | const postcssPresetEnv = require('postcss-preset-env'); 47 | 48 | module.exports = { 49 | plugins: [ 50 | postcssPresetEnv(/* pluginOptions */) 51 | ] 52 | } 53 | ``` 54 | 55 | ## Webpack 56 | 57 | Add [PostCSS Loader] to your project: 58 | 59 | ```bash 60 | npm install postcss-loader --save-dev 61 | ``` 62 | 63 | Use [PostCSS Preset Env] in your Webpack configuration: 64 | 65 | ```js 66 | const postcssPresetEnv = require('postcss-preset-env'); 67 | 68 | module.exports = { 69 | module: { 70 | rules: [ 71 | { 72 | test: /\.css$/, 73 | use: [ 74 | 'style-loader', 75 | { loader: 'css-loader', options: { importLoaders: 1 } }, 76 | { 77 | loader: 'postcss-loader', 78 | options: { 79 | postcssOptions: { 80 | plugins: [postcssPresetEnv(/* pluginOptions */)], 81 | }, 82 | }, 83 | }, 84 | ], 85 | }, 86 | ], 87 | }, 88 | }; 89 | ``` 90 | 91 | ## Create React App 92 | 93 | **PostCSS Preset Env is already bundled with Create React App 2.** 94 | 95 | For Create React App 1, add [React App Rewired] and [React App Rewire PostCSS] 96 | to your project: 97 | 98 | ```bash 99 | npm install react-app-rewired react-app-rewire-postcss --save-dev 100 | ``` 101 | 102 | Use [React App Rewire PostCSS] and [PostCSS Preset Env] in your 103 | `config-overrides.js` file: 104 | 105 | ```js 106 | const reactAppRewirePostcss = require('react-app-rewire-postcss'); 107 | const postcssPresetEnv = require('postcss-preset-env'); 108 | 109 | module.exports = config => reactAppRewirePostcss(config, { 110 | plugins: () => [ 111 | postcssPresetEnv(/* pluginOptions */) 112 | ] 113 | }); 114 | ``` 115 | 116 | ## Gulp 117 | 118 | Add [Gulp PostCSS] to your project: 119 | 120 | ```bash 121 | npm install gulp-postcss --save-dev 122 | ``` 123 | 124 | Use [PostCSS Preset Env] in your Gulpfile: 125 | 126 | ```js 127 | const postcss = require('gulp-postcss'); 128 | const postcssPresetEnv = require('postcss-preset-env'); 129 | 130 | gulp.task('css', () => gulp.src('./src/*.css').pipe( 131 | postcss([ 132 | postcssPresetEnv(/* pluginOptions */) 133 | ]) 134 | ).pipe( 135 | gulp.dest('.') 136 | )); 137 | ``` 138 | 139 | ## Grunt 140 | 141 | Add [Grunt PostCSS] to your project: 142 | 143 | ```bash 144 | npm install grunt-postcss --save-dev 145 | ``` 146 | 147 | Use [PostCSS Preset Env] in your Gruntfile: 148 | 149 | ```js 150 | const postcssPresetEnv = require('postcss-preset-env'); 151 | 152 | grunt.loadNpmTasks('grunt-postcss'); 153 | 154 | grunt.initConfig({ 155 | postcss: { 156 | options: { 157 | processors: [ 158 | postcssPresetEnv(/* pluginOptions */) 159 | ] 160 | }, 161 | dist: { 162 | src: '*.css' 163 | } 164 | } 165 | }); 166 | ``` 167 | 168 | ## Rollup 169 | 170 | Complete [PostCSS CLI](#postcss-cli) setup. 171 | 172 | Add [Rollup Plugin PostCSS] to your project: 173 | 174 | ```bash 175 | npm install rollup-plugin-postcss --save-dev 176 | ``` 177 | 178 | Use [Rollup Plugin PostCSS] in your rollup.config.js: 179 | 180 | ```js 181 | import postcss from 'rollup-plugin-postcss'; 182 | 183 | module.exports = { 184 | input: '...', 185 | output: {...}, 186 | plugins: [ 187 | postcss({/* options */ }) 188 | ] 189 | }; 190 | ``` 191 | 192 | [Gulp PostCSS]: https://github.com/postcss/gulp-postcss 193 | [Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss 194 | [PostCSS]: https://github.com/postcss/postcss 195 | [PostCSS CLI]: https://github.com/postcss/postcss-cli 196 | [PostCSS Loader]: https://github.com/postcss/postcss-loader 197 | [PostCSS Preset Env]: https://github.com/csstools/postcss-preset-env 198 | [React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss 199 | [React App Rewired]: https://github.com/timarney/react-app-rewired 200 | [Rollup Plugin PostCSS]: https://github.com/egoist/rollup-plugin-postcss 201 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # CC0 1.0 Universal 2 | 3 | ## Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an “owner”) of an original work of 8 | authorship and/or a database (each, a “Work”). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific works 12 | (“Commons”) that the public can reliably and without fear of later claims of 13 | infringement build upon, modify, incorporate in other works, reuse and 14 | redistribute as freely as possible in any form whatsoever and for any purposes, 15 | including without limitation commercial purposes. These owners may contribute 16 | to the Commons to promote the ideal of a free culture and the further 17 | production of creative, cultural and scientific works, or to gain reputation or 18 | greater distribution for their Work in part through the use and efforts of 19 | others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation of 22 | additional consideration or compensation, the person associating CC0 with a 23 | Work (the “Affirmer”), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and 25 | publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights (“Copyright and 31 | Related Rights”). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 1. the right to reproduce, adapt, distribute, perform, display, communicate, 34 | and translate a Work; 35 | 2. moral rights retained by the original author(s) and/or performer(s); 36 | 3. publicity and privacy rights pertaining to a person’s image or likeness 37 | depicted in a Work; 38 | 4. rights protecting against unfair competition in regards to a Work, 39 | subject to the limitations in paragraph 4(i), below; 40 | 5. rights protecting the extraction, dissemination, use and reuse of data in 41 | a Work; 42 | 6. database rights (such as those arising under Directive 96/9/EC of the 43 | European Parliament and of the Council of 11 March 1996 on the legal 44 | protection of databases, and under any national implementation thereof, 45 | including any amended or successor version of such directive); and 46 | 7. other similar, equivalent or corresponding rights throughout the world 47 | based on applicable law or treaty, and any national implementations 48 | thereof. 49 | 50 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 51 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 52 | unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright 53 | and Related Rights and associated claims and causes of action, whether now 54 | known or unknown (including existing as well as future claims and causes of 55 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 56 | duration provided by applicable law or treaty (including future time 57 | extensions), (iii) in any current or future medium and for any number of 58 | copies, and (iv) for any purpose whatsoever, including without limitation 59 | commercial, advertising or promotional purposes (the “Waiver”). Affirmer 60 | makes the Waiver for the benefit of each member of the public at large and 61 | to the detriment of Affirmer’s heirs and successors, fully intending that 62 | such Waiver shall not be subject to revocation, rescission, cancellation, 63 | termination, or any other legal or equitable action to disrupt the quiet 64 | enjoyment of the Work by the public as contemplated by Affirmer’s express 65 | Statement of Purpose. 66 | 67 | 3. Public License Fallback. Should any part of the Waiver for any reason be 68 | judged legally invalid or ineffective under applicable law, then the Waiver 69 | shall be preserved to the maximum extent permitted taking into account 70 | Affirmer’s express Statement of Purpose. In addition, to the extent the 71 | Waiver is so judged Affirmer hereby grants to each affected person a 72 | royalty-free, non transferable, non sublicensable, non exclusive, 73 | irrevocable and unconditional license to exercise Affirmer’s Copyright and 74 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 75 | maximum duration provided by applicable law or treaty (including future time 76 | extensions), (iii) in any current or future medium and for any number of 77 | copies, and (iv) for any purpose whatsoever, including without limitation 78 | commercial, advertising or promotional purposes (the “License”). The License 79 | shall be deemed effective as of the date CC0 was applied by Affirmer to the 80 | Work. Should any part of the License for any reason be judged legally 81 | invalid or ineffective under applicable law, such partial invalidity or 82 | ineffectiveness shall not invalidate the remainder of the License, and in 83 | such case Affirmer hereby affirms that he or she will not (i) exercise any 84 | of his or her remaining Copyright and Related Rights in the Work or (ii) 85 | assert any associated claims and causes of action with respect to the Work, 86 | in either case contrary to Affirmer’s express Statement of Purpose. 87 | 88 | 4. Limitations and Disclaimers. 89 | 1. No trademark or patent rights held by Affirmer are waived, abandoned, 90 | surrendered, licensed or otherwise affected by this document. 91 | 2. Affirmer offers the Work as-is and makes no representations or warranties 92 | of any kind concerning the Work, express, implied, statutory or 93 | otherwise, including without limitation warranties of title, 94 | merchantability, fitness for a particular purpose, non infringement, or 95 | the absence of latent or other defects, accuracy, or the present or 96 | absence of errors, whether or not discoverable, all to the greatest 97 | extent permissible under applicable law. 98 | 3. Affirmer disclaims responsibility for clearing rights of other persons 99 | that may apply to the Work or any use thereof, including without 100 | limitation any person’s Copyright and Related Rights in the Work. 101 | Further, Affirmer disclaims responsibility for obtaining any necessary 102 | consents, permissions or other rights required for any use of the Work. 103 | 4. Affirmer understands and acknowledges that Creative Commons is not a 104 | party to this document and has no duty or obligation with respect to this 105 | CC0 or use of the Work. 106 | 107 | For more information, please see 108 | http://creativecommons.org/publicdomain/zero/1.0/. 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
⚠️ PostCSS Preset Env was moved to @csstools/postcss-plugins. ⚠️
2 | Read the announcement
3 | 4 | # PostCSS Preset Env [PostCSS][postcss] 5 | 6 | [npm version][npm-url] 7 | [build status][cli-url] 8 | [support chat][git-url] 9 | 10 | [PostCSS Preset Env] lets you convert modern CSS into something most browsers 11 | can understand, determining the polyfills you need based on your targeted 12 | browsers or runtime environments. 13 | 14 | ```bash 15 | npm install postcss-preset-env 16 | ``` 17 | 18 | ```pcss 19 | @custom-media --viewport-medium (width <= 50rem); 20 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 21 | 22 | :root { 23 | --mainColor: #12345678; 24 | } 25 | 26 | body { 27 | color: var(--mainColor); 28 | font-family: system-ui; 29 | overflow-wrap: break-word; 30 | } 31 | 32 | :--heading { 33 | background-image: image-set(url(img/heading.png) 1x, url(img/heading@2x.png) 2x); 34 | 35 | @media (--viewport-medium) { 36 | margin-block: 0; 37 | } 38 | } 39 | 40 | a { 41 | color: rgb(0 0 100% / 90%); 42 | 43 | &:hover { 44 | color: rebeccapurple; 45 | } 46 | } 47 | 48 | /* becomes */ 49 | 50 | :root { 51 | --mainColor: rgba(18, 52, 86, 0.47059); 52 | } 53 | 54 | body { 55 | color: rgba(18, 52, 86, 0.47059); 56 | color: var(--mainColor); 57 | font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Droid Sans, Helvetica Neue; 58 | word-wrap: break-word; 59 | } 60 | 61 | h1, h2, h3, h4, h5, h6 { 62 | background-image: url(img/heading.png); 63 | } 64 | 65 | @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { 66 | h1, h2, h3, h4, h5, h6 { 67 | background-image: url(img/heading@2x.png) 68 | } 69 | } 70 | 71 | @media (max-width: 50rem) { 72 | h1, h2, h3, h4, h5, h6 { 73 | margin-top: 0; 74 | margin-bottom: 0; 75 | } 76 | } 77 | 78 | a { 79 | color: rgba(0, 0, 255, 0.9) 80 | } 81 | 82 | a:hover { 83 | color: #639; 84 | } 85 | ``` 86 | 87 | Without any configuration options, [PostCSS Preset Env] enables **Stage 2** 88 | features and supports **all** browsers. 89 | 90 | [![Transform with Preset Env][readme-transform-with-preset-env-img]][readme-transform-with-preset-env-url] 91 | [![Style with Preset Env][readme-style-with-preset-env-img]][readme-style-with-preset-env-url] 92 | 93 | ## Usage 94 | 95 | Add [PostCSS Preset Env] to your project: 96 | 97 | ```bash 98 | npm install postcss-preset-env --save-dev 99 | ``` 100 | 101 | Use [PostCSS Preset Env] to process your CSS: 102 | 103 | ```js 104 | const postcssPresetEnv = require('postcss-preset-env'); 105 | 106 | postcssPresetEnv.process(YOUR_CSS /*, processOptions, pluginOptions */); 107 | ``` 108 | 109 | Or use it as a [PostCSS] plugin: 110 | 111 | ```js 112 | const postcss = require('postcss'); 113 | const postcssPresetEnv = require('postcss-preset-env'); 114 | 115 | postcss([ 116 | postcssPresetEnv(/* pluginOptions */) 117 | ]).process(YOUR_CSS /*, processOptions */); 118 | ``` 119 | 120 | [PostCSS Preset Env] runs in all Node environments, with special instructions for: 121 | 122 | | [Node](INSTALL.md#node) | [PostCSS CLI](INSTALL.md#postcss-cli) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) | [Rollup](INSTALL.md#rollup) | 123 | | --- | --- | --- | --- | --- | --- | --- | 124 | 125 | ## Options 126 | 127 | ### stage 128 | 129 | The `stage` option determines which CSS features to polyfill, based upon their 130 | stability in the process of becoming implemented web standards. 131 | 132 | ```js 133 | postcssPresetEnv({ stage: 0 }) 134 | ``` 135 | 136 | The `stage` can be `0` (experimental) through `4` (stable), or `false`. Setting 137 | `stage` to `false` will disable every polyfill. Doing this would only be useful 138 | if you intended to exclusively use the [`features`](#features) option. 139 | 140 | Without any configuration options, [PostCSS Preset Env] enables **Stage 2** 141 | features. 142 | 143 | ### features 144 | 145 | The `features` option enables or disables specific polyfills by ID. Passing 146 | `true` to a specific feature ID will enable its polyfill, while passing `false` 147 | will disable it. [List of IDs](https://github.com/csstools/postcss-preset-env/blob/master/src/lib/plugins-by-id.js#L36) 148 | 149 | ```js 150 | postcssPresetEnv({ 151 | /* use stage 3 features + css nesting rules */ 152 | stage: 3, 153 | features: { 154 | 'nesting-rules': true 155 | } 156 | }) 157 | ``` 158 | 159 | Passing an object to a specific feature ID will both enable and configure it. 160 | 161 | ```js 162 | postcssPresetEnv({ 163 | /* use stage 3 features + css color-mod (warning on unresolved) */ 164 | stage: 3, 165 | features: { 166 | 'color-mod-function': { unresolved: 'warn' } 167 | } 168 | }) 169 | ``` 170 | 171 | Any polyfills not explicitly enabled or disabled through `features` are 172 | determined by the [`stage`](#stage) option. 173 | 174 | ### browsers 175 | 176 | The `browsers` option determines which polyfills are required based upon the 177 | browsers you are supporting. 178 | 179 | [PostCSS Preset Env] supports any standard [browserslist] configuration, which 180 | can be a `.browserslistrc` file, a `browserslist` key in `package.json`, or 181 | `browserslist` environment variables. 182 | 183 | The `browsers` option should only be used when a standard browserslist 184 | configuration is not available. 185 | 186 | ```js 187 | postcssPresetEnv({ browsers: 'last 2 versions' }) 188 | ``` 189 | 190 | If not valid browserslist configuration is specified, the 191 | [default browserslist query](https://github.com/browserslist/browserslist#queries) 192 | will be used. 193 | 194 | ### insertBefore / insertAfter 195 | 196 | The `insertBefore` and `insertAfter` keys allow you to insert other PostCSS 197 | plugins into the chain. This is only useful if you are also using sugary 198 | PostCSS plugins that must execute before or after certain polyfills. 199 | Both `insertBefore` and `insertAfter` support chaining one or multiple plugins. 200 | 201 | ```js 202 | import postcssSimpleVars from 'postcss-simple-vars'; 203 | 204 | postcssPresetEnv({ 205 | insertBefore: { 206 | 'all-property': postcssSimpleVars 207 | } 208 | }) 209 | ``` 210 | 211 | ### autoprefixer 212 | 213 | [PostCSS Preset Env] includes [autoprefixer] and [`browsers`](#browsers) option 214 | will be passed to it automatically. 215 | 216 | Specifying the `autoprefixer` option enables passing 217 | [additional options](https://github.com/postcss/autoprefixer#options) 218 | into [autoprefixer]. 219 | 220 | ```js 221 | postcssPresetEnv({ 222 | autoprefixer: { grid: true } 223 | }) 224 | ``` 225 | 226 | Passing `autoprefixer: false` disables autoprefixer. 227 | 228 | ### preserve 229 | 230 | The `preserve` option determines whether all plugins should receive a 231 | `preserve` option, which may preserve or remove otherwise-polyfilled CSS. By 232 | default, this option is not configured. 233 | 234 | ```js 235 | postcssPresetEnv({ 236 | preserve: false // instruct all plugins to omit pre-polyfilled CSS 237 | }); 238 | ``` 239 | 240 | ### importFrom 241 | 242 | The `importFrom` option specifies sources where variables like Custom Media, 243 | Custom Properties, Custom Selectors, and Environment Variables can be imported 244 | from, which might be CSS, JS, and JSON files, functions, and directly passed 245 | objects. 246 | 247 | ```js 248 | postcssPresetEnv({ 249 | /* 250 | @custom-media --small-viewport (max-width: 30em); 251 | @custom-selector :--heading h1, h2, h3; 252 | :root { --color: red; } 253 | */ 254 | importFrom: 'path/to/file.css' 255 | }); 256 | ``` 257 | 258 | Multiple sources can be passed into this option, and they will be parsed in the 259 | order they are received. JavaScript files, JSON files, functions, and objects 260 | will use different namespaces to import different kinds of variables. 261 | 262 | ```js 263 | postcssPresetEnv({ 264 | importFrom: [ 265 | /* 266 | @custom-media --small-viewport (max-width: 30em); 267 | @custom-selector :--heading h1, h2, h3; 268 | :root { --color: red; } 269 | */ 270 | 'path/to/file.css', 271 | 272 | /* module.exports = { 273 | customMedia: { '--small-viewport': '(max-width: 30em)' }, 274 | customProperties: { '--color': 'red' }, 275 | customSelectors: { ':--heading': 'h1, h2, h3' }, 276 | environmentVariables: { '--branding-padding': '20px' } 277 | } */ 278 | 'and/then/this.js', 279 | 280 | /* { 281 | "custom-media": { "--small-viewport": "(max-width: 30em)" } 282 | "custom-properties": { "--color": "red" }, 283 | "custom-selectors": { ":--heading": "h1, h2, h3" }, 284 | "environment-variables": { "--branding-padding": "20px" } 285 | } */ 286 | 'and/then/that.json', 287 | 288 | { 289 | customMedia: { '--small-viewport': '(max-width: 30em)' }, 290 | customProperties: { '--color': 'red' }, 291 | customSelectors: { ':--heading': 'h1, h2, h3' }, 292 | environmentVariables: { '--branding-padding': '20px' } 293 | }, 294 | () => { 295 | const customMedia = { '--small-viewport': '(max-width: 30em)' }; 296 | const customProperties = { '--color': 'red' }; 297 | const customSelectors = { ':--heading': 'h1, h2, h3' }; 298 | const environmentVariables = { '--branding-padding': '20px' }; 299 | 300 | return { customMedia, customProperties, customSelectors, environmentVariables }; 301 | } 302 | ] 303 | }); 304 | ``` 305 | 306 | ### exportTo 307 | 308 | The `exportTo` option specifies destinations where variables like Custom Media, 309 | Custom Properties, Custom Selectors, and Environment Variables can be exported 310 | to, which might be CSS, JS, and JSON files, functions, and directly passed 311 | objects. 312 | 313 | ```js 314 | postcssPresetEnv({ 315 | /* 316 | @custom-media --small-viewport (max-width: 30em); 317 | @custom-selector :--heading h1, h2, h3; 318 | :root { --color: red; } 319 | */ 320 | exportTo: 'path/to/file.css' 321 | }); 322 | ``` 323 | 324 | Multiple destinations can be passed into this option as well, and they will be 325 | parsed in the order they are received. JavaScript files, JSON files, and 326 | objects will use different namespaces to import different kinds of variables. 327 | 328 | ```js 329 | const cachedObject = {}; 330 | 331 | postcssPresetEnv({ 332 | exportTo: [ 333 | /* 334 | @custom-media --small-viewport (max-width: 30em); 335 | @custom-selector :--heading h1, h2, h3; 336 | :root { --color: red; } 337 | */ 338 | 'path/to/file.css', 339 | 340 | /* module.exports = { 341 | customMedia: { '--small-viewport': '(max-width: 30em)' }, 342 | customProperties: { '--color': 'red' }, 343 | customSelectors: { ':--heading': 'h1, h2, h3' }, 344 | environmentVariables: { '--branding-padding': '20px' } 345 | } */ 346 | 'and/then/this.js', 347 | 348 | /* { 349 | "custom-media": { "--small-viewport": "(max-width: 30em)" } 350 | "custom-properties": { "--color": "red" }, 351 | "custom-selectors": { ":--heading": "h1, h2, h3" }, 352 | "environment-variables": { "--branding-padding": "20px" } 353 | } */ 354 | 'and/then/that.json', 355 | 356 | cachedObject, 357 | variables => { 358 | if ('customProperties' in variables) { 359 | // do something special with customProperties 360 | } 361 | 362 | Object.assign(cachedObject, variables); 363 | } 364 | ] 365 | }); 366 | ``` 367 | 368 | [cli-img]: https://github.com/postcss/postcss-preset-env/workflows/test/badge.svg 369 | [cli-url]: https://github.com/postcss/postcss-preset-env/actions/workflows/test.yml?query=workflow/test 370 | [git-img]: https://img.shields.io/badge/support-chat-blue.svg 371 | [git-url]: https://gitter.im/postcss/postcss 372 | [npm-img]: https://img.shields.io/npm/v/postcss-preset-env.svg 373 | [npm-url]: https://www.npmjs.com/package/postcss-preset-env 374 | 375 | [autoprefixer]: https://github.com/postcss/autoprefixer 376 | [browserslist]: https://github.com/browserslist/browserslist#readme 377 | [caniuse]: https://caniuse.com/ 378 | [cssdb]: https://cssdb.org/ 379 | [PostCSS]: https://github.com/postcss/postcss 380 | [PostCSS Preset Env]: https://github.com/csstools/postcss-preset-env 381 | [readme-style-with-preset-env-img]: https://csstools.github.io/postcss-preset-env/readme-style-with-preset-env.svg 382 | [readme-style-with-preset-env-url]: https://codepen.io/pen?template=OZRovK 383 | [readme-transform-with-preset-env-img]: https://csstools.github.io/postcss-preset-env/readme-transform-with-preset-env.svg 384 | [readme-transform-with-preset-env-url]: https://csstools.github.io/postcss-preset-env/ 385 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-preset-env", 3 | "version": "7.0.1", 4 | "description": "Convert modern CSS into something browsers understand", 5 | "author": "Jonathan Neal ", 6 | "license": "CC0-1.0", 7 | "repository": "csstools/postcss-preset-env", 8 | "homepage": "https://github.com/csstools/postcss-preset-env#readme", 9 | "bugs": "https://github.com/csstools/postcss-preset-env/issues", 10 | "main": "dist/index.js", 11 | "module": "dist/index.mjs", 12 | "files": [ 13 | "dist" 14 | ], 15 | "scripts": { 16 | "build": "npx rollup -c .rollup.js", 17 | "build:watch": "npx rollup -c .rollup.js --watch", 18 | "lint": "npx eslint --cache src", 19 | "lint:fix": "npx eslint --cache --fix", 20 | "pretest": "npm install && npm run build", 21 | "test": "npm run lint && npm run tape", 22 | "tape": "npx postcss-tape", 23 | "prepublishOnly": "npm test" 24 | }, 25 | "engines": { 26 | "node": ">=12" 27 | }, 28 | "peerDependencies": { 29 | "postcss": "^8.3" 30 | }, 31 | "dependencies": { 32 | "autoprefixer": "^10.4.0", 33 | "browserslist": "^4.17.5", 34 | "caniuse-lite": "^1.0.30001272", 35 | "css-blank-pseudo": "^2.0.0", 36 | "css-has-pseudo": "^2.0.0", 37 | "css-prefers-color-scheme": "^5.0.0", 38 | "cssdb": "^5.0.0", 39 | "postcss": "^8.3", 40 | "postcss-attribute-case-insensitive": "^5.0.0", 41 | "postcss-color-functional-notation": "^4.0.1", 42 | "postcss-color-hex-alpha": "^8.0.0", 43 | "postcss-color-rebeccapurple": "^7.0.0", 44 | "postcss-custom-media": "^8.0.0", 45 | "postcss-custom-properties": "^12.0.0", 46 | "postcss-custom-selectors": "^6.0.0", 47 | "postcss-dir-pseudo-class": "^6.0.0", 48 | "postcss-double-position-gradients": "^3.0.1", 49 | "postcss-env-function": "^4.0.2", 50 | "postcss-focus-visible": "^6.0.1", 51 | "postcss-focus-within": "^5.0.1", 52 | "postcss-font-variant": "^5.0.0", 53 | "postcss-gap-properties": "^3.0.0", 54 | "postcss-image-set-function": "^4.0.2", 55 | "postcss-initial": "^4.0.1", 56 | "postcss-lab-function": "^4.0.1", 57 | "postcss-logical": "^5.0.0", 58 | "postcss-media-minmax": "^5.0.0", 59 | "postcss-nesting": "^10.0.2", 60 | "postcss-overflow-shorthand": "^3.0.0", 61 | "postcss-page-break": "^3.0.4", 62 | "postcss-place": "^7.0.1", 63 | "postcss-pseudo-class-any-link": "^7.0.0", 64 | "postcss-replace-overflow-wrap": "^4.0.0", 65 | "postcss-selector-not": "^5.0.0" 66 | }, 67 | "devDependencies": { 68 | "@babel/core": "^7.15.8", 69 | "@babel/preset-env": "^7.15.8", 70 | "eslint": "^8.1.0", 71 | "postcss-simple-vars": "^6.0.3", 72 | "postcss-tape": "^6.0.1", 73 | "pre-commit": "^1.2.2", 74 | "rollup": "^2.58.3", 75 | "rollup-plugin-babel": "^4.4.0" 76 | }, 77 | "babel": { 78 | "presets": [ 79 | [ 80 | "@babel/env", 81 | { 82 | "targets": "maintained node versions" 83 | } 84 | ] 85 | ] 86 | }, 87 | "eslintConfig": { 88 | "env": { 89 | "browser": true, 90 | "es6": true, 91 | "node": true 92 | }, 93 | "extends": "eslint:recommended", 94 | "parserOptions": { 95 | "ecmaVersion": 2020, 96 | "sourceType": "module" 97 | }, 98 | "root": true 99 | }, 100 | "rollup": { 101 | "input": "src/postcss.js", 102 | "plugins": [ 103 | "rollup-plugin-babel" 104 | ], 105 | "output": [ 106 | { 107 | "exports": "default", 108 | "file": "dist/index.js", 109 | "format": "cjs" 110 | }, 111 | { 112 | "file": "dist/index.mjs", 113 | "format": "esm" 114 | } 115 | ] 116 | }, 117 | "keywords": [ 118 | "postcss", 119 | "css", 120 | "postcss-plugin", 121 | "specifications", 122 | "specs", 123 | "features", 124 | "lists", 125 | "stages", 126 | "w3c", 127 | "csswg", 128 | "future", 129 | "next" 130 | ] 131 | } 132 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import plugin from './postcss'; 3 | 4 | if (process.argv.length < 3) { 5 | console.log([ 6 | 'PostCSS Preset Env\n', 7 | ' Transforms Modern CSS\n', 8 | 'Usage:\n', 9 | ' postcss-preset-env source.css transformed.css', 10 | ' postcss-preset-env --in=source.css --out=transformed.css --opts={}', 11 | ' echo "/* a bunch of css */" | focus-within\n' 12 | ].join('\n')); 13 | process.exit(0); 14 | } 15 | 16 | // get process and plugin options from the command line 17 | const fileRegExp = /^[\w/.]+$/; 18 | const argRegExp = /^--(\w+)=("|')?(.+)\2$/; 19 | const relaxedJsonPropRegExp = /(['"])?([a-z0-9A-Z_]+)(['"])?:/g; 20 | const relaxedJsonValueRegExp = /("[a-z0-9A-Z_]+":\s*)(?!true|false|null|\d+)'?([A-z0-9]+)'?([,}])/g; 21 | const argo = process.argv.slice(2).reduce( 22 | (object, arg) => { 23 | const argMatch = arg.match(argRegExp); 24 | const fileMatch = arg.match(fileRegExp); 25 | 26 | if (argMatch) { 27 | object[argMatch[1]] = argMatch[3]; 28 | } else if (fileMatch) { 29 | if (object.from === '') { 30 | object.from = arg; 31 | } else if (object.to === '') { 32 | object.to = arg; 33 | } 34 | } 35 | 36 | return object; 37 | }, 38 | { from: '', to: '', opts: 'null' } 39 | ); 40 | 41 | // get css from command line arguments or stdin 42 | (argo.from === '' ? getStdin() : readFile(argo.from)) 43 | .then(css => { 44 | const pluginOpts = JSON.parse( 45 | argo.opts 46 | .replace(relaxedJsonPropRegExp, '"$2": ') 47 | .replace(relaxedJsonValueRegExp, '$1"$2"$3') 48 | ); 49 | const processOptions = Object.assign({ from: argo.from, to: argo.to || argo.from }, argo.map ? { map: JSON.parse(argo.map) } : {}); 50 | 51 | const result = plugin.process(css, processOptions, pluginOpts); 52 | 53 | if (argo.to === '') { 54 | return result.css; 55 | } else { 56 | return writeFile(argo.to, result.css).then( 57 | () => `CSS was written to "${argo.to}"` 58 | ) 59 | } 60 | }).then( 61 | result => { 62 | console.log(result); 63 | 64 | process.exit(0); 65 | }, 66 | error => { 67 | console.error(error); 68 | 69 | process.exit(1); 70 | } 71 | ); 72 | 73 | function readFile(pathname) { 74 | return new Promise((resolve, reject) => { 75 | fs.readFile(pathname, 'utf8', (error, data) => { 76 | if (error) { 77 | reject(error); 78 | } else { 79 | resolve(data); 80 | } 81 | }); 82 | }); 83 | } 84 | 85 | function writeFile(pathname, data) { 86 | return new Promise((resolve, reject) => { 87 | fs.writeFile(pathname, data, (error, content) => { 88 | if (error) { 89 | reject(error); 90 | } else { 91 | resolve(content); 92 | } 93 | }); 94 | }); 95 | } 96 | 97 | function getStdin() { 98 | return new Promise(resolve => { 99 | let data = ''; 100 | 101 | if (process.stdin.isTTY) { 102 | resolve(data); 103 | } else { 104 | process.stdin.setEncoding('utf8'); 105 | 106 | process.stdin.on('readable', () => { 107 | let chunk; 108 | 109 | // eslint-disable-next-line no-cond-assign 110 | while (chunk = process.stdin.read()) { 111 | data += chunk; 112 | } 113 | }); 114 | 115 | process.stdin.on('end', () => { 116 | resolve(data); 117 | }); 118 | } 119 | }); 120 | } 121 | -------------------------------------------------------------------------------- /src/lib/get-transformed-insertions.js: -------------------------------------------------------------------------------- 1 | // return a list of features to be inserted before or after cssdb features 2 | export default function getTransformedInsertions(insertions, placement) { 3 | return Object.keys(insertions).map( 4 | id => [].concat(insertions[id]).map( 5 | plugin => ({ 6 | [placement]: true, 7 | plugin, 8 | id 9 | }) 10 | ) 11 | ).reduce( 12 | (array, feature) => array.concat(feature), [] 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/get-unsupported-browsers-by-feature.js: -------------------------------------------------------------------------------- 1 | import * as caniuse from 'caniuse-lite'; 2 | 3 | // return a list of browsers that do not support the feature 4 | export default function getUnsupportedBrowsersByFeature(feature) { 5 | const caniuseFeature = caniuse.features[feature]; 6 | 7 | // if feature support can be determined 8 | if (caniuseFeature) { 9 | const stats = caniuse.feature(caniuseFeature).stats; 10 | 11 | // return an array of browsers and versions that do not support the feature 12 | const results = Object.keys(stats).reduce( 13 | (browsers, browser) => browsers.concat( 14 | Object.keys(stats[browser]).filter( 15 | version => stats[browser][version].indexOf('y') !== 0 16 | ).map( 17 | version => `${browser} ${version}` 18 | ) 19 | ), 20 | [] 21 | ); 22 | 23 | return results; 24 | } else { 25 | // otherwise, return that the feature does not work in any browser 26 | return [ '> 0%' ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/ids-by-execution-order.js: -------------------------------------------------------------------------------- 1 | // ids ordered by required execution, then alphabetically 2 | export default [ 3 | 'custom-media-queries', 4 | 'custom-properties', 5 | 'environment-variables', // run environment-variables here to access transpiled custom media params and properties 6 | 'image-set-function', // run images-set-function before nesting-rules so that it may fix nested media 7 | 'media-query-ranges', // run media-query-range and 8 | 'prefers-color-scheme-query', // run prefers-color-scheme-query here to prevent duplicate transpilation after nesting-rules 9 | 'nesting-rules', 10 | 'custom-selectors', // run custom-selectors after nesting-rules to correctly transpile &:--custom-selector 11 | 'any-link-pseudo-class', 12 | 'case-insensitive-attributes', 13 | 'focus-visible-pseudo-class', 14 | 'focus-within-pseudo-class', 15 | 'matches-pseudo-class', // run matches-pseudo-class and 16 | 'not-pseudo-class', // run not-pseudo-class after other selectors have been transpiled 17 | 'logical-properties-and-values', // run logical-properties-and-values before dir-pseudo-class 18 | 'dir-pseudo-class', 19 | 'all-property', // run all-property before other property polyfills 20 | 'color-functional-notation', 21 | 'double-position-gradients', 22 | 'hexadecimal-alpha-notation', 23 | 'lab-function', 24 | 'rebeccapurple-color', 25 | 'color-mod-function', // run color-mod after other color modifications have finished 26 | 'blank-pseudo-class', 27 | 'break-properties', 28 | 'font-variant-property', 29 | 'has-pseudo-class', 30 | 'gap-properties', 31 | 'overflow-property', 32 | 'overflow-wrap-property', 33 | 'place-properties', 34 | 'system-ui-font-family' 35 | ]; 36 | -------------------------------------------------------------------------------- /src/lib/plugins-by-id.js: -------------------------------------------------------------------------------- 1 | import postcssAttributeCaseInsensitive from 'postcss-attribute-case-insensitive'; 2 | import postcssBlankPseudo from 'css-blank-pseudo/postcss'; 3 | import postcssColorFunctionalNotation from 'postcss-color-functional-notation'; 4 | import postcssColorHexAlpha from 'postcss-color-hex-alpha'; 5 | import postcssColorRebeccapurple from 'postcss-color-rebeccapurple'; 6 | import postcssCustomMedia from 'postcss-custom-media'; 7 | import postcssCustomProperties from 'postcss-custom-properties'; 8 | import postcssCustomSelectors from 'postcss-custom-selectors'; 9 | import postcssDirPseudoClass from 'postcss-dir-pseudo-class'; 10 | import postcssDoublePositionGradients from 'postcss-double-position-gradients'; 11 | import postcssEnvFunction from 'postcss-env-function'; 12 | import postcssFocusVisible from 'postcss-focus-visible'; 13 | import postcssFocusWithin from 'postcss-focus-within'; 14 | import postcssFontVariant from 'postcss-font-variant'; 15 | import postcssFontFamilySystemUi from '../patch/postcss-system-ui-font-family'; 16 | import postcssGapProperties from 'postcss-gap-properties'; 17 | import postcssHasPseudo from 'css-has-pseudo/postcss'; 18 | import postcssImageSetPolyfill from 'postcss-image-set-function'; 19 | import postcssInitial from 'postcss-initial'; 20 | import postcssLabFunction from 'postcss-lab-function'; 21 | import postcssLogical from 'postcss-logical'; 22 | import postcssMediaMinmax from 'postcss-media-minmax'; 23 | import postcssNesting from 'postcss-nesting'; 24 | import postcssOverflowShorthand from 'postcss-overflow-shorthand'; 25 | import postcssPageBreak from 'postcss-page-break'; 26 | import postcssPlace from 'postcss-place'; 27 | import postcssPrefersColorScheme from 'css-prefers-color-scheme/postcss'; 28 | import postcssPseudoClassAnyLink from 'postcss-pseudo-class-any-link'; 29 | import postcssReplaceOverflowWrap from 'postcss-replace-overflow-wrap'; 30 | import postcssSelectorNot from 'postcss-selector-not'; 31 | 32 | // postcss plugins ordered by id 33 | export default { 34 | 'all-property': postcssInitial, 35 | 'any-link-pseudo-class': postcssPseudoClassAnyLink, 36 | 'blank-pseudo-class': postcssBlankPseudo, 37 | 'break-properties': postcssPageBreak, 38 | 'case-insensitive-attributes': postcssAttributeCaseInsensitive, 39 | 'color-functional-notation': postcssColorFunctionalNotation, 40 | 'custom-media-queries': postcssCustomMedia, 41 | 'custom-properties': postcssCustomProperties, 42 | 'custom-selectors': postcssCustomSelectors, 43 | 'dir-pseudo-class': postcssDirPseudoClass, 44 | 'double-position-gradients': postcssDoublePositionGradients, 45 | 'environment-variables': postcssEnvFunction, 46 | 'focus-visible-pseudo-class': postcssFocusVisible, 47 | 'focus-within-pseudo-class': postcssFocusWithin, 48 | 'font-variant-property': postcssFontVariant, 49 | 'gap-properties': postcssGapProperties, 50 | 'has-pseudo-class': postcssHasPseudo, 51 | 'hexadecimal-alpha-notation': postcssColorHexAlpha, 52 | 'image-set-function': postcssImageSetPolyfill, 53 | 'lab-function': postcssLabFunction, 54 | 'logical-properties-and-values': postcssLogical, 55 | 'media-query-ranges': postcssMediaMinmax, 56 | 'nesting-rules': postcssNesting, 57 | 'not-pseudo-class': postcssSelectorNot, 58 | 'overflow-property': postcssOverflowShorthand, 59 | 'overflow-wrap-property': postcssReplaceOverflowWrap, 60 | 'place-properties': postcssPlace, 61 | 'prefers-color-scheme-query': postcssPrefersColorScheme, 62 | 'rebeccapurple-color': postcssColorRebeccapurple, 63 | 'system-ui-font-family': postcssFontFamilySystemUi 64 | }; 65 | -------------------------------------------------------------------------------- /src/lib/write-to-exports.js: -------------------------------------------------------------------------------- 1 | /* eslint max-params: ["error", 4] */ 2 | 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | 6 | /* Write Exports to CSS File 7 | /* ========================================================================== */ 8 | 9 | function getCustomMediaAsCss(customMedia) { 10 | const cssContent = Object.keys(customMedia).reduce((cssLines, name) => { 11 | cssLines.push(`@custom-media ${name} ${customMedia[name]};`); 12 | 13 | return cssLines; 14 | }, []).join('\n'); 15 | const css = `${cssContent}\n`; 16 | 17 | return css; 18 | } 19 | 20 | function getCustomPropertiesAsCss(customProperties) { 21 | const cssContent = Object.keys(customProperties).reduce((cssLines, name) => { 22 | cssLines.push(`\t${name}: ${customProperties[name]};`); 23 | 24 | return cssLines; 25 | }, []).join('\n'); 26 | const css = `:root {\n${cssContent}\n}\n`; 27 | 28 | return css; 29 | } 30 | 31 | function getCustomSelectorsAsCss(customSelectors) { 32 | const cssContent = Object.keys(customSelectors).reduce((cssLines, name) => { 33 | cssLines.push(`@custom-selector ${name} ${customSelectors[name]};`); 34 | 35 | return cssLines; 36 | }, []).join('\n'); 37 | const css = `${cssContent}\n`; 38 | 39 | return css; 40 | } 41 | 42 | async function writeExportsToCssFile(to, customMedia, customProperties, customSelectors) { 43 | const customPropertiesAsCss = getCustomPropertiesAsCss(customProperties); 44 | const customMediaAsCss = getCustomMediaAsCss(customMedia); 45 | const customSelectorsAsCss = getCustomSelectorsAsCss(customSelectors); 46 | const css = `${customMediaAsCss}\n${customSelectorsAsCss}\n${customPropertiesAsCss}`; 47 | 48 | await writeFile(to, css); 49 | } 50 | 51 | /* Write Exports to JSON file 52 | /* ========================================================================== */ 53 | 54 | async function writeExportsToJsonFile(to, customMedia, customProperties, customSelectors) { 55 | const jsonContent = JSON.stringify({ 56 | 'custom-media': customMedia, 57 | 'custom-properties': customProperties, 58 | 'custom-selectors': customSelectors 59 | }, null, ' '); 60 | const json = `${jsonContent}\n`; 61 | 62 | await writeFile(to, json); 63 | } 64 | 65 | /* Write Exports to Common JS file 66 | /* ========================================================================== */ 67 | 68 | function getObjectWithKeyAsCjs(key, object) { 69 | const jsContents = Object.keys(object).reduce((jsLines, name) => { 70 | jsLines.push(`\t\t'${escapeForJS(name)}': '${escapeForJS(object[name])}'`); 71 | 72 | return jsLines; 73 | }, []).join(',\n'); 74 | const cjs = `\n\t${key}: {\n${jsContents}\n\t}`; 75 | 76 | return cjs; 77 | } 78 | 79 | async function writeExportsToCjsFile(to, customMedia, customProperties, customSelectors) { 80 | const customMediaAsCjs = getObjectWithKeyAsCjs('customMedia', customMedia); 81 | const customPropertiesAsCjs = getObjectWithKeyAsCjs('customProperties', customProperties); 82 | const customSelectorsAsCjs = getObjectWithKeyAsCjs('customSelectors', customSelectors); 83 | const cjs = `module.exports = {${customMediaAsCjs},${customPropertiesAsCjs},${customSelectorsAsCjs}\n};\n`; 84 | 85 | await writeFile(to, cjs); 86 | } 87 | 88 | /* Write Exports to Module JS file 89 | /* ========================================================================== */ 90 | 91 | function getObjectWithKeyAsMjs(key, object) { 92 | const mjsContents = Object.keys(object).reduce((mjsLines, name) => { 93 | mjsLines.push(`\t'${escapeForJS(name)}': '${escapeForJS(object[name])}'`); 94 | 95 | return mjsLines; 96 | }, []).join(',\n'); 97 | const mjs = `export const ${key} = {\n${mjsContents}\n};\n`; 98 | 99 | return mjs; 100 | } 101 | 102 | async function writeExportsToMjsFile(to, customMedia, customProperties, customSelectors) { 103 | const customMediaAsMjs = getObjectWithKeyAsMjs('customMedia', customMedia); 104 | const customPropertiesAsMjs = getObjectWithKeyAsMjs('customProperties', customProperties); 105 | const customSelectorsAsMjs = getObjectWithKeyAsMjs('customSelectors', customSelectors); 106 | const mjs = `${customMediaAsMjs}\n${customPropertiesAsMjs}\n${customSelectorsAsMjs}`; 107 | 108 | await writeFile(to, mjs); 109 | } 110 | 111 | /* Write Exports to Exports 112 | /* ========================================================================== */ 113 | 114 | export default function writeToExports(customExports, destinations) { 115 | return Promise.all([].concat(destinations).map(async destination => { 116 | if (destination instanceof Function) { 117 | await destination({ 118 | customMedia: getObjectWithStringifiedKeys(customExports.customMedia), 119 | customProperties: getObjectWithStringifiedKeys(customExports.customProperties), 120 | customSelectors: getObjectWithStringifiedKeys(customExports.customSelectors) 121 | }); 122 | } else { 123 | // read the destination as an object 124 | const opts = destination === Object(destination) ? destination : { to: String(destination) }; 125 | 126 | // transformer for Exports into a JSON-compatible object 127 | const toJSON = opts.toJSON || getObjectWithStringifiedKeys; 128 | 129 | if ('customMedia' in opts || 'customProperties' in opts || 'customSelectors' in opts) { 130 | // write directly to an object as customProperties 131 | opts.customMedia = toJSON(customExports.customMedia); 132 | opts.customProperties = toJSON(customExports.customProperties); 133 | opts.customSelectors = toJSON(customExports.customSelectors); 134 | } else if ('custom-media' in opts || 'custom-properties' in opts || 'custom-selectors' in opts) { 135 | // write directly to an object as custom-properties 136 | opts['custom-media'] = toJSON(customExports.customMedia); 137 | opts['custom-properties'] = toJSON(customExports.customProperties); 138 | opts['custom-selectors'] = toJSON(customExports.customSelectors); 139 | } else { 140 | // destination pathname 141 | const to = String(opts.to || ''); 142 | 143 | // type of file being written to 144 | const type = (opts.type || path.extname(opts.to).slice(1)).toLowerCase(); 145 | 146 | // transformed Exports 147 | const customMediaJSON = toJSON(customExports.customMedia); 148 | const customPropertiesJSON = toJSON(customExports.customProperties); 149 | const customSelectorsJSON = toJSON(customExports.customSelectors); 150 | 151 | if (type === 'css') { 152 | await writeExportsToCssFile(to, customMediaJSON, customPropertiesJSON, customSelectorsJSON); 153 | } 154 | 155 | if (type === 'js') { 156 | await writeExportsToCjsFile(to, customMediaJSON, customPropertiesJSON, customSelectorsJSON); 157 | } 158 | 159 | if (type === 'json') { 160 | await writeExportsToJsonFile(to, customMediaJSON, customPropertiesJSON, customSelectorsJSON); 161 | } 162 | 163 | if (type === 'mjs') { 164 | await writeExportsToMjsFile(to, customMediaJSON, customPropertiesJSON, customSelectorsJSON); 165 | } 166 | } 167 | } 168 | })); 169 | } 170 | 171 | /* Helper utilities 172 | /* ========================================================================== */ 173 | 174 | function getObjectWithStringifiedKeys(object) { 175 | return Object.keys(object).reduce((objectJSON, key) => { 176 | objectJSON[key] = String(object[key]); 177 | 178 | return objectJSON; 179 | }, {}); 180 | } 181 | 182 | function writeFile(to, text) { 183 | return new Promise((resolve, reject) => { 184 | fs.writeFile(to, text, error => { 185 | if (error) { 186 | reject(error); 187 | } else { 188 | resolve(); 189 | } 190 | }); 191 | }); 192 | } 193 | 194 | function escapeForJS(string) { 195 | return string.replace(/\\([\s\S])|(')/g, '\\$1$2').replace(/\n/g, '\\n').replace(/\r/g, '\\r'); 196 | } 197 | -------------------------------------------------------------------------------- /src/patch/postcss-system-ui-font-family.js: -------------------------------------------------------------------------------- 1 | export default function postcssSystemUiFont() { 2 | return { 3 | postcssPlugin: 'postcss-system-ui-font', 4 | Declaration(/** @type {import('postcss').Declaration} */ node) { 5 | if (propertyRegExp.test(node.prop)) { 6 | if (!node.value.includes(systemUiFamily.join(', '))) { 7 | node.value = node.value.replace(systemUiMatch, systemUiReplace); 8 | } 9 | } 10 | } 11 | } 12 | } 13 | 14 | postcssSystemUiFont.postcss = true; 15 | 16 | const propertyRegExp = /(?:^(?:-|\\002d){2})|(?:^font(?:-family)?$)/i; 17 | const whitespace = '[\\f\\n\\r\\x09\\x20]'; 18 | const systemUiFamily = [ 19 | 'system-ui', 20 | /* macOS 10.11-10.12 */ '-apple-system', 21 | /* Windows 6+ */ 'Segoe UI', 22 | /* Android 4+ */ 'Roboto', 23 | /* Ubuntu 10.10+ */ 'Ubuntu', 24 | /* Gnome 3+ */ 'Cantarell', 25 | /* KDE Plasma 5+ */ 'Noto Sans', 26 | /* fallback */ 'sans-serif' 27 | ]; 28 | const systemUiMatch = new RegExp(`(^|,|${whitespace}+)(?:system-ui${whitespace}*)(?:,${whitespace}*(?:${systemUiFamily.join('|')})${whitespace}*)?(,|$)`, 'i'); 29 | const systemUiReplace = `$1${systemUiFamily.join(', ')}$2`; 30 | -------------------------------------------------------------------------------- /src/postcss.js: -------------------------------------------------------------------------------- 1 | import autoprefixer from 'autoprefixer'; 2 | import browserslist from 'browserslist'; 3 | import cssdb from 'cssdb'; 4 | import plugins from './lib/plugins-by-id'; 5 | import getTransformedInsertions from './lib/get-transformed-insertions'; 6 | import getUnsupportedBrowsersByFeature from './lib/get-unsupported-browsers-by-feature'; 7 | import idsByExecutionOrder from './lib/ids-by-execution-order'; 8 | import writeToExports from './lib/write-to-exports'; 9 | 10 | const plugin = opts => { 11 | // initialize options 12 | const features = Object(Object(opts).features); 13 | const insertBefore = Object(Object(opts).insertBefore); 14 | const insertAfter = Object(Object(opts).insertAfter); 15 | const browsers = Object(opts).browsers; 16 | const stage = 'stage' in Object(opts) 17 | ? opts.stage === false 18 | ? 5 19 | : parseInt(opts.stage) || 0 20 | : 2; 21 | const autoprefixerOptions = Object(opts).autoprefixer; 22 | const sharedOpts = initializeSharedOpts(Object(opts)); 23 | const stagedAutoprefixer = autoprefixerOptions === false 24 | ? () => {} 25 | : autoprefixer(Object.assign({ overrideBrowserslist: browsers }, autoprefixerOptions)); 26 | 27 | // polyfillable features (those with an available postcss plugin) 28 | const polyfillableFeatures = cssdb.concat( 29 | // additional features to be inserted before cssdb features 30 | getTransformedInsertions(insertBefore, 'insertBefore'), 31 | // additional features to be inserted after cssdb features 32 | getTransformedInsertions(insertAfter, 'insertAfter') 33 | ).filter( 34 | // inserted features or features with an available postcss plugin 35 | feature => feature.insertBefore || feature.id in plugins 36 | ).sort( 37 | // features sorted by execution order and then insertion order 38 | (a, b) => idsByExecutionOrder.indexOf(a.id) - idsByExecutionOrder.indexOf(b.id) || (a.insertBefore ? -1 : b.insertBefore ? 1 : 0) || (a.insertAfter ? 1 : b.insertAfter ? -1 : 0) 39 | ).map( 40 | // polyfillable features as an object 41 | feature => { 42 | // target browsers for the polyfill 43 | const unsupportedBrowsers = getUnsupportedBrowsersByFeature(feature.caniuse); 44 | 45 | return feature.insertBefore || feature.insertAfter ? { 46 | browsers: unsupportedBrowsers, 47 | plugin: feature.plugin, 48 | id: `${feature.insertBefore ? 'before' : 'after'}-${feature.id}`, 49 | stage: 6 50 | } : { 51 | browsers: unsupportedBrowsers, 52 | plugin: plugins[feature.id], 53 | id: feature.id, 54 | stage: feature.stage 55 | }; 56 | } 57 | ); 58 | 59 | // staged features (those at or above the selected stage) 60 | const stagedFeatures = polyfillableFeatures.filter( 61 | feature => feature.id in features 62 | ? features[feature.id] 63 | : feature.stage >= stage 64 | ).map( 65 | feature => { 66 | let options; 67 | let plugin; 68 | 69 | if (features[feature.id] === true) { 70 | // if the plugin is enabled 71 | options = sharedOpts ? Object.assign({}, sharedOpts) : undefined; 72 | } else { 73 | options = sharedOpts 74 | // if the plugin has shared options and individual options 75 | ? Object.assign({}, sharedOpts, features[feature.id]) 76 | // if the plugin has individual options 77 | : Object.assign({}, features[feature.id]); 78 | } 79 | 80 | if (feature.plugin.postcss) { 81 | plugin = feature.plugin(options); 82 | } else { 83 | plugin = feature.plugin; 84 | } 85 | 86 | return { 87 | browsers: feature.browsers, 88 | plugin, 89 | id: feature.id 90 | }; 91 | } 92 | ); 93 | 94 | // browsers supported by the configuration 95 | const supportedBrowsers = browserslist(browsers, { ignoreUnknownVersions: true }); 96 | 97 | // features supported by the stage and browsers 98 | const supportedFeatures = stagedFeatures.filter( 99 | feature => feature.id in features 100 | ? features[feature.id] 101 | : supportedBrowsers.some( 102 | supportedBrowser => browserslist(feature.browsers, { 103 | ignoreUnknownVersions: true 104 | }).some( 105 | polyfillBrowser => polyfillBrowser === supportedBrowser 106 | ) 107 | ) 108 | ); 109 | 110 | const usedPlugins = supportedFeatures.map(feature => feature.plugin); 111 | usedPlugins.push(stagedAutoprefixer); 112 | 113 | return { 114 | postcssPlugin: 'postcss-preset-env', 115 | plugins: usedPlugins, 116 | OnceExit: function() { 117 | if ( Object( opts ).exportTo ) { 118 | writeToExports( sharedOpts.exportTo, opts.exportTo ); 119 | } 120 | } 121 | }; 122 | } 123 | 124 | const initializeSharedOpts = opts => { 125 | if ('importFrom' in opts || 'exportTo' in opts || 'preserve' in opts) { 126 | const sharedOpts = {}; 127 | 128 | if ('importFrom' in opts) { 129 | sharedOpts.importFrom = opts.importFrom; 130 | } 131 | 132 | if ('exportTo' in opts) { 133 | sharedOpts.exportTo = { 134 | customMedia: {}, 135 | customProperties: {}, 136 | customSelectors: {}, 137 | }; 138 | } 139 | 140 | if ('preserve' in opts) { 141 | sharedOpts.preserve = opts.preserve; 142 | } 143 | 144 | return sharedOpts; 145 | } 146 | 147 | return false; 148 | }; 149 | 150 | plugin.postcss = true; 151 | 152 | export default plugin; 153 | -------------------------------------------------------------------------------- /test/basic.autoprefixer.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: 1; 7 | order: var(--order); 8 | } 9 | 10 | .test-image-set-function { 11 | background-image: url(img/test.png); 12 | } 13 | 14 | @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { 15 | 16 | .test-image-set-function { 17 | background-image: url(img/test-2x.png); 18 | } 19 | } 20 | 21 | .test-image-set-function { 22 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 23 | order: 2; 24 | } 25 | 26 | [dir="ltr"] .test-logical-properties-and-values { 27 | margin: 1px 4px 3px 2px; 28 | } 29 | 30 | [dir="rtl"] .test-logical-properties-and-values { 31 | margin: 1px 2px 3px 4px; 32 | } 33 | 34 | .test-logical-properties-and-values { 35 | order: 3; 36 | padding-top: 5px; 37 | padding-bottom: 5px; 38 | } 39 | 40 | .test-nesting-rules { 41 | order: 4; 42 | 43 | & p { 44 | order: 5; 45 | } 46 | 47 | order: 6; 48 | } 49 | 50 | @custom-media --narrow-window (max-width: 30em); 51 | 52 | @media (--narrow-window) { 53 | .test-custom-media-queries { 54 | order: 7; 55 | } 56 | } 57 | 58 | @media (min-width: 480px) and (max-width: 767px) { 59 | .test-media-query-ranges { 60 | order: 8; 61 | } 62 | } 63 | 64 | @custom-media --dark-mode (prefers-color-scheme: dark); 65 | 66 | @media (--dark-mode) { 67 | body { 68 | background-color: black; 69 | color: white; 70 | } 71 | } 72 | 73 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 74 | 75 | .test-custom-selectors:--heading { 76 | order: 9; 77 | } 78 | 79 | .test-case-insensitive-attributes[frame=hsides],.test-case-insensitive-attributes[frame=Hsides],.test-case-insensitive-attributes[frame=hSides],.test-case-insensitive-attributes[frame=HSides],.test-case-insensitive-attributes[frame=hsIdes],.test-case-insensitive-attributes[frame=HsIdes],.test-case-insensitive-attributes[frame=hSIdes],.test-case-insensitive-attributes[frame=HSIdes],.test-case-insensitive-attributes[frame=hsiDes],.test-case-insensitive-attributes[frame=HsiDes],.test-case-insensitive-attributes[frame=hSiDes],.test-case-insensitive-attributes[frame=HSiDes],.test-case-insensitive-attributes[frame=hsIDes],.test-case-insensitive-attributes[frame=HsIDes],.test-case-insensitive-attributes[frame=hSIDes],.test-case-insensitive-attributes[frame=HSIDes],.test-case-insensitive-attributes[frame=hsidEs],.test-case-insensitive-attributes[frame=HsidEs],.test-case-insensitive-attributes[frame=hSidEs],.test-case-insensitive-attributes[frame=HSidEs],.test-case-insensitive-attributes[frame=hsIdEs],.test-case-insensitive-attributes[frame=HsIdEs],.test-case-insensitive-attributes[frame=hSIdEs],.test-case-insensitive-attributes[frame=HSIdEs],.test-case-insensitive-attributes[frame=hsiDEs],.test-case-insensitive-attributes[frame=HsiDEs],.test-case-insensitive-attributes[frame=hSiDEs],.test-case-insensitive-attributes[frame=HSiDEs],.test-case-insensitive-attributes[frame=hsIDEs],.test-case-insensitive-attributes[frame=HsIDEs],.test-case-insensitive-attributes[frame=hSIDEs],.test-case-insensitive-attributes[frame=HSIDEs],.test-case-insensitive-attributes[frame=hsideS],.test-case-insensitive-attributes[frame=HsideS],.test-case-insensitive-attributes[frame=hSideS],.test-case-insensitive-attributes[frame=HSideS],.test-case-insensitive-attributes[frame=hsIdeS],.test-case-insensitive-attributes[frame=HsIdeS],.test-case-insensitive-attributes[frame=hSIdeS],.test-case-insensitive-attributes[frame=HSIdeS],.test-case-insensitive-attributes[frame=hsiDeS],.test-case-insensitive-attributes[frame=HsiDeS],.test-case-insensitive-attributes[frame=hSiDeS],.test-case-insensitive-attributes[frame=HSiDeS],.test-case-insensitive-attributes[frame=hsIDeS],.test-case-insensitive-attributes[frame=HsIDeS],.test-case-insensitive-attributes[frame=hSIDeS],.test-case-insensitive-attributes[frame=HSIDeS],.test-case-insensitive-attributes[frame=hsidES],.test-case-insensitive-attributes[frame=HsidES],.test-case-insensitive-attributes[frame=hSidES],.test-case-insensitive-attributes[frame=HSidES],.test-case-insensitive-attributes[frame=hsIdES],.test-case-insensitive-attributes[frame=HsIdES],.test-case-insensitive-attributes[frame=hSIdES],.test-case-insensitive-attributes[frame=HSIdES],.test-case-insensitive-attributes[frame=hsiDES],.test-case-insensitive-attributes[frame=HsiDES],.test-case-insensitive-attributes[frame=hSiDES],.test-case-insensitive-attributes[frame=HSiDES],.test-case-insensitive-attributes[frame=hsIDES],.test-case-insensitive-attributes[frame=HsIDES],.test-case-insensitive-attributes[frame=hSIDES],.test-case-insensitive-attributes[frame=HSIDES] { 80 | order: 10; 81 | } 82 | 83 | .test-rebeccapurple-color { 84 | color: #639; 85 | order: 11; 86 | } 87 | 88 | .test-hexadecimal-alpha-notation { 89 | background-color: rgba(243,243,243,0.95294); 90 | color: rgba(0,0,0,0.2); 91 | order: 12; 92 | } 93 | 94 | .test-color-functional-notation { 95 | color: rgb(70% 13.5% 13.5% / 50%); 96 | order: 13; 97 | } 98 | 99 | .test-lab-function { 100 | background-color: rgb(178, 34, 34); 101 | color: rgba(178, 34, 34, 0.5); 102 | order: 14; 103 | } 104 | 105 | .test-system-ui-font-family { 106 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; 107 | order: 15; 108 | } 109 | 110 | .test-font-variant-property { 111 | font-feature-settings: "smcp"; 112 | font-variant-caps: small-caps; 113 | order: 16; 114 | } 115 | 116 | .test-all-property { 117 | animation: none 0s ease 0s 1 normal none running; 118 | backface-visibility: visible; 119 | background: transparent none repeat 0 0 / auto auto padding-box border-box scroll; 120 | border: medium none currentColor; 121 | border-collapse: separate; 122 | border-image: none; 123 | border-radius: 0; 124 | border-spacing: 0; 125 | bottom: auto; 126 | box-shadow: none; 127 | box-sizing: content-box; 128 | caption-side: top; 129 | clear: none; 130 | clip: auto; 131 | color: #000; 132 | columns: auto; 133 | column-count: auto; 134 | column-fill: balance; 135 | column-gap: normal; 136 | column-rule: medium none currentColor; 137 | column-span: 1; 138 | column-width: auto; 139 | content: normal; 140 | counter-increment: none; 141 | counter-reset: none; 142 | cursor: auto; 143 | direction: ltr; 144 | display: inline; 145 | empty-cells: show; 146 | float: none; 147 | font-family: serif; 148 | font-size: medium; 149 | font-style: normal; 150 | font-variant: normal; 151 | font-weight: normal; 152 | font-stretch: normal; 153 | line-height: normal; 154 | height: auto; 155 | hyphens: none; 156 | left: auto; 157 | letter-spacing: normal; 158 | list-style: disc outside none; 159 | margin: 0; 160 | max-height: none; 161 | max-width: none; 162 | min-height: 0; 163 | min-width: 0; 164 | opacity: 1; 165 | orphans: 2; 166 | outline: medium none invert; 167 | overflow: visible; 168 | overflow-x: visible; 169 | overflow-y: visible; 170 | padding: 0; 171 | page-break-after: auto; 172 | page-break-before: auto; 173 | page-break-inside: auto; 174 | perspective: none; 175 | perspective-origin: 50% 50%; 176 | position: static; 177 | right: auto; 178 | tab-size: 8; 179 | table-layout: auto; 180 | text-align: left; 181 | text-align-last: auto; 182 | text-decoration: none; 183 | text-indent: 0; 184 | text-shadow: none; 185 | text-transform: none; 186 | top: auto; 187 | transform: none; 188 | transform-origin: 50% 50% 0; 189 | transform-style: flat; 190 | transition: none 0s ease 0s; 191 | unicode-bidi: normal; 192 | vertical-align: baseline; 193 | visibility: visible; 194 | white-space: normal; 195 | widows: 2; 196 | width: auto; 197 | word-spacing: normal; 198 | z-index: auto; 199 | all: initial; 200 | order: 17; 201 | } 202 | 203 | .test-matches-pseudo-class:matches(:first-child, .special) { 204 | order: 18; 205 | } 206 | 207 | .test-not-pseudo-class:not(:first-child):not(.special) { 208 | order: 19; 209 | } 210 | 211 | .test-any-link-pseudo-class:link,.test-any-link-pseudo-class:visited { 212 | order: 20; 213 | } 214 | 215 | .test-any-link-pseudo-class:any-link { 216 | order: 20; 217 | } 218 | 219 | [dir="rtl"] .test-dir-pseudo-class { 220 | order: 21; 221 | } 222 | 223 | .test-overflow-wrap-property { 224 | order: 22; 225 | word-wrap: break-word; 226 | } 227 | 228 | .test-focus-visible-pseudo-class.focus-visible { 229 | order: 23; 230 | } 231 | 232 | .test-focus-visible-pseudo-class:focus-visible { 233 | order: 23; 234 | } 235 | 236 | .test-double-position-gradients { 237 | background-image: conic-gradient(yellowgreen 40%, gold 0deg, gold 75%, #f06 0deg); 238 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 239 | } 240 | 241 | .test-blank-pseudo-class:blank { 242 | background-color: yellow; 243 | } 244 | 245 | .test-has-pseudo-class[\:has\(.inner-class\)] { 246 | background-color: yellow; 247 | } 248 | 249 | .test-has-pseudo-class:has(.inner-class) { 250 | background-color: yellow; 251 | } 252 | -------------------------------------------------------------------------------- /test/basic.autoprefixer.false.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: 1; 7 | order: var(--order); 8 | } 9 | 10 | .test-image-set-function { 11 | background-image: url(img/test.png); 12 | } 13 | 14 | @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { 15 | 16 | .test-image-set-function { 17 | background-image: url(img/test-2x.png); 18 | } 19 | } 20 | 21 | .test-image-set-function { 22 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 23 | order: 2; 24 | } 25 | 26 | [dir="ltr"] .test-logical-properties-and-values { 27 | margin: 1px 4px 3px 2px; 28 | } 29 | 30 | [dir="rtl"] .test-logical-properties-and-values { 31 | margin: 1px 2px 3px 4px; 32 | } 33 | 34 | .test-logical-properties-and-values { 35 | order: 3; 36 | padding-top: 5px; 37 | padding-bottom: 5px; 38 | } 39 | 40 | .test-nesting-rules { 41 | order: 4; 42 | 43 | & p { 44 | order: 5; 45 | } 46 | 47 | order: 6; 48 | } 49 | 50 | @custom-media --narrow-window (max-width: 30em); 51 | 52 | @media (--narrow-window) { 53 | .test-custom-media-queries { 54 | order: 7; 55 | } 56 | } 57 | 58 | @media (min-width: 480px) and (max-width: 767px) { 59 | .test-media-query-ranges { 60 | order: 8; 61 | } 62 | } 63 | 64 | @custom-media --dark-mode (prefers-color-scheme: dark); 65 | 66 | @media (--dark-mode) { 67 | body { 68 | background-color: black; 69 | color: white; 70 | } 71 | } 72 | 73 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 74 | 75 | .test-custom-selectors:--heading { 76 | order: 9; 77 | } 78 | 79 | .test-case-insensitive-attributes[frame=hsides],.test-case-insensitive-attributes[frame=Hsides],.test-case-insensitive-attributes[frame=hSides],.test-case-insensitive-attributes[frame=HSides],.test-case-insensitive-attributes[frame=hsIdes],.test-case-insensitive-attributes[frame=HsIdes],.test-case-insensitive-attributes[frame=hSIdes],.test-case-insensitive-attributes[frame=HSIdes],.test-case-insensitive-attributes[frame=hsiDes],.test-case-insensitive-attributes[frame=HsiDes],.test-case-insensitive-attributes[frame=hSiDes],.test-case-insensitive-attributes[frame=HSiDes],.test-case-insensitive-attributes[frame=hsIDes],.test-case-insensitive-attributes[frame=HsIDes],.test-case-insensitive-attributes[frame=hSIDes],.test-case-insensitive-attributes[frame=HSIDes],.test-case-insensitive-attributes[frame=hsidEs],.test-case-insensitive-attributes[frame=HsidEs],.test-case-insensitive-attributes[frame=hSidEs],.test-case-insensitive-attributes[frame=HSidEs],.test-case-insensitive-attributes[frame=hsIdEs],.test-case-insensitive-attributes[frame=HsIdEs],.test-case-insensitive-attributes[frame=hSIdEs],.test-case-insensitive-attributes[frame=HSIdEs],.test-case-insensitive-attributes[frame=hsiDEs],.test-case-insensitive-attributes[frame=HsiDEs],.test-case-insensitive-attributes[frame=hSiDEs],.test-case-insensitive-attributes[frame=HSiDEs],.test-case-insensitive-attributes[frame=hsIDEs],.test-case-insensitive-attributes[frame=HsIDEs],.test-case-insensitive-attributes[frame=hSIDEs],.test-case-insensitive-attributes[frame=HSIDEs],.test-case-insensitive-attributes[frame=hsideS],.test-case-insensitive-attributes[frame=HsideS],.test-case-insensitive-attributes[frame=hSideS],.test-case-insensitive-attributes[frame=HSideS],.test-case-insensitive-attributes[frame=hsIdeS],.test-case-insensitive-attributes[frame=HsIdeS],.test-case-insensitive-attributes[frame=hSIdeS],.test-case-insensitive-attributes[frame=HSIdeS],.test-case-insensitive-attributes[frame=hsiDeS],.test-case-insensitive-attributes[frame=HsiDeS],.test-case-insensitive-attributes[frame=hSiDeS],.test-case-insensitive-attributes[frame=HSiDeS],.test-case-insensitive-attributes[frame=hsIDeS],.test-case-insensitive-attributes[frame=HsIDeS],.test-case-insensitive-attributes[frame=hSIDeS],.test-case-insensitive-attributes[frame=HSIDeS],.test-case-insensitive-attributes[frame=hsidES],.test-case-insensitive-attributes[frame=HsidES],.test-case-insensitive-attributes[frame=hSidES],.test-case-insensitive-attributes[frame=HSidES],.test-case-insensitive-attributes[frame=hsIdES],.test-case-insensitive-attributes[frame=HsIdES],.test-case-insensitive-attributes[frame=hSIdES],.test-case-insensitive-attributes[frame=HSIdES],.test-case-insensitive-attributes[frame=hsiDES],.test-case-insensitive-attributes[frame=HsiDES],.test-case-insensitive-attributes[frame=hSiDES],.test-case-insensitive-attributes[frame=HSiDES],.test-case-insensitive-attributes[frame=hsIDES],.test-case-insensitive-attributes[frame=HsIDES],.test-case-insensitive-attributes[frame=hSIDES],.test-case-insensitive-attributes[frame=HSIDES] { 80 | order: 10; 81 | } 82 | 83 | .test-rebeccapurple-color { 84 | color: #639; 85 | order: 11; 86 | } 87 | 88 | .test-hexadecimal-alpha-notation { 89 | background-color: rgba(243,243,243,0.95294); 90 | color: rgba(0,0,0,0.2); 91 | order: 12; 92 | } 93 | 94 | .test-color-functional-notation { 95 | color: rgb(70% 13.5% 13.5% / 50%); 96 | order: 13; 97 | } 98 | 99 | .test-lab-function { 100 | background-color: rgb(178, 34, 34); 101 | color: rgba(178, 34, 34, 0.5); 102 | order: 14; 103 | } 104 | 105 | .test-system-ui-font-family { 106 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; 107 | order: 15; 108 | } 109 | 110 | .test-font-variant-property { 111 | font-feature-settings: "smcp"; 112 | font-variant-caps: small-caps; 113 | order: 16; 114 | } 115 | 116 | .test-all-property { 117 | animation: none 0s ease 0s 1 normal none running; 118 | backface-visibility: visible; 119 | background: transparent none repeat 0 0 / auto auto padding-box border-box scroll; 120 | border: medium none currentColor; 121 | border-collapse: separate; 122 | border-image: none; 123 | border-radius: 0; 124 | border-spacing: 0; 125 | bottom: auto; 126 | box-shadow: none; 127 | box-sizing: content-box; 128 | caption-side: top; 129 | clear: none; 130 | clip: auto; 131 | color: #000; 132 | columns: auto; 133 | column-count: auto; 134 | column-fill: balance; 135 | column-gap: normal; 136 | column-rule: medium none currentColor; 137 | column-span: 1; 138 | column-width: auto; 139 | content: normal; 140 | counter-increment: none; 141 | counter-reset: none; 142 | cursor: auto; 143 | direction: ltr; 144 | display: inline; 145 | empty-cells: show; 146 | float: none; 147 | font-family: serif; 148 | font-size: medium; 149 | font-style: normal; 150 | font-variant: normal; 151 | font-weight: normal; 152 | font-stretch: normal; 153 | line-height: normal; 154 | height: auto; 155 | hyphens: none; 156 | left: auto; 157 | letter-spacing: normal; 158 | list-style: disc outside none; 159 | margin: 0; 160 | max-height: none; 161 | max-width: none; 162 | min-height: 0; 163 | min-width: 0; 164 | opacity: 1; 165 | orphans: 2; 166 | outline: medium none invert; 167 | overflow: visible; 168 | overflow-x: visible; 169 | overflow-y: visible; 170 | padding: 0; 171 | page-break-after: auto; 172 | page-break-before: auto; 173 | page-break-inside: auto; 174 | perspective: none; 175 | perspective-origin: 50% 50%; 176 | position: static; 177 | right: auto; 178 | tab-size: 8; 179 | table-layout: auto; 180 | text-align: left; 181 | text-align-last: auto; 182 | text-decoration: none; 183 | text-indent: 0; 184 | text-shadow: none; 185 | text-transform: none; 186 | top: auto; 187 | transform: none; 188 | transform-origin: 50% 50% 0; 189 | transform-style: flat; 190 | transition: none 0s ease 0s; 191 | unicode-bidi: normal; 192 | vertical-align: baseline; 193 | visibility: visible; 194 | white-space: normal; 195 | widows: 2; 196 | width: auto; 197 | word-spacing: normal; 198 | z-index: auto; 199 | all: initial; 200 | order: 17; 201 | } 202 | 203 | .test-matches-pseudo-class:matches(:first-child, .special) { 204 | order: 18; 205 | } 206 | 207 | .test-not-pseudo-class:not(:first-child):not(.special) { 208 | order: 19; 209 | } 210 | 211 | .test-any-link-pseudo-class:link,.test-any-link-pseudo-class:visited { 212 | order: 20; 213 | } 214 | 215 | .test-any-link-pseudo-class:any-link { 216 | order: 20; 217 | } 218 | 219 | [dir="rtl"] .test-dir-pseudo-class { 220 | order: 21; 221 | } 222 | 223 | .test-overflow-wrap-property { 224 | order: 22; 225 | word-wrap: break-word; 226 | } 227 | 228 | .test-focus-visible-pseudo-class.focus-visible { 229 | order: 23; 230 | } 231 | 232 | .test-focus-visible-pseudo-class:focus-visible { 233 | order: 23; 234 | } 235 | 236 | .test-double-position-gradients { 237 | background-image: conic-gradient(yellowgreen 40%, gold 0deg, gold 75%, #f06 0deg); 238 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 239 | } 240 | 241 | .test-blank-pseudo-class:blank { 242 | background-color: yellow; 243 | } 244 | 245 | .test-has-pseudo-class[\:has\(.inner-class\)] { 246 | background-color: yellow; 247 | } 248 | 249 | .test-has-pseudo-class:has(.inner-class) { 250 | background-color: yellow; 251 | } 252 | -------------------------------------------------------------------------------- /test/basic.ch38.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: 1; 7 | order: var(--order); 8 | } 9 | 10 | .test-image-set-function { 11 | background-image: url(img/test.png); 12 | } 13 | 14 | @media (min-resolution: 192dpi) { 15 | 16 | .test-image-set-function { 17 | background-image: url(img/test-2x.png); 18 | } 19 | } 20 | 21 | .test-image-set-function { 22 | background-image: -webkit-image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 23 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 24 | order: 2; 25 | } 26 | 27 | [dir="ltr"] .test-logical-properties-and-values { 28 | margin: 1px 4px 3px 2px; 29 | } 30 | 31 | [dir="rtl"] .test-logical-properties-and-values { 32 | margin: 1px 2px 3px 4px; 33 | } 34 | 35 | .test-logical-properties-and-values { 36 | order: 3; 37 | padding-top: 5px; 38 | padding-bottom: 5px; 39 | } 40 | 41 | .test-nesting-rules { 42 | order: 4; 43 | 44 | & p { 45 | order: 5; 46 | } 47 | 48 | order: 6; 49 | } 50 | 51 | @custom-media --narrow-window (max-width: 30em); 52 | 53 | @media (--narrow-window) { 54 | .test-custom-media-queries { 55 | order: 7; 56 | } 57 | } 58 | 59 | @media (min-width: 480px) and (max-width: 767px) { 60 | .test-media-query-ranges { 61 | order: 8; 62 | } 63 | } 64 | 65 | @custom-media --dark-mode (prefers-color-scheme: dark); 66 | 67 | @media (--dark-mode) { 68 | body { 69 | background-color: black; 70 | color: white; 71 | } 72 | } 73 | 74 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 75 | 76 | .test-custom-selectors:--heading { 77 | order: 9; 78 | } 79 | 80 | .test-case-insensitive-attributes[frame=hsides],.test-case-insensitive-attributes[frame=Hsides],.test-case-insensitive-attributes[frame=hSides],.test-case-insensitive-attributes[frame=HSides],.test-case-insensitive-attributes[frame=hsIdes],.test-case-insensitive-attributes[frame=HsIdes],.test-case-insensitive-attributes[frame=hSIdes],.test-case-insensitive-attributes[frame=HSIdes],.test-case-insensitive-attributes[frame=hsiDes],.test-case-insensitive-attributes[frame=HsiDes],.test-case-insensitive-attributes[frame=hSiDes],.test-case-insensitive-attributes[frame=HSiDes],.test-case-insensitive-attributes[frame=hsIDes],.test-case-insensitive-attributes[frame=HsIDes],.test-case-insensitive-attributes[frame=hSIDes],.test-case-insensitive-attributes[frame=HSIDes],.test-case-insensitive-attributes[frame=hsidEs],.test-case-insensitive-attributes[frame=HsidEs],.test-case-insensitive-attributes[frame=hSidEs],.test-case-insensitive-attributes[frame=HSidEs],.test-case-insensitive-attributes[frame=hsIdEs],.test-case-insensitive-attributes[frame=HsIdEs],.test-case-insensitive-attributes[frame=hSIdEs],.test-case-insensitive-attributes[frame=HSIdEs],.test-case-insensitive-attributes[frame=hsiDEs],.test-case-insensitive-attributes[frame=HsiDEs],.test-case-insensitive-attributes[frame=hSiDEs],.test-case-insensitive-attributes[frame=HSiDEs],.test-case-insensitive-attributes[frame=hsIDEs],.test-case-insensitive-attributes[frame=HsIDEs],.test-case-insensitive-attributes[frame=hSIDEs],.test-case-insensitive-attributes[frame=HSIDEs],.test-case-insensitive-attributes[frame=hsideS],.test-case-insensitive-attributes[frame=HsideS],.test-case-insensitive-attributes[frame=hSideS],.test-case-insensitive-attributes[frame=HSideS],.test-case-insensitive-attributes[frame=hsIdeS],.test-case-insensitive-attributes[frame=HsIdeS],.test-case-insensitive-attributes[frame=hSIdeS],.test-case-insensitive-attributes[frame=HSIdeS],.test-case-insensitive-attributes[frame=hsiDeS],.test-case-insensitive-attributes[frame=HsiDeS],.test-case-insensitive-attributes[frame=hSiDeS],.test-case-insensitive-attributes[frame=HSiDeS],.test-case-insensitive-attributes[frame=hsIDeS],.test-case-insensitive-attributes[frame=HsIDeS],.test-case-insensitive-attributes[frame=hSIDeS],.test-case-insensitive-attributes[frame=HSIDeS],.test-case-insensitive-attributes[frame=hsidES],.test-case-insensitive-attributes[frame=HsidES],.test-case-insensitive-attributes[frame=hSidES],.test-case-insensitive-attributes[frame=HSidES],.test-case-insensitive-attributes[frame=hsIdES],.test-case-insensitive-attributes[frame=HsIdES],.test-case-insensitive-attributes[frame=hSIdES],.test-case-insensitive-attributes[frame=HSIdES],.test-case-insensitive-attributes[frame=hsiDES],.test-case-insensitive-attributes[frame=HsiDES],.test-case-insensitive-attributes[frame=hSiDES],.test-case-insensitive-attributes[frame=HSiDES],.test-case-insensitive-attributes[frame=hsIDES],.test-case-insensitive-attributes[frame=HsIDES],.test-case-insensitive-attributes[frame=hSIDES],.test-case-insensitive-attributes[frame=HSIDES] { 81 | order: 10; 82 | } 83 | 84 | .test-rebeccapurple-color { 85 | color: rebeccapurple; 86 | order: 11; 87 | } 88 | 89 | .test-hexadecimal-alpha-notation { 90 | background-color: rgba(243,243,243,0.95294); 91 | color: rgba(0,0,0,0.2); 92 | order: 12; 93 | } 94 | 95 | .test-color-functional-notation { 96 | color: rgb(70% 13.5% 13.5% / 50%); 97 | order: 13; 98 | } 99 | 100 | .test-lab-function { 101 | background-color: rgb(178, 34, 34); 102 | color: rgba(178, 34, 34, 0.5); 103 | order: 14; 104 | } 105 | 106 | .test-system-ui-font-family { 107 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; 108 | order: 15; 109 | } 110 | 111 | .test-font-variant-property { 112 | -webkit-font-feature-settings: "smcp"; 113 | font-feature-settings: "smcp"; 114 | font-variant-caps: small-caps; 115 | order: 16; 116 | } 117 | 118 | .test-all-property { 119 | all: initial; 120 | order: 17; 121 | } 122 | 123 | .test-matches-pseudo-class:matches(:first-child, .special) { 124 | order: 18; 125 | } 126 | 127 | .test-not-pseudo-class:not(:first-child):not(.special) { 128 | order: 19; 129 | } 130 | 131 | .test-any-link-pseudo-class:-webkit-any-link { 132 | order: 20; 133 | } 134 | 135 | .test-any-link-pseudo-class:any-link { 136 | order: 20; 137 | } 138 | 139 | [dir="rtl"] .test-dir-pseudo-class { 140 | order: 21; 141 | } 142 | 143 | .test-overflow-wrap-property { 144 | order: 22; 145 | overflow-wrap: break-word; 146 | } 147 | 148 | .test-focus-visible-pseudo-class.focus-visible { 149 | order: 23; 150 | } 151 | 152 | .test-focus-visible-pseudo-class:focus-visible { 153 | order: 23; 154 | } 155 | 156 | .test-double-position-gradients { 157 | background-image: conic-gradient(yellowgreen 40%, gold 0deg, gold 75%, #f06 0deg); 158 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 159 | } 160 | 161 | .test-blank-pseudo-class:blank { 162 | background-color: yellow; 163 | } 164 | 165 | .test-has-pseudo-class[\:has\(.inner-class\)] { 166 | background-color: yellow; 167 | } 168 | 169 | .test-has-pseudo-class:has(.inner-class) { 170 | background-color: yellow; 171 | } 172 | -------------------------------------------------------------------------------- /test/basic.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: var(--order); 7 | } 8 | 9 | .test-image-set-function { 10 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 11 | order: 2; 12 | } 13 | 14 | .test-logical-properties-and-values { 15 | margin: logical 1px 2px 3px 4px; 16 | order: 3; 17 | padding-block: 5px; 18 | } 19 | 20 | .test-nesting-rules { 21 | order: 4; 22 | 23 | & p { 24 | order: 5; 25 | } 26 | 27 | order: 6; 28 | } 29 | 30 | @custom-media --narrow-window (max-width: 30em); 31 | 32 | @media (--narrow-window) { 33 | .test-custom-media-queries { 34 | order: 7; 35 | } 36 | } 37 | 38 | @media (480px <= width < 768px) { 39 | .test-media-query-ranges { 40 | order: 8; 41 | } 42 | } 43 | 44 | @custom-media --dark-mode (prefers-color-scheme: dark); 45 | 46 | @media (--dark-mode) { 47 | body { 48 | background-color: black; 49 | color: white; 50 | } 51 | } 52 | 53 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 54 | 55 | .test-custom-selectors:--heading { 56 | order: 9; 57 | } 58 | 59 | .test-case-insensitive-attributes[frame=hsides i] { 60 | order: 10; 61 | } 62 | 63 | .test-rebeccapurple-color { 64 | color: rebeccapurple; 65 | order: 11; 66 | } 67 | 68 | .test-hexadecimal-alpha-notation { 69 | background-color: #f3f3f3f3; 70 | color: #0003; 71 | order: 12; 72 | } 73 | 74 | .test-color-functional-notation { 75 | color: rgb(70% 13.5% 13.5% / 50%); 76 | order: 13; 77 | } 78 | 79 | .test-lab-function { 80 | background-color: lab(40% 56.6 39); 81 | color: lch(40% 68.8 34.5 / 50%); 82 | order: 14; 83 | } 84 | 85 | .test-system-ui-font-family { 86 | font-family: system-ui; 87 | order: 15; 88 | } 89 | 90 | .test-font-variant-property { 91 | font-variant-caps: small-caps; 92 | order: 16; 93 | } 94 | 95 | .test-all-property { 96 | all: initial; 97 | order: 17; 98 | } 99 | 100 | .test-matches-pseudo-class:matches(:first-child, .special) { 101 | order: 18; 102 | } 103 | 104 | .test-not-pseudo-class:not(:first-child, .special) { 105 | order: 19; 106 | } 107 | 108 | .test-any-link-pseudo-class:any-link { 109 | order: 20; 110 | } 111 | 112 | .test-dir-pseudo-class:dir(rtl) { 113 | order: 21; 114 | } 115 | 116 | .test-overflow-wrap-property { 117 | order: 22; 118 | overflow-wrap: break-word; 119 | } 120 | 121 | .test-focus-visible-pseudo-class:focus-visible { 122 | order: 23; 123 | } 124 | 125 | .test-double-position-gradients { 126 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 127 | } 128 | 129 | .test-blank-pseudo-class:blank { 130 | background-color: yellow; 131 | } 132 | 133 | .test-has-pseudo-class:has(.inner-class) { 134 | background-color: yellow; 135 | } 136 | -------------------------------------------------------------------------------- /test/basic.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: 1; 7 | order: var(--order); 8 | } 9 | 10 | .test-image-set-function { 11 | background-image: url(img/test.png); 12 | } 13 | 14 | @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { 15 | 16 | .test-image-set-function { 17 | background-image: url(img/test-2x.png); 18 | } 19 | } 20 | 21 | .test-image-set-function { 22 | background-image: -webkit-image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 23 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 24 | order: 2; 25 | } 26 | 27 | [dir="ltr"] .test-logical-properties-and-values { 28 | margin: 1px 4px 3px 2px; 29 | } 30 | 31 | [dir="rtl"] .test-logical-properties-and-values { 32 | margin: 1px 2px 3px 4px; 33 | } 34 | 35 | .test-logical-properties-and-values { 36 | order: 3; 37 | padding-top: 5px; 38 | padding-bottom: 5px; 39 | } 40 | 41 | .test-nesting-rules { 42 | order: 4; 43 | 44 | & p { 45 | order: 5; 46 | } 47 | 48 | order: 6; 49 | } 50 | 51 | @custom-media --narrow-window (max-width: 30em); 52 | 53 | @media (--narrow-window) { 54 | .test-custom-media-queries { 55 | order: 7; 56 | } 57 | } 58 | 59 | @media (min-width: 480px) and (max-width: 767px) { 60 | .test-media-query-ranges { 61 | order: 8; 62 | } 63 | } 64 | 65 | @custom-media --dark-mode (prefers-color-scheme: dark); 66 | 67 | @media (--dark-mode) { 68 | body { 69 | background-color: black; 70 | color: white; 71 | } 72 | } 73 | 74 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 75 | 76 | .test-custom-selectors:--heading { 77 | order: 9; 78 | } 79 | 80 | .test-case-insensitive-attributes[frame=hsides],.test-case-insensitive-attributes[frame=Hsides],.test-case-insensitive-attributes[frame=hSides],.test-case-insensitive-attributes[frame=HSides],.test-case-insensitive-attributes[frame=hsIdes],.test-case-insensitive-attributes[frame=HsIdes],.test-case-insensitive-attributes[frame=hSIdes],.test-case-insensitive-attributes[frame=HSIdes],.test-case-insensitive-attributes[frame=hsiDes],.test-case-insensitive-attributes[frame=HsiDes],.test-case-insensitive-attributes[frame=hSiDes],.test-case-insensitive-attributes[frame=HSiDes],.test-case-insensitive-attributes[frame=hsIDes],.test-case-insensitive-attributes[frame=HsIDes],.test-case-insensitive-attributes[frame=hSIDes],.test-case-insensitive-attributes[frame=HSIDes],.test-case-insensitive-attributes[frame=hsidEs],.test-case-insensitive-attributes[frame=HsidEs],.test-case-insensitive-attributes[frame=hSidEs],.test-case-insensitive-attributes[frame=HSidEs],.test-case-insensitive-attributes[frame=hsIdEs],.test-case-insensitive-attributes[frame=HsIdEs],.test-case-insensitive-attributes[frame=hSIdEs],.test-case-insensitive-attributes[frame=HSIdEs],.test-case-insensitive-attributes[frame=hsiDEs],.test-case-insensitive-attributes[frame=HsiDEs],.test-case-insensitive-attributes[frame=hSiDEs],.test-case-insensitive-attributes[frame=HSiDEs],.test-case-insensitive-attributes[frame=hsIDEs],.test-case-insensitive-attributes[frame=HsIDEs],.test-case-insensitive-attributes[frame=hSIDEs],.test-case-insensitive-attributes[frame=HSIDEs],.test-case-insensitive-attributes[frame=hsideS],.test-case-insensitive-attributes[frame=HsideS],.test-case-insensitive-attributes[frame=hSideS],.test-case-insensitive-attributes[frame=HSideS],.test-case-insensitive-attributes[frame=hsIdeS],.test-case-insensitive-attributes[frame=HsIdeS],.test-case-insensitive-attributes[frame=hSIdeS],.test-case-insensitive-attributes[frame=HSIdeS],.test-case-insensitive-attributes[frame=hsiDeS],.test-case-insensitive-attributes[frame=HsiDeS],.test-case-insensitive-attributes[frame=hSiDeS],.test-case-insensitive-attributes[frame=HSiDeS],.test-case-insensitive-attributes[frame=hsIDeS],.test-case-insensitive-attributes[frame=HsIDeS],.test-case-insensitive-attributes[frame=hSIDeS],.test-case-insensitive-attributes[frame=HSIDeS],.test-case-insensitive-attributes[frame=hsidES],.test-case-insensitive-attributes[frame=HsidES],.test-case-insensitive-attributes[frame=hSidES],.test-case-insensitive-attributes[frame=HSidES],.test-case-insensitive-attributes[frame=hsIdES],.test-case-insensitive-attributes[frame=HsIdES],.test-case-insensitive-attributes[frame=hSIdES],.test-case-insensitive-attributes[frame=HSIdES],.test-case-insensitive-attributes[frame=hsiDES],.test-case-insensitive-attributes[frame=HsiDES],.test-case-insensitive-attributes[frame=hSiDES],.test-case-insensitive-attributes[frame=HSiDES],.test-case-insensitive-attributes[frame=hsIDES],.test-case-insensitive-attributes[frame=HsIDES],.test-case-insensitive-attributes[frame=hSIDES],.test-case-insensitive-attributes[frame=HSIDES] { 81 | order: 10; 82 | } 83 | 84 | .test-rebeccapurple-color { 85 | color: #639; 86 | order: 11; 87 | } 88 | 89 | .test-hexadecimal-alpha-notation { 90 | background-color: rgba(243,243,243,0.95294); 91 | color: rgba(0,0,0,0.2); 92 | order: 12; 93 | } 94 | 95 | .test-color-functional-notation { 96 | color: rgb(70% 13.5% 13.5% / 50%); 97 | order: 13; 98 | } 99 | 100 | .test-lab-function { 101 | background-color: rgb(178, 34, 34); 102 | color: rgba(178, 34, 34, 0.5); 103 | order: 14; 104 | } 105 | 106 | .test-system-ui-font-family { 107 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; 108 | order: 15; 109 | } 110 | 111 | .test-font-variant-property { 112 | font-feature-settings: "smcp"; 113 | font-variant-caps: small-caps; 114 | order: 16; 115 | } 116 | 117 | .test-all-property { 118 | -webkit-animation: none 0s ease 0s 1 normal none running; 119 | animation: none 0s ease 0s 1 normal none running; 120 | -webkit-backface-visibility: visible; 121 | backface-visibility: visible; 122 | background: transparent none repeat 0 0 / auto auto padding-box border-box scroll; 123 | border: medium none currentColor; 124 | border-collapse: separate; 125 | -o-border-image: none; 126 | border-image: none; 127 | border-radius: 0; 128 | border-spacing: 0; 129 | bottom: auto; 130 | box-shadow: none; 131 | box-sizing: content-box; 132 | caption-side: top; 133 | clear: none; 134 | clip: auto; 135 | color: #000; 136 | -moz-columns: auto; 137 | columns: auto; 138 | -moz-column-count: auto; 139 | column-count: auto; 140 | -moz-column-fill: balance; 141 | column-fill: balance; 142 | -moz-column-gap: normal; 143 | column-gap: normal; 144 | -moz-column-rule: medium none currentColor; 145 | column-rule: medium none currentColor; 146 | -moz-column-span: 1; 147 | column-span: 1; 148 | -moz-column-width: auto; 149 | column-width: auto; 150 | content: normal; 151 | counter-increment: none; 152 | counter-reset: none; 153 | cursor: auto; 154 | direction: ltr; 155 | display: inline; 156 | empty-cells: show; 157 | float: none; 158 | font-family: serif; 159 | font-size: medium; 160 | font-style: normal; 161 | font-variant: normal; 162 | font-weight: normal; 163 | font-stretch: normal; 164 | line-height: normal; 165 | height: auto; 166 | -webkit-hyphens: none; 167 | -ms-hyphens: none; 168 | hyphens: none; 169 | left: auto; 170 | letter-spacing: normal; 171 | list-style: disc outside none; 172 | margin: 0; 173 | max-height: none; 174 | max-width: none; 175 | min-height: 0; 176 | min-width: 0; 177 | opacity: 1; 178 | orphans: 2; 179 | outline: medium none invert; 180 | overflow: visible; 181 | overflow-x: visible; 182 | overflow-y: visible; 183 | padding: 0; 184 | page-break-after: auto; 185 | page-break-before: auto; 186 | page-break-inside: auto; 187 | perspective: none; 188 | perspective-origin: 50% 50%; 189 | position: static; 190 | right: auto; 191 | -moz-tab-size: 8; 192 | -o-tab-size: 8; 193 | tab-size: 8; 194 | table-layout: auto; 195 | text-align: left; 196 | -moz-text-align-last: auto; 197 | text-align-last: auto; 198 | text-decoration: none; 199 | text-indent: 0; 200 | text-shadow: none; 201 | text-transform: none; 202 | top: auto; 203 | transform: none; 204 | transform-origin: 50% 50% 0; 205 | transform-style: flat; 206 | transition: none 0s ease 0s; 207 | unicode-bidi: normal; 208 | vertical-align: baseline; 209 | visibility: visible; 210 | white-space: normal; 211 | widows: 2; 212 | width: auto; 213 | word-spacing: normal; 214 | z-index: auto; 215 | all: initial; 216 | order: 17; 217 | } 218 | 219 | .test-matches-pseudo-class:matches(:first-child, .special) { 220 | order: 18; 221 | } 222 | 223 | .test-not-pseudo-class:not(:first-child):not(.special) { 224 | order: 19; 225 | } 226 | 227 | .test-any-link-pseudo-class:link,.test-any-link-pseudo-class:visited { 228 | order: 20; 229 | } 230 | 231 | .test-any-link-pseudo-class:-webkit-any-link { 232 | order: 20; 233 | } 234 | 235 | .test-any-link-pseudo-class:-moz-any-link { 236 | order: 20; 237 | } 238 | 239 | .test-any-link-pseudo-class:any-link { 240 | order: 20; 241 | } 242 | 243 | [dir="rtl"] .test-dir-pseudo-class { 244 | order: 21; 245 | } 246 | 247 | .test-overflow-wrap-property { 248 | order: 22; 249 | word-wrap: break-word; 250 | } 251 | 252 | .test-focus-visible-pseudo-class.focus-visible { 253 | order: 23; 254 | } 255 | 256 | .test-focus-visible-pseudo-class:focus-visible { 257 | order: 23; 258 | } 259 | 260 | .test-double-position-gradients { 261 | background-image: conic-gradient(yellowgreen 40%, gold 0deg, gold 75%, #f06 0deg); 262 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 263 | } 264 | 265 | .test-blank-pseudo-class:blank { 266 | background-color: yellow; 267 | } 268 | 269 | .test-has-pseudo-class[\:has\(.inner-class\)] { 270 | background-color: yellow; 271 | } 272 | 273 | .test-has-pseudo-class:has(.inner-class) { 274 | background-color: yellow; 275 | } 276 | -------------------------------------------------------------------------------- /test/basic.ff49.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: var(--order); 7 | } 8 | 9 | .test-image-set-function { 10 | background-image: url(img/test.png); 11 | } 12 | 13 | @media (min-resolution: 192dpi) { 14 | 15 | .test-image-set-function { 16 | background-image: url(img/test-2x.png); 17 | } 18 | } 19 | 20 | .test-image-set-function { 21 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 22 | order: 2; 23 | } 24 | 25 | .test-logical-properties-and-values { 26 | margin: logical 1px 2px 3px 4px; 27 | order: 3; 28 | padding-block: 5px; 29 | } 30 | 31 | .test-nesting-rules { 32 | order: 4; 33 | 34 | & p { 35 | order: 5; 36 | } 37 | 38 | order: 6; 39 | } 40 | 41 | @custom-media --narrow-window (max-width: 30em); 42 | 43 | @media (--narrow-window) { 44 | .test-custom-media-queries { 45 | order: 7; 46 | } 47 | } 48 | 49 | @media (min-width: 480px) and (max-width: 767px) { 50 | .test-media-query-ranges { 51 | order: 8; 52 | } 53 | } 54 | 55 | @custom-media --dark-mode (prefers-color-scheme: dark); 56 | 57 | @media (--dark-mode) { 58 | body { 59 | background-color: black; 60 | color: white; 61 | } 62 | } 63 | 64 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 65 | 66 | .test-custom-selectors:--heading { 67 | order: 9; 68 | } 69 | 70 | .test-case-insensitive-attributes[frame=hsides i] { 71 | order: 10; 72 | } 73 | 74 | .test-rebeccapurple-color { 75 | color: rebeccapurple; 76 | order: 11; 77 | } 78 | 79 | .test-hexadecimal-alpha-notation { 80 | background-color: #f3f3f3f3; 81 | color: #0003; 82 | order: 12; 83 | } 84 | 85 | .test-color-functional-notation { 86 | color: rgb(70% 13.5% 13.5% / 50%); 87 | order: 13; 88 | } 89 | 90 | .test-lab-function { 91 | background-color: rgb(178, 34, 34); 92 | color: rgba(178, 34, 34, 0.5); 93 | order: 14; 94 | } 95 | 96 | .test-system-ui-font-family { 97 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; 98 | order: 15; 99 | } 100 | 101 | .test-font-variant-property { 102 | font-variant-caps: small-caps; 103 | order: 16; 104 | } 105 | 106 | .test-all-property { 107 | all: initial; 108 | order: 17; 109 | } 110 | 111 | .test-matches-pseudo-class:matches(:first-child, .special) { 112 | order: 18; 113 | } 114 | 115 | .test-not-pseudo-class:not(:first-child):not(.special) { 116 | order: 19; 117 | } 118 | 119 | .test-any-link-pseudo-class:-moz-any-link { 120 | order: 20; 121 | } 122 | 123 | .test-any-link-pseudo-class:any-link { 124 | order: 20; 125 | } 126 | 127 | .test-dir-pseudo-class:dir(rtl) { 128 | order: 21; 129 | } 130 | 131 | .test-overflow-wrap-property { 132 | order: 22; 133 | overflow-wrap: break-word; 134 | } 135 | 136 | .test-focus-visible-pseudo-class:focus-visible { 137 | order: 23; 138 | } 139 | 140 | .test-double-position-gradients { 141 | background-image: conic-gradient(yellowgreen 40%, gold 0deg, gold 75%, #f06 0deg); 142 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 143 | } 144 | 145 | .test-blank-pseudo-class:blank { 146 | background-color: yellow; 147 | } 148 | 149 | .test-has-pseudo-class[\:has\(.inner-class\)] { 150 | background-color: yellow; 151 | } 152 | 153 | .test-has-pseudo-class:has(.inner-class) { 154 | background-color: yellow; 155 | } 156 | -------------------------------------------------------------------------------- /test/basic.nesting.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: var(--order); 7 | } 8 | 9 | .test-image-set-function { 10 | background-image: -webkit-image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 11 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 12 | order: 2; 13 | } 14 | 15 | .test-logical-properties-and-values { 16 | margin: logical 1px 2px 3px 4px; 17 | order: 3; 18 | padding-block: 5px; 19 | } 20 | 21 | .test-nesting-rules { 22 | order: 4; 23 | } 24 | 25 | .test-nesting-rules p { 26 | order: 5; 27 | } 28 | 29 | .test-nesting-rules { 30 | 31 | order: 6; 32 | } 33 | 34 | @custom-media --narrow-window (max-width: 30em); 35 | 36 | @media (--narrow-window) { 37 | .test-custom-media-queries { 38 | order: 7; 39 | } 40 | } 41 | 42 | @media (480px <= width < 768px) { 43 | .test-media-query-ranges { 44 | order: 8; 45 | } 46 | } 47 | 48 | @custom-media --dark-mode (prefers-color-scheme: dark); 49 | 50 | @media (--dark-mode) { 51 | body { 52 | background-color: black; 53 | color: white; 54 | } 55 | } 56 | 57 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 58 | 59 | .test-custom-selectors:--heading { 60 | order: 9; 61 | } 62 | 63 | .test-case-insensitive-attributes[frame=hsides i] { 64 | order: 10; 65 | } 66 | 67 | .test-rebeccapurple-color { 68 | color: rebeccapurple; 69 | order: 11; 70 | } 71 | 72 | .test-hexadecimal-alpha-notation { 73 | background-color: #f3f3f3f3; 74 | color: #0003; 75 | order: 12; 76 | } 77 | 78 | .test-color-functional-notation { 79 | color: rgb(70% 13.5% 13.5% / 50%); 80 | order: 13; 81 | } 82 | 83 | .test-lab-function { 84 | background-color: lab(40% 56.6 39); 85 | color: lch(40% 68.8 34.5 / 50%); 86 | order: 14; 87 | } 88 | 89 | .test-system-ui-font-family { 90 | font-family: system-ui; 91 | order: 15; 92 | } 93 | 94 | .test-font-variant-property { 95 | font-variant-caps: small-caps; 96 | order: 16; 97 | } 98 | 99 | .test-all-property { 100 | all: initial; 101 | order: 17; 102 | } 103 | 104 | .test-matches-pseudo-class:matches(:first-child, .special) { 105 | order: 18; 106 | } 107 | 108 | .test-not-pseudo-class:not(:first-child, .special) { 109 | order: 19; 110 | } 111 | 112 | .test-any-link-pseudo-class:-webkit-any-link { 113 | order: 20; 114 | } 115 | 116 | .test-any-link-pseudo-class:-moz-any-link { 117 | order: 20; 118 | } 119 | 120 | .test-any-link-pseudo-class:any-link { 121 | order: 20; 122 | } 123 | 124 | .test-dir-pseudo-class:dir(rtl) { 125 | order: 21; 126 | } 127 | 128 | .test-overflow-wrap-property { 129 | order: 22; 130 | overflow-wrap: break-word; 131 | } 132 | 133 | .test-focus-visible-pseudo-class:focus-visible { 134 | order: 23; 135 | } 136 | 137 | .test-double-position-gradients { 138 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 139 | } 140 | 141 | .test-blank-pseudo-class:blank { 142 | background-color: yellow; 143 | } 144 | 145 | .test-has-pseudo-class:has(.inner-class) { 146 | background-color: yellow; 147 | } 148 | -------------------------------------------------------------------------------- /test/basic.stage0-ff49.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: var(--order); 7 | } 8 | 9 | .test-image-set-function { 10 | background-image: url(img/test.png); 11 | } 12 | 13 | @media (min-resolution: 192dpi) { 14 | 15 | .test-image-set-function { 16 | background-image: url(img/test-2x.png); 17 | } 18 | } 19 | 20 | .test-image-set-function { 21 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 22 | order: 2; 23 | } 24 | 25 | .test-logical-properties-and-values { 26 | margin: logical 1px 2px 3px 4px; 27 | order: 3; 28 | padding-block: 5px; 29 | } 30 | 31 | .test-nesting-rules { 32 | order: 4; 33 | } 34 | 35 | .test-nesting-rules p { 36 | order: 5; 37 | } 38 | 39 | .test-nesting-rules { 40 | 41 | order: 6; 42 | } 43 | 44 | @media (max-width: 30em) { 45 | .test-custom-media-queries { 46 | order: 7; 47 | } 48 | } 49 | 50 | @media (min-width: 480px) and (max-width: 767px) { 51 | .test-media-query-ranges { 52 | order: 8; 53 | } 54 | } 55 | 56 | @media (color-index: 48) { 57 | body { 58 | background-color: black; 59 | color: white; 60 | } 61 | } 62 | 63 | @media (prefers-color-scheme: dark) { 64 | body { 65 | background-color: black; 66 | color: white; 67 | } 68 | } 69 | 70 | h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.test-custom-selectors,h5.test-custom-selectors,h6.test-custom-selectors { 71 | order: 9; 72 | } 73 | 74 | .test-case-insensitive-attributes[frame=hsides i] { 75 | order: 10; 76 | } 77 | 78 | .test-rebeccapurple-color { 79 | color: rebeccapurple; 80 | order: 11; 81 | } 82 | 83 | .test-hexadecimal-alpha-notation { 84 | background-color: #f3f3f3f3; 85 | color: #0003; 86 | order: 12; 87 | } 88 | 89 | .test-color-functional-notation { 90 | color: rgba(178, 34, 34, 0.5); 91 | order: 13; 92 | } 93 | 94 | .test-lab-function { 95 | background-color: rgb(178, 34, 34); 96 | color: rgba(178, 34, 34, 0.5); 97 | order: 14; 98 | } 99 | 100 | .test-system-ui-font-family { 101 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; 102 | order: 15; 103 | } 104 | 105 | .test-font-variant-property { 106 | font-variant-caps: small-caps; 107 | order: 16; 108 | } 109 | 110 | .test-all-property { 111 | all: initial; 112 | order: 17; 113 | } 114 | 115 | .test-matches-pseudo-class:matches(:first-child, .special) { 116 | order: 18; 117 | } 118 | 119 | .test-not-pseudo-class:not(:first-child):not(.special) { 120 | order: 19; 121 | } 122 | 123 | .test-any-link-pseudo-class:-moz-any-link { 124 | order: 20; 125 | } 126 | 127 | .test-any-link-pseudo-class:any-link { 128 | order: 20; 129 | } 130 | 131 | .test-dir-pseudo-class:dir(rtl) { 132 | order: 21; 133 | } 134 | 135 | .test-overflow-wrap-property { 136 | order: 22; 137 | overflow-wrap: break-word; 138 | } 139 | 140 | .test-focus-visible-pseudo-class:focus-visible { 141 | order: 23; 142 | } 143 | 144 | .test-double-position-gradients { 145 | background-image: conic-gradient(yellowgreen 40%, gold 0deg, gold 75%, #f06 0deg); 146 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 147 | } 148 | 149 | .test-blank-pseudo-class[blank] { 150 | background-color: yellow; 151 | } 152 | 153 | .test-blank-pseudo-class:blank { 154 | background-color: yellow; 155 | } 156 | 157 | .test-has-pseudo-class[\:has\(.inner-class\)] { 158 | background-color: yellow; 159 | } 160 | 161 | .test-has-pseudo-class:has(.inner-class) { 162 | background-color: yellow; 163 | } 164 | -------------------------------------------------------------------------------- /test/basic.stage0.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --order: 1; 3 | } 4 | 5 | .test-custom-properties { 6 | order: 1; 7 | order: var(--order); 8 | } 9 | 10 | .test-image-set-function { 11 | background-image: url(img/test.png); 12 | } 13 | 14 | @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { 15 | 16 | .test-image-set-function { 17 | background-image: url(img/test-2x.png); 18 | } 19 | } 20 | 21 | .test-image-set-function { 22 | background-image: -webkit-image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 23 | background-image: image-set(url(img/test.png) 1x, url(img/test-2x.png) 2x); 24 | order: 2; 25 | } 26 | 27 | [dir="ltr"] .test-logical-properties-and-values { 28 | margin: 1px 4px 3px 2px; 29 | } 30 | 31 | [dir="rtl"] .test-logical-properties-and-values { 32 | margin: 1px 2px 3px 4px; 33 | } 34 | 35 | .test-logical-properties-and-values { 36 | order: 3; 37 | padding-top: 5px; 38 | padding-bottom: 5px; 39 | } 40 | 41 | .test-nesting-rules { 42 | order: 4; 43 | } 44 | 45 | .test-nesting-rules p { 46 | order: 5; 47 | } 48 | 49 | .test-nesting-rules { 50 | 51 | order: 6; 52 | } 53 | 54 | @media (max-width: 30em) { 55 | .test-custom-media-queries { 56 | order: 7; 57 | } 58 | } 59 | 60 | @media (min-width: 480px) and (max-width: 767px) { 61 | .test-media-query-ranges { 62 | order: 8; 63 | } 64 | } 65 | 66 | @media (color-index: 48) { 67 | body { 68 | background-color: black; 69 | color: white; 70 | } 71 | } 72 | 73 | @media (prefers-color-scheme: dark) { 74 | body { 75 | background-color: black; 76 | color: white; 77 | } 78 | } 79 | 80 | h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.test-custom-selectors,h5.test-custom-selectors,h6.test-custom-selectors { 81 | order: 9; 82 | } 83 | 84 | .test-case-insensitive-attributes[frame=hsides],.test-case-insensitive-attributes[frame=Hsides],.test-case-insensitive-attributes[frame=hSides],.test-case-insensitive-attributes[frame=HSides],.test-case-insensitive-attributes[frame=hsIdes],.test-case-insensitive-attributes[frame=HsIdes],.test-case-insensitive-attributes[frame=hSIdes],.test-case-insensitive-attributes[frame=HSIdes],.test-case-insensitive-attributes[frame=hsiDes],.test-case-insensitive-attributes[frame=HsiDes],.test-case-insensitive-attributes[frame=hSiDes],.test-case-insensitive-attributes[frame=HSiDes],.test-case-insensitive-attributes[frame=hsIDes],.test-case-insensitive-attributes[frame=HsIDes],.test-case-insensitive-attributes[frame=hSIDes],.test-case-insensitive-attributes[frame=HSIDes],.test-case-insensitive-attributes[frame=hsidEs],.test-case-insensitive-attributes[frame=HsidEs],.test-case-insensitive-attributes[frame=hSidEs],.test-case-insensitive-attributes[frame=HSidEs],.test-case-insensitive-attributes[frame=hsIdEs],.test-case-insensitive-attributes[frame=HsIdEs],.test-case-insensitive-attributes[frame=hSIdEs],.test-case-insensitive-attributes[frame=HSIdEs],.test-case-insensitive-attributes[frame=hsiDEs],.test-case-insensitive-attributes[frame=HsiDEs],.test-case-insensitive-attributes[frame=hSiDEs],.test-case-insensitive-attributes[frame=HSiDEs],.test-case-insensitive-attributes[frame=hsIDEs],.test-case-insensitive-attributes[frame=HsIDEs],.test-case-insensitive-attributes[frame=hSIDEs],.test-case-insensitive-attributes[frame=HSIDEs],.test-case-insensitive-attributes[frame=hsideS],.test-case-insensitive-attributes[frame=HsideS],.test-case-insensitive-attributes[frame=hSideS],.test-case-insensitive-attributes[frame=HSideS],.test-case-insensitive-attributes[frame=hsIdeS],.test-case-insensitive-attributes[frame=HsIdeS],.test-case-insensitive-attributes[frame=hSIdeS],.test-case-insensitive-attributes[frame=HSIdeS],.test-case-insensitive-attributes[frame=hsiDeS],.test-case-insensitive-attributes[frame=HsiDeS],.test-case-insensitive-attributes[frame=hSiDeS],.test-case-insensitive-attributes[frame=HSiDeS],.test-case-insensitive-attributes[frame=hsIDeS],.test-case-insensitive-attributes[frame=HsIDeS],.test-case-insensitive-attributes[frame=hSIDeS],.test-case-insensitive-attributes[frame=HSIDeS],.test-case-insensitive-attributes[frame=hsidES],.test-case-insensitive-attributes[frame=HsidES],.test-case-insensitive-attributes[frame=hSidES],.test-case-insensitive-attributes[frame=HSidES],.test-case-insensitive-attributes[frame=hsIdES],.test-case-insensitive-attributes[frame=HsIdES],.test-case-insensitive-attributes[frame=hSIdES],.test-case-insensitive-attributes[frame=HSIdES],.test-case-insensitive-attributes[frame=hsiDES],.test-case-insensitive-attributes[frame=HsiDES],.test-case-insensitive-attributes[frame=hSiDES],.test-case-insensitive-attributes[frame=HSiDES],.test-case-insensitive-attributes[frame=hsIDES],.test-case-insensitive-attributes[frame=HsIDES],.test-case-insensitive-attributes[frame=hSIDES],.test-case-insensitive-attributes[frame=HSIDES] { 85 | order: 10; 86 | } 87 | 88 | .test-rebeccapurple-color { 89 | color: #639; 90 | order: 11; 91 | } 92 | 93 | .test-hexadecimal-alpha-notation { 94 | background-color: rgba(243,243,243,0.95294); 95 | color: rgba(0,0,0,0.2); 96 | order: 12; 97 | } 98 | 99 | .test-color-functional-notation { 100 | color: rgba(178, 34, 34, 0.5); 101 | order: 13; 102 | } 103 | 104 | .test-lab-function { 105 | background-color: rgb(178, 34, 34); 106 | color: rgba(178, 34, 34, 0.5); 107 | order: 14; 108 | } 109 | 110 | .test-system-ui-font-family { 111 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; 112 | order: 15; 113 | } 114 | 115 | .test-font-variant-property { 116 | font-feature-settings: "smcp"; 117 | font-variant-caps: small-caps; 118 | order: 16; 119 | } 120 | 121 | .test-all-property { 122 | -webkit-animation: none 0s ease 0s 1 normal none running; 123 | animation: none 0s ease 0s 1 normal none running; 124 | -webkit-backface-visibility: visible; 125 | backface-visibility: visible; 126 | background: transparent none repeat 0 0 / auto auto padding-box border-box scroll; 127 | border: medium none currentColor; 128 | border-collapse: separate; 129 | -o-border-image: none; 130 | border-image: none; 131 | border-radius: 0; 132 | border-spacing: 0; 133 | bottom: auto; 134 | box-shadow: none; 135 | box-sizing: content-box; 136 | caption-side: top; 137 | clear: none; 138 | clip: auto; 139 | color: #000; 140 | -moz-columns: auto; 141 | columns: auto; 142 | -moz-column-count: auto; 143 | column-count: auto; 144 | -moz-column-fill: balance; 145 | column-fill: balance; 146 | -moz-column-gap: normal; 147 | column-gap: normal; 148 | -moz-column-rule: medium none currentColor; 149 | column-rule: medium none currentColor; 150 | -moz-column-span: 1; 151 | column-span: 1; 152 | -moz-column-width: auto; 153 | column-width: auto; 154 | content: normal; 155 | counter-increment: none; 156 | counter-reset: none; 157 | cursor: auto; 158 | direction: ltr; 159 | display: inline; 160 | empty-cells: show; 161 | float: none; 162 | font-family: serif; 163 | font-size: medium; 164 | font-style: normal; 165 | font-variant: normal; 166 | font-weight: normal; 167 | font-stretch: normal; 168 | line-height: normal; 169 | height: auto; 170 | -webkit-hyphens: none; 171 | -ms-hyphens: none; 172 | hyphens: none; 173 | left: auto; 174 | letter-spacing: normal; 175 | list-style: disc outside none; 176 | margin: 0; 177 | max-height: none; 178 | max-width: none; 179 | min-height: 0; 180 | min-width: 0; 181 | opacity: 1; 182 | orphans: 2; 183 | outline: medium none invert; 184 | overflow: visible; 185 | overflow-x: visible; 186 | overflow-y: visible; 187 | padding: 0; 188 | page-break-after: auto; 189 | page-break-before: auto; 190 | page-break-inside: auto; 191 | perspective: none; 192 | perspective-origin: 50% 50%; 193 | position: static; 194 | right: auto; 195 | -moz-tab-size: 8; 196 | -o-tab-size: 8; 197 | tab-size: 8; 198 | table-layout: auto; 199 | text-align: left; 200 | -moz-text-align-last: auto; 201 | text-align-last: auto; 202 | text-decoration: none; 203 | text-indent: 0; 204 | text-shadow: none; 205 | text-transform: none; 206 | top: auto; 207 | transform: none; 208 | transform-origin: 50% 50% 0; 209 | transform-style: flat; 210 | transition: none 0s ease 0s; 211 | unicode-bidi: normal; 212 | vertical-align: baseline; 213 | visibility: visible; 214 | white-space: normal; 215 | widows: 2; 216 | width: auto; 217 | word-spacing: normal; 218 | z-index: auto; 219 | all: initial; 220 | order: 17; 221 | } 222 | 223 | .test-matches-pseudo-class:matches(:first-child, .special) { 224 | order: 18; 225 | } 226 | 227 | .test-not-pseudo-class:not(:first-child):not(.special) { 228 | order: 19; 229 | } 230 | 231 | .test-any-link-pseudo-class:link,.test-any-link-pseudo-class:visited { 232 | order: 20; 233 | } 234 | 235 | .test-any-link-pseudo-class:-webkit-any-link { 236 | order: 20; 237 | } 238 | 239 | .test-any-link-pseudo-class:-moz-any-link { 240 | order: 20; 241 | } 242 | 243 | .test-any-link-pseudo-class:any-link { 244 | order: 20; 245 | } 246 | 247 | [dir="rtl"] .test-dir-pseudo-class { 248 | order: 21; 249 | } 250 | 251 | .test-overflow-wrap-property { 252 | order: 22; 253 | word-wrap: break-word; 254 | } 255 | 256 | .test-focus-visible-pseudo-class.focus-visible { 257 | order: 23; 258 | } 259 | 260 | .test-focus-visible-pseudo-class:focus-visible { 261 | order: 23; 262 | } 263 | 264 | .test-double-position-gradients { 265 | background-image: conic-gradient(yellowgreen 40%, gold 0deg, gold 75%, #f06 0deg); 266 | background-image: conic-gradient(yellowgreen 40%, gold 0deg 75%, #f06 0deg); 267 | } 268 | 269 | .test-blank-pseudo-class[blank] { 270 | background-color: yellow; 271 | } 272 | 273 | .test-blank-pseudo-class:blank { 274 | background-color: yellow; 275 | } 276 | 277 | .test-has-pseudo-class[\:has\(.inner-class\)] { 278 | background-color: yellow; 279 | } 280 | 281 | .test-has-pseudo-class:has(.inner-class) { 282 | background-color: yellow; 283 | } 284 | -------------------------------------------------------------------------------- /test/custom-properties.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --black: black; 3 | } 4 | 5 | test-css-variables { 6 | color: var(--black); 7 | } 8 | -------------------------------------------------------------------------------- /test/custom-properties.disabled.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --black: black; 3 | } 4 | 5 | test-css-variables { 6 | color: var(--black); 7 | } 8 | -------------------------------------------------------------------------------- /test/custom-properties.enabled.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --black: black; 3 | } 4 | 5 | test-css-variables { 6 | color: black; 7 | color: var(--black); 8 | } 9 | -------------------------------------------------------------------------------- /test/custom-properties.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --black: black; 3 | } 4 | 5 | test-css-variables { 6 | color: black; 7 | color: var(--black); 8 | } 9 | -------------------------------------------------------------------------------- /test/generated-custom-exports.css: -------------------------------------------------------------------------------- 1 | @custom-media --narrow-window (max-width: 30em); 2 | @custom-media --dark-mode (prefers-color-scheme: dark); 3 | 4 | @custom-selector :--heading h1, h2, h3, h4, h5, h6; 5 | 6 | :root { 7 | --order: 1; 8 | } 9 | -------------------------------------------------------------------------------- /test/generated-custom-exports.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | customMedia: { 3 | '--narrow-window': '(max-width: 30em)', 4 | '--dark-mode': '(prefers-color-scheme: dark)' 5 | }, 6 | customProperties: { 7 | '--order': '1' 8 | }, 9 | customSelectors: { 10 | ':--heading': 'h1, h2, h3, h4, h5, h6' 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /test/generated-custom-exports.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom-media": { 3 | "--narrow-window": "(max-width: 30em)", 4 | "--dark-mode": "(prefers-color-scheme: dark)" 5 | }, 6 | "custom-properties": { 7 | "--order": "1" 8 | }, 9 | "custom-selectors": { 10 | ":--heading": "h1, h2, h3, h4, h5, h6" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/generated-custom-exports.mjs: -------------------------------------------------------------------------------- 1 | export const customMedia = { 2 | '--narrow-window': '(max-width: 30em)', 3 | '--dark-mode': '(prefers-color-scheme: dark)' 4 | }; 5 | 6 | export const customProperties = { 7 | '--order': '1' 8 | }; 9 | 10 | export const customSelectors = { 11 | ':--heading': 'h1, h2, h3, h4, h5, h6' 12 | }; 13 | -------------------------------------------------------------------------------- /test/import.css: -------------------------------------------------------------------------------- 1 | .test-custom-properties { 2 | order: var(--order); 3 | } 4 | 5 | @media (--narrow-window) { 6 | .test-custom-media-queries { 7 | order: 2; 8 | } 9 | } 10 | 11 | .test-custom-selectors { 12 | &:--heading { 13 | order: 3; 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /test/import.expect.css: -------------------------------------------------------------------------------- 1 | .test-custom-properties { 2 | order: 1; 3 | order: var(--order); 4 | } 5 | 6 | @media (max-width: 40rem) { 7 | .test-custom-media-queries { 8 | order: 2; 9 | } 10 | } 11 | 12 | .test-custom-selectors:is(h1),.test-custom-selectors:is(h2),.test-custom-selectors:is(h3),.test-custom-selectors:is(h4),.test-custom-selectors:is(h5),.test-custom-selectors:is(h6) { 13 | order: 3; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /test/insert.after.expect.css: -------------------------------------------------------------------------------- 1 | $font: system-ui; 2 | 3 | .test-variable { 4 | font-family: $font; 5 | } 6 | -------------------------------------------------------------------------------- /test/insert.before.expect.css: -------------------------------------------------------------------------------- 1 | .test-variable { 2 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /test/insert.css: -------------------------------------------------------------------------------- 1 | $font: system-ui; 2 | 3 | .test-variable { 4 | font-family: $font; 5 | } 6 | --------------------------------------------------------------------------------