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].
[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