Repository: postcss/autoprefixer Branch: main Commit: 360f2d9ecbad Files: 268 Total size: 485.9 KB Directory structure: gitextract_70q035xe/ ├── .editorconfig ├── .github/ │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ └── workflows/ │ ├── release.yml │ └── test.yml ├── .gitignore ├── .npmignore ├── AUTHORS ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin/ │ └── autoprefixer ├── data/ │ └── prefixes.js ├── eslint.config.mjs ├── lib/ │ ├── at-rule.js │ ├── autoprefixer.d.ts │ ├── autoprefixer.js │ ├── brackets.js │ ├── browsers.js │ ├── declaration.js │ ├── hacks/ │ │ ├── align-content.js │ │ ├── align-items.js │ │ ├── align-self.js │ │ ├── animation.js │ │ ├── appearance.js │ │ ├── autofill.js │ │ ├── backdrop-filter.js │ │ ├── background-clip.js │ │ ├── background-size.js │ │ ├── block-logical.js │ │ ├── border-image.js │ │ ├── border-radius.js │ │ ├── break-props.js │ │ ├── cross-fade.js │ │ ├── display-flex.js │ │ ├── display-grid.js │ │ ├── file-selector-button.js │ │ ├── filter-value.js │ │ ├── filter.js │ │ ├── flex-basis.js │ │ ├── flex-direction.js │ │ ├── flex-flow.js │ │ ├── flex-grow.js │ │ ├── flex-shrink.js │ │ ├── flex-spec.js │ │ ├── flex-wrap.js │ │ ├── flex.js │ │ ├── fullscreen.js │ │ ├── gradient.js │ │ ├── grid-area.js │ │ ├── grid-column-align.js │ │ ├── grid-end.js │ │ ├── grid-row-align.js │ │ ├── grid-row-column.js │ │ ├── grid-rows-columns.js │ │ ├── grid-start.js │ │ ├── grid-template-areas.js │ │ ├── grid-template.js │ │ ├── grid-utils.js │ │ ├── image-rendering.js │ │ ├── image-set.js │ │ ├── inline-logical.js │ │ ├── intrinsic.js │ │ ├── justify-content.js │ │ ├── mask-border.js │ │ ├── mask-composite.js │ │ ├── order.js │ │ ├── overscroll-behavior.js │ │ ├── pixelated.js │ │ ├── place-self.js │ │ ├── placeholder-shown.js │ │ ├── placeholder.js │ │ ├── print-color-adjust.js │ │ ├── text-decoration-skip-ink.js │ │ ├── text-decoration.js │ │ ├── text-emphasis-position.js │ │ ├── transform-decl.js │ │ ├── user-select.js │ │ └── writing-mode.js │ ├── info.js │ ├── old-selector.js │ ├── old-value.js │ ├── prefixer.js │ ├── prefixes.js │ ├── processor.js │ ├── resolution.js │ ├── selector.js │ ├── supports.js │ ├── transition.js │ ├── utils.js │ ├── value.js │ └── vendor.js ├── package.json ├── patches/ │ └── yargs@17.7.2.patch └── test/ ├── at-rule.test.js ├── autoprefixer.test.js ├── brackets.test.js ├── browsers.test.js ├── cases/ │ ├── 3d-transform.css │ ├── 3d-transform.out.css │ ├── advanced-filter.css │ ├── advanced-filter.out.css │ ├── animation.css │ ├── animation.out.css │ ├── appearance.css │ ├── appearance.out.css │ ├── at-rules.css │ ├── at-rules.out.css │ ├── autofill.css │ ├── autofill.out.css │ ├── backdrop-filter.css │ ├── backdrop-filter.out.css │ ├── backdrop.css │ ├── backdrop.out.css │ ├── background-clip.css │ ├── background-clip.out.css │ ├── background-size.css │ ├── background-size.out.css │ ├── border-image.css │ ├── border-image.out.css │ ├── border-radius.css │ ├── border-radius.out.css │ ├── cascade.css │ ├── cascade.out.css │ ├── check-down.css │ ├── check-down.out.css │ ├── comments.css │ ├── comments.out.css │ ├── config/ │ │ ├── browserslist │ │ ├── test.css │ │ ├── test.out.css │ │ └── test.production.css │ ├── content.css │ ├── cross-fade.css │ ├── cross-fade.out.css │ ├── custom-prefix.css │ ├── custom-prefix.out.css │ ├── disabled.css │ ├── disabled.out.css │ ├── double.css │ ├── double.out.css │ ├── element.css │ ├── element.out.css │ ├── example.css │ ├── example.out.css │ ├── file-selector-button.css │ ├── file-selector-button.out.css │ ├── filter.css │ ├── filter.out.css │ ├── flex-rewrite.css │ ├── flex-rewrite.out.css │ ├── flexbox.css │ ├── flexbox.out.css │ ├── fullscreen.css │ ├── fullscreen.out.css │ ├── gradient-fix.css │ ├── gradient-fix.out.css │ ├── gradient.css │ ├── gradient.out.css │ ├── grid-area-media-sequence.css │ ├── grid-area-media-sequence.out.css │ ├── grid-area.css │ ├── grid-area.out.css │ ├── grid-areas-duplicate-complex.css │ ├── grid-areas-duplicate-complex.out.css │ ├── grid-autoplacement.css │ ├── grid-autoplacement.out.css │ ├── grid-gap.css │ ├── grid-gap.out.css │ ├── grid-media-rules.css │ ├── grid-media-rules.out.css │ ├── grid-options.autoplace.out.css │ ├── grid-options.css │ ├── grid-options.disabled.out.css │ ├── grid-options.no-autoplace.out.css │ ├── grid-status.css │ ├── grid-status.out.css │ ├── grid-template-areas.css │ ├── grid-template-areas.out.css │ ├── grid-template.css │ ├── grid-template.out.css │ ├── grid.css │ ├── grid.disabled.css │ ├── grid.out.css │ ├── grouping-rule.css │ ├── grouping-rule.out.css │ ├── ignore-next.css │ ├── ignore-next.out.css │ ├── image-rendering.css │ ├── image-rendering.out.css │ ├── image-set.css │ ├── image-set.out.css │ ├── intrinsic.css │ ├── intrinsic.ff.css │ ├── intrinsic.out.css │ ├── keyframes.css │ ├── keyframes.out.css │ ├── logical.css │ ├── logical.out.css │ ├── mask-border.css │ ├── mask-border.out.css │ ├── mask-composite.css │ ├── mask-composite.out.css │ ├── mistakes.css │ ├── mistakes.out.css │ ├── multicolumn.css │ ├── multicolumn.out.css │ ├── notes.css │ ├── notes.out.css │ ├── overscroll-behavior.css │ ├── overscroll-behavior.out.css │ ├── pie.css │ ├── placeholder-shown.css │ ├── placeholder-shown.out.css │ ├── placeholder.css │ ├── placeholder.out.css │ ├── print-color-adjust.css │ ├── print-color-adjust.out.css │ ├── resolution.css │ ├── resolution.out.css │ ├── scope.css │ ├── scope.out.css │ ├── selectors.css │ ├── selectors.out.css │ ├── style.css │ ├── style.out.css │ ├── supports.css │ ├── supports.out.css │ ├── syntax.css │ ├── text-decoration.css │ ├── text-decoration.out.css │ ├── text-decoration.shorthand.out.css │ ├── text-emphasis-position.css │ ├── text-emphasis-position.out.css │ ├── transition-no-warning.css │ ├── transition-no-warning.out.css │ ├── transition-spec.css │ ├── transition-spec.out.css │ ├── transition.css │ ├── transition.out.css │ ├── trim.css │ ├── uncascade.css │ ├── uncascade.out.css │ ├── user-select.css │ ├── user-select.out.css │ ├── value-hack.css │ ├── value-hack.out.css │ ├── values.css │ ├── values.out.css │ ├── vendor-hack.css │ ├── vendor-hack.out.css │ ├── viewport.css │ ├── viewport.out.css │ ├── webkit-line-clamp.css │ ├── webkit-line-clamp.out.css │ ├── writing-mode.css │ └── writing-mode.out.css ├── declaration.test.js ├── info.test.js ├── old-selector.test.js ├── old-value.test.js ├── postcss.test.js ├── prefixer.test.js ├── prefixes.test.js ├── selector.test.js ├── supports.test.js ├── utils.test.js └── value.test.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true ================================================ FILE: .github/CONTRIBUTING.md ================================================ # Contributing to Autoprefixer * [Filing Issues](#filing-issues) * [Getting Started](#getting-started) * [Adding a Prefix](#adding-a-prefix) ## Filing Issues - If you have problems with the output CSS from Autoprefixer; you’ll need to post: 1. Input CSS. 2. Output CSS. 3. Expected output CSS. 4. Browserslist config. If you use `browsers` or option, create Browserslist config first. 5. Run `npm ls | grep autoprefixer` and check output. - If Autoprefixer throws error: 1. Input CSS. 2. Browserslist config. 3. Error stacktrace (copy and paste the error message that you get so we can identify where the problem is). 4. Run `npm ls | grep autoprefixer` and check output. 5. Run `npm ls | grep postcss` and check output. ## Getting Started Before you begin contributing make sure you have a [GitHub account]. * [Fork the repository](https://github.com/postcss/autoprefixer) * Clone a copy of it to your computer: `git clone https://github.com/USERNAME/autoprefixer` (replace `USERNAME` to your GitHub name). * Ensure that you have the [pnpm](https://pnpm.io/) package manager installed. * Run `pnpm install` this will install all dependencies needed to run tests. [GitHub account]: https://github.com/signup/free ## Adding a Prefix We’ll explain how would you go about adding a CSS feature to Autoprefixer. For example, we’ll add support for a CSS feature called `background-clip: text`. Note: Remember that the feature that you want to add must also be supported on [Can I use](https://caniuse.com/). 1. Create a topic branch from the `master` branch. Like this: `git checkout -b background-clip-text`. 2. To add support for a CSS feature you must call the convert function inside of `data/prefixes.js` and specify three parameters: data, options and a callback. Like this: ```js f(require('caniuse-lite/data/features/background-clip-text'), browsers => prefix(['background-clip'], { feature: 'background-clip-text', browsers }) ) ``` 3. If the prefix is simple (`-webkit-` for Safari, `-moz-` for Firefox, all use the same syntax) go to step 8. 4. If you need some non-standard behavior for the prefix (`-webkit-` prefix for Firefox or different syntax for different browsers) you will need to create a “hack”. Create a JS file in the `libs/hacks` folder using the name of the CSS feature as the filename. 5. Check out other hacks for examples. In this new class change the prefix for `IE` to `-webkit-`. [See complete example](https://github.com/postcss/Autoprefixer/blob/73c7b6ab090a9a9a03869b3099096af00be7eb7d/lib/hacks/background-clip.js) 6. Load the new hack in `lib/prefixes.js`. Load it into `Declaration` if you need a prefix for property name. Load it into `Value` if you need a prefix for value. ```js Declaration.hack(require('./hacks/background-clip')) ``` 7. Create a `.css` and `.out.css` example in `test/cases`. Add this test to `test/autoprefixer.test.js`: ```js it('supports background-clip', () => check('background-clip')) ``` If you need different browsers, change `prefixer()` function in the top of test file. 8. Run `pnpm test`. 9. Push the branch to GitHub. 10. Open your fork on GitHub an send pull request. ================================================ FILE: .github/FUNDING.yml ================================================ open_collective: postcss tidelift: npm/autoprefixer github: ai ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: tags: - '*' permissions: contents: write jobs: release: name: Release On Tag if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - name: Checkout the repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Extract the changelog id: changelog run: | TAG_NAME=${GITHUB_REF/refs\/tags\//} READ_SECTION=false CHANGELOG="" while IFS= read -r line; do if [[ "$line" =~ ^#+\ +(.*) ]]; then if [[ "${BASH_REMATCH[1]}" == "$TAG_NAME" ]]; then READ_SECTION=true elif [[ "$READ_SECTION" == true ]]; then break fi elif [[ "$READ_SECTION" == true ]]; then CHANGELOG+="$line"$'\n' fi done < "CHANGELOG.md" CHANGELOG=$(echo "$CHANGELOG" | awk '/./ {$1=$1;print}') echo "changelog_content<> $GITHUB_OUTPUT echo "$CHANGELOG" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - name: Create the release if: steps.changelog.outputs.changelog_content != '' uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 with: name: ${{ github.ref_name }} body: '${{ steps.changelog.outputs.changelog_content }}' draft: false prerelease: false ================================================ FILE: .github/workflows/test.yml ================================================ name: Test on: push: branches: - main pull_request: permissions: contents: read jobs: full: name: Node.js Latest Full runs-on: ubuntu-latest steps: - name: Checkout the repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 with: version: 10 - name: Install Node.js uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 25 cache: pnpm - name: Install dependencies run: pnpm install --frozen-lockfile --ignore-scripts - name: Run tests run: pnpm test short: runs-on: ubuntu-latest strategy: matrix: node-version: - 24 - 22 - 20 - 18 name: Node.js ${{ matrix.node-version }} Quick steps: - name: Checkout the repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 with: version: 10 - name: Install Node.js ${{ matrix.node-version }} uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} cache: pnpm - name: Install dependencies run: pnpm install --frozen-lockfile --ignore-scripts - name: Run unit tests run: pnpm unit old: runs-on: ubuntu-latest strategy: matrix: node-version: - 16 - 14 - 12 - 10 name: Node.js ${{ matrix.node-version }} Quick steps: - name: Checkout the repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 with: version: 3 env: ACTIONS_ALLOW_UNSECURE_COMMANDS: true - name: Install Node.js ${{ matrix.node-version }} uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} - name: Install dependencies run: pnpm install --no-frozen-lockfile --ignore-scripts - name: Run unit tests run: for f in test/*.test.js; do node "$f"; done ================================================ FILE: .gitignore ================================================ node_modules/ coverage/ ================================================ FILE: .npmignore ================================================ test/ coverage/ logo.svg AUTHORS eslint.config.mjs patches ================================================ FILE: AUTHORS ================================================ # This is the official list of Autoprefixer authors for copyright purposes. # # This does not necessarily list everyone who has contributed code, since in # some cases, their employer may be the copyright holder. To see the full list # of contributors, see the revision history in source control or # https://github.com/postcss/autoprefixer/graphs/contributors # # Authors who wish to be recognized in this file should add themselves (or # their employer, as appropriate). Aaron Adam Lynch Adonis K Adriaan Albert Juhé Lluveras Aleksei Androsov Aleksey Shvayka Aleks Hudochenkov Alexey Komarov Alexey Plutalov Anders Olsen Sandvik Andreas Haller Andreas Lind Andrew Rhoads Andrey Deryabin Andrey Sitnik Andrey Taritsyn Andy Trevorah Antoine du Hamel Anton Khlynovskiy Artem Yavorsky aruseni Ben Briggs bernig Bogdan Chadkin Bogdan Dolin brainopia Brandon Mathis Chad von Nau Chi Vinh Le Christian Oliff Cory House Cory Simmons Craig Martin Damon Daniel Garcia-Carrillo Daniel Tonon Daniel Tschinder Danny Pule Darius David Narbutovich David Pike Denys Kniazevych Denis Sokolov Diablohu Dominik Porada Dominik Schilling dotch Dmitry Semigradsky Eduard Kyvenko Efremov Alexey eitanr Erik Sundahl Eugene Datsky Even Stensberg Evgeny Petukhov Evilebot Tnawi Fangzhou Li Forrest York Gibr Google Inc. Gregory Eremin GU Yiling Gustavo Real Hallvord R. M. Steen heady HongShaoRou Huáng Jùnliàng Iain Beeston Igor Adamenko Ivan Ivan Malov Ivan Verevkin Filipe W. Lima Jack Moore Jason Kuhrt Jeff Escalante Joan León Johannes J. Schmidt John Kreitlow Jonathan Ong Josh Gillies Joshua Hall Juan Martin Marco Junliang Huang jvdanilo Kevin Pfefferle Kieran Kir Shatrov Kiwi kizu Leonya Khachaturov Lovchikov Anton L.T Lucas Caton Luciano Battagliero Luke Page martco Mat Gadd Matt Buresh Matt Smith Maxime Thirouin Max Mechanic Michael Beil Michael Prentice Michael Scott Hertzberg Michał Gołębiowski-Owczarek Mikael Jorhult Morton Fox mvasilkov Nick Howes Nick Marchenko Nic Nilov Nick Schonning nickspielgist Niels Dequeker Nikolay Burlov Oleh Aloshkin Oleksandr Sergiienko Pavel Pustovalov Pavel Vostrikov Paul Statezny Peter van Westen Peter Zotov Phani Kandula Phil Dokas Rafael Silva Ray Lehnhoff ReadmeCritic Reinaldo Schiehll René Stalder Richard Wang Rob Howell Roland Warmerdam Roman Dvornov Roy Revelt Ryan Zimmerman Sami Suo-Heikki Sasha Koss Sean Anderson Semen Levenson Sergey Belov Sergey Leschina Sergey Lysenko sethjgore Šime Vidas Simon Lydell Sindre Sorhus Sire Stanislav Stanislav Lashmanov Stephen Edgar Steve Mao Stig Otnes Kolstad Subash Pathak sunhao Sven Wagner Timothy tomdavenport Tony Ganch Vegard Andreas Larsen Vera Surkova Vincent De Oliveira Vishnu Ravi Vladimir Pouzanov vladkens Yet Another Minion Yuriy Alekseyev Yury Timofeev Даниил Пронин 一丝 刘祺 忆初 ================================================ FILE: CHANGELOG.md ================================================ # Change Log This project adheres to [Semantic Versioning](http://semver.org/). ## 10.4.27 * Removed development key from `package.json`. ## 10.4.26 * Reduced package size. ## 10.4.25 * Fixed broken gradients on CSS Custom Properties (by @serger777). ## 10.4.24 * Made Autoprefixer a little faster (by @Cherry). ## 10.4.23 * Reduced dependencies (by @hyperz111). ## 10.4.22 * Fixed `stretch` prefixes on new Can I Use database. * Updated `fraction.js`. ## 10.4.21 * Fixed old `-moz-` prefix for `:placeholder-shown` (by @Marukome0743). ## 10.4.20 * Fixed `fit-content` prefix for Firefox. ## 10.4.19 * Removed `end value has mixed support, consider using flex-end` warning since `end`/`start` now have good support. ## 10.4.18 * Fixed removing `-webkit-box-orient` on `-webkit-line-clamp` (@Goodwine). ## 10.4.17 * Fixed `user-select: contain` prefixes. ## 10.4.16 * Improved performance (by Romain Menke). * Fixed docs (by Christian Oliff). ## 10.4.15 * Fixed `::backdrop` prefixes (by 一丝). * Fixed docs (by Christian Oliff). ## 10.4.14 * Improved startup time and reduced JS bundle size (by Kārlis Gaņģis). ## 10.4.13 * Fixed missed prefixes on vendor prefixes in name of CSS Custom Property. ## 10.4.12 * Fixed support of unit-less zero angle in backgrounds (by 一丝). ## 10.4.11 * Fixed `text-decoration` prefixes by moving to MDN data (by Romain Menke). ## 10.4.10 * Fixed `unicode-bidi` prefixes by moving to MDN data. ## 10.4.9 * Fixed `css-unicode-bidi` issue from latest Can I Use. ## 10.4.8 * Do not print `color-adjust` warning if `print-color-adjust` also is in rule. ## 10.4.7 * Fixed `print-color-adjust` support in Firefox. ## 10.4.6 * Fixed `print-color-adjust` support. ## 10.4.5 * Fixed `NaN` in grid (by @SukkaW). ## 10.4.4 * Fixed `package.funding` to have same value between all PostCSS packages. ## 10.4.3 * Fixed `package.funding` (by Álvaro Mondéjar). ## 10.4.2 * Fixed missed `-webkit-` prefix for `width: stretch`. ## 10.4.1 * Fixed `::file-selector-button` data (by Luke Warlow). ## 10.4 “ǃke e꞉ ǀxarra ǁke” * Added `:autofill` support (by Luke Warlow). ## 10.3.7 * Replaced `nanocolors` to `picocolors`. ## 10.3.6 * Updated `nanocolors`. * Reduced package size. ## 10.3.5 * Replaced `colorette` to `nanocolors`. ## 10.3.4 * Fixed `stretch` value in latest Firefox. ## 10.3.3 * Fixed wrong `-moz-` prefix from `::file-selector-button` (by Usman Yunusov). ## 10.3.2 * Fixed `::file-selector-button` support (by Usman Yunusov). ## 10.3.1 * Fixed adding wrong prefixes to `content` (by Luke Warlow). ## 10.3 “Чести своей не отдам никому” * Added `::file-selector-button` support (by Luke Warlow). ## 10.2.6 * Fixed “no prefixes needed” warning (by @Pwntheon). ## 10.2.5 * Fixed `:` support in `@supports` (by Dmitry Semigradsky). * Fixed docs (by Christian Oliff). ## 10.2.4 * Fixed browser names in `npx autoprefixer --info`. ## 10.2.3 * Fixed PostCSS 8 support. ## 10.2.2 * Fixed PostCSS 8 plugins compatibility. ## 10.2.1 * Fixed `transition-property` warnings (by @Sheraff). ## 10.2 “Sub rosa” * Added TypeScript definitions (by Dmitry Semigradsky). * Fixed docs (by Florian Pellet). ## 10.1 “Pula” * Added `dpcm` unit support to `min-resolution: 2dppx` (by Robert Eisele). * Fixed rational approximation in `min-resolution` (by Robert Eisele). ## 10.0.4 * Fixed `Cannot read property 'proxyOf' of undefined` error (by Igor Kamyshev). ## 10.0.3 * Fixed `substract` to `subtract` value for `mask-composite` (by Michelle Enos). ## 10.0.2 * Removed `-ms-user-select: all` because IE and old Edge don’t support it. * Fixed Grid Layout warning. ## 10.0.1 * Fix PostCSS 8.1 compatability. * Add our OpenCollective to `package.json`. * Clean up code (by Sukka). ## 10.0 “Alis volat propriis” * Removed support for Node.js 6.x, 8.x, 11.x. * Moved `postcss` to `peerDependencies`. * Moved to PostCSS 8. ## 9.8.6 * Fixed `env` option. ## 9.8.5 * Improved Grid Layout warnings (by Daniel Tonon). * Fixed `align-self` and `justify-self` with `display: flex` (by Daniel Tonon). ## 9.8.4 * Replace color output library. ## 9.8.3 * Return old non-LTS Node.js versions to avoid breaking changes. ## 9.8.2 * Remove Node.js 13.0-13.7 from supported engines, because of buggy ESM support. ## 9.8.1 * Replace `chalk` to `kleur` (by Luke Edwards). * Update docs (by @mbomb007). ## 9.8 “Vigilo Confido” * Add `:placeholder-shown` support (by Tanguy Krotoff). ## 9.7.6 * Revert `-webkit-stretch` fix. ## 9.7.5 * Fix `-webkit-stretch` support. ## 9.7.4 * Fix warning text (by Dmitry Ishkov). ## 9.7.3 * Fix compatibility with PostCSS Modules. ## 9.7.2 * Add `-ms-user-select: element` support. * Add funding link for `npm fund`. ## 9.7.1 * Avoid unnecessary transitions in prefixed selectors (by Andrey Alexandrov). * Fix `fit-content` for Firefox. ## 9.7 “Ad Victoriam” * Add `AUTOPREFIXER_GRID` env variable to enable Grid Layout polyfill for IE. * Fix `Cannot read property 'grid' of undefined` error. ## 9.6.5 * Fix selector prefixing (by Andrey Alexandrov). ## 9.6.4 * Now the real fix for `'startsWith' of undefined` error. ## 9.6.3 * Fix `Cannot read property 'startsWith' of undefined` error. ## 9.6.2 * Fix false `Replace fill to stretch` warning. ## 9.6.1 * Fix `-webkit-line-clamp` truncating multi-line text support. ## 9.6 “Nunc id vides, nunc ne vides” * Show warning about Browserslist config on `browser` option. * Add warning-less `overrideBrowserslist` option. * Add `text-orientation` support. * Add `min-resolution: 2x` alias support. * Add `.github/CONTRIBUTING.md` (by Juan Martin Marco). ## 9.5.1 * Fix `backdrop-filter` for Edge (by Oleh Aloshkin). * Fix `min-resolution` media query support in Firefox < 16. ## 9.5 “Draco dormiens nunquam titillandus” * Add `mask-composite` support (by Semen Levenson). ## 9.4.10 * Add warning for named Grid rows. ## 9.4.9 * Fix `grid-template` and `@media` case (by Bogdan Dolin). ## 9.4.8 * Fix `calc()` support in Grid gap. ## 9.4.7 * Fix infinite loop on mismatched parens. ## 9.4.6 * Fix warning text (by Albert Juhé Lluveras). ## 9.4.5 * Fix `text-decoration-skip-ink` support. ## 9.4.4 * Use `direction` value for `-ms-writing-mode` (by Denys Kniazevych). * Fix warning text (by @zzzzBov). ## 9.4.3 * Add warning to force `flex-start` instead of `start` (by Antoine du Hamel). * Fix docs (by Christian Oliff). ## 9.4.2 * Fix Grid autoplacement warning. ## 9.4.1 * Fix unnecessary Flexbox prefixes in Grid elements. ## 9.4 “Advance Australia” * Add Grid autoplacement for `-ms-` (by Bogdan Dolin). * Improve docs and warnings (by Daniel Tonon). * Remove some unnecessary warnings for Grid (by Andrey Alexandrov). ## 9.3.1 * Fix Grid prefixes with `repeat()` value (by Bogdan Dolin). ## 9.3 “Labor omnia vincit” * Add `place-self` support (by Bogdan Dolin). * Fix Grid row/column span inheritance bug (by Bogdan Dolin). ## 9.2.1 * Fix broken AST. ## 9.2 “Onyi est glavnaya krepost” * Add `/* autoprefixer grid: on */` control comment (by Andrey Alexandrov). * Add duplicate `grid-area` support (by Bogdan Dolin). * Fix `grid-gap` support for rules with different specifity (by Bogdan Dolin). * Disable Grid in `@supports` at-rule with non-supported Grid features. * Improve Grid warnings (by Daniel Tonon). * Improve docs (by Joshua Hall, Mat Gadd, Roy Revelt, and Ivan). ## 9.1.5 * Remove `@babel/register` from dependencies. ## 9.1.4 * Use Babel 7. ## 9.1.3 * Sort properties in `autoprefixer --info` alphabetically. * Fix old Firefox gradient prefix. ## 9.1.2 * Fix `autoprefixer --info` in new Node.js. ## 9.1.1 * Retain `grid-gap` through `@media` (by Bogdan Dolin). * Fix `grid-template` and `@media` (by Bogdan Dolin). * Fix Grid areas searching error (by Bogdan Dolin). * Fix `span X` Grid prefix (by Bogdan Dolin). * Fix docs (by Eduard Kyvenko). ## 9.1 “Equality before the law” * Add `background-clip: text` support. * Fix adding Grid span for IE (by Bogdan Dolin). ## 9.0.2 * Show warning on Grid area names conflict (by Bogdan Dolin). * Fix documentation (by Sven Wagner). ## 9.0.1 * Fix nested at-rules in Grid prefixes (by Ivan Malov). ## 9.0 “A Mari Usque Ad Mare” * Remove Node.js 9 and Node.js 4 support. * Remove IE and “dead” browsers from Babel. * Use PostCSS 7.0. * Use Browserslist 4.0. ## 8.6.5 * Do not show Grid warnings if IE was not selected. ## 8.6.4 * Fix `stretch` prefix in Chrome >= 46. ## 8.6.3 * Add warnings for unsupported Grid features. * Add warnings about wrong Grid properties. * Add note about `grid` option for grid properties in `autoprefixer --info`. ## 8.6.2 * Fix error during adding Grid prefixes in `@media` (by Evgeny Petukhov). ## 8.6.1 * Fix `grid-template` with media queries (by Evgeny Petukhov). ## 8.6 “Follow Reason” * Add `gap` support (by Evgeny Petukhov). * Add two values support for `grid-gap` and `gap` (by Evgeny Petukhov). * Add `ignoreUnknownVersions` option for Browserslist. ## 8.5.2 * Fix `grid-template` support wit auto row sizes (by Yury Timofeev). ## 8.5.1 * Remove unnecessary warning on `-webkit-fill-available`. ## 8.5 “Muito Nobre e Sempre Leal” * Add `grid-gap` support (by Evgeny Petukhov). * Fix radial gradients direction fix. * Fix docs (by Phani Kandula and Huáng Jùnliàng). ## 8.4.1 * Fix working in old PostCSS versions (by Diablohu). ## 8.4 “Non in aves, sed in angues” * Add `/* autoprefixer: ignore next */` control comment (by Pavel Vostrikov). ## 8.3 “Benigno Numine” * Add `@media` support to `grid-template` (by Evgeny Petukhov). * Fix `radial-gradient` direction warning (by Gustavo Real). ## 8.2 “Ad Astra per Aspera” * Add `color-adjust` (by Sergey Lysenko, Stanislav Botev, and Yuriy Alekseyev). ## 8.1 “Rex, Familia et Ultio” * Add `overscroll-behavior` support. * Add `grid-template` shortcut support (by Evgeny Petukhov). * Add better `grid-column-end` and `grid-row-end` support (by Evgeny Petukhov). * Fix Grid properties support in `@supports`. ## 8.0 “Excelsior” * Use Browserslist 3.0. * Rename `autoprefixer-info` CLI tool to `autoprefixer --info`. * Remove `break-*` to `page-break-*` conversion for Firefox. ## 7.2.6 * Fix `-ms-` prefix for grid cells with same `grid-area` (by Evgeny Petukhov). ## 7.2.5 * Fix multiple prefixes in declaration value. ## 7.2.4 * Fix IE 10 support. ## 7.2.3 * Fix `grid-template-areas` in `@media` (by Evgeny Petukhov). ## 7.2.2 * Fix `_autoprefixerDisabled is undefined` issue. ## 7.2.1 * Fix IE and other old JS runtimes support. ## 7.2 “Ordem e Progresso” * Add `grid-template-areas` support (by Evgeny Petukhov). * Add `grid-template` support (by Evgeny Petukhov). * Add `grid-area` support (by Alexey Komarov). * Add `autoprefixer-info` CLI tool. * Add wrong `radial-gradient` properties warning. * Use current working dir on missed `from` in `info()` (by Phil Dokas). * Fix `grid-row` and `grid-column` support (by Alexey Komarov). * Do not prefix `reverse` animation direction. * Improve test coverage (by Dmitry Semigradsky). ## 7.1.6 * Add warning for using `browserslist` option instead of `browsers`. * Add warning for multiple control comments in the same scope. * Fix `Invalid array length` error during indent changes. ## 7.1.5 * Fix `::placeholder` prefix for Edge. * Fix `inherit`/`initial`/`unset` values for `flex-direction`. * Fix RegExp usage in gradients (by Yet Another Minion). ## 7.1.4 * Fix `radial-gradient` direction conversion. * Fix `image-set` in `cursor`. ## 7.1.3 * Add warning for old `radial-gradient` direction syntax. ## 7.1.2 * Fix `text-decoration` shortcut support. ## 7.1.1 * Remove non-`-webkit-` intrinsic prefixes in Grid Layout (by 一丝). ## 7.1 “Universitas litterarum” * Add `unicode-bidi` support. * Add `-webkit-appearance` support for Edge. * Add `from` option to `info()`. * Fix intrinsic widths prefixes in Grid Layout. ## 7.0.1 * Fix Autoprefixer for old JS runtimes. ## 7.0 “Coelestem adspicit lucem” * Remove node.js 0.12 support. * Use PostCSS 6.0. * Use Browserslist 2. * Use `caniuse-lite` instead of `caniuse-db` (by Ben Briggs). * Use `^` for Browserslist dependencies, instead of `~`. * Rewrite project from CoffeeScript to Babel (by Dmitry Semigradsky). * Disable Grid Layout prefixes for IE by default. * Fix `-ms-grid-column-align`. * Move tests to Jest. ## 6.7.7 * Fix `order` for non-digit values. ## 6.7.6 * Fix `font-kerning` (by Chi Vinh Le). ## 6.7.5 * Fix `text-decoration-skip` in iOS (by Chi Vinh Le). * Fix `clip-path` (by Chi Vinh Le). ## 6.7.4 * Improve `browsers` option perfomance. * Update CoffeeScript compiler. ## 6.7.3 * Fix compatibility with “Intrinsic & Extrinsic Sizing” spec update. ## 6.7.2 * Do not prefix grid/flexbox in `@supports` on `grid: false`/`flexbox: false`. ## 6.7.1 * Update Browserslist with `last n version` fix. ## 6.7 “Krungthep doot thep saang” * Add Electron support in browsers list (by Kilian Valkhof). * Add `flex-flow` partial support for Flexbox 2009 specification. * Fix browsers `0` version issue in some Can I Use data. ## 6.6.1 * Add metadata to use Autoprefixer in JSS tests (by Chi Vinh Le). ## 6.6 “Kaiyuan” * Add `browserslist` key in `package.json` support. * Add support for separated environments in browserslist config. * Add `browserslist-stats.json` file support to load custom usage statistics. ## 6.5.4 * Fix unitless 0 basis in IE10/IE11 shorthand flex (by Google). ## 6.5.3 * Add error for popular mistake with `browser` option instead of `browsers`. ## 6.5.2 * Clean prefixes data (by Reinaldo Schiehll). ## 6.5.1 * Fix selectors with `:--` prefix support. ## 6.5 “Einigkeit und Recht und Freiheit” * Add `defaults` keyword to browsers requirements. * Fix CSS Grid Layout support. * Fix `align-self` cleaning. ## 6.4.1 * Fix node cloning after some PostCSS plugins. ## 6.4 “Hic et ubique terrarum” * Add `:any-link` selector support. * Add `text-decoration-skip` support. * Add `transition: duration property` support. * Fix `-webkit-` prefix for `backface-visibility`. * Fix `rad` unit support in gradients (by 刘祺). * Fix `transition` support in Opera 12. * Removed Safari TP Grid prefixes support. ## 6.3.7 * Fix rare `Cannot read property 'constructor' of null` issue. ## 6.3.6 * Add Safari TP prefix support for Grid Layout. ## 6.3.5 * Fix duplicate prefixes for `-ms-interpolation-mode`. ## 6.3.4 * Show users coverage for selected browsers in `info()`. ## 6.3.3 * Fix transition warning. ## 6.3.2 * Fix jspm support (by Sean Anderson). ## 6.3.1 * Fix compatibility with Flexibility polyfill. ## 6.3 “Pro rege et lege” * Add Grid Layout support. * Add `text-spacing` support. * Add `> 10% in my stats` browsers query with custom usage statistics. * Add options to disable `@supports`, Flexbox or Grid support. * Fix compatibility with other PostCSS plugins. ## 6.2.3 * Fix error on broken transition with double comma. ## 6.2.2 * Fix issues in broken transitions. ## 6.2.1 * Fix AST error in transition warning (by @jvdanilo). ## 6.2 “Fluctuat nec mergitur” * Use `fill` instead of `fill-available` according spec changes (by 一丝). * Add `fill` support for logical dimension properties (by 一丝). * Add `text-emphasis` support (by 一丝). * Add prefixes to `@supports` only for compatible browsers. * Add `rad`, `grad` and `turn` units support to linear gradients. * Add some `deg` directions support for old WebKit linear gradients. * Fix `@supports` parenthesis (by @heady). * Add warning when prefixes could not be generated for complicated `transition-property` values. * Add warning for outdated `fill-available` value. * Add warning for wrong `text-emphasis-position` value. * Add “time capsule” warning for prefix-less future. * Normalizes all warning messages. ## 6.1.2 * Fix gradient hack on some parameters (by Alexey Efremov). ## 6.1.1 * Fix `cursor: grab` and `cursor: grabbing` support. ## 6.1 “Bil-shaʿb wa lil-shaʿb” * Change `transition` support to output more robust CSS. * Add `:read-only` support. * Add support for `appearance` with any values. * Add CSS-in-JS support via `postcss-js`. * Add loud `/*! autoprefixer: off */` control comments support. * Convert `rotateZ` to `rotate` for `-ms-transform`. * Use `postcss-value-parser` to carefully work with gradients. * Remove `-ms-transform-style` and `-o-transform-style` that never existed. ## 6.0.3 * Fix old gradient direction warning. ## 6.0.2 * Remove unnecessary `-khtml-` prefix too. ## 6.0.1 * Fix `cross-fade()` support (by 一丝). ## 6.0 “Eureka” * CLI was removed from `autoprefixer` package to `autoprefixer-cli`. * `autoprefixer-core` and `autoprefixer` packages was merged back. * Remove `autoprefixer(opt).process(css)`, use `autoprefixer.process(css, opt)`. * Remove `safe` option. Use separated Safe parser from PostCSS. * Remove Opera 12.1 from default query. * Use PostCSS 5.0 API. * Add custom syntaxes support. * Add `image-set` support (by 一丝). * Add `mask-border` support (by 一丝). * Add `filter()` function support (by Vincent De Oliveira). * Add `backdrop-filter` support (by Vincent De Oliveira). * Add `element()` support (by Vincent De Oliveira). * Add CSS Regions support. * Add Scroll Snap Points support. * Add `writing-mode` support. * Add `::backdrop` support. * Add `cross-fade()` support. * Add other `break-` properties support. * Add Microsoft Edge support (by Andrey Polischuk). * Add `not` keyword and exclude browsers by query. * Add version ranges `IE 6-9` (by Ben Briggs). * Fix `filter` in `transition` support on Safari. * Fix `url()` parsing. * Fix `pixelated` cleaning. * Always show old gradient direction warning. ## 5.2.1 * Fix parent-less node issue on some cases (by Josh Gillies). ## 5.2 “Dont tread on me” * Add `appearance` support. * Warn users on old gradient direction or flexbox syntax. * Add `add: false` option to disable new prefixes adding. * Make Autoprefixer 30% faster. * Use PostCSS 4.1 plugin API. * Add prefixes for `pixelated` instead of `crisp-edges` in `image-rendering`. * Do not add `::placeholder` prefixes for `:placeholder-shown`. * Fix `text-decoration` prefixes. * `autoprefixer.process()` was deprecated. Use PostCSS API. ## 5.1.11 * Update `num2fraction` to fix resolution media query (by 一丝). ## 5.1.10 * Do not generate `-webkit-image-rendering`. ## 5.1.9 * Fix DynJS compatibility (by Nick Howes). ## 5.1.8 * Fix gradients in `mask` and `mask-image` properties. * Fix old webkit prefix on some unsupported gradients. ## 5.1.7 * Fix placeholder selector (by Vincent De Oliveira). ## 5.1.6 * Use official `::placeholder-shown` selector (by Vincent De Oliveira). ## 5.1.5 * Add transition support for CSS Masks properties. ## 5.1.4 * Use `-webkit-` prefix for Opera Mobile 24. ## 5.1.3 * Add IE support for `image-rendering: crisp-edges`. ## 5.1.2 * Add never existed `@-ms-keyframes` to common mistake. ## 5.1.1 * Safer value split in `flex` hack. ## 5.1 “Jianyuan” * Add support for resolution media query (by 一丝). * Higher accuracy while removing prefixes in values. * Add support for logical properties (by 一丝). * Add `@viewport` support. * Add `text-overflow` support (by 一丝). * Add `text-emphasis` support (by 一丝). * Add `image-rendering: crisp-edges` support. * Add `text-align-last` support. * Return `autoprefixer.defaults` as alias to current `browserslist.defaults`. * Save code style while adding prefixes to `@keyframes` and `@viewport`. * Do not remove `-webkit-background-clip` with non-spec `text` value. * Fix `-webkit-filter` in `transition`. * Better support for browser versions joined on Can I Use like `ios_saf 7.0-7.1` (by Vincent De Oliveira). * Fix compatibility with `postcss-import` (by Jason Kuhrt). * Fix Flexbox prefixes for BlackBerry and UC Browser. * Fix gradient prefixes for old Chrome. ## 5.0 “Pravda vítězí” * Use PostCSS 4.0. * Use Browserslist to parse browsers queries. * Use global `browserslist` config. * Add `> 5% in US` query to select browsers by usage in some country. * Add `object-fit` and `object-position` properties support. * Add CSS Shape properties support. * Fix UC Browser name in debug info. * Remove `autoprefixer.defaults` and use defaults from Browserslist. ## 4.0.2 * Remove `o-border-radius`, which is common mistake in legacy CSS. ## 4.0.1 * Fix `@supports` support with brackets in values (by Vincent De Oliveira). ## 4.0 “Indivisibiliter ac Inseparabiliter” * Become 2.5 times fatser by new PostCSS 3.0 parser. * Do not remove outdated prefixes by `remove: false` option. * `map.inline` and `map.sourcesContent` options are now `true` by default. * Add `box-decoration-break` support. * Do not add old `-webkit-` prefix for gradients with `px` units. * Use previous source map to show origin source of CSS syntax error. * Use `from` option from previous source map `file` field. * Set `to` value to `from` if `to` option is missing. * Trim Unicode BOM on source maps parsing. * Parse at-rules without spaces like `@import"file"`. * Better previous `sourceMappingURL` annotation comment cleaning. * Do not remove previous `sourceMappingURL` comment on `map.annotation: false`. ## 3.1.2 * Update Firefox ESR version from 24 to 31. ## 3.1.1 * Use Flexbox 2009 spec for Android stock browser < 4.4. ## 3.1 “Satyameva Jayate” * Do not remove comments from prefixed values (by Eitan Rousso). * Allow Safari 6.1 to use final Flexbox spec (by John Kreitlow). * Fix `filter` value in `transition` in Webkits. * Show greetings if your browsers don’t require any prefixes. * Add `<=` and `<` browsers requirement (by Andreas Lind). ## 3.0.1 * Fix `autoprefixer.postcss` in callbacks. ## 3.0 “Liberté, Égalité, Fraternité” * Project was split to autoprefixer (with CLI) and autoprefixer-core. * `autoprefixer()` now receives only `options` object with `browsers` key. * GNU format for syntax error messages from PostCSS 2.2. ## 2.2 “Mobilis in mobili” * Allow to disable Autoprefixer for some rule by control comment. * Use PostCSS 2.1 with Safe Mode option and broken source line in CSS syntax error messages. ## 2.1.1 * Fix `-webkit-background-size` hack for `contain` and `cover` values. * Don’t add `-webkit-` prefix to `filter` with SVG (by Vincent De Oliveira). ## 2.1 “Eleftheria i thanatos” * Add support for `clip-path` and `mask` properties. * Return `-webkit-` prefix to `filter` with SVG URI. ## 2.0.2 * Add readable names for new browsers from 2.0 release. * Don’t add `-webkit-` prefix to `filter` with SVG URI. * Don’t add `-o-` prefix 3D transforms. ## 2.0.1 * Save declaration style, when clone declaration to prefix. ## 2.0 “Hongik Ingan” * Based on PostCSS 1.0. See [options changes](https://github.com/postcss/postcss/releases/tag/1.0.0). * Restore visual cascade after declaration removing. * Enable visual cascade by default. * Prefix declareation in `@supports` at-rule conditions. * Add all browsers from Can I Use: `ie_mob`, `and_chr`, `and_ff`, `op_mob` and `op_mini`. * Allow to use latest Autoprefixer from GitHub by npm. * Add `--no-cascade`, `--annotation` and `--sources-content` options to binary. ## 1.3.1 * Fix gradient hack, when `background` property contains color. ## 1.3 “Tenka Fubu” * Add `text-size-adjust` support. * Add `background-size` to support Android 2. ## 1.2 “Meiji” * Use Can I Use data from official `caniuse-db` npm package. * Remove package data update from binary. * Use increment value instead of current date in minor versions. ## 1.1 “Nutrisco et extingo” * Add source map annotation comment support. * Add inline source map support. * Autodetect previous source map. * Fix source maps support on Windows. * Fix source maps support in subdirectory. * Prefix selector even if it is already prefixed by developer. * Add option `cascade` to create nice visual cascade of prefixes. * Fix flexbox support for IE 10 (by Roland Warmerdam). * Better `break-inside` support. * Fix prefixing, when two same properties are near. ### 20140222 * Add `touch-action` support. ### 20140226 * Chrome 33 is moved to released versions. * Add Chrome 36 data. ### 20140302 * Add `text-decoration-*` properties support. * Update browsers usage statistics. * Use new PostCSS version. ### 20140319 * Check already prefixed properties after current declaration. * Normalize spaces before already prefixed check. * Firefox 28 is moved to released versions. * Add Firefox 31 data. * Add some Blackberry data. ### 20140327 * Don’t use `-ms-transform` in `@keyframes`, because IE 9 doesn’t support animations. * Update BlackBerry 10 data. ### 20140403 * Update browsers usage statistics. * Opera 20 is moved to released versions. * Add Opera 22 data. ### 20140410 * Chrome 34 is moved to released versions. * Add Chrome 37 data. * Fix Chrome 36 data. ### 20140429 * Fix `display: inline-flex` support by 2009 spec. * Fix old WebKit gradient converter (by Sergey Belov). * Fix CSS 3 cursors data (by Nick Schonning). ### 20140430 * Separate 2D and 3D transform prefixes to clean unnecessary `-ms-` prefixes. * Firefox 29 is moved to released versions. * Add Firefox 32 data. ### 20140510 * Do not add `-ms-` prefix for `transform` with 3D functions. * Update browsers global usage statistics. ### 20140512 * Remove unnecessary `-moz-` prefix for `wavy` in `text-decoration`. * Update Safari data for font properties. ### 20140521 * Chrome 36 is moved to released versions. * Add Chrome 38 data. ### 20140523 * Opera 21 is moved to released versions. * Add Opera 23 data. ### 20140605 * Allow to parse gradients without space between color and position. * Add iOS 8, Safari 8 and Android 4.4.3 data. * Update browsers usage statistics. ## 1.0 “Plus ultra” * Source map support. * Save origin indents and code formatting. * Change CSS parser to PostCSS. * Preserve vendor-prefixed properties put right after unprefixed ones. * Rename `compile()` to `process()` and return result object, instead of CSS string. * Rename `inspect()` to `info()`. * Add in binary `-d` option to specify output directory. * Binary now will not concat output files. * Allow to select last versions for specified browser. * Add full browser names aliases: `firefox`, `explorer` and `blackberry`. * Ignore case in browser names. * Change license to MIT. * Add prefixes inside custom at-rules. * Add only necessary prefixes to selector inside prefixed at-rule. * Safer backgrounds list parser in gradient hack. * Prefix `@keyframes` inside `@media`. * Don’t prefix values for CSS3 PIE properties. * Binary now shows file name in syntax error. * Use browserify to build standalone version. ### 20131225 * Fix deprecated API convertor. * Add `::placeholder` support for Firefix >= 18. * Fix vendor prefixes order. ### 20140103 * Add `-webkit-` prefix for `sticky` position. * Update browsers popularity statistics. ### 20140109 * Add selectors and at-rules sections to debug info. * Fix outdated prefixes cleaning. ### 20140110 * Add `Firefox ESR` browser requirement. * Opera 18 is moved to released versions. * Add Opera 20 data. ### 20140117 * Chrome 32 is moved to released versions. * Add Opera 34 data. ### 20140130 * Fix flexbox properties names in transitions. * Add Chrome 35 and Firefox 29 data. ### 20140203 * Android 4.4 stock browser and Opera 19 are moved to released versions. * Add Opera 21 data. * Update browsers usage statistics. ### 20140213 * Add case insensitive to IE’s filter hack (by Dominik Schilling). * Improve selector prefixing in some rare cases (by Simon Lydell). * Firefox 27 is moved to released versions. * Add Firefox 30 data. ## 0.8 “Unbowed, Unbent, Unbroken” * Add more browsers to defaults ("> 1%, last 2 versions, ff 17, opera 12.1" instead of just "last 2 browsers"). * Keep vendor prefixes without unprefixed version (like vendor-specific hacks). * Convert gradients to old WebKit syntax (actual for Android 2.3). * Better support for several syntaxes with one prefix (like Flexbox and gradients in WebKit). * Add intrinsic and extrinsic sizing values support. * Remove never existed prefixes from common mistakes (like -ms-transition). * Add Opera 17 data. * Fix selector prefixes order. * Fix browser versions order in inspect. ### 20130903 * Fix old WebKit gradients convertor on rgba() colors. * Allow to write old direction syntax in gradients. ### 20130906 * Fix direction syntax in radial gradients. * Don’t prefix IE filter with modern syntax. ### 20130911 * Fix parsing property name with spaces. ### 20130919 * Fix processing custom framework prefixes (by Johannes J. Schmidt). * Concat outputs if several files compiled to one output. * Decrease standalone build size by removing unnecessary Binary class. * iOS 7 is moved to released versions. * Clean up binary code (by Simon Lydell). ### 20130923 * Firefox 24 is moved to released versions. ### 20131001 * Add support for grab, grabbing, zoom-in and zoom-out cursor values. ### 20131006 * Chrome 30 is moved to released versions. ### 20131007 * Don’t add another prefixes in rule with prefixed selector. ### 20131009 * Opera 17 is moved to released versions. ### 20131015 * Fix converting multiple gradients to old webkit syntax (by Aleksei Androsov). ### 20131017 * Fix @host at-rule parsing. ### 20131020 * IE 11 and Andrid 4.3 is moved to released versions. * Add Opera 18 data. * Add @namespace support. * Sort browser versions in data file. ### 20131029 * Add Safari 6.1 data. * Add fx alias for Firefox. ### 20131104 * Update Android future version to 4.4. * Google Chrome 32 added to future versions list. * Firefox 25 now is actual version, 27 and 28 added to future versions. * Browsers statistics are updated. ### 20131205 * Google Chrome 33 added to future releases list. * Google Chrome 31 moved to current releases list. ### 20131209 * Use old webkit gradients for old iOS and Safari (by Chad von Nau). * Fix direction conversion for old webkit gradients (by Chad von Nau). * Update browsers popularity statistics. ### 20131213 * Firefox ESR in default browsers was changed to 24 version. * Firefox 26 was moved to current releases list. * Firefox 28 was added to future releases list. ## 0.7 “We Do Not Sow” * Add vendor prefixes to selectors. * Add ::selection and ::placeholder selectors support. * Allow to load support data from Can I Use pull requests. * Remove deprecated API. ### 20130806 * Add hyphens support. ### 20130807 * Add tab-size support. * Add :fullscreen support. ### 20130808 * Allow to select browser versions by > and >= operator. * Fix flex properties in transition. ### 20130810 * Add Firefox 25 data. ### 20130824 * Add Chrome 31 and 30 data. * Fix CSS comments parsing (by vladkens). ## 0.6 “As High As Honor” * New faster API, which cache preprocessed data. Old API is deprecated. * A lot of perfomance improvements. * Add Opera 15 -webkit- prefix support. * Update Chrome 29 and Safari 7 prefixes data. * Add minor browsers in popularity select. * Better syntax error messages. ### 20130721 * Add Chrome 30 data. ### 20130728 * Don’t remove non-standard -webkit-background-clip: text. * Don’t remove IE hack on CSS parse. ### 20130729 * Add Opera 16 data. * Fix “Invalid range in character class” error on Firefox. ### 20130730 * Fix correct clone comments inside keyframes (by Alexey Plutalov). * Fix angle recalculation in gradients (by Roman Komarov). ### 20130731 * Add border-image support. ## 0.5 “Ours is the Fury” * Rewrite Autoprefixer to be more flexible. * Use css, instead of Rework, to fix CSS parsing errors faster. * Fix a lot of CSS parsing errors. ### 20130616 * More useful message for CSS parsing errors. * Remove old WebKit gradient syntax. * Fix parsing error on comment with braces. ### 20130617 * Remove old Mozilla border-radius. * Don’t prefix old IE filter. * Remove old background-clip, background-size and background-origin prefixes. * Speed up regexps in values. * Allow to hack property declarations. ### 20130625 * Convert flexbox properties to 2009 and 2012 specifications. * Improve messages on syntax errors. ### 20130626 * Add Firefox 24 data. * Add prefixes for font-feature-settings. ### 20130629 * Fix convert flex properties to old box-flex. ## 0.4 “Winter Is Coming” * Remove outdated prefixes. * Add border-radius and box-shadow properties to database. * Change degrees in webkit gradients. ### 20130515 * Add old syntax in gradient direction. * Add old syntax for display: flex. * Update browser global usage statistics. ### 20130521 * Add Firefox 23 data. ### 20130524 * Add Chrome 29 data. ### 20130528 * Fix compatibilty with Rework from git master. * Add minor browsers to data, which can be selected only directly. ### 20130530 * Add Opera 15 and iOS 6.1 data. * Fix iOS versions in properties and values data. ### 20130603 * Use latest Rework 0.15 with a lot of CSS parsing fixes. * Update browsers usage statistics. ## 0.3 “Growing Strong” * Rename `autoprefixer.filter()` to `autoprefixer.rework()`. * Use own filters instead of Rework’s `prefix` and `prefixValue`. * Smarter value prefixer without false match “order” in “border”. * 40% faster. * Don’t add unnecessary properties instead of Rework’s `prefixValue`. * Don’t change properties order. * Sort properties and values in inspect output. * Add main to component config (by Jonathan Ong). * Fix documentation (by Sergey Leschina and Mark Vasilkov). ### 20130424 * Fix value override in prefixer. ### 20130427 * Prefix several same values in one property. * Fix Windows support in binary. * Improve print errors in binary. ### 20130502 * Don’t add -webkit- prefix to IE filter. * Don’t duplicate prefixes on second run. ## 0.2 “Hear Me Roar!” * Update parse libraries. * Use component package manager to build standalone script. * Add inspect to standalone script. ## 0.1 “Fire and Blood” * Initial release. ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright 2013 Andrey Sitnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Autoprefixer [![Cult Of Martians][cult-img]][cult] [PostCSS] plugin to parse CSS and add vendor prefixes to CSS rules using values from [Can I Use]. It is recommended by Google and used in Twitter and Alibaba. Write your CSS rules without vendor prefixes (in fact, forget about them entirely): ```css ::placeholder { color: gray; } .image { width: stretch; } ``` Autoprefixer will use the data based on current browser popularity and property support to apply prefixes for you. You can try the [interactive demo] of Autoprefixer. ```css ::-moz-placeholder { color: gray; } ::placeholder { color: gray; } .image { width: -webkit-fill-available; width: -moz-available; width: stretch; } ``` Twitter account for news and releases: [@autoprefixer]. Sponsored by Evil Martians [interactive demo]: https://autoprefixer.github.io/ [@autoprefixer]: https://twitter.com/autoprefixer [Can I Use]: https://caniuse.com/ [cult-img]: https://cultofmartians.com/assets/badges/badge.svg [PostCSS]: https://github.com/postcss/postcss [cult]: https://cultofmartians.com/tasks/autoprefixer-grid.html ## Contents * [Contents](#contents) * [Browsers](#browsers) * [FAQ](#faq) * [Does Autoprefixer polyfill Grid Layout for IE?](#does-autoprefixer-polyfill-grid-layout-for-ie) * [Does it add polyfills?](#does-it-add-polyfills) * [Why doesn’t Autoprefixer add prefixes to `border-radius`?](#why-doesnt-autoprefixer-add-prefixes-to-border-radius) * [Why does Autoprefixer use unprefixed properties in `@-webkit-keyframes`?](#why-does-autoprefixer-use-unprefixed-properties-in--webkit-keyframes) * [How to work with legacy `-webkit-` only code?](#how-to-work-with-legacy--webkit--only-code) * [Does Autoprefixer add `-epub-` prefix?](#does-autoprefixer-add--epub--prefix) * [Why doesn’t Autoprefixer transform generic font-family `system-ui`?](#why-doesnt-autoprefixer-transform-generic-font-family-system-ui) * [Usage](#usage) * [Gulp](#gulp) * [Webpack](#webpack) * [CSS-in-JS](#css-in-js) * [CLI](#cli) * [Other Build Tools](#other-build-tools) * [Preprocessors](#preprocessors) * [GUI Tools](#gui-tools) * [JavaScript](#javascript) * [Text Editors and IDE](#text-editors-and-ide) * [Warnings](#warnings) * [Disabling](#disabling) * [Prefixes](#prefixes) * [Features](#features) * [Control Comments](#control-comments) * [Options](#options) * [Environment Variables](#environment-variables) * [Using environment variables to support CSS Grid prefixes in Create React App](#using-environment-variables-to-support-css-grid-prefixes-in-create-react-app) * [Grid Autoplacement support in IE](#grid-autoplacement-support-in-ie) * [Beware of enabling autoplacement in old projects](#beware-of-enabling-autoplacement-in-old-projects) * [Autoplacement limitations](#autoplacement-limitations) * [Both columns and rows must be defined](#both-columns-and-rows-must-be-defined) * [Repeat auto-fit and auto-fill are not supported](#repeat-auto-fit-and-auto-fill-are-not-supported) * [No manual cell placement or column/row spans allowed inside an autoplacement grid](#no-manual-cell-placement-or-columnrow-spans-allowed-inside-an-autoplacement-grid) * [Do not create `::before` and `::after` pseudo elements](#do-not-create-before-and-after-pseudo-elements) * [When changing the `grid gap` value, columns and rows must be re-declared](#when-changing-the-grid-gap-value-columns-and-rows-must-be-re-declared) * [Debug](#debug) * [Security Contact](#security-contact) * [For Enterprise](#for-enterprise) ## Browsers Autoprefixer uses [Browserslist], so you can specify the browsers you want to target in your project with queries like `> 5%` (see [Best Practices]). The best way to provide browsers is a `.browserslistrc` file in your project root, or by adding a `browserslist` key to your `package.json`. We recommend the use of these options over passing options to Autoprefixer so that the config can be shared with other tools such as [babel-preset-env] and [Stylelint]. See [Browserslist docs] for queries, browser names, config format, and defaults. [Browserslist docs]: https://github.com/browserslist/browserslist#queries [babel-preset-env]: https://github.com/babel/babel/tree/master/packages/babel-preset-env [Best Practices]: https://github.com/browserslist/browserslist#best-practices [Browserslist]: https://github.com/browserslist/browserslist [Stylelint]: https://stylelint.io/ ## FAQ ### Does Autoprefixer polyfill Grid Layout for IE? Autoprefixer can be used to translate modern CSS Grid syntax into IE 10 and IE 11 syntax, but this polyfill will not work in 100% of cases. This is why it is disabled by default. First, you need to enable Grid prefixes by using either the `grid: "autoplace"` option or the `/* autoprefixer grid: autoplace */` control comment. Also you can use environment variable to enable Grid: `AUTOPREFIXER_GRID=autoplace npm build`. Second, you need to test every fix with Grid in IE. It is not an enable and forget feature, but it is still very useful. Financial Times and Yandex use it in production. Third, there is only very limited auto placement support. Read the [Grid Autoplacement support in IE](#grid-autoplacement-support-in-ie) section for more details. Fourth, if you are not using the autoplacement feature, the best way to use Autoprefixer is by using `grid-template` or `grid-template-areas`. ```css .page { display: grid; grid-gap: 33px; grid-template: "head head head" 1fr "nav main main" minmax(100px, 1fr) "nav foot foot" 2fr / 1fr 100px 1fr; } .page__head { grid-area: head; } .page__nav { grid-area: nav; } .page__main { grid-area: main; } .page__footer { grid-area: foot; } ``` See also: * [The guide about Grids in IE and Autoprefixer]. * [`postcss-gap-properties`] to use new `gap` property instead of old `grid-gap`. * [`postcss-grid-kiss`] has alternate “everything in one property” syntax, which makes using Autoprefixer’s Grid translations safer. [The guide about Grids in IE and Autoprefixer]: https://css-tricks.com/css-grid-in-ie-css-grid-and-the-new-autoprefixer/ [`postcss-gap-properties`]: https://github.com/jonathantneal/postcss-gap-properties [`postcss-grid-kiss`]: https://github.com/sylvainpolletvillard/postcss-grid-kiss ### Does it add polyfills? No. Autoprefixer only adds prefixes. Most new CSS features will require client side JavaScript to handle a new behavior correctly. Depending on what you consider to be a “polyfill”, you can take a look at some other tools and libraries. If you are just looking for syntax sugar, you might take a look at: - [postcss-preset-env] is a plugins preset with polyfills and Autoprefixer to write future CSS today. - [Oldie], a PostCSS plugin that handles some IE hacks (opacity, rgba, etc). - [postcss-flexbugs-fixes], a PostCSS plugin to fix flexbox issues. [postcss-flexbugs-fixes]: https://github.com/luisrudge/postcss-flexbugs-fixes [postcss-preset-env]: https://github.com/jonathantneal/postcss-preset-env [Oldie]: https://github.com/jonathantneal/oldie ### Why doesn’t Autoprefixer add prefixes to `border-radius`? Developers are often surprised by how few prefixes are required today. If Autoprefixer doesn’t add prefixes to your CSS, check if they’re still required on [Can I Use]. [Can I Use]: https://caniuse.com/ ### Why does Autoprefixer use unprefixed properties in `@-webkit-keyframes`? Browser teams can remove some prefixes before others, so we try to use all combinations of prefixed/unprefixed values. ### How to work with legacy `-webkit-` only code? Autoprefixer needs unprefixed property to add prefixes. So if you only wrote `-webkit-gradient` without W3C’s `gradient`, Autoprefixer will not add other prefixes. But [PostCSS] has plugins to convert CSS to unprefixed state. Use [postcss-unprefix] before Autoprefixer. [postcss-unprefix]: https://github.com/gucong3000/postcss-unprefix ### Does Autoprefixer add `-epub-` prefix? No, Autoprefixer works only with browsers prefixes from Can I Use. But you can use [postcss-epub] for prefixing ePub3 properties. [postcss-epub]: https://github.com/Rycochet/postcss-epub ### Why doesn’t Autoprefixer transform generic font-family `system-ui`? `system-ui` is technically not a prefix and the transformation is not future-proof. You can use [postcss-font-family-system-ui] to transform `system-ui` to a practical font-family list. [postcss-font-family-system-ui]: https://github.com/JLHwung/postcss-font-family-system-ui ## Usage ### Gulp In Gulp you can use [gulp-postcss] with `autoprefixer` npm package. ```js gulp.task('autoprefixer', () => { const autoprefixer = require('autoprefixer') const sourcemaps = require('gulp-sourcemaps') const postcss = require('gulp-postcss') return gulp.src('./src/*.css') .pipe(sourcemaps.init()) .pipe(postcss([ autoprefixer() ])) .pipe(sourcemaps.write('.')) .pipe(gulp.dest('./dest')) }) ``` With `gulp-postcss` you also can combine Autoprefixer with [other PostCSS plugins]. [gulp-postcss]: https://github.com/postcss/gulp-postcss [other PostCSS plugins]: https://github.com/postcss/postcss#plugins ### Webpack In [webpack] you can use [postcss-loader] with `autoprefixer` and [other PostCSS plugins]. ```js module.exports = { module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader", "postcss-loader"] } ] } } ``` And create a `postcss.config.js` with: ```js module.exports = { plugins: [ require('autoprefixer') ] } ``` [other PostCSS plugins]: https://github.com/postcss/postcss#plugins [postcss-loader]: https://github.com/postcss/postcss-loader [webpack]: https://webpack.js.org/ ### CSS-in-JS The best way to use PostCSS with CSS-in-JS is [`astroturf`]. Add its loader to your `webpack.config.js`: ```js module.exports = { module: { rules: [ { test: /\.css$/, use: ['style-loader', 'postcss-loader'], }, { test: /\.jsx?$/, use: ['babel-loader', 'astroturf/loader'], } ] } } ``` Then create `postcss.config.js`: ```js module.exports = { plugins: [ require('autoprefixer') ] } ``` [`astroturf`]: https://github.com/4Catalyzer/astroturf ### CLI You can use the [postcss-cli] to run Autoprefixer from CLI: ```sh npm install postcss postcss-cli autoprefixer npx postcss *.css --use autoprefixer -d build/ ``` See `postcss -h` for help. [postcss-cli]: https://github.com/postcss/postcss-cli ### Other Build Tools * **Grunt:** [grunt-postcss] * **Ruby on Rails**: [autoprefixer-rails] * **Neutrino**: [neutrino-middleware-postcss] * **Jekyll**: add `autoprefixer-rails` and `jekyll-assets` to `Gemfile` * **Brunch**: [postcss-brunch] * **Broccoli**: [broccoli-postcss] * **Middleman**: [middleman-autoprefixer] * **Mincer**: add `autoprefixer` npm package and enable it: `environment.enable('autoprefixer')` [neutrino-middleware-postcss]: https://www.npmjs.com/package/neutrino-middleware-postcss [middleman-autoprefixer]: https://github.com/middleman/middleman-autoprefixer [autoprefixer-rails]: https://github.com/ai/autoprefixer-rails [broccoli-postcss]: https://github.com/jeffjewiss/broccoli-postcss [postcss-brunch]: https://github.com/iamvdo/postcss-brunch [grunt-postcss]: https://github.com/C-Lodder/grunt-postcss #### Preprocessors * **Less**: [less-plugin-autoprefix] * **Stylus**: [autoprefixer-stylus] * **Compass**: [autoprefixer-rails#compass] [less-plugin-autoprefix]: https://github.com/less/less-plugin-autoprefix [autoprefixer-stylus]: https://github.com/jenius/autoprefixer-stylus [autoprefixer-rails#compass]: https://github.com/ai/autoprefixer-rails#compass #### GUI Tools * [CodeKit](https://codekitapp.com/help/autoprefixer/) * [Prepros](https://prepros.io) ### JavaScript You can use Autoprefixer with [PostCSS] in your Node.js application or if you want to develop an Autoprefixer plugin for a new environment. ```js const autoprefixer = require('autoprefixer') const postcss = require('postcss') postcss([ autoprefixer ]).process(css).then(result => { result.warnings().forEach(warn => { console.warn(warn.toString()) }) console.log(result.css) }) ``` There is also a [standalone build] for the browser or for a non-Node.js runtime. You can use [html-autoprefixer] to process HTML with inlined CSS. [html-autoprefixer]: https://github.com/RebelMail/html-autoprefixer [standalone build]: https://raw.github.com/ai/autoprefixer-rails/master/vendor/autoprefixer.js [PostCSS]: https://github.com/postcss/postcss ### Text Editors and IDE Autoprefixer should be used in assets build tools. Text editor plugins are not a good solution, because prefixes decrease code readability and you will need to change values in all prefixed properties. I recommend you to learn how to use build tools like [Parcel]. They work much better and will open you a whole new world of useful plugins and automation. If you can’t move to a build tool, you can use text editor plugins: * [Visual Studio Code](https://github.com/mrmlnc/vscode-autoprefixer) * [Sublime Text](https://github.com/sindresorhus/sublime-autoprefixer) [Parcel]: https://parceljs.org/ ## Warnings Autoprefixer uses the [PostCSS warning API] to warn about really important problems in your CSS: * Old direction syntax in gradients. * Old unprefixed `display: box` instead of `display: flex` by latest specification version. You can get warnings from `result.warnings()`: ```js result.warnings().forEach(warn => { console.warn(warn.toString()) }) ``` Every Autoprefixer runner should display these warnings. [PostCSS warning API]: https://postcss.org/api/#warning ## Disabling ### Prefixes Autoprefixer was designed to have no interface – it just works. If you need some browser specific hack just write a prefixed property after the unprefixed one. ```css a { transform: scale(0.5); -moz-transform: scale(0.6); } ``` If some prefixes were generated incorrectly, please create an [issue on GitHub]. [issue on GitHub]: https://github.com/postcss/autoprefixer/issues ### Features You can use these plugin options to control some of Autoprefixer’s features. * `grid: "autoplace"` will enable `-ms-` prefixes for Grid Layout including some [limited autoplacement support](#grid-autoplacement-support-in-ie). * `supports: false` will disable `@supports` parameters prefixing. * `flexbox: false` will disable flexbox properties prefixing. Or `flexbox: "no-2009"` will add prefixes only for final and IE versions of specification. * `remove: false` will disable cleaning outdated prefixes. You should set them inside the plugin like so: ```js autoprefixer({ grid: 'autoplace' }) ``` ### Control Comments If you do not need Autoprefixer in some part of your CSS, you can use control comments to disable Autoprefixer. ```css .a { transition: 1s; /* will be prefixed */ } .b { /* autoprefixer: off */ transition: 1s; /* will not be prefixed */ } .c { /* autoprefixer: ignore next */ transition: 1s; /* will not be prefixed */ mask: url(image.png); /* will be prefixed */ } ``` There are three types of control comments: * `/* autoprefixer: (on|off) */`: enable/disable all Autoprefixer translations for the whole block both *before* and *after* the comment. * `/* autoprefixer: ignore next */`: disable Autoprefixer only for the next property or next rule selector or at-rule parameters (but not rule/at‑rule body). * `/* autoprefixer grid: (autoplace|no-autoplace|off) */`: control how Autoprefixer handles grid translations for the whole block: * `autoplace`: enable grid translations with autoplacement support. * `no-autoplace`: enable grid translations with autoplacement support *disabled* (alias for deprecated value `on`). * `off`: disable all grid translations. You can also use comments recursively: ```css /* autoprefixer: off */ @supports (transition: all) { /* autoprefixer: on */ a { /* autoprefixer: off */ } } ``` Note that comments that disable the whole block should not be featured in the same block twice: ```css /* How not to use block level control comments */ .do-not-do-this { /* autoprefixer: off */ transition: 1s; /* autoprefixer: on */ transform: rotate(20deg); } ``` ## Options Function `autoprefixer(options)` returns a new PostCSS plugin. See [PostCSS API] for plugin usage documentation. ```js autoprefixer({ cascade: false }) ``` Available options are: * `env` (string): environment for Browserslist. * `cascade` (boolean): should Autoprefixer use Visual Cascade, if CSS is uncompressed. Default: `true` * `add` (boolean): should Autoprefixer add prefixes. Default is `true`. * `remove` (boolean): should Autoprefixer [remove outdated] prefixes. Default is `true`. * `supports` (boolean): should Autoprefixer add prefixes for `@supports` parameters. Default is `true`. * `flexbox` (boolean|string): should Autoprefixer add prefixes for flexbox properties. With `"no-2009"` value Autoprefixer will add prefixes only for final and IE 10 versions of specification. Default is `true`. * `grid` (false|`"autoplace"`|`"no-autoplace"`): should Autoprefixer add IE 10-11 prefixes for Grid Layout properties? * `false` (default): prevent Autoprefixer from outputting CSS Grid translations. * `"autoplace"`: enable Autoprefixer grid translations and *include* autoplacement support. You can also use `/* autoprefixer grid: autoplace */` in your CSS. * `"no-autoplace"`: enable Autoprefixer grid translations but *exclude* autoplacement support. You can also use `/* autoprefixer grid: no-autoplace */` in your CSS. (alias for the deprecated `true` value) * `stats` (object): custom [usage statistics] for `> 10% in my stats` browsers query. * `overrideBrowserslist` (array): list of queries for target browsers. Try to not use it. The best practice is to use `.browserslistrc` config or `browserslist` key in `package.json` to share target browsers with Babel, ESLint and Stylelint. See [Browserslist docs] for available queries and default value. * `ignoreUnknownVersions` (boolean): do not raise error on unknown browser version in Browserslist config. Default is `false`. Plugin object has `info()` method for debugging purpose. You can use PostCSS processor to process several CSS files to increase performance. [usage statistics]: https://github.com/browserslist/browserslist#custom-usage-data [PostCSS API]: https://postcss.org/api/ ## Environment Variables * `AUTOPREFIXER_GRID`: (`autoplace`|`no-autoplace`) should Autoprefixer add IE 10-11 prefixes for Grid Layout properties? * `autoplace`: enable Autoprefixer grid translations and *include* autoplacement support. * `no-autoplace`: enable Autoprefixer grid translations but *exclude* autoplacement support. Environment variables are useful, when you want to change Autoprefixer options but don't have access to config files. [Create React App] is a good example of this. [Create React App]: (https://reactjs.org/docs/create-a-new-react-app.html#create-react-app) ### Using environment variables to support CSS Grid prefixes in Create React App 1. Install the latest version of Autoprefixer and [cross-env](https://www.npmjs.com/package/cross-env): ``` npm install autoprefixer@latest cross-env --save-dev ``` 2. Under `"browserslist"` > `"development"` in the package.json file, add `"last 1 ie version"` ``` "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version", "last 1 ie version" ] } ``` 3. Update `"scripts"` in the package.json file to the following: ``` "scripts": { "start": "cross-env AUTOPREFIXER_GRID=autoplace react-scripts start", "build": "cross-env AUTOPREFIXER_GRID=autoplace react-scripts build", "test": "cross-env AUTOPREFIXER_GRID=autoplace react-scripts test", "eject": "react-scripts eject" }, ``` Replace `autoplace` with `no-autoplace` in the above example if you prefer to disable Autoprefixer Grid autoplacement support. Now when you run `npm start` you will see CSS Grid prefixes automatically being applied to your output CSS. See also [Browserslist environment variables] for more examples on how to use environment variables in your project. [Browserslist environment variables]: https://github.com/browserslist/browserslist#environment-variables ## Grid Autoplacement support in IE If the `grid` option is set to `"autoplace"`, limited autoplacement support is added to Autoprefixers grid translations. You can also use the `/* autoprefixer grid: autoplace */` control comment or `AUTOPREFIXER_GRID=autoplace npm build` environment variable. Autoprefixer will only autoplace grid cells if both `grid-template-rows` and `grid-template-columns` has been set. If `grid-template` or `grid-template-areas` has been set, Autoprefixer will use area based cell placement instead. Autoprefixer supports autoplacement by using `nth-child` CSS selectors. It creates [number of columns] x [number of rows] `nth-child` selectors. For this reason Autoplacement is only supported within the explicit grid. ```css /* Input CSS */ /* autoprefixer grid: autoplace */ .autoplacement-example { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: auto auto; grid-gap: 20px; } ``` ```css /* Output CSS */ /* autoprefixer grid: autoplace */ .autoplacement-example { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 20px 1fr; grid-template-columns: 1fr 1fr; -ms-grid-rows: auto 20px auto; grid-template-rows: auto auto; grid-gap: 20px; } .autoplacement-example > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .autoplacement-example > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 3; } .autoplacement-example > *:nth-child(3) { -ms-grid-row: 3; -ms-grid-column: 1; } .autoplacement-example > *:nth-child(4) { -ms-grid-row: 3; -ms-grid-column: 3; } ``` ### Beware of enabling autoplacement in old projects Be careful about enabling autoplacement in any already established projects that have previously not used Autoprefixer's grid autoplacement feature before. If this was your html: ```html
``` The following CSS will not work as expected with the autoplacement feature enabled: ```css /* Unsafe CSS when Autoplacement is enabled */ .grid-cell { grid-column: 2; grid-row: 2; } .grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); } ``` Swapping the rules around will not fix the issue either: ```css /* Also unsafe to use this CSS */ .grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); } .grid-cell { grid-column: 2; grid-row: 2; } ``` One way to deal with this issue is to disable autoplacement in the grid-declaration rule: ```css /* Disable autoplacement to fix the issue */ .grid { /* autoprefixer grid: no-autoplace */ display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); } .grid-cell { grid-column: 2; grid-row: 2; } ``` The absolute best way to integrate autoplacement into already existing projects though is to leave autoplacement turned off by default and then use a control comment to enable it when needed. This method is far less likely to cause something on the site to break. ```css /* Disable autoplacement by default in old projects */ /* autoprefixer grid: no-autoplace */ /* Old code will function the same way it always has */ .old-grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); } .old-grid-cell { grid-column: 2; grid-row: 2; } /* Enable autoplacement when you want to use it in new code */ .new-autoplace-friendly-grid { /* autoprefixer grid: autoplace */ display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, auto); } ``` Note that the `grid: "no-autoplace"` setting and the `/* autoprefixer grid: no-autoplace */` control comment share identical functionality to the `grid: true` setting and the `/* autoprefixer grid: on */` control comment. There is no need to refactor old code to use `no-autoplace` in place of the old `true` and `on` statements. ### Autoplacement limitations #### Both columns and rows must be defined Autoplacement only works inside the explicit grid. The columns and rows need to be defined so that Autoprefixer knows how many `nth-child` selectors to generate. ```css .not-allowed { display: grid; grid-template-columns: repeat(3, 1fr); } .is-allowed { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(10, auto); } ``` #### Repeat auto-fit and auto-fill are not supported The `repeat(auto-fit, ...)` and `repeat(auto-fill, ...)` grid functionality relies on knowledge from the browser about screen dimensions and the number of available grid items for it to work properly. Autoprefixer does not have access to this information so unfortunately this little snippet will _never_ be IE friendly. ```css .grid { /* This will never be IE friendly */ grid-template-columns: repeat(auto-fit, min-max(200px, 1fr)) } ``` #### No manual cell placement or column/row spans allowed inside an autoplacement grid Elements must not be manually placed or given column/row spans inside an autoplacement grid. Only the most basic of autoplacement grids are supported. Grid cells can still be placed manually outside the the explicit grid though. Support for manually placing individual grid cells inside an explicit autoplacement grid is planned for a future release. ```css .autoplacement-grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, auto); } /* Grid cells placed inside the explicit grid will break the layout in IE */ .not-permitted-grid-cell { grid-column: 1; grid-row: 1; } /* Grid cells placed outside the explicit grid will work in IE */ .permitted-grid-cell { grid-column: 1 / span 2; grid-row: 4; } ``` If manual cell placement is required, we recommend using `grid-template` or `grid-template-areas` instead: ```css .page { display: grid; grid-gap: 30px; grid-template: "head head" "nav main" minmax(100px, 1fr) "foot foot" / 200px 1fr; } .page__head { grid-area: head; } .page__nav { grid-area: nav; } .page__main { grid-area: main; } .page__footer { grid-area: foot; } ``` #### Do not create `::before` and `::after` pseudo elements Let's say you have this HTML: ```html
``` And you write this CSS: ```css .grid { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: auto; } .grid::before { content: 'before'; } .grid::after { content: 'after'; } ``` This will be the output: ```css .grid { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 1fr; grid-template-columns: 1fr 1fr; -ms-grid-rows: auto; grid-template-rows: auto; } .grid > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .grid > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 2; } .grid::before { content: 'before'; } .grid::after { content: 'after'; } ``` IE will place `.grid-cell`, `::before` and `::after` in row 1 column 1. Modern browsers on the other hand will place `::before` in row 1 column 1, `.grid-cell` in row 1 column 2, and `::after` in row 2 column 1. See this [CodePen](https://codepen.io/daniel-tonon/pen/gBymVw) to see a visualization of the issue. View the CodePen in both a modern browser and IE to see the difference. Note that you can still create `::before` and `::after` elements as long as you manually place them outside the explicit grid. #### When changing the `grid gap` value, columns and rows must be re-declared If you wish to change the size of a `grid-gap`, you will need to redeclare the grid columns and rows. ```css .grid { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: auto; grid-gap: 50px; } /* This will *NOT* work in IE */ @media (max-width: 600px) { .grid { grid-gap: 20px; } } /* This will *NOT* work in IE */ .grid.small-gap { grid-gap: 20px; } ``` ```css .grid { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: auto; grid-gap: 50px; } /* This *WILL* work in IE */ @media (max-width: 600px) { .grid { grid-template-columns: 1fr 1fr; grid-template-rows: auto; grid-gap: 20px; } } /* This *WILL* work in IE */ .grid.small-gap { grid-template-columns: 1fr 1fr; grid-template-rows: auto; grid-gap: 20px; } ``` ## Debug Run `npx autoprefixer --info` in your project directory to check which browsers are selected and which properties will be prefixed: ```console $ npx autoprefixer --info Browsers: Edge: 16 These browsers account for 0.26% of all users globally At-Rules: @viewport: ms Selectors: ::placeholder: ms Properties: appearance: webkit flow-from: ms flow-into: ms hyphens: ms overscroll-behavior: ms region-fragment: ms scroll-snap-coordinate: ms scroll-snap-destination: ms scroll-snap-points-x: ms scroll-snap-points-y: ms scroll-snap-type: ms text-size-adjust: ms text-spacing: ms user-select: ms ``` JS API is also available: ```js console.log(autoprefixer().info()) ``` ## Security Contact To report a security vulnerability, please use the [Tidelift security contact]. Tidelift will coordinate the fix and disclosure. [Tidelift security contact]: https://tidelift.com/security ## For Enterprise Available as part of the Tidelift Subscription. The maintainers of `autoprefixer` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-autoprefixer?utm_source=npm-autoprefixer&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) ================================================ FILE: bin/autoprefixer ================================================ #!/usr/bin/env node let mode = process.argv[2] if (mode === '--info') { process.stdout.write(require('../')().info() + '\n') } else if (mode === '--version') { process.stdout.write( 'autoprefixer ' + require('../package.json').version + '\n' ) } else { process.stdout.write( 'autoprefix\n' + '\n' + 'Options:\n' + ' --info Show target browsers and used prefixes\n' + ' --version Show version number\n' + ' --help Show help\n' + '\n' + 'Usage:\n' + ' autoprefixer --info\n' ) } ================================================ FILE: data/prefixes.js ================================================ let unpack = require('caniuse-lite/dist/unpacker/feature') function browsersSort(a, b) { a = a.split(' ') b = b.split(' ') if (a[0] > b[0]) { return 1 } else if (a[0] < b[0]) { return -1 } else { return Math.sign(parseFloat(a[1]) - parseFloat(b[1])) } } // Convert Can I Use data function f(data, opts, callback) { data = unpack(data) if (!callback) { ;[callback, opts] = [opts, {}] } let match = opts.match || /\sx($|\s)/ let need = [] for (let browser in data.stats) { let versions = data.stats[browser] for (let version in versions) { let support = versions[version] if (support.match(match)) { need.push(browser + ' ' + version) } } } callback(need.sort(browsersSort)) } // Add data for all properties let result = {} function prefix(names, data) { for (let name of names) { result[name] = Object.assign({}, data) } } function add(names, data) { for (let name of names) { result[name].browsers = result[name].browsers .concat(data.browsers) .sort(browsersSort) } } module.exports = result // Border Radius let prefixBorderRadius = require('caniuse-lite/data/features/border-radius') f(prefixBorderRadius, browsers => prefix( [ 'border-radius', 'border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius' ], { browsers, feature: 'border-radius', mistakes: ['-khtml-', '-ms-', '-o-'] } ) ) // Box Shadow let prefixBoxshadow = require('caniuse-lite/data/features/css-boxshadow') f(prefixBoxshadow, browsers => prefix(['box-shadow'], { browsers, feature: 'css-boxshadow', mistakes: ['-khtml-'] }) ) // Animation let prefixAnimation = require('caniuse-lite/data/features/css-animation') f(prefixAnimation, browsers => prefix( [ 'animation', 'animation-name', 'animation-duration', 'animation-delay', 'animation-direction', 'animation-fill-mode', 'animation-iteration-count', 'animation-play-state', 'animation-timing-function', '@keyframes' ], { browsers, feature: 'css-animation', mistakes: ['-khtml-', '-ms-'] } ) ) // Transition let prefixTransition = require('caniuse-lite/data/features/css-transitions') f(prefixTransition, browsers => prefix( [ 'transition', 'transition-property', 'transition-duration', 'transition-delay', 'transition-timing-function' ], { browsers, feature: 'css-transitions', mistakes: ['-khtml-', '-ms-'] } ) ) // Transform 2D let prefixTransform2d = require('caniuse-lite/data/features/transforms2d') f(prefixTransform2d, browsers => prefix(['transform', 'transform-origin'], { browsers, feature: 'transforms2d' }) ) // Transform 3D let prefixTransforms3d = require('caniuse-lite/data/features/transforms3d') f(prefixTransforms3d, browsers => { prefix(['perspective', 'perspective-origin'], { browsers, feature: 'transforms3d' }) return prefix(['transform-style'], { browsers, feature: 'transforms3d', mistakes: ['-ms-', '-o-'] }) }) f(prefixTransforms3d, { match: /y\sx|y\s#2/ }, browsers => prefix(['backface-visibility'], { browsers, feature: 'transforms3d', mistakes: ['-ms-', '-o-'] }) ) // Gradients let prefixGradients = require('caniuse-lite/data/features/css-gradients') f(prefixGradients, { match: /y\sx/ }, browsers => prefix( [ 'linear-gradient', 'repeating-linear-gradient', 'radial-gradient', 'repeating-radial-gradient' ], { browsers, feature: 'css-gradients', mistakes: ['-ms-'], props: [ 'background', 'background-image', 'border-image', 'mask', 'list-style', 'list-style-image', 'content', 'mask-image' ] } ) ) f(prefixGradients, { match: /a\sx/ }, browsers => { browsers = browsers.map(i => { if (/firefox|op/.test(i)) { return i } else { return `${i} old` } }) return add( [ 'linear-gradient', 'repeating-linear-gradient', 'radial-gradient', 'repeating-radial-gradient' ], { browsers, feature: 'css-gradients' } ) }) // Box sizing let prefixBoxsizing = require('caniuse-lite/data/features/css3-boxsizing') f(prefixBoxsizing, browsers => prefix(['box-sizing'], { browsers, feature: 'css3-boxsizing' }) ) // Filter Effects let prefixFilters = require('caniuse-lite/data/features/css-filters') f(prefixFilters, browsers => prefix(['filter'], { browsers, feature: 'css-filters' }) ) // filter() function let prefixFilterFunction = require('caniuse-lite/data/features/css-filter-function') f(prefixFilterFunction, browsers => prefix(['filter-function'], { browsers, feature: 'css-filter-function', props: [ 'background', 'background-image', 'border-image', 'mask', 'list-style', 'list-style-image', 'content', 'mask-image' ] }) ) // Backdrop-filter let prefixBackdropFilter = require('caniuse-lite/data/features/css-backdrop-filter') f(prefixBackdropFilter, { match: /y\sx|y\s#2/ }, browsers => prefix(['backdrop-filter'], { browsers, feature: 'css-backdrop-filter' }) ) // element() function let prefixElementFunction = require('caniuse-lite/data/features/css-element-function') f(prefixElementFunction, browsers => prefix(['element'], { browsers, feature: 'css-element-function', props: [ 'background', 'background-image', 'border-image', 'mask', 'list-style', 'list-style-image', 'content', 'mask-image' ] }) ) // Multicolumns let prefixMulticolumns = require('caniuse-lite/data/features/multicolumn') f(prefixMulticolumns, browsers => { prefix( [ 'columns', 'column-width', 'column-gap', 'column-rule', 'column-rule-color', 'column-rule-width', 'column-count', 'column-rule-style', 'column-span', 'column-fill' ], { browsers, feature: 'multicolumn' } ) let noff = browsers.filter(i => !/firefox/.test(i)) prefix(['break-before', 'break-after', 'break-inside'], { browsers: noff, feature: 'multicolumn' }) }) // User select let prefixUserSelect = require('caniuse-lite/data/features/user-select-none') f(prefixUserSelect, browsers => prefix(['user-select'], { browsers, feature: 'user-select-none', mistakes: ['-khtml-'] }) ) // Flexible Box Layout let prefixFlexbox = require('caniuse-lite/data/features/flexbox') f(prefixFlexbox, { match: /a\sx/ }, browsers => { browsers = browsers.map(i => { if (/ie|firefox/.test(i)) { return i } else { return `${i} 2009` } }) prefix(['display-flex', 'inline-flex'], { browsers, feature: 'flexbox', props: ['display'] }) prefix(['flex', 'flex-grow', 'flex-shrink', 'flex-basis'], { browsers, feature: 'flexbox' }) prefix( [ 'flex-direction', 'flex-wrap', 'flex-flow', 'justify-content', 'order', 'align-items', 'align-self', 'align-content' ], { browsers, feature: 'flexbox' } ) }) f(prefixFlexbox, { match: /y\sx/ }, browsers => { add(['display-flex', 'inline-flex'], { browsers, feature: 'flexbox' }) add(['flex', 'flex-grow', 'flex-shrink', 'flex-basis'], { browsers, feature: 'flexbox' }) add( [ 'flex-direction', 'flex-wrap', 'flex-flow', 'justify-content', 'order', 'align-items', 'align-self', 'align-content' ], { browsers, feature: 'flexbox' } ) }) // calc() unit let prefixCalc = require('caniuse-lite/data/features/calc') f(prefixCalc, browsers => prefix(['calc'], { browsers, feature: 'calc', props: ['*'] }) ) // Background options let prefixBackgroundOptions = require('caniuse-lite/data/features/background-img-opts') f(prefixBackgroundOptions, browsers => prefix(['background-origin', 'background-size'], { browsers, feature: 'background-img-opts' }) ) // background-clip: text let prefixBackgroundClipText = require('caniuse-lite/data/features/background-clip-text') f(prefixBackgroundClipText, browsers => prefix(['background-clip'], { browsers, feature: 'background-clip-text' }) ) // Font feature settings let prefixFontFeature = require('caniuse-lite/data/features/font-feature') f(prefixFontFeature, browsers => prefix( [ 'font-feature-settings', 'font-variant-ligatures', 'font-language-override' ], { browsers, feature: 'font-feature' } ) ) // CSS font-kerning property let prefixFontKerning = require('caniuse-lite/data/features/font-kerning') f(prefixFontKerning, browsers => prefix(['font-kerning'], { browsers, feature: 'font-kerning' }) ) // Border image let prefixBorderImage = require('caniuse-lite/data/features/border-image') f(prefixBorderImage, browsers => prefix(['border-image'], { browsers, feature: 'border-image' }) ) // Selection selector let prefixSelection = require('caniuse-lite/data/features/css-selection') f(prefixSelection, browsers => prefix(['::selection'], { browsers, feature: 'css-selection', selector: true }) ) // Placeholder selector let prefixPlaceholder = require('caniuse-lite/data/features/css-placeholder') f(prefixPlaceholder, browsers => { prefix(['::placeholder'], { browsers: browsers.concat(['ie 10 old', 'ie 11 old', 'firefox 18 old']), feature: 'css-placeholder', selector: true }) }) // Placeholder-shown selector let prefixPlaceholderShown = require('caniuse-lite/data/features/css-placeholder-shown') f(prefixPlaceholderShown, browsers => { prefix([':placeholder-shown'], { browsers, feature: 'css-placeholder-shown', selector: true }) }) // Hyphenation let prefixHyphens = require('caniuse-lite/data/features/css-hyphens') f(prefixHyphens, browsers => prefix(['hyphens'], { browsers, feature: 'css-hyphens' }) ) // Fullscreen selector let prefixFullscreen = require('caniuse-lite/data/features/fullscreen') f(prefixFullscreen, browsers => prefix([':fullscreen'], { browsers, feature: 'fullscreen', selector: true }) ) // ::backdrop pseudo-element // https://caniuse.com/mdn-css_selectors_backdrop let prefixBackdrop = require('caniuse-lite/data/features/mdn-css-backdrop-pseudo-element') f(prefixBackdrop, browsers => prefix(['::backdrop'], { browsers, feature: 'backdrop', selector: true }) ) // File selector button let prefixFileSelectorButton = require('caniuse-lite/data/features/css-file-selector-button') f(prefixFileSelectorButton, browsers => prefix(['::file-selector-button'], { browsers, feature: 'file-selector-button', selector: true }) ) // :autofill let prefixAutofill = require('caniuse-lite/data/features/css-autofill') f(prefixAutofill, browsers => prefix([':autofill'], { browsers, feature: 'css-autofill', selector: true }) ) // Tab size let prefixTabsize = require('caniuse-lite/data/features/css3-tabsize') f(prefixTabsize, browsers => prefix(['tab-size'], { browsers, feature: 'css3-tabsize' }) ) // Intrinsic & extrinsic sizing let prefixIntrinsic = require('caniuse-lite/data/features/intrinsic-width') let sizeProps = [ 'width', 'min-width', 'max-width', 'height', 'min-height', 'max-height', 'inline-size', 'min-inline-size', 'max-inline-size', 'block-size', 'min-block-size', 'max-block-size', 'grid', 'grid-template', 'grid-template-rows', 'grid-template-columns', 'grid-auto-columns', 'grid-auto-rows' ] f(prefixIntrinsic, browsers => prefix(['max-content', 'min-content'], { browsers, feature: 'intrinsic-width', props: sizeProps }) ) f(prefixIntrinsic, { match: /x|\s#4/ }, browsers => prefix(['fill', 'fill-available'], { browsers, feature: 'intrinsic-width', props: sizeProps }) ) f(prefixIntrinsic, { match: /x|\s#5/ }, browsers => { let ffFix = browsers.filter(i => { let [name, version] = i.split(' ') if (name === 'firefox' || name === 'and_ff') { return parseInt(version) < 94 } else { return true } }) return prefix(['fit-content'], { browsers: ffFix, feature: 'intrinsic-width', props: sizeProps }) }) // Stretch value let prefixStretch = require('caniuse-lite/data/features/css-width-stretch') f(prefixStretch, browsers => { f(prefixIntrinsic, { match: /x|\s#2/ }, firefox => { browsers = browsers.concat(firefox) }) return prefix(['stretch'], { browsers, feature: 'css-width-stretch', props: sizeProps }) }) // Zoom cursors let prefixCursorsNew = require('caniuse-lite/data/features/css3-cursors-newer') f(prefixCursorsNew, browsers => prefix(['zoom-in', 'zoom-out'], { browsers, feature: 'css3-cursors-newer', props: ['cursor'] }) ) // Grab cursors let prefixCursorsGrab = require('caniuse-lite/data/features/css3-cursors-grab') f(prefixCursorsGrab, browsers => prefix(['grab', 'grabbing'], { browsers, feature: 'css3-cursors-grab', props: ['cursor'] }) ) // Sticky position let prefixSticky = require('caniuse-lite/data/features/css-sticky') f(prefixSticky, browsers => prefix(['sticky'], { browsers, feature: 'css-sticky', props: ['position'] }) ) // Pointer Events let prefixPointer = require('caniuse-lite/data/features/pointer') f(prefixPointer, browsers => prefix(['touch-action'], { browsers, feature: 'pointer' }) ) // Text decoration let prefixDecoration = require('caniuse-lite/data/features/text-decoration') f(prefixDecoration, { match: /x.*#[235]/ }, browsers => prefix(['text-decoration-skip', 'text-decoration-skip-ink'], { browsers, feature: 'text-decoration' }) ) let prefixDecorationShorthand = require('caniuse-lite/data/features/mdn-text-decoration-shorthand') f(prefixDecorationShorthand, browsers => prefix(['text-decoration'], { browsers, feature: 'text-decoration' }) ) let prefixDecorationColor = require('caniuse-lite/data/features/mdn-text-decoration-color') f(prefixDecorationColor, browsers => prefix(['text-decoration-color'], { browsers, feature: 'text-decoration' }) ) let prefixDecorationLine = require('caniuse-lite/data/features/mdn-text-decoration-line') f(prefixDecorationLine, browsers => prefix(['text-decoration-line'], { browsers, feature: 'text-decoration' }) ) let prefixDecorationStyle = require('caniuse-lite/data/features/mdn-text-decoration-style') f(prefixDecorationStyle, browsers => prefix(['text-decoration-style'], { browsers, feature: 'text-decoration' }) ) // Text Size Adjust let prefixTextSizeAdjust = require('caniuse-lite/data/features/text-size-adjust') f(prefixTextSizeAdjust, browsers => prefix(['text-size-adjust'], { browsers, feature: 'text-size-adjust' }) ) // CSS Masks let prefixCssMasks = require('caniuse-lite/data/features/css-masks') f(prefixCssMasks, browsers => { prefix( [ 'mask-clip', 'mask-composite', 'mask-image', 'mask-origin', 'mask-repeat', 'mask-border-repeat', 'mask-border-source' ], { browsers, feature: 'css-masks' } ) prefix( [ 'mask', 'mask-position', 'mask-size', 'mask-border', 'mask-border-outset', 'mask-border-width', 'mask-border-slice' ], { browsers, feature: 'css-masks' } ) }) // CSS clip-path property let prefixClipPath = require('caniuse-lite/data/features/css-clip-path') f(prefixClipPath, browsers => prefix(['clip-path'], { browsers, feature: 'css-clip-path' }) ) // Fragmented Borders and Backgrounds let prefixBoxdecoration = require('caniuse-lite/data/features/css-boxdecorationbreak') f(prefixBoxdecoration, browsers => prefix(['box-decoration-break'], { browsers, feature: 'css-boxdecorationbreak' }) ) // CSS3 object-fit/object-position let prefixObjectFit = require('caniuse-lite/data/features/object-fit') f(prefixObjectFit, browsers => prefix(['object-fit', 'object-position'], { browsers, feature: 'object-fit' }) ) // CSS Shapes let prefixShapes = require('caniuse-lite/data/features/css-shapes') f(prefixShapes, browsers => prefix(['shape-margin', 'shape-outside', 'shape-image-threshold'], { browsers, feature: 'css-shapes' }) ) // CSS3 text-overflow let prefixTextOverflow = require('caniuse-lite/data/features/text-overflow') f(prefixTextOverflow, browsers => prefix(['text-overflow'], { browsers, feature: 'text-overflow' }) ) // Viewport at-rule let prefixDeviceadaptation = require('caniuse-lite/data/features/css-deviceadaptation') f(prefixDeviceadaptation, browsers => prefix(['@viewport'], { browsers, feature: 'css-deviceadaptation' }) ) // Resolution Media Queries let prefixResolut = require('caniuse-lite/data/features/css-media-resolution') f(prefixResolut, { match: /( x($| )|a #2)/ }, browsers => prefix(['@resolution'], { browsers, feature: 'css-media-resolution' }) ) // CSS text-align-last let prefixTextAlignLast = require('caniuse-lite/data/features/css-text-align-last') f(prefixTextAlignLast, browsers => prefix(['text-align-last'], { browsers, feature: 'css-text-align-last' }) ) // Crisp Edges Image Rendering Algorithm let prefixCrispedges = require('caniuse-lite/data/features/css-crisp-edges') f(prefixCrispedges, { match: /y x|a x #1/ }, browsers => prefix(['pixelated'], { browsers, feature: 'css-crisp-edges', props: ['image-rendering'] }) ) f(prefixCrispedges, { match: /a x #2/ }, browsers => prefix(['image-rendering'], { browsers, feature: 'css-crisp-edges' }) ) // Logical Properties let prefixLogicalProps = require('caniuse-lite/data/features/css-logical-props') f(prefixLogicalProps, browsers => prefix( [ 'border-inline-start', 'border-inline-end', 'margin-inline-start', 'margin-inline-end', 'padding-inline-start', 'padding-inline-end' ], { browsers, feature: 'css-logical-props' } ) ) f(prefixLogicalProps, { match: /x\s#2/ }, browsers => prefix( [ 'border-block-start', 'border-block-end', 'margin-block-start', 'margin-block-end', 'padding-block-start', 'padding-block-end' ], { browsers, feature: 'css-logical-props' } ) ) // CSS appearance let prefixAppearance = require('caniuse-lite/data/features/css-appearance') f(prefixAppearance, { match: /#2|x/ }, browsers => prefix(['appearance'], { browsers, feature: 'css-appearance' }) ) // CSS Scroll snap points let prefixSnappoints = require('caniuse-lite/data/features/css-snappoints') f(prefixSnappoints, browsers => prefix( [ 'scroll-snap-type', 'scroll-snap-coordinate', 'scroll-snap-destination', 'scroll-snap-points-x', 'scroll-snap-points-y' ], { browsers, feature: 'css-snappoints' } ) ) // CSS Regions let prefixRegions = require('caniuse-lite/data/features/css-regions') f(prefixRegions, browsers => prefix(['flow-into', 'flow-from', 'region-fragment'], { browsers, feature: 'css-regions' }) ) // CSS image-set let prefixImageSet = require('caniuse-lite/data/features/css-image-set') f(prefixImageSet, browsers => prefix(['image-set'], { browsers, feature: 'css-image-set', props: [ 'background', 'background-image', 'border-image', 'cursor', 'mask', 'mask-image', 'list-style', 'list-style-image', 'content' ] }) ) // Writing Mode let prefixWritingMode = require('caniuse-lite/data/features/css-writing-mode') f(prefixWritingMode, { match: /a|x/ }, browsers => prefix(['writing-mode'], { browsers, feature: 'css-writing-mode' }) ) // Cross-Fade Function let prefixCrossFade = require('caniuse-lite/data/features/css-cross-fade') f(prefixCrossFade, browsers => prefix(['cross-fade'], { browsers, feature: 'css-cross-fade', props: [ 'background', 'background-image', 'border-image', 'mask', 'list-style', 'list-style-image', 'content', 'mask-image' ] }) ) // Read Only selector let prefixReadOnly = require('caniuse-lite/data/features/css-read-only-write') f(prefixReadOnly, browsers => prefix([':read-only', ':read-write'], { browsers, feature: 'css-read-only-write', selector: true }) ) // Text Emphasize let prefixTextEmphasis = require('caniuse-lite/data/features/text-emphasis') f(prefixTextEmphasis, browsers => prefix( [ 'text-emphasis', 'text-emphasis-position', 'text-emphasis-style', 'text-emphasis-color' ], { browsers, feature: 'text-emphasis' } ) ) // CSS Grid Layout let prefixGrid = require('caniuse-lite/data/features/css-grid') f(prefixGrid, browsers => { prefix(['display-grid', 'inline-grid'], { browsers, feature: 'css-grid', props: ['display'] }) prefix( [ 'grid-template-columns', 'grid-template-rows', 'grid-row-start', 'grid-column-start', 'grid-row-end', 'grid-column-end', 'grid-row', 'grid-column', 'grid-area', 'grid-template', 'grid-template-areas', 'place-self' ], { browsers, feature: 'css-grid' } ) }) f(prefixGrid, { match: /a x/ }, browsers => prefix(['grid-column-align', 'grid-row-align'], { browsers, feature: 'css-grid' }) ) // CSS text-spacing let prefixTextSpacing = require('caniuse-lite/data/features/css-text-spacing') f(prefixTextSpacing, browsers => prefix(['text-spacing'], { browsers, feature: 'css-text-spacing' }) ) // :any-link selector let prefixAnyLink = require('caniuse-lite/data/features/css-any-link') f(prefixAnyLink, browsers => prefix([':any-link'], { browsers, feature: 'css-any-link', selector: true }) ) // unicode-bidi let bidiIsolate = require('caniuse-lite/data/features/mdn-css-unicode-bidi-isolate') f(bidiIsolate, browsers => prefix(['isolate'], { browsers, feature: 'css-unicode-bidi', props: ['unicode-bidi'] }) ) let bidiPlaintext = require('caniuse-lite/data/features/mdn-css-unicode-bidi-plaintext') f(bidiPlaintext, browsers => prefix(['plaintext'], { browsers, feature: 'css-unicode-bidi', props: ['unicode-bidi'] }) ) let bidiOverride = require('caniuse-lite/data/features/mdn-css-unicode-bidi-isolate-override') f(bidiOverride, { match: /y x/ }, browsers => prefix(['isolate-override'], { browsers, feature: 'css-unicode-bidi', props: ['unicode-bidi'] }) ) // overscroll-behavior selector let prefixOverscroll = require('caniuse-lite/data/features/css-overscroll-behavior') f(prefixOverscroll, { match: /a #1/ }, browsers => prefix(['overscroll-behavior'], { browsers, feature: 'css-overscroll-behavior' }) ) // text-orientation let prefixTextOrientation = require('caniuse-lite/data/features/css-text-orientation') f(prefixTextOrientation, browsers => prefix(['text-orientation'], { browsers, feature: 'css-text-orientation' }) ) // print-color-adjust let prefixPrintAdjust = require('caniuse-lite/data/features/css-print-color-adjust') f(prefixPrintAdjust, browsers => prefix(['print-color-adjust', 'color-adjust'], { browsers, feature: 'css-print-color-adjust' }) ) ================================================ FILE: eslint.config.mjs ================================================ import loguxConfig from '@logux/eslint-config' export default [ { ignores: ['coverage'] }, ...loguxConfig, { rules: { 'n/prefer-node-protocol': 'off', 'no-console': 'off' } }, { files: ['bin/autoprefixer'], rules: { 'n/global-require': 'off', 'n/no-unsupported-features/es-syntax': 'off' } }, { files: ['data/prefixes.js'], rules: { 'import/order': 'off' } } ] ================================================ FILE: lib/at-rule.js ================================================ let Prefixer = require('./prefixer') class AtRule extends Prefixer { /** * Clone and add prefixes for at-rule */ add(rule, prefix) { let prefixed = prefix + rule.name let already = rule.parent.some( i => i.name === prefixed && i.params === rule.params ) if (already) { return undefined } let cloned = this.clone(rule, { name: prefixed }) return rule.parent.insertBefore(rule, cloned) } /** * Clone node with prefixes */ process(node) { let parent = this.parentPrefix(node) for (let prefix of this.prefixes) { if (!parent || parent === prefix) { this.add(node, prefix) } } } } module.exports = AtRule ================================================ FILE: lib/autoprefixer.d.ts ================================================ import { Plugin } from 'postcss' import { Stats } from 'browserslist' declare function autoprefixer( ...args: [...T, autoprefixer.Options] ): Plugin & autoprefixer.ExportedAPI declare function autoprefixer( browsers: string[], options?: autoprefixer.Options ): Plugin & autoprefixer.ExportedAPI declare function autoprefixer( options?: autoprefixer.Options ): Plugin & autoprefixer.ExportedAPI declare namespace autoprefixer { type GridValue = 'autoplace' | 'no-autoplace' interface Options { /** environment for `Browserslist` */ env?: string /** should Autoprefixer use Visual Cascade, if CSS is uncompressed */ cascade?: boolean /** should Autoprefixer add prefixes. */ add?: boolean /** should Autoprefixer [remove outdated] prefixes */ remove?: boolean /** should Autoprefixer add prefixes for @supports parameters. */ supports?: boolean /** should Autoprefixer add prefixes for flexbox properties */ flexbox?: boolean | 'no-2009' /** should Autoprefixer add IE 10-11 prefixes for Grid Layout properties */ grid?: boolean | GridValue /** custom usage statistics for > 10% in my stats browsers query */ stats?: Stats /** * list of queries for target browsers. * Try to not use it. * The best practice is to use `.browserslistrc` config or `browserslist` key in `package.json` * to share target browsers with Babel, ESLint and Stylelint */ overrideBrowserslist?: string | string[] /** do not raise error on unknown browser version in `Browserslist` config. */ ignoreUnknownVersions?: boolean } interface ExportedAPI { /** Autoprefixer data */ data: { browsers: { [browser: string]: object | undefined } prefixes: { [prefixName: string]: object | undefined } } /** Autoprefixer default browsers */ defaults: string[] /** Inspect with default Autoprefixer */ info(options?: { from?: string }): string options: Options browsers: string | string[] } /** Autoprefixer data */ let data: ExportedAPI['data'] /** Autoprefixer default browsers */ let defaults: ExportedAPI['defaults'] /** Inspect with default Autoprefixer */ let info: ExportedAPI['info'] let postcss: true } declare global { namespace NodeJS { interface ProcessEnv { AUTOPREFIXER_GRID?: autoprefixer.GridValue } } } export = autoprefixer ================================================ FILE: lib/autoprefixer.js ================================================ let browserslist = require('browserslist') let { agents } = require('caniuse-lite/dist/unpacker/agents') let pico = require('picocolors') let dataPrefixes = require('../data/prefixes') let Browsers = require('./browsers') let getInfo = require('./info') let Prefixes = require('./prefixes') let autoprefixerData = { browsers: agents, prefixes: dataPrefixes } const WARNING = '\n' + ' Replace Autoprefixer `browsers` option to Browserslist config.\n' + ' Use `browserslist` key in `package.json` or `.browserslistrc` file.\n' + '\n' + ' Using `browsers` option can cause errors. Browserslist config can\n' + ' be used for Babel, Autoprefixer, postcss-normalize and other tools.\n' + '\n' + ' If you really need to use option, rename it to `overrideBrowserslist`.\n' + '\n' + ' Learn more at:\n' + ' https://github.com/browserslist/browserslist#readme\n' + ' https://twitter.com/browserslist\n' + '\n' function isPlainObject(obj) { return Object.prototype.toString.apply(obj) === '[object Object]' } let cache = new Map() function timeCapsule(result, prefixes) { if (prefixes.browsers.selected.length === 0) { return } if (prefixes.add.selectors.length > 0) { return } if (Object.keys(prefixes.add).length > 2) { return } /* c8 ignore next 11 */ result.warn( 'Autoprefixer target browsers do not need any prefixes.' + 'You do not need Autoprefixer anymore.\n' + 'Check your Browserslist config to be sure that your targets ' + 'are set up correctly.\n' + '\n' + ' Learn more at:\n' + ' https://github.com/postcss/autoprefixer#readme\n' + ' https://github.com/browserslist/browserslist#readme\n' + '\n' ) } module.exports = plugin function plugin(...reqs) { let options if (reqs.length === 1 && isPlainObject(reqs[0])) { options = reqs[0] reqs = undefined } else if (reqs.length === 0 || (reqs.length === 1 && !reqs[0])) { reqs = undefined } else if (reqs.length <= 2 && (Array.isArray(reqs[0]) || !reqs[0])) { options = reqs[1] reqs = reqs[0] } else if (typeof reqs[reqs.length - 1] === 'object') { options = reqs.pop() } if (!options) { options = {} } if (options.browser) { throw new Error( 'Change `browser` option to `overrideBrowserslist` in Autoprefixer' ) } else if (options.browserslist) { throw new Error( 'Change `browserslist` option to `overrideBrowserslist` in Autoprefixer' ) } if (options.overrideBrowserslist) { reqs = options.overrideBrowserslist } else if (options.browsers) { if (typeof console !== 'undefined' && console.warn) { console.warn( pico.red(WARNING.replace(/`[^`]+`/g, i => pico.yellow(i.slice(1, -1)))) ) } reqs = options.browsers } let brwlstOpts = { env: options.env, ignoreUnknownVersions: options.ignoreUnknownVersions, stats: options.stats } function loadPrefixes(opts) { let d = autoprefixerData let browsers = new Browsers(d.browsers, reqs, opts, brwlstOpts) let key = browsers.selected.join(', ') + JSON.stringify(options) if (!cache.has(key)) { cache.set(key, new Prefixes(d.prefixes, browsers, options)) } return cache.get(key) } return { browsers: reqs, info(opts) { opts = opts || {} opts.from = opts.from || process.cwd() return getInfo(loadPrefixes(opts)) }, options, postcssPlugin: 'autoprefixer', prepare(result) { let prefixes = loadPrefixes({ env: options.env, from: result.opts.from }) return { OnceExit(root) { timeCapsule(result, prefixes) if (options.remove !== false) { prefixes.processor.remove(root, result) } if (options.add !== false) { prefixes.processor.add(root, result) } } } } } } plugin.postcss = true /** * Autoprefixer data */ plugin.data = autoprefixerData /** * Autoprefixer default browsers */ plugin.defaults = browserslist.defaults /** * Inspect with default Autoprefixer */ plugin.info = () => plugin().info() ================================================ FILE: lib/brackets.js ================================================ function last(array) { return array[array.length - 1] } let brackets = { /** * Parse string to nodes tree */ parse(str) { let current = [''] let stack = [current] for (let sym of str) { if (sym === '(') { current = [''] last(stack).push(current) stack.push(current) continue } if (sym === ')') { stack.pop() current = last(stack) current.push('') continue } current[current.length - 1] += sym } return stack[0] }, /** * Generate output string by nodes tree */ stringify(ast) { let result = '' for (let i of ast) { if (typeof i === 'object') { result += `(${brackets.stringify(i)})` continue } result += i } return result } } module.exports = brackets ================================================ FILE: lib/browsers.js ================================================ let browserslist = require('browserslist') let { agents } = require('caniuse-lite/dist/unpacker/agents') let utils = require('./utils') class Browsers { constructor(data, requirements, options, browserslistOpts) { this.data = data this.options = options || {} this.browserslistOpts = browserslistOpts || {} this.selected = this.parse(requirements) } /** * Return all prefixes for default browser data */ static prefixes() { if (this.prefixesCache) { return this.prefixesCache } this.prefixesCache = [] for (let name in agents) { this.prefixesCache.push(`-${agents[name].prefix}-`) } this.prefixesCache = utils .uniq(this.prefixesCache) .sort((a, b) => b.length - a.length) return this.prefixesCache } /** * Check is value contain any possible prefix */ static withPrefix(value) { if (!this.prefixesRegexp) { this.prefixesRegexp = new RegExp(this.prefixes().join('|')) } return this.prefixesRegexp.test(value) } /** * Is browser is selected by requirements */ isSelected(browser) { return this.selected.includes(browser) } /** * Return browsers selected by requirements */ parse(requirements) { let opts = {} for (let i in this.browserslistOpts) { opts[i] = this.browserslistOpts[i] } opts.path = this.options.from return browserslist(requirements, opts) } /** * Return prefix for selected browser */ prefix(browser) { let [name, version] = browser.split(' ') let data = this.data[name] let prefix = data.prefix_exceptions && data.prefix_exceptions[version] if (!prefix) { prefix = data.prefix } return `-${prefix}-` } } module.exports = Browsers ================================================ FILE: lib/declaration.js ================================================ let Browsers = require('./browsers') let Prefixer = require('./prefixer') let utils = require('./utils') class Declaration extends Prefixer { /** * Clone and add prefixes for declaration */ add(decl, prefix, prefixes, result) { let prefixed = this.prefixed(decl.prop, prefix) if ( this.isAlready(decl, prefixed) || this.otherPrefixes(decl.value, prefix) ) { return undefined } return this.insert(decl, prefix, prefixes, result) } /** * Calculate indentation to create visual cascade */ calcBefore(prefixes, decl, prefix = '') { let max = this.maxPrefixed(prefixes, decl) let diff = max - utils.removeNote(prefix).length let before = decl.raw('before') if (diff > 0) { before += Array(diff).fill(' ').join('') } return before } /** * Always true, because we already get prefixer by property name */ check(/* decl */) { return true } /** * Clone and insert new declaration */ insert(decl, prefix, prefixes) { let cloned = this.set(this.clone(decl), prefix) if (!cloned) return undefined let already = decl.parent.some( i => i.prop === cloned.prop && i.value === cloned.value ) if (already) { return undefined } if (this.needCascade(decl)) { cloned.raws.before = this.calcBefore(prefixes, decl, prefix) } return decl.parent.insertBefore(decl, cloned) } /** * Did this declaration has this prefix above */ isAlready(decl, prefixed) { let already = this.all.group(decl).up(i => i.prop === prefixed) if (!already) { already = this.all.group(decl).down(i => i.prop === prefixed) } return already } /** * Return maximum length of possible prefixed property */ maxPrefixed(prefixes, decl) { if (decl._autoprefixerMax) { return decl._autoprefixerMax } let max = 0 for (let prefix of prefixes) { prefix = utils.removeNote(prefix) if (prefix.length > max) { max = prefix.length } } decl._autoprefixerMax = max return decl._autoprefixerMax } /** * Should we use visual cascade for prefixes */ needCascade(decl) { if (!decl._autoprefixerCascade) { decl._autoprefixerCascade = this.all.options.cascade !== false && decl.raw('before').includes('\n') } return decl._autoprefixerCascade } /** * Return unprefixed version of property */ normalize(prop) { return prop } /** * Return list of prefixed properties to clean old prefixes */ old(prop, prefix) { return [this.prefixed(prop, prefix)] } /** * Check `value`, that it contain other prefixes, rather than `prefix` */ otherPrefixes(value, prefix) { for (let other of Browsers.prefixes()) { if (other === prefix) { continue } if (value.includes(other)) { return value.replace(/var\([^)]+\)/, '').includes(other) } } return false } /** * Return prefixed version of property */ prefixed(prop, prefix) { return prefix + prop } /** * Add spaces for visual cascade */ process(decl, result) { if (!this.needCascade(decl)) { super.process(decl, result) return } let prefixes = super.process(decl, result) if (!prefixes || !prefixes.length) { return } this.restoreBefore(decl) decl.raws.before = this.calcBefore(prefixes, decl) } /** * Remove visual cascade */ restoreBefore(decl) { let lines = decl.raw('before').split('\n') let min = lines[lines.length - 1] this.all.group(decl).up(prefixed => { let array = prefixed.raw('before').split('\n') let last = array[array.length - 1] if (last.length < min.length) { min = last } }) lines[lines.length - 1] = min decl.raws.before = lines.join('\n') } /** * Set prefix to declaration */ set(decl, prefix) { decl.prop = this.prefixed(decl.prop, prefix) return decl } } module.exports = Declaration ================================================ FILE: lib/hacks/align-content.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class AlignContent extends Declaration { /** * Return property name by final spec */ normalize() { return 'align-content' } /** * Change property name for 2012 spec */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2012) { return prefix + 'flex-line-pack' } return super.prefixed(prop, prefix) } /** * Change value for 2012 spec and ignore prefix for 2009 */ set(decl, prefix) { let spec = flexSpec(prefix)[0] if (spec === 2012) { decl.value = AlignContent.oldValues[decl.value] || decl.value return super.set(decl, prefix) } if (spec === 'final') { return super.set(decl, prefix) } return undefined } } AlignContent.names = ['align-content', 'flex-line-pack'] AlignContent.oldValues = { 'flex-end': 'end', 'flex-start': 'start', 'space-around': 'distribute', 'space-between': 'justify' } module.exports = AlignContent ================================================ FILE: lib/hacks/align-items.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class AlignItems extends Declaration { /** * Return property name by final spec */ normalize() { return 'align-items' } /** * Change property name for 2009 and 2012 specs */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2009) { return prefix + 'box-align' } if (spec === 2012) { return prefix + 'flex-align' } return super.prefixed(prop, prefix) } /** * Change value for 2009 and 2012 specs */ set(decl, prefix) { let spec = flexSpec(prefix)[0] if (spec === 2009 || spec === 2012) { decl.value = AlignItems.oldValues[decl.value] || decl.value } return super.set(decl, prefix) } } AlignItems.names = ['align-items', 'flex-align', 'box-align'] AlignItems.oldValues = { 'flex-end': 'end', 'flex-start': 'start' } module.exports = AlignItems ================================================ FILE: lib/hacks/align-self.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class AlignSelf extends Declaration { check(decl) { return ( decl.parent && !decl.parent.some(i => { return i.prop && i.prop.startsWith('grid-') }) ) } /** * Return property name by final spec */ normalize() { return 'align-self' } /** * Change property name for 2012 specs */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2012) { return prefix + 'flex-item-align' } return super.prefixed(prop, prefix) } /** * Change value for 2012 spec and ignore prefix for 2009 */ set(decl, prefix) { let spec = flexSpec(prefix)[0] if (spec === 2012) { decl.value = AlignSelf.oldValues[decl.value] || decl.value return super.set(decl, prefix) } if (spec === 'final') { return super.set(decl, prefix) } return undefined } } AlignSelf.names = ['align-self', 'flex-item-align'] AlignSelf.oldValues = { 'flex-end': 'end', 'flex-start': 'start' } module.exports = AlignSelf ================================================ FILE: lib/hacks/animation.js ================================================ let Declaration = require('../declaration') class Animation extends Declaration { /** * Don’t add prefixes for modern values. */ check(decl) { return !decl.value.split(/\s+/).some(i => { let lower = i.toLowerCase() return lower === 'reverse' || lower === 'alternate-reverse' }) } } Animation.names = ['animation', 'animation-direction'] module.exports = Animation ================================================ FILE: lib/hacks/appearance.js ================================================ let Declaration = require('../declaration') let utils = require('../utils') class Appearance extends Declaration { constructor(name, prefixes, all) { super(name, prefixes, all) if (this.prefixes) { this.prefixes = utils.uniq( this.prefixes.map(i => { if (i === '-ms-') { return '-webkit-' } return i }) ) } } } Appearance.names = ['appearance'] module.exports = Appearance ================================================ FILE: lib/hacks/autofill.js ================================================ let Selector = require('../selector') let utils = require('../utils') class Autofill extends Selector { constructor(name, prefixes, all) { super(name, prefixes, all) if (this.prefixes) { this.prefixes = utils.uniq(this.prefixes.map(() => '-webkit-')) } } /** * Return different selectors depend on prefix */ prefixed(prefix) { if (prefix === '-webkit-') { return ':-webkit-autofill' } return `:${prefix}autofill` } } Autofill.names = [':autofill'] module.exports = Autofill ================================================ FILE: lib/hacks/backdrop-filter.js ================================================ let Declaration = require('../declaration') let utils = require('../utils') class BackdropFilter extends Declaration { constructor(name, prefixes, all) { super(name, prefixes, all) if (this.prefixes) { this.prefixes = utils.uniq( this.prefixes.map(i => { return i === '-ms-' ? '-webkit-' : i }) ) } } } BackdropFilter.names = ['backdrop-filter'] module.exports = BackdropFilter ================================================ FILE: lib/hacks/background-clip.js ================================================ let Declaration = require('../declaration') let utils = require('../utils') class BackgroundClip extends Declaration { constructor(name, prefixes, all) { super(name, prefixes, all) if (this.prefixes) { this.prefixes = utils.uniq( this.prefixes.map(i => { return i === '-ms-' ? '-webkit-' : i }) ) } } check(decl) { return decl.value.toLowerCase() === 'text' } } BackgroundClip.names = ['background-clip'] module.exports = BackgroundClip ================================================ FILE: lib/hacks/background-size.js ================================================ let Declaration = require('../declaration') class BackgroundSize extends Declaration { /** * Duplication parameter for -webkit- browsers */ set(decl, prefix) { let value = decl.value.toLowerCase() if ( prefix === '-webkit-' && !value.includes(' ') && value !== 'contain' && value !== 'cover' ) { decl.value = decl.value + ' ' + decl.value } return super.set(decl, prefix) } } BackgroundSize.names = ['background-size'] module.exports = BackgroundSize ================================================ FILE: lib/hacks/block-logical.js ================================================ let Declaration = require('../declaration') class BlockLogical extends Declaration { /** * Return property name by spec */ normalize(prop) { if (prop.includes('-before')) { return prop.replace('-before', '-block-start') } return prop.replace('-after', '-block-end') } /** * Use old syntax for -moz- and -webkit- */ prefixed(prop, prefix) { if (prop.includes('-start')) { return prefix + prop.replace('-block-start', '-before') } return prefix + prop.replace('-block-end', '-after') } } BlockLogical.names = [ 'border-block-start', 'border-block-end', 'margin-block-start', 'margin-block-end', 'padding-block-start', 'padding-block-end', 'border-before', 'border-after', 'margin-before', 'margin-after', 'padding-before', 'padding-after' ] module.exports = BlockLogical ================================================ FILE: lib/hacks/border-image.js ================================================ let Declaration = require('../declaration') class BorderImage extends Declaration { /** * Remove fill parameter for prefixed declarations */ set(decl, prefix) { decl.value = decl.value.replace(/\s+fill(\s)/, '$1') return super.set(decl, prefix) } } BorderImage.names = ['border-image'] module.exports = BorderImage ================================================ FILE: lib/hacks/border-radius.js ================================================ let Declaration = require('../declaration') class BorderRadius extends Declaration { /** * Return unprefixed version of property */ normalize(prop) { return BorderRadius.toNormal[prop] || prop } /** * Change syntax, when add Mozilla prefix */ prefixed(prop, prefix) { if (prefix === '-moz-') { return prefix + (BorderRadius.toMozilla[prop] || prop) } return super.prefixed(prop, prefix) } } BorderRadius.names = ['border-radius'] BorderRadius.toMozilla = {} BorderRadius.toNormal = {} for (let ver of ['top', 'bottom']) { for (let hor of ['left', 'right']) { let normal = `border-${ver}-${hor}-radius` let mozilla = `border-radius-${ver}${hor}` BorderRadius.names.push(normal) BorderRadius.names.push(mozilla) BorderRadius.toMozilla[normal] = mozilla BorderRadius.toNormal[mozilla] = normal } } module.exports = BorderRadius ================================================ FILE: lib/hacks/break-props.js ================================================ let Declaration = require('../declaration') class BreakProps extends Declaration { /** * Don’t prefix some values */ insert(decl, prefix, prefixes) { if (decl.prop !== 'break-inside') { return super.insert(decl, prefix, prefixes) } if (/region/i.test(decl.value) || /page/i.test(decl.value)) { return undefined } return super.insert(decl, prefix, prefixes) } /** * Return property name by final spec */ normalize(prop) { if (prop.includes('inside')) { return 'break-inside' } if (prop.includes('before')) { return 'break-before' } return 'break-after' } /** * Change name for -webkit- and -moz- prefix */ prefixed(prop, prefix) { return `${prefix}column-${prop}` } /** * Change prefixed value for avoid-column and avoid-page */ set(decl, prefix) { if ( (decl.prop === 'break-inside' && decl.value === 'avoid-column') || decl.value === 'avoid-page' ) { decl.value = 'avoid' } return super.set(decl, prefix) } } BreakProps.names = [ 'break-inside', 'page-break-inside', 'column-break-inside', 'break-before', 'page-break-before', 'column-break-before', 'break-after', 'page-break-after', 'column-break-after' ] module.exports = BreakProps ================================================ FILE: lib/hacks/cross-fade.js ================================================ let list = require('postcss').list let Value = require('../value') class CrossFade extends Value { replace(string, prefix) { return list .space(string) .map(value => { if (value.slice(0, +this.name.length + 1) !== this.name + '(') { return value } let close = value.lastIndexOf(')') let after = value.slice(close + 1) let args = value.slice(this.name.length + 1, close) if (prefix === '-webkit-') { let match = args.match(/\d*.?\d+%?/) if (match) { args = args.slice(match[0].length).trim() args += `, ${match[0]}` } else { args += ', 0.5' } } return prefix + this.name + '(' + args + ')' + after }) .join(' ') } } CrossFade.names = ['cross-fade'] module.exports = CrossFade ================================================ FILE: lib/hacks/display-flex.js ================================================ let OldValue = require('../old-value') let Value = require('../value') let flexSpec = require('./flex-spec') class DisplayFlex extends Value { constructor(name, prefixes) { super(name, prefixes) if (name === 'display-flex') { this.name = 'flex' } } /** * Faster check for flex value */ check(decl) { return decl.prop === 'display' && decl.value === this.name } /** * Change value for old specs */ old(prefix) { let prefixed = this.prefixed(prefix) if (!prefixed) return undefined return new OldValue(this.name, prefixed) } /** * Return value by spec */ prefixed(prefix) { let spec, value ;[spec, prefix] = flexSpec(prefix) if (spec === 2009) { if (this.name === 'flex') { value = 'box' } else { value = 'inline-box' } } else if (spec === 2012) { if (this.name === 'flex') { value = 'flexbox' } else { value = 'inline-flexbox' } } else if (spec === 'final') { value = this.name } return prefix + value } /** * Add prefix to value depend on flebox spec version */ replace(string, prefix) { return this.prefixed(prefix) } } DisplayFlex.names = ['display-flex', 'inline-flex'] module.exports = DisplayFlex ================================================ FILE: lib/hacks/display-grid.js ================================================ let Value = require('../value') class DisplayGrid extends Value { constructor(name, prefixes) { super(name, prefixes) if (name === 'display-grid') { this.name = 'grid' } } /** * Faster check for flex value */ check(decl) { return decl.prop === 'display' && decl.value === this.name } } DisplayGrid.names = ['display-grid', 'inline-grid'] module.exports = DisplayGrid ================================================ FILE: lib/hacks/file-selector-button.js ================================================ let Selector = require('../selector') let utils = require('../utils') class FileSelectorButton extends Selector { constructor(name, prefixes, all) { super(name, prefixes, all) if (this.prefixes) { this.prefixes = utils.uniq(this.prefixes.map(() => '-webkit-')) } } /** * Return different selectors depend on prefix */ prefixed(prefix) { if (prefix === '-webkit-') { return '::-webkit-file-upload-button' } return `::${prefix}file-selector-button` } } FileSelectorButton.names = ['::file-selector-button'] module.exports = FileSelectorButton ================================================ FILE: lib/hacks/filter-value.js ================================================ let Value = require('../value') class FilterValue extends Value { constructor(name, prefixes) { super(name, prefixes) if (name === 'filter-function') { this.name = 'filter' } } } FilterValue.names = ['filter', 'filter-function'] module.exports = FilterValue ================================================ FILE: lib/hacks/filter.js ================================================ let Declaration = require('../declaration') class Filter extends Declaration { /** * Check is it Internet Explorer filter */ check(decl) { let v = decl.value return ( !v.toLowerCase().includes('alpha(') && !v.includes('DXImageTransform.Microsoft') && !v.includes('data:image/svg+xml') ) } } Filter.names = ['filter'] module.exports = Filter ================================================ FILE: lib/hacks/flex-basis.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class FlexBasis extends Declaration { /** * Return property name by final spec */ normalize() { return 'flex-basis' } /** * Return flex property for 2012 spec */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2012) { return prefix + 'flex-preferred-size' } return super.prefixed(prop, prefix) } /** * Ignore 2009 spec and use flex property for 2012 */ set(decl, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2012 || spec === 'final') { return super.set(decl, prefix) } return undefined } } FlexBasis.names = ['flex-basis', 'flex-preferred-size'] module.exports = FlexBasis ================================================ FILE: lib/hacks/flex-direction.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class FlexDirection extends Declaration { /** * Use two properties for 2009 spec */ insert(decl, prefix, prefixes) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec !== 2009) { return super.insert(decl, prefix, prefixes) } let already = decl.parent.some( i => i.prop === prefix + 'box-orient' || i.prop === prefix + 'box-direction' ) if (already) { return undefined } let v = decl.value let dir, orient if (v === 'inherit' || v === 'initial' || v === 'unset') { orient = v dir = v } else { orient = v.includes('row') ? 'horizontal' : 'vertical' dir = v.includes('reverse') ? 'reverse' : 'normal' } let cloned = this.clone(decl) cloned.prop = prefix + 'box-orient' cloned.value = orient if (this.needCascade(decl)) { cloned.raws.before = this.calcBefore(prefixes, decl, prefix) } decl.parent.insertBefore(decl, cloned) cloned = this.clone(decl) cloned.prop = prefix + 'box-direction' cloned.value = dir if (this.needCascade(decl)) { cloned.raws.before = this.calcBefore(prefixes, decl, prefix) } return decl.parent.insertBefore(decl, cloned) } /** * Return property name by final spec */ normalize() { return 'flex-direction' } /** * Clean two properties for 2009 spec */ old(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2009) { return [prefix + 'box-orient', prefix + 'box-direction'] } else { return super.old(prop, prefix) } } } FlexDirection.names = ['flex-direction', 'box-direction', 'box-orient'] module.exports = FlexDirection ================================================ FILE: lib/hacks/flex-flow.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class FlexFlow extends Declaration { /** * Use two properties for 2009 spec */ insert(decl, prefix, prefixes) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec !== 2009) { return super.insert(decl, prefix, prefixes) } let values = decl.value .split(/\s+/) .filter(i => i !== 'wrap' && i !== 'nowrap' && 'wrap-reverse') if (values.length === 0) { return undefined } let already = decl.parent.some( i => i.prop === prefix + 'box-orient' || i.prop === prefix + 'box-direction' ) if (already) { return undefined } let value = values[0] let orient = value.includes('row') ? 'horizontal' : 'vertical' let dir = value.includes('reverse') ? 'reverse' : 'normal' let cloned = this.clone(decl) cloned.prop = prefix + 'box-orient' cloned.value = orient if (this.needCascade(decl)) { cloned.raws.before = this.calcBefore(prefixes, decl, prefix) } decl.parent.insertBefore(decl, cloned) cloned = this.clone(decl) cloned.prop = prefix + 'box-direction' cloned.value = dir if (this.needCascade(decl)) { cloned.raws.before = this.calcBefore(prefixes, decl, prefix) } return decl.parent.insertBefore(decl, cloned) } } FlexFlow.names = ['flex-flow', 'box-direction', 'box-orient'] module.exports = FlexFlow ================================================ FILE: lib/hacks/flex-grow.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class Flex extends Declaration { /** * Return property name by final spec */ normalize() { return 'flex' } /** * Return flex property for 2009 and 2012 specs */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2009) { return prefix + 'box-flex' } if (spec === 2012) { return prefix + 'flex-positive' } return super.prefixed(prop, prefix) } } Flex.names = ['flex-grow', 'flex-positive'] module.exports = Flex ================================================ FILE: lib/hacks/flex-shrink.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class FlexShrink extends Declaration { /** * Return property name by final spec */ normalize() { return 'flex-shrink' } /** * Return flex property for 2012 spec */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2012) { return prefix + 'flex-negative' } return super.prefixed(prop, prefix) } /** * Ignore 2009 spec and use flex property for 2012 */ set(decl, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2012 || spec === 'final') { return super.set(decl, prefix) } return undefined } } FlexShrink.names = ['flex-shrink', 'flex-negative'] module.exports = FlexShrink ================================================ FILE: lib/hacks/flex-spec.js ================================================ /** * Return flexbox spec versions by prefix */ module.exports = function (prefix) { let spec if (prefix === '-webkit- 2009' || prefix === '-moz-') { spec = 2009 } else if (prefix === '-ms-') { spec = 2012 } else if (prefix === '-webkit-') { spec = 'final' } if (prefix === '-webkit- 2009') { prefix = '-webkit-' } return [spec, prefix] } ================================================ FILE: lib/hacks/flex-wrap.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class FlexWrap extends Declaration { /** * Don't add prefix for 2009 spec */ set(decl, prefix) { let spec = flexSpec(prefix)[0] if (spec !== 2009) { return super.set(decl, prefix) } return undefined } } FlexWrap.names = ['flex-wrap'] module.exports = FlexWrap ================================================ FILE: lib/hacks/flex.js ================================================ let list = require('postcss').list let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class Flex extends Declaration { /** * Return property name by final spec */ normalize() { return 'flex' } /** * Change property name for 2009 spec */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2009) { return prefix + 'box-flex' } return super.prefixed(prop, prefix) } /** * Spec 2009 supports only first argument * Spec 2012 disallows unitless basis */ set(decl, prefix) { let spec = flexSpec(prefix)[0] if (spec === 2009) { decl.value = list.space(decl.value)[0] decl.value = Flex.oldValues[decl.value] || decl.value return super.set(decl, prefix) } if (spec === 2012) { let components = list.space(decl.value) if (components.length === 3 && components[2] === '0') { decl.value = components.slice(0, 2).concat('0px').join(' ') } } return super.set(decl, prefix) } } Flex.names = ['flex', 'box-flex'] Flex.oldValues = { auto: '1', none: '0' } module.exports = Flex ================================================ FILE: lib/hacks/fullscreen.js ================================================ let Selector = require('../selector') class Fullscreen extends Selector { /** * Return different selectors depend on prefix */ prefixed(prefix) { if (prefix === '-webkit-') { return ':-webkit-full-screen' } if (prefix === '-moz-') { return ':-moz-full-screen' } return `:${prefix}fullscreen` } } Fullscreen.names = [':fullscreen'] module.exports = Fullscreen ================================================ FILE: lib/hacks/gradient.js ================================================ let parser = require('postcss-value-parser') let OldValue = require('../old-value') let utils = require('../utils') let Value = require('../value') let IS_DIRECTION = /top|left|right|bottom/gi class Gradient extends Value { /** * Do not add non-webkit prefixes for list-style and object */ add(decl, prefix) { let p = decl.prop if (p.includes('mask')) { if (prefix === '-webkit-' || prefix === '-webkit- old') { return super.add(decl, prefix) } } else if ( p === 'list-style' || p === 'list-style-image' || p === 'content' ) { if (prefix === '-webkit-' || prefix === '-webkit- old') { return super.add(decl, prefix) } } else { return super.add(decl, prefix) } return undefined } /** * Get div token from exists parameters */ cloneDiv(params) { for (let i of params) { if (i.type === 'div' && i.value === ',') { return i } } return { after: ' ', type: 'div', value: ',' } } /** * Change colors syntax to old webkit */ colorStops(params) { let result = [] for (let i = 0; i < params.length; i++) { let pos let param = params[i] let item if (i === 0) { continue } let color = parser.stringify(param[0]) if (param[1] && param[1].type === 'word') { pos = param[1].value } else if (param[2] && param[2].type === 'word') { pos = param[2].value } let stop if (i === 1 && (!pos || pos === '0%')) { stop = `from(${color})` } else if (i === params.length - 1 && (!pos || pos === '100%')) { stop = `to(${color})` } else if (pos) { stop = `color-stop(${pos}, ${color})` } else { stop = `color-stop(${color})` } let div = param[param.length - 1] params[i] = [{ type: 'word', value: stop }] if (div.type === 'div' && div.value === ',') { item = params[i].push(div) } result.push(item) } return result } /** * Change new direction to old */ convertDirection(params) { if (params.length > 0) { if (params[0].value === 'to') { this.fixDirection(params) } else if (params[0].value.includes('deg')) { this.fixAngle(params) } else if (this.isRadial(params)) { this.fixRadial(params) } } return params } /** * Add 90 degrees */ fixAngle(params) { let first = params[0].value first = parseFloat(first) first = Math.abs(450 - first) % 360 first = this.roundFloat(first, 3) params[0].value = `${first}deg` } /** * Replace `to top left` to `bottom right` */ fixDirection(params) { params.splice(0, 2) for (let param of params) { if (param.type === 'div') { break } if (param.type === 'word') { param.value = this.revertDirection(param.value) } } } /** * Fix radial direction syntax */ fixRadial(params) { let first = [] let second = [] let a, b, c, i, next for (i = 0; i < params.length - 2; i++) { a = params[i] b = params[i + 1] c = params[i + 2] if (a.type === 'space' && b.value === 'at' && c.type === 'space') { next = i + 3 break } else { first.push(a) } } let div for (i = next; i < params.length; i++) { if (params[i].type === 'div') { div = params[i] break } else { second.push(params[i]) } } params.splice(0, i, ...second, div, ...first) } /** * Look for at word */ isRadial(params) { let state = 'before' for (let param of params) { if (state === 'before' && param.type === 'space') { state = 'at' } else if (state === 'at' && param.value === 'at') { state = 'after' } else if (state === 'after' && param.type === 'space') { return true } else if (param.type === 'div') { break } else { state = 'before' } } return false } /** * Replace old direction to new */ newDirection(params) { if (params[0].value === 'to') { return params } IS_DIRECTION.lastIndex = 0 // reset search index of global regexp if (!IS_DIRECTION.test(params[0].value)) { return params } params.unshift( { type: 'word', value: 'to' }, { type: 'space', value: ' ' } ) for (let i = 2; i < params.length; i++) { if (params[i].type === 'div') { break } if (params[i].type === 'word') { params[i].value = this.revertDirection(params[i].value) } } return params } /** * Normalize angle */ normalize(nodes, gradientName) { if (!nodes[0]) return nodes if (/-?\d+(.\d+)?grad/.test(nodes[0].value)) { nodes[0].value = this.normalizeUnit(nodes[0].value, 400) } else if (/-?\d+(.\d+)?rad/.test(nodes[0].value)) { nodes[0].value = this.normalizeUnit(nodes[0].value, 2 * Math.PI) } else if (/-?\d+(.\d+)?turn/.test(nodes[0].value)) { nodes[0].value = this.normalizeUnit(nodes[0].value, 1) } else if (nodes[0].value.includes('deg')) { let num = parseFloat(nodes[0].value) num = (num % 360 + 360) % 360 nodes[0].value = `${num}deg` } if ( gradientName === 'linear-gradient' || gradientName === 'repeating-linear-gradient' ) { let direction = nodes[0].value // Unitless zero for `` values are allowed in CSS gradients and transforms. // Spec: https://github.com/w3c/csswg-drafts/commit/602789171429b2231223ab1e5acf8f7f11652eb3 if (direction === '0deg' || direction === '0') { nodes = this.replaceFirst(nodes, 'to', ' ', 'top') } else if (direction === '90deg') { nodes = this.replaceFirst(nodes, 'to', ' ', 'right') } else if (direction === '180deg') { nodes = this.replaceFirst(nodes, 'to', ' ', 'bottom') // default value } else if (direction === '270deg') { nodes = this.replaceFirst(nodes, 'to', ' ', 'left') } } return nodes } /** * Convert angle unit to deg */ normalizeUnit(str, full) { let num = parseFloat(str) let deg = (num / full) * 360 return `${deg}deg` } /** * Remove old WebKit gradient too */ old(prefix) { if (prefix === '-webkit-') { let type if (this.name === 'linear-gradient') { type = 'linear' } else if (this.name === 'repeating-linear-gradient') { type = 'repeating-linear' } else if (this.name === 'repeating-radial-gradient') { type = 'repeating-radial' } else { type = 'radial' } let string = '-gradient' let regexp = utils.regexp( `-webkit-(${type}-gradient|gradient\\(\\s*${type})`, false ) return new OldValue(this.name, prefix + this.name, string, regexp) } else { return super.old(prefix) } } /** * Change direction syntax to old webkit */ oldDirection(params) { let div = this.cloneDiv(params[0]) if (params[0][0].value !== 'to') { return params.unshift([ { type: 'word', value: Gradient.oldDirections.bottom }, div ]) } else { let words = [] for (let node of params[0].slice(2)) { if (node.type === 'word') { words.push(node.value.toLowerCase()) } } words = words.join(' ') let old = Gradient.oldDirections[words] || words params[0] = [{ type: 'word', value: old }, div] return params[0] } } /** * Convert to old webkit syntax */ oldWebkit(node) { let { nodes } = node let string = parser.stringify(node.nodes) if (this.name !== 'linear-gradient') { return false } if (nodes[0] && nodes[0].value.includes('deg')) { return false } if ( string.includes('px') || string.includes('-corner') || string.includes('-side') ) { return false } if (string.includes('var(')) { return false } let params = [[]] for (let i of nodes) { params[params.length - 1].push(i) if (i.type === 'div' && i.value === ',') { params.push([]) } } this.oldDirection(params) this.colorStops(params) node.nodes = [] for (let param of params) { node.nodes.push(...param) } node.nodes.unshift( { type: 'word', value: 'linear' }, this.cloneDiv(node.nodes) ) node.value = '-webkit-gradient' return true } /** * Change degrees for webkit prefix */ replace(string, prefix) { let ast = parser(string) for (let node of ast.nodes) { let gradientName = this.name // gradient name if (node.type === 'function' && node.value === gradientName) { node.nodes = this.newDirection(node.nodes) node.nodes = this.normalize(node.nodes, gradientName) if (prefix === '-webkit- old') { let changes = this.oldWebkit(node) if (!changes) { return false } } else { node.nodes = this.convertDirection(node.nodes) node.value = prefix + node.value } } } return ast.toString() } /** * Replace first token */ replaceFirst(params, ...words) { let prefix = words.map(i => { if (i === ' ') { return { type: 'space', value: i } } return { type: 'word', value: i } }) return prefix.concat(params.slice(1)) } revertDirection(word) { return Gradient.directions[word.toLowerCase()] || word } /** * Round float and save digits under dot */ roundFloat(float, digits) { return parseFloat(float.toFixed(digits)) } } Gradient.names = [ 'linear-gradient', 'repeating-linear-gradient', 'radial-gradient', 'repeating-radial-gradient' ] Gradient.directions = { bottom: 'top', left: 'right', right: 'left', top: 'bottom' // default value } // Direction to replace Gradient.oldDirections = { 'bottom': 'left top, left bottom', 'bottom left': 'right top, left bottom', 'bottom right': 'left top, right bottom', 'left': 'right top, left top', 'left bottom': 'right top, left bottom', 'left top': 'right bottom, left top', 'right': 'left top, right top', 'right bottom': 'left top, right bottom', 'right top': 'left bottom, right top', 'top': 'left bottom, left top', 'top left': 'right bottom, left top', 'top right': 'left bottom, right top' } module.exports = Gradient ================================================ FILE: lib/hacks/grid-area.js ================================================ let Declaration = require('../declaration') let utils = require('./grid-utils') class GridArea extends Declaration { /** * Translate grid-area to separate -ms- prefixed properties */ insert(decl, prefix, prefixes, result) { if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) let values = utils.parse(decl) let [rowStart, rowSpan] = utils.translate(values, 0, 2) let [columnStart, columnSpan] = utils.translate(values, 1, 3) ;[ ['grid-row', rowStart], ['grid-row-span', rowSpan], ['grid-column', columnStart], ['grid-column-span', columnSpan] ].forEach(([prop, value]) => { utils.insertDecl(decl, prop, value) }) utils.warnTemplateSelectorNotFound(decl, result) utils.warnIfGridRowColumnExists(decl, result) return undefined } } GridArea.names = ['grid-area'] module.exports = GridArea ================================================ FILE: lib/hacks/grid-column-align.js ================================================ let Declaration = require('../declaration') class GridColumnAlign extends Declaration { /** * Do not prefix flexbox values */ check(decl) { return !decl.value.includes('flex-') && decl.value !== 'baseline' } /** * Change IE property back */ normalize() { return 'justify-self' } /** * Change property name for IE */ prefixed(prop, prefix) { return prefix + 'grid-column-align' } } GridColumnAlign.names = ['grid-column-align'] module.exports = GridColumnAlign ================================================ FILE: lib/hacks/grid-end.js ================================================ let Declaration = require('../declaration') let { isPureNumber } = require('../utils') class GridEnd extends Declaration { /** * Change repeating syntax for IE */ insert(decl, prefix, prefixes, result) { if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) let clonedDecl = this.clone(decl) let startProp = decl.prop.replace(/end$/, 'start') let spanProp = prefix + decl.prop.replace(/end$/, 'span') if (decl.parent.some(i => i.prop === spanProp)) { return undefined } clonedDecl.prop = spanProp if (decl.value.includes('span')) { clonedDecl.value = decl.value.replace(/span\s/i, '') } else { let startDecl decl.parent.walkDecls(startProp, d => { startDecl = d }) if (startDecl) { if (isPureNumber(startDecl.value)) { let value = Number(decl.value) - Number(startDecl.value) + '' clonedDecl.value = value } else { return undefined } } else { decl.warn( result, `Can not prefix ${decl.prop} (${startProp} is not found)` ) } } decl.cloneBefore(clonedDecl) return undefined } } GridEnd.names = ['grid-row-end', 'grid-column-end'] module.exports = GridEnd ================================================ FILE: lib/hacks/grid-row-align.js ================================================ let Declaration = require('../declaration') class GridRowAlign extends Declaration { /** * Do not prefix flexbox values */ check(decl) { return !decl.value.includes('flex-') && decl.value !== 'baseline' } /** * Change IE property back */ normalize() { return 'align-self' } /** * Change property name for IE */ prefixed(prop, prefix) { return prefix + 'grid-row-align' } } GridRowAlign.names = ['grid-row-align'] module.exports = GridRowAlign ================================================ FILE: lib/hacks/grid-row-column.js ================================================ let Declaration = require('../declaration') let utils = require('./grid-utils') class GridRowColumn extends Declaration { /** * Translate grid-row / grid-column to separate -ms- prefixed properties */ insert(decl, prefix, prefixes) { if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) let values = utils.parse(decl) let [start, span] = utils.translate(values, 0, 1) let hasStartValueSpan = values[0] && values[0].includes('span') if (hasStartValueSpan) { span = values[0].join('').replace(/\D/g, '') } ;[ [decl.prop, start], [`${decl.prop}-span`, span] ].forEach(([prop, value]) => { utils.insertDecl(decl, prop, value) }) return undefined } } GridRowColumn.names = ['grid-row', 'grid-column'] module.exports = GridRowColumn ================================================ FILE: lib/hacks/grid-rows-columns.js ================================================ let Declaration = require('../declaration') let Processor = require('../processor') let { autoplaceGridItems, getGridGap, inheritGridGap, prefixTrackProp, prefixTrackValue } = require('./grid-utils') class GridRowsColumns extends Declaration { insert(decl, prefix, prefixes, result) { if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) let { parent, prop, value } = decl let isRowProp = prop.includes('rows') let isColumnProp = prop.includes('columns') let hasGridTemplate = parent.some( i => i.prop === 'grid-template' || i.prop === 'grid-template-areas' ) /** * Not to prefix rows declaration if grid-template(-areas) is present */ if (hasGridTemplate && isRowProp) { return false } let processor = new Processor({ options: {} }) let status = processor.gridStatus(parent, result) let gap = getGridGap(decl) gap = inheritGridGap(decl, gap) || gap let gapValue = isRowProp ? gap.row : gap.column if ((status === 'no-autoplace' || status === true) && !hasGridTemplate) { gapValue = null } let prefixValue = prefixTrackValue({ gap: gapValue, value }) /** * Insert prefixes */ decl.cloneBefore({ prop: prefixTrackProp({ prefix, prop }), value: prefixValue }) let autoflow = parent.nodes.find(i => i.prop === 'grid-auto-flow') let autoflowValue = 'row' if (autoflow && !processor.disabled(autoflow, result)) { autoflowValue = autoflow.value.trim() } if (status === 'autoplace') { /** * Show warning if grid-template-rows decl is not found */ let rowDecl = parent.nodes.find(i => i.prop === 'grid-template-rows') if (!rowDecl && hasGridTemplate) { return undefined } else if (!rowDecl && !hasGridTemplate) { decl.warn( result, 'Autoplacement does not work without grid-template-rows property' ) return undefined } /** * Show warning if grid-template-columns decl is not found */ let columnDecl = parent.nodes.find(i => { return i.prop === 'grid-template-columns' }) if (!columnDecl && !hasGridTemplate) { decl.warn( result, 'Autoplacement does not work without grid-template-columns property' ) } /** * Autoplace grid items */ if (isColumnProp && !hasGridTemplate) { autoplaceGridItems(decl, result, gap, autoflowValue) } } return undefined } /** * Change IE property back */ normalize(prop) { return prop.replace(/^grid-(rows|columns)/, 'grid-template-$1') } /** * Change property name for IE */ prefixed(prop, prefix) { if (prefix === '-ms-') { return prefixTrackProp({ prefix, prop }) } return super.prefixed(prop, prefix) } } GridRowsColumns.names = [ 'grid-template-rows', 'grid-template-columns', 'grid-rows', 'grid-columns' ] module.exports = GridRowsColumns ================================================ FILE: lib/hacks/grid-start.js ================================================ let Declaration = require('../declaration') class GridStart extends Declaration { /** * Do not add prefix for unsupported value in IE */ check(decl) { let value = decl.value return !value.includes('/') && !value.includes('span') } /** * Return a final spec property */ normalize(prop) { return prop.replace('-start', '') } /** * Change property name for IE */ prefixed(prop, prefix) { let result = super.prefixed(prop, prefix) if (prefix === '-ms-') { result = result.replace('-start', '') } return result } } GridStart.names = ['grid-row-start', 'grid-column-start'] module.exports = GridStart ================================================ FILE: lib/hacks/grid-template-areas.js ================================================ let Declaration = require('../declaration') let { getGridGap, inheritGridGap, parseGridAreas, prefixTrackProp, prefixTrackValue, warnGridGap, warnMissedAreas } = require('./grid-utils') function getGridRows(tpl) { return tpl .trim() .slice(1, -1) .split(/["']\s*["']?/g) } class GridTemplateAreas extends Declaration { /** * Translate grid-template-areas to separate -ms- prefixed properties */ insert(decl, prefix, prefixes, result) { if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) let hasColumns = false let hasRows = false let parent = decl.parent let gap = getGridGap(decl) gap = inheritGridGap(decl, gap) || gap // remove already prefixed rows // to prevent doubling prefixes parent.walkDecls(/-ms-grid-rows/, i => i.remove()) // add empty tracks to rows parent.walkDecls(/grid-template-(rows|columns)/, trackDecl => { if (trackDecl.prop === 'grid-template-rows') { hasRows = true let { prop, value } = trackDecl trackDecl.cloneBefore({ prop: prefixTrackProp({ prefix, prop }), value: prefixTrackValue({ gap: gap.row, value }) }) } else { hasColumns = true } }) let gridRows = getGridRows(decl.value) if (hasColumns && !hasRows && gap.row && gridRows.length > 1) { decl.cloneBefore({ prop: '-ms-grid-rows', raws: {}, value: prefixTrackValue({ gap: gap.row, value: `repeat(${gridRows.length}, auto)` }) }) } // warnings warnGridGap({ decl, gap, hasColumns, result }) let areas = parseGridAreas({ gap, rows: gridRows }) warnMissedAreas(areas, decl, result) return decl } } GridTemplateAreas.names = ['grid-template-areas'] module.exports = GridTemplateAreas ================================================ FILE: lib/hacks/grid-template.js ================================================ let Declaration = require('../declaration') let { getGridGap, inheritGridGap, parseTemplate, warnGridGap, warnMissedAreas } = require('./grid-utils') class GridTemplate extends Declaration { /** * Translate grid-template to separate -ms- prefixed properties */ insert(decl, prefix, prefixes, result) { if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) if (decl.parent.some(i => i.prop === '-ms-grid-rows')) { return undefined } let gap = getGridGap(decl) /** * we must insert inherited gap values in some cases: * if we are inside media query && if we have no grid-gap value */ let inheritedGap = inheritGridGap(decl, gap) let { areas, columns, rows } = parseTemplate({ decl, gap: inheritedGap || gap }) let hasAreas = Object.keys(areas).length > 0 let hasRows = Boolean(rows) let hasColumns = Boolean(columns) warnGridGap({ decl, gap, hasColumns, result }) warnMissedAreas(areas, decl, result) if ((hasRows && hasColumns) || hasAreas) { decl.cloneBefore({ prop: '-ms-grid-rows', raws: {}, value: rows }) } if (hasColumns) { decl.cloneBefore({ prop: '-ms-grid-columns', raws: {}, value: columns }) } return decl } } GridTemplate.names = ['grid-template'] module.exports = GridTemplate ================================================ FILE: lib/hacks/grid-utils.js ================================================ let parser = require('postcss-value-parser') let list = require('postcss').list let uniq = require('../utils').uniq let escapeRegexp = require('../utils').escapeRegexp let splitSelector = require('../utils').splitSelector function convert(value) { if ( value && value.length === 2 && value[0] === 'span' && parseInt(value[1], 10) > 0 ) { return [false, parseInt(value[1], 10)] } if (value && value.length === 1 && parseInt(value[0], 10) > 0) { return [parseInt(value[0], 10), false] } return [false, false] } exports.translate = translate function translate(values, startIndex, endIndex) { let startValue = values[startIndex] let endValue = values[endIndex] if (!startValue) { return [false, false] } let [start, spanStart] = convert(startValue) let [end, spanEnd] = convert(endValue) if (start && !endValue) { return [start, false] } if (spanStart && end) { return [end - spanStart, spanStart] } if (start && spanEnd) { return [start, spanEnd] } if (start && end) { return [start, end - start] } return [false, false] } exports.parse = parse function parse(decl) { let node = parser(decl.value) let values = [] let current = 0 values[current] = [] for (let i of node.nodes) { if (i.type === 'div') { current += 1 values[current] = [] } else if (i.type === 'word') { values[current].push(i.value) } } return values } exports.insertDecl = insertDecl function insertDecl(decl, prop, value) { if (value && !decl.parent.some(i => i.prop === `-ms-${prop}`)) { decl.cloneBefore({ prop: `-ms-${prop}`, value: value.toString() }) } } // Track transforms exports.prefixTrackProp = prefixTrackProp function prefixTrackProp({ prefix, prop }) { return prefix + prop.replace('template-', '') } function transformRepeat({ nodes }, { gap }) { let { count, size } = nodes.reduce( (result, node) => { if (node.type === 'div' && node.value === ',') { result.key = 'size' } else { result[result.key].push(parser.stringify(node)) } return result }, { count: [], key: 'count', size: [] } ) // insert gap values if (gap) { size = size.filter(i => i.trim()) let val = [] for (let i = 1; i <= count; i++) { size.forEach((item, index) => { if (index > 0 || i > 1) { val.push(gap) } val.push(item) }) } return val.join(' ') } return `(${size.join('')})[${count.join('')}]` } exports.prefixTrackValue = prefixTrackValue function prefixTrackValue({ gap, value }) { let result = parser(value).nodes.reduce((nodes, node) => { if (node.type === 'function' && node.value === 'repeat') { nodes.push({ type: 'word', value: transformRepeat(node, { gap }) }) return nodes } if (gap && node.type === 'space') { nodes.push( { type: 'space', value: ' ' }, { type: 'word', value: gap }, node ) return nodes } nodes.push(node) return nodes }, []) return parser.stringify(result) } // Parse grid-template-areas let DOTS = /^\.+$/ function track(start, end) { return { end, span: end - start, start } } function getColumns(line) { return line.trim().split(/\s+/g) } exports.parseGridAreas = parseGridAreas function parseGridAreas({ gap, rows }) { return rows.reduce((areas, line, rowIndex) => { if (gap.row) rowIndex *= 2 if (line.trim() === '') return areas getColumns(line).forEach((area, columnIndex) => { if (DOTS.test(area)) return if (gap.column) columnIndex *= 2 if (typeof areas[area] === 'undefined') { areas[area] = { column: track(columnIndex + 1, columnIndex + 2), row: track(rowIndex + 1, rowIndex + 2) } } else { let { column, row } = areas[area] column.start = Math.min(column.start, columnIndex + 1) column.end = Math.max(column.end, columnIndex + 2) column.span = column.end - column.start row.start = Math.min(row.start, rowIndex + 1) row.end = Math.max(row.end, rowIndex + 2) row.span = row.end - row.start } }) return areas }, {}) } // Parse grid-template function testTrack(node) { return node.type === 'word' && /^\[.+]$/.test(node.value) } function verifyRowSize(result) { if (result.areas.length > result.rows.length) { result.rows.push('auto') } return result } exports.parseTemplate = parseTemplate function parseTemplate({ decl, gap }) { let gridTemplate = parser(decl.value).nodes.reduce( (result, node) => { let { type, value } = node if (testTrack(node) || type === 'space') return result // area if (type === 'string') { result = verifyRowSize(result) result.areas.push(value) } // values and function if (type === 'word' || type === 'function') { result[result.key].push(parser.stringify(node)) } // divider(/) if (type === 'div' && value === '/') { result.key = 'columns' result = verifyRowSize(result) } return result }, { areas: [], columns: [], key: 'rows', rows: [] } ) return { areas: parseGridAreas({ gap, rows: gridTemplate.areas }), columns: prefixTrackValue({ gap: gap.column, value: gridTemplate.columns.join(' ') }), rows: prefixTrackValue({ gap: gap.row, value: gridTemplate.rows.join(' ') }) } } // Insert parsed grid areas /** * Get an array of -ms- prefixed props and values * @param {Object} [area] area object with column and row data * @param {Boolean} [addRowSpan] should we add grid-column-row value? * @param {Boolean} [addColumnSpan] should we add grid-column-span value? * @return {Array} */ function getMSDecls(area, addRowSpan = false, addColumnSpan = false) { let result = [ { prop: '-ms-grid-row', value: String(area.row.start) } ] if (area.row.span > 1 || addRowSpan) { result.push({ prop: '-ms-grid-row-span', value: String(area.row.span) }) } result.push({ prop: '-ms-grid-column', value: String(area.column.start) }) if (area.column.span > 1 || addColumnSpan) { result.push({ prop: '-ms-grid-column-span', value: String(area.column.span) }) } return result } function getParentMedia(parent) { if (parent.type === 'atrule' && parent.name === 'media') { return parent } if (!parent.parent) { return false } return getParentMedia(parent.parent) } /** * change selectors for rules with duplicate grid-areas. * @param {Array} rules * @param {Array} templateSelectors * @return {Array} rules with changed selectors */ function changeDuplicateAreaSelectors(ruleSelectors, templateSelectors) { ruleSelectors = ruleSelectors.map(selector => { let selectorBySpace = list.space(selector) let selectorByComma = list.comma(selector) if (selectorBySpace.length > selectorByComma.length) { selector = selectorBySpace.slice(-1).join('') } return selector }) return ruleSelectors.map(ruleSelector => { let newSelector = templateSelectors.map((tplSelector, index) => { let space = index === 0 ? '' : ' ' return `${space}${tplSelector} > ${ruleSelector}` }) return newSelector }) } /** * check if selector of rules are equal * @param {Rule} ruleA * @param {Rule} ruleB * @return {Boolean} */ function selectorsEqual(ruleA, ruleB) { return ruleA.selectors.some(sel => { return ruleB.selectors.includes(sel) }) } /** * Parse data from all grid-template(-areas) declarations * @param {Root} css css root * @return {Object} parsed data */ function parseGridTemplatesData(css) { let parsed = [] // we walk through every grid-template(-areas) declaration and store // data with the same area names inside the item css.walkDecls(/grid-template(-areas)?$/, d => { let rule = d.parent let media = getParentMedia(rule) let gap = getGridGap(d) let inheritedGap = inheritGridGap(d, gap) let { areas } = parseTemplate({ decl: d, gap: inheritedGap || gap }) let areaNames = Object.keys(areas) // skip node if it doesn't have areas if (areaNames.length === 0) { return true } // check parsed array for item that include the same area names // return index of that item let index = parsed.reduce((acc, { allAreas }, idx) => { let hasAreas = allAreas && areaNames.some(area => allAreas.includes(area)) return hasAreas ? idx : acc }, null) if (index !== null) { // index is found, add the grid-template data to that item let { allAreas, rules } = parsed[index] // check if rule has no duplicate area names let hasNoDuplicates = rules.some(r => { return r.hasDuplicates === false && selectorsEqual(r, rule) }) let duplicatesFound = false // check need to gather all duplicate area names let duplicateAreaNames = rules.reduce((acc, r) => { if (!r.params && selectorsEqual(r, rule)) { duplicatesFound = true return r.duplicateAreaNames } if (!duplicatesFound) { areaNames.forEach(name => { if (r.areas[name]) { acc.push(name) } }) } return uniq(acc) }, []) // update grid-row/column-span values for areas with duplicate // area names. @see #1084 and #1146 rules.forEach(r => { areaNames.forEach(name => { let area = r.areas[name] if (area && area.row.span !== areas[name].row.span) { areas[name].row.updateSpan = true } if (area && area.column.span !== areas[name].column.span) { areas[name].column.updateSpan = true } }) }) parsed[index].allAreas = uniq([...allAreas, ...areaNames]) parsed[index].rules.push({ areas, duplicateAreaNames, hasDuplicates: !hasNoDuplicates, node: rule, params: media.params, selectors: rule.selectors }) } else { // index is NOT found, push the new item to the parsed array parsed.push({ allAreas: areaNames, areasCount: 0, rules: [ { areas, duplicateAreaNames: [], duplicateRules: [], hasDuplicates: false, node: rule, params: media.params, selectors: rule.selectors } ] }) } return undefined }) return parsed } /** * insert prefixed grid-area declarations * @param {Root} css css root * @param {Function} isDisabled check if the rule is disabled * @return {void} */ exports.insertAreas = insertAreas function insertAreas(css, isDisabled) { // parse grid-template declarations let gridTemplatesData = parseGridTemplatesData(css) // return undefined if no declarations found if (gridTemplatesData.length === 0) { return undefined } // we need to store the rules that we will insert later let rulesToInsert = {} css.walkDecls('grid-area', gridArea => { let gridAreaRule = gridArea.parent let hasPrefixedRow = gridAreaRule.first.prop === '-ms-grid-row' let gridAreaMedia = getParentMedia(gridAreaRule) if (isDisabled(gridArea)) { return undefined } let gridAreaRuleIndex = css.index(gridAreaMedia || gridAreaRule) let value = gridArea.value // found the data that matches grid-area identifier let data = gridTemplatesData.filter(d => d.allAreas.includes(value))[0] if (!data) { return true } let lastArea = data.allAreas[data.allAreas.length - 1] let selectorBySpace = list.space(gridAreaRule.selector) let selectorByComma = list.comma(gridAreaRule.selector) let selectorIsComplex = selectorBySpace.length > 1 && selectorBySpace.length > selectorByComma.length // prevent doubling of prefixes if (hasPrefixedRow) { return false } // create the empty object with the key as the last area name // e.g if we have templates with "a b c" values, "c" will be the last area if (!rulesToInsert[lastArea]) { rulesToInsert[lastArea] = {} } let lastRuleIsSet = false // walk through every grid-template rule data for (let rule of data.rules) { let area = rule.areas[value] let hasDuplicateName = rule.duplicateAreaNames.includes(value) // if we can't find the area name, update lastRule and continue if (!area) { let lastRule = rulesToInsert[lastArea].lastRule let lastRuleIndex if (lastRule) { lastRuleIndex = css.index(lastRule) } else { /* c8 ignore next 2 */ lastRuleIndex = -1 } if (gridAreaRuleIndex > lastRuleIndex) { rulesToInsert[lastArea].lastRule = gridAreaMedia || gridAreaRule } continue } // for grid-templates inside media rule we need to create empty // array to push prefixed grid-area rules later if (rule.params && !rulesToInsert[lastArea][rule.params]) { rulesToInsert[lastArea][rule.params] = [] } if ((!rule.hasDuplicates || !hasDuplicateName) && !rule.params) { // grid-template has no duplicates and not inside media rule getMSDecls(area, false, false) .reverse() .forEach(i => gridAreaRule.prepend( Object.assign(i, { raws: { between: gridArea.raws.between } }) ) ) rulesToInsert[lastArea].lastRule = gridAreaRule lastRuleIsSet = true } else if (rule.hasDuplicates && !rule.params && !selectorIsComplex) { // grid-template has duplicates and not inside media rule let cloned = gridAreaRule.clone() cloned.removeAll() getMSDecls(area, area.row.updateSpan, area.column.updateSpan) .reverse() .forEach(i => cloned.prepend( Object.assign(i, { raws: { between: gridArea.raws.between } }) ) ) cloned.selectors = changeDuplicateAreaSelectors( cloned.selectors, rule.selectors ) if (rulesToInsert[lastArea].lastRule) { rulesToInsert[lastArea].lastRule.after(cloned) } rulesToInsert[lastArea].lastRule = cloned lastRuleIsSet = true } else if ( rule.hasDuplicates && !rule.params && selectorIsComplex && gridAreaRule.selector.includes(rule.selectors[0]) ) { // grid-template has duplicates and not inside media rule // and the selector is complex gridAreaRule.walkDecls(/-ms-grid-(row|column)/, d => d.remove()) getMSDecls(area, area.row.updateSpan, area.column.updateSpan) .reverse() .forEach(i => gridAreaRule.prepend( Object.assign(i, { raws: { between: gridArea.raws.between } }) ) ) } else if (rule.params) { // grid-template is inside media rule // if we're inside media rule, we need to store prefixed rules // inside rulesToInsert object to be able to preserve the order of media // rules and merge them easily let cloned = gridAreaRule.clone() cloned.removeAll() getMSDecls(area, area.row.updateSpan, area.column.updateSpan) .reverse() .forEach(i => cloned.prepend( Object.assign(i, { raws: { between: gridArea.raws.between } }) ) ) if (rule.hasDuplicates && hasDuplicateName) { cloned.selectors = changeDuplicateAreaSelectors( cloned.selectors, rule.selectors ) } cloned.raws = rule.node.raws if (css.index(rule.node.parent) > gridAreaRuleIndex) { // append the prefixed rules right inside media rule // with grid-template rule.node.parent.append(cloned) } else { // store the rule to insert later rulesToInsert[lastArea][rule.params].push(cloned) } // set new rule as last rule ONLY if we didn't set lastRule for // this grid-area before if (!lastRuleIsSet) { rulesToInsert[lastArea].lastRule = gridAreaMedia || gridAreaRule } } } return undefined }) // append stored rules inside the media rules Object.keys(rulesToInsert).forEach(area => { let data = rulesToInsert[area] let lastRule = data.lastRule Object.keys(data) .reverse() .filter(p => p !== 'lastRule') .forEach(params => { if (data[params].length > 0 && lastRule) { lastRule.after({ name: 'media', params }) lastRule.next().append(data[params]) } }) }) return undefined } /** * Warn user if grid area identifiers are not found * @param {Object} areas * @param {Declaration} decl * @param {Result} result * @return {void} */ exports.warnMissedAreas = warnMissedAreas function warnMissedAreas(areas, decl, result) { let missed = Object.keys(areas) decl.root().walkDecls('grid-area', gridArea => { missed = missed.filter(e => e !== gridArea.value) }) if (missed.length > 0) { decl.warn(result, 'Can not find grid areas: ' + missed.join(', ')) } return undefined } /** * compare selectors with grid-area rule and grid-template rule * show warning if grid-template selector is not found * (this function used for grid-area rule) * @param {Declaration} decl * @param {Result} result * @return {void} */ exports.warnTemplateSelectorNotFound = warnTemplateSelectorNotFound function warnTemplateSelectorNotFound(decl, result) { let rule = decl.parent let root = decl.root() let duplicatesFound = false // slice selector array. Remove the last part (for comparison) let slicedSelectorArr = list .space(rule.selector) .filter(str => str !== '>') .slice(0, -1) // we need to compare only if selector is complex. // e.g '.grid-cell' is simple, but '.parent > .grid-cell' is complex if (slicedSelectorArr.length > 0) { let gridTemplateFound = false let foundAreaSelector = null root.walkDecls(/grid-template(-areas)?$/, d => { let parent = d.parent let templateSelectors = parent.selectors let { areas } = parseTemplate({ decl: d, gap: getGridGap(d) }) let hasArea = areas[decl.value] // find the the matching selectors for (let tplSelector of templateSelectors) { if (gridTemplateFound) { break } let tplSelectorArr = list.space(tplSelector).filter(str => str !== '>') gridTemplateFound = tplSelectorArr.every( (item, idx) => item === slicedSelectorArr[idx] ) } if (gridTemplateFound || !hasArea) { return true } if (!foundAreaSelector) { foundAreaSelector = parent.selector } // if we found the duplicate area with different selector if (foundAreaSelector && foundAreaSelector !== parent.selector) { duplicatesFound = true } return undefined }) // warn user if we didn't find template if (!gridTemplateFound && duplicatesFound) { decl.warn( result, 'Autoprefixer cannot find a grid-template ' + `containing the duplicate grid-area "${decl.value}" ` + `with full selector matching: ${slicedSelectorArr.join(' ')}` ) } } } /** * warn user if both grid-area and grid-(row|column) * declarations are present in the same rule * @param {Declaration} decl * @param {Result} result * @return {void} */ exports.warnIfGridRowColumnExists = warnIfGridRowColumnExists function warnIfGridRowColumnExists(decl, result) { let rule = decl.parent let decls = [] rule.walkDecls(/^grid-(row|column)/, d => { if ( !d.prop.endsWith('-end') && !d.value.startsWith('span') && !d.prop.endsWith('-gap') ) { decls.push(d) } }) if (decls.length > 0) { decls.forEach(d => { d.warn( result, 'You already have a grid-area declaration present in the rule. ' + `You should use either grid-area or ${d.prop}, not both` ) }) } return undefined } // Gap utils exports.getGridGap = getGridGap function getGridGap(decl) { let gap = {} // try to find gap let testGap = /^(grid-)?((row|column)-)?gap$/ decl.parent.walkDecls(testGap, ({ prop, value }) => { if (/^(grid-)?gap$/.test(prop)) { let [row, , column] = parser(value).nodes gap.row = row && parser.stringify(row) gap.column = column ? parser.stringify(column) : gap.row } if (/^(grid-)?row-gap$/.test(prop)) gap.row = value if (/^(grid-)?column-gap$/.test(prop)) gap.column = value }) return gap } /** * parse media parameters (for example 'min-width: 500px') * @param {String} params parameter to parse * @return {} */ function parseMediaParams(params) { if (!params) { return [] } let parsed = parser(params) let prop let value parsed.walk(node => { if (node.type === 'word' && /min|max/g.test(node.value)) { prop = node.value } else if (node.value.includes('px')) { value = parseInt(node.value.replace(/\D/g, '')) } }) return [prop, value] } /** * Compare the selectors and decide if we * need to inherit gap from compared selector or not. * @type {String} selA * @type {String} selB * @return {Boolean} */ function shouldInheritGap(selA, selB) { let result // get arrays of selector split in 3-deep array let splitSelectorArrA = splitSelector(selA) let splitSelectorArrB = splitSelector(selB) if (splitSelectorArrA[0].length < splitSelectorArrB[0].length) { // abort if selectorA has lower descendant specificity then selectorB // (e.g '.grid' and '.hello .world .grid') return false } else if (splitSelectorArrA[0].length > splitSelectorArrB[0].length) { // if selectorA has higher descendant specificity then selectorB // (e.g '.foo .bar .grid' and '.grid') let idx = splitSelectorArrA[0].reduce((res, [item], index) => { let firstSelectorPart = splitSelectorArrB[0][0][0] if (item === firstSelectorPart) { return index } return false }, false) if (idx) { result = splitSelectorArrB[0].every((arr, index) => { return arr.every( (part, innerIndex) => // because selectorA has more space elements, we need to slice // selectorA array by 'idx' number to compare them splitSelectorArrA[0].slice(idx)[index][innerIndex] === part ) }) } } else { // if selectorA has the same descendant specificity as selectorB // this condition covers cases such as: '.grid.foo.bar' and '.grid' result = splitSelectorArrB.some(byCommaArr => { return byCommaArr.every((bySpaceArr, index) => { return bySpaceArr.every( (part, innerIndex) => splitSelectorArrA[0][index][innerIndex] === part ) }) }) } return result } /** * inherit grid gap values from the closest rule above * with the same selector * @param {Declaration} decl * @param {Object} gap gap values * @return {Object | Boolean} return gap values or false (if not found) */ exports.inheritGridGap = inheritGridGap function inheritGridGap(decl, gap) { let rule = decl.parent let mediaRule = getParentMedia(rule) let root = rule.root() // get an array of selector split in 3-deep array let splitSelectorArr = splitSelector(rule.selector) // abort if the rule already has gaps if (Object.keys(gap).length > 0) { return false } // e.g ['min-width'] let [prop] = parseMediaParams(mediaRule.params) let lastBySpace = splitSelectorArr[0] // get escaped value from the selector // if we have '.grid-2.foo.bar' selector, will be '\.grid\-2' let escaped = escapeRegexp(lastBySpace[lastBySpace.length - 1][0]) let regexp = new RegExp(`(${escaped}$)|(${escaped}[,.])`) // find the closest rule with the same selector let closestRuleGap root.walkRules(regexp, r => { let gridGap // abort if are checking the same rule if (rule.toString() === r.toString()) { return false } // find grid-gap values r.walkDecls('grid-gap', d => (gridGap = getGridGap(d))) // skip rule without gaps if (!gridGap || Object.keys(gridGap).length === 0) { return true } // skip rules that should not be inherited from if (!shouldInheritGap(rule.selector, r.selector)) { return true } let media = getParentMedia(r) if (media) { // if we are inside media, we need to check that media props match // e.g ('min-width' === 'min-width') let propToCompare = parseMediaParams(media.params)[0] if (propToCompare === prop) { closestRuleGap = gridGap return true } } else { closestRuleGap = gridGap return true } return undefined }) // if we find the closest gap object if (closestRuleGap && Object.keys(closestRuleGap).length > 0) { return closestRuleGap } return false } exports.warnGridGap = warnGridGap function warnGridGap({ decl, gap, hasColumns, result }) { let hasBothGaps = gap.row && gap.column if (!hasColumns && (hasBothGaps || (gap.column && !gap.row))) { delete gap.column decl.warn( result, 'Can not implement grid-gap without grid-template-columns' ) } } /** * normalize the grid-template-rows/columns values * @param {String} str grid-template-rows/columns value * @return {Array} normalized array with values * @example * let normalized = normalizeRowColumn('1fr repeat(2, 20px 50px) 1fr') * normalized // <= ['1fr', '20px', '50px', '20px', '50px', '1fr'] */ function normalizeRowColumn(str) { let normalized = parser(str).nodes.reduce((result, node) => { if (node.type === 'function' && node.value === 'repeat') { let key = 'count' let [count, value] = node.nodes.reduce( (acc, n) => { if (n.type === 'word' && key === 'count') { acc[0] = Math.abs(parseInt(n.value)) return acc } if (n.type === 'div' && n.value === ',') { key = 'value' return acc } if (key === 'value') { acc[1] += parser.stringify(n) } return acc }, [0, ''] ) if (count) { for (let i = 0; i < count; i++) { result.push(value) } } return result } if (node.type === 'space') { return result } result.push(parser.stringify(node)) return result }, []) return normalized } exports.autoplaceGridItems = autoplaceGridItems /** * Autoplace grid items * @param {Declaration} decl * @param {Result} result * @param {Object} gap gap values * @param {String} autoflowValue grid-auto-flow value * @return {void} * @see https://github.com/postcss/autoprefixer/issues/1148 */ function autoplaceGridItems(decl, result, gap, autoflowValue = 'row') { let { parent } = decl let rowDecl = parent.nodes.find(i => i.prop === 'grid-template-rows') let rows = normalizeRowColumn(rowDecl.value) let columns = normalizeRowColumn(decl.value) // Build array of area names with dummy values. If we have 3 columns and // 2 rows, filledRows will be equal to ['1 2 3', '4 5 6'] let filledRows = rows.map((_, rowIndex) => { return Array.from( { length: columns.length }, (v, k) => k + rowIndex * columns.length + 1 ).join(' ') }) let areas = parseGridAreas({ gap, rows: filledRows }) let keys = Object.keys(areas) let items = keys.map(i => areas[i]) // Change the order of cells if grid-auto-flow value is 'column' if (autoflowValue.includes('column')) { items = items.sort((a, b) => a.column.start - b.column.start) } // Insert new rules items.reverse().forEach((item, index) => { let { column, row } = item let nodeSelector = parent.selectors .map(sel => sel + ` > *:nth-child(${keys.length - index})`) .join(', ') // create new rule let node = parent.clone().removeAll() // change rule selector node.selector = nodeSelector // insert prefixed row/column values node.append({ prop: '-ms-grid-row', value: row.start }) node.append({ prop: '-ms-grid-column', value: column.start }) // insert rule parent.after(node) }) return undefined } ================================================ FILE: lib/hacks/image-rendering.js ================================================ let Declaration = require('../declaration') class ImageRendering extends Declaration { /** * Add hack only for crisp-edges */ check(decl) { return decl.value === 'pixelated' } /** * Return property name by spec */ normalize() { return 'image-rendering' } /** * Change property name for IE */ prefixed(prop, prefix) { if (prefix === '-ms-') { return '-ms-interpolation-mode' } return super.prefixed(prop, prefix) } /** * Warn on old value */ process(node, result) { return super.process(node, result) } /** * Change property and value for IE */ set(decl, prefix) { if (prefix !== '-ms-') return super.set(decl, prefix) decl.prop = '-ms-interpolation-mode' decl.value = 'nearest-neighbor' return decl } } ImageRendering.names = ['image-rendering', 'interpolation-mode'] module.exports = ImageRendering ================================================ FILE: lib/hacks/image-set.js ================================================ let Value = require('../value') class ImageSet extends Value { /** * Use non-standard name for WebKit and Firefox */ replace(string, prefix) { let fixed = super.replace(string, prefix) if (prefix === '-webkit-') { fixed = fixed.replace(/("[^"]+"|'[^']+')(\s+\d+\w)/gi, 'url($1)$2') } return fixed } } ImageSet.names = ['image-set'] module.exports = ImageSet ================================================ FILE: lib/hacks/inline-logical.js ================================================ let Declaration = require('../declaration') class InlineLogical extends Declaration { /** * Return property name by spec */ normalize(prop) { return prop.replace(/(margin|padding|border)-(start|end)/, '$1-inline-$2') } /** * Use old syntax for -moz- and -webkit- */ prefixed(prop, prefix) { return prefix + prop.replace('-inline', '') } } InlineLogical.names = [ 'border-inline-start', 'border-inline-end', 'margin-inline-start', 'margin-inline-end', 'padding-inline-start', 'padding-inline-end', 'border-start', 'border-end', 'margin-start', 'margin-end', 'padding-start', 'padding-end' ] module.exports = InlineLogical ================================================ FILE: lib/hacks/intrinsic.js ================================================ let OldValue = require('../old-value') let Value = require('../value') function regexp(name) { return new RegExp(`(^|[\\s,(])(${name}($|[\\s),]))`, 'gi') } class Intrinsic extends Value { add(decl, prefix) { if (decl.prop.includes('grid') && prefix !== '-webkit-') { return undefined } return super.add(decl, prefix) } isStretch() { return ( this.name === 'stretch' || this.name === 'fill' || this.name === 'fill-available' ) } old(prefix) { let prefixed = prefix + this.name if (this.isStretch()) { if (prefix === '-moz-') { prefixed = '-moz-available' } else if (prefix === '-webkit-') { prefixed = '-webkit-fill-available' } } return new OldValue(this.name, prefixed, prefixed, regexp(prefixed)) } regexp() { if (!this.regexpCache) this.regexpCache = regexp(this.name) return this.regexpCache } replace(string, prefix) { if (prefix === '-moz-' && this.isStretch()) { return string.replace(this.regexp(), '$1-moz-available$3') } if (prefix === '-webkit-' && this.isStretch()) { return string.replace(this.regexp(), '$1-webkit-fill-available$3') } return super.replace(string, prefix) } } Intrinsic.names = [ 'max-content', 'min-content', 'fit-content', 'fill', 'fill-available', 'stretch' ] module.exports = Intrinsic ================================================ FILE: lib/hacks/justify-content.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class JustifyContent extends Declaration { /** * Return property name by final spec */ normalize() { return 'justify-content' } /** * Change property name for 2009 and 2012 specs */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2009) { return prefix + 'box-pack' } if (spec === 2012) { return prefix + 'flex-pack' } return super.prefixed(prop, prefix) } /** * Change value for 2009 and 2012 specs */ set(decl, prefix) { let spec = flexSpec(prefix)[0] if (spec === 2009 || spec === 2012) { let value = JustifyContent.oldValues[decl.value] || decl.value decl.value = value if (spec !== 2009 || value !== 'distribute') { return super.set(decl, prefix) } } else if (spec === 'final') { return super.set(decl, prefix) } return undefined } } JustifyContent.names = ['justify-content', 'flex-pack', 'box-pack'] JustifyContent.oldValues = { 'flex-end': 'end', 'flex-start': 'start', 'space-around': 'distribute', 'space-between': 'justify' } module.exports = JustifyContent ================================================ FILE: lib/hacks/mask-border.js ================================================ let Declaration = require('../declaration') class MaskBorder extends Declaration { /** * Return property name by final spec */ normalize() { return this.name.replace('box-image', 'border') } /** * Return flex property for 2012 spec */ prefixed(prop, prefix) { let result = super.prefixed(prop, prefix) if (prefix === '-webkit-') { result = result.replace('border', 'box-image') } return result } } MaskBorder.names = [ 'mask-border', 'mask-border-source', 'mask-border-slice', 'mask-border-width', 'mask-border-outset', 'mask-border-repeat', 'mask-box-image', 'mask-box-image-source', 'mask-box-image-slice', 'mask-box-image-width', 'mask-box-image-outset', 'mask-box-image-repeat' ] module.exports = MaskBorder ================================================ FILE: lib/hacks/mask-composite.js ================================================ let Declaration = require('../declaration') class MaskComposite extends Declaration { /** * Prefix mask-composite for webkit */ insert(decl, prefix, prefixes) { let isCompositeProp = decl.prop === 'mask-composite' let compositeValues if (isCompositeProp) { compositeValues = decl.value.split(',') } else { compositeValues = decl.value.match(MaskComposite.regexp) || [] } compositeValues = compositeValues.map(el => el.trim()).filter(el => el) let hasCompositeValues = compositeValues.length let compositeDecl if (hasCompositeValues) { compositeDecl = this.clone(decl) compositeDecl.value = compositeValues .map(value => MaskComposite.oldValues[value] || value) .join(', ') if (compositeValues.includes('intersect')) { compositeDecl.value += ', xor' } compositeDecl.prop = prefix + 'mask-composite' } if (isCompositeProp) { if (!hasCompositeValues) { return undefined } if (this.needCascade(decl)) { compositeDecl.raws.before = this.calcBefore(prefixes, decl, prefix) } return decl.parent.insertBefore(decl, compositeDecl) } let cloned = this.clone(decl) cloned.prop = prefix + cloned.prop if (hasCompositeValues) { cloned.value = cloned.value.replace(MaskComposite.regexp, '') } if (this.needCascade(decl)) { cloned.raws.before = this.calcBefore(prefixes, decl, prefix) } decl.parent.insertBefore(decl, cloned) if (!hasCompositeValues) { return decl } if (this.needCascade(decl)) { compositeDecl.raws.before = this.calcBefore(prefixes, decl, prefix) } return decl.parent.insertBefore(decl, compositeDecl) } } MaskComposite.names = ['mask', 'mask-composite'] MaskComposite.oldValues = { add: 'source-over', exclude: 'xor', intersect: 'source-in', subtract: 'source-out' } MaskComposite.regexp = new RegExp( `\\s+(${Object.keys(MaskComposite.oldValues).join( '|' )})\\b(?!\\))\\s*(?=[,])`, 'ig' ) module.exports = MaskComposite ================================================ FILE: lib/hacks/order.js ================================================ let Declaration = require('../declaration') let flexSpec = require('./flex-spec') class Order extends Declaration { /** * Return property name by final spec */ normalize() { return 'order' } /** * Change property name for 2009 and 2012 specs */ prefixed(prop, prefix) { let spec ;[spec, prefix] = flexSpec(prefix) if (spec === 2009) { return prefix + 'box-ordinal-group' } if (spec === 2012) { return prefix + 'flex-order' } return super.prefixed(prop, prefix) } /** * Fix value for 2009 spec */ set(decl, prefix) { let spec = flexSpec(prefix)[0] if (spec === 2009 && /\d/.test(decl.value)) { decl.value = (parseInt(decl.value) + 1).toString() return super.set(decl, prefix) } return super.set(decl, prefix) } } Order.names = ['order', 'flex-order', 'box-ordinal-group'] module.exports = Order ================================================ FILE: lib/hacks/overscroll-behavior.js ================================================ let Declaration = require('../declaration') class OverscrollBehavior extends Declaration { /** * Return property name by spec */ normalize() { return 'overscroll-behavior' } /** * Change property name for IE */ prefixed(prop, prefix) { return prefix + 'scroll-chaining' } /** * Change value for IE */ set(decl, prefix) { if (decl.value === 'auto') { decl.value = 'chained' } else if (decl.value === 'none' || decl.value === 'contain') { decl.value = 'none' } return super.set(decl, prefix) } } OverscrollBehavior.names = ['overscroll-behavior', 'scroll-chaining'] module.exports = OverscrollBehavior ================================================ FILE: lib/hacks/pixelated.js ================================================ let OldValue = require('../old-value') let Value = require('../value') class Pixelated extends Value { /** * Different name for WebKit and Firefox */ old(prefix) { if (prefix === '-webkit-') { return new OldValue(this.name, '-webkit-optimize-contrast') } if (prefix === '-moz-') { return new OldValue(this.name, '-moz-crisp-edges') } return super.old(prefix) } /** * Use non-standard name for WebKit and Firefox */ replace(string, prefix) { if (prefix === '-webkit-') { return string.replace(this.regexp(), '$1-webkit-optimize-contrast') } if (prefix === '-moz-') { return string.replace(this.regexp(), '$1-moz-crisp-edges') } return super.replace(string, prefix) } } Pixelated.names = ['pixelated'] module.exports = Pixelated ================================================ FILE: lib/hacks/place-self.js ================================================ let Declaration = require('../declaration') let utils = require('./grid-utils') class PlaceSelf extends Declaration { /** * Translate place-self to separate -ms- prefixed properties */ insert(decl, prefix, prefixes) { if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) // prevent doubling of prefixes if (decl.parent.some(i => i.prop === '-ms-grid-row-align')) { return undefined } let [[first, second]] = utils.parse(decl) if (second) { utils.insertDecl(decl, 'grid-row-align', first) utils.insertDecl(decl, 'grid-column-align', second) } else { utils.insertDecl(decl, 'grid-row-align', first) utils.insertDecl(decl, 'grid-column-align', first) } return undefined } } PlaceSelf.names = ['place-self'] module.exports = PlaceSelf ================================================ FILE: lib/hacks/placeholder-shown.js ================================================ let Selector = require('../selector') class PlaceholderShown extends Selector { /** * Return different selectors depend on prefix */ prefixed(prefix) { if (prefix === '-moz-') { return ':-moz-placeholder' } else if (prefix === '-ms-') { return ':-ms-input-placeholder' } return `:${prefix}placeholder-shown` } } PlaceholderShown.names = [':placeholder-shown'] module.exports = PlaceholderShown ================================================ FILE: lib/hacks/placeholder.js ================================================ let Selector = require('../selector') class Placeholder extends Selector { /** * Add old mozilla to possible prefixes */ possible() { return super.possible().concat(['-moz- old', '-ms- old']) } /** * Return different selectors depend on prefix */ prefixed(prefix) { if (prefix === '-webkit-') { return '::-webkit-input-placeholder' } if (prefix === '-ms-') { return '::-ms-input-placeholder' } if (prefix === '-ms- old') { return ':-ms-input-placeholder' } if (prefix === '-moz- old') { return ':-moz-placeholder' } return `::${prefix}placeholder` } } Placeholder.names = ['::placeholder'] module.exports = Placeholder ================================================ FILE: lib/hacks/print-color-adjust.js ================================================ let Declaration = require('../declaration') class PrintColorAdjust extends Declaration { /** * Return property name by spec */ normalize() { return 'print-color-adjust' } /** * Change property name for WebKit-based browsers */ prefixed(prop, prefix) { if (prefix === '-moz-') { return 'color-adjust' } else { return prefix + 'print-color-adjust' } } } PrintColorAdjust.names = ['print-color-adjust', 'color-adjust'] module.exports = PrintColorAdjust ================================================ FILE: lib/hacks/text-decoration-skip-ink.js ================================================ let Declaration = require('../declaration') class TextDecorationSkipInk extends Declaration { /** * Change prefix for ink value */ set(decl, prefix) { if (decl.prop === 'text-decoration-skip-ink' && decl.value === 'auto') { decl.prop = prefix + 'text-decoration-skip' decl.value = 'ink' return decl } else { return super.set(decl, prefix) } } } TextDecorationSkipInk.names = [ 'text-decoration-skip-ink', 'text-decoration-skip' ] module.exports = TextDecorationSkipInk ================================================ FILE: lib/hacks/text-decoration.js ================================================ let Declaration = require('../declaration') const BASIC = [ 'none', 'underline', 'overline', 'line-through', 'blink', 'inherit', 'initial', 'unset' ] class TextDecoration extends Declaration { /** * Do not add prefixes for basic values. */ check(decl) { return decl.value.split(/\s+/).some(i => !BASIC.includes(i)) } } TextDecoration.names = ['text-decoration'] module.exports = TextDecoration ================================================ FILE: lib/hacks/text-emphasis-position.js ================================================ let Declaration = require('../declaration') class TextEmphasisPosition extends Declaration { set(decl, prefix) { if (prefix === '-webkit-') { decl.value = decl.value.replace(/\s*(right|left)\s*/i, '') } return super.set(decl, prefix) } } TextEmphasisPosition.names = ['text-emphasis-position'] module.exports = TextEmphasisPosition ================================================ FILE: lib/hacks/transform-decl.js ================================================ let Declaration = require('../declaration') class TransformDecl extends Declaration { /** * Is transform contain 3D commands */ contain3d(decl) { if (decl.prop === 'transform-origin') { return false } for (let func of TransformDecl.functions3d) { if (decl.value.includes(`${func}(`)) { return true } } return false } /** * Don't add prefix for IE in keyframes */ insert(decl, prefix, prefixes) { if (prefix === '-ms-') { if (!this.contain3d(decl) && !this.keyframeParents(decl)) { return super.insert(decl, prefix, prefixes) } } else if (prefix === '-o-') { if (!this.contain3d(decl)) { return super.insert(decl, prefix, prefixes) } } else { return super.insert(decl, prefix, prefixes) } return undefined } /** * Recursively check all parents for @keyframes */ keyframeParents(decl) { let { parent } = decl while (parent) { if (parent.type === 'atrule' && parent.name === 'keyframes') { return true } ;({ parent } = parent) } return false } /** * Replace rotateZ to rotate for IE 9 */ set(decl, prefix) { decl = super.set(decl, prefix) if (prefix === '-ms-') { decl.value = decl.value.replace(/rotatez/gi, 'rotate') } return decl } } TransformDecl.names = ['transform', 'transform-origin'] TransformDecl.functions3d = [ 'matrix3d', 'translate3d', 'translateZ', 'scale3d', 'scaleZ', 'rotate3d', 'rotateX', 'rotateY', 'perspective' ] module.exports = TransformDecl ================================================ FILE: lib/hacks/user-select.js ================================================ let Declaration = require('../declaration') class UserSelect extends Declaration { /** * Avoid prefixing all in IE */ insert(decl, prefix, prefixes) { if (decl.value === 'all' && prefix === '-ms-') { return undefined } else if ( decl.value === 'contain' && (prefix === '-moz-' || prefix === '-webkit-') ) { return undefined } else { return super.insert(decl, prefix, prefixes) } } /** * Change prefixed value for IE */ set(decl, prefix) { if (prefix === '-ms-' && decl.value === 'contain') { decl.value = 'element' } return super.set(decl, prefix) } } UserSelect.names = ['user-select'] module.exports = UserSelect ================================================ FILE: lib/hacks/writing-mode.js ================================================ let Declaration = require('../declaration') class WritingMode extends Declaration { insert(decl, prefix, prefixes) { if (prefix === '-ms-') { let cloned = this.set(this.clone(decl), prefix) if (this.needCascade(decl)) { cloned.raws.before = this.calcBefore(prefixes, decl, prefix) } let direction = 'ltr' decl.parent.nodes.forEach(i => { if (i.prop === 'direction') { if (i.value === 'rtl' || i.value === 'ltr') direction = i.value } }) cloned.value = WritingMode.msValues[direction][decl.value] || decl.value return decl.parent.insertBefore(decl, cloned) } return super.insert(decl, prefix, prefixes) } } WritingMode.names = ['writing-mode'] WritingMode.msValues = { ltr: { 'horizontal-tb': 'lr-tb', 'vertical-lr': 'tb-lr', 'vertical-rl': 'tb-rl' }, rtl: { 'horizontal-tb': 'rl-tb', 'vertical-lr': 'bt-lr', 'vertical-rl': 'bt-rl' } } module.exports = WritingMode ================================================ FILE: lib/info.js ================================================ let browserslist = require('browserslist') function capitalize(str) { return str.slice(0, 1).toUpperCase() + str.slice(1) } const NAMES = { and_chr: 'Chrome for Android', and_ff: 'Firefox for Android', and_qq: 'QQ Browser', and_uc: 'UC for Android', baidu: 'Baidu Browser', ie: 'IE', ie_mob: 'IE Mobile', ios_saf: 'iOS Safari', kaios: 'KaiOS Browser', op_mini: 'Opera Mini', op_mob: 'Opera Mobile', samsung: 'Samsung Internet' } function prefix(name, prefixes, note) { let out = ` ${name}` if (note) out += ' *' out += ': ' out += prefixes.map(i => i.replace(/^-(.*)-$/g, '$1')).join(', ') out += '\n' return out } module.exports = function (prefixes) { if (prefixes.browsers.selected.length === 0) { return 'No browsers selected' } let versions = {} for (let browser of prefixes.browsers.selected) { let parts = browser.split(' ') let name = parts[0] let version = parts[1] name = NAMES[name] || capitalize(name) if (versions[name]) { versions[name].push(version) } else { versions[name] = [version] } } let out = 'Browsers:\n' for (let browser in versions) { let list = versions[browser] list = list.sort((a, b) => parseFloat(b) - parseFloat(a)) out += ` ${browser}: ${list.join(', ')}\n` } let coverage = browserslist.coverage(prefixes.browsers.selected) let round = Math.round(coverage * 100) / 100.0 out += `\nThese browsers account for ${round}% of all users globally\n` let atrules = [] for (let name in prefixes.add) { let data = prefixes.add[name] if (name[0] === '@' && data.prefixes) { atrules.push(prefix(name, data.prefixes)) } } if (atrules.length > 0) { out += `\nAt-Rules:\n${atrules.sort().join('')}` } let selectors = [] for (let selector of prefixes.add.selectors) { if (selector.prefixes) { selectors.push(prefix(selector.name, selector.prefixes)) } } if (selectors.length > 0) { out += `\nSelectors:\n${selectors.sort().join('')}` } let values = [] let props = [] let hadGrid = false for (let name in prefixes.add) { let data = prefixes.add[name] if (name[0] !== '@' && data.prefixes) { let grid = name.indexOf('grid-') === 0 if (grid) hadGrid = true props.push(prefix(name, data.prefixes, grid)) } if (!Array.isArray(data.values)) { continue } for (let value of data.values) { let grid = value.name.includes('grid') if (grid) hadGrid = true let string = prefix(value.name, value.prefixes, grid) if (!values.includes(string)) { values.push(string) } } } if (props.length > 0) { out += `\nProperties:\n${props.sort().join('')}` } if (values.length > 0) { out += `\nValues:\n${values.sort().join('')}` } if (hadGrid) { out += '\n* - Prefixes will be added only on grid: true option.\n' } if (!atrules.length && !selectors.length && !props.length && !values.length) { out += "\nAwesome! Your browsers don't require any vendor prefixes." + '\nNow you can remove Autoprefixer from build steps.' } return out } ================================================ FILE: lib/old-selector.js ================================================ class OldSelector { constructor(selector, prefix) { this.prefix = prefix this.prefixed = selector.prefixed(this.prefix) this.regexp = selector.regexp(this.prefix) this.prefixeds = selector .possible() .map(x => [selector.prefixed(x), selector.regexp(x)]) this.unprefixed = selector.name this.nameRegexp = selector.regexp() } /** * Does rule contain an unnecessary prefixed selector */ check(rule) { if (!rule.selector.includes(this.prefixed)) { return false } if (!rule.selector.match(this.regexp)) { return false } if (this.isHack(rule)) { return false } return true } /** * Is rule a hack without unprefixed version bottom */ isHack(rule) { let index = rule.parent.index(rule) + 1 let rules = rule.parent.nodes while (index < rules.length) { let before = rules[index].selector if (!before) { return true } if (before.includes(this.unprefixed) && before.match(this.nameRegexp)) { return false } let some = false for (let [string, regexp] of this.prefixeds) { if (before.includes(string) && before.match(regexp)) { some = true break } } if (!some) { return true } index += 1 } return true } } module.exports = OldSelector ================================================ FILE: lib/old-value.js ================================================ let utils = require('./utils') class OldValue { constructor(unprefixed, prefixed, string, regexp) { this.unprefixed = unprefixed this.prefixed = prefixed this.string = string || prefixed this.regexp = regexp || utils.regexp(prefixed) } /** * Check, that value contain old value */ check(value) { if (value.includes(this.string)) { return !!value.match(this.regexp) } return false } } module.exports = OldValue ================================================ FILE: lib/prefixer.js ================================================ let Browsers = require('./browsers') let utils = require('./utils') let vendor = require('./vendor') /** * Recursively clone objects */ function clone(obj, parent) { let cloned = new obj.constructor() for (let i of Object.keys(obj || {})) { let value = obj[i] if (i === 'parent' && typeof value === 'object') { if (parent) { cloned[i] = parent } } else if (i === 'source' || i === null) { cloned[i] = value } else if (Array.isArray(value)) { cloned[i] = value.map(x => clone(x, cloned)) } else if ( i !== '_autoprefixerPrefix' && i !== '_autoprefixerValues' && i !== 'proxyCache' ) { if (typeof value === 'object' && value !== null) { value = clone(value, cloned) } cloned[i] = value } } return cloned } class Prefixer { constructor(name, prefixes, all) { this.prefixes = prefixes this.name = name this.all = all } /** * Clone node and clean autprefixer custom caches */ static clone(node, overrides) { let cloned = clone(node) for (let name in overrides) { cloned[name] = overrides[name] } return cloned } /** * Add hack to selected names */ static hack(klass) { if (!this.hacks) { this.hacks = {} } return klass.names.map(name => { this.hacks[name] = klass return this.hacks[name] }) } /** * Load hacks for some names */ static load(name, prefixes, all) { let Klass = this.hacks && this.hacks[name] if (Klass) { return new Klass(name, prefixes, all) } else { return new this(name, prefixes, all) } } /** * Shortcut for Prefixer.clone */ clone(node, overrides) { return Prefixer.clone(node, overrides) } /** * Find prefix in node parents */ parentPrefix(node) { let prefix if (typeof node._autoprefixerPrefix !== 'undefined') { prefix = node._autoprefixerPrefix } else if (node.type === 'decl' && node.prop[0] === '-') { prefix = vendor.prefix(node.prop) } else if (node.type === 'root') { prefix = false } else if ( node.type === 'rule' && node.selector.includes(':-') && /:(-\w+-)/.test(node.selector) ) { prefix = node.selector.match(/:(-\w+-)/)[1] } else if (node.type === 'atrule' && node.name[0] === '-') { prefix = vendor.prefix(node.name) } else { prefix = this.parentPrefix(node.parent) } if (!Browsers.prefixes().includes(prefix)) { prefix = false } node._autoprefixerPrefix = prefix return node._autoprefixerPrefix } /** * Clone node with prefixes */ process(node, result) { if (!this.check(node)) { return undefined } let parent = this.parentPrefix(node) let prefixes = this.prefixes.filter( prefix => !parent || parent === utils.removeNote(prefix) ) let added = [] for (let prefix of prefixes) { if (this.add(node, prefix, added.concat([prefix]), result)) { added.push(prefix) } } return added } } module.exports = Prefixer ================================================ FILE: lib/prefixes.js ================================================ let AtRule = require('./at-rule') let Browsers = require('./browsers') let Declaration = require('./declaration') let hackAlignContent = require('./hacks/align-content') let hackAlignItems = require('./hacks/align-items') let hackAlignSelf = require('./hacks/align-self') let hackAnimation = require('./hacks/animation') let hackAppearance = require('./hacks/appearance') let hackAutofill = require('./hacks/autofill') let hackBackdropFilter = require('./hacks/backdrop-filter') let hackBackgroundClip = require('./hacks/background-clip') let hackBackgroundSize = require('./hacks/background-size') let hackBlockLogical = require('./hacks/block-logical') let hackBorderImage = require('./hacks/border-image') let hackBorderRadius = require('./hacks/border-radius') let hackBreakProps = require('./hacks/break-props') let hackCrossFade = require('./hacks/cross-fade') let hackDisplayFlex = require('./hacks/display-flex') let hackDisplayGrid = require('./hacks/display-grid') let hackFileSelectorButton = require('./hacks/file-selector-button') let hackFilter = require('./hacks/filter') let hackFilterValue = require('./hacks/filter-value') let hackFlex = require('./hacks/flex') let hackFlexBasis = require('./hacks/flex-basis') let hackFlexDirection = require('./hacks/flex-direction') let hackFlexFlow = require('./hacks/flex-flow') let hackFlexGrow = require('./hacks/flex-grow') let hackFlexShrink = require('./hacks/flex-shrink') let hackFlexWrap = require('./hacks/flex-wrap') let hackFullscreen = require('./hacks/fullscreen') let hackGradient = require('./hacks/gradient') let hackGridArea = require('./hacks/grid-area') let hackGridColumnAlign = require('./hacks/grid-column-align') let hackGridEnd = require('./hacks/grid-end') let hackGridRowAlign = require('./hacks/grid-row-align') let hackGridRowColumn = require('./hacks/grid-row-column') let hackGridRowsColumns = require('./hacks/grid-rows-columns') let hackGridStart = require('./hacks/grid-start') let hackGridTemplate = require('./hacks/grid-template') let hackGridTemplateAreas = require('./hacks/grid-template-areas') let hackImageRendering = require('./hacks/image-rendering') let hackImageSet = require('./hacks/image-set') let hackInlineLogical = require('./hacks/inline-logical') let hackIntrinsic = require('./hacks/intrinsic') let hackJustifyContent = require('./hacks/justify-content') let hackMaskBorder = require('./hacks/mask-border') let hackMaskComposite = require('./hacks/mask-composite') let hackOrder = require('./hacks/order') let hackOverscrollBehavior = require('./hacks/overscroll-behavior') let hackPixelated = require('./hacks/pixelated') let hackPlaceSelf = require('./hacks/place-self') let hackPlaceholder = require('./hacks/placeholder') let hackPlaceholderShown = require('./hacks/placeholder-shown') let hackPrintColorAdjust = require('./hacks/print-color-adjust') let hackTextDecoration = require('./hacks/text-decoration') let hackTextDecorationSkipInk = require('./hacks/text-decoration-skip-ink') let hackTextEmphasisPosition = require('./hacks/text-emphasis-position') let hackTransformDecl = require('./hacks/transform-decl') let hackUserSelect = require('./hacks/user-select') let hackWritingMode = require('./hacks/writing-mode') let Processor = require('./processor') let Resolution = require('./resolution') let Selector = require('./selector') let Supports = require('./supports') let Transition = require('./transition') let utils = require('./utils') let Value = require('./value') let vendor = require('./vendor') Selector.hack(hackAutofill) Selector.hack(hackFullscreen) Selector.hack(hackPlaceholder) Selector.hack(hackPlaceholderShown) Selector.hack(hackFileSelectorButton) Declaration.hack(hackFlex) Declaration.hack(hackOrder) Declaration.hack(hackFilter) Declaration.hack(hackGridEnd) Declaration.hack(hackAnimation) Declaration.hack(hackFlexFlow) Declaration.hack(hackFlexGrow) Declaration.hack(hackFlexWrap) Declaration.hack(hackGridArea) Declaration.hack(hackPlaceSelf) Declaration.hack(hackGridStart) Declaration.hack(hackAlignSelf) Declaration.hack(hackAppearance) Declaration.hack(hackFlexBasis) Declaration.hack(hackMaskBorder) Declaration.hack(hackMaskComposite) Declaration.hack(hackAlignItems) Declaration.hack(hackUserSelect) Declaration.hack(hackFlexShrink) Declaration.hack(hackBreakProps) Declaration.hack(hackWritingMode) Declaration.hack(hackBorderImage) Declaration.hack(hackAlignContent) Declaration.hack(hackBorderRadius) Declaration.hack(hackBlockLogical) Declaration.hack(hackGridTemplate) Declaration.hack(hackInlineLogical) Declaration.hack(hackGridRowAlign) Declaration.hack(hackTransformDecl) Declaration.hack(hackFlexDirection) Declaration.hack(hackImageRendering) Declaration.hack(hackBackdropFilter) Declaration.hack(hackBackgroundClip) Declaration.hack(hackTextDecoration) Declaration.hack(hackJustifyContent) Declaration.hack(hackBackgroundSize) Declaration.hack(hackGridRowColumn) Declaration.hack(hackGridRowsColumns) Declaration.hack(hackGridColumnAlign) Declaration.hack(hackOverscrollBehavior) Declaration.hack(hackGridTemplateAreas) Declaration.hack(hackPrintColorAdjust) Declaration.hack(hackTextEmphasisPosition) Declaration.hack(hackTextDecorationSkipInk) Value.hack(hackGradient) Value.hack(hackIntrinsic) Value.hack(hackPixelated) Value.hack(hackImageSet) Value.hack(hackCrossFade) Value.hack(hackDisplayFlex) Value.hack(hackDisplayGrid) Value.hack(hackFilterValue) let declsCache = new Map() class Prefixes { constructor(data, browsers, options = {}) { this.data = data this.browsers = browsers this.options = options ;[this.add, this.remove] = this.preprocess(this.select(this.data)) this.transition = new Transition(this) this.processor = new Processor(this) } /** * Return clone instance to remove all prefixes */ cleaner() { if (this.cleanerCache) { return this.cleanerCache } if (this.browsers.selected.length) { let empty = new Browsers(this.browsers.data, []) this.cleanerCache = new Prefixes(this.data, empty, this.options) } else { return this } return this.cleanerCache } /** * Declaration loader with caching */ decl(prop) { if (!declsCache.has(prop)) { declsCache.set(prop, Declaration.load(prop)) } return declsCache.get(prop) } /** * Group declaration by unprefixed property to check them */ group(decl) { let rule = decl.parent let index = rule.index(decl) let { length } = rule.nodes let unprefixed = this.unprefixed(decl.prop) let checker = (step, callback) => { index += step while (index >= 0 && index < length) { let other = rule.nodes[index] if (other.type === 'decl') { if (step === -1 && other.prop === unprefixed) { if (!Browsers.withPrefix(other.value)) { break } } if (this.unprefixed(other.prop) !== unprefixed) { break } else if (callback(other) === true) { return true } if (step === +1 && other.prop === unprefixed) { if (!Browsers.withPrefix(other.value)) { break } } } index += step } return false } return { down(callback) { return checker(+1, callback) }, up(callback) { return checker(-1, callback) } } } /** * Normalize prefix for remover */ normalize(prop) { return this.decl(prop).normalize(prop) } /** * Return prefixed version of property */ prefixed(prop, prefix) { prop = vendor.unprefixed(prop) return this.decl(prop).prefixed(prop, prefix) } /** * Cache prefixes data to fast CSS processing */ preprocess(selected) { let add = { '@supports': new Supports(Prefixes, this), 'selectors': [] } for (let name in selected.add) { let prefixes = selected.add[name] if (name === '@keyframes' || name === '@viewport') { add[name] = new AtRule(name, prefixes, this) } else if (name === '@resolution') { add[name] = new Resolution(name, prefixes, this) } else if (this.data[name].selector) { add.selectors.push(Selector.load(name, prefixes, this)) } else { let props = this.data[name].props if (props) { let value = Value.load(name, prefixes, this) for (let prop of props) { if (!add[prop]) { add[prop] = { values: [] } } add[prop].values.push(value) } } else { let values = (add[name] && add[name].values) || [] add[name] = Declaration.load(name, prefixes, this) add[name].values = values } } } let remove = { selectors: [] } for (let name in selected.remove) { let prefixes = selected.remove[name] if (this.data[name].selector) { let selector = Selector.load(name, prefixes) for (let prefix of prefixes) { remove.selectors.push(selector.old(prefix)) } } else if (name === '@keyframes' || name === '@viewport') { for (let prefix of prefixes) { let prefixed = `@${prefix}${name.slice(1)}` remove[prefixed] = { remove: true } } } else if (name === '@resolution') { remove[name] = new Resolution(name, prefixes, this) } else { let props = this.data[name].props if (props) { let value = Value.load(name, [], this) for (let prefix of prefixes) { let old = value.old(prefix) if (old) { for (let prop of props) { if (!remove[prop]) { remove[prop] = {} } if (!remove[prop].values) { remove[prop].values = [] } remove[prop].values.push(old) } } } } else { for (let p of prefixes) { let olds = this.decl(name).old(name, p) if (name === 'align-self') { let a = add[name] && add[name].prefixes if (a) { if (p === '-webkit- 2009' && a.includes('-webkit-')) { continue } else if (p === '-webkit-' && a.includes('-webkit- 2009')) { continue } } } for (let prefixed of olds) { if (!remove[prefixed]) { remove[prefixed] = {} } remove[prefixed].remove = true } } } } } return [add, remove] } /** * Select prefixes from data, which is necessary for selected browsers */ select(list) { let selected = { add: {}, remove: {} } for (let name in list) { let data = list[name] let add = data.browsers.map(i => { let params = i.split(' ') return { browser: `${params[0]} ${params[1]}`, note: params[2] } }) let notes = add .filter(i => i.note) .map(i => `${this.browsers.prefix(i.browser)} ${i.note}`) notes = utils.uniq(notes) add = add .filter(i => this.browsers.isSelected(i.browser)) .map(i => { let prefix = this.browsers.prefix(i.browser) if (i.note) { return `${prefix} ${i.note}` } else { return prefix } }) add = this.sort(utils.uniq(add)) if (this.options.flexbox === 'no-2009') { add = add.filter(i => !i.includes('2009')) } let all = data.browsers.map(i => this.browsers.prefix(i)) if (data.mistakes) { all = all.concat(data.mistakes) } all = all.concat(notes) all = utils.uniq(all) if (add.length) { selected.add[name] = add if (add.length < all.length) { selected.remove[name] = all.filter(i => !add.includes(i)) } } else { selected.remove[name] = all } } return selected } /** * Sort vendor prefixes */ sort(prefixes) { return prefixes.sort((a, b) => { let aLength = utils.removeNote(a).length let bLength = utils.removeNote(b).length if (aLength === bLength) { return b.length - a.length } else { return bLength - aLength } }) } /** * Return unprefixed version of property */ unprefixed(prop) { let value = this.normalize(vendor.unprefixed(prop)) if (value === 'flex-direction') { value = 'flex-flow' } return value } /** * Return values, which must be prefixed in selected property */ values(type, prop) { let data = this[type] let global = data['*'] && data['*'].values let values = data[prop] && data[prop].values if (global && values) { return utils.uniq(global.concat(values)) } else { return global || values || [] } } } module.exports = Prefixes ================================================ FILE: lib/processor.js ================================================ let parser = require('postcss-value-parser') let Value = require('./value') let insertAreas = require('./hacks/grid-utils').insertAreas const OLD_LINEAR = /(^|[^-])linear-gradient\(\s*(top|left|right|bottom)/i const OLD_RADIAL = /(^|[^-])radial-gradient\(\s*\d+(\w*|%)\s+\d+(\w*|%)\s*,/i const IGNORE_NEXT = /(!\s*)?autoprefixer:\s*ignore\s+next/i const GRID_REGEX = /(!\s*)?autoprefixer\s*grid:\s*(on|off|(no-)?autoplace)/i const SIZES = [ 'width', 'height', 'min-width', 'max-width', 'min-height', 'max-height', 'inline-size', 'min-inline-size', 'max-inline-size', 'block-size', 'min-block-size', 'max-block-size' ] function hasGridTemplate(decl) { return decl.parent.some( i => i.prop === 'grid-template' || i.prop === 'grid-template-areas' ) } function hasRowsAndColumns(decl) { let hasRows = decl.parent.some(i => i.prop === 'grid-template-rows') let hasColumns = decl.parent.some(i => i.prop === 'grid-template-columns') return hasRows && hasColumns } class Processor { constructor(prefixes) { this.prefixes = prefixes } /** * Add necessary prefixes */ add(css, result) { // At-rules let resolution = this.prefixes.add['@resolution'] let keyframes = this.prefixes.add['@keyframes'] let viewport = this.prefixes.add['@viewport'] let supports = this.prefixes.add['@supports'] css.walkAtRules(rule => { if (rule.name === 'keyframes') { if (!this.disabled(rule, result)) { return keyframes && keyframes.process(rule) } } else if (rule.name === 'viewport') { if (!this.disabled(rule, result)) { return viewport && viewport.process(rule) } } else if (rule.name === 'supports') { if ( this.prefixes.options.supports !== false && !this.disabled(rule, result) ) { return supports.process(rule) } } else if (rule.name === 'media' && rule.params.includes('-resolution')) { if (!this.disabled(rule, result)) { return resolution && resolution.process(rule) } } return undefined }) // Selectors css.walkRules(rule => { if (this.disabled(rule, result)) return undefined return this.prefixes.add.selectors.map(selector => { return selector.process(rule, result) }) }) function insideGrid(decl) { return decl.parent.nodes.some(node => { if (node.type !== 'decl') return false let displayGrid = node.prop === 'display' && /(inline-)?grid/.test(node.value) let gridTemplate = node.prop.startsWith('grid-template') let gridGap = /^grid-([A-z]+-)?gap/.test(node.prop) return displayGrid || gridTemplate || gridGap }) } let gridPrefixes = this.gridStatus(css, result) && this.prefixes.add['grid-area'] && this.prefixes.add['grid-area'].prefixes css.walkDecls(decl => { if (this.disabledDecl(decl, result)) return undefined let parent = decl.parent let prop = decl.prop let value = decl.value if (prop === 'color-adjust') { if (parent.every(i => i.prop !== 'print-color-adjust')) { result.warn( 'Replace color-adjust to print-color-adjust. ' + 'The color-adjust shorthand is currently deprecated.', { node: decl } ) } } else if (prop === 'grid-row-span') { result.warn( 'grid-row-span is not part of final Grid Layout. Use grid-row.', { node: decl } ) return undefined } else if (prop === 'grid-column-span') { result.warn( 'grid-column-span is not part of final Grid Layout. Use grid-column.', { node: decl } ) return undefined } else if (prop === 'display' && value === 'box') { result.warn( 'You should write display: flex by final spec ' + 'instead of display: box', { node: decl } ) return undefined } else if (prop === 'text-emphasis-position') { if (value === 'under' || value === 'over') { result.warn( 'You should use 2 values for text-emphasis-position ' + 'For example, `under left` instead of just `under`.', { node: decl } ) } } else if (prop === 'text-decoration-skip' && value === 'ink') { result.warn( 'Replace text-decoration-skip: ink to ' + 'text-decoration-skip-ink: auto, because spec had been changed', { node: decl } ) } else { if (gridPrefixes && this.gridStatus(decl, result)) { if (decl.value === 'subgrid') { result.warn('IE does not support subgrid', { node: decl }) } if (/^(align|justify|place)-items$/.test(prop) && insideGrid(decl)) { let fixed = prop.replace('-items', '-self') result.warn( `IE does not support ${prop} on grid containers. ` + `Try using ${fixed} on child elements instead: ` + `${decl.parent.selector} > * { ${fixed}: ${decl.value} }`, { node: decl } ) } else if ( /^(align|justify|place)-content$/.test(prop) && insideGrid(decl) ) { result.warn(`IE does not support ${decl.prop} on grid containers`, { node: decl }) } else if (prop === 'display' && decl.value === 'contents') { result.warn( 'Please do not use display: contents; ' + 'if you have grid setting enabled', { node: decl } ) return undefined } else if (decl.prop === 'grid-gap') { let status = this.gridStatus(decl, result) if ( status === 'autoplace' && !hasRowsAndColumns(decl) && !hasGridTemplate(decl) ) { result.warn( 'grid-gap only works if grid-template(-areas) is being ' + 'used or both rows and columns have been declared ' + 'and cells have not been manually ' + 'placed inside the explicit grid', { node: decl } ) } else if ( (status === true || status === 'no-autoplace') && !hasGridTemplate(decl) ) { result.warn( 'grid-gap only works if grid-template(-areas) is being used', { node: decl } ) } } else if (prop === 'grid-auto-columns') { result.warn('grid-auto-columns is not supported by IE', { node: decl }) return undefined } else if (prop === 'grid-auto-rows') { result.warn('grid-auto-rows is not supported by IE', { node: decl }) return undefined } else if (prop === 'grid-auto-flow') { let hasRows = parent.some(i => i.prop === 'grid-template-rows') let hasCols = parent.some(i => i.prop === 'grid-template-columns') if (hasGridTemplate(decl)) { result.warn('grid-auto-flow is not supported by IE', { node: decl }) } else if (value.includes('dense')) { result.warn('grid-auto-flow: dense is not supported by IE', { node: decl }) } else if (!hasRows && !hasCols) { result.warn( 'grid-auto-flow works only if grid-template-rows and ' + 'grid-template-columns are present in the same rule', { node: decl } ) } return undefined } else if (value.includes('auto-fit')) { result.warn('auto-fit value is not supported by IE', { node: decl, word: 'auto-fit' }) return undefined } else if (value.includes('auto-fill')) { result.warn('auto-fill value is not supported by IE', { node: decl, word: 'auto-fill' }) return undefined } else if (prop.startsWith('grid-template') && value.includes('[')) { result.warn( 'Autoprefixer currently does not support line names. ' + 'Try using grid-template-areas instead.', { node: decl, word: '[' } ) } } if (value.includes('radial-gradient')) { if (OLD_RADIAL.test(decl.value)) { result.warn( 'Gradient has outdated direction syntax. ' + 'New syntax is like `closest-side at 0 0` ' + 'instead of `0 0, closest-side`.', { node: decl } ) } else { let ast = parser(value) for (let i of ast.nodes) { if (i.type === 'function' && i.value === 'radial-gradient') { for (let word of i.nodes) { if (word.type === 'word') { if (word.value === 'cover') { result.warn( 'Gradient has outdated direction syntax. ' + 'Replace `cover` to `farthest-corner`.', { node: decl } ) } else if (word.value === 'contain') { result.warn( 'Gradient has outdated direction syntax. ' + 'Replace `contain` to `closest-side`.', { node: decl } ) } } } } } } } if (value.includes('linear-gradient')) { if (OLD_LINEAR.test(value)) { result.warn( 'Gradient has outdated direction syntax. ' + 'New syntax is like `to left` instead of `right`.', { node: decl } ) } } } if (SIZES.includes(decl.prop)) { if (!decl.value.includes('-fill-available')) { if (decl.value.includes('fill-available')) { result.warn( 'Replace fill-available to stretch, ' + 'because spec had been changed', { node: decl } ) } else if (decl.value.includes('fill')) { let ast = parser(value) if (ast.nodes.some(i => i.type === 'word' && i.value === 'fill')) { result.warn( 'Replace fill to stretch, because spec had been changed', { node: decl } ) } } } } let prefixer if (decl.prop === 'transition' || decl.prop === 'transition-property') { // Transition return this.prefixes.transition.add(decl, result) } else if (decl.prop === 'align-self') { // align-self flexbox or grid let display = this.displayType(decl) if (display !== 'grid' && this.prefixes.options.flexbox !== false) { prefixer = this.prefixes.add['align-self'] if (prefixer && prefixer.prefixes) { prefixer.process(decl) } } if (this.gridStatus(decl, result) !== false) { prefixer = this.prefixes.add['grid-row-align'] if (prefixer && prefixer.prefixes) { return prefixer.process(decl, result) } } } else if (decl.prop === 'justify-self') { // justify-self flexbox or grid if (this.gridStatus(decl, result) !== false) { prefixer = this.prefixes.add['grid-column-align'] if (prefixer && prefixer.prefixes) { return prefixer.process(decl, result) } } } else if (decl.prop === 'place-self') { prefixer = this.prefixes.add['place-self'] if ( prefixer && prefixer.prefixes && this.gridStatus(decl, result) !== false ) { return prefixer.process(decl, result) } } else { // Properties prefixer = this.prefixes.add[decl.prop] if (prefixer && prefixer.prefixes) { return prefixer.process(decl, result) } } return undefined }) // Insert grid-area prefixes. We need to be able to store the different // rules as a data and hack API is not enough for this if (this.gridStatus(css, result)) { insertAreas(css, this.disabled) } // Values return css.walkDecls(decl => { if (this.disabledValue(decl, result)) return let unprefixed = this.prefixes.unprefixed(decl.prop) let list = this.prefixes.values('add', unprefixed) if (Array.isArray(list)) { for (let value of list) { if (value.process) value.process(decl, result) } } Value.save(this.prefixes, decl) }) } /** * Check for control comment and global options */ disabled(node, result) { if (!node) return false if (node._autoprefixerDisabled !== undefined) { return node._autoprefixerDisabled } if (node.parent) { let p = node.prev() if (p && p.type === 'comment' && IGNORE_NEXT.test(p.text)) { node._autoprefixerDisabled = true node._autoprefixerSelfDisabled = true return true } } let value = null if (node.nodes) { let status node.each(i => { if (i.type !== 'comment') return if (/(!\s*)?autoprefixer:\s*(off|on)/i.test(i.text)) { if (typeof status !== 'undefined') { result.warn( 'Second Autoprefixer control comment ' + 'was ignored. Autoprefixer applies control ' + 'comment to whole block, not to next rules.', { node: i } ) } else { status = /on/i.test(i.text) } } }) if (status !== undefined) { value = !status } } if (!node.nodes || value === null) { if (node.parent) { let isParentDisabled = this.disabled(node.parent, result) if (node.parent._autoprefixerSelfDisabled === true) { value = false } else { value = isParentDisabled } } else { value = false } } node._autoprefixerDisabled = value return value } /** * Check for grid/flexbox options. */ disabledDecl(node, result) { if (node.type === 'decl' && this.gridStatus(node, result) === false) { if (node.prop.includes('grid') || node.prop === 'justify-items') { return true } } if (node.type === 'decl' && this.prefixes.options.flexbox === false) { let other = ['order', 'justify-content', 'align-items', 'align-content'] if (node.prop.includes('flex') || other.includes(node.prop)) { return true } } return this.disabled(node, result) } /** * Check for grid/flexbox options. */ disabledValue(node, result) { if (this.gridStatus(node, result) === false && node.type === 'decl') { if (node.prop === 'display' && node.value.includes('grid')) { return true } } if (this.prefixes.options.flexbox === false && node.type === 'decl') { if (node.prop === 'display' && node.value.includes('flex')) { return true } } if (node.type === 'decl' && node.prop === 'content') { return true } return this.disabled(node, result) } /** * Is it flebox or grid rule */ displayType(decl) { for (let i of decl.parent.nodes) { if (i.prop !== 'display') { continue } if (i.value.includes('flex')) { return 'flex' } if (i.value.includes('grid')) { return 'grid' } } return false } /** * Set grid option via control comment */ gridStatus(node, result) { if (!node) return false if (node._autoprefixerGridStatus !== undefined) { return node._autoprefixerGridStatus } let value = null if (node.nodes) { let status node.each(i => { if (i.type !== 'comment') return if (GRID_REGEX.test(i.text)) { let hasAutoplace = /:\s*autoplace/i.test(i.text) let noAutoplace = /no-autoplace/i.test(i.text) if (typeof status !== 'undefined') { result.warn( 'Second Autoprefixer grid control comment was ' + 'ignored. Autoprefixer applies control comments to the whole ' + 'block, not to the next rules.', { node: i } ) } else if (hasAutoplace) { status = 'autoplace' } else if (noAutoplace) { status = true } else { status = /on/i.test(i.text) } } }) if (status !== undefined) { value = status } } if (node.type === 'atrule' && node.name === 'supports') { let params = node.params if (params.includes('grid') && params.includes('auto')) { value = false } } if (!node.nodes || value === null) { if (node.parent) { let isParentGrid = this.gridStatus(node.parent, result) if (node.parent._autoprefixerSelfDisabled === true) { value = false } else { value = isParentGrid } } else if (typeof this.prefixes.options.grid !== 'undefined') { value = this.prefixes.options.grid } else if (typeof process.env.AUTOPREFIXER_GRID !== 'undefined') { if (process.env.AUTOPREFIXER_GRID === 'autoplace') { value = 'autoplace' } else { value = true } } else { value = false } } node._autoprefixerGridStatus = value return value } /** * Normalize spaces in cascade declaration group */ reduceSpaces(decl) { let stop = false this.prefixes.group(decl).up(() => { stop = true return true }) if (stop) { return } let parts = decl.raw('before').split('\n') let prevMin = parts[parts.length - 1].length let diff = false this.prefixes.group(decl).down(other => { parts = other.raw('before').split('\n') let last = parts.length - 1 if (parts[last].length > prevMin) { if (diff === false) { diff = parts[last].length - prevMin } parts[last] = parts[last].slice(0, -diff) other.raws.before = parts.join('\n') } }) } /** * Remove unnecessary pefixes */ remove(css, result) { // At-rules let resolution = this.prefixes.remove['@resolution'] css.walkAtRules((rule, i) => { if (this.prefixes.remove[`@${rule.name}`]) { if (!this.disabled(rule, result)) { rule.parent.removeChild(i) } } else if ( rule.name === 'media' && rule.params.includes('-resolution') && resolution ) { resolution.clean(rule) } }) // Selectors css.walkRules((rule, i) => { if (this.disabled(rule, result)) return for (let checker of this.prefixes.remove.selectors) { if (checker.check(rule)) { rule.parent.removeChild(i) return } } }) return css.walkDecls((decl, i) => { if (this.disabled(decl, result)) return let rule = decl.parent let unprefixed = this.prefixes.unprefixed(decl.prop) // Transition if (decl.prop === 'transition' || decl.prop === 'transition-property') { this.prefixes.transition.remove(decl) } // Properties if ( this.prefixes.remove[decl.prop] && this.prefixes.remove[decl.prop].remove ) { let notHack = this.prefixes.group(decl).down(other => { return this.prefixes.normalize(other.prop) === unprefixed }) if (unprefixed === 'flex-flow') { notHack = true } if (decl.prop === '-webkit-box-orient') { let hacks = { 'flex-direction': true, 'flex-flow': true } if (!decl.parent.some(j => hacks[j.prop])) return } if (notHack && !this.withHackValue(decl)) { if (decl.raw('before').includes('\n')) { this.reduceSpaces(decl) } rule.removeChild(i) return } } // Values for (let checker of this.prefixes.values('remove', unprefixed)) { if (!checker.check) continue if (!checker.check(decl.value)) continue unprefixed = checker.unprefixed let notHack = this.prefixes.group(decl).down(other => { return other.value.includes(unprefixed) }) if (notHack) { rule.removeChild(i) return } } }) } /** * Some rare old values, which is not in standard */ withHackValue(decl) { return ( (decl.prop === '-webkit-background-clip' && decl.value === 'text') || // Do not remove -webkit-box-orient when -webkit-line-clamp is present. // https://github.com/postcss/autoprefixer/issues/1510 (decl.prop === '-webkit-box-orient' && decl.parent.some(d => d.prop === '-webkit-line-clamp')) ) } } module.exports = Processor ================================================ FILE: lib/resolution.js ================================================ let FractionJs = require('fraction.js') let Prefixer = require('./prefixer') let utils = require('./utils') const REGEXP = /(min|max)-resolution\s*:\s*\d*\.?\d+(dppx|dpcm|dpi|x)/gi const SPLIT = /(min|max)-resolution(\s*:\s*)(\d*\.?\d+)(dppx|dpcm|dpi|x)/i class Resolution extends Prefixer { /** * Remove prefixed queries */ clean(rule) { if (!this.bad) { this.bad = [] for (let prefix of this.prefixes) { this.bad.push(this.prefixName(prefix, 'min')) this.bad.push(this.prefixName(prefix, 'max')) } } rule.params = utils.editList(rule.params, queries => { return queries.filter(query => this.bad.every(i => !query.includes(i))) }) } /** * Return prefixed query name */ prefixName(prefix, name) { if (prefix === '-moz-') { return name + '--moz-device-pixel-ratio' } else { return prefix + name + '-device-pixel-ratio' } } /** * Return prefixed query */ prefixQuery(prefix, name, colon, value, units) { value = new FractionJs(value) // 1dpcm = 2.54dpi // 1dppx = 96dpi if (units === 'dpi') { value = value.div(96) } else if (units === 'dpcm') { value = value.mul(2.54).div(96) } value = value.simplify() if (prefix === '-o-') { value = value.n + '/' + value.d } return this.prefixName(prefix, name) + colon + value } /** * Add prefixed queries */ process(rule) { let parent = this.parentPrefix(rule) let prefixes = parent ? [parent] : this.prefixes rule.params = utils.editList(rule.params, (origin, prefixed) => { for (let query of origin) { if ( !query.includes('min-resolution') && !query.includes('max-resolution') ) { prefixed.push(query) continue } for (let prefix of prefixes) { let processed = query.replace(REGEXP, str => { let parts = str.match(SPLIT) return this.prefixQuery( prefix, parts[1], parts[2], parts[3], parts[4] ) }) prefixed.push(processed) } prefixed.push(query) } return utils.uniq(prefixed) }) } } module.exports = Resolution ================================================ FILE: lib/selector.js ================================================ let { list } = require('postcss') let Browsers = require('./browsers') let OldSelector = require('./old-selector') let Prefixer = require('./prefixer') let utils = require('./utils') class Selector extends Prefixer { constructor(name, prefixes, all) { super(name, prefixes, all) this.regexpCache = new Map() } /** * Clone and add prefixes for at-rule */ add(rule, prefix) { let prefixeds = this.prefixeds(rule) if (this.already(rule, prefixeds, prefix)) { return } let cloned = this.clone(rule, { selector: prefixeds[this.name][prefix] }) rule.parent.insertBefore(rule, cloned) } /** * Is rule already prefixed before */ already(rule, prefixeds, prefix) { let index = rule.parent.index(rule) - 1 while (index >= 0) { let before = rule.parent.nodes[index] if (before.type !== 'rule') { return false } let some = false for (let key in prefixeds[this.name]) { let prefixed = prefixeds[this.name][key] if (before.selector === prefixed) { if (prefix === key) { return true } else { some = true break } } } if (!some) { return false } index -= 1 } return false } /** * Is rule selectors need to be prefixed */ check(rule) { if (rule.selector.includes(this.name)) { return !!rule.selector.match(this.regexp()) } return false } /** * Return function to fast find prefixed selector */ old(prefix) { return new OldSelector(this, prefix) } /** * All possible prefixes */ possible() { return Browsers.prefixes() } /** * Return prefixed version of selector */ prefixed(prefix) { return this.name.replace(/^(\W*)/, `$1${prefix}`) } /** * Return all possible selector prefixes */ prefixeds(rule) { if (rule._autoprefixerPrefixeds) { if (rule._autoprefixerPrefixeds[this.name]) { return rule._autoprefixerPrefixeds } } else { rule._autoprefixerPrefixeds = {} } let prefixeds = {} if (rule.selector.includes(',')) { let ruleParts = list.comma(rule.selector) let toProcess = ruleParts.filter(el => el.includes(this.name)) for (let prefix of this.possible()) { prefixeds[prefix] = toProcess .map(el => this.replace(el, prefix)) .join(', ') } } else { for (let prefix of this.possible()) { prefixeds[prefix] = this.replace(rule.selector, prefix) } } rule._autoprefixerPrefixeds[this.name] = prefixeds return rule._autoprefixerPrefixeds } /** * Lazy loadRegExp for name */ regexp(prefix) { if (!this.regexpCache.has(prefix)) { let name = prefix ? this.prefixed(prefix) : this.name this.regexpCache.set( prefix, new RegExp(`(^|[^:"'=])${utils.escapeRegexp(name)}`, 'gi') ) } return this.regexpCache.get(prefix) } /** * Replace selectors by prefixed one */ replace(selector, prefix) { return selector.replace(this.regexp(), `$1${this.prefixed(prefix)}`) } } module.exports = Selector ================================================ FILE: lib/supports.js ================================================ let featureQueries = require('caniuse-lite/data/features/css-featurequeries.js') let feature = require('caniuse-lite/dist/unpacker/feature') let { parse } = require('postcss') let brackets = require('./brackets') let Browsers = require('./browsers') let utils = require('./utils') let Value = require('./value') let data = feature(featureQueries) let supported = [] for (let browser in data.stats) { let versions = data.stats[browser] for (let version in versions) { let support = versions[version] if (/y/.test(support)) { supported.push(browser + ' ' + version) } } } class Supports { constructor(Prefixes, all) { this.Prefixes = Prefixes this.all = all } /** * Add prefixes */ add(nodes, all) { return nodes.map(i => { if (this.isProp(i)) { let prefixed = this.prefixed(i[0]) if (prefixed.length > 1) { return this.convert(prefixed) } return i } if (typeof i === 'object') { return this.add(i, all) } return i }) } /** * Clean brackets with one child */ cleanBrackets(nodes) { return nodes.map(i => { if (typeof i !== 'object') { return i } if (i.length === 1 && typeof i[0] === 'object') { return this.cleanBrackets(i[0]) } return this.cleanBrackets(i) }) } /** * Add " or " between properties and convert it to brackets format */ convert(progress) { let result = [''] for (let i of progress) { result.push([`${i.prop}: ${i.value}`]) result.push(' or ') } result[result.length - 1] = '' return result } /** * Check global options */ disabled(node) { if (!this.all.options.grid) { if (node.prop === 'display' && node.value.includes('grid')) { return true } if (node.prop.includes('grid') || node.prop === 'justify-items') { return true } } if (this.all.options.flexbox === false) { if (node.prop === 'display' && node.value.includes('flex')) { return true } let other = ['order', 'justify-content', 'align-items', 'align-content'] if (node.prop.includes('flex') || other.includes(node.prop)) { return true } } return false } /** * Return true if prefixed property has no unprefixed */ isHack(all, unprefixed) { let check = new RegExp(`(\\(|\\s)${utils.escapeRegexp(unprefixed)}:`) return !check.test(all) } /** * Return true if brackets node is "not" word */ isNot(node) { return typeof node === 'string' && /not\s*/i.test(node) } /** * Return true if brackets node is "or" word */ isOr(node) { return typeof node === 'string' && /\s*or\s*/i.test(node) } /** * Return true if brackets node is (prop: value) */ isProp(node) { return ( typeof node === 'object' && node.length === 1 && typeof node[0] === 'string' ) } /** * Compress value functions into a string nodes */ normalize(nodes) { if (typeof nodes !== 'object') { return nodes } nodes = nodes.filter(i => i !== '') if (typeof nodes[0] === 'string') { let firstNode = nodes[0].trim() if ( firstNode.includes(':') || firstNode === 'selector' || firstNode === 'not selector' ) { return [brackets.stringify(nodes)] } } return nodes.map(i => this.normalize(i)) } /** * Parse string into declaration property and value */ parse(str) { let parts = str.split(':') let prop = parts[0] let value = parts[1] if (!value) value = '' return [prop.trim(), value.trim()] } /** * Return array of Declaration with all necessary prefixes */ prefixed(str) { let rule = this.virtual(str) if (this.disabled(rule.first)) { return rule.nodes } let result = { warn: () => null } let prefixer = this.prefixer().add[rule.first.prop] prefixer && prefixer.process && prefixer.process(rule.first, result) for (let decl of rule.nodes) { for (let value of this.prefixer().values('add', rule.first.prop)) { value.process(decl) } Value.save(this.all, decl) } return rule.nodes } /** * Return prefixer only with @supports supported browsers */ prefixer() { if (this.prefixerCache) { return this.prefixerCache } let filtered = this.all.browsers.selected.filter(i => { return supported.includes(i) }) let browsers = new Browsers( this.all.browsers.data, filtered, this.all.options ) this.prefixerCache = new this.Prefixes( this.all.data, browsers, this.all.options ) return this.prefixerCache } /** * Add prefixed declaration */ process(rule) { let ast = brackets.parse(rule.params) ast = this.normalize(ast) ast = this.remove(ast, rule.params) ast = this.add(ast, rule.params) ast = this.cleanBrackets(ast) rule.params = brackets.stringify(ast) } /** * Remove all unnecessary prefixes */ remove(nodes, all) { let i = 0 while (i < nodes.length) { if ( !this.isNot(nodes[i - 1]) && this.isProp(nodes[i]) && this.isOr(nodes[i + 1]) ) { if (this.toRemove(nodes[i][0], all)) { nodes.splice(i, 2) continue } i += 2 continue } if (typeof nodes[i] === 'object') { nodes[i] = this.remove(nodes[i], all) } i += 1 } return nodes } /** * Return true if we need to remove node */ toRemove(str, all) { let [prop, value] = this.parse(str) let unprefixed = this.all.unprefixed(prop) let cleaner = this.all.cleaner() if ( cleaner.remove[prop] && cleaner.remove[prop].remove && !this.isHack(all, unprefixed) ) { return true } for (let checker of cleaner.values('remove', unprefixed)) { if (checker.check(value)) { return true } } return false } /** * Create virtual rule to process it by prefixer */ virtual(str) { let [prop, value] = this.parse(str) let rule = parse('a{}').first rule.append({ prop, raws: { before: '' }, value }) return rule } } module.exports = Supports ================================================ FILE: lib/transition.js ================================================ let { list } = require('postcss') let parser = require('postcss-value-parser') let Browsers = require('./browsers') let vendor = require('./vendor') class Transition { constructor(prefixes) { this.props = ['transition', 'transition-property'] this.prefixes = prefixes } /** * Process transition and add prefixes for all necessary properties */ add(decl, result) { let prefix, prop let add = this.prefixes.add[decl.prop] let vendorPrefixes = this.ruleVendorPrefixes(decl) let declPrefixes = vendorPrefixes || (add && add.prefixes) || [] let params = this.parse(decl.value) let names = params.map(i => this.findProp(i)) let added = [] if (names.some(i => i[0] === '-')) { return } for (let param of params) { prop = this.findProp(param) if (prop[0] === '-') continue let prefixer = this.prefixes.add[prop] if (!prefixer || !prefixer.prefixes) continue for (prefix of prefixer.prefixes) { if (vendorPrefixes && !vendorPrefixes.some(p => prefix.includes(p))) { continue } let prefixed = this.prefixes.prefixed(prop, prefix) if (prefixed !== '-ms-transform' && !names.includes(prefixed)) { if (!this.disabled(prop, prefix)) { added.push(this.clone(prop, prefixed, param)) } } } } params = params.concat(added) let value = this.stringify(params) let webkitClean = this.stringify( this.cleanFromUnprefixed(params, '-webkit-') ) if (declPrefixes.includes('-webkit-')) { this.cloneBefore(decl, `-webkit-${decl.prop}`, webkitClean) } this.cloneBefore(decl, decl.prop, webkitClean) if (declPrefixes.includes('-o-')) { let operaClean = this.stringify(this.cleanFromUnprefixed(params, '-o-')) this.cloneBefore(decl, `-o-${decl.prop}`, operaClean) } for (prefix of declPrefixes) { if (prefix !== '-webkit-' && prefix !== '-o-') { let prefixValue = this.stringify( this.cleanOtherPrefixes(params, prefix) ) this.cloneBefore(decl, prefix + decl.prop, prefixValue) } } if (value !== decl.value && !this.already(decl, decl.prop, value)) { this.checkForWarning(result, decl) decl.cloneBefore() decl.value = value } } /** * Does we already have this declaration */ already(decl, prop, value) { return decl.parent.some(i => i.prop === prop && i.value === value) } /** * Show transition-property warning */ checkForWarning(result, decl) { if (decl.prop !== 'transition-property') { return } let isPrefixed = false let hasAssociatedProp = false decl.parent.each(i => { if (i.type !== 'decl') { return undefined } if (i.prop.indexOf('transition-') !== 0) { return undefined } let values = list.comma(i.value) // check if current Rule's transition-property comma separated value list needs prefixes if (i.prop === 'transition-property') { values.forEach(value => { let lookup = this.prefixes.add[value] if (lookup && lookup.prefixes && lookup.prefixes.length > 0) { isPrefixed = true } }) return undefined } // check if another transition-* prop in current Rule has comma separated value list hasAssociatedProp = hasAssociatedProp || values.length > 1 return false }) if (isPrefixed && hasAssociatedProp) { decl.warn( result, 'Replace transition-property to transition, ' + 'because Autoprefixer could not support ' + 'any cases of transition-property ' + 'and other transition-*' ) } } /** * Remove all non-webkit prefixes and unprefixed params if we have prefixed */ cleanFromUnprefixed(params, prefix) { let remove = params .map(i => this.findProp(i)) .filter(i => i.slice(0, prefix.length) === prefix) .map(i => this.prefixes.unprefixed(i)) let result = [] for (let param of params) { let prop = this.findProp(param) let p = vendor.prefix(prop) if (!remove.includes(prop) && (p === prefix || p === '')) { result.push(param) } } return result } cleanOtherPrefixes(params, prefix) { return params.filter(param => { let current = vendor.prefix(this.findProp(param)) return current === '' || current === prefix }) } /** * Return new param array with different name */ clone(origin, name, param) { let result = [] let changed = false for (let i of param) { if (!changed && i.type === 'word' && i.value === origin) { result.push({ type: 'word', value: name }) changed = true } else { result.push(i) } } return result } /** * Add declaration if it is not exist */ cloneBefore(decl, prop, value) { if (!this.already(decl, prop, value)) { decl.cloneBefore({ prop, value }) } } /** * Check property for disabled by option */ disabled(prop, prefix) { let other = ['order', 'justify-content', 'align-self', 'align-content'] if (prop.includes('flex') || other.includes(prop)) { if (this.prefixes.options.flexbox === false) { return true } if (this.prefixes.options.flexbox === 'no-2009') { return prefix.includes('2009') } } return undefined } /** * Find or create separator */ div(params) { for (let param of params) { for (let node of param) { if (node.type === 'div' && node.value === ',') { return node } } } return { after: ' ', type: 'div', value: ',' } } /** * Find property name */ findProp(param) { let prop = param[0].value if (/^\d/.test(prop)) { for (let [i, token] of param.entries()) { if (i !== 0 && token.type === 'word') { return token.value } } } return prop } /** * Parse properties list to array */ parse(value) { let ast = parser(value) let result = [] let param = [] for (let node of ast.nodes) { param.push(node) if (node.type === 'div' && node.value === ',') { result.push(param) param = [] } } result.push(param) return result.filter(i => i.length > 0) } /** * Process transition and remove all unnecessary properties */ remove(decl) { let params = this.parse(decl.value) params = params.filter(i => { let prop = this.prefixes.remove[this.findProp(i)] return !prop || !prop.remove }) let value = this.stringify(params) if (decl.value === value) { return } if (params.length === 0) { decl.remove() return } let double = decl.parent.some(i => { return i.prop === decl.prop && i.value === value }) let smaller = decl.parent.some(i => { return i !== decl && i.prop === decl.prop && i.value.length > value.length }) if (double || smaller) { decl.remove() return } decl.value = value } /** * Check if transition prop is inside vendor specific rule */ ruleVendorPrefixes(decl) { let { parent } = decl if (parent.type !== 'rule') { return false } else if (!parent.selector.includes(':-')) { return false } let selectors = Browsers.prefixes().filter(s => parent.selector.includes(':' + s) ) return selectors.length > 0 ? selectors : false } /** * Return properties string from array */ stringify(params) { if (params.length === 0) { return '' } let nodes = [] for (let param of params) { if (param[param.length - 1].type !== 'div') { param.push(this.div(params)) } nodes.push(...param) } if (nodes[0].type === 'div') { nodes = nodes.slice(1) } if (nodes[nodes.length - 1].type === 'div') { nodes = nodes.slice(0, +-2 + 1 || undefined) } return parser.stringify({ nodes }) } } module.exports = Transition ================================================ FILE: lib/utils.js ================================================ let { list } = require('postcss') /** * Throw special error, to tell beniary, * that this error is from Autoprefixer. */ module.exports.error = function (text) { let err = new Error(text) err.autoprefixer = true throw err } /** * Return array, that doesn’t contain duplicates. */ module.exports.uniq = function (array) { return [...new Set(array)] } /** * Return "-webkit-" on "-webkit- old" */ module.exports.removeNote = function (string) { if (!string.includes(' ')) { return string } return string.split(' ')[0] } /** * Escape RegExp symbols */ module.exports.escapeRegexp = function (string) { return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&') } /** * Return regexp to check, that CSS string contain word */ module.exports.regexp = function (word, escape = true) { if (escape) { word = this.escapeRegexp(word) } return new RegExp(`(^|[\\s,(])(${word}($|[\\s(,]))`, 'gi') } /** * Change comma list */ module.exports.editList = function (value, callback) { let origin = list.comma(value) let changed = callback(origin, []) if (origin === changed) { return value } let join = value.match(/,\s*/) join = join ? join[0] : ', ' return changed.join(join) } /** * Split the selector into parts. * It returns 3 level deep array because selectors can be comma * separated (1), space separated (2), and combined (3) * @param {String} selector selector string * @return {Array>} 3 level deep array of split selector * @see utils.test.js for examples */ module.exports.splitSelector = function (selector) { return list.comma(selector).map(i => { return list.space(i).map(k => { return k.split(/(?=\.|#)/g) }) }) } /** * Return true if a given value only contains numbers. * @param {*} value * @returns {boolean} */ module.exports.isPureNumber = function (value) { if (typeof value === 'number') { return true } if (typeof value === 'string') { return /^[0-9]+$/.test(value) } return false } ================================================ FILE: lib/value.js ================================================ let OldValue = require('./old-value') let Prefixer = require('./prefixer') let utils = require('./utils') let vendor = require('./vendor') class Value extends Prefixer { /** * Clone decl for each prefixed values */ static save(prefixes, decl) { let prop = decl.prop let result = [] for (let prefix in decl._autoprefixerValues) { let value = decl._autoprefixerValues[prefix] if (value === decl.value) { continue } let item let propPrefix = vendor.prefix(prop) if (propPrefix === '-pie-') { continue } if (propPrefix === prefix) { item = decl.value = value result.push(item) continue } let prefixed = prefixes.prefixed(prop, prefix) let rule = decl.parent if (!rule.every(i => i.prop !== prefixed)) { result.push(item) continue } let trimmed = value.replace(/\s+/, ' ') let already = rule.some( i => i.prop === decl.prop && i.value.replace(/\s+/, ' ') === trimmed ) if (already) { result.push(item) continue } let cloned = this.clone(decl, { value }) item = decl.parent.insertBefore(decl, cloned) result.push(item) } return result } /** * Save values with next prefixed token */ add(decl, prefix) { if (!decl._autoprefixerValues) { decl._autoprefixerValues = {} } let value = decl._autoprefixerValues[prefix] || this.value(decl) let before do { before = value value = this.replace(value, prefix) if (value === false) return } while (value !== before) decl._autoprefixerValues[prefix] = value } /** * Is declaration need to be prefixed */ check(decl) { let value = decl.value if (!value.includes(this.name)) { return false } return !!value.match(this.regexp()) } /** * Return function to fast find prefixed value */ old(prefix) { return new OldValue(this.name, prefix + this.name) } /** * Lazy regexp loading */ regexp() { return this.regexpCache || (this.regexpCache = utils.regexp(this.name)) } /** * Add prefix to values in string */ replace(string, prefix) { return string.replace(this.regexp(), `$1${prefix}$2`) } /** * Get value with comments if it was not changed */ value(decl) { if (decl.raws.value && decl.raws.value.value === decl.value) { return decl.raws.value.raw } else { return decl.value } } } module.exports = Value ================================================ FILE: lib/vendor.js ================================================ module.exports = { prefix(prop) { let match = prop.match(/^(-\w+-)/) if (match) { return match[0] } return '' }, unprefixed(prop) { return prop.replace(/^-\w+-/, '') } } ================================================ FILE: package.json ================================================ { "name": "autoprefixer", "version": "10.4.27", "description": "Parse CSS and add vendor prefixes to CSS rules using values from the Can I Use website", "engines": { "node": "^10 || ^12 || >=14" }, "keywords": [ "autoprefixer", "css", "prefix", "postcss", "postcss-plugin" ], "scripts": { "unit": "uvu . '\\.test\\.js$'", "test:coverage": "c8 node node_modules/uvu/bin.js . '\\.test\\.js$'", "test:lint": "eslint . bin/*", "test:size": "size-limit", "test": "pnpm run /^test:/" }, "main": "lib/autoprefixer.js", "bin": "bin/autoprefixer", "types": "lib/autoprefixer.d.ts", "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "author": "Andrey Sitnik ", "license": "MIT", "repository": "postcss/autoprefixer", "bugs": { "url": "https://github.com/postcss/autoprefixer/issues" }, "peerDependencies": { "postcss": "^8.1.0" }, "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "devDependencies": { "@logux/eslint-config": "^57.0.2", "@size-limit/preset-small-lib": "12.0.0", "actions-up": "^1.12.0", "c8": "^11.0.0", "clean-publish": "^6.0.3", "eslint": "^10.0.2", "nanospy": "^1.0.0", "postcss": "^8.5.6", "size-limit": "^12.0.0", "uvu": "^0.5.6" }, "prettier": { "arrowParens": "avoid", "jsxSingleQuote": false, "quoteProps": "consistent", "semi": false, "singleQuote": true, "trailingComma": "none" }, "size-limit": [ { "limit": "70 KB" } ], "c8": { "exclude": [ "test/*" ], "lines": 100, "check-coverage": true, "reporter": [ "text", "lcov" ], "skip-full": true, "clean": true }, "clean-publish": { "cleanDocs": true }, "pnpm": { "patchedDependencies": { "yargs@17.7.2": "patches/yargs@17.7.2.patch" } } } ================================================ FILE: patches/yargs@17.7.2.patch ================================================ diff --git a/browser.d.ts b/browser.d.ts deleted file mode 100644 index 21f3fc69190b574ab8456514d3da1972afa53973..0000000000000000000000000000000000000000 diff --git a/package.json b/package.json index 389cc6b064b5f888e7f9d718f5440feabdce57ad..c1ae265542ad386fa2214914b0186ade81dd6ee2 100644 --- a/package.json +++ b/package.json @@ -20,13 +20,10 @@ "import": "./browser.mjs", "types": "./browser.d.ts" }, - "./yargs": [ - { - "import": "./yargs.mjs", - "require": "./yargs" - }, - "./yargs" - ] + "./yargs": { + "require": "./index.cjs", + "import": "./yargs.mjs" + } }, "type": "module", "module": "./index.mjs", ================================================ FILE: test/at-rule.test.js ================================================ let { parse } = require('postcss') let { test } = require('uvu') let { equal } = require('uvu/assert') let AtRule = require('../lib/at-rule') test('adds prefixes', () => { let keyframes = new AtRule('@keyframes', ['-moz-', '-ms-']) let css = parse( '@-moz-keyframes b {} ' + '@-ms-keyframes a {} ' + '@keyframes a {}' ) keyframes.process(css.last) equal( css.toString(), '@-moz-keyframes b {} ' + '@-ms-keyframes a {} ' + '@-moz-keyframes a {} ' + '@keyframes a {}' ) }) test.run() ================================================ FILE: test/autoprefixer.test.js ================================================ let { readFileSync } = require('fs') let { restoreAll, spyOn } = require('nanospy') let { join } = require('path') let postcss = require('postcss') let { test } = require('uvu') let { equal, match, not, throws, type } = require('uvu/assert') let autoprefixer = require('..') let grider = autoprefixer({ cascade: false, grid: 'autoplace', overrideBrowserslist: ['Chrome 25', 'Edge 12', 'IE 10'] }) let cleaner = autoprefixer({ overrideBrowserslist: [] }) let compiler = autoprefixer({ overrideBrowserslist: ['Chrome 25', 'Opera 12'] }) let filterer = autoprefixer({ overrideBrowserslist: ['Chrome 25', 'Safari 9', 'Firefox 39'] }) let borderer = autoprefixer({ overrideBrowserslist: ['Safari 4', 'Firefox 3.6'] }) let cascader = autoprefixer({ cascade: true, overrideBrowserslist: ['Chrome > 19', 'Firefox 21', 'IE 10'] }) let keyframer = autoprefixer({ overrideBrowserslist: ['Chrome > 19', 'Opera 12'] }) let flexboxer = autoprefixer({ overrideBrowserslist: ['Chrome > 19', 'Firefox 21', 'IE 10'] }) let without3d = autoprefixer({ overrideBrowserslist: ['Opera 12', 'IE > 0'] }) let supporter = autoprefixer({ overrideBrowserslist: ['Chrome 25', 'Chrome 28', 'IE > 0'] }) let uncascader = autoprefixer({ overrideBrowserslist: ['Firefox 15'] }) let gradienter = autoprefixer({ overrideBrowserslist: ['Chrome 25', 'Opera 12', 'Android 2.3'] }) let grouping = autoprefixer({ grid: 'autoplace', overrideBrowserslist: ['Chrome 25', 'Firefox > 17', 'IE 10', 'Edge 12'] }) let ffgradienter = autoprefixer({ overrideBrowserslist: ['Chrome 25', 'Opera 12', 'Firefox 6'] }) let selectorer = autoprefixer({ overrideBrowserslist: ['Chrome 25', 'Firefox > 17', 'IE 10', 'Edge 12'] }) let fileSelectorButtoner = autoprefixer({ overrideBrowserslist: ['Chrome > 25', 'Firefox >= 82'] }) let backdroper = autoprefixer({ overrideBrowserslist: ['IE >= 11', 'Chrome < 32', 'Safari >= 15.4'] }) let placeholderShowner = autoprefixer({ overrideBrowserslist: ['IE >= 10'] }) let transitionSpec = autoprefixer({ overrideBrowserslist: ['Chrome > 19', 'Firefox 14', 'IE 10', 'Opera 12'] }) let intrinsicer = autoprefixer({ overrideBrowserslist: ['Chrome 25', 'Firefox 22', 'Safari 10'] }) let imagerender = autoprefixer({ overrideBrowserslist: ['iOS 8', 'iOS 6.1', 'FF 22', 'IE 11', 'Opera 12'] }) let backgrounder = autoprefixer({ overrideBrowserslist: ['Firefox 3.6', 'Android 2.3'] }) let resolutioner = autoprefixer({ overrideBrowserslist: ['Safari 7', 'Opera 12', 'Firefox 15'] }) let overscroller = autoprefixer({ overrideBrowserslist: ['Edge 17'] }) let clipper = autoprefixer({ overrideBrowserslist: ['Safari 7', 'Edge 14'] }) let example = autoprefixer({ overrideBrowserslist: ['defaults'] }) let autofiller = autoprefixer({ overrideBrowserslist: ['Chrome > 90', 'Firefox >= 82'] }) let textDecorator = autoprefixer({ overrideBrowserslist: ['Chrome >= 57', 'Firefox >= 36', 'Safari >= 12.1'] }) let content = autoprefixer({ overrideBrowserslist: [ '> 2%', 'last 2 years', 'ie 11', 'not ie_mob > 0', 'not dead' ] }) function prefixer(name) { if ( name === 'grid' || name === 'grid-gap' || name === 'grid-area' || name === 'grid-template' || name === 'grid-template-areas' ) { return grider } else if ( name === 'filter' || name === 'advanced-filter' || name === 'element' ) { return filterer } else if ( name === 'vendor-hack' || name === 'value-hack' || name === 'mistakes' ) { return cleaner } else if ( name === 'flexbox' || name === 'flex-rewrite' || name === 'double' || name === 'viewport' || name === 'appearance' ) { return flexboxer } else if ( name === 'intrinsic' || name === 'multicolumn' || name === 'logical' || name === 'text-decoration' || name === 'at-rules' ) { return intrinsicer } else if (name === 'text-decoration-shorthand') { return textDecorator } else if (name === 'selectors' || name === 'placeholder') { return selectorer } else if (name === 'selectors' || name === 'file-selector-button') { return fileSelectorButtoner } else if (name === 'selectors' || name === 'backdrop') { return backdroper } else if ( name === 'selectors' || name === 'autofill' || name === 'print-color-adjust' ) { return autofiller } else if (name === 'placeholder-shown') { return placeholderShowner } else if (name === 'backdrop-filter' || name === 'overscroll-behavior') { return overscroller } else if (name === 'background-clip' || name === 'user-select') { return clipper } else if (name === 'image-rendering' || name === 'writing-mode') { return imagerender } else if (name === 'keyframes') { return keyframer } else if (name === 'border-radius') { return borderer } else if (name === 'gradient') { return gradienter } else if (name === 'gradient-fix') { return ffgradienter } else if (name === 'grouping-rule') { return grouping } else if (name === 'cascade') { return cascader } else if (name === '3d-transform') { return without3d } else if (name === 'background-size') { return backgrounder } else if (name === 'uncascade') { return uncascader } else if (name === 'example') { return example } else if (name === 'resolution') { return resolutioner } else if (name === 'supports') { return supporter } else if (name === 'transition-spec') { return transitionSpec } else if (name === 'content') { return content } else { return compiler } } function read(name) { let file = join(__dirname, '/cases/' + name + '.css') return readFileSync(file).toString() } function universalizer(string) { return string.replace(/\r/g, '') } function check(from, instance = prefixer(from)) { let input = read(from) let output = read(from + '.out') let result = postcss([instance]).process(input) equal(result.warnings().length, 0) equal(universalizer(result.css), universalizer(output)) } const COMMONS = [ 'transition', 'values', 'keyframes', 'gradient', 'flex-rewrite', 'flexbox', 'filter', 'border-image', 'border-radius', 'notes', 'selectors', 'placeholder', 'placeholder-shown', 'fullscreen', 'intrinsic', 'mistakes', 'custom-prefix', 'cascade', 'double', 'multicolumn', '3d-transform', 'background-size', 'supports', 'viewport', 'resolution', 'logical', 'appearance', 'advanced-filter', 'element', 'image-set', 'image-rendering', 'mask-border', 'writing-mode', 'cross-fade', 'gradient-fix', 'text-emphasis-position', 'grid', 'grid-area', 'grid-template', 'grid-template-areas', 'grid-gap', 'print-color-adjust' ] test.after.each(() => { delete process.env.AUTOPREFIXER_GRID restoreAll() }) test('throws on wrong options', () => { throws(() => { autoprefixer({ browser: ['chrome 25', 'opera 12'] }) }, /overrideBrowserslist/) throws(() => { autoprefixer({ browserslist: ['chrome 25', 'opera 12'] }) }, /overrideBrowserslist/) }) let options = { cascade: false, grid: false } let browsers = ['chrome 25', 'opera 12'] test('sets options via options object', () => { let allOptions = Object.assign(options, { overrideBrowserslist: browsers }) let instance = autoprefixer(allOptions) equal(instance.options, allOptions) equal(instance.browsers, browsers) }) test('sets options via array of browsers as first argument and object', () => { let instance = autoprefixer(browsers, options) equal(instance.options, options) equal(instance.browsers, browsers) }) test('sets options via browsers as arguments and options object', () => { let instance = autoprefixer(...browsers, options) equal(instance.options, options) equal(instance.browsers, browsers) }) test('has default browsers', () => { type(autoprefixer.defaults.length, 'number') }) test('shows warning on browsers option', () => { let consoleWarn = spyOn(console, 'warn', () => {}) let instance = autoprefixer({ browsers: ['last 1 version'] }) equal(instance.browsers, ['last 1 version']) equal(consoleWarn.callCount, 1) match(consoleWarn.calls[0][0], 'overrideBrowserslist') }) test('passes statistics to Browserslist', () => { let stats = { chrome: { 10: 10, 11: 40 }, ie: { 10: 10, 11: 40 } } match( autoprefixer({ overrideBrowserslist: '> 20% in my stats', stats }).info(), /Browsers:\n\s\sChrome: 11\n\s\sIE: 11\n/ ) }) test('prefixes values', () => { check('values') }) test('prefixes @keyframes', () => { check('keyframes') }) test('prefixes @viewport', () => { check('viewport') }) test('prefixes selectors', () => { check('selectors') }) test('prefixes resolution query', () => { check('resolution') }) test('removes common mistakes', () => { check('mistakes') }) test('reads notes for prefixes', () => { check('notes') }) test('keeps vendor-specific hacks', () => { check('vendor-hack') }) test('keeps values with vendor hacks', () => { check('value-hack') }) test('works with comments', () => { check('comments') }) test('uses visual cascade', () => { check('cascade') }) test('works with properties near', () => { check('double') }) test('checks prefixed in hacks', () => { check('check-down') }) test('normalize cascade after remove', () => { check('uncascade') }) test('prefix decls in @supports', () => { check('supports') }) test('saves declaration style', () => { check('style') }) test('uses ignore next control comments', () => { check('ignore-next') }) test('uses block control comments', () => { check('disabled') }) test('has actual example in docs', () => { check('example') }) test('process grouping rules correctly', () => { check('grouping-rule') }) test('transition on vendor specific rule', () => { check('transition-spec') }) test('ignore prefix in vendor at rules', () => { check('at-rules') }) test('ignore content property', () => { let input = read('content') let result = postcss([prefixer('scope')]).process(input) equal(result.css, input) }) test('uses control comments to whole scope', () => { let input = read('scope') let output = read('scope.out') let result = postcss([prefixer('scope')]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :5:3: Second Autoprefixer control comment ' + 'was ignored. Autoprefixer applies control comment to whole block, ' + 'not to next rules.' ] ) }) test('sets grid option via comment', () => { let input = read('grid-status') let output = read('grid-status.out') let ap = autoprefixer({ overrideBrowserslist: ['last 2 versions', 'IE 11'] }) let result = postcss([ap]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :2:1: Second Autoprefixer grid control ' + 'comment was ignored. Autoprefixer applies control comments ' + 'to the whole block, not to the next rules.', 'autoprefixer: :20:3: Second Autoprefixer grid control ' + 'comment was ignored. Autoprefixer applies control comments ' + 'to the whole block, not to the next rules.', 'autoprefixer: :47:3: Second Autoprefixer grid control ' + 'comment was ignored. Autoprefixer applies control comments ' + 'to the whole block, not to the next rules.' ] ) }) test('prefixes transition', () => { let input = read('transition') let output = read('transition.out') let result = postcss([prefixer('transition')]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :23:3: Replace transition-property ' + 'to transition, because Autoprefixer could not support any cases ' + 'of transition-property and other transition-*' ] ) }) test('does not raise unnecessary warnings when prefixing transition', () => { check('transition-no-warning') }) test('works with broken transition', () => { let input = 'a{transition:,,}' let output = 'a{-webkit-transition:;-o-transition:;transition:}' let result = postcss([prefixer('transition')]).process(input) equal(result.css, output) }) test('should ignore spaces inside values', () => { let css = read('trim') equal(postcss([flexboxer]).process(css).css, css) }) test('removes unnecessary prefixes', () => { let processor = postcss([cleaner]) for (let i of COMMONS) { if (i === 'gradient-fix') continue if (i === 'cascade') continue if (i === 'mistakes') continue if (i === 'flex-rewrite') continue if (i === 'grid') continue if (i === 'grid-gap') continue if (i === 'grid-area') continue if (i === 'grid-template') continue if (i === 'grid-template-areas') continue let input = read(i + '.out') let output = read(i) equal(processor.process(input).css, output) } }) test('media does not should nested', () => { let processor = postcss([grider]) let input = read('grid-media-rules') let output = read('grid-media-rules.out') equal(processor.process(input).css, output) }) test('does not remove unnecessary prefixes on request', () => { for (let i of ['transition', 'values', 'fullscreen']) { let keeper = autoprefixer({ overrideBrowserslist: [], remove: false }) let css = read(i + '.out') equal(postcss([keeper]).process(css).css, css) } }) test('does not add prefixes on request', () => { for (let i of ['transition', 'values', 'fullscreen']) { let remover = autoprefixer({ add: false, overrideBrowserslist: ['Opera 12'] }) let unprefixed = read(i) equal(postcss([remover]).process(unprefixed).css, unprefixed) } }) test('prevents doubling prefixes', () => { for (let i of COMMONS) { let processor = postcss([prefixer(i)]) let input = read(i) let output = read(i + '.out') let result = processor.process(processor.process(input)).css equal(universalizer(result), universalizer(output)) } }) function isContainerNode(node) { return 'nodes' in node } test('does not broke AST', () => { function checkParent(node) { node.walk(child => { type(child.parent, 'object') if (isContainerNode(child)) checkParent(child) }) } for (let i of COMMONS) { let processor = postcss([prefixer(i)]) let input = read(i) checkParent(processor.process(input).root) } }) test('parses difficult files', () => { let input = read('syntax') let result = postcss([cleaner]).process(input) equal(result.css, input) }) test('marks parsing errors', () => { throws(() => { postcss([cleaner]).process('a {').css }, ':1:1: Unclosed block') }) test('shows file name in parse error', () => { throws(() => { postcss([cleaner]).process('a {', { from: 'a.css' }).css }, /a.css:1:1: /) }) test('uses browserslist config', () => { let from = join(__dirname, 'cases/config/test.css') let input = read('config/test') let output = read('config/test.out') let processor = postcss([autoprefixer]) equal(processor.process(input, { from }).css, output) }) test('sets browserslist environment', () => { let from = join(__dirname, 'cases/config/test.css') let input = read('config/test') let output = read('config/test.production') let processor = postcss([autoprefixer({ env: 'development' })]) equal(processor.process(input, { from }).css, output) }) test('takes values from other PostCSS plugins', () => { function plugin(root) { root.walkDecls(i => { i.value = 'calc(0)' }) } let result = postcss([plugin, compiler]).process('a{width:0/**/0}') equal(result.css, 'a{width:-webkit-calc(0);width:calc(0)}') }) test('has option to disable @supports support', () => { let css = '@supports (cursor: grab) {}' let instance = autoprefixer({ overrideBrowserslist: ['Chrome 28'], supports: false }) let result = postcss([instance]).process(css) equal(result.css, css) }) test('has disabled grid options by default', () => { let ap = autoprefixer({ overrideBrowserslist: ['Edge 12', 'IE 10'] }) let input = read('grid') let output = read('grid.disabled') let result = postcss([ap]).process(input) equal(result.css, output) }) test('has different outputs for different grid options', () => { function ap(gridValue) { return autoprefixer({ grid: gridValue, overrideBrowserslist: ['Edge 12', 'IE 10'] }) } let input = read('grid-options') let outputAutoplace = read('grid-options.autoplace.out') let outputNoAutoplace = read('grid-options.no-autoplace.out') let outputDisabled = read('grid-options.disabled.out') let resultAutoplace = postcss([ap('autoplace')]).process(input).css let resultNoAutoplace = postcss([ap('no-autoplace')]).process(input).css let resultEnabled = postcss([ap(true)]).process(input).css let resultDisabled = postcss([ap(false)]).process(input).css // output for grid: 'autoplace' equal(resultAutoplace, outputAutoplace) // output for grid: 'no-autoplace' equal(resultNoAutoplace, outputNoAutoplace) // output for grid: true is the same as for 'no-autoplace' equal(resultEnabled, outputNoAutoplace) // output for grid: false equal(resultDisabled, outputDisabled) }) test('has different outputs for different grid environment variables', () => { function ap(gridValue) { process.env.AUTOPREFIXER_GRID = gridValue return autoprefixer({ overrideBrowserslist: ['Edge 12', 'IE 10'] }) } let input = read('grid-options') let outputAutoplace = read('grid-options.autoplace.out') let outputNoAutoplace = read('grid-options.no-autoplace.out') let resultAutoplace = postcss([ap('autoplace')]).process(input).css equal(resultAutoplace, outputAutoplace) let resultNoAutoplace = postcss([ap('no-autoplace')]).process(input).css equal(resultNoAutoplace, outputNoAutoplace) }) test('has option to disable flexbox support', () => { let css = read('flexbox') let instance = autoprefixer({ flexbox: false, overrideBrowserslist: ['IE 10'] }) let result = postcss([instance]).process(css) equal(result.css, css) }) test('has option to disable 2009 flexbox support', () => { let ap = autoprefixer({ flexbox: 'no-2009', overrideBrowserslist: ['Chrome > 19'] }) let css = 'a{flex:1;transition:flex}' let result = postcss([ap]).process(css) equal( result.css, 'a{' + '-webkit-flex:1;flex:1;' + '-webkit-transition:-webkit-flex;transition:-webkit-flex;' + 'transition:flex;transition:flex, -webkit-flex' + '}' ) }) test('returns inspect string', () => { match( autoprefixer({ overrideBrowserslist: ['chrome 25'] }).info(), /Browsers:\s+Chrome: 25/ ) }) test('uses browserslist config in inspect', () => { let from = join(__dirname, 'cases/config') match(autoprefixer().info({ from }), /Browsers:\s+IE: 10/) }) test('ignores unknown versions on request', () => { throws(() => { autoprefixer({ overrideBrowserslist: ['ie 100'] }).info() }, /Unknown version 100 of ie/) not.throws(() => { autoprefixer({ ignoreUnknownVersions: true, overrideBrowserslist: ['ie 100'] }).info() }) }) test('works with CSS Modules', () => { postcss([autoprefixer()]).process(':export { selectors: _1q6ho_2 }').css }) test('ignores prefix IE filter', () => { check('filter') }) test('supports webkit filters', () => { check('advanced-filter') }) test('changes border image syntax', () => { check('border-image') }) test('supports old Mozilla prefixes', () => { check('border-radius') }) test('supports all flexbox syntaxes', () => { check('flexbox') }) test('supports map flexbox props', () => { check('flex-rewrite') }) test('supports all fullscreens', () => { check('fullscreen') }) test('supports file-selector-button', () => { check('file-selector-button') }) test('supports ::backdrop', () => { check('backdrop') }) test('supports custom prefixes', () => { check('custom-prefix') }) test('fixes break properties', () => { check('multicolumn') }) test('ignores some 3D transforms', () => { check('3d-transform') }) test('supports background-size', () => { check('background-size') }) test('supports background-clip', () => { check('background-clip') }) test('supports logical properties', () => { check('logical') }) test('supports appearance', () => { check('appearance') }) test('supports all placeholders', () => { check('placeholder') }) test('supports placeholder-shown', () => { check('placeholder-shown') }) test('supports image-rendering', () => { check('image-rendering') }) test('supports border-box mask', () => { check('mask-border') }) test('supports mask-composite', () => { check('mask-composite') }) test('supports image-set()', () => { check('image-set') }) test('supports writing-mode', () => { check('writing-mode') }) test('supports cross-fade()', () => { check('cross-fade') }) test('ignores modern direction', () => { check('animation') }) test('supports overscroll-behavior', () => { check('overscroll-behavior') }) test('supports print-color-adjust', () => { let input = read('print-color-adjust') let output = read('print-color-adjust.out') let result = postcss([prefixer('print-color-adjust')]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :2:3: Replace color-adjust ' + 'to print-color-adjust. The color-adjust shorthand ' + 'is currently deprecated.' ] ) }) test('supports backdrop-filter', () => { check('backdrop-filter') }) test('supports user-select hack for IE', () => { check('user-select') }) test('supports appearance for IE', () => { let instance = autoprefixer({ overrideBrowserslist: 'Edge 15' }) let result = postcss([instance]).process('a { appearance: none }') equal(result.css, 'a { -webkit-appearance: none; appearance: none }') }) test('changes angle in gradient', () => { let input = read('gradient') let output = read('gradient.out') let result = postcss([prefixer('gradient')]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :18:3: Gradient has outdated direction ' + 'syntax. New syntax is like `closest-side at 0 0` instead of ' + '`0 0, closest-side`.', 'autoprefixer: :38:3: Gradient has outdated direction ' + 'syntax. New syntax is like `to left` instead of `right`.', 'autoprefixer: :100:3: Gradient has outdated ' + 'direction syntax. Replace `cover` to `farthest-corner`.', 'autoprefixer: :104:3: Gradient has outdated ' + 'direction syntax. Replace `contain` to `closest-side`.' ] ) check('gradient-fix') }) test('skips old webkit gradient for CSS variables', () => { let input = 'a { background: linear-gradient(var(--direction), red, blue); }' let result = postcss([gradienter]).process(input) // Should not generate -webkit-gradient() when CSS variables are present equal(result.css.includes('-webkit-gradient('), false) // Should still generate -webkit-linear-gradient() equal(result.css.includes('-webkit-linear-gradient('), true) }) test('warns on old flexbox display', () => { let result = postcss([flexboxer]).process('a{ display: box; }') equal(result.css, 'a{ display: box; }') equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :1:4: You should write display: flex ' + 'by final spec instead of display: box' ] ) }) test('supports intrinsic sizing', () => { let input = read('intrinsic') let output = read('intrinsic.out') let result = postcss([prefixer('intrinsic')]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :15:3: Replace fill to stretch, ' + 'because spec had been changed', 'autoprefixer: :19:3: Replace fill-available ' + 'to stretch, because spec had been changed' ] ) }) test('supports text-emphasis', () => { let input = read('text-emphasis-position') let output = read('text-emphasis-position.out') let instance = prefixer('text-emphasis-position') let result = postcss([instance]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :14:3: You should use 2 values ' + 'for text-emphasis-position For example, `under left` ' + 'instead of just `under`.' ] ) }) test('supports grid layout', () => { let input = read('grid') let output = read('grid.out') let instance = prefixer('grid') let result = postcss([instance]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :3:3: Autoplacement does not work ' + 'without grid-template-rows property', 'autoprefixer: :12:3: Autoplacement does not work ' + 'without grid-template-columns property', 'autoprefixer: :36:3: Can not prefix grid-column-end ' + '(grid-column-start is not found)', 'autoprefixer: :37:3: IE does not support subgrid', 'autoprefixer: :39:3: Can not implement grid-gap ' + 'without grid-template-columns', 'autoprefixer: :39:3: Can not find grid areas: ' + 'head, nav, main, foot', 'autoprefixer: :57:3: Can not implement grid-gap ' + 'without grid-template-columns', 'autoprefixer: :57:3: Can not find grid areas: a', 'autoprefixer: :65:3: Can not implement grid-gap ' + 'without grid-template-columns', 'autoprefixer: :65:3: Can not find grid areas: b', 'autoprefixer: :73:3: Can not find grid areas: c', 'autoprefixer: :81:3: Can not find grid areas: d', 'autoprefixer: :116:3: grid-column-span is not part ' + 'of final Grid Layout. Use grid-column.', 'autoprefixer: :117:3: grid-row-span is not part ' + 'of final Grid Layout. Use grid-row.', 'autoprefixer: :118:3: grid-auto-columns is not ' + 'supported by IE', 'autoprefixer: :119:3: grid-auto-rows is not ' + 'supported by IE', 'autoprefixer: :121:33: auto-fill value is not ' + 'supported by IE', 'autoprefixer: :122:30: auto-fit value is not ' + 'supported by IE', 'autoprefixer: :138:3: Please do not use ' + 'display: contents; if you have grid setting enabled', 'autoprefixer: :142:3: IE does not support align-items ' + 'on grid containers. Try using align-self on child elements instead: ' + '.warn_ie_align > * { align-self: center }', 'autoprefixer: :147:3: IE does not support justify-items ' + 'on grid containers. Try using justify-self on child elements ' + 'instead: .warn_ie_justify > * { justify-self: center }', 'autoprefixer: :152:3: IE does not support justify-content ' + 'on grid containers', 'autoprefixer: :157:3: IE does not support place-items ' + 'on grid containers. Try using place-self on child elements ' + 'instead: .warn_place_items > * { place-self: start end }', 'autoprefixer: :181:3: grid-auto-flow is not supported by IE', 'autoprefixer: :203:26: Autoprefixer currently does not ' + 'support line names. Try using grid-template-areas instead.' ] ) let input2 = read('grid-template') let output2 = read('grid-template.out') let instance2 = prefixer('grid-template') let result2 = postcss([instance2]).process(input2) equal(result2.css, output2) }) test('supports grid autoplacement', () => { let input = read('grid-autoplacement') let output = read('grid-autoplacement.out') let instance = prefixer('grid') let result = postcss([instance]).process(input) equal(result.css, output) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :47:3: grid-auto-flow: dense ' + 'is not supported by IE', 'autoprefixer: :48:3: Autoplacement does not work ' + 'without grid-template-rows property', 'autoprefixer: :53:3: grid-auto-flow works only if grid-temp' + 'late-rows and grid-template-columns are present in the same rule', 'autoprefixer: :60:3: grid-gap only works if grid-temp' + 'late(-areas) is being used', 'autoprefixer: :64:3: Autoplacement does not work ' + 'without grid-template-rows property', 'autoprefixer: :65:3: grid-gap only works if grid-temp' + 'late(-areas) is being used or both rows and columns have been ' + 'declared and cells have not been ' + 'manually placed inside the explicit grid' ] ) }) test('shows Grid warnings only for IE', () => { let input = 'a { grid-template-rows: repeat(auto-fit, 1px) }' let instance = autoprefixer({ grid: true, overrideBrowserslist: 'chrome 27' }) let result = postcss([instance]).process(input) equal(result.warnings().length, 0) }) test('warns if rule has both grid-area and grid-(row|column) decls', () => { let input = read('grid-area') let instance = prefixer('grid-area') let result = postcss([instance]).process(input) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :28:3: You already have a grid-area ' + 'declaration present in the rule. You should use either ' + 'grid-area or grid-row, not both', 'autoprefixer: :29:3: You already have a grid-area ' + 'declaration present in the rule. You should use either ' + 'grid-area or grid-column, not both', 'autoprefixer: :34:3: You already have a grid-area ' + 'declaration present in the rule. You should use either ' + 'grid-area or grid-column, not both' ] ) }) test('warns if rule with grid-area has no parent with grid-template', () => { let input = read('grid-template-areas') let instance = prefixer('grid-area') let result = postcss([instance]).process(input) equal( result .warnings() .map(i => i.toString()) .filter(str => str.includes('grid-template')), [ 'autoprefixer: :144:3: Autoprefixer cannot find ' + 'a grid-template containing the duplicate grid-area ' + '"child" with full selector matching: .uncle', 'autoprefixer: :149:3: Autoprefixer cannot find ' + 'a grid-template containing the duplicate grid-area ' + '"child" with full selector matching: .uncle', 'autoprefixer: :154:3: Autoprefixer cannot find ' + 'a grid-template containing the duplicate grid-area ' + '"child" with full selector matching: .grand-parent .uncle-second', 'autoprefixer: :159:3: Autoprefixer cannot find ' + 'a grid-template containing the duplicate grid-area ' + '"child" with full selector matching: .grand-parent .uncle-second', 'autoprefixer: :164:3: Autoprefixer cannot find ' + 'a grid-template containing the duplicate grid-area ' + '"child" with full selector matching: .grand-parent .father.uncle', 'autoprefixer: :169:3: Autoprefixer cannot find ' + 'a grid-template containing the duplicate grid-area ' + '"child" with full selector matching: .grand-parent.uncle .father' ] ) }) test('should preserve @media rules with grid-area', () => { let input = read('grid-area-media-sequence') let output = read('grid-area-media-sequence.out') let instance = prefixer('grid-area') let result = postcss([instance]).process(input) equal(result.css, output) }) test('should merge complex duplicate grid-area rules successfully', () => { let input = read('grid-areas-duplicate-complex') let output = read('grid-areas-duplicate-complex.out') let instance = prefixer('grid-area') let result = postcss([instance]).process(input) equal(result.css, output) }) test('ignores values for CSS3PIE props', () => { let css = read('pie') equal(postcss([compiler]).process(css).css, css) }) test('add prefix for backface-visibility for Safari 9', () => { let input = 'a{ ' + 'backface-visibility: hidden; ' + 'transform-style: preserve-3d }' let ap = autoprefixer({ flexbox: false, overrideBrowserslist: ['Safari 9'] }) equal( postcss([ap]).process(input).css, 'a{ ' + '-webkit-backface-visibility: hidden; ' + 'backface-visibility: hidden; ' + 'transform-style: preserve-3d }' ) }) test('supports text-decoration', () => { let input = read('text-decoration') let output = read('text-decoration.out') let instance = prefixer('text-decoration') let result = postcss([instance]).process(input) equal(universalizer(result.css), universalizer(output)) equal( result.warnings().map(i => i.toString()), [ 'autoprefixer: :32:3: Replace text-decoration-skip: ink ' + 'to text-decoration-skip-ink: auto, because spec had been changed' ] ) }) test('supports text-decoration shorthand', () => { let input = read('text-decoration') let output = read('text-decoration.shorthand.out') let instance = prefixer('text-decoration-shorthand') let result = postcss([instance]).process(input) equal(universalizer(result.css), universalizer(output)) }) test('supports -webkit-line-clamp', () => { let input = read('webkit-line-clamp') let output = read('webkit-line-clamp.out') let result = postcss([cleaner]).process(input) equal(universalizer(result.css), universalizer(output)) equal(result.warnings().length, 0) }) test('supports latest Firefox stretch', () => { let input = read('intrinsic') let output = read('intrinsic.ff') let result = postcss([ autoprefixer({ overrideBrowserslist: 'firefox 90' }) ]).process(input) equal(result.css, output) equal(result.warnings().length, 2) }) test.run() ================================================ FILE: test/brackets.test.js ================================================ let { test } = require('uvu') let { equal } = require('uvu/assert') let brackets = require('../lib/brackets') test('parses simple string', () => { equal(brackets.parse('test'), ['test']) }) test('parses brackets', () => { equal(brackets.parse('a (b) a'), ['a ', ['b'], ' a']) }) test('parses many brackets', () => { equal(brackets.parse('a (b ()) a'), ['a ', ['b ', [''], ''], ' a']) }) test('parses errors', () => { equal(brackets.parse('a (b ('), ['a ', ['b ', ['']]]) }) test('stringifies simple string', () => { equal(brackets.stringify(['test']), 'test') }) test('stringifies brackets', () => { equal(brackets.stringify(['a ', ['b'], ' a']), 'a (b) a') }) test('stringifies many brackets', () => { equal(brackets.stringify(['a ', ['b ', [''], ''], ' a']), 'a (b ()) a') }) test.run() ================================================ FILE: test/browsers.test.js ================================================ let { agents } = require('caniuse-lite/dist/unpacker/agents') let { join } = require('path') let { test } = require('uvu') let { equal, is } = require('uvu/assert') let Browsers = require('../lib/browsers') test('returns prefixes by default data', () => { equal(Browsers.prefixes(), ['-webkit-', '-moz-', '-ms-', '-o-']) }) test('finds possible prefix', () => { is(Browsers.withPrefix('1 -o-calc(1)'), true) is(Browsers.withPrefix('1 calc(1)'), false) }) test('allows to select no browsers', () => { let browsers = new Browsers(agents, []) equal(browsers.selected.length, 0) }) test('selects by older version', () => { let browsers = new Browsers(agents, ['ie < 7']) equal(browsers.selected, ['ie 6', 'ie 5.5']) }) test('combines requirements', () => { let browsers = new Browsers(agents, ['ie 10', 'ie < 6']) equal(browsers.selected, ['ie 10', 'ie 5.5']) }) test('has aliases', () => { equal(new Browsers(agents, ['fx 10']).selected, ['firefox 10']) equal(new Browsers(agents, ['ff 10']).selected, ['firefox 10']) }) test('ignores case', () => { equal(new Browsers(agents, ['Firefox 10']).selected, ['firefox 10']) }) test('uses browserslist config', () => { let from = join(__dirname, 'cases/config/test.css') equal(new Browsers(agents, undefined, { from }).selected, ['ie 10']) }) test('returns browser prefix', () => { let browsers = new Browsers(agents, ['chrome 30']) equal(browsers.prefix('chrome 30'), '-webkit-') }) test('returns right prefix for Operas', () => { let browsers = new Browsers(agents, ['last 1 opera version']) equal(browsers.prefix('opera 12'), '-o-') equal(browsers.prefix(browsers.selected[0]), '-webkit-') equal(browsers.prefix('op_mob 12'), '-o-') equal(browsers.prefix(browsers.selected[0]), '-webkit-') }) test('return true for selected browsers', () => { let browsers = new Browsers(agents, ['chrome 30', 'chrome 31']) is(browsers.isSelected('chrome 30'), true) is(browsers.isSelected('ie 6'), false) }) test.run() ================================================ FILE: test/cases/3d-transform.css ================================================ a { transition: transform 1s; transform: rotateX(45deg); } b { transform: translateX(45deg); transform-origin: 0 0; } em { transform: rotateZ(45deg); } @keyframes anim { from { transform: rotate(90deg); } } ================================================ FILE: test/cases/3d-transform.out.css ================================================ a { -o-transition: -o-transform 1s; transition: transform 1s; transition: transform 1s, -o-transform 1s; transform: rotateX(45deg); } b { -ms-transform: translateX(45deg); -o-transform: translateX(45deg); transform: translateX(45deg); -ms-transform-origin: 0 0; -o-transform-origin: 0 0; transform-origin: 0 0; } em { -ms-transform: rotate(45deg); -o-transform: rotateZ(45deg); transform: rotateZ(45deg); } @-o-keyframes anim { from { -o-transform: rotate(90deg); transform: rotate(90deg); } } @keyframes anim { from { -o-transform: rotate(90deg); transform: rotate(90deg); } } ================================================ FILE: test/cases/advanced-filter.css ================================================ div { backdrop-filter: blur(2px); } div { background: filter(url('image.jpg'), blur(2px)); } div { background: url(image.jpg), filter(url('image.jpg'), blur(2px)); } ================================================ FILE: test/cases/advanced-filter.out.css ================================================ div { -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); } div { background: -webkit-filter(url('image.jpg'), blur(2px)); background: filter(url('image.jpg'), blur(2px)); } div { background: url(image.jpg), -webkit-filter(url('image.jpg'), blur(2px)); background: url(image.jpg), filter(url('image.jpg'), blur(2px)); } ================================================ FILE: test/cases/animation.css ================================================ a { animation-direction: alternate; animation-direction: reverse; animation-direction: REVERSE; animation-direction: alternate-reverse; animation: rotation-reverse; animation: rotation-reverse reverse; } ================================================ FILE: test/cases/animation.out.css ================================================ a { -webkit-animation-direction: alternate; -o-animation-direction: alternate; animation-direction: alternate; animation-direction: reverse; animation-direction: REVERSE; animation-direction: alternate-reverse; -webkit-animation: rotation-reverse; -o-animation: rotation-reverse; animation: rotation-reverse; animation: rotation-reverse reverse; } ================================================ FILE: test/cases/appearance.css ================================================ a { appearance: none; } b { appearance: auto; } ================================================ FILE: test/cases/appearance.out.css ================================================ a { -webkit-appearance: none; -moz-appearance: none; appearance: none; } b { -webkit-appearance: auto; -moz-appearance: auto; appearance: auto; } ================================================ FILE: test/cases/at-rules.css ================================================ body { appearance: none; } .block { transform: translate(20px, 30px); } @-webkit-region .region { .block { transform: translate(20px, 30px); } .block__button { appearance: none; } } @-moz-document domain('example.com') { body { appearance: none; } .block { transform: translate(20px, 30px); } } ================================================ FILE: test/cases/at-rules.out.css ================================================ body { -webkit-appearance: none; -moz-appearance: none; appearance: none; } .block { -webkit-transform: translate(20px, 30px); transform: translate(20px, 30px); } @-webkit-region .region { .block { -webkit-transform: translate(20px, 30px); transform: translate(20px, 30px); } .block__button { -webkit-appearance: none; appearance: none; } } @-moz-document domain('example.com') { body { -moz-appearance: none; appearance: none; } .block { transform: translate(20px, 30px); } } ================================================ FILE: test/cases/autofill.css ================================================ input:autofill { background-color: red; } ================================================ FILE: test/cases/autofill.out.css ================================================ input:-webkit-autofill { background-color: red; } input:autofill { background-color: red; } ================================================ FILE: test/cases/backdrop-filter.css ================================================ a { backdrop-filter: blur(2px); } ================================================ FILE: test/cases/backdrop-filter.out.css ================================================ a { -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); } ================================================ FILE: test/cases/backdrop.css ================================================ ::backdrop { color: green; } ================================================ FILE: test/cases/backdrop.out.css ================================================ ::-ms-backdrop { color: green; } ::backdrop { color: green; } ================================================ FILE: test/cases/background-clip.css ================================================ a { background-clip: text; } b { background-clip: content-box; } ================================================ FILE: test/cases/background-clip.out.css ================================================ a { -webkit-background-clip: text; background-clip: text; } b { background-clip: content-box; } ================================================ FILE: test/cases/background-size.css ================================================ a { background-size: 20px } b { background-size: contain } ================================================ FILE: test/cases/background-size.out.css ================================================ a { -webkit-background-size: 20px 20px; -moz-background-size: 20px; background-size: 20px } b { -webkit-background-size: contain; -moz-background-size: contain; background-size: contain } ================================================ FILE: test/cases/border-image.css ================================================ a { border-image: linear-gradient(black, white) 20% fill stretch stretch; } ================================================ FILE: test/cases/border-image.out.css ================================================ a { -o-border-image: -o-linear-gradient(black, white) 20% stretch stretch; border-image: -webkit-linear-gradient(black, white) 20% fill stretch stretch; border-image: linear-gradient(black, white) 20% fill stretch stretch; } ================================================ FILE: test/cases/border-radius.css ================================================ a { border-radius: 5px; border-top-left-radius: 3px; border-bottom-right-radius: 3px; } ================================================ FILE: test/cases/border-radius.out.css ================================================ a { -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; -webkit-border-top-left-radius: 3px; -moz-border-radius-topleft: 3px; border-top-left-radius: 3px; -webkit-border-bottom-right-radius: 3px; -moz-border-radius-bottomright: 3px; border-bottom-right-radius: 3px; } ================================================ FILE: test/cases/cascade.css ================================================ a { flex-direction: row; mask: none } b { mask: none } ================================================ FILE: test/cases/cascade.out.css ================================================ a { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; -moz-box-orient: horizontal; -moz-box-direction: normal; -ms-flex-direction: row; flex-direction: row; -webkit-mask: none; mask: none } b { -webkit-mask: none; mask: none } ================================================ FILE: test/cases/check-down.css ================================================ * { transition: all 1s; -o-transition: all 1s } ================================================ FILE: test/cases/check-down.out.css ================================================ * { -webkit-transition: all 1s; transition: all 1s; -o-transition: all 1s } ================================================ FILE: test/cases/comments.css ================================================ a { /* transition */ transition: all 1s; height: calc(/* comment before */100% - /* comment inside */ 10px/* comment after */); } /* placeholder */ ::placeholder { } ================================================ FILE: test/cases/comments.out.css ================================================ a { /* transition */ -webkit-transition: all 1s; -o-transition: all 1s; transition: all 1s; height: -webkit-calc(/* comment before */100% - /* comment inside */ 10px/* comment after */); height: calc(/* comment before */100% - /* comment inside */ 10px/* comment after */); } /* placeholder */ ::-webkit-input-placeholder { } ::placeholder { } ================================================ FILE: test/cases/config/browserslist ================================================ ie 10 [development] chrome 25 ================================================ FILE: test/cases/config/test.css ================================================ a { display: flex; } ================================================ FILE: test/cases/config/test.out.css ================================================ a { display: -ms-flexbox; display: flex; } ================================================ FILE: test/cases/config/test.production.css ================================================ a { display: -webkit-flex; display: flex; } ================================================ FILE: test/cases/content.css ================================================ a { content: "Element 'div' not allowed as child of element 'span' in this context."; } ================================================ FILE: test/cases/cross-fade.css ================================================ a { background-image: cross-fade(20% url(foo.png), url(bar.png)); } b { background-image: cross-fade(url(foo.png), url(bar.png)); } h1 { background-image: cross-fade(10.823% url(foo.png), url(bar.png)); } h2 { background-image: cross-fade(0.59 url(foo.png), url(bar.png)); } h3 { background-image: cross-fade(.59 url(foo.png), url(bar.png)); } .foo { background-image: cross-fade(.59 linear-gradient(white, black), radial-gradient(circle closest-corner, white, black)); } ================================================ FILE: test/cases/cross-fade.out.css ================================================ a { background-image: -webkit-cross-fade(url(foo.png), url(bar.png), 20%); background-image: cross-fade(20% url(foo.png), url(bar.png)); } b { background-image: -webkit-cross-fade(url(foo.png), url(bar.png), 0.5); background-image: cross-fade(url(foo.png), url(bar.png)); } h1 { background-image: -webkit-cross-fade(url(foo.png), url(bar.png), 10.823%); background-image: cross-fade(10.823% url(foo.png), url(bar.png)); } h2 { background-image: -webkit-cross-fade(url(foo.png), url(bar.png), 0.59); background-image: cross-fade(0.59 url(foo.png), url(bar.png)); } h3 { background-image: -webkit-cross-fade(url(foo.png), url(bar.png), .59); background-image: cross-fade(.59 url(foo.png), url(bar.png)); } .foo { background-image: -webkit-cross-fade(linear-gradient(white, black), radial-gradient(circle closest-corner, white, black), .59); background-image: cross-fade(.59 linear-gradient(white, black), radial-gradient(circle closest-corner, white, black)); } ================================================ FILE: test/cases/custom-prefix.css ================================================ a { -evil-up: calc(10px + 1); } ================================================ FILE: test/cases/custom-prefix.out.css ================================================ a { -evil-up: -webkit-calc(10px + 1); -evil-up: calc(10px + 1); } ================================================ FILE: test/cases/disabled.css ================================================ a { -webkit-border-radius: 4px; border-radius: 4px; mask: none; } b { /* autoprefixer: off */ -webkit-border-radius: 4px; border-radius: 4px; mask: none; } .loud { /*! autoprefixer: off */ -webkit-border-radius: 4px; border-radius: 4px; mask: none; } .case { /*autoprefixer:OFF*/ -webkit-border-radius: 4px; border-radius: 4px; mask: none; } @support (transition: 1s) { /* autoprefixer: off */ :fullscreen { -webkit-border-radius: 4px; border-radius: 4px; mask: none; } ::placeholder { /*autoprefixer: on*/ -webkit-border-radius: 4px; border-radius: 4px; mask: none; } } ================================================ FILE: test/cases/disabled.out.css ================================================ a { border-radius: 4px; -webkit-mask: none; mask: none; } b { /* autoprefixer: off */ -webkit-border-radius: 4px; border-radius: 4px; mask: none; } .loud { /*! autoprefixer: off */ -webkit-border-radius: 4px; border-radius: 4px; mask: none; } .case { /*autoprefixer:OFF*/ -webkit-border-radius: 4px; border-radius: 4px; mask: none; } @support (transition: 1s) { /* autoprefixer: off */ :fullscreen { -webkit-border-radius: 4px; border-radius: 4px; mask: none; } ::-webkit-input-placeholder { /*autoprefixer: on*/ border-radius: 4px; -webkit-mask: none; mask: none; } ::placeholder { /*autoprefixer: on*/ border-radius: 4px; -webkit-mask: none; mask: none; } } ================================================ FILE: test/cases/double.css ================================================ a { flex-basis: 8.33333%; flex-basis: calc(100% / 12 * 1) } ================================================ FILE: test/cases/double.out.css ================================================ a { -webkit-flex-basis: 8.33333%; -ms-flex-preferred-size: 8.33333%; flex-basis: 8.33333%; -webkit-flex-basis: -webkit-calc(100% / 12 * 1); -ms-flex-preferred-size: calc(100% / 12 * 1); flex-basis: calc(100% / 12 * 1) } ================================================ FILE: test/cases/element.css ================================================ div { background: element(#id); } div { background: url(image.jpg), element(#id); } ================================================ FILE: test/cases/element.out.css ================================================ div { background: -moz-element(#id); background: element(#id); } div { background: url(image.jpg), -moz-element(#id); background: url(image.jpg), element(#id); } ================================================ FILE: test/cases/example.css ================================================ ::placeholder { color: gray; } .image { width: stretch; } ================================================ FILE: test/cases/example.out.css ================================================ ::-moz-placeholder { color: gray; } ::placeholder { color: gray; } .image { width: -webkit-fill-available; width: -moz-available; width: stretch; } ================================================ FILE: test/cases/file-selector-button.css ================================================ ::file-selector-button { background: black } input::file-selector-button { color: black; } input:hover::file-selector-button { color: white; } ================================================ FILE: test/cases/file-selector-button.out.css ================================================ ::-webkit-file-upload-button { background: black } ::file-selector-button { background: black } input::-webkit-file-upload-button { color: black; } input::file-selector-button { color: black; } input:hover::-webkit-file-upload-button { color: white; } input:hover::file-selector-button { color: white; } ================================================ FILE: test/cases/filter.css ================================================ a { filter: blur(10px); transition: filter 2s; } div { filter: progid:DXImageTransform.Microsoft.Alpha(opacity=50); } b { filter: alpha(opacity=100); } em { filter: Alpha(opacity=100); } ================================================ FILE: test/cases/filter.out.css ================================================ a { -webkit-filter: blur(10px); filter: blur(10px); -webkit-transition: -webkit-filter 2s; transition: -webkit-filter 2s; transition: filter 2s; transition: filter 2s, -webkit-filter 2s; } div { filter: progid:DXImageTransform.Microsoft.Alpha(opacity=50); } b { filter: alpha(opacity=100); } em { filter: Alpha(opacity=100); } ================================================ FILE: test/cases/flex-rewrite.css ================================================ .a { flex-grow: 0; } ================================================ FILE: test/cases/flex-rewrite.out.css ================================================ .a { -webkit-box-flex: 0; -webkit-flex-grow: 0; -moz-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } ================================================ FILE: test/cases/flexbox.css ================================================ a { -js-display: flex; display: flex; flex-flow: row; order: 0; flex: 0 1 2; transition: flex 200ms; } .inline { display: inline-flex; align-self: auto; align-content: stretch; flex: auto; } .a { display: flex; flex-direction: row; justify-content: flex-start; align-items: flex-start; flex-wrap: nowrap; align-content: flex-start; align-self: flex-start; flex: none; } .b { display: flex; flex-direction: row-reverse; justify-content: flex-end; align-items: flex-end; flex-wrap: wrap; align-content: flex-end; align-self: flex-end; flex-shrink: 1; } .c { display: flex; flex-direction: column; justify-content: center; align-items: center; flex-wrap: reverse-wrap; align-content: center; align-self: center; flex-basis: auto; } .e { display: flex; flex-direction: column-reverse; justify-content: space-between; align-items: baseline; align-content: space-between; align-self: baseline; } .f { display: flex; justify-content: space-around; align-items: stretch; align-content: space-around; align-self: stretch; } .g { display: flex; flex: calc(1em + 1px) 0 0; } .h { flex-flow: column wrap; } .i { flex-flow: nowrap; } .inherit { order: inherit; flex-direction: inherit; } @supports (display: flex) { .foo { display: flex; } } @supports (flex: auto) { .foo { flex: auto; } } ================================================ FILE: test/cases/flexbox.out.css ================================================ a { -js-display: flex; display: -webkit-box; display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-flow: row; -moz-box-orient: horizontal; -moz-box-direction: normal; -ms-flex-flow: row; flex-flow: row; -webkit-box-ordinal-group: 1; -webkit-order: 0; -moz-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; -webkit-box-flex: 0; -webkit-flex: 0 1 2; -moz-box-flex: 0; -ms-flex: 0 1 2; flex: 0 1 2; -webkit-transition: -webkit-box-flex 200ms, -webkit-flex 200ms; transition: -webkit-box-flex 200ms, -webkit-flex 200ms; transition: flex 200ms; transition: flex 200ms, -webkit-box-flex 200ms, -webkit-flex 200ms, -moz-box-flex 200ms, -ms-flex 200ms; } .inline { display: -webkit-inline-box; display: -webkit-inline-flex; display: -moz-inline-box; display: -ms-inline-flexbox; display: inline-flex; -webkit-align-self: auto; -ms-flex-item-align: auto; align-self: auto; -webkit-align-content: stretch; -ms-flex-line-pack: stretch; align-content: stretch; -webkit-box-flex: 1; -webkit-flex: auto; -moz-box-flex: 1; -ms-flex: auto; flex: auto; } .a { display: -webkit-box; display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; -moz-box-orient: horizontal; -moz-box-direction: normal; -ms-flex-direction: row; flex-direction: row; -webkit-box-pack: start; -webkit-justify-content: flex-start; -moz-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; -webkit-box-align: start; -webkit-align-items: flex-start; -moz-box-align: start; -ms-flex-align: start; align-items: flex-start; -webkit-flex-wrap: nowrap; -ms-flex-wrap: nowrap; flex-wrap: nowrap; -webkit-align-content: flex-start; -ms-flex-line-pack: start; align-content: flex-start; -webkit-align-self: flex-start; -ms-flex-item-align: start; align-self: flex-start; -webkit-box-flex: 0; -webkit-flex: none; -moz-box-flex: 0; -ms-flex: none; flex: none; } .b { display: -webkit-box; display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -webkit-flex-direction: row-reverse; -moz-box-orient: horizontal; -moz-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; -webkit-box-pack: end; -webkit-justify-content: flex-end; -moz-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; -webkit-box-align: end; -webkit-align-items: flex-end; -moz-box-align: end; -ms-flex-align: end; align-items: flex-end; -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; -webkit-align-content: flex-end; -ms-flex-line-pack: end; align-content: flex-end; -webkit-align-self: flex-end; -ms-flex-item-align: end; align-self: flex-end; -webkit-flex-shrink: 1; -ms-flex-negative: 1; flex-shrink: 1; } .c { display: -webkit-box; display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -moz-box-orient: vertical; -moz-box-direction: normal; -ms-flex-direction: column; flex-direction: column; -webkit-box-pack: center; -webkit-justify-content: center; -moz-box-pack: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -webkit-align-items: center; -moz-box-align: center; -ms-flex-align: center; align-items: center; -webkit-flex-wrap: reverse-wrap; -ms-flex-wrap: reverse-wrap; flex-wrap: reverse-wrap; -webkit-align-content: center; -ms-flex-line-pack: center; align-content: center; -webkit-align-self: center; -ms-flex-item-align: center; align-self: center; -webkit-flex-basis: auto; -ms-flex-preferred-size: auto; flex-basis: auto; } .e { display: -webkit-box; display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: reverse; -webkit-flex-direction: column-reverse; -moz-box-orient: vertical; -moz-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; -webkit-box-pack: justify; -webkit-justify-content: space-between; -moz-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; -webkit-box-align: baseline; -webkit-align-items: baseline; -moz-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; -webkit-align-content: space-between; -ms-flex-line-pack: justify; align-content: space-between; -webkit-align-self: baseline; -ms-flex-item-align: baseline; align-self: baseline; } .f { display: -webkit-box; display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: flex; -webkit-justify-content: space-around; -ms-flex-pack: distribute; justify-content: space-around; -webkit-box-align: stretch; -webkit-align-items: stretch; -moz-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; -webkit-align-content: space-around; -ms-flex-line-pack: distribute; align-content: space-around; -webkit-align-self: stretch; -ms-flex-item-align: stretch; align-self: stretch; } .g { display: -webkit-box; display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: flex; -webkit-box-flex: -webkit-calc(1em + 1px); -webkit-flex: -webkit-calc(1em + 1px) 0 0; -moz-box-flex: calc(1em + 1px); -ms-flex: calc(1em + 1px) 0 0px; flex: calc(1em + 1px) 0 0; } .h { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-flow: column wrap; -moz-box-orient: vertical; -moz-box-direction: normal; -ms-flex-flow: column wrap; flex-flow: column wrap; } .i { -webkit-flex-flow: nowrap; -ms-flex-flow: nowrap; flex-flow: nowrap; } .inherit { -webkit-box-ordinal-group: inherit; -webkit-order: inherit; -moz-box-ordinal-group: inherit; -ms-flex-order: inherit; order: inherit; -webkit-box-orient: inherit; -webkit-box-direction: inherit; -webkit-flex-direction: inherit; -moz-box-orient: inherit; -moz-box-direction: inherit; -ms-flex-direction: inherit; flex-direction: inherit; } @supports ((display: -webkit-flex) or (display: flex)) { .foo { display: -webkit-box; display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: flex; } } @supports ((-webkit-flex: auto) or (flex: auto)) { .foo { -webkit-box-flex: 1; -webkit-flex: auto; -moz-box-flex: 1; -ms-flex: auto; flex: auto; } } ================================================ FILE: test/cases/fullscreen.css ================================================ :fullscreen { background: black } ================================================ FILE: test/cases/fullscreen.out.css ================================================ :-webkit-full-screen { background: black } :fullscreen { background: black } ================================================ FILE: test/cases/gradient-fix.css ================================================ .test { background-image: -webkit-linear-gradient(yellow 50%, red 50%); background-image: linear-gradient(to , red 50%); } ================================================ FILE: test/cases/gradient-fix.out.css ================================================ .test { background-image: -webkit-linear-gradient(yellow 50%, red 50%); background-image: -webkit-linear-gradient(red 50%); background-image: -moz-linear-gradient(red 50%); background-image: -o-linear-gradient(red 50%); background-image: linear-gradient(to , red 50%); } ================================================ FILE: test/cases/gradient.css ================================================ a { background: linear-gradient(350.5deg, white, black), linear-gradient(-130deg, black, white), linear-gradient(45deg, black, white); } b { background-image: linear-gradient(rgba(0,0,0,1), white), linear-gradient(white, black); } strong { background: linear-gradient(to top, transparent, rgba(0, 0, 0, 0.8) 20px, #000 30px, #000) no-repeat; } div { background-image: radial-gradient(to left, white, black), repeating-linear-gradient(to bottom right, black, white), repeating-radial-gradient(to top, aqua, red); } .old-radial { background: radial-gradient(0 50%, ellipse farthest-corner, black, white); } .simple1 { background: linear-gradient(black, white); } .simple2 { background: linear-gradient(to left, black 0%, rgba(0, 0, 0, 0.5)50%, white 100%); } .simple3 { background: linear-gradient(to left, black 50%, white 100%); } .simple4 { background: linear-gradient(to right top, black, white); } .direction { background: linear-gradient(top left, black, rgba(0, 0, 0, 0.5), white); } .silent { background: -webkit-linear-gradient(top left, black, white); } .radial { background: radial-gradient(farthest-side at 0 50%, white, black); } .second { background: red linear-gradient(red, blue); background: url('logo.png'), linear-gradient(#fff, #000); } .px { background: linear-gradient(black 0, white 100px); } .list { list-style-image: linear-gradient(white, black); } .mask { mask: linear-gradient(white, black); } .newline { background-image: linear-gradient( white, black ), linear-gradient( black, white ); } .convert { background: linear-gradient(0deg, white, black); background: linear-gradient(90deg, white, black); background: linear-gradient(180deg, white, black); background: linear-gradient(270deg, white, black); } .grad { background: linear-gradient(1grad, white, black); } .rad { background: linear-gradient(1rad, white, black); } .turn { background: linear-gradient(0.3turn, white, black); } .norm { background: linear-gradient(-90deg, white, black); } .mask { mask-image: radial-gradient(circle at 86% 86%, transparent 8px, black 8px); } .cover { background: radial-gradient(ellipse cover at center, white, black); } .contain { background: radial-gradient(contain at center, white, black); } .no-div { background: linear-gradient(black); } .background-shorthand { background: radial-gradient(#FFF, transparent) 0 0 / cover no-repeat #F0F; } .background-advanced { background: radial-gradient(ellipse farthest-corner at 5px 15px, rgba(214, 168, 18, 0.7) 0%, rgba(255, 21, 177, 0.7) 50%, rgba(210, 7, 148, 0.7) 95%), radial-gradient(#FFF, transparent), url(path/to/image.jpg) 50%/cover; } .multiradial { mask-image: radial-gradient(circle closest-corner at 100% 50%, #000, transparent); } .broken { mask-image: radial-gradient(white, black); } .loop { background-image: url("https://test.com/lol(test.png"), radial-gradient(yellow, black, yellow); } .unitless-zero { background-image: linear-gradient(0, green, blue); background: repeating-linear-gradient(0, blue, red 33.3%) } .zero-grad { background: linear-gradient(0grad, green, blue); background-image: repeating-linear-gradient(0grad, blue, red 33.3%) } .zero-rad { background: linear-gradient(0rad, green, blue); } .zero-turn { background: linear-gradient(0turn, green, blue); } ================================================ FILE: test/cases/gradient.out.css ================================================ a { background: -webkit-linear-gradient(99.5deg, white, black), -webkit-linear-gradient(220deg, black, white), -webkit-linear-gradient(45deg, black, white); background: -o-linear-gradient(99.5deg, white, black), -o-linear-gradient(220deg, black, white), -o-linear-gradient(45deg, black, white); background: linear-gradient(350.5deg, white, black), linear-gradient(-130deg, black, white), linear-gradient(45deg, black, white); } b { background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,1)), to(white)), -webkit-gradient(linear, left top, left bottom, from(white), to(black)); background-image: -webkit-linear-gradient(rgba(0,0,0,1), white), -webkit-linear-gradient(white, black); background-image: -o-linear-gradient(rgba(0,0,0,1), white), -o-linear-gradient(white, black); background-image: linear-gradient(rgba(0,0,0,1), white), linear-gradient(white, black); } strong { background: -webkit-linear-gradient(bottom, transparent, rgba(0, 0, 0, 0.8) 20px, #000 30px, #000) no-repeat; background: -o-linear-gradient(bottom, transparent, rgba(0, 0, 0, 0.8) 20px, #000 30px, #000) no-repeat; background: linear-gradient(to top, transparent, rgba(0, 0, 0, 0.8) 20px, #000 30px, #000) no-repeat; } div { background-image: -webkit-radial-gradient(right, white, black), -webkit-repeating-linear-gradient(top left, black, white), -webkit-repeating-radial-gradient(bottom, aqua, red); background-image: -o-radial-gradient(right, white, black), -o-repeating-linear-gradient(top left, black, white), -o-repeating-radial-gradient(bottom, aqua, red); background-image: radial-gradient(to left, white, black), repeating-linear-gradient(to bottom right, black, white), repeating-radial-gradient(to top, aqua, red); } .old-radial { background: -webkit-radial-gradient(0 50%, ellipse farthest-corner, black, white); background: -o-radial-gradient(0 50%, ellipse farthest-corner, black, white); background: radial-gradient(0 50%, ellipse farthest-corner, black, white); } .simple1 { background: -webkit-gradient(linear, left top, left bottom, from(black), to(white)); background: -webkit-linear-gradient(black, white); background: -o-linear-gradient(black, white); background: linear-gradient(black, white); } .simple2 { background: -webkit-gradient(linear, right top, left top, from(black), color-stop(50%, rgba(0, 0, 0, 0.5)), to(white)); background: -webkit-linear-gradient(right, black 0%, rgba(0, 0, 0, 0.5)50%, white 100%); background: -o-linear-gradient(right, black 0%, rgba(0, 0, 0, 0.5)50%, white 100%); background: linear-gradient(to left, black 0%, rgba(0, 0, 0, 0.5)50%, white 100%); } .simple3 { background: -webkit-gradient(linear, right top, left top, color-stop(50%, black), to(white)); background: -webkit-linear-gradient(right, black 50%, white 100%); background: -o-linear-gradient(right, black 50%, white 100%); background: linear-gradient(to left, black 50%, white 100%); } .simple4 { background: -webkit-gradient(linear, left bottom, right top, from(black), to(white)); background: -webkit-linear-gradient(left bottom, black, white); background: -o-linear-gradient(left bottom, black, white); background: linear-gradient(to right top, black, white); } .direction { background: -webkit-gradient(linear, left top, right bottom, from(black), color-stop(rgba(0, 0, 0, 0.5)), to(white)); background: -webkit-linear-gradient(top left, black, rgba(0, 0, 0, 0.5), white); background: -o-linear-gradient(top left, black, rgba(0, 0, 0, 0.5), white); background: linear-gradient(top left, black, rgba(0, 0, 0, 0.5), white); } .silent { background: -webkit-linear-gradient(top left, black, white); } .radial { background: -webkit-radial-gradient(0 50%, farthest-side, white, black); background: -o-radial-gradient(0 50%, farthest-side, white, black); background: radial-gradient(farthest-side at 0 50%, white, black); } .second { background: red -webkit-gradient(linear, left top, left bottom, from(red), to(blue)); background: red -webkit-linear-gradient(red, blue); background: red -o-linear-gradient(red, blue); background: red linear-gradient(red, blue); background: url('logo.png'), -webkit-gradient(linear, left top, left bottom, from(#fff), to(#000)); background: url('logo.png'), -webkit-linear-gradient(#fff, #000); background: url('logo.png'), -o-linear-gradient(#fff, #000); background: url('logo.png'), linear-gradient(#fff, #000); } .px { background: -webkit-linear-gradient(black 0, white 100px); background: -o-linear-gradient(black 0, white 100px); background: linear-gradient(black 0, white 100px); } .list { list-style-image: -webkit-gradient(linear, left top, left bottom, from(white), to(black)); list-style-image: -webkit-linear-gradient(white, black); list-style-image: linear-gradient(white, black); } .mask { -webkit-mask: -webkit-gradient(linear, left top, left bottom, from(white), to(black)); -webkit-mask: -webkit-linear-gradient(white, black); mask: -webkit-gradient(linear, left top, left bottom, from(white), to(black)); mask: linear-gradient(white, black); } .newline { background-image: -webkit-gradient( linear, left top, left bottom, from(white), to(black) ), -webkit-gradient( linear, left top, left bottom, from(black), to(white) ); background-image: -webkit-linear-gradient( white, black ), -webkit-linear-gradient( black, white ); background-image: -o-linear-gradient( white, black ), -o-linear-gradient( black, white ); background-image: linear-gradient( white, black ), linear-gradient( black, white ); } .convert { background: -webkit-gradient(linear, left bottom, left top, from(white), to(black)); background: -webkit-linear-gradient(bottom, white, black); background: -o-linear-gradient(bottom, white, black); background: linear-gradient(0deg, white, black); background: -webkit-gradient(linear, left top, right top, from(white), to(black)); background: -webkit-linear-gradient(left, white, black); background: -o-linear-gradient(left, white, black); background: linear-gradient(90deg, white, black); background: -webkit-gradient(linear, left top, left bottom, from(white), to(black)); background: -webkit-linear-gradient(top, white, black); background: -o-linear-gradient(top, white, black); background: linear-gradient(180deg, white, black); background: -webkit-gradient(linear, right top, left top, from(white), to(black)); background: -webkit-linear-gradient(right, white, black); background: -o-linear-gradient(right, white, black); background: linear-gradient(270deg, white, black); } .grad { background: -webkit-linear-gradient(89.1deg, white, black); background: -o-linear-gradient(89.1deg, white, black); background: linear-gradient(1grad, white, black); } .rad { background: -webkit-linear-gradient(32.704deg, white, black); background: -o-linear-gradient(32.704deg, white, black); background: linear-gradient(1rad, white, black); } .turn { background: -webkit-linear-gradient(342deg, white, black); background: -o-linear-gradient(342deg, white, black); background: linear-gradient(0.3turn, white, black); } .norm { background: -webkit-gradient(linear, right top, left top, from(white), to(black)); background: -webkit-linear-gradient(right, white, black); background: -o-linear-gradient(right, white, black); background: linear-gradient(-90deg, white, black); } .mask { -webkit-mask-image: -webkit-radial-gradient(86% 86%, circle, transparent 8px, black 8px); mask-image: radial-gradient(circle at 86% 86%, transparent 8px, black 8px); } .cover { background: -webkit-radial-gradient(center, ellipse cover, white, black); background: -o-radial-gradient(center, ellipse cover, white, black); background: radial-gradient(ellipse cover at center, white, black); } .contain { background: -webkit-radial-gradient(center, contain, white, black); background: -o-radial-gradient(center, contain, white, black); background: radial-gradient(contain at center, white, black); } .no-div { background: -webkit-gradient(linear, left top, left bottom, from(black)); background: -webkit-linear-gradient(black); background: -o-linear-gradient(black); background: linear-gradient(black); } .background-shorthand { background: -webkit-radial-gradient(#FFF, transparent) 0 0 / cover no-repeat #F0F; background: -o-radial-gradient(#FFF, transparent) 0 0 / cover no-repeat #F0F; background: radial-gradient(#FFF, transparent) 0 0 / cover no-repeat #F0F; } .background-advanced { background: -webkit-radial-gradient(5px 15px, ellipse farthest-corner, rgba(214, 168, 18, 0.7) 0%, rgba(255, 21, 177, 0.7) 50%, rgba(210, 7, 148, 0.7) 95%), -webkit-radial-gradient(#FFF, transparent), url(path/to/image.jpg) 50%/cover; background: -o-radial-gradient(5px 15px, ellipse farthest-corner, rgba(214, 168, 18, 0.7) 0%, rgba(255, 21, 177, 0.7) 50%, rgba(210, 7, 148, 0.7) 95%), -o-radial-gradient(#FFF, transparent), url(path/to/image.jpg) 50%/cover; background: radial-gradient(ellipse farthest-corner at 5px 15px, rgba(214, 168, 18, 0.7) 0%, rgba(255, 21, 177, 0.7) 50%, rgba(210, 7, 148, 0.7) 95%), radial-gradient(#FFF, transparent), url(path/to/image.jpg) 50%/cover; } .multiradial { -webkit-mask-image: -webkit-radial-gradient(100% 50%, circle closest-corner, #000, transparent); mask-image: radial-gradient(circle closest-corner at 100% 50%, #000, transparent); } .broken { -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black); } .loop { background-image: url("https://test.com/lol(test.png"), -webkit-radial-gradient(yellow, black, yellow); background-image: url("https://test.com/lol(test.png"), -o-radial-gradient(yellow, black, yellow); background-image: url("https://test.com/lol(test.png"), radial-gradient(yellow, black, yellow); } .unitless-zero { background-image: -webkit-gradient(linear, left bottom, left top, from(green), to(blue)); background-image: -webkit-linear-gradient(bottom, green, blue); background-image: -o-linear-gradient(bottom, green, blue); background-image: linear-gradient(0, green, blue); background: -webkit-repeating-linear-gradient(bottom, blue, red 33.3%); background: -o-repeating-linear-gradient(bottom, blue, red 33.3%); background: repeating-linear-gradient(0, blue, red 33.3%) } .zero-grad { background: -webkit-gradient(linear, left bottom, left top, from(green), to(blue)); background: -webkit-linear-gradient(bottom, green, blue); background: -o-linear-gradient(bottom, green, blue); background: linear-gradient(0grad, green, blue); background-image: -webkit-repeating-linear-gradient(bottom, blue, red 33.3%); background-image: -o-repeating-linear-gradient(bottom, blue, red 33.3%); background-image: repeating-linear-gradient(0grad, blue, red 33.3%) } .zero-rad { background: -webkit-gradient(linear, left bottom, left top, from(green), to(blue)); background: -webkit-linear-gradient(bottom, green, blue); background: -o-linear-gradient(bottom, green, blue); background: linear-gradient(0rad, green, blue); } .zero-turn { background: -webkit-gradient(linear, left bottom, left top, from(green), to(blue)); background: -webkit-linear-gradient(bottom, green, blue); background: -o-linear-gradient(bottom, green, blue); background: linear-gradient(0turn, green, blue); } ================================================ FILE: test/cases/grid-area-media-sequence.css ================================================ .grid-template-sequence { display: grid; grid-template: "foo foo" "bar bar" "baz baz" / 1fr 1fr; } @media (min-width: 300px) { .grid-template-sequence { grid-template: "foo foo" "bar bar" 20px / 1fr 1fr; } } @media (min-width: 600px) { .grid-template-sequence { grid-template: "foo foo" "bar bar" 20px / 1fr 1fr; } } @media (min-width: 900px) { .grid-template-sequence { grid-template: "x x" "foo foo" "bar bar" 20px / 1fr 1fr; } } .foo { grid-area: foo; } .bar { grid-area: bar; } .x { grid-area: x; } @media (max-width: 900px) { .grid-2 { grid-template: "a a" "a a" "b b" 20px / 1fr 1fr; } } @media (max-width: 400px) { .grid-2 { grid-template: "a a" "a a" "b b" 20px / 1fr 1fr; } } ================================================ FILE: test/cases/grid-area-media-sequence.out.css ================================================ .grid-template-sequence { display: -ms-grid; display: grid; -ms-grid-rows: auto auto auto; -ms-grid-columns: 1fr 1fr; grid-template: "foo foo" "bar bar" "baz baz" / 1fr 1fr; } @media (min-width: 300px) { .grid-template-sequence { -ms-grid-rows: auto 20px; -ms-grid-columns: 1fr 1fr; grid-template: "foo foo" "bar bar" 20px / 1fr 1fr; } } @media (min-width: 600px) { .grid-template-sequence { -ms-grid-rows: auto 20px; -ms-grid-columns: 1fr 1fr; grid-template: "foo foo" "bar bar" 20px / 1fr 1fr; } } @media (min-width: 900px) { .grid-template-sequence { -ms-grid-rows: auto auto 20px; -ms-grid-columns: 1fr 1fr; grid-template: "x x" "foo foo" "bar bar" 20px / 1fr 1fr; } } .foo { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: foo; } .bar { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: bar; } .x { grid-area: x; } @media (min-width: 300px) { .foo { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } .bar { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 2; } } @media (min-width: 600px) { .foo { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } .bar { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 2; } } @media (min-width: 900px) { .foo { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 2; } .bar { -ms-grid-row: 3; -ms-grid-column: 1; -ms-grid-column-span: 2; } .x { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } } @media (max-width: 900px) { .grid-2 { -ms-grid-rows: auto auto 20px; -ms-grid-columns: 1fr 1fr; grid-template: "a a" "a a" "b b" 20px / 1fr 1fr; } } @media (max-width: 400px) { .grid-2 { -ms-grid-rows: auto auto 20px; -ms-grid-columns: 1fr 1fr; grid-template: "a a" "a a" "b b" 20px / 1fr 1fr; } } ================================================ FILE: test/cases/grid-area.css ================================================ .a { grid-area: 5 / 1 / span 1 / span 5; } .b { grid-area: span 1 / span 3 / 4 / 4; } .c { grid-area: 2 / 2; } .d { grid-area: "custom-ident"; } .e { grid-area: 2 / 2 / 3 / 8; } .f { grid-area: 2 / 2 / 3; } /* emit warning */ .g { grid-area: 3 / 1; grid-row: 1; grid-column: 2; } .h { grid-area: 3 / 1; grid-column: 1 / span 4; } /* there should be no warning */ .i { grid-area: 3 / 1; grid-column-end: span 4; } .j { grid-area: 3 / 1; grid-row-end: span 4; } ================================================ FILE: test/cases/grid-area.out.css ================================================ .a { -ms-grid-row: 5; -ms-grid-row-span: 1; -ms-grid-column: 1; -ms-grid-column-span: 5; grid-area: 5 / 1 / span 1 / span 5; } .b { -ms-grid-row: 3; -ms-grid-row-span: 1; -ms-grid-column: 1; -ms-grid-column-span: 3; grid-area: span 1 / span 3 / 4 / 4; } .c { -ms-grid-row: 2; -ms-grid-column: 2; grid-area: 2 / 2; } .d { grid-area: "custom-ident"; } .e { -ms-grid-row: 2; -ms-grid-row-span: 1; -ms-grid-column: 2; -ms-grid-column-span: 6; grid-area: 2 / 2 / 3 / 8; } .f { -ms-grid-row: 2; -ms-grid-row-span: 1; -ms-grid-column: 2; grid-area: 2 / 2 / 3; } /* emit warning */ .g { -ms-grid-row: 3; -ms-grid-column: 1; grid-area: 3 / 1; grid-row: 1; grid-column: 2; } .h { -ms-grid-row: 3; -ms-grid-column: 1; grid-area: 3 / 1; -ms-grid-column-span: 4; grid-column: 1 / span 4; } /* there should be no warning */ .i { -ms-grid-row: 3; -ms-grid-column: 1; grid-area: 3 / 1; -ms-grid-column-span: 4; grid-column-end: span 4; } .j { -ms-grid-row: 3; -ms-grid-column: 1; grid-area: 3 / 1; -ms-grid-row-span: 4; grid-row-end: span 4; } ================================================ FILE: test/cases/grid-areas-duplicate-complex.css ================================================ /*******************************\ COMPLEX DUPLICATE AREAS TEST 1 \*******************************/ #main > #content { grid-area: content; } #main > .sidebar { grid-area: first-sb; } #main > .sidebar.second { grid-area: second-sb; } @media (min-width: 660px) { #main { display: grid; grid-template: "..... ... content ....."/20% 1em 1fr 20%; } #main.has-first-sidebar { grid-template: "..... ... first-sb .... content ....."/20% 1em 300px 30px 1fr 20%; } #main.has-second-sidebar { grid-template: "..... ... content .... second-sb ....."/20% 1em 1fr 30px 300px 20%; } #main.has-first-sidebar.has-second-sidebar { grid-template: "..... ... first-sb .... content .... second-sb ....."/20% 1em 200px 30px 1fr 30px 200px 20%; } } @media (max-width: 1499px) { #main { grid-template: "..... ... content ....."/10% 1em 1fr 10%; } #main.has-first-sidebar { grid-template: "..... ... first-sb .... content ....."/10% 1em 250px 30px 1fr 10%; } #main.has-second-sidebar { grid-template: "..... ... content .... second-sb ....."/10% 1em 1fr 30px 250px 10%; } #main.has-first-sidebar.has-second-sidebar { grid-template: "..... ... first-sb .... content .... second-sb ....."/10% 1em 200px 30px 1fr 30px 200px 10%; } } @media (max-width: 959px) { #main { grid-template: "..... ... content ....."/2em 1em 1fr 2em; } #main.has-first-sidebar { grid-template: "..... ... first-sb .... content ....."/2em 1em 200px 30px 1fr 2em; } #main.has-second-sidebar { grid-template: "..... ... content .... second-sb ....."/2em 1em 1fr 30px 200px 2em; } #main.has-first-sidebar.has-second-sidebar { grid-template: "..... ... first-sb .... content .... second-sb ....."/2em 1em 150px 30px 1fr 30px 150px 2em; } } /*******************************\ COMPLEX DUPLICATE AREAS TEST 2 \*******************************/ #main-second { display: grid; grid-template: "..... ... content-2 ....."/20% 1em 1fr 20%; } #main-second.has-first-sidebar { grid-template: "..... ... first-sb-2 .... content-2 ....."/20% 1em 300px 30px 1fr 20%; } #main-second.has-second-sidebar { grid-template: "..... ... content-2 .... second-sb-2 ....."/20% 1em 1fr 30px 300px 20%; } #main-second.has-first-sidebar.has-second-sidebar { grid-template: "..... ... first-sb-2 .... content-2 .... second-sb-2 ....."/20% 1em 200px 30px 1fr 30px 200px 20%; } @media (max-width: 960px) { #main-second { grid-template: "..... ... content-2 ....."/2em 1em 1fr 2em; } #main-second.has-first-sidebar { grid-template: "..... ... first-sb-2 .... content-2 ....."/2em 1em 200px 30px 1fr 2em; } #main-second.has-second-sidebar { grid-template: "..... ... content-2 .... second-sb-2 ....."/2em 1em 1fr 30px 200px 2em; } #main-second.has-first-sidebar.has-second-sidebar { grid-template: "..... ... first-sb-2 .... content-2 .... second-sb-2 ....."/2em 1em 150px 30px 1fr 30px 150px 2em; } } .content { grid-area: content-2; } .sidebar { grid-area: first-sb-2; } .sidebar.second { grid-area: second-sb-2; } /* rule with not found identifier (should not break the compiler) */ .hello { grid-area: world; } ================================================ FILE: test/cases/grid-areas-duplicate-complex.out.css ================================================ /*******************************\ COMPLEX DUPLICATE AREAS TEST 1 \*******************************/ #main > #content { grid-area: content; } #main > .sidebar { grid-area: first-sb; } #main > .sidebar.second { grid-area: second-sb; } @media (min-width: 660px) { #main { display: -ms-grid; display: grid; -ms-grid-rows: auto; -ms-grid-columns: 20% 1em 1fr 20%; grid-template: "..... ... content ....."/20% 1em 1fr 20%; } #main.has-first-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 20% 1em 300px 30px 1fr 20%; grid-template: "..... ... first-sb .... content ....."/20% 1em 300px 30px 1fr 20%; } #main.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 20% 1em 1fr 30px 300px 20%; grid-template: "..... ... content .... second-sb ....."/20% 1em 1fr 30px 300px 20%; } #main.has-first-sidebar.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 20% 1em 200px 30px 1fr 30px 200px 20%; grid-template: "..... ... first-sb .... content .... second-sb ....."/20% 1em 200px 30px 1fr 30px 200px 20%; } #main > #content { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 5; } #main.has-second-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar.has-second-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 5; } #main > .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar.has-second-sidebar > .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } #main > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 5; } #main.has-first-sidebar.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 7; } } @media (max-width: 1499px) { #main { -ms-grid-rows: auto; -ms-grid-columns: 10% 1em 1fr 10%; grid-template: "..... ... content ....."/10% 1em 1fr 10%; } #main.has-first-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 10% 1em 250px 30px 1fr 10%; grid-template: "..... ... first-sb .... content ....."/10% 1em 250px 30px 1fr 10%; } #main.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 10% 1em 1fr 30px 250px 10%; grid-template: "..... ... content .... second-sb ....."/10% 1em 1fr 30px 250px 10%; } #main.has-first-sidebar.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 10% 1em 200px 30px 1fr 30px 200px 10%; grid-template: "..... ... first-sb .... content .... second-sb ....."/10% 1em 200px 30px 1fr 30px 200px 10%; } #main > #content { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 5; } #main.has-second-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar.has-second-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 5; } #main.has-first-sidebar > .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar.has-second-sidebar > .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 5; } #main.has-first-sidebar.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 7; } } @media (max-width: 959px) { #main { -ms-grid-rows: auto; -ms-grid-columns: 2em 1em 1fr 2em; grid-template: "..... ... content ....."/2em 1em 1fr 2em; } #main.has-first-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 2em 1em 200px 30px 1fr 2em; grid-template: "..... ... first-sb .... content ....."/2em 1em 200px 30px 1fr 2em; } #main.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 2em 1em 1fr 30px 200px 2em; grid-template: "..... ... content .... second-sb ....."/2em 1em 1fr 30px 200px 2em; } #main.has-first-sidebar.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 2em 1em 150px 30px 1fr 30px 150px 2em; grid-template: "..... ... first-sb .... content .... second-sb ....."/2em 1em 150px 30px 1fr 30px 150px 2em; } #main > #content { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 5; } #main.has-second-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar.has-second-sidebar > #content { -ms-grid-row: 1; -ms-grid-column: 5; } #main.has-first-sidebar > .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-first-sidebar.has-second-sidebar > .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } #main.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 5; } #main.has-first-sidebar.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 7; } } /*******************************\ COMPLEX DUPLICATE AREAS TEST 2 \*******************************/ #main-second { display: -ms-grid; display: grid; -ms-grid-rows: auto; -ms-grid-columns: 20% 1em 1fr 20%; grid-template: "..... ... content-2 ....."/20% 1em 1fr 20%; } #main-second.has-first-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 20% 1em 300px 30px 1fr 20%; grid-template: "..... ... first-sb-2 .... content-2 ....."/20% 1em 300px 30px 1fr 20%; } #main-second.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 20% 1em 1fr 30px 300px 20%; grid-template: "..... ... content-2 .... second-sb-2 ....."/20% 1em 1fr 30px 300px 20%; } #main-second.has-first-sidebar.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 20% 1em 200px 30px 1fr 30px 200px 20%; grid-template: "..... ... first-sb-2 .... content-2 .... second-sb-2 ....."/20% 1em 200px 30px 1fr 30px 200px 20%; } @media (max-width: 960px) { #main-second { -ms-grid-rows: auto; -ms-grid-columns: 2em 1em 1fr 2em; grid-template: "..... ... content-2 ....."/2em 1em 1fr 2em; } #main-second.has-first-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 2em 1em 200px 30px 1fr 2em; grid-template: "..... ... first-sb-2 .... content-2 ....."/2em 1em 200px 30px 1fr 2em; } #main-second.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 2em 1em 1fr 30px 200px 2em; grid-template: "..... ... content-2 .... second-sb-2 ....."/2em 1em 1fr 30px 200px 2em; } #main-second.has-first-sidebar.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 2em 1em 150px 30px 1fr 30px 150px 2em; grid-template: "..... ... first-sb-2 .... content-2 .... second-sb-2 ....."/2em 1em 150px 30px 1fr 30px 150px 2em; } } .content { -ms-grid-row: 1; -ms-grid-column: 3; grid-area: content-2; } #main-second.has-first-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 5; } #main-second.has-second-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 3; } #main-second.has-first-sidebar.has-second-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 5; } .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; grid-area: first-sb-2; } #main-second.has-first-sidebar.has-second-sidebar > .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 5; grid-area: second-sb-2; } #main-second.has-first-sidebar.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 7; } @media (max-width: 960px) { .content { -ms-grid-row: 1; -ms-grid-column: 3; } #main-second.has-first-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 5; } #main-second.has-second-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 3; } #main-second.has-first-sidebar.has-second-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 5; } .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } #main-second.has-first-sidebar.has-second-sidebar > .sidebar { -ms-grid-row: 1; -ms-grid-column: 3; } .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 5; } #main-second.has-first-sidebar.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 7; } } /* rule with not found identifier (should not break the compiler) */ .hello { grid-area: world; } ================================================ FILE: test/cases/grid-autoplacement.css ================================================ .grid-basic { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; grid-gap: 30px; } /* Must work with repeat function */ .grid-repeat { display: grid; grid-template-columns: repeat(4, 1fr 2fr); grid-template-rows: repeat(2, auto); grid-gap: 30px; } /* Complex case */ .grid-complex { display: grid; grid-template-columns: 1fr repeat(2, minmax(100px, 200px)) 1fr; grid-template-rows: 100px repeat(2, auto); grid-gap: 30px 5px; } /* Must work inside media */ @media (min-width: 400px) { .grid-media { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; grid-gap: 30px; } } /* Must consider autoflow value */ .grid-flow-column { display: grid; grid-auto-flow: column; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto auto; grid-gap: 30px; } /* Show warning if rows are not defined and dense value is used */ .grid-warning-dense { grid-auto-flow: column dense; grid-template-columns: 1fr 1fr 1fr; } /* Show warning autoflow used without rows/columns */ .grid-warning-autoflow { grid-auto-flow: column; } .grid-autoplace-disabled { /* autoprefixer grid: no-autoplace */ grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; grid-gap: 30px; } .grid-autoplace-gap-warn { grid-template-columns: 1fr 1fr 1fr; grid-gap: 30px; } ================================================ FILE: test/cases/grid-autoplacement.out.css ================================================ .grid-basic { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 30px 1fr 30px 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto; grid-template-rows: auto; grid-gap: 30px; } .grid-basic > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .grid-basic > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 3; } .grid-basic > *:nth-child(3) { -ms-grid-row: 1; -ms-grid-column: 5; } /* Must work with repeat function */ .grid-repeat { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 30px 2fr 30px 1fr 30px 2fr 30px 1fr 30px 2fr 30px 1fr 30px 2fr; grid-template-columns: repeat(4, 1fr 2fr); -ms-grid-rows: auto 30px auto; grid-template-rows: repeat(2, auto); grid-gap: 30px; } .grid-repeat > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .grid-repeat > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 3; } .grid-repeat > *:nth-child(3) { -ms-grid-row: 1; -ms-grid-column: 5; } .grid-repeat > *:nth-child(4) { -ms-grid-row: 1; -ms-grid-column: 7; } .grid-repeat > *:nth-child(5) { -ms-grid-row: 3; -ms-grid-column: 1; } .grid-repeat > *:nth-child(6) { -ms-grid-row: 3; -ms-grid-column: 3; } .grid-repeat > *:nth-child(7) { -ms-grid-row: 3; -ms-grid-column: 5; } .grid-repeat > *:nth-child(8) { -ms-grid-row: 3; -ms-grid-column: 7; } /* Complex case */ .grid-complex { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 5px minmax(100px, 200px) 5px minmax(100px, 200px) 5px 1fr; grid-template-columns: 1fr repeat(2, minmax(100px, 200px)) 1fr; -ms-grid-rows: 100px 30px auto 30px auto; grid-template-rows: 100px repeat(2, auto); grid-gap: 30px 5px; } .grid-complex > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .grid-complex > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 3; } .grid-complex > *:nth-child(3) { -ms-grid-row: 1; -ms-grid-column: 5; } .grid-complex > *:nth-child(4) { -ms-grid-row: 1; -ms-grid-column: 7; } .grid-complex > *:nth-child(5) { -ms-grid-row: 3; -ms-grid-column: 1; } .grid-complex > *:nth-child(6) { -ms-grid-row: 3; -ms-grid-column: 3; } .grid-complex > *:nth-child(7) { -ms-grid-row: 3; -ms-grid-column: 5; } .grid-complex > *:nth-child(8) { -ms-grid-row: 3; -ms-grid-column: 7; } .grid-complex > *:nth-child(9) { -ms-grid-row: 5; -ms-grid-column: 1; } .grid-complex > *:nth-child(10) { -ms-grid-row: 5; -ms-grid-column: 3; } .grid-complex > *:nth-child(11) { -ms-grid-row: 5; -ms-grid-column: 5; } .grid-complex > *:nth-child(12) { -ms-grid-row: 5; -ms-grid-column: 7; } /* Must work inside media */ @media (min-width: 400px) { .grid-media { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 30px 1fr 30px 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto; grid-template-rows: auto; grid-gap: 30px; } .grid-media > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .grid-media > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 3; } .grid-media > *:nth-child(3) { -ms-grid-row: 1; -ms-grid-column: 5; } } /* Must consider autoflow value */ .grid-flow-column { display: -ms-grid; display: grid; grid-auto-flow: column; -ms-grid-columns: 1fr 30px 1fr 30px 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto 30px auto; grid-template-rows: auto auto; grid-gap: 30px; } .grid-flow-column > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .grid-flow-column > *:nth-child(2) { -ms-grid-row: 3; -ms-grid-column: 1; } .grid-flow-column > *:nth-child(3) { -ms-grid-row: 1; -ms-grid-column: 3; } .grid-flow-column > *:nth-child(4) { -ms-grid-row: 3; -ms-grid-column: 3; } .grid-flow-column > *:nth-child(5) { -ms-grid-row: 1; -ms-grid-column: 5; } .grid-flow-column > *:nth-child(6) { -ms-grid-row: 3; -ms-grid-column: 5; } /* Show warning if rows are not defined and dense value is used */ .grid-warning-dense { grid-auto-flow: column dense; -ms-grid-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; } /* Show warning autoflow used without rows/columns */ .grid-warning-autoflow { grid-auto-flow: column; } .grid-autoplace-disabled { /* autoprefixer grid: no-autoplace */ -ms-grid-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto; grid-template-rows: auto; grid-gap: 30px; } .grid-autoplace-gap-warn { -ms-grid-columns: 1fr 30px 1fr 30px 1fr; grid-template-columns: 1fr 1fr 1fr; grid-gap: 30px; } ================================================ FILE: test/cases/grid-gap.css ================================================ .a { grid-gap: 1rem; grid-template-rows: 1fr minmax(100px, 1fr) 2fr; grid-template-columns: repeat(3, 1fr); grid-template-areas: "head head head" "nav main main" "nav foot foot"; } .b { grid-area: head; } .c { grid-area: main; } .d { grid-area: nav; } .e { grid-area: foot; } .f { gap: 10px 20px; grid-template-columns: 1fr 1fr 1fr; grid-template-areas: "head head head" "nav main main" "nav foot foot"; } .calc { grid-template: 1fr 1fr / 1fr 1fr; gap: calc(1% + 5px); } .a-shortcut { grid-gap: 1rem 2rem; grid-template: "head-shortcut head-shortcut head-shortcut" 1fr "nav-shortcut main-shortcut main-shortcut" minmax(100px, 1fr) "nav-shortcut foot-shortcut foot-shortcut" 2fr / 1fr 100px 1fr; } .b-shortcut { grid-area: head-shortcut; } .c-shortcut { grid-area: main-shortcut; } .d-shortcut { grid-area: nav-shortcut; } .e-shortcut { grid-area: foot-shortcut; } .f-shortcut { grid-gap: 10px; grid-template: "head-shortcut head-shortcut head-shortcut" "nav-shortcut main-shortcut main-shortcut" "nav-shortcut foot-shortcut foot-shortcut" / 1fr 1fr 1fr; } .parent .grid { grid-gap: 30px; display: grid; grid-template: "a b" 100px "c d" 100px "e f" 100px / 1fr 1fr; } /* must not inherit gap from .parent .grid */ .grid { display: grid; grid-template: "a b" 100px "c d" 100px "e f" 100px / 1fr 1fr; } .grid-2 { display: grid; grid-gap: 50px; grid-template: "x x" 100px "y y" 100px "z z" 100px / 1fr 1fr; } .grid-2.modifier-b { grid-gap: 22px; grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2.modifier-b */ .grid-2.modifier-b.foo { grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2.modifier-b */ .grid-2.modifier-b.bar { grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2.modifier-b */ .parent .grid-2.modifier-b.bar { grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2 */ .grid-2.modifier { grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2 */ .parent .grid-2 { grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } @media (min-width: 600px){ .grid { grid-gap: 10px; grid-template-areas: "a b c" "d e f"; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 100px); } } @media (min-width: 700px){ .grid { grid-gap: 20px; grid-template-areas: "a b c" "d e f"; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 100px); } } @media (min-width: 900px){ .grid { grid-template-areas: "a b c d e f"; grid-template-columns: repeat(6, 1fr); grid-template-rows: 100px; } } @media (max-width: 400px){ .grid { grid-template-areas: "a" "b" "c" "d" "e" "f"; grid-template-columns: 1fr; grid-template-rows: repeat(6, 100px); } } @media (max-width: 1400px){ .grid-2 { grid-template: "x y z" 100px "x y z" 100px / 1fr 1fr; } /* must inherit gap from .grid-2 */ .grid-2.modifier { grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2 */ .grid-2.modifier.modifier-c { grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2 */ .parent .grid-2 { grid-template-areas: "x x" "y y" "z z"; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 100px); } } @media (max-width: 1000px){ .grid-2 { grid-gap: 20px; grid-template-areas: "x y z" "x y z"; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 100px); } /* must inherit gap from .grid-2 */ .grid-2.modifier { grid-template-areas: "hi hi hi" "hi hi hi"; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 100px); } } @media (max-width: 700px){ .grid-2 { grid-template-areas: "x x y y z z"; grid-template-columns: repeat(6, 1fr); grid-template-rows: 100px; } /* must NOT inherit gap from .grid-2 */ .grid-2.modifier { grid-gap: 70px; grid-template-areas: "x y z" "x y z"; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 100px); } } @media (max-width: 400px){ .grid-2 { grid-template-areas: "x" "x" "y" "y" "z" "z"; grid-template-columns: 1fr; grid-template-rows: repeat(6, 100px); } /* must inherit gap from .grid-2.modifier */ .grid-2.modifier { grid-template-areas: "x y z" "x y z"; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 100px); } } ================================================ FILE: test/cases/grid-gap.out.css ================================================ .a { grid-gap: 1rem; -ms-grid-rows: 1fr 1rem minmax(100px, 1fr) 1rem 2fr; grid-template-rows: 1fr minmax(100px, 1fr) 2fr; -ms-grid-columns: 1fr 1rem 1fr 1rem 1fr; grid-template-columns: repeat(3, 1fr); grid-template-areas: "head head head" "nav main main" "nav foot foot"; } .b { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 5; grid-area: head; } .f > .b { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 5; } .c { -ms-grid-row: 3; -ms-grid-column: 3; -ms-grid-column-span: 3; grid-area: main; } .f > .c { -ms-grid-row: 3; -ms-grid-column: 3; -ms-grid-column-span: 3; } .d { -ms-grid-row: 3; -ms-grid-row-span: 3; -ms-grid-column: 1; grid-area: nav; } .f > .d { -ms-grid-row: 3; -ms-grid-row-span: 3; -ms-grid-column: 1; } .e { -ms-grid-row: 5; -ms-grid-column: 3; -ms-grid-column-span: 3; grid-area: foot; } .f > .e { -ms-grid-row: 5; -ms-grid-column: 3; -ms-grid-column-span: 3; } .f { gap: 10px 20px; -ms-grid-columns: 1fr 20px 1fr 20px 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto 10px auto 10px auto; grid-template-areas: "head head head" "nav main main" "nav foot foot"; } .calc { -ms-grid-rows: 1fr -webkit-calc(1% + 5px) 1fr; -ms-grid-rows: 1fr calc(1% + 5px) 1fr; -ms-grid-columns: 1fr -webkit-calc(1% + 5px) 1fr; -ms-grid-columns: 1fr calc(1% + 5px) 1fr; grid-template: 1fr 1fr / 1fr 1fr; gap: -webkit-calc(1% + 5px); gap: calc(1% + 5px); } .a-shortcut { grid-gap: 1rem 2rem; -ms-grid-rows: 1fr 1rem minmax(100px, 1fr) 1rem 2fr; -ms-grid-columns: 1fr 2rem 100px 2rem 1fr; grid-template: "head-shortcut head-shortcut head-shortcut" 1fr "nav-shortcut main-shortcut main-shortcut" minmax(100px, 1fr) "nav-shortcut foot-shortcut foot-shortcut" 2fr / 1fr 100px 1fr; } .b-shortcut { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 5; grid-area: head-shortcut; } .f-shortcut > .b-shortcut { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 5; } .c-shortcut { -ms-grid-row: 3; -ms-grid-column: 3; -ms-grid-column-span: 3; grid-area: main-shortcut; } .f-shortcut > .c-shortcut { -ms-grid-row: 3; -ms-grid-column: 3; -ms-grid-column-span: 3; } .d-shortcut { -ms-grid-row: 3; -ms-grid-row-span: 3; -ms-grid-column: 1; grid-area: nav-shortcut; } .f-shortcut > .d-shortcut { -ms-grid-row: 3; -ms-grid-row-span: 3; -ms-grid-column: 1; } .e-shortcut { -ms-grid-row: 5; -ms-grid-column: 3; -ms-grid-column-span: 3; grid-area: foot-shortcut; } .f-shortcut > .e-shortcut { -ms-grid-row: 5; -ms-grid-column: 3; -ms-grid-column-span: 3; } .f-shortcut { grid-gap: 10px; -ms-grid-rows: auto 10px auto 10px auto; -ms-grid-columns: 1fr 10px 1fr 10px 1fr; grid-template: "head-shortcut head-shortcut head-shortcut" "nav-shortcut main-shortcut main-shortcut" "nav-shortcut foot-shortcut foot-shortcut" / 1fr 1fr 1fr; } .parent .grid { grid-gap: 30px; display: -ms-grid; display: grid; -ms-grid-rows: 100px 30px 100px 30px 100px; -ms-grid-columns: 1fr 30px 1fr; grid-template: "a b" 100px "c d" 100px "e f" 100px / 1fr 1fr; } /* must not inherit gap from .parent .grid */ .grid { display: -ms-grid; display: grid; -ms-grid-rows: 100px 100px 100px; -ms-grid-columns: 1fr 1fr; grid-template: "a b" 100px "c d" 100px "e f" 100px / 1fr 1fr; } .grid-2 { display: -ms-grid; display: grid; grid-gap: 50px; -ms-grid-rows: 100px 50px 100px 50px 100px; -ms-grid-columns: 1fr 50px 1fr; grid-template: "x x" 100px "y y" 100px "z z" 100px / 1fr 1fr; } .grid-2.modifier-b { grid-gap: 22px; grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 22px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 22px 100px 22px 100px; grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2.modifier-b */ .grid-2.modifier-b.foo { grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 22px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 22px 100px 22px 100px; grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2.modifier-b */ .grid-2.modifier-b.bar { grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 22px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 22px 100px 22px 100px; grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2.modifier-b */ .parent .grid-2.modifier-b.bar { grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 22px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 22px 100px 22px 100px; grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2 */ .grid-2.modifier { grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 50px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 50px 100px 50px 100px; grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2 */ .parent .grid-2 { grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 50px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 50px 100px 50px 100px; grid-template-rows: repeat(3, 100px); } @media (min-width: 600px){ .grid { grid-gap: 10px; grid-template-areas: "a b c" "d e f"; -ms-grid-columns: 1fr 10px 1fr 10px 1fr; grid-template-columns: repeat(3, 1fr); -ms-grid-rows: 100px 10px 100px; grid-template-rows: repeat(2, 100px); } } @media (min-width: 700px){ .grid { grid-gap: 20px; grid-template-areas: "a b c" "d e f"; -ms-grid-columns: 1fr 20px 1fr 20px 1fr; grid-template-columns: repeat(3, 1fr); -ms-grid-rows: 100px 20px 100px; grid-template-rows: repeat(2, 100px); } } @media (min-width: 900px){ .grid { grid-template-areas: "a b c d e f"; -ms-grid-columns: 1fr 20px 1fr 20px 1fr 20px 1fr 20px 1fr 20px 1fr; grid-template-columns: repeat(6, 1fr); -ms-grid-rows: 100px; grid-template-rows: 100px; } } @media (max-width: 400px){ .grid { grid-template-areas: "a" "b" "c" "d" "e" "f"; -ms-grid-columns: 1fr; grid-template-columns: 1fr; -ms-grid-rows: (100px)[6]; grid-template-rows: repeat(6, 100px); } } @media (max-width: 1400px){ .grid-2 { -ms-grid-rows: 100px 50px 100px; -ms-grid-columns: 1fr 50px 1fr; grid-template: "x y z" 100px "x y z" 100px / 1fr 1fr; } /* must inherit gap from .grid-2 */ .grid-2.modifier { grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 50px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 50px 100px 50px 100px; grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2 */ .grid-2.modifier.modifier-c { grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 50px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 50px 100px 50px 100px; grid-template-rows: repeat(3, 100px); } /* must inherit gap from .grid-2 */ .parent .grid-2 { grid-template-areas: "x x" "y y" "z z"; -ms-grid-columns: 1fr 50px 1fr; grid-template-columns: repeat(2, 1fr); -ms-grid-rows: 100px 50px 100px 50px 100px; grid-template-rows: repeat(3, 100px); } } @media (max-width: 1000px){ .grid-2 { grid-gap: 20px; grid-template-areas: "x y z" "x y z"; -ms-grid-columns: 1fr 20px 1fr 20px 1fr; grid-template-columns: repeat(3, 1fr); -ms-grid-rows: 100px 20px 100px; grid-template-rows: repeat(2, 100px); } /* must inherit gap from .grid-2 */ .grid-2.modifier { grid-template-areas: "hi hi hi" "hi hi hi"; -ms-grid-columns: 1fr 20px 1fr 20px 1fr; grid-template-columns: repeat(3, 1fr); -ms-grid-rows: 100px 20px 100px; grid-template-rows: repeat(2, 100px); } } @media (max-width: 700px){ .grid-2 { grid-template-areas: "x x y y z z"; -ms-grid-columns: 1fr 20px 1fr 20px 1fr 20px 1fr 20px 1fr 20px 1fr; grid-template-columns: repeat(6, 1fr); -ms-grid-rows: 100px; grid-template-rows: 100px; } /* must NOT inherit gap from .grid-2 */ .grid-2.modifier { grid-gap: 70px; grid-template-areas: "x y z" "x y z"; -ms-grid-columns: 1fr 70px 1fr 70px 1fr; grid-template-columns: repeat(3, 1fr); -ms-grid-rows: 100px 70px 100px; grid-template-rows: repeat(2, 100px); } } @media (max-width: 400px){ .grid-2 { grid-template-areas: "x" "x" "y" "y" "z" "z"; -ms-grid-columns: 1fr; grid-template-columns: 1fr; -ms-grid-rows: 100px 20px 100px 20px 100px 20px 100px 20px 100px 20px 100px; grid-template-rows: repeat(6, 100px); } /* must inherit gap from .grid-2.modifier */ .grid-2.modifier { grid-template-areas: "x y z" "x y z"; -ms-grid-columns: 1fr 70px 1fr 70px 1fr; grid-template-columns: repeat(3, 1fr); -ms-grid-rows: 100px 70px 100px; grid-template-rows: repeat(2, 100px); } } ================================================ FILE: test/cases/grid-media-rules.css ================================================ @media (min-width: 30em) { .wrapper { display: grid; grid-template-areas: "a b"; } } @media (min-width: 60em) { .wrapper { grid-template-areas: "a b"; } } @media (min-width: 30em) { .a { grid-area: a; } } @media (min-width: 30em) { .b { grid-area: b; } } ================================================ FILE: test/cases/grid-media-rules.out.css ================================================ @media (min-width: 30em) { .wrapper { display: -ms-grid; display: grid; grid-template-areas: "a b"; } } @media (min-width: 60em) { .wrapper { grid-template-areas: "a b"; } } @media (min-width: 30em) { .a { grid-area: a; } } @media (min-width: 30em) { .b { grid-area: b; } } @media (min-width: 30em) { .a { -ms-grid-row: 1; -ms-grid-column: 1; } .b { -ms-grid-row: 1; -ms-grid-column: 2; } } @media (min-width: 60em) { .a { -ms-grid-row: 1; -ms-grid-column: 1; } .b { -ms-grid-row: 1; -ms-grid-column: 2; } } ================================================ FILE: test/cases/grid-options.autoplace.out.css ================================================ /*****************\ AREAS ALGORITHM \*****************/ .grid-template-areas { display: -ms-grid; display: grid; grid-template-areas: "a-conflict a-conflict" "b-conflict b-conflict"; } .grid-template-areas.conflict { display: -ms-grid; display: grid; grid-template-areas: "a-conflict" "b-conflict"; } .a { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: a-conflict; } .grid-template-areas.conflict > .a { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .b { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: b-conflict; } .grid-template-areas.conflict > .b { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 1; } .grid-template { -ms-grid-rows: 30px 1fr 30px; -ms-grid-columns: 120px (250px 10px)[4]; grid-template: [header-left] "head head" 30px [header-right] [main-left] "nav main" 1fr [main-right] [footer-left] "nav foot" 30px [footer-right] / 120px repeat(4, 250px 10px); } @media (min-width: 1000px) { .grid-template { -ms-grid-rows: 30px auto 1fr 30px; -ms-grid-columns: 1fr; grid-template: [header-left] "head" 30px [header-right] [nav-left] "nav" auto [nav-right] [main-left] "main" 1fr [main-right] [footer-left] "foot" 30px [footer-right] / 1fr; } } .grid-template.conflict { -ms-grid-rows: auto 20px auto; -ms-grid-columns: 1fr 1fr; grid-template: "head head" "nav main" 20px "nav foot" / 1fr 1fr; } .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: head; } .grid-template.conflict > .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } .main { -ms-grid-row: 2; -ms-grid-column: 2; grid-area: main; } .grid-template.conflict > .main { -ms-grid-row: 2; -ms-grid-column: 2; } .nav { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 1; grid-area: nav; } .grid-template.conflict > .nav { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 1; } .foot { -ms-grid-row: 3; -ms-grid-column: 2; grid-area: foot; } .grid-template.conflict > .foot { -ms-grid-row: 3; -ms-grid-column: 2; } @media (min-width: 1000px) { .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .main { -ms-grid-row: 3; -ms-grid-column: 1; } .nav { -ms-grid-row: 2; -ms-grid-row-span: 1; -ms-grid-column: 1; } .foot { -ms-grid-row: 4; -ms-grid-column: 1; } } /*********************\ AUTOPLACE ALGORITHM \*********************/ .grid-autoplace { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 30px 1fr 30px 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto; grid-template-rows: auto; grid-gap: 30px; } .grid-autoplace > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .grid-autoplace > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 3; } .grid-autoplace > *:nth-child(3) { -ms-grid-row: 1; -ms-grid-column: 5; } ================================================ FILE: test/cases/grid-options.css ================================================ /*****************\ AREAS ALGORITHM \*****************/ .grid-template-areas { display: grid; grid-template-areas: "a-conflict a-conflict" "b-conflict b-conflict"; } .grid-template-areas.conflict { display: grid; grid-template-areas: "a-conflict" "b-conflict"; } .a { grid-area: a-conflict; } .b { grid-area: b-conflict; } .grid-template { grid-template: [header-left] "head head" 30px [header-right] [main-left] "nav main" 1fr [main-right] [footer-left] "nav foot" 30px [footer-right] / 120px repeat(4, 250px 10px); } @media (min-width: 1000px) { .grid-template { grid-template: [header-left] "head" 30px [header-right] [nav-left] "nav" auto [nav-right] [main-left] "main" 1fr [main-right] [footer-left] "foot" 30px [footer-right] / 1fr; } } .grid-template.conflict { grid-template: "head head" "nav main" 20px "nav foot" / 1fr 1fr; } .head { grid-area: head; } .main { grid-area: main; } .nav { grid-area: nav; } .foot { grid-area: foot; } /*********************\ AUTOPLACE ALGORITHM \*********************/ .grid-autoplace { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; grid-gap: 30px; } ================================================ FILE: test/cases/grid-options.disabled.out.css ================================================ /*****************\ AREAS ALGORITHM \*****************/ .grid-template-areas { display: grid; grid-template-areas: "a-conflict a-conflict" "b-conflict b-conflict"; } .grid-template-areas.conflict { display: grid; grid-template-areas: "a-conflict" "b-conflict"; } .a { grid-area: a-conflict; } .b { grid-area: b-conflict; } .grid-template { grid-template: [header-left] "head head" 30px [header-right] [main-left] "nav main" 1fr [main-right] [footer-left] "nav foot" 30px [footer-right] / 120px repeat(4, 250px 10px); } @media (min-width: 1000px) { .grid-template { grid-template: [header-left] "head" 30px [header-right] [nav-left] "nav" auto [nav-right] [main-left] "main" 1fr [main-right] [footer-left] "foot" 30px [footer-right] / 1fr; } } .grid-template.conflict { grid-template: "head head" "nav main" 20px "nav foot" / 1fr 1fr; } .head { grid-area: head; } .main { grid-area: main; } .nav { grid-area: nav; } .foot { grid-area: foot; } /*********************\ AUTOPLACE ALGORITHM \*********************/ .grid-autoplace { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; grid-gap: 30px; } ================================================ FILE: test/cases/grid-options.no-autoplace.out.css ================================================ /*****************\ AREAS ALGORITHM \*****************/ .grid-template-areas { display: -ms-grid; display: grid; grid-template-areas: "a-conflict a-conflict" "b-conflict b-conflict"; } .grid-template-areas.conflict { display: -ms-grid; display: grid; grid-template-areas: "a-conflict" "b-conflict"; } .a { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: a-conflict; } .grid-template-areas.conflict > .a { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .b { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: b-conflict; } .grid-template-areas.conflict > .b { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 1; } .grid-template { -ms-grid-rows: 30px 1fr 30px; -ms-grid-columns: 120px (250px 10px)[4]; grid-template: [header-left] "head head" 30px [header-right] [main-left] "nav main" 1fr [main-right] [footer-left] "nav foot" 30px [footer-right] / 120px repeat(4, 250px 10px); } @media (min-width: 1000px) { .grid-template { -ms-grid-rows: 30px auto 1fr 30px; -ms-grid-columns: 1fr; grid-template: [header-left] "head" 30px [header-right] [nav-left] "nav" auto [nav-right] [main-left] "main" 1fr [main-right] [footer-left] "foot" 30px [footer-right] / 1fr; } } .grid-template.conflict { -ms-grid-rows: auto 20px auto; -ms-grid-columns: 1fr 1fr; grid-template: "head head" "nav main" 20px "nav foot" / 1fr 1fr; } .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: head; } .grid-template.conflict > .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } .main { -ms-grid-row: 2; -ms-grid-column: 2; grid-area: main; } .grid-template.conflict > .main { -ms-grid-row: 2; -ms-grid-column: 2; } .nav { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 1; grid-area: nav; } .grid-template.conflict > .nav { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 1; } .foot { -ms-grid-row: 3; -ms-grid-column: 2; grid-area: foot; } .grid-template.conflict > .foot { -ms-grid-row: 3; -ms-grid-column: 2; } @media (min-width: 1000px) { .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .main { -ms-grid-row: 3; -ms-grid-column: 1; } .nav { -ms-grid-row: 2; -ms-grid-row-span: 1; -ms-grid-column: 1; } .foot { -ms-grid-row: 4; -ms-grid-column: 1; } } /*********************\ AUTOPLACE ALGORITHM \*********************/ .grid-autoplace { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto; grid-template-rows: auto; grid-gap: 30px; } ================================================ FILE: test/cases/grid-status.css ================================================ /* autoprefixer grid: on */ /* autoprefixer grid: off */ .grid-disabled { /* autoprefixer: off */ display: grid; grid-auto-flow: column; } .grid { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 100px 100px; /* autoprefixer: ignore next */ grid-auto-flow: column; } .grid-status-off { /* autoprefixer grid: off */ /* autoprefixer grid: off */ display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 100px 100px; } .grid-2 { display: grid; } @supports (grid-auto-rows: 100px) { .disabled { display: grid; grid-auto-rows: 100px; } } .grid-autoplace-enabled { /* autoprefixer grid: autoplace */ display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; grid-gap: 30px; } .grid-autoplace-disabled { /* autoprefixer grid: off */ /* autoprefixer grid: autoplace */ grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; grid-gap: 30px; } .grid-autoplace-disabled-2 { /* autoprefixer grid: no-autoplace */ grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; } ================================================ FILE: test/cases/grid-status.out.css ================================================ /* autoprefixer grid: on */ /* autoprefixer grid: off */ .grid-disabled { /* autoprefixer: off */ display: grid; grid-auto-flow: column; } .grid { display: -ms-grid; display: grid; -ms-grid-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: 100px 100px; grid-template-rows: 100px 100px; /* autoprefixer: ignore next */ grid-auto-flow: column; } .grid-status-off { /* autoprefixer grid: off */ /* autoprefixer grid: off */ display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 100px 100px; } .grid-2 { display: -ms-grid; display: grid; } @supports (grid-auto-rows: 100px) { .disabled { display: grid; grid-auto-rows: 100px; } } .grid-autoplace-enabled { /* autoprefixer grid: autoplace */ display: -ms-grid; display: grid; -ms-grid-columns: 1fr 30px 1fr 30px 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto; grid-template-rows: auto; grid-gap: 30px; } .grid-autoplace-enabled > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .grid-autoplace-enabled > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 3; } .grid-autoplace-enabled > *:nth-child(3) { -ms-grid-row: 1; -ms-grid-column: 5; } .grid-autoplace-disabled { /* autoprefixer grid: off */ /* autoprefixer grid: autoplace */ grid-template-columns: 1fr 1fr 1fr; grid-template-rows: auto; grid-gap: 30px; } .grid-autoplace-disabled-2 { /* autoprefixer grid: no-autoplace */ -ms-grid-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; -ms-grid-rows: auto; grid-template-rows: auto; } ================================================ FILE: test/cases/grid-template-areas.css ================================================ .a { grid-template-areas: "head head" "nav main" "nav foot" "another"; } @media (max-width: 1000px) { .a { grid-template-areas: "head main"; } } .b { grid-area: head; } .c { grid-area: main; } .d { grid-area: nav; } .e { grid-area: foot; } /****************\ CONFLICT TESTS \****************/ .grid { display: grid; grid-template-areas: "a-conflict a-conflict" "b-conflict b-conflict"; } .grid.conflict { display: grid; grid-template-areas: "a-conflict" "b-conflict"; } .a { grid-area: a-conflict; } .b { grid-area: b-conflict; } /****************\ MQ CONFLICT TEST \****************/ .grid-mq { display: grid; grid-template-areas: "a-mq b-mq"; } @media (max-width: 600px) { .grid-mq { grid-template-areas: "a-mq" "b-mq"; } } .grid.conflict { display: grid; grid-template-areas: "a-mq" "b-mq"; } @media (min-width: 601px) { .grid.conflict { grid-template-areas: "a-mq b-mq"; } } .a { grid-area: a-mq; } .b { grid-area: b-mq; } /*************************\ OVERRIDE CONFLICT TESTS \*************************/ .grid-overide { display: grid; grid-template-areas: "overide"; } .grid-overide > .cell { grid-area: overide; } .grid-overide .cell { grid-area: overide; } .grand-parent .mother { display: grid; grid-template-areas: "child"; } .grand-parent .father { display: grid; grid-template-areas: "child child"; } .grand-parent .father-second { display: grid; grid-template-areas: "child-unique"; } @media (min-width: 600px) { .grand-parent .father-second { grid-template-areas: "child-unique child-unique"; } } .grand-parent .mother .cell { grid-area: child; } .grand-parent .mother > .cell { grid-area: child; } .grand-parent .father .cell { grid-area: child; } .grand-parent .father > .cell { grid-area: child; } /* emit warning */ .uncle .cell { grid-area: child; } /* emit warning */ .uncle > .cell { grid-area: child; } /* emit warning */ .grand-parent .uncle-second .cell { grid-area: child; } /* emit warning */ .grand-parent .uncle-second > .cell { grid-area: child; } /* emit warning */ .grand-parent .father.uncle .cell { grid-area: child; } /* emit warning */ .grand-parent.uncle .father > .cell { grid-area: child; } /* must not emit warning */ .uncle .cell { grid-area: child-unique; } /* must not emit warning */ .uncle > .cell { grid-area: child-unique; } /*************************\ MULTI SELECTOR TEST \*************************/ .grand-parent .uncle, .grand-parent .aunt { display: grid; grid-template-areas: "cousin"; } .grand-parent .uncle .cousin { grid-area: cousin; } .grand-parent .uncle > .cousin { grid-area: cousin; } .grand-parent .aunt .cousin { grid-area: cousin; } .grand-parent .aunt > .cousin { grid-area: cousin; } /*************************\ CONTROL COMMENTS TEST \*************************/ /* must not add prefixes */ .uncle .cell { /* autoprefixer: off */ grid-area: child; } /* must not add prefixes */ .uncle .cell { /* autoprefixer: ignore next */ grid-area: child; } .grid-example { display: grid; } @media (max-width: 767.98px) { .grid-example { grid-template-areas: 'first first' 'second third'; } } .grid-area-first { grid-area: first; } .grid-area-second { grid-area: second; } .grid-area-third { grid-area: third; } ================================================ FILE: test/cases/grid-template-areas.out.css ================================================ .a { grid-template-areas: "head head" "nav main" "nav foot" "another"; } @media (max-width: 1000px) { .a { grid-template-areas: "head main"; } } .b { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: head; } .c { -ms-grid-row: 2; -ms-grid-column: 2; grid-area: main; } .d { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 1; grid-area: nav; } .e { -ms-grid-row: 3; -ms-grid-column: 2; grid-area: foot; } @media (max-width: 1000px) { .b { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .c { -ms-grid-row: 1; -ms-grid-column: 2; } } /****************\ CONFLICT TESTS \****************/ .grid { display: -ms-grid; display: grid; grid-template-areas: "a-conflict a-conflict" "b-conflict b-conflict"; } .grid.conflict { display: -ms-grid; display: grid; grid-template-areas: "a-conflict" "b-conflict"; } .a { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: a-conflict; } .grid.conflict > .a { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .b { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: b-conflict; } .grid.conflict > .b { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 1; } /****************\ MQ CONFLICT TEST \****************/ .grid-mq { display: -ms-grid; display: grid; grid-template-areas: "a-mq b-mq"; } @media (max-width: 600px) { .grid-mq { grid-template-areas: "a-mq" "b-mq"; } } .grid.conflict { display: -ms-grid; display: grid; grid-template-areas: "a-mq" "b-mq"; } @media (min-width: 601px) { .grid.conflict { grid-template-areas: "a-mq b-mq"; } } .a { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: a-mq; } .grid.conflict > .a { -ms-grid-row: 1; -ms-grid-column: 1; } .b { -ms-grid-row: 1; -ms-grid-column: 2; grid-area: b-mq; } .grid.conflict > .b { -ms-grid-row: 2; -ms-grid-column: 1; } @media (max-width: 600px) { .a { -ms-grid-row: 1; -ms-grid-column: 1; } .b { -ms-grid-row: 2; -ms-grid-column: 1; } } @media (min-width: 601px) { .grid.conflict > .a { -ms-grid-row: 1; -ms-grid-column: 1; } .grid.conflict > .b { -ms-grid-row: 1; -ms-grid-column: 2; } } /*************************\ OVERRIDE CONFLICT TESTS \*************************/ .grid-overide { display: -ms-grid; display: grid; grid-template-areas: "overide"; } .grid-overide > .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: overide; } .grid-overide .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: overide; } .grand-parent .mother { display: -ms-grid; display: grid; grid-template-areas: "child"; } .grand-parent .father { display: -ms-grid; display: grid; grid-template-areas: "child child"; } .grand-parent .father-second { display: -ms-grid; display: grid; grid-template-areas: "child-unique"; } @media (min-width: 600px) { .grand-parent .father-second { grid-template-areas: "child-unique child-unique"; } } .grand-parent .mother .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child; } .grand-parent .mother > .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child; } .grand-parent .father .cell { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: child; } .grand-parent .father > .cell { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: child; } /* emit warning */ .uncle .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child; } /* emit warning */ .uncle > .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child; } /* emit warning */ .grand-parent .uncle-second .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child; } /* emit warning */ .grand-parent .uncle-second > .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child; } /* emit warning */ .grand-parent .father.uncle .cell { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: child; } /* emit warning */ .grand-parent.uncle .father > .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child; } /* must not emit warning */ .uncle .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child-unique; } /* must not emit warning */ .uncle > .cell { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: child-unique; } @media (min-width: 600px) { .uncle .cell { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } .uncle > .cell { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } } /*************************\ MULTI SELECTOR TEST \*************************/ .grand-parent .uncle, .grand-parent .aunt { display: -ms-grid; display: grid; grid-template-areas: "cousin"; } .grand-parent .uncle .cousin { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: cousin; } .grand-parent .uncle > .cousin { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: cousin; } .grand-parent .aunt .cousin { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: cousin; } .grand-parent .aunt > .cousin { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: cousin; } /*************************\ CONTROL COMMENTS TEST \*************************/ /* must not add prefixes */ .uncle .cell { /* autoprefixer: off */ grid-area: child; } /* must not add prefixes */ .uncle .cell { /* autoprefixer: ignore next */ grid-area: child; } .grid-example { display: -ms-grid; display: grid; } @media (max-width: 767.98px) { .grid-example { grid-template-areas: 'first first' 'second third'; } } .grid-area-first { grid-area: first; } .grid-area-second { grid-area: second; } .grid-area-third { grid-area: third; } @media (max-width: 767.98px) { .grid-area-first { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } .grid-area-second { -ms-grid-row: 2; -ms-grid-column: 1; } .grid-area-third { -ms-grid-row: 2; -ms-grid-column: 2; } } ================================================ FILE: test/cases/grid-template.css ================================================ .a { grid-template: 10px repeat(4, 250px 10px) 20px / auto 1fr; } .b { grid-template: [header-left] "head head" 30px [header-right] [main-left] "nav main" 1fr [main-right] [footer-left] "nav foot" 30px [footer-right] / 120px repeat(4, 250px 10px); } @media (min-width: 1000px) { .b { grid-template: [header-left] "head" 30px [header-right] [nav-left] "nav" auto [nav-right] [main-left] "main" 1fr [main-right] [footer-left] "foot" 30px [footer-right] / 1fr; } } .c { grid-template: "hd hd hd hd hd hd hd hd hd" minmax(100px, auto) "sd sd sd mn mn mn mn mn mn" minmax(100px, auto) "ft ft ft ft ft ft ft ft ft" minmax(100px, auto) / 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; } .d { grid-template: 10px / 200px; } .e { grid-template: 10px; } .f { grid-template: "head head" "nav main" 20px "nav foot" / 1fr 1fr; } .head { grid-area: head; } .nav { grid-area: nav; } .main { grid-area: main; } .foot { grid-area: foot; } .hd { grid-area: hd; } .sd { grid-area: sd; } .mn { grid-area: mn; } .ft { grid-area: ft; } .grid { display: grid; grid-gap: 10px; grid-template: "a b" 100px "c d" 100px "e f" 100px / 1fr 1fr; } @media (min-width: 600px){ .grid { grid-gap: 10px; grid-template: "a b c" 100px "d e f" 100px / 1fr 1fr 1fr; } } .cell-A { grid-area: a; } .cell-B { grid-area: b; } .cell-C { grid-area: c; } .cell-D { grid-area: d; } .cell-E { grid-area: e; } .cell-F { grid-area: f; } .g { display: grid; grid-gap: 10px; grid-template: "g g" 100px "g g" 100px "h h" 100px / 1fr 1fr; } /* Should trigger a warning */ .g-conflict { display: grid; grid-gap: 10px; grid-template: "g g" 100px "g g" 100px "h h" 100px / 1fr 1fr; } @media (min-width: 600px) { /* This should *not* trigger a warning */ .g { display: grid; grid-gap: 10px; grid-template: "g h" 100px / 1fr 1fr; } /* This *should* trigger a warning */ .g-conflict-2 { display: grid; grid-gap: 10px; grid-template: "g h" 100px / 1fr 1fr; } } /* comma list tests */ /* None of these should throw any warnings (unless specified) */ .i, .j { display: grid; grid-gap: 10px; grid-template: "i j" 100px / 1fr 1fr; } @media (max-width: 600px) { .i { grid-template: "i" 100px "j" 100px / 1fr; } .j { grid-template: "i" 100px "j" 100px / 1fr; } /* This one should throw a warning */ .k { grid-template: "i" 100px "j" 100px / 1fr; } } @media (min-width: 900px) { .i, .j { display: grid; grid-gap: 10px; grid-template: "i" 100px "j" 100px / 1fr; } } /* media query test */ @media (min-width: 601px) { .l { display: grid; grid-gap: 10px; grid-template: "l m" 100px / 1fr 1fr; } } @media (max-width: 600px) { .l { display: grid; grid-gap: 10px; grid-template: "l m" 100px / 1fr 1fr; } /* this should display a warning */ .m { display: grid; grid-gap: 10px; grid-template: "m z" 100px / 1fr 1fr; } /* this should display a warning */ .z, .second-z { display: grid; grid-gap: 10px; grid-template: ". z" 100px / 1fr 1fr; } } .k-1 { grid-area: g; } .k-2 { grid-area: h; } .k-3 { grid-area: i; } .k-4 { grid-area: j; } .k-5 { grid-area: l; } .k-6, .k-7 { grid-area: m; } .k-8, .k-9 { grid-area: z; } ================================================ FILE: test/cases/grid-template.out.css ================================================ .a { -ms-grid-rows: 10px (250px 10px)[4] 20px; -ms-grid-columns: auto 1fr; grid-template: 10px repeat(4, 250px 10px) 20px / auto 1fr; } .b { -ms-grid-rows: 30px 1fr 30px; -ms-grid-columns: 120px (250px 10px)[4]; grid-template: [header-left] "head head" 30px [header-right] [main-left] "nav main" 1fr [main-right] [footer-left] "nav foot" 30px [footer-right] / 120px repeat(4, 250px 10px); } @media (min-width: 1000px) { .b { -ms-grid-rows: 30px auto 1fr 30px; -ms-grid-columns: 1fr; grid-template: [header-left] "head" 30px [header-right] [nav-left] "nav" auto [nav-right] [main-left] "main" 1fr [main-right] [footer-left] "foot" 30px [footer-right] / 1fr; } } .c { -ms-grid-rows: minmax(100px, auto) minmax(100px, auto) minmax(100px, auto); -ms-grid-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; grid-template: "hd hd hd hd hd hd hd hd hd" minmax(100px, auto) "sd sd sd mn mn mn mn mn mn" minmax(100px, auto) "ft ft ft ft ft ft ft ft ft" minmax(100px, auto) / 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; } .d { -ms-grid-rows: 10px; -ms-grid-columns: 200px; grid-template: 10px / 200px; } .e { grid-template: 10px; } .f { -ms-grid-rows: auto 20px auto; -ms-grid-columns: 1fr 1fr; grid-template: "head head" "nav main" 20px "nav foot" / 1fr 1fr; } .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: head; } .f > .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; } .nav { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 1; grid-area: nav; } .f > .nav { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 1; } .main { -ms-grid-row: 2; -ms-grid-column: 2; grid-area: main; } .f > .main { -ms-grid-row: 2; -ms-grid-column: 2; } .foot { -ms-grid-row: 3; -ms-grid-column: 2; grid-area: foot; } .f > .foot { -ms-grid-row: 3; -ms-grid-column: 2; } @media (min-width: 1000px) { .head { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .nav { -ms-grid-row: 2; -ms-grid-row-span: 1; -ms-grid-column: 1; } .main { -ms-grid-row: 3; -ms-grid-column: 1; } .foot { -ms-grid-row: 4; -ms-grid-column: 1; } } .hd { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 9; grid-area: hd; } .sd { -ms-grid-row: 2; -ms-grid-column: 1; -ms-grid-column-span: 3; grid-area: sd; } .mn { -ms-grid-row: 2; -ms-grid-column: 4; -ms-grid-column-span: 6; grid-area: mn; } .ft { -ms-grid-row: 3; -ms-grid-column: 1; -ms-grid-column-span: 9; grid-area: ft; } .grid { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px 10px 100px 10px 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "a b" 100px "c d" 100px "e f" 100px / 1fr 1fr; } @media (min-width: 600px){ .grid { grid-gap: 10px; -ms-grid-rows: 100px 10px 100px; -ms-grid-columns: 1fr 10px 1fr 10px 1fr; grid-template: "a b c" 100px "d e f" 100px / 1fr 1fr 1fr; } } .cell-A { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: a; } .cell-B { -ms-grid-row: 1; -ms-grid-column: 3; grid-area: b; } .cell-C { -ms-grid-row: 3; -ms-grid-column: 1; grid-area: c; } .cell-D { -ms-grid-row: 3; -ms-grid-column: 3; grid-area: d; } .cell-E { -ms-grid-row: 5; -ms-grid-column: 1; grid-area: e; } .cell-F { -ms-grid-row: 5; -ms-grid-column: 3; grid-area: f; } @media (min-width: 600px) { .cell-A { -ms-grid-row: 1; -ms-grid-column: 1; } .cell-B { -ms-grid-row: 1; -ms-grid-column: 3; } .cell-C { -ms-grid-row: 1; -ms-grid-column: 5; } .cell-D { -ms-grid-row: 3; -ms-grid-column: 1; } .cell-E { -ms-grid-row: 3; -ms-grid-column: 3; } .cell-F { -ms-grid-row: 3; -ms-grid-column: 5; } } .g { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px 10px 100px 10px 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "g g" 100px "g g" 100px "h h" 100px / 1fr 1fr; } /* Should trigger a warning */ .g-conflict { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px 10px 100px 10px 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "g g" 100px "g g" 100px "h h" 100px / 1fr 1fr; } @media (min-width: 600px) { /* This should *not* trigger a warning */ .g { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "g h" 100px / 1fr 1fr; } /* This *should* trigger a warning */ .g-conflict-2 { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "g h" 100px / 1fr 1fr; } } /* comma list tests */ /* None of these should throw any warnings (unless specified) */ .i, .j { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "i j" 100px / 1fr 1fr; } @media (max-width: 600px) { .i { -ms-grid-rows: 100px 10px 100px; -ms-grid-columns: 1fr; grid-template: "i" 100px "j" 100px / 1fr; } .j { -ms-grid-rows: 100px 10px 100px; -ms-grid-columns: 1fr; grid-template: "i" 100px "j" 100px / 1fr; } /* This one should throw a warning */ .k { -ms-grid-rows: 100px 100px; -ms-grid-columns: 1fr; grid-template: "i" 100px "j" 100px / 1fr; } } @media (min-width: 900px) { .i, .j { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px 10px 100px; -ms-grid-columns: 1fr; grid-template: "i" 100px "j" 100px / 1fr; } } /* media query test */ @media (min-width: 601px) { .l { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "l m" 100px / 1fr 1fr; } } @media (max-width: 600px) { .l { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "l m" 100px / 1fr 1fr; } /* this should display a warning */ .m { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: "m z" 100px / 1fr 1fr; } /* this should display a warning */ .z, .second-z { display: -ms-grid; display: grid; grid-gap: 10px; -ms-grid-rows: 100px; -ms-grid-columns: 1fr 10px 1fr; grid-template: ". z" 100px / 1fr 1fr; } } .k-1 { -ms-grid-row: 1; -ms-grid-row-span: 3; -ms-grid-column: 1; -ms-grid-column-span: 3; grid-area: g; } .g-conflict > .k-1 { -ms-grid-row: 1; -ms-grid-row-span: 3; -ms-grid-column: 1; -ms-grid-column-span: 3; } .k-2 { -ms-grid-row: 5; -ms-grid-column: 1; -ms-grid-column-span: 3; grid-area: h; } .g-conflict > .k-2 { -ms-grid-row: 5; -ms-grid-column: 1; -ms-grid-column-span: 3; } @media (min-width: 600px) { .k-1 { -ms-grid-row: 1; -ms-grid-row-span: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .g-conflict-2 > .k-1 { -ms-grid-row: 1; -ms-grid-row-span: 1; -ms-grid-column: 1; -ms-grid-column-span: 1; } .k-2 { -ms-grid-row: 1; -ms-grid-column: 3; -ms-grid-column-span: 1; } .g-conflict-2 > .k-2 { -ms-grid-row: 1; -ms-grid-column: 3; -ms-grid-column-span: 1; } } .k-3 { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: i; } .k-4 { -ms-grid-row: 1; -ms-grid-column: 3; grid-area: j; } @media (max-width: 600px) { .k-3 { -ms-grid-row: 1; -ms-grid-column: 1; } .k-3 { -ms-grid-row: 1; -ms-grid-column: 1; } .k > .k-3 { -ms-grid-row: 1; -ms-grid-column: 1; } .k-4 { -ms-grid-row: 3; -ms-grid-column: 1; } .k-4 { -ms-grid-row: 3; -ms-grid-column: 1; } .k > .k-4 { -ms-grid-row: 2; -ms-grid-column: 1; } } @media (min-width: 900px) { .k-3 { -ms-grid-row: 1; -ms-grid-column: 1; } .k-4 { -ms-grid-row: 3; -ms-grid-column: 1; } } .k-5 { grid-area: l; } .k-6, .k-7 { grid-area: m; } .k-8, .k-9 { grid-area: z; } @media (min-width: 601px) { .k-5 { -ms-grid-row: 1; -ms-grid-column: 1; } .k-6, .k-7 { -ms-grid-row: 1; -ms-grid-column: 3; } } @media (max-width: 600px) { .k-5 { -ms-grid-row: 1; -ms-grid-column: 1; } .k-6, .k-7 { -ms-grid-row: 1; -ms-grid-column: 3; } .m > .k-6, .m > .k-7 { -ms-grid-row: 1; -ms-grid-column: 1; } .k-8, .k-9 { -ms-grid-row: 1; -ms-grid-column: 3; } .z > .k-8, .second-z > .k-8, .z > .k-9, .second-z > .k-9 { -ms-grid-row: 1; -ms-grid-column: 3; } } ================================================ FILE: test/cases/grid.css ================================================ .a { display: grid; grid-template-columns: auto 1fr; justify-self: stretch; align-self: stretch; grid-column-start: 1; grid-column-end: span 3; } .b { display: inline-grid; grid-template-rows: 10px repeat(4, 250px 10px) 20px; justify-self: stretch; align-self: stretch; grid-row-start: 1; grid-row-end: span 3; } .c { grid-column: 1 / span 3; } .d { grid-column: 1 / 3; grid-row: span 2 / 5; } .e { grid-column-start: 1; grid-column-end: 3; grid-row-start: 1; grid-row-end: 4; } .warn { grid-column-end: 3; grid: subgrid; grid-gap: 1rem; grid-template-areas: "head head" "nav main" "foot ...."; } .no-warn { /* autoprefixer grid: off */ grid-column-end: 3; grid: subgrid; grid-gap: 1rem; grid-template-areas: "head head" "nav main" "foot ...."; } .warn-gap-rows { grid-gap: 1rem; grid-template-rows: 1fr 1fr; grid-template-areas: "a a" "a a"; } .warn-gap-rows { grid-column-gap: 1rem; grid-template-rows: 1fr 1fr; grid-template-areas: "b b" "b b"; } .warn-gap-columns { grid-gap: 1rem; grid-template-columns: 1fr 1fr; grid-template-areas: "c c" "c c"; } .warn-gap-columns { grid-row-gap: 1rem; grid-template-columns: 1fr 1fr; grid-template-areas: "d d" "d d"; } .unknown { justify-self: start; align-self: start; } .flex { justify-self: flex-end; align-self: flex-end; } /* should add grid prefixes because a flex container can be a grid item */ .align-justify-grid-flex { display: flex; justify-self: start; align-self: end; } @supports (display: grid) { .foo { display: grid; } } @supports (justify-items: start) { .foo { justify-items: start; } } .warn { grid-column-span: 2; grid-row-span: 2; grid-auto-columns: 100px; grid-auto-rows: 100px; grid-auto-flow: column; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); grid-template-rows: repeat(auto-fit, minmax(400px, 1fr)); } .baz { grid-column: span 3; } .bar { grid-row: span 3; } .warn-display-contents { display: grid; } /* Emit warning if grid enabled */ .warn-display-contents .grid { display: contents; } .warn_ie_align { align-items: center; display: grid; } .warn_ie_justify { justify-items: center; display: grid; } .warn_ie_justify_content { justify-content: center; display: grid; } .warn_place_items { place-items: start end; display: grid; } .place-self-a { place-self: center; } .place-self-b { place-self: start end; } /* must have correct -ms-grid-rows/columns values */ .grid-correct-rows-columns { display: grid; grid-template-columns: 50px repeat(2, 1fr 2fr) 50px; grid-template-rows: repeat(1, auto 100px); grid-gap: 20px; grid-template-areas: ". . . . . ." ". . . . . ."; } .warn-auto-flow { grid-auto-flow: column; grid-template-columns: 1fr 1fr; grid-template-areas: ". ." ". ."; } .center-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); grid-template-areas: ". z z ." ". z z ."; } .center-item { grid-area: z; align-self: center; justify-self: center; } .named { grid-template-rows: 1fr; grid-template-columns: [content] 1fr; } /** https://github.com/postcss/autoprefixer/issues/1446 */ .issue-1446 { grid-row-start: span 3; grid-row-end: 5; } ================================================ FILE: test/cases/grid.disabled.css ================================================ .a { display: grid; grid-template-columns: auto 1fr; justify-self: stretch; align-self: stretch; grid-column-start: 1; grid-column-end: span 3; } .b { display: inline-grid; grid-template-rows: 10px repeat(4, 250px 10px) 20px; justify-self: stretch; align-self: stretch; grid-row-start: 1; grid-row-end: span 3; } .c { grid-column: 1 / span 3; } .d { grid-column: 1 / 3; grid-row: span 2 / 5; } .e { grid-column-start: 1; grid-column-end: 3; grid-row-start: 1; grid-row-end: 4; } .warn { grid-column-end: 3; grid: subgrid; grid-gap: 1rem; grid-template-areas: "head head" "nav main" "foot ...."; } .no-warn { /* autoprefixer grid: off */ grid-column-end: 3; grid: subgrid; grid-gap: 1rem; grid-template-areas: "head head" "nav main" "foot ...."; } .warn-gap-rows { grid-gap: 1rem; grid-template-rows: 1fr 1fr; grid-template-areas: "a a" "a a"; } .warn-gap-rows { grid-column-gap: 1rem; grid-template-rows: 1fr 1fr; grid-template-areas: "b b" "b b"; } .warn-gap-columns { grid-gap: 1rem; grid-template-columns: 1fr 1fr; grid-template-areas: "c c" "c c"; } .warn-gap-columns { grid-row-gap: 1rem; grid-template-columns: 1fr 1fr; grid-template-areas: "d d" "d d"; } .unknown { justify-self: start; -ms-flex-item-align: start; align-self: start; } .flex { justify-self: flex-end; -ms-flex-item-align: end; align-self: flex-end; } /* should add grid prefixes because a flex container can be a grid item */ .align-justify-grid-flex { display: -ms-flexbox; display: flex; justify-self: start; -ms-flex-item-align: end; align-self: end; } @supports (display: grid) { .foo { display: grid; } } @supports (justify-items: start) { .foo { justify-items: start; } } .warn { grid-column-span: 2; grid-row-span: 2; grid-auto-columns: 100px; grid-auto-rows: 100px; grid-auto-flow: column; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); grid-template-rows: repeat(auto-fit, minmax(400px, 1fr)); } .baz { grid-column: span 3; } .bar { grid-row: span 3; } .warn-display-contents { display: grid; } /* Emit warning if grid enabled */ .warn-display-contents .grid { display: contents; } .warn_ie_align { -ms-flex-align: center; align-items: center; display: grid; } .warn_ie_justify { justify-items: center; display: grid; } .warn_ie_justify_content { -ms-flex-pack: center; justify-content: center; display: grid; } .warn_place_items { place-items: start end; display: grid; } .place-self-a { place-self: center; } .place-self-b { place-self: start end; } /* must have correct -ms-grid-rows/columns values */ .grid-correct-rows-columns { display: grid; grid-template-columns: 50px repeat(2, 1fr 2fr) 50px; grid-template-rows: repeat(1, auto 100px); grid-gap: 20px; grid-template-areas: ". . . . . ." ". . . . . ."; } .warn-auto-flow { grid-auto-flow: column; grid-template-columns: 1fr 1fr; grid-template-areas: ". ." ". ."; } .center-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); grid-template-areas: ". z z ." ". z z ."; } .center-item { grid-area: z; align-self: center; justify-self: center; } .named { grid-template-rows: 1fr; grid-template-columns: [content] 1fr; } /** https://github.com/postcss/autoprefixer/issues/1446 */ .issue-1446 { grid-row-start: span 3; grid-row-end: 5; } ================================================ FILE: test/cases/grid.out.css ================================================ .a { display: -ms-grid; display: grid; -ms-grid-columns: auto 1fr; grid-template-columns: auto 1fr; -ms-grid-column-align: stretch; justify-self: stretch; -ms-grid-row-align: stretch; align-self: stretch; -ms-grid-column: 1; grid-column-start: 1; -ms-grid-column-span: 3; grid-column-end: span 3; } .b { display: -ms-inline-grid; display: inline-grid; -ms-grid-rows: 10px (250px 10px)[4] 20px; grid-template-rows: 10px repeat(4, 250px 10px) 20px; -ms-grid-column-align: stretch; justify-self: stretch; -ms-grid-row-align: stretch; align-self: stretch; -ms-grid-row: 1; grid-row-start: 1; -ms-grid-row-span: 3; grid-row-end: span 3; } .c { -ms-grid-column: 1; -ms-grid-column-span: 3; grid-column: 1 / span 3; } .d { -ms-grid-column: 1; -ms-grid-column-span: 2; grid-column: 1 / 3; -ms-grid-row: 3; -ms-grid-row-span: 2; grid-row: span 2 / 5; } .e { -ms-grid-column: 1; grid-column-start: 1; -ms-grid-column-span: 2; grid-column-end: 3; -ms-grid-row: 1; grid-row-start: 1; -ms-grid-row-span: 3; grid-row-end: 4; } .warn { -ms-grid-column-span: 3; grid-column-end: 3; grid: subgrid; grid-gap: 1rem; grid-template-areas: "head head" "nav main" "foot ...."; } .no-warn { /* autoprefixer grid: off */ grid-column-end: 3; grid: subgrid; grid-gap: 1rem; grid-template-areas: "head head" "nav main" "foot ...."; } .warn-gap-rows { grid-gap: 1rem; -ms-grid-rows: 1fr 1rem 1fr; grid-template-rows: 1fr 1fr; grid-template-areas: "a a" "a a"; } .warn-gap-rows { grid-column-gap: 1rem; -ms-grid-rows: 1fr 1fr; grid-template-rows: 1fr 1fr; grid-template-areas: "b b" "b b"; } .warn-gap-columns { grid-gap: 1rem; -ms-grid-columns: 1fr 1rem 1fr; grid-template-columns: 1fr 1fr; -ms-grid-rows: auto 1rem auto; grid-template-areas: "c c" "c c"; } .warn-gap-columns { grid-row-gap: 1rem; -ms-grid-columns: 1fr 1fr; grid-template-columns: 1fr 1fr; -ms-grid-rows: auto 1rem auto; grid-template-areas: "d d" "d d"; } .unknown { -ms-grid-column-align: start; justify-self: start; -webkit-align-self: start; -ms-flex-item-align: start; -ms-grid-row-align: start; align-self: start; } .flex { justify-self: flex-end; -webkit-align-self: flex-end; -ms-flex-item-align: end; align-self: flex-end; } /* should add grid prefixes because a flex container can be a grid item */ .align-justify-grid-flex { display: -webkit-flex; display: -ms-flexbox; display: flex; -ms-grid-column-align: start; justify-self: start; -webkit-align-self: end; -ms-flex-item-align: end; -ms-grid-row-align: end; align-self: end; } @supports ((display: -ms-grid) or (display: grid)) { .foo { display: -ms-grid; display: grid; } } @supports (justify-items: start) { .foo { justify-items: start; } } .warn { grid-column-span: 2; grid-row-span: 2; grid-auto-columns: 100px; grid-auto-rows: 100px; grid-auto-flow: column; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); grid-template-rows: repeat(auto-fit, minmax(400px, 1fr)); } .baz { -ms-grid-column-span: 3; grid-column: span 3; } .bar { -ms-grid-row-span: 3; grid-row: span 3; } .warn-display-contents { display: -ms-grid; display: grid; } /* Emit warning if grid enabled */ .warn-display-contents .grid { display: contents; } .warn_ie_align { -webkit-align-items: center; -ms-flex-align: center; align-items: center; display: -ms-grid; display: grid; } .warn_ie_justify { justify-items: center; display: -ms-grid; display: grid; } .warn_ie_justify_content { -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; display: -ms-grid; display: grid; } .warn_place_items { place-items: start end; display: -ms-grid; display: grid; } .place-self-a { -ms-grid-row-align: center; -ms-grid-column-align: center; place-self: center; } .place-self-b { -ms-grid-row-align: start; -ms-grid-column-align: end; place-self: start end; } /* must have correct -ms-grid-rows/columns values */ .grid-correct-rows-columns { display: -ms-grid; display: grid; -ms-grid-columns: 50px 20px 1fr 20px 2fr 20px 1fr 20px 2fr 20px 50px; grid-template-columns: 50px repeat(2, 1fr 2fr) 50px; -ms-grid-rows: auto 20px 100px; grid-template-rows: repeat(1, auto 100px); grid-gap: 20px; grid-template-areas: ". . . . . ." ". . . . . ."; } .warn-auto-flow { grid-auto-flow: column; -ms-grid-columns: 1fr 1fr; grid-template-columns: 1fr 1fr; grid-template-areas: ". ." ". ."; } .center-wrapper { display: -ms-grid; display: grid; -ms-grid-columns: (1fr)[4]; grid-template-columns: repeat(4, 1fr); grid-template-areas: ". z z ." ". z z ."; } .center-item { -ms-grid-row: 1; -ms-grid-row-span: 2; -ms-grid-column: 2; -ms-grid-column-span: 2; grid-area: z; -ms-grid-row-align: center; align-self: center; -ms-grid-column-align: center; justify-self: center; } .named { -ms-grid-rows: 1fr; grid-template-rows: 1fr; -ms-grid-columns: [content] 1fr; grid-template-columns: [content] 1fr; } .named > *:nth-child(1) { -ms-grid-row: 1; -ms-grid-column: 1; } .named > *:nth-child(2) { -ms-grid-row: 1; -ms-grid-column: 2; } /** https://github.com/postcss/autoprefixer/issues/1446 */ .issue-1446 { grid-row-start: span 3; grid-row-end: 5; } ================================================ FILE: test/cases/grouping-rule.css ================================================ .grid { display: grid; } .a, .b, .c::selection, .d:read-only, .e::placeholder { color: yellow; } ::selection { color: red; } :read-only { color: black; } .f:read-write, .g:read-write { background: #fff; } ================================================ FILE: test/cases/grouping-rule.out.css ================================================ .grid { display: -ms-grid; display: grid; } .c::-moz-selection { color: yellow; } .e::-webkit-input-placeholder { color: yellow; } .e:-moz-placeholder { color: yellow; } .e::-moz-placeholder { color: yellow; } .e:-ms-input-placeholder { color: yellow; } .e::-ms-input-placeholder { color: yellow; } .d:-moz-read-only { color: yellow; } .a, .b, .c::selection, .d:read-only, .e::placeholder { color: yellow; } ::-moz-selection { color: red; } ::selection { color: red; } :-moz-read-only { color: black; } :read-only { color: black; } .f:-moz-read-write, .g:-moz-read-write { background: #fff; } .f:read-write, .g:read-write { background: #fff; } ================================================ FILE: test/cases/ignore-next.css ================================================ .ignore-property { /* autoprefixer: ignore next */ mask: none; background: linear-gradient(to bottom, white, black); } .ignore-property_nospaces-comment { /* autoprefixer:ignore next */ mask: none; background: linear-gradient(to bottom, white, black); } /* autoprefixer: ignore next */ .ignore-rule::placeholder { mask: none; } /* autoprefixer: ignore next */ @media (min-resolution: 2dppx) { .ignore-at-rule::placeholder { mask: none; } } ================================================ FILE: test/cases/ignore-next.out.css ================================================ .ignore-property { /* autoprefixer: ignore next */ mask: none; background: -webkit-linear-gradient(top, white, black); background: -o-linear-gradient(top, white, black); background: linear-gradient(to bottom, white, black); } .ignore-property_nospaces-comment { /* autoprefixer:ignore next */ mask: none; background: -webkit-linear-gradient(top, white, black); background: -o-linear-gradient(top, white, black); background: linear-gradient(to bottom, white, black); } /* autoprefixer: ignore next */ .ignore-rule::placeholder { -webkit-mask: none; mask: none; } /* autoprefixer: ignore next */ @media (min-resolution: 2dppx) { .ignore-at-rule::-webkit-input-placeholder { -webkit-mask: none; mask: none; } .ignore-at-rule::placeholder { -webkit-mask: none; mask: none; } } ================================================ FILE: test/cases/image-rendering.css ================================================ img { image-rendering: crisp-edges; } img.other { image-rendering: pixelated; } img.already { -ms-interpolation-mode: nearest-neighbor; display: block; image-rendering: crisp-edges; image-rendering: pixelated; } ================================================ FILE: test/cases/image-rendering.out.css ================================================ img { image-rendering: crisp-edges; } img.other { -ms-interpolation-mode: nearest-neighbor; image-rendering: -webkit-optimize-contrast; image-rendering: -moz-crisp-edges; image-rendering: -o-pixelated; image-rendering: pixelated; } img.already { -ms-interpolation-mode: nearest-neighbor; display: block; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast; image-rendering: -moz-crisp-edges; image-rendering: -o-pixelated; image-rendering: pixelated; } ================================================ FILE: test/cases/image-set.css ================================================ a { background-image: image-set(url(foo@1x.png) 1x, url(foo@2x.png) 2x); } h1 { background-image: image-set('foo@1x.png' 1x, "foo@2x.png" 2x); } ================================================ FILE: test/cases/image-set.out.css ================================================ a { background-image: -webkit-image-set(url(foo@1x.png) 1x, url(foo@2x.png) 2x); background-image: image-set(url(foo@1x.png) 1x, url(foo@2x.png) 2x); } h1 { background-image: -webkit-image-set(url('foo@1x.png') 1x, url("foo@2x.png") 2x); background-image: image-set('foo@1x.png' 1x, "foo@2x.png" 2x); } ================================================ FILE: test/cases/intrinsic.css ================================================ a { width: stretch; } b { height: max-content; } p { block-size: min-content; min-inline-size: fit-content; } .outdated { width: fill; } .old { width: fill-available; } .ok { width: calc(100% - var(--jqx-circular-progress-bar-fill-size)); } .grid { grid: min-content max-content / fit-content(500px); } .grid-template { grid-template: min-content / fit-content(10px) max-content; grid-template: max-content 1fr max-content max-content / max-content 1fr; } .grid-template-columns { grid-template-columns: minmax(100px, min-content); } .grid-auto-columns { grid-auto-columns: min-content max-content; } .ignore { width: -webkit-fill-available; } ================================================ FILE: test/cases/intrinsic.ff.css ================================================ a { width: -moz-available; width: stretch; } b { height: max-content; } p { block-size: min-content; min-inline-size: -moz-fit-content; min-inline-size: fit-content; } .outdated { width: fill; } .old { width: fill-available; } .ok { width: calc(100% - var(--jqx-circular-progress-bar-fill-size)); } .grid { grid: min-content max-content / fit-content(500px); } .grid-template { grid-template: min-content / fit-content(10px) max-content; grid-template: max-content 1fr max-content max-content / max-content 1fr; } .grid-template-columns { grid-template-columns: minmax(100px, min-content); } .grid-auto-columns { grid-auto-columns: min-content max-content; } .ignore { width: -webkit-fill-available; } ================================================ FILE: test/cases/intrinsic.out.css ================================================ a { width: -webkit-fill-available; width: -moz-available; width: stretch; } b { height: -webkit-max-content; height: -moz-max-content; height: max-content; } p { block-size: -webkit-min-content; block-size: -moz-min-content; block-size: min-content; min-inline-size: -webkit-fit-content; min-inline-size: -moz-fit-content; min-inline-size: fit-content; } .outdated { width: -webkit-fill-available; width: -moz-available; width: fill; } .old { width: -webkit-fill-available; width: -moz-available; width: fill-available; } .ok { width: -webkit-calc(100% - var(--jqx-circular-progress-bar-fill-size)); width: calc(100% - var(--jqx-circular-progress-bar-fill-size)); } .grid { grid: -webkit-min-content -webkit-max-content / fit-content(500px); grid: min-content max-content / fit-content(500px); } .grid-template { grid-template: -webkit-min-content / fit-content(10px) -webkit-max-content; grid-template: min-content / fit-content(10px) max-content; grid-template: -webkit-max-content 1fr -webkit-max-content -webkit-max-content / -webkit-max-content 1fr; grid-template: max-content 1fr max-content max-content / max-content 1fr; } .grid-template-columns { grid-template-columns: minmax(100px, -webkit-min-content); grid-template-columns: minmax(100px, min-content); } .grid-auto-columns { grid-auto-columns: -webkit-min-content -webkit-max-content; grid-auto-columns: min-content max-content; } .ignore { width: -webkit-fill-available; } ================================================ FILE: test/cases/keyframes.css ================================================ @keyframes anim { from { top: calc(10% + 10px); transform: rotate(10deg) } 50% { top: 0; display: flex } to { top: calc(10%); transform: rotate(0) } } @media screen { @keyframes inside {} } @keyframes spaces { from { color: black } to { color: white } } ================================================ FILE: test/cases/keyframes.out.css ================================================ @-webkit-keyframes anim { from { top: -webkit-calc(10% + 10px); top: calc(10% + 10px); -webkit-transform: rotate(10deg); transform: rotate(10deg) } 50% { top: 0; display: -webkit-box; display: -webkit-flex; display: flex } to { top: -webkit-calc(10%); top: calc(10%); -webkit-transform: rotate(0); transform: rotate(0) } } @-o-keyframes anim { from { top: calc(10% + 10px); -o-transform: rotate(10deg); transform: rotate(10deg) } 50% { top: 0; display: flex } to { top: calc(10%); -o-transform: rotate(0); transform: rotate(0) } } @keyframes anim { from { top: -webkit-calc(10% + 10px); top: calc(10% + 10px); -webkit-transform: rotate(10deg); -o-transform: rotate(10deg); transform: rotate(10deg) } 50% { top: 0; display: -webkit-box; display: -webkit-flex; display: flex } to { top: -webkit-calc(10%); top: calc(10%); -webkit-transform: rotate(0); -o-transform: rotate(0); transform: rotate(0) } } @media screen { @-webkit-keyframes inside {} @-o-keyframes inside {} @keyframes inside {} } @-webkit-keyframes spaces { from { color: black } to { color: white } } @-o-keyframes spaces { from { color: black } to { color: white } } @keyframes spaces { from { color: black } to { color: white } } ================================================ FILE: test/cases/logical.css ================================================ a { margin-block-start: 1px; margin-inline-start: 1px; padding-inline-end: 1px; } .border { border-block-end: 1px; border-inline-end: 1px; } ================================================ FILE: test/cases/logical.out.css ================================================ a { -webkit-margin-before: 1px; margin-block-start: 1px; -webkit-margin-start: 1px; -moz-margin-start: 1px; margin-inline-start: 1px; -webkit-padding-end: 1px; -moz-padding-end: 1px; padding-inline-end: 1px; } .border { -webkit-border-after: 1px; border-block-end: 1px; -webkit-border-end: 1px; -moz-border-end: 1px; border-inline-end: 1px; } ================================================ FILE: test/cases/mask-border.css ================================================ a { mask-border-source: url(image.png); mask-border-slice: 50% fill; mask-border-width: auto 1 50%; mask-border-outset: 0 1 2; mask-border-repeat: repeat space; mask-border: url(#foo) 1 fill; } ================================================ FILE: test/cases/mask-border.out.css ================================================ a { -webkit-mask-box-image-source: url(image.png); mask-border-source: url(image.png); -webkit-mask-box-image-slice: 50% fill; mask-border-slice: 50% fill; -webkit-mask-box-image-width: auto 1 50%; mask-border-width: auto 1 50%; -webkit-mask-box-image-outset: 0 1 2; mask-border-outset: 0 1 2; -webkit-mask-box-image-repeat: repeat space; mask-border-repeat: repeat space; -webkit-mask-box-image: url(#foo) 1 fill; mask-border: url(#foo) 1 fill; } ================================================ FILE: test/cases/mask-composite.css ================================================ a { mask: url(add.png) add, url(substract.png); } a { mask: url(intersect.png) intersect, url(exclude.png); } a { mask: url(image.png) intersect, url(image.png) add, url(image.png); } a { mask-composite: add; } a { mask-composite:; } a { mask-composite: add, subtract, exclude; } ================================================ FILE: test/cases/mask-composite.out.css ================================================ a { -webkit-mask: url(add.png), url(substract.png); -webkit-mask-composite: source-over; mask: url(add.png) add, url(substract.png); } a { -webkit-mask: url(intersect.png), url(exclude.png); -webkit-mask-composite: source-in, xor; mask: url(intersect.png) intersect, url(exclude.png); } a { -webkit-mask: url(image.png), url(image.png), url(image.png); -webkit-mask-composite: source-in, source-over, xor; mask: url(image.png) intersect, url(image.png) add, url(image.png); } a { -webkit-mask-composite: source-over; mask-composite: add; } a { mask-composite:; } a { -webkit-mask-composite: source-over, source-out, xor; mask-composite: add, subtract, exclude; } ================================================ FILE: test/cases/mistakes.css ================================================ a { -ms-transition: all 1s; transition: all 1s; -ms-border-radius: 5px; border-radius: 5px; background: -ms-linear-gradient(white, black); background: linear-gradient(white, black); } ================================================ FILE: test/cases/mistakes.out.css ================================================ a { transition: all 1s; border-radius: 5px; background: linear-gradient(white, black); } ================================================ FILE: test/cases/multicolumn.css ================================================ .a { break-inside: auto; break-before: auto; break-after: auto; } .b { break-inside: avoid; } .c { break-inside: avoid-column; } .d { break-inside: avoid-page; } .e { break-inside: avoid-region; } .f { break-inside: region; } ================================================ FILE: test/cases/multicolumn.out.css ================================================ .a { -webkit-column-break-inside: auto; break-inside: auto; -webkit-column-break-before: auto; break-before: auto; -webkit-column-break-after: auto; break-after: auto; } .b { -webkit-column-break-inside: avoid; break-inside: avoid; } .c { -webkit-column-break-inside: avoid; break-inside: avoid-column; } .d { break-inside: avoid-page; } .e { break-inside: avoid-region; } .f { break-inside: region; } ================================================ FILE: test/cases/notes.css ================================================ a { display: flex; } ================================================ FILE: test/cases/notes.out.css ================================================ a { display: -webkit-flex; display: flex; } ================================================ FILE: test/cases/overscroll-behavior.css ================================================ .none { overscroll-behavior: none; } .contain { overscroll-behavior: contain; } .auto { overscroll-behavior: auto; } .inherit { overscroll-behavior: inherit; } ================================================ FILE: test/cases/overscroll-behavior.out.css ================================================ .none { -ms-scroll-chaining: none; overscroll-behavior: none; } .contain { -ms-scroll-chaining: none; overscroll-behavior: contain; } .auto { -ms-scroll-chaining: chained; overscroll-behavior: auto; } .inherit { -ms-scroll-chaining: inherit; overscroll-behavior: inherit; } ================================================ FILE: test/cases/pie.css ================================================ a { -pie-background: linear-gradient(white, black) } ================================================ FILE: test/cases/placeholder-shown.css ================================================ :placeholder-shown { background: #eee } ================================================ FILE: test/cases/placeholder-shown.out.css ================================================ :-ms-input-placeholder { background: #eee } :placeholder-shown { background: #eee } ================================================ FILE: test/cases/placeholder.css ================================================ ::placeholder { color: #999 } ================================================ FILE: test/cases/placeholder.out.css ================================================ ::-webkit-input-placeholder { color: #999 } :-moz-placeholder { color: #999 } ::-moz-placeholder { color: #999 } :-ms-input-placeholder { color: #999 } ::-ms-input-placeholder { color: #999 } ::placeholder { color: #999 } ================================================ FILE: test/cases/print-color-adjust.css ================================================ .a { color-adjust: economy; } .b { print-color-adjust: exact; } ================================================ FILE: test/cases/print-color-adjust.out.css ================================================ .a { -webkit-print-color-adjust: economy; color-adjust: economy; } .b { -webkit-print-color-adjust: exact; color-adjust: exact; print-color-adjust: exact; } ================================================ FILE: test/cases/resolution.css ================================================ @media (min-resolution: 2dppx), (min-resolution: 192dpi) { } @media (min-resolution: 2.5dppx) { } @media (min-resolution: 144dpi) { } @media (min-resolution: 2x) { } @media (min-resolution: 120dpi) { } @media (min-resolution: 2dppx) { } @media only screen and (min-resolution: 124.8dpi) { } @media (min-resolution: 113.38dpcm) { } ================================================ FILE: test/cases/resolution.out.css ================================================ @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx), (min-resolution: 192dpi) { } @media (-webkit-min-device-pixel-ratio: 2.5), (min--moz-device-pixel-ratio: 2.5), (-o-min-device-pixel-ratio: 5/2), (min-resolution: 2.5dppx) { } @media (-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-resolution: 144dpi) { } @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2x) { } @media (-webkit-min-device-pixel-ratio: 1.25), (min--moz-device-pixel-ratio: 1.25), (-o-min-device-pixel-ratio: 5/4), (min-resolution: 120dpi) { } @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx) { } @media only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 13/10), only screen and (min-resolution: 124.8dpi) { } @media (-webkit-min-device-pixel-ratio: 3), (min--moz-device-pixel-ratio: 3), (-o-min-device-pixel-ratio: 3/1), (min-resolution: 113.38dpcm) { } ================================================ FILE: test/cases/scope.css ================================================ a { /* autoprefixer: off */ -webkit-border-radius: 4px; border-radius: 4px; /* autoprefixer: on */ mask: none; } ================================================ FILE: test/cases/scope.out.css ================================================ a { /* autoprefixer: off */ -webkit-border-radius: 4px; border-radius: 4px; /* autoprefixer: on */ mask: none; } ================================================ FILE: test/cases/selectors.css ================================================ [href=:fullscreen] {} :fullscreen a { box-sizing: border-box } :fullscreen a { color: black } :-moz-full-screen a { transform: translate3d(0, 0, 0); } ================================================ FILE: test/cases/selectors.out.css ================================================ [href=:fullscreen] {} :-webkit-full-screen a { box-sizing: border-box } :-moz-full-screen a { -moz-box-sizing: border-box; box-sizing: border-box } :fullscreen a { -moz-box-sizing: border-box; box-sizing: border-box } :-webkit-full-screen a { color: black } :-moz-full-screen a { color: black } :fullscreen a { color: black } :-moz-full-screen a { transform: translate3d(0, 0, 0); } ================================================ FILE: test/cases/style.css ================================================ a { color : black; display: flex } ================================================ FILE: test/cases/style.out.css ================================================ a { color : black; display: -webkit-flex; display: flex } ================================================ FILE: test/cases/supports.css ================================================ @supports (cursor: grab) or not (mask: none) and (color: black) { a { color: black; } } @supports not (display: flex) { a { color: #000; } } @supports ((perspective: 1px) and (not (-webkit-overflow-scrolling: touch))) { a { color: #000; } } @supports (animation-name: test) and ((animation-timing-function: steps(4, end)) or (animation-timing-function: step-end)) { animation-name: test; } @supports (transition: color 1s) { transition: color 1s; } ================================================ FILE: test/cases/supports.out.css ================================================ @supports ((cursor: -webkit-grab) or (cursor: grab)) or not ((-webkit-mask: none) or (mask: none)) and (color: black) { a { color: black; } } @supports not ((display: -webkit-flex) or (display: flex)) { a { color: #000; } } @supports (((-webkit-perspective: 1px) or (perspective: 1px)) and (not (-webkit-overflow-scrolling: touch))) { a { color: #000; } } @supports ((-webkit-animation-name: test) or (animation-name: test)) and (((-webkit-animation-timing-function: steps(4, end)) or (animation-timing-function: steps(4, end))) or ((-webkit-animation-timing-function: step-end) or (animation-timing-function: step-end))) { -webkit-animation-name: test; animation-name: test; } @supports (transition: color 1s) { -webkit-transition: color 1s; transition: color 1s; } ================================================ FILE: test/cases/syntax.css ================================================ @page { margin: 0.5cm; } /* comment */ @-moz-document url-prefix() { a { color: black; } } a { /* c */ color/**/: white; padding: 0 /*{}*/ 1px /*}*/ 2px; } ================================================ FILE: test/cases/text-decoration.css ================================================ .shorthand { text-decoration: overline double red; } .shorthand-single-value { text-decoration: underline; } .full { text-decoration-color: green; text-decoration-line: line-through; text-decoration-style: double; } .old { text-decoration: underline; } .global { text-decoration: unset; } .skip { text-decoration-skip: spaces; } .ink { text-decoration-skip-ink: auto; } .old-ink { text-decoration-skip: ink; } ================================================ FILE: test/cases/text-decoration.out.css ================================================ .shorthand { -webkit-text-decoration: overline double red; text-decoration: overline double red; } .shorthand-single-value { text-decoration: underline; } .full { -webkit-text-decoration-color: green; -moz-text-decoration-color: green; text-decoration-color: green; -webkit-text-decoration-line: line-through; -moz-text-decoration-line: line-through; text-decoration-line: line-through; -webkit-text-decoration-style: double; -moz-text-decoration-style: double; text-decoration-style: double; } .old { text-decoration: underline; } .global { text-decoration: unset; } .skip { -webkit-text-decoration-skip: spaces; text-decoration-skip: spaces; } .ink { -webkit-text-decoration-skip: ink; text-decoration-skip-ink: auto; } .old-ink { -webkit-text-decoration-skip: ink; text-decoration-skip: ink; } ================================================ FILE: test/cases/text-decoration.shorthand.out.css ================================================ .shorthand { -webkit-text-decoration: overline double red; text-decoration: overline double red; } .shorthand-single-value { text-decoration: underline; } .full { text-decoration-color: green; text-decoration-line: line-through; text-decoration-style: double; } .old { text-decoration: underline; } .global { text-decoration: unset; } .skip { text-decoration-skip: spaces; } .ink { text-decoration-skip-ink: auto; } .old-ink { text-decoration-skip: ink; } ================================================ FILE: test/cases/text-emphasis-position.css ================================================ a { text-emphasis-position: over left; } em { text-emphasis-position: under right; } .reverse { text-emphasis-position: left over; } .wrong { text-emphasis-position: over; } ================================================ FILE: test/cases/text-emphasis-position.out.css ================================================ a { -webkit-text-emphasis-position: over; text-emphasis-position: over left; } em { -webkit-text-emphasis-position: under; text-emphasis-position: under right; } .reverse { -webkit-text-emphasis-position: over; text-emphasis-position: left over; } .wrong { -webkit-text-emphasis-position: over; text-emphasis-position: over; } ================================================ FILE: test/cases/transition-no-warning.css ================================================ .no-warn { transition-property: color, opacity; transition-duration: 1s, 2s; } ================================================ FILE: test/cases/transition-no-warning.out.css ================================================ .no-warn { -webkit-transition-property: color, opacity; -o-transition-property: color, opacity; transition-property: color, opacity; -webkit-transition-duration: 1s, 2s; -o-transition-duration: 1s, 2s; transition-duration: 1s, 2s; } ================================================ FILE: test/cases/transition-spec.css ================================================ input[type=range]::-moz-range-thumb { transition: color 200ms, transform 200ms; transform: rotate(10deg); } input[type=range]::-webkit-slider-thumb { transition: none; transition-property: all; } button::-moz-submit-invalid { opacity: 1; transform: translateX(45px); transition: opacity 0.5s 2s, transform 0.5s 0.5s; } @supports (transition: opacity 0.5s 2s, transform 0.5s 0.5s) { button::-moz-submit-invalid { opacity: 1; transform: translateX(45px); transition: opacity 0.5s 2s, transform 0.5s 0.5s; } button { opacity: 1; transform: translateX(45px); transition: opacity 0.5s 2s, transform 0.5s 0.5s; } } button::-webkit-search-cancel-button { display: flex; transition: color 200ms, transform 200ms; transform: rotate(10deg); } button::-webkit-search-cancel-button { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-transition: color 200ms, -webkit-transform 200ms; transition: color 200ms, -webkit-transform 200ms; transition: color 200ms, transform 200ms; transition: color 200ms, transform 200ms, -webkit-transform 200ms; -webkit-transform: rotate(10deg); transform: rotate(10deg); } .a::-webkit-search-cancel-button { display: flex; flex-flow: row; order: 0; flex: 0 1 2; transition: flex 200ms; } ================================================ FILE: test/cases/transition-spec.out.css ================================================ input[type=range]::-moz-range-thumb { -moz-transition: color 200ms, transform 200ms, -moz-transform 200ms; transition: color 200ms, transform 200ms; transition: color 200ms, transform 200ms, -moz-transform 200ms; -moz-transform: rotate(10deg); transform: rotate(10deg); } input[type=range]::-webkit-slider-thumb { -webkit-transition: none; transition: none; -webkit-transition-property: all; transition-property: all; } button::-moz-submit-invalid { opacity: 1; -moz-transform: translateX(45px); transform: translateX(45px); -moz-transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; transition: opacity 0.5s 2s, transform 0.5s 0.5s; transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; } @supports (transition: opacity 0.5s 2s, transform 0.5s 0.5s) { button::-moz-submit-invalid { opacity: 1; -moz-transform: translateX(45px); transform: translateX(45px); -moz-transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; transition: opacity 0.5s 2s, transform 0.5s 0.5s; transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; } button { opacity: 1; -webkit-transform: translateX(45px); -moz-transform: translateX(45px); -o-transform: translateX(45px); transform: translateX(45px); -webkit-transition: opacity 0.5s 2s, -webkit-transform 0.5s 0.5s; transition: opacity 0.5s 2s, -webkit-transform 0.5s 0.5s; -o-transition: opacity 0.5s 2s, -o-transform 0.5s 0.5s; -moz-transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; transition: opacity 0.5s 2s, transform 0.5s 0.5s; transition: opacity 0.5s 2s, transform 0.5s 0.5s, -webkit-transform 0.5s 0.5s, -moz-transform 0.5s 0.5s, -o-transform 0.5s 0.5s; } } button::-webkit-search-cancel-button { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-transition: color 200ms, -webkit-transform 200ms; transition: color 200ms, -webkit-transform 200ms; transition: color 200ms, transform 200ms; transition: color 200ms, transform 200ms, -webkit-transform 200ms; -webkit-transform: rotate(10deg); transform: rotate(10deg); } button::-webkit-search-cancel-button { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-transition: color 200ms, -webkit-transform 200ms; transition: color 200ms, -webkit-transform 200ms; transition: color 200ms, transform 200ms; transition: color 200ms, transform 200ms, -webkit-transform 200ms; -webkit-transform: rotate(10deg); transform: rotate(10deg); } .a::-webkit-search-cancel-button { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-flow: row; flex-flow: row; -webkit-box-ordinal-group: 1; -webkit-order: 0; order: 0; -webkit-box-flex: 0; -webkit-flex: 0 1 2; flex: 0 1 2; -webkit-transition: -webkit-box-flex 200ms, -webkit-flex 200ms; transition: -webkit-box-flex 200ms, -webkit-flex 200ms; transition: flex 200ms; transition: flex 200ms, -webkit-box-flex 200ms, -webkit-flex 200ms; } ================================================ FILE: test/cases/transition.css ================================================ a { transition: color 200ms, transform 200ms; transform: rotate(10deg); } div { transition-property: filter; animation-name: rotating; } .good { transition-property: filter; transition-duration: 1s; } .good2 { transition-property: color, filter; transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1); } .bad { /* safe with different node types */ transition-property: color, filter; transition-duration: 1s, 2s; } .revert { transition: 200ms transform; } ================================================ FILE: test/cases/transition.out.css ================================================ a { -webkit-transition: color 200ms, -webkit-transform 200ms; transition: color 200ms, -webkit-transform 200ms; -o-transition: color 200ms, -o-transform 200ms; transition: color 200ms, transform 200ms; transition: color 200ms, transform 200ms, -webkit-transform 200ms, -o-transform 200ms; -webkit-transform: rotate(10deg); -o-transform: rotate(10deg); transform: rotate(10deg); } div { -webkit-transition-property: -webkit-filter; transition-property: -webkit-filter; -o-transition-property: filter; transition-property: filter; transition-property: filter, -webkit-filter; -webkit-animation-name: rotating; -o-animation-name: rotating; animation-name: rotating; } .good { -webkit-transition-property: -webkit-filter; transition-property: -webkit-filter; -o-transition-property: filter; transition-property: filter; transition-property: filter, -webkit-filter; -webkit-transition-duration: 1s; -o-transition-duration: 1s; transition-duration: 1s; } .good2 { -webkit-transition-property: color, -webkit-filter; transition-property: color, -webkit-filter; -o-transition-property: color, filter; transition-property: color, filter; transition-property: color, filter, -webkit-filter; -webkit-transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1); -o-transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1); transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1); } .bad { /* safe with different node types */ -webkit-transition-property: color, -webkit-filter; transition-property: color, -webkit-filter; -o-transition-property: color, filter; transition-property: color, filter; transition-property: color, filter, -webkit-filter; -webkit-transition-duration: 1s, 2s; -o-transition-duration: 1s, 2s; transition-duration: 1s, 2s; } .revert { -webkit-transition: 200ms -webkit-transform; transition: 200ms -webkit-transform; -o-transition: 200ms -o-transform; transition: 200ms transform; transition: 200ms transform, 200ms -webkit-transform, 200ms -o-transform; } ================================================ FILE: test/cases/trim.css ================================================ a { background: -webkit-linear-gradient(top, #f00 0%, #fff 100%); background: linear-gradient(to bottom, #f00 0%, #fff 100%); } ================================================ FILE: test/cases/uncascade.css ================================================ a { -webkit-transition: 1s; -moz-transition: 1s; transition: 1s } a { -moz-transition: 1s; -o-transition: 1s; transition: 1s } a { -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px } ================================================ FILE: test/cases/uncascade.out.css ================================================ a { -moz-transition: 1s; transition: 1s } a { -moz-transition: 1s; transition: 1s } a { border-radius: 4px } ================================================ FILE: test/cases/user-select.css ================================================ a { user-select: none; } b { user-select: contain; } .all { user-select: all; } .var { user-select: var(--o-select); } ================================================ FILE: test/cases/user-select.out.css ================================================ a { -webkit-user-select: none; -ms-user-select: none; user-select: none; } b { -ms-user-select: element; user-select: contain; } .all { -webkit-user-select: all; user-select: all; } .var { -webkit-user-select: var(--o-select); -ms-user-select: var(--o-select); user-select: var(--o-select); } ================================================ FILE: test/cases/value-hack.css ================================================ .not-hack { width: -webkit-calc(30% + 1px); width: calc(30% + 1px); display: -webkit-box; display: flex; transition: -webkit-filter 1s, filter 1s; } .not-hack2 { transition: -webkit-filter 1s; transition: filter 1s; } .hack { width: -webkit-calc(30% + 1px); display: -webkit-box; } ================================================ FILE: test/cases/value-hack.out.css ================================================ .not-hack { width: calc(30% + 1px); display: flex; transition: filter 1s; } .not-hack2 { transition: filter 1s; } .hack { width: -webkit-calc(30% + 1px); display: -webkit-box; } ================================================ FILE: test/cases/values.css ================================================ a { margin: calc(5% + 5px) calc(10% + 10px); background: linear-gradient(black, white), radial-gradient(white, black); content: " linear-gradient(black, white) "; } ================================================ FILE: test/cases/values.out.css ================================================ a { margin: -webkit-calc(5% + 5px) -webkit-calc(10% + 10px); margin: calc(5% + 5px) calc(10% + 10px); background: -webkit-linear-gradient(black, white), -webkit-radial-gradient(white, black); background: -o-linear-gradient(black, white), -o-radial-gradient(white, black); background: linear-gradient(black, white), radial-gradient(white, black); content: " linear-gradient(black, white) "; } ================================================ FILE: test/cases/vendor-hack.css ================================================ .a { -moz-transform: scale(.9999); transform: scale(.9999); } .b { -moz-transform: scale(.9999); } .c { transform: scale(1); -moz-transform: scale(2); } ================================================ FILE: test/cases/vendor-hack.out.css ================================================ .a { transform: scale(.9999); } .b { -moz-transform: scale(.9999); } .c { transform: scale(1); -moz-transform: scale(2); } ================================================ FILE: test/cases/viewport.css ================================================ @viewport { width: device-width; } ================================================ FILE: test/cases/viewport.out.css ================================================ @-ms-viewport { width: device-width; } @viewport { width: device-width; } ================================================ FILE: test/cases/webkit-line-clamp.css ================================================ .limit-text { overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .simple-clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .clamp-with-flex-direction { flex-direction: column; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .clamp-with-flex-direction-and-box-direction { flex-direction: column; display: -webkit-box; -webkit-box-orient: vertical; -webkit-box-direction: reverse; -webkit-line-clamp: 2; } .clamp-display-webkit-box-only { display: -webkit-box; -webkit-line-clamp: 2; } .clamp-display-webkit-box-orient-vertical-only { -webkit-box-orient: vertical; -webkit-line-clamp: 2; } ================================================ FILE: test/cases/webkit-line-clamp.out.css ================================================ .limit-text { overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .simple-clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .clamp-with-flex-direction { flex-direction: column; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .clamp-with-flex-direction-and-box-direction { flex-direction: column; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .clamp-display-webkit-box-only { display: -webkit-box; -webkit-line-clamp: 2; } .clamp-display-webkit-box-orient-vertical-only { -webkit-box-orient: vertical; -webkit-line-clamp: 2; } ================================================ FILE: test/cases/writing-mode.css ================================================ .one { writing-mode: horizontal-tb; } .two { writing-mode: vertical-rl; } .three { writing-mode: vertical-lr; } .rtl-vertical-rl { writing-mode: vertical-rl; direction: rtl; } .rtl-vertical-lr { writing-mode: vertical-lr; direction: rtl; } .rtl-horizontal-tb { writing-mode: horizontal-tb; direction: rtl; } .rtl-horizontal-tb-override-direction { writing-mode: horizontal-tb; direction: rtl; direction: ltr; } ================================================ FILE: test/cases/writing-mode.out.css ================================================ .one { -webkit-writing-mode: horizontal-tb; -ms-writing-mode: lr-tb; writing-mode: horizontal-tb; } .two { -webkit-writing-mode: vertical-rl; -ms-writing-mode: tb-rl; writing-mode: vertical-rl; } .three { -webkit-writing-mode: vertical-lr; -ms-writing-mode: tb-lr; writing-mode: vertical-lr; } .rtl-vertical-rl { -webkit-writing-mode: vertical-rl; -ms-writing-mode: bt-rl; writing-mode: vertical-rl; direction: rtl; } .rtl-vertical-lr { -webkit-writing-mode: vertical-lr; -ms-writing-mode: bt-lr; writing-mode: vertical-lr; direction: rtl; } .rtl-horizontal-tb { -webkit-writing-mode: horizontal-tb; -ms-writing-mode: rl-tb; writing-mode: horizontal-tb; direction: rtl; } .rtl-horizontal-tb-override-direction { -webkit-writing-mode: horizontal-tb; -ms-writing-mode: lr-tb; writing-mode: horizontal-tb; direction: rtl; direction: ltr; } ================================================ FILE: test/declaration.test.js ================================================ let { parse } = require('postcss') let { test } = require('uvu') let { equal, is } = require('uvu/assert') let Declaration = require('../lib/declaration') let Prefixes = require('../lib/prefixes') let prefixes, tabsize test.before.each(() => { prefixes = new Prefixes({}, {}) tabsize = new Declaration('tab-size', ['-moz-', '-ms-'], prefixes) }) test.after.each(() => { delete prefixes.options.cascade }) test('checks values for other prefixes', () => { is(tabsize.otherPrefixes('black', '-moz-'), false) is(tabsize.otherPrefixes('-moz-black', '-moz-'), false) is(tabsize.otherPrefixes('-dev-black', '-moz-'), false) is(tabsize.otherPrefixes('-ms-black', '-moz-'), true) }) test('returns true by default', () => { let css = parse('a {\n tab-size: 4 }') is(tabsize.needCascade(css.first.first), true) }) test('return false is disabled', () => { prefixes.options.cascade = false let css = parse('a {\n tab-size: 4 }') is(tabsize.needCascade(css.first.first), false) }) test('returns false on declarations in one line', () => { let css = parse('a { tab-size: 4 } a {\n tab-size: 4 }') is(tabsize.needCascade(css.first.first), false) is(tabsize.needCascade(css.last.first), true) }) test('returns max prefix length', () => { let decl = parse('a { tab-size: 4 }').first.first let list = ['-webkit-', '-webkit- old', '-moz-'] equal(tabsize.maxPrefixed(list, decl), 8) }) test('returns before with cascade', () => { let decl = parse('a { tab-size: 4 }').first.first let list = ['-webkit-', '-moz- old', '-moz-'] equal(tabsize.calcBefore(list, decl, '-moz- old'), ' ') }) test('removes cascade', () => { let css = parse('a {\n' + ' -moz-tab-size: 4;\n' + ' tab-size: 4 }') let decl = css.first.nodes[1] tabsize.restoreBefore(decl) equal(decl.raws.before, '\n ') }) test('returns prefixed property', () => { let css = parse('a { tab-size: 2 }') let decl = css.first.first equal(tabsize.prefixed(decl.prop, '-moz-'), '-moz-tab-size') }) test('returns property name by specification', () => { equal(tabsize.normalize('tab-size'), 'tab-size') }) test('adds prefixes', () => { let css = parse('a { -moz-tab-size: 2; tab-size: 2 }') tabsize.process(css.first.nodes[1]) equal(css.toString(), 'a { -moz-tab-size: 2; -ms-tab-size: 2; tab-size: 2 }') }) test('checks parents prefix', () => { let css = parse('::-moz-selection a { tab-size: 2 }') tabsize.process(css.first.first) equal(css.toString(), '::-moz-selection a { -moz-tab-size: 2; tab-size: 2 }') }) test('checks value for prefixes', () => { let css = parse('a { tab-size: -ms-calc(2) }') tabsize.process(css.first.first) equal( css.toString(), 'a { -ms-tab-size: -ms-calc(2); tab-size: -ms-calc(2) }' ) }) test('returns list of prefixeds', () => { equal(tabsize.old('tab-size', '-moz-'), ['-moz-tab-size']) }) test.run() ================================================ FILE: test/info.test.js ================================================ let browserslist = require('browserslist') let { agents } = require('caniuse-lite/dist/unpacker/agents') let { test } = require('uvu') let { equal, match } = require('uvu/assert') let Browsers = require('../lib/browsers') let info = require('../lib/info') let Prefixes = require('../lib/prefixes') let data = { browsers: agents, prefixes: { '@keyframes': { browsers: ['firefox 21'] }, 'a': { browsers: ['firefox 21', 'firefox 20', 'chrome 30'], transition: true }, 'b': { browsers: ['ie 6', 'firefox 20'], props: ['a', '*'] }, 'c': { browsers: ['firefox 21'], props: ['c'] }, 'd': { browsers: ['firefox 21'], selector: true }, 'grid': { browsers: ['ie 6'], props: ['display'] }, 'grid-row': { browsers: ['ie 6'] }, 'transition': { browsers: ['firefox 21'] } } } test('returns selected browsers and prefixes', () => { let browsers = new Browsers(data.browsers, [ 'chrome 30', 'firefox 21', 'firefox 20', 'ie 6' ]) let prefixes = new Prefixes(data.prefixes, browsers) let coverage = browserslist.coverage([ 'chrome 30', 'firefox 21', 'firefox 20', 'ie 6' ]) let round = Math.round(coverage * 100) / 100.0 equal( info(prefixes), 'Browsers:\n' + ' Chrome: 30\n' + ' Firefox: 21, 20\n' + ' IE: 6\n' + '\n' + `These browsers account for ${round}% ` + 'of all users globally\n' + '\n' + 'At-Rules:\n' + ' @keyframes: moz\n' + '\n' + 'Selectors:\n' + ' d: moz\n' + '\n' + 'Properties:\n' + ' a: webkit, moz\n' + ' grid-row *: ms\n' + ' transition: moz\n' + '\n' + 'Values:\n' + ' b: moz, ms\n' + ' c: moz\n' + ' grid *: ms\n' + '\n' + '* - Prefixes will be added only on grid: true option.\n' ) }) test('does not show transitions unless they are necessary', () => { let browsers = new Browsers(data.browsers, ['chrome 30', 'firefox 20']) let prefixes = new Prefixes(data.prefixes, browsers) let coverage = browserslist.coverage(['chrome 30', 'firefox 20']) let round = Math.round(coverage * 100) / 100.0 equal( info(prefixes), 'Browsers:\n' + ' Chrome: 30\n' + ' Firefox: 20\n' + '\n' + `These browsers account for ${round}% ` + 'of all users globally\n' + '\n' + 'Properties:\n' + ' a: webkit, moz\n' + '\n' + 'Values:\n' + ' b: moz\n' ) }) test('returns string for empty prefixes', () => { let browsers = new Browsers(data.browsers, ['ie 7']) let prefixes = new Prefixes(data.prefixes, browsers) match(info(prefixes), /remove Autoprefixer/) }) test('returns string for empty browsers', () => { let browsers = new Browsers(data.browsers, []) let prefixes = new Prefixes(data.prefixes, browsers) equal(info(prefixes), 'No browsers selected') }) test.run() ================================================ FILE: test/old-selector.test.js ================================================ let { parse } = require('postcss') let { test } = require('uvu') let { is } = require('uvu/assert') let Selector = require('../lib/selector') let selector = new Selector('::selection', ['-moz-', '-ms-']) let old = selector.old('-moz-') test('returns true on last rule', () => { let css = parse('::selection {} ::-moz-selection {}') is(old.isHack(css.last), true) }) test('stops on another type', () => { let css = parse('::-moz-selection {} ' + '@keyframes anim {} ::selection {}') is(old.isHack(css.first), true) }) test('stops on another selector', () => { let css = parse('::-moz-selection {} a {} ::selection {}') is(old.isHack(css.first), true) }) test('finds unprefixed selector', () => { let css = parse('::-moz-selection {} ' + '::-o-selection {} ::selection {}') is(old.isHack(css.first), false) }) test('finds old selector', () => { let css = parse('body::-moz-selection {} body::selection {}') is(old.check(css.first), true) }) test('finds right', () => { let css = parse('body:::-moz-selection {}') is(old.check(css.first), false) }) test.run() ================================================ FILE: test/old-value.test.js ================================================ let { test } = require('uvu') let { is } = require('uvu/assert') let OldValue = require('../lib/old-value') test('checks value in string', () => { let old = new OldValue('calc', '-o-calc') is(old.check('1px -o-calc(1px)'), true) is(old.check('1px calc(1px)'), false) }) test('allows custom checks', () => { let old = new OldValue('calc', '-o-calc', 'calc', /calc/) is(old.check('1px calc(1px)'), true) }) test.run() ================================================ FILE: test/postcss.test.js ================================================ let postcss = require('postcss') let { test } = require('uvu') let { equal } = require('uvu/assert') let autoprefixer = require('..') test('works with other PostCSS plugins', () => { let plugin = () => { return { AtRule: { mixin: (atRule, { Declaration }) => { atRule.replaceWith( new Declaration({ prop: 'user-select', value: 'none' }) ) } }, postcssPlugin: 'test', Rule(rule) { rule.selector = 'b' } } } plugin.postcss = true let result = postcss([ plugin(), autoprefixer({ overrideBrowserslist: 'chrome 40' }) ]).process('a{ @mixin; }', { from: 'a.css' }) equal(result.css, 'b{ -webkit-user-select: none; user-select: none; }') }) test.run() ================================================ FILE: test/prefixer.test.js ================================================ let { parse } = require('postcss') let { test } = require('uvu') let { equal, is, type } = require('uvu/assert') let Prefixer = require('../lib/prefixer') let css, prefix test.before.each(() => { prefix = new Prefixer() css = parse( '@-ms-keyframes a { to { } } ' + ':-moz-full-screen { } a { } ' + '@-dev-keyframes s { to { } }' ) }) test('registers hacks for subclasses', () => { class A extends Prefixer {} class Hack extends A {} Hack.names = ['a', 'b'] A.hack(Hack) equal(A.hacks, { a: Hack, b: Hack }) type(Prefixer.hacks, 'undefined') }) test('loads hacks', () => { class A extends Prefixer { constructor() { super() this.klass = 'a' } } class Hack extends A { constructor() { super() this.klass = 'hack' } } A.hacks = { hacked: Hack } equal(A.load('hacked').klass, 'hack') equal(A.load('a').klass, 'a') }) test('cleans custom properties', () => { let rule = css.first.first rule._autoprefixerPrefix = '-ms-' rule._autoprefixerValues = { '-ms-': 1 } let cloned = Prefixer.clone(rule, { selector: 'from' }) equal(cloned.selector, 'from') type(cloned._autoprefixerPrefix, 'undefined') type(cloned._autoprefixerValues, 'undefined') }) test('fixed declaration between', () => { let parsed = parse('a { color : black }') let cloned = Prefixer.clone(parsed.first.first) equal(cloned.raws.between, ' : ') }) test('works with root node', () => { is(prefix.parentPrefix(css), false) }) test('finds in at-rules', () => { equal(prefix.parentPrefix(css.first), '-ms-') }) test('finds in selectors', () => { equal(prefix.parentPrefix(css.nodes[1]), '-moz-') }) test('finds in parents', () => { let decl = css.first.first equal(prefix.parentPrefix(decl), '-ms-') is(prefix.parentPrefix(css.nodes[2]), false) }) test('caches prefix', () => { prefix.parentPrefix(css.first) equal(css.first._autoprefixerPrefix, '-ms-') css.first._autoprefixerPrefix = false is(prefix.parentPrefix(css.first), false) }) test('finds only browsers prefixes', () => { is(prefix.parentPrefix(css.nodes[2]), false) }) test('works with selector contained --', () => { let parsed = parse(':--a { color: black }') is(prefix.parentPrefix(parsed.first.first), false) }) test.run() ================================================ FILE: test/prefixes.test.js ================================================ let { agents } = require('caniuse-lite/dist/unpacker/agents') let { parse } = require('postcss') let { test } = require('uvu') let { equal, is } = require('uvu/assert') let Browsers = require('../lib/browsers') let Declaration = require('../lib/declaration') let OldValue = require('../lib/old-value') let Prefixes = require('../lib/prefixes') let Selector = require('../lib/selector') let Supports = require('../lib/supports') let Value = require('../lib/value') let data = { browsers: agents, prefixes: { a: { browsers: ['firefox 21', 'firefox 20 old', 'chrome 30', 'ie 6'] }, b: { browsers: ['ie 7 new', 'firefox 20'], mistakes: ['-webkit-'], props: ['a', '*'] }, c: { browsers: ['ie 7', 'firefox 20'], selector: true } } } let empty = new Prefixes({}, new Browsers(data.browsers, [])) let fill = new Prefixes( data.prefixes, new Browsers(data.browsers, ['firefox 21', 'ie 7']) ) let cSel = new Selector('c', ['-ms-'], fill) let bVal = new Value('b', ['-ms- new'], fill) let aProp = new Declaration('a', ['-moz-'], fill) aProp.values = [bVal] function old(prefixed) { let name = prefixed.replace(/-[^-]+-( old)?/, '') return new OldValue(name, prefixed) } test('selects necessary prefixes', () => { equal(fill.select(data.prefixes), { add: { a: ['-moz-'], b: ['-ms- new'], c: ['-ms-'] }, remove: { a: ['-webkit-', '-ms-', '-moz- old'], b: ['-ms-', '-moz-', '-webkit-'], c: ['-moz-'] } }) }) test('preprocesses prefixes add data', () => { equal(fill.add, { '@supports': new Supports(Prefixes, fill), '*': { values: [bVal] }, 'a': aProp, 'selectors': [cSel] }) }) test('preprocesses prefixes remove data', () => { equal( JSON.parse(JSON.stringify(fill.remove)), JSON.parse( JSON.stringify({ '-moz- olda': { remove: true }, '-ms-a': { remove: true }, '-webkit-a': { remove: true }, '*': { values: [old('-ms-b'), old('-moz-b'), old('-webkit-b')] }, 'a': { values: [old('-ms-b'), old('-moz-b'), old('-webkit-b')] }, 'selectors': [cSel.old('-moz-')] }) ) ) }) test('returns itself is no browsers are selected', () => { equal(empty.cleaner(), empty) }) test('returns Prefixes with empty browsers', () => { let cleaner = new Prefixes(data.prefixes, new Browsers(data.browsers, [])) equal(Object.keys(fill.cleaner().add).length, 2) equal(fill.cleaner().remove, cleaner.remove) }) test('loads declarations by property', () => { equal(empty.decl('a'), new Declaration('a')) }) test('caches values', () => { equal(empty.decl('a'), empty.decl('a')) }) test('returns unprefixed version', () => { equal(empty.unprefixed('-moz-a'), 'a') }) test('adds prefix', () => { equal(empty.prefixed('a', '-ms-'), '-ms-a') }) test('changes prefix', () => { equal(empty.prefixed('a', '-ms-'), '-ms-a') }) test('returns values for this and all properties', () => { equal(fill.values('add', 'a'), [bVal]) equal(fill.values('remove', 'a'), [ old('-ms-b'), old('-moz-b'), old('-webkit-b') ]) }) test('checks prefix group', () => { let css = parse('a { -ms-a: 1; -o-a: 1; a: 1; b: 2 }') let props = [] empty.group(css.first.first).down(i => props.push(i.prop)) equal(props, ['-o-a', 'a']) }) test('checks prefix groups', () => { let css = parse('a { -ms-a: 1; -o-a: 1; ' + 'a: -o-calc(1); a: 1; a: 2 }') let props = [] empty.group(css.first.first).down(i => props.push(i.prop)) equal(props, ['-o-a', 'a', 'a']) }) test('returns check decls inside group', () => { let css = parse('a { -moz-a: 1; -ms-a: 1; -o-a: 1; a: 1 }') let decl = css.first.first is( empty.group(decl).down(i => i.prop === '-o-a'), true ) is( empty.group(decl).down(i => i.prop === '-o-b'), false ) }) test('checks prefix group', () => { let css = parse('a { b: 2; -ms-a: 1; -o-a: 1; a: 1 }') let props = [] empty.group(css.first.nodes[3]).up(i => props.push(i.prop)) equal(props, ['-o-a', '-ms-a']) }) test('checks prefix groups', () => { let css = parse('a { a: 2; -ms-a: 1; ' + '-o-a: 1; a: -o-calc(1); a: 1 }') let props = [] empty.group(css.first.nodes[4]).up(i => props.push(i.prop)) equal(props, ['a', '-o-a', '-ms-a']) }) test('returns check decls inside group', () => { let css = parse('a { -moz-a: 1; -ms-a: 1; -o-a: 1; a: 1 }') let decl = css.first.nodes[3] is( empty.group(decl).up(i => i.prop === '-ms-a'), true ) is( empty.group(decl).up(i => i.prop === '-ms-b'), false ) }) test.run() ================================================ FILE: test/selector.test.js ================================================ let { parse } = require('postcss') let { test } = require('uvu') let { equal, is } = require('uvu/assert') let Selector = require('../lib/selector') let prefixeds, selector test.before.each(() => { selector = new Selector('::selection', ['-moz-', '-ms-']) prefixeds = selector.prefixeds(parse('::selection {}').first) }) test('adds prefix after non-letters symbols', () => { equal(selector.prefixed('-moz-'), '::-moz-selection') }) test('creates regexp for prefix', () => { let regexp = selector.regexp('-moz-') is(regexp.test('::-moz-selection'), true) is(regexp.test('::selection'), false) }) test('creates regexp without prefix', () => { let regexp = selector.regexp() is(regexp.test('::-moz-selection'), false) is(regexp.test('::selection'), true) }) test('checks rule selectors', () => { let css = parse( 'body .selection {}, ' + ':::selection {}, body ::selection {}' ) is(selector.check(css.nodes[0]), false) is(selector.check(css.nodes[1]), false) is(selector.check(css.nodes[2]), true) }) test('grouping rule gets correct _autoprefixerPrefixeds property', () => { let css = parse('.c::selection, .d:read-only {}') let rSel = new Selector(':read-only', ['-moz-']) selector.prefixeds(css.first) rSel.prefixeds(css.first) equal(css.first._autoprefixerPrefixeds, { '::selection': { '-moz-': '.c::-moz-selection', '-ms-': '.c::-ms-selection', '-o-': '.c::-o-selection', '-webkit-': '.c::-webkit-selection' }, ':read-only': { '-moz-': '.d:-moz-read-only', '-ms-': '.d:-ms-read-only', '-o-': '.d:-o-read-only', '-webkit-': '.d:-webkit-read-only' } }) }) test('returns all available prefixed selectors', () => { let css = parse('::selection {}') equal(selector.prefixeds(css.first), { '::selection': { '-moz-': '::-moz-selection', '-ms-': '::-ms-selection', '-o-': '::-o-selection', '-webkit-': '::-webkit-selection' } }) }) test('returns false on first element', () => { let css = parse('::selection {}') is(selector.already(css.first, prefixeds, '-moz-'), false) }) test('stops on another type', () => { let css = parse('::-moz-selection {} ' + '@keyframes anim {} ::selection {}') is(selector.already(css.nodes[2], prefixeds, '-moz-'), false) }) test('stops on another selector', () => { let css = parse('::-moz-selection {} a {} ::selection {}') is(selector.already(css.nodes[2], prefixeds, '-moz-'), false) }) test('finds prefixed even if unknown prefix is between', () => { let css = parse('::-moz-selection {} ' + '::-o-selection {} ::selection {}') is(selector.already(css.nodes[2], prefixeds, '-moz-'), true) }) test('adds prefix to selectors', () => { equal( selector.replace('body ::selection, input::selection, a', '-ms-'), 'body ::-ms-selection, input::-ms-selection, a' ) }) test('adds prefixes', () => { let css = parse('b ::-moz-selection{} b ::selection{}') selector.process(css.nodes[1]) equal( css.toString(), 'b ::-moz-selection{} b ::-ms-selection{} b ::selection{}' ) }) test('checks parents prefix', () => { let css = parse('@-moz-page{ ::selection{} }') selector.process(css.first.first) equal(css.toString(), '@-moz-page{ ::-moz-selection{} ::selection{} }') }) test('returns object to find old selector', () => { let old = selector.old('-moz-') equal(old.unprefixed, '::selection') equal(old.prefix, '-moz-') }) test.run() ================================================ FILE: test/supports.test.js ================================================ let { test } = require('uvu') let { equal } = require('uvu/assert') let brackets = require('../lib/brackets') let Browsers = require('../lib/browsers') let Prefixes = require('../lib/prefixes') let Supports = require('../lib/supports') let browsers = new Browsers( { firefox: { prefix: 'moz', versions: ['firefox 22'] } }, ['firefox 22', 'firefox 21'] ) let prefixes = new Prefixes( { a: { browsers: ['firefox 22'] }, b: { browsers: ['firefox 22'], props: 'c' } }, browsers ) let supports = new Supports(Prefixes, prefixes) function rm(str) { let ast = supports.normalize(brackets.parse(str)) return brackets.stringify(supports.remove(ast, str)) } function clean(str) { let ast = supports.normalize(brackets.parse(str)) return brackets.stringify(supports.cleanBrackets(ast)) } test('splits property name and value', () => { equal(supports.parse('color:black'), ['color', 'black']) }) test('cleans spaces', () => { equal(supports.parse(' color : black '), ['color', 'black']) }) test('parses everything', () => { equal(supports.parse('color'), ['color', '']) }) test('returns virtual rule', () => { let decl = supports.virtual('color: black') equal(decl.type, 'rule') equal(decl.toString(), 'a{color: black}') }) test('works with broken CSS', () => { let decl = supports.virtual('color black') equal(decl.type, 'rule') }) test('returns decls with prefixed property', () => { let decls = supports.prefixed('a: one') equal(decls.length, 2) equal(decls[0].toString(), '-moz-a: one') equal(decls[1].toString(), 'a: one') }) test('returns decls with prefixed value', () => { let decls = supports.prefixed('c: b') equal(decls.length, 2) equal(decls[0].toString(), 'c: -moz-b') equal(decls[1].toString(), 'c: b') }) test('reduces empty string', () => { equal(supports.normalize([['', ['a'], '']]), [[['a']]]) }) test('reduces declaration to string', () => { equal(supports.normalize(['a: b', ['1']]), ['a: b(1)']) }) test('reduces wrapped declaration to string', () => { equal(supports.normalize(['', ['a: b', ['1']], '']), [['a: b(1)']]) }) test('remove prefixed properties', () => { equal(rm('(-moz-a: 1) or (a: 1)'), '(a: 1)') }) test('remove prefixed properties inside', () => { equal(rm('(((-moz-a: 1) or (a: 1)))'), '(((a: 1)))') }) test('remove prefixed values', () => { equal(rm('(c: -moz-b) or (c: -b-)'), '(c: -b-)') }) test('keeps and-conditions', () => { equal(rm('(-moz-a: 1) and (a: 1)'), '(-moz-a: 1) and (a: 1)') }) test('keeps not-conditions', () => { equal(rm('not (-moz-a: 1) or (a: 1)'), 'not (-moz-a: 1) or (a: 1)') }) test('keeps hacks', () => { equal(rm('(-moz-a: 1) or (b: 2)'), '(-moz-a: 1) or (b: 2)') }) test('uses only browsers with @supports support', () => { equal(supports.prefixer().browsers.selected, ['firefox 22']) }) test('normalize brackets', () => { equal(clean('((a: 1))'), '(a: 1)') }) test('normalize brackets recursively', () => { equal(clean('(((a: 1) or ((b: 2))))'), '((a: 1) or (b: 2))') }) test('adds params with prefixed value', () => { let rule = { params: '(c: b)' } supports.process(rule) equal(rule.params, '((c: -moz-b) or (c: b))') }) test('adds params with prefixed function', () => { let rule = { params: '(c: b(1))' } supports.process(rule) equal(rule.params, '((c: -moz-b(1)) or (c: b(1)))') }) test('replaces params with prefixed property', () => { let rule = { params: '(color black) and not (a: 1)' } supports.process(rule) equal(rule.params, '(color black) and not ((-moz-a: 1) or (a: 1))') }) test("shouldn't throw errors", () => { let rule = { params: 'not selector(:is(a, b))' } supports.process(rule) equal(rule.params, 'not selector(:is(a, b))') }) test("shouldn't throw errors (2)", () => { let rule = { params: ' (selector( :nth-child(1n of a, b) )) or (c: b(1)) ' } supports.process(rule) equal( rule.params, ' (selector( :nth-child(1n of a, b) )) or ((c: -moz-b(1)) or (c: b(1))) ' ) }) test.run() ================================================ FILE: test/utils.test.js ================================================ let { test } = require('uvu') let { equal, is, throws } = require('uvu/assert') let utils = require('../lib/utils') test('raises an error', () => { throws(() => { utils.error('A') }, 'A') }) test('marks an error', () => { let error = null try { utils.error('A') } catch (e) { error = e } is(error.autoprefixer, true) }) test('filters doubles in array', () => { equal(utils.uniq(['1', '1', '2', '3', '3']), ['1', '2', '3']) }) test('removes note', () => { equal(utils.removeNote('-webkit- note'), '-webkit-') equal(utils.removeNote('-webkit-'), '-webkit-') }) test('escapes RegExp symbols', () => { equal(utils.escapeRegexp('^[()\\]'), '\\^\\[\\(\\)\\\\\\]') }) test('generates RegExp that finds tokens in CSS values', () => { let regexp = utils.regexp('foo') function check(string) { return string.match(regexp) !== null } is(check('foo'), true) is(check('Foo'), true) is(check('one, foo, two'), true) is(check('one(),foo(),two()'), true) equal('foo(), a, foo'.replace(regexp, '$1b$2'), 'bfoo(), a, bfoo') is(check('foob'), false) is(check('(foo)'), false) is(check('-a-foo'), false) }) test('escapes string if needed', () => { let regexp = utils.regexp('(a|b)') function check(string) { return string.match(regexp) !== null } is(check('a'), false) is(check('(a|b)'), true) regexp = utils.regexp('(a|b)', false) is(check('a'), true) is(check('b'), true) }) test('does save without changes', () => { let list = utils.editList('a,\nb, c', parsed => parsed) equal(list, 'a,\nb, c') }) test('changes list', () => { let list = utils.editList('a, b', (parsed, edit) => { equal(parsed, ['a', 'b']) equal(edit, []) return ['1', '2'] }) equal(list, '1, 2') }) test('saves comma', () => { let list = utils.editList('a,\nb', () => ['1', '2']) equal(list, '1,\n2') }) test('parse one value', () => { let list = utils.editList('1', parsed => [parsed[0], '2']) equal(list, '1, 2') }) test('splits simple selectors into an array', () => { let arr1 = utils.splitSelector('#foo.bar') let arr2 = utils.splitSelector('.foo, .bar') equal(arr1, [[['#foo', '.bar']]]) equal(arr2, [[['.foo']], [['.bar']]]) }) test('splits complex selectors into an array', () => { let arr = utils.splitSelector( '#foo.bar .child-one.mod .child-two.mod, .baz, .hello' ) equal(arr, [ [ ['#foo', '.bar'], ['.child-one', '.mod'], ['.child-two', '.mod'] ], [['.baz']], [['.hello']] ]) }) test('detects numbers', () => { equal(utils.isPureNumber(42), true) equal(utils.isPureNumber('42'), true) equal(utils.isPureNumber('autoprefixer'), false) equal(utils.isPureNumber(''), false) equal(utils.isPureNumber({}), false) equal(utils.isPureNumber(undefined), false) equal(utils.isPureNumber(true), false) }) test.run() ================================================ FILE: test/value.test.js ================================================ let { parse } = require('postcss') let { test } = require('uvu') let { equal, is, type } = require('uvu/assert') let OldValue = require('../lib/old-value') let Prefixes = require('../lib/prefixes') let Value = require('../lib/value') let prefixes = new Prefixes() let calc test.before.each(() => { calc = new Value('calc', ['-moz-', '-ms-']) }) test('clones declaration', () => { let css = parse('a { prop: v }') let width = css.first.first width._autoprefixerValues = { '-ms-': '-ms-v' } Value.save(prefixes, width) equal(css.toString(), 'a { prop: -ms-v; prop: v }') }) test('updates declaration with prefix', () => { let css = parse('a { -ms-prop: v }') let width = css.first.first width._autoprefixerValues = { '-ms-': '-ms-v' } Value.save(prefixes, width) equal(css.toString(), 'a { -ms-prop: -ms-v }') }) test('ignores on another prefix property', () => { let css = parse('a { -ms-prop: v; prop: v }') let width = css.first.last width._autoprefixerValues = { '-ms-': '-ms-v' } Value.save(prefixes, width) equal(css.toString(), 'a { -ms-prop: v; prop: v }') }) test('ignores prefixes without changes', () => { let css = parse('a { prop: v }') let width = css.first.first width._autoprefixerValues = { '-ms-': 'v' } Value.save(prefixes, width) equal(css.toString(), 'a { prop: v }') }) test('checks value in string', () => { let css = parse( 'a { 0: calc(1px + 1em); ' + '1: 1px calc(1px + 1em); ' + '2: (calc(1px + 1em)); ' + '3: -ms-calc; ' + '4: calced; }' ) is(calc.check(css.first.nodes[0]), true) is(calc.check(css.first.nodes[1]), true) is(calc.check(css.first.nodes[2]), true) is(calc.check(css.first.nodes[3]), false) is(calc.check(css.first.nodes[4]), false) }) test('check prefixed value', () => { equal(calc.old('-ms-'), new OldValue('calc', '-ms-calc')) }) test('adds prefix to value', () => { equal(calc.replace('1px calc(1em)', '-ms-'), '1px -ms-calc(1em)') equal(calc.replace('1px,calc(1em)', '-ms-'), '1px,-ms-calc(1em)') }) test('adds prefixes', () => { let css = parse('a { width: calc(1em) calc(1%) }') let width = css.first.first calc.process(width) equal(width._autoprefixerValues, { '-moz-': '-moz-calc(1em) -moz-calc(1%)', '-ms-': '-ms-calc(1em) -ms-calc(1%)' }) }) test('checks parents prefix', () => { let css = parse('::-moz-fullscreen a { width: calc(1%) }') let width = css.first.first calc.process(width) equal(width._autoprefixerValues, { '-moz-': '-moz-calc(1%)' }) }) test('checks property prefix', () => { let css = parse('a { -moz-width: calc(1%); -o-width: calc(1%) }') let decls = css.first.nodes calc.process(decls[0]) equal(decls[0]._autoprefixerValues, { '-moz-': '-moz-calc(1%)' }) calc.process(decls[1]) type(decls[1]._autoprefixerValues, 'undefined') }) test.run()