├── .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 ├── logo.svg ├── package.json ├── pnpm-lock.yaml └── 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 /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: postcss 2 | tidelift: npm/autoprefixer 3 | github: ai 4 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | permissions: 7 | contents: write 8 | jobs: 9 | release: 10 | name: Release On Tag 11 | if: startsWith(github.ref, 'refs/tags/') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout the repository 15 | uses: actions/checkout@v4 16 | - name: Extract the changelog 17 | id: changelog 18 | run: | 19 | TAG_NAME=${GITHUB_REF/refs\/tags\//} 20 | READ_SECTION=false 21 | CHANGELOG="" 22 | while IFS= read -r line; do 23 | if [[ "$line" =~ ^#+\ +(.*) ]]; then 24 | if [[ "${BASH_REMATCH[1]}" == "$TAG_NAME" ]]; then 25 | READ_SECTION=true 26 | elif [[ "$READ_SECTION" == true ]]; then 27 | break 28 | fi 29 | elif [[ "$READ_SECTION" == true ]]; then 30 | CHANGELOG+="$line"$'\n' 31 | fi 32 | done < "CHANGELOG.md" 33 | CHANGELOG=$(echo "$CHANGELOG" | awk '/./ {$1=$1;print}') 34 | echo "changelog_content<> $GITHUB_OUTPUT 35 | echo "$CHANGELOG" >> $GITHUB_OUTPUT 36 | echo "EOF" >> $GITHUB_OUTPUT 37 | - name: Create the release 38 | if: steps.changelog.outputs.changelog_content != '' 39 | uses: softprops/action-gh-release@v1 40 | with: 41 | name: ${{ github.ref_name }} 42 | body: '${{ steps.changelog.outputs.changelog_content }}' 43 | draft: false 44 | prerelease: false 45 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | permissions: 8 | contents: read 9 | jobs: 10 | full: 11 | name: Node.js Latest Full 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout the repository 15 | uses: actions/checkout@v4 16 | - name: Install pnpm 17 | uses: pnpm/action-setup@v4 18 | with: 19 | version: 10 20 | - name: Install Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: 23 24 | cache: pnpm 25 | - name: Install dependencies 26 | run: pnpm install --frozen-lockfile --ignore-scripts 27 | - name: Run tests 28 | run: pnpm test 29 | short: 30 | runs-on: ubuntu-latest 31 | strategy: 32 | matrix: 33 | node-version: 34 | - 22 35 | - 20 36 | - 18 37 | name: Node.js ${{ matrix.node-version }} Quick 38 | steps: 39 | - name: Checkout the repository 40 | uses: actions/checkout@v4 41 | - name: Install pnpm 42 | uses: pnpm/action-setup@v4 43 | with: 44 | version: 10 45 | - name: Install Node.js ${{ matrix.node-version }} 46 | uses: actions/setup-node@v4 47 | with: 48 | node-version: ${{ matrix.node-version }} 49 | cache: pnpm 50 | - name: Install dependencies 51 | run: pnpm install --frozen-lockfile --ignore-scripts 52 | - name: Run unit tests 53 | run: pnpm unit 54 | old: 55 | runs-on: ubuntu-latest 56 | strategy: 57 | matrix: 58 | node-version: 59 | - 16 60 | - 14 61 | - 12 62 | - 10 63 | name: Node.js ${{ matrix.node-version }} Quick 64 | steps: 65 | - name: Checkout the repository 66 | uses: actions/checkout@v4 67 | - name: Install pnpm 68 | uses: pnpm/action-setup@v1 69 | with: 70 | version: 3 71 | env: 72 | ACTIONS_ALLOW_UNSECURE_COMMANDS: true 73 | - name: Install Node.js ${{ matrix.node-version }} 74 | uses: actions/setup-node@v4 75 | with: 76 | node-version: ${{ matrix.node-version }} 77 | - name: Install dependencies 78 | run: pnpm install --no-frozen-lockfile --ignore-scripts 79 | - name: Run unit tests 80 | run: pnpm unit 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | coverage/ 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | coverage/ 3 | 4 | logo.svg 5 | AUTHORS 6 | eslint.config.mjs 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2013 Andrey Sitnik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /bin/autoprefixer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | let mode = process.argv[2] 4 | if (mode === '--info') { 5 | process.stdout.write(require('../')().info() + '\n') 6 | } else if (mode === '--version') { 7 | process.stdout.write( 8 | 'autoprefixer ' + require('../package.json').version + '\n' 9 | ) 10 | } else { 11 | process.stdout.write( 12 | 'autoprefix\n' + 13 | '\n' + 14 | 'Options:\n' + 15 | ' --info Show target browsers and used prefixes\n' + 16 | ' --version Show version number\n' + 17 | ' --help Show help\n' + 18 | '\n' + 19 | 'Usage:\n' + 20 | ' autoprefixer --info\n' 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import loguxConfig from '@logux/eslint-config' 2 | 3 | export default [ 4 | { 5 | ignores: ['coverage'] 6 | }, 7 | ...loguxConfig, 8 | { 9 | rules: { 10 | 'n/prefer-node-protocol': 'off', 11 | 'no-console': 'off' 12 | } 13 | }, 14 | { 15 | files: ['bin/autoprefixer'], 16 | rules: { 17 | 'n/global-require': 'off', 18 | 'n/no-unsupported-features/es-syntax': 'off' 19 | } 20 | }, 21 | { 22 | files: ['data/prefixes.js'], 23 | rules: { 24 | 'import/order': 'off' 25 | } 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /lib/at-rule.js: -------------------------------------------------------------------------------- 1 | let Prefixer = require('./prefixer') 2 | 3 | class AtRule extends Prefixer { 4 | /** 5 | * Clone and add prefixes for at-rule 6 | */ 7 | add(rule, prefix) { 8 | let prefixed = prefix + rule.name 9 | 10 | let already = rule.parent.some( 11 | i => i.name === prefixed && i.params === rule.params 12 | ) 13 | if (already) { 14 | return undefined 15 | } 16 | 17 | let cloned = this.clone(rule, { name: prefixed }) 18 | return rule.parent.insertBefore(rule, cloned) 19 | } 20 | 21 | /** 22 | * Clone node with prefixes 23 | */ 24 | process(node) { 25 | let parent = this.parentPrefix(node) 26 | 27 | for (let prefix of this.prefixes) { 28 | if (!parent || parent === prefix) { 29 | this.add(node, prefix) 30 | } 31 | } 32 | } 33 | } 34 | 35 | module.exports = AtRule 36 | -------------------------------------------------------------------------------- /lib/autoprefixer.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'postcss' 2 | import { Stats } from 'browserslist' 3 | 4 | declare function autoprefixer( 5 | ...args: [...T, autoprefixer.Options] 6 | ): Plugin & autoprefixer.ExportedAPI 7 | 8 | declare function autoprefixer( 9 | browsers: string[], 10 | options?: autoprefixer.Options 11 | ): Plugin & autoprefixer.ExportedAPI 12 | 13 | declare function autoprefixer( 14 | options?: autoprefixer.Options 15 | ): Plugin & autoprefixer.ExportedAPI 16 | 17 | declare namespace autoprefixer { 18 | type GridValue = 'autoplace' | 'no-autoplace' 19 | 20 | interface Options { 21 | /** environment for `Browserslist` */ 22 | env?: string 23 | 24 | /** should Autoprefixer use Visual Cascade, if CSS is uncompressed */ 25 | cascade?: boolean 26 | 27 | /** should Autoprefixer add prefixes. */ 28 | add?: boolean 29 | 30 | /** should Autoprefixer [remove outdated] prefixes */ 31 | remove?: boolean 32 | 33 | /** should Autoprefixer add prefixes for @supports parameters. */ 34 | supports?: boolean 35 | 36 | /** should Autoprefixer add prefixes for flexbox properties */ 37 | flexbox?: boolean | 'no-2009' 38 | 39 | /** should Autoprefixer add IE 10-11 prefixes for Grid Layout properties */ 40 | grid?: boolean | GridValue 41 | 42 | /** custom usage statistics for > 10% in my stats browsers query */ 43 | stats?: Stats 44 | 45 | /** 46 | * list of queries for target browsers. 47 | * Try to not use it. 48 | * The best practice is to use `.browserslistrc` config or `browserslist` key in `package.json` 49 | * to share target browsers with Babel, ESLint and Stylelint 50 | */ 51 | overrideBrowserslist?: string | string[] 52 | 53 | /** do not raise error on unknown browser version in `Browserslist` config. */ 54 | ignoreUnknownVersions?: boolean 55 | } 56 | 57 | interface ExportedAPI { 58 | /** Autoprefixer data */ 59 | data: { 60 | browsers: { [browser: string]: object | undefined } 61 | prefixes: { [prefixName: string]: object | undefined } 62 | } 63 | 64 | /** Autoprefixer default browsers */ 65 | defaults: string[] 66 | 67 | /** Inspect with default Autoprefixer */ 68 | info(options?: { from?: string }): string 69 | 70 | options: Options 71 | 72 | browsers: string | string[] 73 | } 74 | 75 | /** Autoprefixer data */ 76 | let data: ExportedAPI['data'] 77 | 78 | /** Autoprefixer default browsers */ 79 | let defaults: ExportedAPI['defaults'] 80 | 81 | /** Inspect with default Autoprefixer */ 82 | let info: ExportedAPI['info'] 83 | 84 | let postcss: true 85 | } 86 | 87 | declare global { 88 | namespace NodeJS { 89 | interface ProcessEnv { 90 | AUTOPREFIXER_GRID?: autoprefixer.GridValue 91 | } 92 | } 93 | } 94 | 95 | export = autoprefixer 96 | -------------------------------------------------------------------------------- /lib/brackets.js: -------------------------------------------------------------------------------- 1 | function last(array) { 2 | return array[array.length - 1] 3 | } 4 | 5 | let brackets = { 6 | /** 7 | * Parse string to nodes tree 8 | */ 9 | parse(str) { 10 | let current = [''] 11 | let stack = [current] 12 | 13 | for (let sym of str) { 14 | if (sym === '(') { 15 | current = [''] 16 | last(stack).push(current) 17 | stack.push(current) 18 | continue 19 | } 20 | 21 | if (sym === ')') { 22 | stack.pop() 23 | current = last(stack) 24 | current.push('') 25 | continue 26 | } 27 | 28 | current[current.length - 1] += sym 29 | } 30 | 31 | return stack[0] 32 | }, 33 | 34 | /** 35 | * Generate output string by nodes tree 36 | */ 37 | stringify(ast) { 38 | let result = '' 39 | for (let i of ast) { 40 | if (typeof i === 'object') { 41 | result += `(${brackets.stringify(i)})` 42 | continue 43 | } 44 | 45 | result += i 46 | } 47 | return result 48 | } 49 | } 50 | 51 | module.exports = brackets 52 | -------------------------------------------------------------------------------- /lib/browsers.js: -------------------------------------------------------------------------------- 1 | let browserslist = require('browserslist') 2 | let { agents } = require('caniuse-lite/dist/unpacker/agents') 3 | 4 | let utils = require('./utils') 5 | 6 | class Browsers { 7 | constructor(data, requirements, options, browserslistOpts) { 8 | this.data = data 9 | this.options = options || {} 10 | this.browserslistOpts = browserslistOpts || {} 11 | this.selected = this.parse(requirements) 12 | } 13 | 14 | /** 15 | * Return all prefixes for default browser data 16 | */ 17 | static prefixes() { 18 | if (this.prefixesCache) { 19 | return this.prefixesCache 20 | } 21 | 22 | this.prefixesCache = [] 23 | for (let name in agents) { 24 | this.prefixesCache.push(`-${agents[name].prefix}-`) 25 | } 26 | 27 | this.prefixesCache = utils 28 | .uniq(this.prefixesCache) 29 | .sort((a, b) => b.length - a.length) 30 | 31 | return this.prefixesCache 32 | } 33 | 34 | /** 35 | * Check is value contain any possible prefix 36 | */ 37 | static withPrefix(value) { 38 | if (!this.prefixesRegexp) { 39 | this.prefixesRegexp = new RegExp(this.prefixes().join('|')) 40 | } 41 | 42 | return this.prefixesRegexp.test(value) 43 | } 44 | 45 | /** 46 | * Is browser is selected by requirements 47 | */ 48 | isSelected(browser) { 49 | return this.selected.includes(browser) 50 | } 51 | 52 | /** 53 | * Return browsers selected by requirements 54 | */ 55 | parse(requirements) { 56 | let opts = {} 57 | for (let i in this.browserslistOpts) { 58 | opts[i] = this.browserslistOpts[i] 59 | } 60 | opts.path = this.options.from 61 | return browserslist(requirements, opts) 62 | } 63 | 64 | /** 65 | * Return prefix for selected browser 66 | */ 67 | prefix(browser) { 68 | let [name, version] = browser.split(' ') 69 | let data = this.data[name] 70 | 71 | let prefix = data.prefix_exceptions && data.prefix_exceptions[version] 72 | if (!prefix) { 73 | prefix = data.prefix 74 | } 75 | return `-${prefix}-` 76 | } 77 | } 78 | 79 | module.exports = Browsers 80 | -------------------------------------------------------------------------------- /lib/hacks/align-content.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class AlignContent extends Declaration { 5 | /** 6 | * Return property name by final spec 7 | */ 8 | normalize() { 9 | return 'align-content' 10 | } 11 | 12 | /** 13 | * Change property name for 2012 spec 14 | */ 15 | prefixed(prop, prefix) { 16 | let spec 17 | ;[spec, prefix] = flexSpec(prefix) 18 | if (spec === 2012) { 19 | return prefix + 'flex-line-pack' 20 | } 21 | return super.prefixed(prop, prefix) 22 | } 23 | 24 | /** 25 | * Change value for 2012 spec and ignore prefix for 2009 26 | */ 27 | set(decl, prefix) { 28 | let spec = flexSpec(prefix)[0] 29 | if (spec === 2012) { 30 | decl.value = AlignContent.oldValues[decl.value] || decl.value 31 | return super.set(decl, prefix) 32 | } 33 | if (spec === 'final') { 34 | return super.set(decl, prefix) 35 | } 36 | return undefined 37 | } 38 | } 39 | 40 | AlignContent.names = ['align-content', 'flex-line-pack'] 41 | 42 | AlignContent.oldValues = { 43 | 'flex-end': 'end', 44 | 'flex-start': 'start', 45 | 'space-around': 'distribute', 46 | 'space-between': 'justify' 47 | } 48 | 49 | module.exports = AlignContent 50 | -------------------------------------------------------------------------------- /lib/hacks/align-items.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class AlignItems extends Declaration { 5 | /** 6 | * Return property name by final spec 7 | */ 8 | normalize() { 9 | return 'align-items' 10 | } 11 | 12 | /** 13 | * Change property name for 2009 and 2012 specs 14 | */ 15 | prefixed(prop, prefix) { 16 | let spec 17 | ;[spec, prefix] = flexSpec(prefix) 18 | if (spec === 2009) { 19 | return prefix + 'box-align' 20 | } 21 | if (spec === 2012) { 22 | return prefix + 'flex-align' 23 | } 24 | return super.prefixed(prop, prefix) 25 | } 26 | 27 | /** 28 | * Change value for 2009 and 2012 specs 29 | */ 30 | set(decl, prefix) { 31 | let spec = flexSpec(prefix)[0] 32 | if (spec === 2009 || spec === 2012) { 33 | decl.value = AlignItems.oldValues[decl.value] || decl.value 34 | } 35 | return super.set(decl, prefix) 36 | } 37 | } 38 | 39 | AlignItems.names = ['align-items', 'flex-align', 'box-align'] 40 | 41 | AlignItems.oldValues = { 42 | 'flex-end': 'end', 43 | 'flex-start': 'start' 44 | } 45 | 46 | module.exports = AlignItems 47 | -------------------------------------------------------------------------------- /lib/hacks/align-self.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class AlignSelf extends Declaration { 5 | check(decl) { 6 | return ( 7 | decl.parent && 8 | !decl.parent.some(i => { 9 | return i.prop && i.prop.startsWith('grid-') 10 | }) 11 | ) 12 | } 13 | 14 | /** 15 | * Return property name by final spec 16 | */ 17 | normalize() { 18 | return 'align-self' 19 | } 20 | 21 | /** 22 | * Change property name for 2012 specs 23 | */ 24 | prefixed(prop, prefix) { 25 | let spec 26 | ;[spec, prefix] = flexSpec(prefix) 27 | if (spec === 2012) { 28 | return prefix + 'flex-item-align' 29 | } 30 | return super.prefixed(prop, prefix) 31 | } 32 | 33 | /** 34 | * Change value for 2012 spec and ignore prefix for 2009 35 | */ 36 | set(decl, prefix) { 37 | let spec = flexSpec(prefix)[0] 38 | if (spec === 2012) { 39 | decl.value = AlignSelf.oldValues[decl.value] || decl.value 40 | return super.set(decl, prefix) 41 | } 42 | if (spec === 'final') { 43 | return super.set(decl, prefix) 44 | } 45 | return undefined 46 | } 47 | } 48 | 49 | AlignSelf.names = ['align-self', 'flex-item-align'] 50 | 51 | AlignSelf.oldValues = { 52 | 'flex-end': 'end', 53 | 'flex-start': 'start' 54 | } 55 | 56 | module.exports = AlignSelf 57 | -------------------------------------------------------------------------------- /lib/hacks/animation.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class Animation extends Declaration { 4 | /** 5 | * Don’t add prefixes for modern values. 6 | */ 7 | check(decl) { 8 | return !decl.value.split(/\s+/).some(i => { 9 | let lower = i.toLowerCase() 10 | return lower === 'reverse' || lower === 'alternate-reverse' 11 | }) 12 | } 13 | } 14 | 15 | Animation.names = ['animation', 'animation-direction'] 16 | 17 | module.exports = Animation 18 | -------------------------------------------------------------------------------- /lib/hacks/appearance.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let utils = require('../utils') 3 | 4 | class Appearance extends Declaration { 5 | constructor(name, prefixes, all) { 6 | super(name, prefixes, all) 7 | 8 | if (this.prefixes) { 9 | this.prefixes = utils.uniq( 10 | this.prefixes.map(i => { 11 | if (i === '-ms-') { 12 | return '-webkit-' 13 | } 14 | return i 15 | }) 16 | ) 17 | } 18 | } 19 | } 20 | 21 | Appearance.names = ['appearance'] 22 | 23 | module.exports = Appearance 24 | -------------------------------------------------------------------------------- /lib/hacks/autofill.js: -------------------------------------------------------------------------------- 1 | let Selector = require('../selector') 2 | let utils = require('../utils') 3 | 4 | class Autofill extends Selector { 5 | constructor(name, prefixes, all) { 6 | super(name, prefixes, all) 7 | 8 | if (this.prefixes) { 9 | this.prefixes = utils.uniq(this.prefixes.map(() => '-webkit-')) 10 | } 11 | } 12 | 13 | /** 14 | * Return different selectors depend on prefix 15 | */ 16 | prefixed(prefix) { 17 | if (prefix === '-webkit-') { 18 | return ':-webkit-autofill' 19 | } 20 | return `:${prefix}autofill` 21 | } 22 | } 23 | 24 | Autofill.names = [':autofill'] 25 | 26 | module.exports = Autofill 27 | -------------------------------------------------------------------------------- /lib/hacks/backdrop-filter.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let utils = require('../utils') 3 | 4 | class BackdropFilter extends Declaration { 5 | constructor(name, prefixes, all) { 6 | super(name, prefixes, all) 7 | 8 | if (this.prefixes) { 9 | this.prefixes = utils.uniq( 10 | this.prefixes.map(i => { 11 | return i === '-ms-' ? '-webkit-' : i 12 | }) 13 | ) 14 | } 15 | } 16 | } 17 | 18 | BackdropFilter.names = ['backdrop-filter'] 19 | 20 | module.exports = BackdropFilter 21 | -------------------------------------------------------------------------------- /lib/hacks/background-clip.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let utils = require('../utils') 3 | 4 | class BackgroundClip extends Declaration { 5 | constructor(name, prefixes, all) { 6 | super(name, prefixes, all) 7 | 8 | if (this.prefixes) { 9 | this.prefixes = utils.uniq( 10 | this.prefixes.map(i => { 11 | return i === '-ms-' ? '-webkit-' : i 12 | }) 13 | ) 14 | } 15 | } 16 | 17 | check(decl) { 18 | return decl.value.toLowerCase() === 'text' 19 | } 20 | } 21 | 22 | BackgroundClip.names = ['background-clip'] 23 | 24 | module.exports = BackgroundClip 25 | -------------------------------------------------------------------------------- /lib/hacks/background-size.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class BackgroundSize extends Declaration { 4 | /** 5 | * Duplication parameter for -webkit- browsers 6 | */ 7 | set(decl, prefix) { 8 | let value = decl.value.toLowerCase() 9 | if ( 10 | prefix === '-webkit-' && 11 | !value.includes(' ') && 12 | value !== 'contain' && 13 | value !== 'cover' 14 | ) { 15 | decl.value = decl.value + ' ' + decl.value 16 | } 17 | return super.set(decl, prefix) 18 | } 19 | } 20 | 21 | BackgroundSize.names = ['background-size'] 22 | 23 | module.exports = BackgroundSize 24 | -------------------------------------------------------------------------------- /lib/hacks/block-logical.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class BlockLogical extends Declaration { 4 | /** 5 | * Return property name by spec 6 | */ 7 | normalize(prop) { 8 | if (prop.includes('-before')) { 9 | return prop.replace('-before', '-block-start') 10 | } 11 | return prop.replace('-after', '-block-end') 12 | } 13 | 14 | /** 15 | * Use old syntax for -moz- and -webkit- 16 | */ 17 | prefixed(prop, prefix) { 18 | if (prop.includes('-start')) { 19 | return prefix + prop.replace('-block-start', '-before') 20 | } 21 | return prefix + prop.replace('-block-end', '-after') 22 | } 23 | } 24 | 25 | BlockLogical.names = [ 26 | 'border-block-start', 27 | 'border-block-end', 28 | 'margin-block-start', 29 | 'margin-block-end', 30 | 'padding-block-start', 31 | 'padding-block-end', 32 | 'border-before', 33 | 'border-after', 34 | 'margin-before', 35 | 'margin-after', 36 | 'padding-before', 37 | 'padding-after' 38 | ] 39 | 40 | module.exports = BlockLogical 41 | -------------------------------------------------------------------------------- /lib/hacks/border-image.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class BorderImage extends Declaration { 4 | /** 5 | * Remove fill parameter for prefixed declarations 6 | */ 7 | set(decl, prefix) { 8 | decl.value = decl.value.replace(/\s+fill(\s)/, '$1') 9 | return super.set(decl, prefix) 10 | } 11 | } 12 | 13 | BorderImage.names = ['border-image'] 14 | 15 | module.exports = BorderImage 16 | -------------------------------------------------------------------------------- /lib/hacks/border-radius.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class BorderRadius extends Declaration { 4 | /** 5 | * Return unprefixed version of property 6 | */ 7 | normalize(prop) { 8 | return BorderRadius.toNormal[prop] || prop 9 | } 10 | 11 | /** 12 | * Change syntax, when add Mozilla prefix 13 | */ 14 | prefixed(prop, prefix) { 15 | if (prefix === '-moz-') { 16 | return prefix + (BorderRadius.toMozilla[prop] || prop) 17 | } 18 | return super.prefixed(prop, prefix) 19 | } 20 | } 21 | 22 | BorderRadius.names = ['border-radius'] 23 | 24 | BorderRadius.toMozilla = {} 25 | BorderRadius.toNormal = {} 26 | 27 | for (let ver of ['top', 'bottom']) { 28 | for (let hor of ['left', 'right']) { 29 | let normal = `border-${ver}-${hor}-radius` 30 | let mozilla = `border-radius-${ver}${hor}` 31 | 32 | BorderRadius.names.push(normal) 33 | BorderRadius.names.push(mozilla) 34 | 35 | BorderRadius.toMozilla[normal] = mozilla 36 | BorderRadius.toNormal[mozilla] = normal 37 | } 38 | } 39 | 40 | module.exports = BorderRadius 41 | -------------------------------------------------------------------------------- /lib/hacks/break-props.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class BreakProps extends Declaration { 4 | /** 5 | * Don’t prefix some values 6 | */ 7 | insert(decl, prefix, prefixes) { 8 | if (decl.prop !== 'break-inside') { 9 | return super.insert(decl, prefix, prefixes) 10 | } 11 | if (/region/i.test(decl.value) || /page/i.test(decl.value)) { 12 | return undefined 13 | } 14 | return super.insert(decl, prefix, prefixes) 15 | } 16 | 17 | /** 18 | * Return property name by final spec 19 | */ 20 | normalize(prop) { 21 | if (prop.includes('inside')) { 22 | return 'break-inside' 23 | } 24 | if (prop.includes('before')) { 25 | return 'break-before' 26 | } 27 | return 'break-after' 28 | } 29 | 30 | /** 31 | * Change name for -webkit- and -moz- prefix 32 | */ 33 | prefixed(prop, prefix) { 34 | return `${prefix}column-${prop}` 35 | } 36 | 37 | /** 38 | * Change prefixed value for avoid-column and avoid-page 39 | */ 40 | set(decl, prefix) { 41 | if ( 42 | (decl.prop === 'break-inside' && decl.value === 'avoid-column') || 43 | decl.value === 'avoid-page' 44 | ) { 45 | decl.value = 'avoid' 46 | } 47 | return super.set(decl, prefix) 48 | } 49 | } 50 | 51 | BreakProps.names = [ 52 | 'break-inside', 53 | 'page-break-inside', 54 | 'column-break-inside', 55 | 'break-before', 56 | 'page-break-before', 57 | 'column-break-before', 58 | 'break-after', 59 | 'page-break-after', 60 | 'column-break-after' 61 | ] 62 | 63 | module.exports = BreakProps 64 | -------------------------------------------------------------------------------- /lib/hacks/cross-fade.js: -------------------------------------------------------------------------------- 1 | let list = require('postcss').list 2 | 3 | let Value = require('../value') 4 | 5 | class CrossFade extends Value { 6 | replace(string, prefix) { 7 | return list 8 | .space(string) 9 | .map(value => { 10 | if (value.slice(0, +this.name.length + 1) !== this.name + '(') { 11 | return value 12 | } 13 | 14 | let close = value.lastIndexOf(')') 15 | let after = value.slice(close + 1) 16 | let args = value.slice(this.name.length + 1, close) 17 | 18 | if (prefix === '-webkit-') { 19 | let match = args.match(/\d*.?\d+%?/) 20 | if (match) { 21 | args = args.slice(match[0].length).trim() 22 | args += `, ${match[0]}` 23 | } else { 24 | args += ', 0.5' 25 | } 26 | } 27 | return prefix + this.name + '(' + args + ')' + after 28 | }) 29 | .join(' ') 30 | } 31 | } 32 | 33 | CrossFade.names = ['cross-fade'] 34 | 35 | module.exports = CrossFade 36 | -------------------------------------------------------------------------------- /lib/hacks/display-flex.js: -------------------------------------------------------------------------------- 1 | let OldValue = require('../old-value') 2 | let Value = require('../value') 3 | let flexSpec = require('./flex-spec') 4 | 5 | class DisplayFlex extends Value { 6 | constructor(name, prefixes) { 7 | super(name, prefixes) 8 | if (name === 'display-flex') { 9 | this.name = 'flex' 10 | } 11 | } 12 | 13 | /** 14 | * Faster check for flex value 15 | */ 16 | check(decl) { 17 | return decl.prop === 'display' && decl.value === this.name 18 | } 19 | 20 | /** 21 | * Change value for old specs 22 | */ 23 | old(prefix) { 24 | let prefixed = this.prefixed(prefix) 25 | if (!prefixed) return undefined 26 | return new OldValue(this.name, prefixed) 27 | } 28 | 29 | /** 30 | * Return value by spec 31 | */ 32 | prefixed(prefix) { 33 | let spec, value 34 | ;[spec, prefix] = flexSpec(prefix) 35 | 36 | if (spec === 2009) { 37 | if (this.name === 'flex') { 38 | value = 'box' 39 | } else { 40 | value = 'inline-box' 41 | } 42 | } else if (spec === 2012) { 43 | if (this.name === 'flex') { 44 | value = 'flexbox' 45 | } else { 46 | value = 'inline-flexbox' 47 | } 48 | } else if (spec === 'final') { 49 | value = this.name 50 | } 51 | 52 | return prefix + value 53 | } 54 | 55 | /** 56 | * Add prefix to value depend on flebox spec version 57 | */ 58 | replace(string, prefix) { 59 | return this.prefixed(prefix) 60 | } 61 | } 62 | 63 | DisplayFlex.names = ['display-flex', 'inline-flex'] 64 | 65 | module.exports = DisplayFlex 66 | -------------------------------------------------------------------------------- /lib/hacks/display-grid.js: -------------------------------------------------------------------------------- 1 | let Value = require('../value') 2 | 3 | class DisplayGrid extends Value { 4 | constructor(name, prefixes) { 5 | super(name, prefixes) 6 | if (name === 'display-grid') { 7 | this.name = 'grid' 8 | } 9 | } 10 | 11 | /** 12 | * Faster check for flex value 13 | */ 14 | check(decl) { 15 | return decl.prop === 'display' && decl.value === this.name 16 | } 17 | } 18 | 19 | DisplayGrid.names = ['display-grid', 'inline-grid'] 20 | 21 | module.exports = DisplayGrid 22 | -------------------------------------------------------------------------------- /lib/hacks/file-selector-button.js: -------------------------------------------------------------------------------- 1 | let Selector = require('../selector') 2 | let utils = require('../utils') 3 | 4 | class FileSelectorButton extends Selector { 5 | constructor(name, prefixes, all) { 6 | super(name, prefixes, all) 7 | 8 | if (this.prefixes) { 9 | this.prefixes = utils.uniq(this.prefixes.map(() => '-webkit-')) 10 | } 11 | } 12 | 13 | /** 14 | * Return different selectors depend on prefix 15 | */ 16 | prefixed(prefix) { 17 | if (prefix === '-webkit-') { 18 | return '::-webkit-file-upload-button' 19 | } 20 | return `::${prefix}file-selector-button` 21 | } 22 | } 23 | 24 | FileSelectorButton.names = ['::file-selector-button'] 25 | 26 | module.exports = FileSelectorButton 27 | -------------------------------------------------------------------------------- /lib/hacks/filter-value.js: -------------------------------------------------------------------------------- 1 | let Value = require('../value') 2 | 3 | class FilterValue extends Value { 4 | constructor(name, prefixes) { 5 | super(name, prefixes) 6 | if (name === 'filter-function') { 7 | this.name = 'filter' 8 | } 9 | } 10 | } 11 | 12 | FilterValue.names = ['filter', 'filter-function'] 13 | 14 | module.exports = FilterValue 15 | -------------------------------------------------------------------------------- /lib/hacks/filter.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class Filter extends Declaration { 4 | /** 5 | * Check is it Internet Explorer filter 6 | */ 7 | check(decl) { 8 | let v = decl.value 9 | return ( 10 | !v.toLowerCase().includes('alpha(') && 11 | !v.includes('DXImageTransform.Microsoft') && 12 | !v.includes('data:image/svg+xml') 13 | ) 14 | } 15 | } 16 | 17 | Filter.names = ['filter'] 18 | 19 | module.exports = Filter 20 | -------------------------------------------------------------------------------- /lib/hacks/flex-basis.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class FlexBasis extends Declaration { 5 | /** 6 | * Return property name by final spec 7 | */ 8 | normalize() { 9 | return 'flex-basis' 10 | } 11 | 12 | /** 13 | * Return flex property for 2012 spec 14 | */ 15 | prefixed(prop, prefix) { 16 | let spec 17 | ;[spec, prefix] = flexSpec(prefix) 18 | if (spec === 2012) { 19 | return prefix + 'flex-preferred-size' 20 | } 21 | return super.prefixed(prop, prefix) 22 | } 23 | 24 | /** 25 | * Ignore 2009 spec and use flex property for 2012 26 | */ 27 | set(decl, prefix) { 28 | let spec 29 | ;[spec, prefix] = flexSpec(prefix) 30 | if (spec === 2012 || spec === 'final') { 31 | return super.set(decl, prefix) 32 | } 33 | return undefined 34 | } 35 | } 36 | 37 | FlexBasis.names = ['flex-basis', 'flex-preferred-size'] 38 | 39 | module.exports = FlexBasis 40 | -------------------------------------------------------------------------------- /lib/hacks/flex-direction.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class FlexDirection extends Declaration { 5 | /** 6 | * Use two properties for 2009 spec 7 | */ 8 | insert(decl, prefix, prefixes) { 9 | let spec 10 | ;[spec, prefix] = flexSpec(prefix) 11 | if (spec !== 2009) { 12 | return super.insert(decl, prefix, prefixes) 13 | } 14 | let already = decl.parent.some( 15 | i => 16 | i.prop === prefix + 'box-orient' || i.prop === prefix + 'box-direction' 17 | ) 18 | if (already) { 19 | return undefined 20 | } 21 | 22 | let v = decl.value 23 | let dir, orient 24 | if (v === 'inherit' || v === 'initial' || v === 'unset') { 25 | orient = v 26 | dir = v 27 | } else { 28 | orient = v.includes('row') ? 'horizontal' : 'vertical' 29 | dir = v.includes('reverse') ? 'reverse' : 'normal' 30 | } 31 | 32 | let cloned = this.clone(decl) 33 | cloned.prop = prefix + 'box-orient' 34 | cloned.value = orient 35 | if (this.needCascade(decl)) { 36 | cloned.raws.before = this.calcBefore(prefixes, decl, prefix) 37 | } 38 | decl.parent.insertBefore(decl, cloned) 39 | 40 | cloned = this.clone(decl) 41 | cloned.prop = prefix + 'box-direction' 42 | cloned.value = dir 43 | if (this.needCascade(decl)) { 44 | cloned.raws.before = this.calcBefore(prefixes, decl, prefix) 45 | } 46 | return decl.parent.insertBefore(decl, cloned) 47 | } 48 | 49 | /** 50 | * Return property name by final spec 51 | */ 52 | normalize() { 53 | return 'flex-direction' 54 | } 55 | 56 | /** 57 | * Clean two properties for 2009 spec 58 | */ 59 | old(prop, prefix) { 60 | let spec 61 | ;[spec, prefix] = flexSpec(prefix) 62 | if (spec === 2009) { 63 | return [prefix + 'box-orient', prefix + 'box-direction'] 64 | } else { 65 | return super.old(prop, prefix) 66 | } 67 | } 68 | } 69 | 70 | FlexDirection.names = ['flex-direction', 'box-direction', 'box-orient'] 71 | 72 | module.exports = FlexDirection 73 | -------------------------------------------------------------------------------- /lib/hacks/flex-flow.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class FlexFlow extends Declaration { 5 | /** 6 | * Use two properties for 2009 spec 7 | */ 8 | insert(decl, prefix, prefixes) { 9 | let spec 10 | ;[spec, prefix] = flexSpec(prefix) 11 | if (spec !== 2009) { 12 | return super.insert(decl, prefix, prefixes) 13 | } 14 | let values = decl.value 15 | .split(/\s+/) 16 | .filter(i => i !== 'wrap' && i !== 'nowrap' && 'wrap-reverse') 17 | if (values.length === 0) { 18 | return undefined 19 | } 20 | 21 | let already = decl.parent.some( 22 | i => 23 | i.prop === prefix + 'box-orient' || i.prop === prefix + 'box-direction' 24 | ) 25 | if (already) { 26 | return undefined 27 | } 28 | 29 | let value = values[0] 30 | let orient = value.includes('row') ? 'horizontal' : 'vertical' 31 | let dir = value.includes('reverse') ? 'reverse' : 'normal' 32 | 33 | let cloned = this.clone(decl) 34 | cloned.prop = prefix + 'box-orient' 35 | cloned.value = orient 36 | if (this.needCascade(decl)) { 37 | cloned.raws.before = this.calcBefore(prefixes, decl, prefix) 38 | } 39 | decl.parent.insertBefore(decl, cloned) 40 | 41 | cloned = this.clone(decl) 42 | cloned.prop = prefix + 'box-direction' 43 | cloned.value = dir 44 | if (this.needCascade(decl)) { 45 | cloned.raws.before = this.calcBefore(prefixes, decl, prefix) 46 | } 47 | return decl.parent.insertBefore(decl, cloned) 48 | } 49 | } 50 | 51 | FlexFlow.names = ['flex-flow', 'box-direction', 'box-orient'] 52 | 53 | module.exports = FlexFlow 54 | -------------------------------------------------------------------------------- /lib/hacks/flex-grow.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class Flex extends Declaration { 5 | /** 6 | * Return property name by final spec 7 | */ 8 | normalize() { 9 | return 'flex' 10 | } 11 | 12 | /** 13 | * Return flex property for 2009 and 2012 specs 14 | */ 15 | prefixed(prop, prefix) { 16 | let spec 17 | ;[spec, prefix] = flexSpec(prefix) 18 | if (spec === 2009) { 19 | return prefix + 'box-flex' 20 | } 21 | if (spec === 2012) { 22 | return prefix + 'flex-positive' 23 | } 24 | return super.prefixed(prop, prefix) 25 | } 26 | } 27 | 28 | Flex.names = ['flex-grow', 'flex-positive'] 29 | 30 | module.exports = Flex 31 | -------------------------------------------------------------------------------- /lib/hacks/flex-shrink.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class FlexShrink extends Declaration { 5 | /** 6 | * Return property name by final spec 7 | */ 8 | normalize() { 9 | return 'flex-shrink' 10 | } 11 | 12 | /** 13 | * Return flex property for 2012 spec 14 | */ 15 | prefixed(prop, prefix) { 16 | let spec 17 | ;[spec, prefix] = flexSpec(prefix) 18 | if (spec === 2012) { 19 | return prefix + 'flex-negative' 20 | } 21 | return super.prefixed(prop, prefix) 22 | } 23 | 24 | /** 25 | * Ignore 2009 spec and use flex property for 2012 26 | */ 27 | set(decl, prefix) { 28 | let spec 29 | ;[spec, prefix] = flexSpec(prefix) 30 | if (spec === 2012 || spec === 'final') { 31 | return super.set(decl, prefix) 32 | } 33 | return undefined 34 | } 35 | } 36 | 37 | FlexShrink.names = ['flex-shrink', 'flex-negative'] 38 | 39 | module.exports = FlexShrink 40 | -------------------------------------------------------------------------------- /lib/hacks/flex-spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Return flexbox spec versions by prefix 3 | */ 4 | module.exports = function (prefix) { 5 | let spec 6 | if (prefix === '-webkit- 2009' || prefix === '-moz-') { 7 | spec = 2009 8 | } else if (prefix === '-ms-') { 9 | spec = 2012 10 | } else if (prefix === '-webkit-') { 11 | spec = 'final' 12 | } 13 | 14 | if (prefix === '-webkit- 2009') { 15 | prefix = '-webkit-' 16 | } 17 | 18 | return [spec, prefix] 19 | } 20 | -------------------------------------------------------------------------------- /lib/hacks/flex-wrap.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class FlexWrap extends Declaration { 5 | /** 6 | * Don't add prefix for 2009 spec 7 | */ 8 | set(decl, prefix) { 9 | let spec = flexSpec(prefix)[0] 10 | if (spec !== 2009) { 11 | return super.set(decl, prefix) 12 | } 13 | return undefined 14 | } 15 | } 16 | 17 | FlexWrap.names = ['flex-wrap'] 18 | 19 | module.exports = FlexWrap 20 | -------------------------------------------------------------------------------- /lib/hacks/flex.js: -------------------------------------------------------------------------------- 1 | let list = require('postcss').list 2 | 3 | let Declaration = require('../declaration') 4 | let flexSpec = require('./flex-spec') 5 | 6 | class Flex extends Declaration { 7 | /** 8 | * Return property name by final spec 9 | */ 10 | normalize() { 11 | return 'flex' 12 | } 13 | 14 | /** 15 | * Change property name for 2009 spec 16 | */ 17 | prefixed(prop, prefix) { 18 | let spec 19 | ;[spec, prefix] = flexSpec(prefix) 20 | if (spec === 2009) { 21 | return prefix + 'box-flex' 22 | } 23 | return super.prefixed(prop, prefix) 24 | } 25 | 26 | /** 27 | * Spec 2009 supports only first argument 28 | * Spec 2012 disallows unitless basis 29 | */ 30 | set(decl, prefix) { 31 | let spec = flexSpec(prefix)[0] 32 | if (spec === 2009) { 33 | decl.value = list.space(decl.value)[0] 34 | decl.value = Flex.oldValues[decl.value] || decl.value 35 | return super.set(decl, prefix) 36 | } 37 | if (spec === 2012) { 38 | let components = list.space(decl.value) 39 | if (components.length === 3 && components[2] === '0') { 40 | decl.value = components.slice(0, 2).concat('0px').join(' ') 41 | } 42 | } 43 | return super.set(decl, prefix) 44 | } 45 | } 46 | 47 | Flex.names = ['flex', 'box-flex'] 48 | 49 | Flex.oldValues = { 50 | auto: '1', 51 | none: '0' 52 | } 53 | 54 | module.exports = Flex 55 | -------------------------------------------------------------------------------- /lib/hacks/fullscreen.js: -------------------------------------------------------------------------------- 1 | let Selector = require('../selector') 2 | 3 | class Fullscreen extends Selector { 4 | /** 5 | * Return different selectors depend on prefix 6 | */ 7 | prefixed(prefix) { 8 | if (prefix === '-webkit-') { 9 | return ':-webkit-full-screen' 10 | } 11 | if (prefix === '-moz-') { 12 | return ':-moz-full-screen' 13 | } 14 | return `:${prefix}fullscreen` 15 | } 16 | } 17 | 18 | Fullscreen.names = [':fullscreen'] 19 | 20 | module.exports = Fullscreen 21 | -------------------------------------------------------------------------------- /lib/hacks/grid-area.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let utils = require('./grid-utils') 3 | 4 | class GridArea extends Declaration { 5 | /** 6 | * Translate grid-area to separate -ms- prefixed properties 7 | */ 8 | insert(decl, prefix, prefixes, result) { 9 | if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) 10 | 11 | let values = utils.parse(decl) 12 | 13 | let [rowStart, rowSpan] = utils.translate(values, 0, 2) 14 | let [columnStart, columnSpan] = utils.translate(values, 1, 3) 15 | 16 | ;[ 17 | ['grid-row', rowStart], 18 | ['grid-row-span', rowSpan], 19 | ['grid-column', columnStart], 20 | ['grid-column-span', columnSpan] 21 | ].forEach(([prop, value]) => { 22 | utils.insertDecl(decl, prop, value) 23 | }) 24 | 25 | utils.warnTemplateSelectorNotFound(decl, result) 26 | utils.warnIfGridRowColumnExists(decl, result) 27 | 28 | return undefined 29 | } 30 | } 31 | 32 | GridArea.names = ['grid-area'] 33 | 34 | module.exports = GridArea 35 | -------------------------------------------------------------------------------- /lib/hacks/grid-column-align.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class GridColumnAlign extends Declaration { 4 | /** 5 | * Do not prefix flexbox values 6 | */ 7 | check(decl) { 8 | return !decl.value.includes('flex-') && decl.value !== 'baseline' 9 | } 10 | 11 | /** 12 | * Change IE property back 13 | */ 14 | normalize() { 15 | return 'justify-self' 16 | } 17 | 18 | /** 19 | * Change property name for IE 20 | */ 21 | prefixed(prop, prefix) { 22 | return prefix + 'grid-column-align' 23 | } 24 | } 25 | 26 | GridColumnAlign.names = ['grid-column-align'] 27 | 28 | module.exports = GridColumnAlign 29 | -------------------------------------------------------------------------------- /lib/hacks/grid-end.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let { isPureNumber } = require('../utils') 3 | 4 | class GridEnd extends Declaration { 5 | /** 6 | * Change repeating syntax for IE 7 | */ 8 | insert(decl, prefix, prefixes, result) { 9 | if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) 10 | 11 | let clonedDecl = this.clone(decl) 12 | 13 | let startProp = decl.prop.replace(/end$/, 'start') 14 | let spanProp = prefix + decl.prop.replace(/end$/, 'span') 15 | 16 | if (decl.parent.some(i => i.prop === spanProp)) { 17 | return undefined 18 | } 19 | 20 | clonedDecl.prop = spanProp 21 | 22 | if (decl.value.includes('span')) { 23 | clonedDecl.value = decl.value.replace(/span\s/i, '') 24 | } else { 25 | let startDecl 26 | decl.parent.walkDecls(startProp, d => { 27 | startDecl = d 28 | }) 29 | if (startDecl) { 30 | if (isPureNumber(startDecl.value)) { 31 | let value = Number(decl.value) - Number(startDecl.value) + '' 32 | clonedDecl.value = value 33 | } else { 34 | return undefined 35 | } 36 | } else { 37 | decl.warn( 38 | result, 39 | `Can not prefix ${decl.prop} (${startProp} is not found)` 40 | ) 41 | } 42 | } 43 | 44 | decl.cloneBefore(clonedDecl) 45 | 46 | return undefined 47 | } 48 | } 49 | 50 | GridEnd.names = ['grid-row-end', 'grid-column-end'] 51 | 52 | module.exports = GridEnd 53 | -------------------------------------------------------------------------------- /lib/hacks/grid-row-align.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class GridRowAlign extends Declaration { 4 | /** 5 | * Do not prefix flexbox values 6 | */ 7 | check(decl) { 8 | return !decl.value.includes('flex-') && decl.value !== 'baseline' 9 | } 10 | 11 | /** 12 | * Change IE property back 13 | */ 14 | normalize() { 15 | return 'align-self' 16 | } 17 | 18 | /** 19 | * Change property name for IE 20 | */ 21 | prefixed(prop, prefix) { 22 | return prefix + 'grid-row-align' 23 | } 24 | } 25 | 26 | GridRowAlign.names = ['grid-row-align'] 27 | 28 | module.exports = GridRowAlign 29 | -------------------------------------------------------------------------------- /lib/hacks/grid-row-column.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let utils = require('./grid-utils') 3 | 4 | class GridRowColumn extends Declaration { 5 | /** 6 | * Translate grid-row / grid-column to separate -ms- prefixed properties 7 | */ 8 | insert(decl, prefix, prefixes) { 9 | if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) 10 | 11 | let values = utils.parse(decl) 12 | let [start, span] = utils.translate(values, 0, 1) 13 | 14 | let hasStartValueSpan = values[0] && values[0].includes('span') 15 | 16 | if (hasStartValueSpan) { 17 | span = values[0].join('').replace(/\D/g, '') 18 | } 19 | 20 | ;[ 21 | [decl.prop, start], 22 | [`${decl.prop}-span`, span] 23 | ].forEach(([prop, value]) => { 24 | utils.insertDecl(decl, prop, value) 25 | }) 26 | 27 | return undefined 28 | } 29 | } 30 | 31 | GridRowColumn.names = ['grid-row', 'grid-column'] 32 | 33 | module.exports = GridRowColumn 34 | -------------------------------------------------------------------------------- /lib/hacks/grid-rows-columns.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let Processor = require('../processor') 3 | let { 4 | autoplaceGridItems, 5 | getGridGap, 6 | inheritGridGap, 7 | prefixTrackProp, 8 | prefixTrackValue 9 | } = require('./grid-utils') 10 | 11 | class GridRowsColumns extends Declaration { 12 | insert(decl, prefix, prefixes, result) { 13 | if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) 14 | 15 | let { parent, prop, value } = decl 16 | let isRowProp = prop.includes('rows') 17 | let isColumnProp = prop.includes('columns') 18 | 19 | let hasGridTemplate = parent.some( 20 | i => i.prop === 'grid-template' || i.prop === 'grid-template-areas' 21 | ) 22 | 23 | /** 24 | * Not to prefix rows declaration if grid-template(-areas) is present 25 | */ 26 | if (hasGridTemplate && isRowProp) { 27 | return false 28 | } 29 | 30 | let processor = new Processor({ options: {} }) 31 | let status = processor.gridStatus(parent, result) 32 | let gap = getGridGap(decl) 33 | gap = inheritGridGap(decl, gap) || gap 34 | 35 | let gapValue = isRowProp ? gap.row : gap.column 36 | 37 | if ((status === 'no-autoplace' || status === true) && !hasGridTemplate) { 38 | gapValue = null 39 | } 40 | 41 | let prefixValue = prefixTrackValue({ 42 | gap: gapValue, 43 | value 44 | }) 45 | 46 | /** 47 | * Insert prefixes 48 | */ 49 | decl.cloneBefore({ 50 | prop: prefixTrackProp({ prefix, prop }), 51 | value: prefixValue 52 | }) 53 | 54 | let autoflow = parent.nodes.find(i => i.prop === 'grid-auto-flow') 55 | let autoflowValue = 'row' 56 | 57 | if (autoflow && !processor.disabled(autoflow, result)) { 58 | autoflowValue = autoflow.value.trim() 59 | } 60 | if (status === 'autoplace') { 61 | /** 62 | * Show warning if grid-template-rows decl is not found 63 | */ 64 | let rowDecl = parent.nodes.find(i => i.prop === 'grid-template-rows') 65 | 66 | if (!rowDecl && hasGridTemplate) { 67 | return undefined 68 | } else if (!rowDecl && !hasGridTemplate) { 69 | decl.warn( 70 | result, 71 | 'Autoplacement does not work without grid-template-rows property' 72 | ) 73 | return undefined 74 | } 75 | 76 | /** 77 | * Show warning if grid-template-columns decl is not found 78 | */ 79 | let columnDecl = parent.nodes.find(i => { 80 | return i.prop === 'grid-template-columns' 81 | }) 82 | if (!columnDecl && !hasGridTemplate) { 83 | decl.warn( 84 | result, 85 | 'Autoplacement does not work without grid-template-columns property' 86 | ) 87 | } 88 | 89 | /** 90 | * Autoplace grid items 91 | */ 92 | if (isColumnProp && !hasGridTemplate) { 93 | autoplaceGridItems(decl, result, gap, autoflowValue) 94 | } 95 | } 96 | 97 | return undefined 98 | } 99 | 100 | /** 101 | * Change IE property back 102 | */ 103 | normalize(prop) { 104 | return prop.replace(/^grid-(rows|columns)/, 'grid-template-$1') 105 | } 106 | 107 | /** 108 | * Change property name for IE 109 | */ 110 | prefixed(prop, prefix) { 111 | if (prefix === '-ms-') { 112 | return prefixTrackProp({ prefix, prop }) 113 | } 114 | return super.prefixed(prop, prefix) 115 | } 116 | } 117 | 118 | GridRowsColumns.names = [ 119 | 'grid-template-rows', 120 | 'grid-template-columns', 121 | 'grid-rows', 122 | 'grid-columns' 123 | ] 124 | 125 | module.exports = GridRowsColumns 126 | -------------------------------------------------------------------------------- /lib/hacks/grid-start.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class GridStart extends Declaration { 4 | /** 5 | * Do not add prefix for unsupported value in IE 6 | */ 7 | check(decl) { 8 | let value = decl.value 9 | return !value.includes('/') && !value.includes('span') 10 | } 11 | 12 | /** 13 | * Return a final spec property 14 | */ 15 | normalize(prop) { 16 | return prop.replace('-start', '') 17 | } 18 | 19 | /** 20 | * Change property name for IE 21 | */ 22 | prefixed(prop, prefix) { 23 | let result = super.prefixed(prop, prefix) 24 | if (prefix === '-ms-') { 25 | result = result.replace('-start', '') 26 | } 27 | return result 28 | } 29 | } 30 | 31 | GridStart.names = ['grid-row-start', 'grid-column-start'] 32 | 33 | module.exports = GridStart 34 | -------------------------------------------------------------------------------- /lib/hacks/grid-template-areas.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let { 3 | getGridGap, 4 | inheritGridGap, 5 | parseGridAreas, 6 | prefixTrackProp, 7 | prefixTrackValue, 8 | warnGridGap, 9 | warnMissedAreas 10 | } = require('./grid-utils') 11 | 12 | function getGridRows(tpl) { 13 | return tpl 14 | .trim() 15 | .slice(1, -1) 16 | .split(/["']\s*["']?/g) 17 | } 18 | 19 | class GridTemplateAreas extends Declaration { 20 | /** 21 | * Translate grid-template-areas to separate -ms- prefixed properties 22 | */ 23 | insert(decl, prefix, prefixes, result) { 24 | if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) 25 | 26 | let hasColumns = false 27 | let hasRows = false 28 | let parent = decl.parent 29 | let gap = getGridGap(decl) 30 | gap = inheritGridGap(decl, gap) || gap 31 | 32 | // remove already prefixed rows 33 | // to prevent doubling prefixes 34 | parent.walkDecls(/-ms-grid-rows/, i => i.remove()) 35 | 36 | // add empty tracks to rows 37 | parent.walkDecls(/grid-template-(rows|columns)/, trackDecl => { 38 | if (trackDecl.prop === 'grid-template-rows') { 39 | hasRows = true 40 | let { prop, value } = trackDecl 41 | trackDecl.cloneBefore({ 42 | prop: prefixTrackProp({ prefix, prop }), 43 | value: prefixTrackValue({ gap: gap.row, value }) 44 | }) 45 | } else { 46 | hasColumns = true 47 | } 48 | }) 49 | 50 | let gridRows = getGridRows(decl.value) 51 | 52 | if (hasColumns && !hasRows && gap.row && gridRows.length > 1) { 53 | decl.cloneBefore({ 54 | prop: '-ms-grid-rows', 55 | raws: {}, 56 | value: prefixTrackValue({ 57 | gap: gap.row, 58 | value: `repeat(${gridRows.length}, auto)` 59 | }) 60 | }) 61 | } 62 | 63 | // warnings 64 | warnGridGap({ 65 | decl, 66 | gap, 67 | hasColumns, 68 | result 69 | }) 70 | 71 | let areas = parseGridAreas({ 72 | gap, 73 | rows: gridRows 74 | }) 75 | 76 | warnMissedAreas(areas, decl, result) 77 | 78 | return decl 79 | } 80 | } 81 | 82 | GridTemplateAreas.names = ['grid-template-areas'] 83 | 84 | module.exports = GridTemplateAreas 85 | -------------------------------------------------------------------------------- /lib/hacks/grid-template.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let { 3 | getGridGap, 4 | inheritGridGap, 5 | parseTemplate, 6 | warnGridGap, 7 | warnMissedAreas 8 | } = require('./grid-utils') 9 | 10 | class GridTemplate extends Declaration { 11 | /** 12 | * Translate grid-template to separate -ms- prefixed properties 13 | */ 14 | insert(decl, prefix, prefixes, result) { 15 | if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) 16 | 17 | if (decl.parent.some(i => i.prop === '-ms-grid-rows')) { 18 | return undefined 19 | } 20 | 21 | let gap = getGridGap(decl) 22 | 23 | /** 24 | * we must insert inherited gap values in some cases: 25 | * if we are inside media query && if we have no grid-gap value 26 | */ 27 | let inheritedGap = inheritGridGap(decl, gap) 28 | 29 | let { areas, columns, rows } = parseTemplate({ 30 | decl, 31 | gap: inheritedGap || gap 32 | }) 33 | 34 | let hasAreas = Object.keys(areas).length > 0 35 | let hasRows = Boolean(rows) 36 | let hasColumns = Boolean(columns) 37 | 38 | warnGridGap({ 39 | decl, 40 | gap, 41 | hasColumns, 42 | result 43 | }) 44 | 45 | warnMissedAreas(areas, decl, result) 46 | 47 | if ((hasRows && hasColumns) || hasAreas) { 48 | decl.cloneBefore({ 49 | prop: '-ms-grid-rows', 50 | raws: {}, 51 | value: rows 52 | }) 53 | } 54 | 55 | if (hasColumns) { 56 | decl.cloneBefore({ 57 | prop: '-ms-grid-columns', 58 | raws: {}, 59 | value: columns 60 | }) 61 | } 62 | 63 | return decl 64 | } 65 | } 66 | 67 | GridTemplate.names = ['grid-template'] 68 | 69 | module.exports = GridTemplate 70 | -------------------------------------------------------------------------------- /lib/hacks/image-rendering.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class ImageRendering extends Declaration { 4 | /** 5 | * Add hack only for crisp-edges 6 | */ 7 | check(decl) { 8 | return decl.value === 'pixelated' 9 | } 10 | 11 | /** 12 | * Return property name by spec 13 | */ 14 | normalize() { 15 | return 'image-rendering' 16 | } 17 | 18 | /** 19 | * Change property name for IE 20 | */ 21 | prefixed(prop, prefix) { 22 | if (prefix === '-ms-') { 23 | return '-ms-interpolation-mode' 24 | } 25 | return super.prefixed(prop, prefix) 26 | } 27 | 28 | /** 29 | * Warn on old value 30 | */ 31 | process(node, result) { 32 | return super.process(node, result) 33 | } 34 | 35 | /** 36 | * Change property and value for IE 37 | */ 38 | set(decl, prefix) { 39 | if (prefix !== '-ms-') return super.set(decl, prefix) 40 | decl.prop = '-ms-interpolation-mode' 41 | decl.value = 'nearest-neighbor' 42 | return decl 43 | } 44 | } 45 | 46 | ImageRendering.names = ['image-rendering', 'interpolation-mode'] 47 | 48 | module.exports = ImageRendering 49 | -------------------------------------------------------------------------------- /lib/hacks/image-set.js: -------------------------------------------------------------------------------- 1 | let Value = require('../value') 2 | 3 | class ImageSet extends Value { 4 | /** 5 | * Use non-standard name for WebKit and Firefox 6 | */ 7 | replace(string, prefix) { 8 | let fixed = super.replace(string, prefix) 9 | if (prefix === '-webkit-') { 10 | fixed = fixed.replace(/("[^"]+"|'[^']+')(\s+\d+\w)/gi, 'url($1)$2') 11 | } 12 | return fixed 13 | } 14 | } 15 | 16 | ImageSet.names = ['image-set'] 17 | 18 | module.exports = ImageSet 19 | -------------------------------------------------------------------------------- /lib/hacks/inline-logical.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class InlineLogical extends Declaration { 4 | /** 5 | * Return property name by spec 6 | */ 7 | normalize(prop) { 8 | return prop.replace(/(margin|padding|border)-(start|end)/, '$1-inline-$2') 9 | } 10 | 11 | /** 12 | * Use old syntax for -moz- and -webkit- 13 | */ 14 | prefixed(prop, prefix) { 15 | return prefix + prop.replace('-inline', '') 16 | } 17 | } 18 | 19 | InlineLogical.names = [ 20 | 'border-inline-start', 21 | 'border-inline-end', 22 | 'margin-inline-start', 23 | 'margin-inline-end', 24 | 'padding-inline-start', 25 | 'padding-inline-end', 26 | 'border-start', 27 | 'border-end', 28 | 'margin-start', 29 | 'margin-end', 30 | 'padding-start', 31 | 'padding-end' 32 | ] 33 | 34 | module.exports = InlineLogical 35 | -------------------------------------------------------------------------------- /lib/hacks/intrinsic.js: -------------------------------------------------------------------------------- 1 | let OldValue = require('../old-value') 2 | let Value = require('../value') 3 | 4 | function regexp(name) { 5 | return new RegExp(`(^|[\\s,(])(${name}($|[\\s),]))`, 'gi') 6 | } 7 | 8 | class Intrinsic extends Value { 9 | add(decl, prefix) { 10 | if (decl.prop.includes('grid') && prefix !== '-webkit-') { 11 | return undefined 12 | } 13 | return super.add(decl, prefix) 14 | } 15 | 16 | isStretch() { 17 | return ( 18 | this.name === 'stretch' || 19 | this.name === 'fill' || 20 | this.name === 'fill-available' 21 | ) 22 | } 23 | 24 | old(prefix) { 25 | let prefixed = prefix + this.name 26 | if (this.isStretch()) { 27 | if (prefix === '-moz-') { 28 | prefixed = '-moz-available' 29 | } else if (prefix === '-webkit-') { 30 | prefixed = '-webkit-fill-available' 31 | } 32 | } 33 | return new OldValue(this.name, prefixed, prefixed, regexp(prefixed)) 34 | } 35 | 36 | regexp() { 37 | if (!this.regexpCache) this.regexpCache = regexp(this.name) 38 | return this.regexpCache 39 | } 40 | 41 | replace(string, prefix) { 42 | if (prefix === '-moz-' && this.isStretch()) { 43 | return string.replace(this.regexp(), '$1-moz-available$3') 44 | } 45 | if (prefix === '-webkit-' && this.isStretch()) { 46 | return string.replace(this.regexp(), '$1-webkit-fill-available$3') 47 | } 48 | return super.replace(string, prefix) 49 | } 50 | } 51 | 52 | Intrinsic.names = [ 53 | 'max-content', 54 | 'min-content', 55 | 'fit-content', 56 | 'fill', 57 | 'fill-available', 58 | 'stretch' 59 | ] 60 | 61 | module.exports = Intrinsic 62 | -------------------------------------------------------------------------------- /lib/hacks/justify-content.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class JustifyContent extends Declaration { 5 | /** 6 | * Return property name by final spec 7 | */ 8 | normalize() { 9 | return 'justify-content' 10 | } 11 | 12 | /** 13 | * Change property name for 2009 and 2012 specs 14 | */ 15 | prefixed(prop, prefix) { 16 | let spec 17 | ;[spec, prefix] = flexSpec(prefix) 18 | if (spec === 2009) { 19 | return prefix + 'box-pack' 20 | } 21 | if (spec === 2012) { 22 | return prefix + 'flex-pack' 23 | } 24 | return super.prefixed(prop, prefix) 25 | } 26 | 27 | /** 28 | * Change value for 2009 and 2012 specs 29 | */ 30 | set(decl, prefix) { 31 | let spec = flexSpec(prefix)[0] 32 | if (spec === 2009 || spec === 2012) { 33 | let value = JustifyContent.oldValues[decl.value] || decl.value 34 | decl.value = value 35 | if (spec !== 2009 || value !== 'distribute') { 36 | return super.set(decl, prefix) 37 | } 38 | } else if (spec === 'final') { 39 | return super.set(decl, prefix) 40 | } 41 | return undefined 42 | } 43 | } 44 | 45 | JustifyContent.names = ['justify-content', 'flex-pack', 'box-pack'] 46 | 47 | JustifyContent.oldValues = { 48 | 'flex-end': 'end', 49 | 'flex-start': 'start', 50 | 'space-around': 'distribute', 51 | 'space-between': 'justify' 52 | } 53 | 54 | module.exports = JustifyContent 55 | -------------------------------------------------------------------------------- /lib/hacks/mask-border.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class MaskBorder extends Declaration { 4 | /** 5 | * Return property name by final spec 6 | */ 7 | normalize() { 8 | return this.name.replace('box-image', 'border') 9 | } 10 | 11 | /** 12 | * Return flex property for 2012 spec 13 | */ 14 | prefixed(prop, prefix) { 15 | let result = super.prefixed(prop, prefix) 16 | if (prefix === '-webkit-') { 17 | result = result.replace('border', 'box-image') 18 | } 19 | return result 20 | } 21 | } 22 | 23 | MaskBorder.names = [ 24 | 'mask-border', 25 | 'mask-border-source', 26 | 'mask-border-slice', 27 | 'mask-border-width', 28 | 'mask-border-outset', 29 | 'mask-border-repeat', 30 | 'mask-box-image', 31 | 'mask-box-image-source', 32 | 'mask-box-image-slice', 33 | 'mask-box-image-width', 34 | 'mask-box-image-outset', 35 | 'mask-box-image-repeat' 36 | ] 37 | 38 | module.exports = MaskBorder 39 | -------------------------------------------------------------------------------- /lib/hacks/mask-composite.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class MaskComposite extends Declaration { 4 | /** 5 | * Prefix mask-composite for webkit 6 | */ 7 | insert(decl, prefix, prefixes) { 8 | let isCompositeProp = decl.prop === 'mask-composite' 9 | 10 | let compositeValues 11 | 12 | if (isCompositeProp) { 13 | compositeValues = decl.value.split(',') 14 | } else { 15 | compositeValues = decl.value.match(MaskComposite.regexp) || [] 16 | } 17 | 18 | compositeValues = compositeValues.map(el => el.trim()).filter(el => el) 19 | let hasCompositeValues = compositeValues.length 20 | 21 | let compositeDecl 22 | 23 | if (hasCompositeValues) { 24 | compositeDecl = this.clone(decl) 25 | compositeDecl.value = compositeValues 26 | .map(value => MaskComposite.oldValues[value] || value) 27 | .join(', ') 28 | 29 | if (compositeValues.includes('intersect')) { 30 | compositeDecl.value += ', xor' 31 | } 32 | 33 | compositeDecl.prop = prefix + 'mask-composite' 34 | } 35 | 36 | if (isCompositeProp) { 37 | if (!hasCompositeValues) { 38 | return undefined 39 | } 40 | 41 | if (this.needCascade(decl)) { 42 | compositeDecl.raws.before = this.calcBefore(prefixes, decl, prefix) 43 | } 44 | 45 | return decl.parent.insertBefore(decl, compositeDecl) 46 | } 47 | 48 | let cloned = this.clone(decl) 49 | cloned.prop = prefix + cloned.prop 50 | 51 | if (hasCompositeValues) { 52 | cloned.value = cloned.value.replace(MaskComposite.regexp, '') 53 | } 54 | 55 | if (this.needCascade(decl)) { 56 | cloned.raws.before = this.calcBefore(prefixes, decl, prefix) 57 | } 58 | 59 | decl.parent.insertBefore(decl, cloned) 60 | 61 | if (!hasCompositeValues) { 62 | return decl 63 | } 64 | 65 | if (this.needCascade(decl)) { 66 | compositeDecl.raws.before = this.calcBefore(prefixes, decl, prefix) 67 | } 68 | return decl.parent.insertBefore(decl, compositeDecl) 69 | } 70 | } 71 | 72 | MaskComposite.names = ['mask', 'mask-composite'] 73 | 74 | MaskComposite.oldValues = { 75 | add: 'source-over', 76 | exclude: 'xor', 77 | intersect: 'source-in', 78 | subtract: 'source-out' 79 | } 80 | 81 | MaskComposite.regexp = new RegExp( 82 | `\\s+(${Object.keys(MaskComposite.oldValues).join( 83 | '|' 84 | )})\\b(?!\\))\\s*(?=[,])`, 85 | 'ig' 86 | ) 87 | 88 | module.exports = MaskComposite 89 | -------------------------------------------------------------------------------- /lib/hacks/order.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let flexSpec = require('./flex-spec') 3 | 4 | class Order extends Declaration { 5 | /** 6 | * Return property name by final spec 7 | */ 8 | normalize() { 9 | return 'order' 10 | } 11 | 12 | /** 13 | * Change property name for 2009 and 2012 specs 14 | */ 15 | prefixed(prop, prefix) { 16 | let spec 17 | ;[spec, prefix] = flexSpec(prefix) 18 | if (spec === 2009) { 19 | return prefix + 'box-ordinal-group' 20 | } 21 | if (spec === 2012) { 22 | return prefix + 'flex-order' 23 | } 24 | return super.prefixed(prop, prefix) 25 | } 26 | 27 | /** 28 | * Fix value for 2009 spec 29 | */ 30 | set(decl, prefix) { 31 | let spec = flexSpec(prefix)[0] 32 | if (spec === 2009 && /\d/.test(decl.value)) { 33 | decl.value = (parseInt(decl.value) + 1).toString() 34 | return super.set(decl, prefix) 35 | } 36 | return super.set(decl, prefix) 37 | } 38 | } 39 | 40 | Order.names = ['order', 'flex-order', 'box-ordinal-group'] 41 | 42 | module.exports = Order 43 | -------------------------------------------------------------------------------- /lib/hacks/overscroll-behavior.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class OverscrollBehavior extends Declaration { 4 | /** 5 | * Return property name by spec 6 | */ 7 | normalize() { 8 | return 'overscroll-behavior' 9 | } 10 | 11 | /** 12 | * Change property name for IE 13 | */ 14 | prefixed(prop, prefix) { 15 | return prefix + 'scroll-chaining' 16 | } 17 | 18 | /** 19 | * Change value for IE 20 | */ 21 | set(decl, prefix) { 22 | if (decl.value === 'auto') { 23 | decl.value = 'chained' 24 | } else if (decl.value === 'none' || decl.value === 'contain') { 25 | decl.value = 'none' 26 | } 27 | return super.set(decl, prefix) 28 | } 29 | } 30 | 31 | OverscrollBehavior.names = ['overscroll-behavior', 'scroll-chaining'] 32 | 33 | module.exports = OverscrollBehavior 34 | -------------------------------------------------------------------------------- /lib/hacks/pixelated.js: -------------------------------------------------------------------------------- 1 | let OldValue = require('../old-value') 2 | let Value = require('../value') 3 | 4 | class Pixelated extends Value { 5 | /** 6 | * Different name for WebKit and Firefox 7 | */ 8 | old(prefix) { 9 | if (prefix === '-webkit-') { 10 | return new OldValue(this.name, '-webkit-optimize-contrast') 11 | } 12 | if (prefix === '-moz-') { 13 | return new OldValue(this.name, '-moz-crisp-edges') 14 | } 15 | return super.old(prefix) 16 | } 17 | 18 | /** 19 | * Use non-standard name for WebKit and Firefox 20 | */ 21 | replace(string, prefix) { 22 | if (prefix === '-webkit-') { 23 | return string.replace(this.regexp(), '$1-webkit-optimize-contrast') 24 | } 25 | if (prefix === '-moz-') { 26 | return string.replace(this.regexp(), '$1-moz-crisp-edges') 27 | } 28 | return super.replace(string, prefix) 29 | } 30 | } 31 | 32 | Pixelated.names = ['pixelated'] 33 | 34 | module.exports = Pixelated 35 | -------------------------------------------------------------------------------- /lib/hacks/place-self.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | let utils = require('./grid-utils') 3 | 4 | class PlaceSelf extends Declaration { 5 | /** 6 | * Translate place-self to separate -ms- prefixed properties 7 | */ 8 | insert(decl, prefix, prefixes) { 9 | if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes) 10 | 11 | // prevent doubling of prefixes 12 | if (decl.parent.some(i => i.prop === '-ms-grid-row-align')) { 13 | return undefined 14 | } 15 | 16 | let [[first, second]] = utils.parse(decl) 17 | 18 | if (second) { 19 | utils.insertDecl(decl, 'grid-row-align', first) 20 | utils.insertDecl(decl, 'grid-column-align', second) 21 | } else { 22 | utils.insertDecl(decl, 'grid-row-align', first) 23 | utils.insertDecl(decl, 'grid-column-align', first) 24 | } 25 | 26 | return undefined 27 | } 28 | } 29 | 30 | PlaceSelf.names = ['place-self'] 31 | 32 | module.exports = PlaceSelf 33 | -------------------------------------------------------------------------------- /lib/hacks/placeholder-shown.js: -------------------------------------------------------------------------------- 1 | let Selector = require('../selector') 2 | 3 | class PlaceholderShown extends Selector { 4 | /** 5 | * Return different selectors depend on prefix 6 | */ 7 | prefixed(prefix) { 8 | if (prefix === '-moz-') { 9 | return ':-moz-placeholder' 10 | } else if (prefix === '-ms-') { 11 | return ':-ms-input-placeholder' 12 | } 13 | return `:${prefix}placeholder-shown` 14 | } 15 | } 16 | 17 | PlaceholderShown.names = [':placeholder-shown'] 18 | 19 | module.exports = PlaceholderShown 20 | -------------------------------------------------------------------------------- /lib/hacks/placeholder.js: -------------------------------------------------------------------------------- 1 | let Selector = require('../selector') 2 | 3 | class Placeholder extends Selector { 4 | /** 5 | * Add old mozilla to possible prefixes 6 | */ 7 | possible() { 8 | return super.possible().concat(['-moz- old', '-ms- old']) 9 | } 10 | 11 | /** 12 | * Return different selectors depend on prefix 13 | */ 14 | prefixed(prefix) { 15 | if (prefix === '-webkit-') { 16 | return '::-webkit-input-placeholder' 17 | } 18 | if (prefix === '-ms-') { 19 | return '::-ms-input-placeholder' 20 | } 21 | if (prefix === '-ms- old') { 22 | return ':-ms-input-placeholder' 23 | } 24 | if (prefix === '-moz- old') { 25 | return ':-moz-placeholder' 26 | } 27 | return `::${prefix}placeholder` 28 | } 29 | } 30 | 31 | Placeholder.names = ['::placeholder'] 32 | 33 | module.exports = Placeholder 34 | -------------------------------------------------------------------------------- /lib/hacks/print-color-adjust.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class PrintColorAdjust extends Declaration { 4 | /** 5 | * Return property name by spec 6 | */ 7 | normalize() { 8 | return 'print-color-adjust' 9 | } 10 | 11 | /** 12 | * Change property name for WebKit-based browsers 13 | */ 14 | prefixed(prop, prefix) { 15 | if (prefix === '-moz-') { 16 | return 'color-adjust' 17 | } else { 18 | return prefix + 'print-color-adjust' 19 | } 20 | } 21 | } 22 | 23 | PrintColorAdjust.names = ['print-color-adjust', 'color-adjust'] 24 | 25 | module.exports = PrintColorAdjust 26 | -------------------------------------------------------------------------------- /lib/hacks/text-decoration-skip-ink.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class TextDecorationSkipInk extends Declaration { 4 | /** 5 | * Change prefix for ink value 6 | */ 7 | set(decl, prefix) { 8 | if (decl.prop === 'text-decoration-skip-ink' && decl.value === 'auto') { 9 | decl.prop = prefix + 'text-decoration-skip' 10 | decl.value = 'ink' 11 | return decl 12 | } else { 13 | return super.set(decl, prefix) 14 | } 15 | } 16 | } 17 | 18 | TextDecorationSkipInk.names = [ 19 | 'text-decoration-skip-ink', 20 | 'text-decoration-skip' 21 | ] 22 | 23 | module.exports = TextDecorationSkipInk 24 | -------------------------------------------------------------------------------- /lib/hacks/text-decoration.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | const BASIC = [ 4 | 'none', 5 | 'underline', 6 | 'overline', 7 | 'line-through', 8 | 'blink', 9 | 'inherit', 10 | 'initial', 11 | 'unset' 12 | ] 13 | 14 | class TextDecoration extends Declaration { 15 | /** 16 | * Do not add prefixes for basic values. 17 | */ 18 | check(decl) { 19 | return decl.value.split(/\s+/).some(i => !BASIC.includes(i)) 20 | } 21 | } 22 | 23 | TextDecoration.names = ['text-decoration'] 24 | 25 | module.exports = TextDecoration 26 | -------------------------------------------------------------------------------- /lib/hacks/text-emphasis-position.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class TextEmphasisPosition extends Declaration { 4 | set(decl, prefix) { 5 | if (prefix === '-webkit-') { 6 | decl.value = decl.value.replace(/\s*(right|left)\s*/i, '') 7 | } 8 | return super.set(decl, prefix) 9 | } 10 | } 11 | 12 | TextEmphasisPosition.names = ['text-emphasis-position'] 13 | 14 | module.exports = TextEmphasisPosition 15 | -------------------------------------------------------------------------------- /lib/hacks/transform-decl.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class TransformDecl extends Declaration { 4 | /** 5 | * Is transform contain 3D commands 6 | */ 7 | contain3d(decl) { 8 | if (decl.prop === 'transform-origin') { 9 | return false 10 | } 11 | 12 | for (let func of TransformDecl.functions3d) { 13 | if (decl.value.includes(`${func}(`)) { 14 | return true 15 | } 16 | } 17 | 18 | return false 19 | } 20 | 21 | /** 22 | * Don't add prefix for IE in keyframes 23 | */ 24 | insert(decl, prefix, prefixes) { 25 | if (prefix === '-ms-') { 26 | if (!this.contain3d(decl) && !this.keyframeParents(decl)) { 27 | return super.insert(decl, prefix, prefixes) 28 | } 29 | } else if (prefix === '-o-') { 30 | if (!this.contain3d(decl)) { 31 | return super.insert(decl, prefix, prefixes) 32 | } 33 | } else { 34 | return super.insert(decl, prefix, prefixes) 35 | } 36 | return undefined 37 | } 38 | 39 | /** 40 | * Recursively check all parents for @keyframes 41 | */ 42 | keyframeParents(decl) { 43 | let { parent } = decl 44 | while (parent) { 45 | if (parent.type === 'atrule' && parent.name === 'keyframes') { 46 | return true 47 | } 48 | ;({ parent } = parent) 49 | } 50 | return false 51 | } 52 | 53 | /** 54 | * Replace rotateZ to rotate for IE 9 55 | */ 56 | set(decl, prefix) { 57 | decl = super.set(decl, prefix) 58 | if (prefix === '-ms-') { 59 | decl.value = decl.value.replace(/rotatez/gi, 'rotate') 60 | } 61 | return decl 62 | } 63 | } 64 | 65 | TransformDecl.names = ['transform', 'transform-origin'] 66 | 67 | TransformDecl.functions3d = [ 68 | 'matrix3d', 69 | 'translate3d', 70 | 'translateZ', 71 | 'scale3d', 72 | 'scaleZ', 73 | 'rotate3d', 74 | 'rotateX', 75 | 'rotateY', 76 | 'perspective' 77 | ] 78 | 79 | module.exports = TransformDecl 80 | -------------------------------------------------------------------------------- /lib/hacks/user-select.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class UserSelect extends Declaration { 4 | /** 5 | * Avoid prefixing all in IE 6 | */ 7 | insert(decl, prefix, prefixes) { 8 | if (decl.value === 'all' && prefix === '-ms-') { 9 | return undefined 10 | } else if ( 11 | decl.value === 'contain' && 12 | (prefix === '-moz-' || prefix === '-webkit-') 13 | ) { 14 | return undefined 15 | } else { 16 | return super.insert(decl, prefix, prefixes) 17 | } 18 | } 19 | 20 | /** 21 | * Change prefixed value for IE 22 | */ 23 | set(decl, prefix) { 24 | if (prefix === '-ms-' && decl.value === 'contain') { 25 | decl.value = 'element' 26 | } 27 | return super.set(decl, prefix) 28 | } 29 | } 30 | 31 | UserSelect.names = ['user-select'] 32 | 33 | module.exports = UserSelect 34 | -------------------------------------------------------------------------------- /lib/hacks/writing-mode.js: -------------------------------------------------------------------------------- 1 | let Declaration = require('../declaration') 2 | 3 | class WritingMode extends Declaration { 4 | insert(decl, prefix, prefixes) { 5 | if (prefix === '-ms-') { 6 | let cloned = this.set(this.clone(decl), prefix) 7 | 8 | if (this.needCascade(decl)) { 9 | cloned.raws.before = this.calcBefore(prefixes, decl, prefix) 10 | } 11 | let direction = 'ltr' 12 | 13 | decl.parent.nodes.forEach(i => { 14 | if (i.prop === 'direction') { 15 | if (i.value === 'rtl' || i.value === 'ltr') direction = i.value 16 | } 17 | }) 18 | 19 | cloned.value = WritingMode.msValues[direction][decl.value] || decl.value 20 | return decl.parent.insertBefore(decl, cloned) 21 | } 22 | 23 | return super.insert(decl, prefix, prefixes) 24 | } 25 | } 26 | 27 | WritingMode.names = ['writing-mode'] 28 | 29 | WritingMode.msValues = { 30 | ltr: { 31 | 'horizontal-tb': 'lr-tb', 32 | 'vertical-lr': 'tb-lr', 33 | 'vertical-rl': 'tb-rl' 34 | }, 35 | rtl: { 36 | 'horizontal-tb': 'rl-tb', 37 | 'vertical-lr': 'bt-lr', 38 | 'vertical-rl': 'bt-rl' 39 | } 40 | } 41 | 42 | module.exports = WritingMode 43 | -------------------------------------------------------------------------------- /lib/info.js: -------------------------------------------------------------------------------- 1 | let browserslist = require('browserslist') 2 | 3 | function capitalize(str) { 4 | return str.slice(0, 1).toUpperCase() + str.slice(1) 5 | } 6 | 7 | const NAMES = { 8 | and_chr: 'Chrome for Android', 9 | and_ff: 'Firefox for Android', 10 | and_qq: 'QQ Browser', 11 | and_uc: 'UC for Android', 12 | baidu: 'Baidu Browser', 13 | ie: 'IE', 14 | ie_mob: 'IE Mobile', 15 | ios_saf: 'iOS Safari', 16 | kaios: 'KaiOS Browser', 17 | op_mini: 'Opera Mini', 18 | op_mob: 'Opera Mobile', 19 | samsung: 'Samsung Internet' 20 | } 21 | 22 | function prefix(name, prefixes, note) { 23 | let out = ` ${name}` 24 | if (note) out += ' *' 25 | out += ': ' 26 | out += prefixes.map(i => i.replace(/^-(.*)-$/g, '$1')).join(', ') 27 | out += '\n' 28 | return out 29 | } 30 | 31 | module.exports = function (prefixes) { 32 | if (prefixes.browsers.selected.length === 0) { 33 | return 'No browsers selected' 34 | } 35 | 36 | let versions = {} 37 | for (let browser of prefixes.browsers.selected) { 38 | let parts = browser.split(' ') 39 | let name = parts[0] 40 | let version = parts[1] 41 | 42 | name = NAMES[name] || capitalize(name) 43 | if (versions[name]) { 44 | versions[name].push(version) 45 | } else { 46 | versions[name] = [version] 47 | } 48 | } 49 | 50 | let out = 'Browsers:\n' 51 | for (let browser in versions) { 52 | let list = versions[browser] 53 | list = list.sort((a, b) => parseFloat(b) - parseFloat(a)) 54 | out += ` ${browser}: ${list.join(', ')}\n` 55 | } 56 | 57 | let coverage = browserslist.coverage(prefixes.browsers.selected) 58 | let round = Math.round(coverage * 100) / 100.0 59 | out += `\nThese browsers account for ${round}% of all users globally\n` 60 | 61 | let atrules = [] 62 | for (let name in prefixes.add) { 63 | let data = prefixes.add[name] 64 | if (name[0] === '@' && data.prefixes) { 65 | atrules.push(prefix(name, data.prefixes)) 66 | } 67 | } 68 | if (atrules.length > 0) { 69 | out += `\nAt-Rules:\n${atrules.sort().join('')}` 70 | } 71 | 72 | let selectors = [] 73 | for (let selector of prefixes.add.selectors) { 74 | if (selector.prefixes) { 75 | selectors.push(prefix(selector.name, selector.prefixes)) 76 | } 77 | } 78 | if (selectors.length > 0) { 79 | out += `\nSelectors:\n${selectors.sort().join('')}` 80 | } 81 | 82 | let values = [] 83 | let props = [] 84 | let hadGrid = false 85 | for (let name in prefixes.add) { 86 | let data = prefixes.add[name] 87 | if (name[0] !== '@' && data.prefixes) { 88 | let grid = name.indexOf('grid-') === 0 89 | if (grid) hadGrid = true 90 | props.push(prefix(name, data.prefixes, grid)) 91 | } 92 | 93 | if (!Array.isArray(data.values)) { 94 | continue 95 | } 96 | for (let value of data.values) { 97 | let grid = value.name.includes('grid') 98 | if (grid) hadGrid = true 99 | let string = prefix(value.name, value.prefixes, grid) 100 | if (!values.includes(string)) { 101 | values.push(string) 102 | } 103 | } 104 | } 105 | 106 | if (props.length > 0) { 107 | out += `\nProperties:\n${props.sort().join('')}` 108 | } 109 | if (values.length > 0) { 110 | out += `\nValues:\n${values.sort().join('')}` 111 | } 112 | if (hadGrid) { 113 | out += '\n* - Prefixes will be added only on grid: true option.\n' 114 | } 115 | 116 | if (!atrules.length && !selectors.length && !props.length && !values.length) { 117 | out += 118 | "\nAwesome! Your browsers don't require any vendor prefixes." + 119 | '\nNow you can remove Autoprefixer from build steps.' 120 | } 121 | 122 | return out 123 | } 124 | -------------------------------------------------------------------------------- /lib/old-selector.js: -------------------------------------------------------------------------------- 1 | class OldSelector { 2 | constructor(selector, prefix) { 3 | this.prefix = prefix 4 | this.prefixed = selector.prefixed(this.prefix) 5 | this.regexp = selector.regexp(this.prefix) 6 | 7 | this.prefixeds = selector 8 | .possible() 9 | .map(x => [selector.prefixed(x), selector.regexp(x)]) 10 | 11 | this.unprefixed = selector.name 12 | this.nameRegexp = selector.regexp() 13 | } 14 | 15 | /** 16 | * Does rule contain an unnecessary prefixed selector 17 | */ 18 | check(rule) { 19 | if (!rule.selector.includes(this.prefixed)) { 20 | return false 21 | } 22 | if (!rule.selector.match(this.regexp)) { 23 | return false 24 | } 25 | if (this.isHack(rule)) { 26 | return false 27 | } 28 | return true 29 | } 30 | 31 | /** 32 | * Is rule a hack without unprefixed version bottom 33 | */ 34 | isHack(rule) { 35 | let index = rule.parent.index(rule) + 1 36 | let rules = rule.parent.nodes 37 | 38 | while (index < rules.length) { 39 | let before = rules[index].selector 40 | if (!before) { 41 | return true 42 | } 43 | 44 | if (before.includes(this.unprefixed) && before.match(this.nameRegexp)) { 45 | return false 46 | } 47 | 48 | let some = false 49 | for (let [string, regexp] of this.prefixeds) { 50 | if (before.includes(string) && before.match(regexp)) { 51 | some = true 52 | break 53 | } 54 | } 55 | 56 | if (!some) { 57 | return true 58 | } 59 | 60 | index += 1 61 | } 62 | 63 | return true 64 | } 65 | } 66 | 67 | module.exports = OldSelector 68 | -------------------------------------------------------------------------------- /lib/old-value.js: -------------------------------------------------------------------------------- 1 | let utils = require('./utils') 2 | 3 | class OldValue { 4 | constructor(unprefixed, prefixed, string, regexp) { 5 | this.unprefixed = unprefixed 6 | this.prefixed = prefixed 7 | this.string = string || prefixed 8 | this.regexp = regexp || utils.regexp(prefixed) 9 | } 10 | 11 | /** 12 | * Check, that value contain old value 13 | */ 14 | check(value) { 15 | if (value.includes(this.string)) { 16 | return !!value.match(this.regexp) 17 | } 18 | return false 19 | } 20 | } 21 | 22 | module.exports = OldValue 23 | -------------------------------------------------------------------------------- /lib/prefixer.js: -------------------------------------------------------------------------------- 1 | let Browsers = require('./browsers') 2 | let utils = require('./utils') 3 | let vendor = require('./vendor') 4 | 5 | /** 6 | * Recursively clone objects 7 | */ 8 | function clone(obj, parent) { 9 | let cloned = new obj.constructor() 10 | 11 | for (let i of Object.keys(obj || {})) { 12 | let value = obj[i] 13 | if (i === 'parent' && typeof value === 'object') { 14 | if (parent) { 15 | cloned[i] = parent 16 | } 17 | } else if (i === 'source' || i === null) { 18 | cloned[i] = value 19 | } else if (Array.isArray(value)) { 20 | cloned[i] = value.map(x => clone(x, cloned)) 21 | } else if ( 22 | i !== '_autoprefixerPrefix' && 23 | i !== '_autoprefixerValues' && 24 | i !== 'proxyCache' 25 | ) { 26 | if (typeof value === 'object' && value !== null) { 27 | value = clone(value, cloned) 28 | } 29 | cloned[i] = value 30 | } 31 | } 32 | 33 | return cloned 34 | } 35 | 36 | class Prefixer { 37 | constructor(name, prefixes, all) { 38 | this.prefixes = prefixes 39 | this.name = name 40 | this.all = all 41 | } 42 | 43 | /** 44 | * Clone node and clean autprefixer custom caches 45 | */ 46 | static clone(node, overrides) { 47 | let cloned = clone(node) 48 | for (let name in overrides) { 49 | cloned[name] = overrides[name] 50 | } 51 | return cloned 52 | } 53 | 54 | /** 55 | * Add hack to selected names 56 | */ 57 | static hack(klass) { 58 | if (!this.hacks) { 59 | this.hacks = {} 60 | } 61 | return klass.names.map(name => { 62 | this.hacks[name] = klass 63 | return this.hacks[name] 64 | }) 65 | } 66 | 67 | /** 68 | * Load hacks for some names 69 | */ 70 | static load(name, prefixes, all) { 71 | let Klass = this.hacks && this.hacks[name] 72 | if (Klass) { 73 | return new Klass(name, prefixes, all) 74 | } else { 75 | return new this(name, prefixes, all) 76 | } 77 | } 78 | 79 | /** 80 | * Shortcut for Prefixer.clone 81 | */ 82 | clone(node, overrides) { 83 | return Prefixer.clone(node, overrides) 84 | } 85 | 86 | /** 87 | * Find prefix in node parents 88 | */ 89 | parentPrefix(node) { 90 | let prefix 91 | 92 | if (typeof node._autoprefixerPrefix !== 'undefined') { 93 | prefix = node._autoprefixerPrefix 94 | } else if (node.type === 'decl' && node.prop[0] === '-') { 95 | prefix = vendor.prefix(node.prop) 96 | } else if (node.type === 'root') { 97 | prefix = false 98 | } else if ( 99 | node.type === 'rule' && 100 | node.selector.includes(':-') && 101 | /:(-\w+-)/.test(node.selector) 102 | ) { 103 | prefix = node.selector.match(/:(-\w+-)/)[1] 104 | } else if (node.type === 'atrule' && node.name[0] === '-') { 105 | prefix = vendor.prefix(node.name) 106 | } else { 107 | prefix = this.parentPrefix(node.parent) 108 | } 109 | 110 | if (!Browsers.prefixes().includes(prefix)) { 111 | prefix = false 112 | } 113 | 114 | node._autoprefixerPrefix = prefix 115 | 116 | return node._autoprefixerPrefix 117 | } 118 | 119 | /** 120 | * Clone node with prefixes 121 | */ 122 | process(node, result) { 123 | if (!this.check(node)) { 124 | return undefined 125 | } 126 | 127 | let parent = this.parentPrefix(node) 128 | 129 | let prefixes = this.prefixes.filter( 130 | prefix => !parent || parent === utils.removeNote(prefix) 131 | ) 132 | 133 | let added = [] 134 | for (let prefix of prefixes) { 135 | if (this.add(node, prefix, added.concat([prefix]), result)) { 136 | added.push(prefix) 137 | } 138 | } 139 | 140 | return added 141 | } 142 | } 143 | 144 | module.exports = Prefixer 145 | -------------------------------------------------------------------------------- /lib/resolution.js: -------------------------------------------------------------------------------- 1 | let FractionJs = require('fraction.js') 2 | 3 | let Prefixer = require('./prefixer') 4 | let utils = require('./utils') 5 | 6 | const REGEXP = /(min|max)-resolution\s*:\s*\d*\.?\d+(dppx|dpcm|dpi|x)/gi 7 | const SPLIT = /(min|max)-resolution(\s*:\s*)(\d*\.?\d+)(dppx|dpcm|dpi|x)/i 8 | 9 | class Resolution extends Prefixer { 10 | /** 11 | * Remove prefixed queries 12 | */ 13 | clean(rule) { 14 | if (!this.bad) { 15 | this.bad = [] 16 | for (let prefix of this.prefixes) { 17 | this.bad.push(this.prefixName(prefix, 'min')) 18 | this.bad.push(this.prefixName(prefix, 'max')) 19 | } 20 | } 21 | 22 | rule.params = utils.editList(rule.params, queries => { 23 | return queries.filter(query => this.bad.every(i => !query.includes(i))) 24 | }) 25 | } 26 | 27 | /** 28 | * Return prefixed query name 29 | */ 30 | prefixName(prefix, name) { 31 | if (prefix === '-moz-') { 32 | return name + '--moz-device-pixel-ratio' 33 | } else { 34 | return prefix + name + '-device-pixel-ratio' 35 | } 36 | } 37 | 38 | /** 39 | * Return prefixed query 40 | */ 41 | prefixQuery(prefix, name, colon, value, units) { 42 | value = new FractionJs(value) 43 | 44 | // 1dpcm = 2.54dpi 45 | // 1dppx = 96dpi 46 | if (units === 'dpi') { 47 | value = value.div(96) 48 | } else if (units === 'dpcm') { 49 | value = value.mul(2.54).div(96) 50 | } 51 | value = value.simplify() 52 | 53 | if (prefix === '-o-') { 54 | value = value.n + '/' + value.d 55 | } 56 | return this.prefixName(prefix, name) + colon + value 57 | } 58 | 59 | /** 60 | * Add prefixed queries 61 | */ 62 | process(rule) { 63 | let parent = this.parentPrefix(rule) 64 | let prefixes = parent ? [parent] : this.prefixes 65 | 66 | rule.params = utils.editList(rule.params, (origin, prefixed) => { 67 | for (let query of origin) { 68 | if ( 69 | !query.includes('min-resolution') && 70 | !query.includes('max-resolution') 71 | ) { 72 | prefixed.push(query) 73 | continue 74 | } 75 | 76 | for (let prefix of prefixes) { 77 | let processed = query.replace(REGEXP, str => { 78 | let parts = str.match(SPLIT) 79 | return this.prefixQuery( 80 | prefix, 81 | parts[1], 82 | parts[2], 83 | parts[3], 84 | parts[4] 85 | ) 86 | }) 87 | prefixed.push(processed) 88 | } 89 | prefixed.push(query) 90 | } 91 | 92 | return utils.uniq(prefixed) 93 | }) 94 | } 95 | } 96 | 97 | module.exports = Resolution 98 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | let { list } = require('postcss') 2 | 3 | /** 4 | * Throw special error, to tell beniary, 5 | * that this error is from Autoprefixer. 6 | */ 7 | module.exports.error = function (text) { 8 | let err = new Error(text) 9 | err.autoprefixer = true 10 | throw err 11 | } 12 | 13 | /** 14 | * Return array, that doesn’t contain duplicates. 15 | */ 16 | module.exports.uniq = function (array) { 17 | return [...new Set(array)] 18 | } 19 | 20 | /** 21 | * Return "-webkit-" on "-webkit- old" 22 | */ 23 | module.exports.removeNote = function (string) { 24 | if (!string.includes(' ')) { 25 | return string 26 | } 27 | 28 | return string.split(' ')[0] 29 | } 30 | 31 | /** 32 | * Escape RegExp symbols 33 | */ 34 | module.exports.escapeRegexp = function (string) { 35 | return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&') 36 | } 37 | 38 | /** 39 | * Return regexp to check, that CSS string contain word 40 | */ 41 | module.exports.regexp = function (word, escape = true) { 42 | if (escape) { 43 | word = this.escapeRegexp(word) 44 | } 45 | return new RegExp(`(^|[\\s,(])(${word}($|[\\s(,]))`, 'gi') 46 | } 47 | 48 | /** 49 | * Change comma list 50 | */ 51 | module.exports.editList = function (value, callback) { 52 | let origin = list.comma(value) 53 | let changed = callback(origin, []) 54 | 55 | if (origin === changed) { 56 | return value 57 | } 58 | 59 | let join = value.match(/,\s*/) 60 | join = join ? join[0] : ', ' 61 | return changed.join(join) 62 | } 63 | 64 | /** 65 | * Split the selector into parts. 66 | * It returns 3 level deep array because selectors can be comma 67 | * separated (1), space separated (2), and combined (3) 68 | * @param {String} selector selector string 69 | * @return {Array>} 3 level deep array of split selector 70 | * @see utils.test.js for examples 71 | */ 72 | module.exports.splitSelector = function (selector) { 73 | return list.comma(selector).map(i => { 74 | return list.space(i).map(k => { 75 | return k.split(/(?=\.|#)/g) 76 | }) 77 | }) 78 | } 79 | 80 | /** 81 | * Return true if a given value only contains numbers. 82 | * @param {*} value 83 | * @returns {boolean} 84 | */ 85 | module.exports.isPureNumber = function (value) { 86 | if (typeof value === 'number') { 87 | return true 88 | } 89 | if (typeof value === 'string') { 90 | return /^[0-9]+$/.test(value) 91 | } 92 | return false 93 | } 94 | -------------------------------------------------------------------------------- /lib/value.js: -------------------------------------------------------------------------------- 1 | let OldValue = require('./old-value') 2 | let Prefixer = require('./prefixer') 3 | let utils = require('./utils') 4 | let vendor = require('./vendor') 5 | 6 | class Value extends Prefixer { 7 | /** 8 | * Clone decl for each prefixed values 9 | */ 10 | static save(prefixes, decl) { 11 | let prop = decl.prop 12 | let result = [] 13 | 14 | for (let prefix in decl._autoprefixerValues) { 15 | let value = decl._autoprefixerValues[prefix] 16 | 17 | if (value === decl.value) { 18 | continue 19 | } 20 | 21 | let item 22 | let propPrefix = vendor.prefix(prop) 23 | 24 | if (propPrefix === '-pie-') { 25 | continue 26 | } 27 | 28 | if (propPrefix === prefix) { 29 | item = decl.value = value 30 | result.push(item) 31 | continue 32 | } 33 | 34 | let prefixed = prefixes.prefixed(prop, prefix) 35 | let rule = decl.parent 36 | 37 | if (!rule.every(i => i.prop !== prefixed)) { 38 | result.push(item) 39 | continue 40 | } 41 | 42 | let trimmed = value.replace(/\s+/, ' ') 43 | let already = rule.some( 44 | i => i.prop === decl.prop && i.value.replace(/\s+/, ' ') === trimmed 45 | ) 46 | 47 | if (already) { 48 | result.push(item) 49 | continue 50 | } 51 | 52 | let cloned = this.clone(decl, { value }) 53 | item = decl.parent.insertBefore(decl, cloned) 54 | 55 | result.push(item) 56 | } 57 | 58 | return result 59 | } 60 | 61 | /** 62 | * Save values with next prefixed token 63 | */ 64 | add(decl, prefix) { 65 | if (!decl._autoprefixerValues) { 66 | decl._autoprefixerValues = {} 67 | } 68 | let value = decl._autoprefixerValues[prefix] || this.value(decl) 69 | 70 | let before 71 | do { 72 | before = value 73 | value = this.replace(value, prefix) 74 | if (value === false) return 75 | } while (value !== before) 76 | 77 | decl._autoprefixerValues[prefix] = value 78 | } 79 | 80 | /** 81 | * Is declaration need to be prefixed 82 | */ 83 | check(decl) { 84 | let value = decl.value 85 | if (!value.includes(this.name)) { 86 | return false 87 | } 88 | 89 | return !!value.match(this.regexp()) 90 | } 91 | 92 | /** 93 | * Return function to fast find prefixed value 94 | */ 95 | old(prefix) { 96 | return new OldValue(this.name, prefix + this.name) 97 | } 98 | 99 | /** 100 | * Lazy regexp loading 101 | */ 102 | regexp() { 103 | return this.regexpCache || (this.regexpCache = utils.regexp(this.name)) 104 | } 105 | 106 | /** 107 | * Add prefix to values in string 108 | */ 109 | replace(string, prefix) { 110 | return string.replace(this.regexp(), `$1${prefix}$2`) 111 | } 112 | 113 | /** 114 | * Get value with comments if it was not changed 115 | */ 116 | value(decl) { 117 | if (decl.raws.value && decl.raws.value.value === decl.value) { 118 | return decl.raws.value.raw 119 | } else { 120 | return decl.value 121 | } 122 | } 123 | } 124 | 125 | module.exports = Value 126 | -------------------------------------------------------------------------------- /lib/vendor.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | prefix(prop) { 3 | let match = prop.match(/^(-\w+-)/) 4 | if (match) { 5 | return match[0] 6 | } 7 | 8 | return '' 9 | }, 10 | 11 | unprefixed(prop) { 12 | return prop.replace(/^-\w+-/, '') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autoprefixer", 3 | "version": "10.4.21", 4 | "description": "Parse CSS and add vendor prefixes to CSS rules using values from the Can I Use website", 5 | "engines": { 6 | "node": "^10 || ^12 || >=14" 7 | }, 8 | "keywords": [ 9 | "autoprefixer", 10 | "css", 11 | "prefix", 12 | "postcss", 13 | "postcss-plugin" 14 | ], 15 | "scripts": { 16 | "unit": "uvu . '\\.test\\.js$'", 17 | "test:coverage": "c8 pnpm unit", 18 | "test:lint": "eslint . bin/*", 19 | "test:size": "size-limit", 20 | "test": "pnpm run /^test:/" 21 | }, 22 | "main": "lib/autoprefixer.js", 23 | "bin": "bin/autoprefixer", 24 | "types": "lib/autoprefixer.d.ts", 25 | "funding": [ 26 | { 27 | "type": "opencollective", 28 | "url": "https://opencollective.com/postcss/" 29 | }, 30 | { 31 | "type": "tidelift", 32 | "url": "https://tidelift.com/funding/github/npm/autoprefixer" 33 | }, 34 | { 35 | "type": "github", 36 | "url": "https://github.com/sponsors/ai" 37 | } 38 | ], 39 | "author": "Andrey Sitnik ", 40 | "license": "MIT", 41 | "repository": "postcss/autoprefixer", 42 | "bugs": { 43 | "url": "https://github.com/postcss/autoprefixer/issues" 44 | }, 45 | "peerDependencies": { 46 | "postcss": "^8.1.0" 47 | }, 48 | "dependencies": { 49 | "browserslist": "^4.24.4", 50 | "caniuse-lite": "^1.0.30001702", 51 | "fraction.js": "^4.3.7", 52 | "normalize-range": "^0.1.2", 53 | "picocolors": "^1.1.1", 54 | "postcss-value-parser": "^4.2.0" 55 | }, 56 | "devDependencies": { 57 | "@logux/eslint-config": "^55.0.1", 58 | "@size-limit/preset-small-lib": "11.2.0", 59 | "c8": "^10.1.3", 60 | "clean-publish": "^5.1.0", 61 | "eslint": "^9.22.0", 62 | "nanospy": "^1.0.0", 63 | "postcss": "^8.5.3", 64 | "size-limit": "^11.2.0", 65 | "uvu": "^0.5.6" 66 | }, 67 | "prettier": { 68 | "arrowParens": "avoid", 69 | "jsxSingleQuote": false, 70 | "quoteProps": "consistent", 71 | "semi": false, 72 | "singleQuote": true, 73 | "trailingComma": "none" 74 | }, 75 | "size-limit": [ 76 | { 77 | "limit": "50 KB" 78 | } 79 | ], 80 | "c8": { 81 | "exclude": [ 82 | "test/*" 83 | ], 84 | "lines": 100, 85 | "check-coverage": true, 86 | "reporter": [ 87 | "text", 88 | "lcov" 89 | ], 90 | "skip-full": true, 91 | "clean": true 92 | }, 93 | "clean-publish": { 94 | "cleanDocs": true 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /test/at-rule.test.js: -------------------------------------------------------------------------------- 1 | let { parse } = require('postcss') 2 | let { test } = require('uvu') 3 | let { equal } = require('uvu/assert') 4 | 5 | let AtRule = require('../lib/at-rule') 6 | 7 | test('adds prefixes', () => { 8 | let keyframes = new AtRule('@keyframes', ['-moz-', '-ms-']) 9 | 10 | let css = parse( 11 | '@-moz-keyframes b {} ' + '@-ms-keyframes a {} ' + '@keyframes a {}' 12 | ) 13 | keyframes.process(css.last) 14 | equal( 15 | css.toString(), 16 | '@-moz-keyframes b {} ' + 17 | '@-ms-keyframes a {} ' + 18 | '@-moz-keyframes a {} ' + 19 | '@keyframes a {}' 20 | ) 21 | }) 22 | 23 | test.run() 24 | -------------------------------------------------------------------------------- /test/brackets.test.js: -------------------------------------------------------------------------------- 1 | let { test } = require('uvu') 2 | let { equal } = require('uvu/assert') 3 | 4 | let brackets = require('../lib/brackets') 5 | 6 | test('parses simple string', () => { 7 | equal(brackets.parse('test'), ['test']) 8 | }) 9 | 10 | test('parses brackets', () => { 11 | equal(brackets.parse('a (b) a'), ['a ', ['b'], ' a']) 12 | }) 13 | 14 | test('parses many brackets', () => { 15 | equal(brackets.parse('a (b ()) a'), ['a ', ['b ', [''], ''], ' a']) 16 | }) 17 | 18 | test('parses errors', () => { 19 | equal(brackets.parse('a (b ('), ['a ', ['b ', ['']]]) 20 | }) 21 | 22 | test('stringifies simple string', () => { 23 | equal(brackets.stringify(['test']), 'test') 24 | }) 25 | 26 | test('stringifies brackets', () => { 27 | equal(brackets.stringify(['a ', ['b'], ' a']), 'a (b) a') 28 | }) 29 | 30 | test('stringifies many brackets', () => { 31 | equal(brackets.stringify(['a ', ['b ', [''], ''], ' a']), 'a (b ()) a') 32 | }) 33 | 34 | test.run() 35 | -------------------------------------------------------------------------------- /test/browsers.test.js: -------------------------------------------------------------------------------- 1 | let { agents } = require('caniuse-lite/dist/unpacker/agents') 2 | let { join } = require('path') 3 | let { test } = require('uvu') 4 | let { equal, is } = require('uvu/assert') 5 | 6 | let Browsers = require('../lib/browsers') 7 | 8 | test('returns prefixes by default data', () => { 9 | equal(Browsers.prefixes(), ['-webkit-', '-moz-', '-ms-', '-o-']) 10 | }) 11 | 12 | test('finds possible prefix', () => { 13 | is(Browsers.withPrefix('1 -o-calc(1)'), true) 14 | is(Browsers.withPrefix('1 calc(1)'), false) 15 | }) 16 | 17 | test('allows to select no browsers', () => { 18 | let browsers = new Browsers(agents, []) 19 | equal(browsers.selected.length, 0) 20 | }) 21 | 22 | test('selects by older version', () => { 23 | let browsers = new Browsers(agents, ['ie < 7']) 24 | equal(browsers.selected, ['ie 6', 'ie 5.5']) 25 | }) 26 | 27 | test('combines requirements', () => { 28 | let browsers = new Browsers(agents, ['ie 10', 'ie < 6']) 29 | equal(browsers.selected, ['ie 10', 'ie 5.5']) 30 | }) 31 | 32 | test('has aliases', () => { 33 | equal(new Browsers(agents, ['fx 10']).selected, ['firefox 10']) 34 | equal(new Browsers(agents, ['ff 10']).selected, ['firefox 10']) 35 | }) 36 | 37 | test('ignores case', () => { 38 | equal(new Browsers(agents, ['Firefox 10']).selected, ['firefox 10']) 39 | }) 40 | 41 | test('uses browserslist config', () => { 42 | let from = join(__dirname, 'cases/config/test.css') 43 | equal(new Browsers(agents, undefined, { from }).selected, ['ie 10']) 44 | }) 45 | 46 | test('returns browser prefix', () => { 47 | let browsers = new Browsers(agents, ['chrome 30']) 48 | equal(browsers.prefix('chrome 30'), '-webkit-') 49 | }) 50 | 51 | test('returns right prefix for Operas', () => { 52 | let browsers = new Browsers(agents, ['last 1 opera version']) 53 | equal(browsers.prefix('opera 12'), '-o-') 54 | equal(browsers.prefix(browsers.selected[0]), '-webkit-') 55 | equal(browsers.prefix('op_mob 12'), '-o-') 56 | equal(browsers.prefix(browsers.selected[0]), '-webkit-') 57 | }) 58 | 59 | test('return true for selected browsers', () => { 60 | let browsers = new Browsers(agents, ['chrome 30', 'chrome 31']) 61 | is(browsers.isSelected('chrome 30'), true) 62 | is(browsers.isSelected('ie 6'), false) 63 | }) 64 | 65 | test.run() 66 | -------------------------------------------------------------------------------- /test/cases/3d-transform.css: -------------------------------------------------------------------------------- 1 | a { 2 | transition: transform 1s; 3 | transform: rotateX(45deg); 4 | } 5 | b { 6 | transform: translateX(45deg); 7 | transform-origin: 0 0; 8 | } 9 | em { 10 | transform: rotateZ(45deg); 11 | } 12 | @keyframes anim { 13 | from { 14 | transform: rotate(90deg); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/cases/3d-transform.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -o-transition: -o-transform 1s; 3 | transition: transform 1s; 4 | transition: transform 1s, -o-transform 1s; 5 | transform: rotateX(45deg); 6 | } 7 | b { 8 | -ms-transform: translateX(45deg); 9 | -o-transform: translateX(45deg); 10 | transform: translateX(45deg); 11 | -ms-transform-origin: 0 0; 12 | -o-transform-origin: 0 0; 13 | transform-origin: 0 0; 14 | } 15 | em { 16 | -ms-transform: rotate(45deg); 17 | -o-transform: rotateZ(45deg); 18 | transform: rotateZ(45deg); 19 | } 20 | @-o-keyframes anim { 21 | from { 22 | -o-transform: rotate(90deg); 23 | transform: rotate(90deg); 24 | } 25 | } 26 | @keyframes anim { 27 | from { 28 | -o-transform: rotate(90deg); 29 | transform: rotate(90deg); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/cases/advanced-filter.css: -------------------------------------------------------------------------------- 1 | div { 2 | backdrop-filter: blur(2px); 3 | } 4 | 5 | div { 6 | background: filter(url('image.jpg'), blur(2px)); 7 | } 8 | 9 | div { 10 | background: url(image.jpg), filter(url('image.jpg'), blur(2px)); 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/advanced-filter.out.css: -------------------------------------------------------------------------------- 1 | div { 2 | -webkit-backdrop-filter: blur(2px); 3 | backdrop-filter: blur(2px); 4 | } 5 | 6 | div { 7 | background: -webkit-filter(url('image.jpg'), blur(2px)); 8 | background: filter(url('image.jpg'), blur(2px)); 9 | } 10 | 11 | div { 12 | background: url(image.jpg), -webkit-filter(url('image.jpg'), blur(2px)); 13 | background: url(image.jpg), filter(url('image.jpg'), blur(2px)); 14 | } 15 | -------------------------------------------------------------------------------- /test/cases/animation.css: -------------------------------------------------------------------------------- 1 | a { 2 | animation-direction: alternate; 3 | animation-direction: reverse; 4 | animation-direction: REVERSE; 5 | animation-direction: alternate-reverse; 6 | animation: rotation-reverse; 7 | animation: rotation-reverse reverse; 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/animation.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-animation-direction: alternate; 3 | -o-animation-direction: alternate; 4 | animation-direction: alternate; 5 | animation-direction: reverse; 6 | animation-direction: REVERSE; 7 | animation-direction: alternate-reverse; 8 | -webkit-animation: rotation-reverse; 9 | -o-animation: rotation-reverse; 10 | animation: rotation-reverse; 11 | animation: rotation-reverse reverse; 12 | } 13 | -------------------------------------------------------------------------------- /test/cases/appearance.css: -------------------------------------------------------------------------------- 1 | a { 2 | appearance: none; 3 | } 4 | 5 | b { 6 | appearance: auto; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/appearance.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-appearance: none; 3 | -moz-appearance: none; 4 | appearance: none; 5 | } 6 | 7 | b { 8 | -webkit-appearance: auto; 9 | -moz-appearance: auto; 10 | appearance: auto; 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/at-rules.css: -------------------------------------------------------------------------------- 1 | body { 2 | appearance: none; 3 | } 4 | 5 | .block { 6 | transform: translate(20px, 30px); 7 | } 8 | 9 | @-webkit-region .region { 10 | .block { 11 | transform: translate(20px, 30px); 12 | } 13 | 14 | .block__button { 15 | appearance: none; 16 | } 17 | } 18 | 19 | @-moz-document domain('example.com') { 20 | body { 21 | appearance: none; 22 | } 23 | 24 | .block { 25 | transform: translate(20px, 30px); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/cases/at-rules.out.css: -------------------------------------------------------------------------------- 1 | body { 2 | -webkit-appearance: none; 3 | -moz-appearance: none; 4 | appearance: none; 5 | } 6 | 7 | .block { 8 | -webkit-transform: translate(20px, 30px); 9 | transform: translate(20px, 30px); 10 | } 11 | 12 | @-webkit-region .region { 13 | .block { 14 | -webkit-transform: translate(20px, 30px); 15 | transform: translate(20px, 30px); 16 | } 17 | 18 | .block__button { 19 | -webkit-appearance: none; 20 | appearance: none; 21 | } 22 | } 23 | 24 | @-moz-document domain('example.com') { 25 | body { 26 | -moz-appearance: none; 27 | appearance: none; 28 | } 29 | 30 | .block { 31 | transform: translate(20px, 30px); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/cases/autofill.css: -------------------------------------------------------------------------------- 1 | input:autofill { 2 | background-color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/autofill.out.css: -------------------------------------------------------------------------------- 1 | input:-webkit-autofill { 2 | background-color: red; 3 | } 4 | 5 | input:autofill { 6 | background-color: red; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/backdrop-filter.css: -------------------------------------------------------------------------------- 1 | a { 2 | backdrop-filter: blur(2px); 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/backdrop-filter.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-backdrop-filter: blur(2px); 3 | backdrop-filter: blur(2px); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/backdrop.css: -------------------------------------------------------------------------------- 1 | ::backdrop { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/backdrop.out.css: -------------------------------------------------------------------------------- 1 | ::-ms-backdrop { 2 | color: green; 3 | } 4 | ::backdrop { 5 | color: green; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/background-clip.css: -------------------------------------------------------------------------------- 1 | a { 2 | background-clip: text; 3 | } 4 | 5 | b { 6 | background-clip: content-box; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/background-clip.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-background-clip: text; 3 | background-clip: text; 4 | } 5 | 6 | b { 7 | background-clip: content-box; 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/background-size.css: -------------------------------------------------------------------------------- 1 | a { 2 | background-size: 20px 3 | } 4 | 5 | b { 6 | background-size: contain 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/background-size.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-background-size: 20px 20px; 3 | -moz-background-size: 20px; 4 | background-size: 20px 5 | } 6 | 7 | b { 8 | -webkit-background-size: contain; 9 | -moz-background-size: contain; 10 | background-size: contain 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/border-image.css: -------------------------------------------------------------------------------- 1 | a { 2 | border-image: linear-gradient(black, white) 20% fill stretch stretch; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/border-image.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -o-border-image: -o-linear-gradient(black, white) 20% stretch stretch; 3 | border-image: -webkit-linear-gradient(black, white) 20% fill stretch stretch; 4 | border-image: linear-gradient(black, white) 20% fill stretch stretch; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/border-radius.css: -------------------------------------------------------------------------------- 1 | a { 2 | border-radius: 5px; 3 | border-top-left-radius: 3px; 4 | border-bottom-right-radius: 3px; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/border-radius.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-border-radius: 5px; 3 | -moz-border-radius: 5px; 4 | border-radius: 5px; 5 | -webkit-border-top-left-radius: 3px; 6 | -moz-border-radius-topleft: 3px; 7 | border-top-left-radius: 3px; 8 | -webkit-border-bottom-right-radius: 3px; 9 | -moz-border-radius-bottomright: 3px; 10 | border-bottom-right-radius: 3px; 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/cascade.css: -------------------------------------------------------------------------------- 1 | a { 2 | flex-direction: row; 3 | mask: none 4 | } 5 | 6 | b { mask: none } 7 | -------------------------------------------------------------------------------- /test/cases/cascade.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-box-orient: horizontal; 3 | -webkit-box-direction: normal; 4 | -webkit-flex-direction: row; 5 | -moz-box-orient: horizontal; 6 | -moz-box-direction: normal; 7 | -ms-flex-direction: row; 8 | flex-direction: row; 9 | -webkit-mask: none; 10 | mask: none 11 | } 12 | 13 | b { -webkit-mask: none; mask: none } 14 | -------------------------------------------------------------------------------- /test/cases/check-down.css: -------------------------------------------------------------------------------- 1 | * { 2 | transition: all 1s; 3 | -o-transition: all 1s 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/check-down.out.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-transition: all 1s; 3 | transition: all 1s; 4 | -o-transition: all 1s 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/comments.css: -------------------------------------------------------------------------------- 1 | a { 2 | /* transition */ 3 | transition: all 1s; 4 | height: calc(/* comment before */100% - /* comment inside */ 10px/* comment after */); 5 | } 6 | 7 | /* placeholder */ 8 | ::placeholder { } 9 | -------------------------------------------------------------------------------- /test/cases/comments.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | /* transition */ 3 | -webkit-transition: all 1s; 4 | -o-transition: all 1s; 5 | transition: all 1s; 6 | height: -webkit-calc(/* comment before */100% - /* comment inside */ 10px/* comment after */); 7 | height: calc(/* comment before */100% - /* comment inside */ 10px/* comment after */); 8 | } 9 | 10 | /* placeholder */ 11 | ::-webkit-input-placeholder { } 12 | ::placeholder { } 13 | -------------------------------------------------------------------------------- /test/cases/config/browserslist: -------------------------------------------------------------------------------- 1 | ie 10 2 | 3 | [development] 4 | chrome 25 5 | -------------------------------------------------------------------------------- /test/cases/config/test.css: -------------------------------------------------------------------------------- 1 | a { 2 | display: flex; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/config/test.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | display: -ms-flexbox; 3 | display: flex; 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/config/test.production.css: -------------------------------------------------------------------------------- 1 | a { 2 | display: -webkit-flex; 3 | display: flex; 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/content.css: -------------------------------------------------------------------------------- 1 | a { 2 | content: "Element 'div' not allowed as child of element 'span' in this context."; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/cross-fade.css: -------------------------------------------------------------------------------- 1 | a { 2 | background-image: cross-fade(20% url(foo.png), url(bar.png)); 3 | } 4 | 5 | b { 6 | background-image: cross-fade(url(foo.png), url(bar.png)); 7 | } 8 | 9 | h1 { 10 | background-image: cross-fade(10.823% url(foo.png), url(bar.png)); 11 | } 12 | 13 | h2 { 14 | background-image: cross-fade(0.59 url(foo.png), url(bar.png)); 15 | } 16 | 17 | h3 { 18 | background-image: cross-fade(.59 url(foo.png), url(bar.png)); 19 | } 20 | 21 | .foo { 22 | background-image: cross-fade(.59 linear-gradient(white, black), radial-gradient(circle closest-corner, white, black)); 23 | } 24 | -------------------------------------------------------------------------------- /test/cases/cross-fade.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | background-image: -webkit-cross-fade(url(foo.png), url(bar.png), 20%); 3 | background-image: cross-fade(20% url(foo.png), url(bar.png)); 4 | } 5 | 6 | b { 7 | background-image: -webkit-cross-fade(url(foo.png), url(bar.png), 0.5); 8 | background-image: cross-fade(url(foo.png), url(bar.png)); 9 | } 10 | 11 | h1 { 12 | background-image: -webkit-cross-fade(url(foo.png), url(bar.png), 10.823%); 13 | background-image: cross-fade(10.823% url(foo.png), url(bar.png)); 14 | } 15 | 16 | h2 { 17 | background-image: -webkit-cross-fade(url(foo.png), url(bar.png), 0.59); 18 | background-image: cross-fade(0.59 url(foo.png), url(bar.png)); 19 | } 20 | 21 | h3 { 22 | background-image: -webkit-cross-fade(url(foo.png), url(bar.png), .59); 23 | background-image: cross-fade(.59 url(foo.png), url(bar.png)); 24 | } 25 | 26 | .foo { 27 | background-image: -webkit-cross-fade(linear-gradient(white, black), radial-gradient(circle closest-corner, white, black), .59); 28 | background-image: cross-fade(.59 linear-gradient(white, black), radial-gradient(circle closest-corner, white, black)); 29 | } 30 | -------------------------------------------------------------------------------- /test/cases/custom-prefix.css: -------------------------------------------------------------------------------- 1 | a { 2 | -evil-up: calc(10px + 1); 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/custom-prefix.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -evil-up: -webkit-calc(10px + 1); 3 | -evil-up: calc(10px + 1); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/disabled.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-border-radius: 4px; 3 | border-radius: 4px; 4 | mask: none; 5 | } 6 | 7 | b { 8 | /* autoprefixer: off */ 9 | -webkit-border-radius: 4px; 10 | border-radius: 4px; 11 | mask: none; 12 | } 13 | 14 | .loud { 15 | /*! autoprefixer: off */ 16 | -webkit-border-radius: 4px; 17 | border-radius: 4px; 18 | mask: none; 19 | } 20 | 21 | .case { 22 | /*autoprefixer:OFF*/ 23 | -webkit-border-radius: 4px; 24 | border-radius: 4px; 25 | mask: none; 26 | } 27 | 28 | @support (transition: 1s) { 29 | /* autoprefixer: off */ 30 | :fullscreen { 31 | -webkit-border-radius: 4px; 32 | border-radius: 4px; 33 | mask: none; 34 | } 35 | 36 | ::placeholder { 37 | /*autoprefixer: on*/ 38 | -webkit-border-radius: 4px; 39 | border-radius: 4px; 40 | mask: none; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/cases/disabled.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | border-radius: 4px; 3 | -webkit-mask: none; 4 | mask: none; 5 | } 6 | 7 | b { 8 | /* autoprefixer: off */ 9 | -webkit-border-radius: 4px; 10 | border-radius: 4px; 11 | mask: none; 12 | } 13 | 14 | .loud { 15 | /*! autoprefixer: off */ 16 | -webkit-border-radius: 4px; 17 | border-radius: 4px; 18 | mask: none; 19 | } 20 | 21 | .case { 22 | /*autoprefixer:OFF*/ 23 | -webkit-border-radius: 4px; 24 | border-radius: 4px; 25 | mask: none; 26 | } 27 | 28 | @support (transition: 1s) { 29 | /* autoprefixer: off */ 30 | :fullscreen { 31 | -webkit-border-radius: 4px; 32 | border-radius: 4px; 33 | mask: none; 34 | } 35 | 36 | ::-webkit-input-placeholder { 37 | /*autoprefixer: on*/ 38 | border-radius: 4px; 39 | -webkit-mask: none; 40 | mask: none; 41 | } 42 | 43 | ::placeholder { 44 | /*autoprefixer: on*/ 45 | border-radius: 4px; 46 | -webkit-mask: none; 47 | mask: none; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/cases/double.css: -------------------------------------------------------------------------------- 1 | a { 2 | flex-basis: 8.33333%; 3 | flex-basis: calc(100% / 12 * 1) 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/double.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-flex-basis: 8.33333%; 3 | -ms-flex-preferred-size: 8.33333%; 4 | flex-basis: 8.33333%; 5 | -webkit-flex-basis: -webkit-calc(100% / 12 * 1); 6 | -ms-flex-preferred-size: calc(100% / 12 * 1); 7 | flex-basis: calc(100% / 12 * 1) 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/element.css: -------------------------------------------------------------------------------- 1 | div { 2 | background: element(#id); 3 | } 4 | 5 | div { 6 | background: url(image.jpg), element(#id); 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/element.out.css: -------------------------------------------------------------------------------- 1 | div { 2 | background: -moz-element(#id); 3 | background: element(#id); 4 | } 5 | 6 | div { 7 | background: url(image.jpg), -moz-element(#id); 8 | background: url(image.jpg), element(#id); 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/example.css: -------------------------------------------------------------------------------- 1 | ::placeholder { 2 | color: gray; 3 | } 4 | 5 | .image { 6 | background-image: url(image@1x.png); 7 | } 8 | @media (min-resolution: 2dppx) { 9 | .image { 10 | background-image: url(image@2x.png); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/cases/example.out.css: -------------------------------------------------------------------------------- 1 | ::-moz-placeholder { 2 | color: gray; 3 | } 4 | 5 | ::placeholder { 6 | color: gray; 7 | } 8 | 9 | .image { 10 | background-image: url(image@1x.png); 11 | } 12 | @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) { 13 | .image { 14 | background-image: url(image@2x.png); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/cases/file-selector-button.css: -------------------------------------------------------------------------------- 1 | ::file-selector-button { 2 | background: black 3 | } 4 | 5 | input::file-selector-button { 6 | color: black; 7 | } 8 | 9 | input:hover::file-selector-button { 10 | color: white; 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/file-selector-button.out.css: -------------------------------------------------------------------------------- 1 | ::-webkit-file-upload-button { 2 | background: black 3 | } 4 | 5 | ::file-selector-button { 6 | background: black 7 | } 8 | 9 | input::-webkit-file-upload-button { 10 | color: black; 11 | } 12 | 13 | input::file-selector-button { 14 | color: black; 15 | } 16 | 17 | input:hover::-webkit-file-upload-button { 18 | color: white; 19 | } 20 | 21 | input:hover::file-selector-button { 22 | color: white; 23 | } 24 | -------------------------------------------------------------------------------- /test/cases/filter.css: -------------------------------------------------------------------------------- 1 | a { 2 | filter: blur(10px); 3 | transition: filter 2s; 4 | } 5 | 6 | div { 7 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=50); 8 | } 9 | 10 | b { 11 | filter: alpha(opacity=100); 12 | } 13 | 14 | em { 15 | filter: Alpha(opacity=100); 16 | } 17 | -------------------------------------------------------------------------------- /test/cases/filter.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-filter: blur(10px); 3 | filter: blur(10px); 4 | -webkit-transition: -webkit-filter 2s; 5 | transition: -webkit-filter 2s; 6 | transition: filter 2s; 7 | transition: filter 2s, -webkit-filter 2s; 8 | } 9 | 10 | div { 11 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=50); 12 | } 13 | 14 | b { 15 | filter: alpha(opacity=100); 16 | } 17 | 18 | em { 19 | filter: Alpha(opacity=100); 20 | } 21 | -------------------------------------------------------------------------------- /test/cases/flex-rewrite.css: -------------------------------------------------------------------------------- 1 | .a { 2 | flex-grow: 0; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/flex-rewrite.out.css: -------------------------------------------------------------------------------- 1 | .a { 2 | -webkit-box-flex: 0; 3 | -webkit-flex-grow: 0; 4 | -moz-box-flex: 0; 5 | -ms-flex-positive: 0; 6 | flex-grow: 0; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/flexbox.css: -------------------------------------------------------------------------------- 1 | a { 2 | -js-display: flex; 3 | display: flex; 4 | flex-flow: row; 5 | order: 0; 6 | flex: 0 1 2; 7 | transition: flex 200ms; 8 | } 9 | .inline { 10 | display: inline-flex; 11 | align-self: auto; 12 | align-content: stretch; 13 | flex: auto; 14 | } 15 | 16 | .a { 17 | display: flex; 18 | flex-direction: row; 19 | justify-content: flex-start; 20 | align-items: flex-start; 21 | flex-wrap: nowrap; 22 | align-content: flex-start; 23 | align-self: flex-start; 24 | flex: none; 25 | } 26 | .b { 27 | display: flex; 28 | flex-direction: row-reverse; 29 | justify-content: flex-end; 30 | align-items: flex-end; 31 | flex-wrap: wrap; 32 | align-content: flex-end; 33 | align-self: flex-end; 34 | flex-shrink: 1; 35 | } 36 | .c { 37 | display: flex; 38 | flex-direction: column; 39 | justify-content: center; 40 | align-items: center; 41 | flex-wrap: reverse-wrap; 42 | align-content: center; 43 | align-self: center; 44 | flex-basis: auto; 45 | } 46 | .e { 47 | display: flex; 48 | flex-direction: column-reverse; 49 | justify-content: space-between; 50 | align-items: baseline; 51 | align-content: space-between; 52 | align-self: baseline; 53 | } 54 | .f { 55 | display: flex; 56 | justify-content: space-around; 57 | align-items: stretch; 58 | align-content: space-around; 59 | align-self: stretch; 60 | } 61 | .g { 62 | display: flex; 63 | flex: calc(1em + 1px) 0 0; 64 | } 65 | .h { 66 | flex-flow: column wrap; 67 | } 68 | .i { 69 | flex-flow: nowrap; 70 | } 71 | .inherit { 72 | order: inherit; 73 | flex-direction: inherit; 74 | } 75 | @supports (display: flex) { 76 | .foo { 77 | display: flex; 78 | } 79 | } 80 | @supports (flex: auto) { 81 | .foo { 82 | flex: auto; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/cases/fullscreen.css: -------------------------------------------------------------------------------- 1 | :fullscreen { 2 | background: black 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/fullscreen.out.css: -------------------------------------------------------------------------------- 1 | :-webkit-full-screen { 2 | background: black 3 | } 4 | :fullscreen { 5 | background: black 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/gradient-fix.css: -------------------------------------------------------------------------------- 1 | .test { 2 | background-image: -webkit-linear-gradient(yellow 50%, red 50%); 3 | background-image: linear-gradient(to , red 50%); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/gradient-fix.out.css: -------------------------------------------------------------------------------- 1 | .test { 2 | background-image: -webkit-linear-gradient(yellow 50%, red 50%); 3 | background-image: -webkit-linear-gradient(red 50%); 4 | background-image: -moz-linear-gradient(red 50%); 5 | background-image: -o-linear-gradient(red 50%); 6 | background-image: linear-gradient(to , red 50%); 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/grid-area-media-sequence.css: -------------------------------------------------------------------------------- 1 | .grid-template-sequence { 2 | display: grid; 3 | grid-template: 4 | "foo foo" 5 | "bar bar" 6 | "baz baz" 7 | / 1fr 1fr; 8 | } 9 | 10 | @media (min-width: 300px) { 11 | .grid-template-sequence { 12 | grid-template: 13 | "foo foo" 14 | "bar bar" 20px 15 | / 1fr 1fr; 16 | } 17 | } 18 | 19 | @media (min-width: 600px) { 20 | .grid-template-sequence { 21 | grid-template: 22 | "foo foo" 23 | "bar bar" 20px 24 | / 1fr 1fr; 25 | } 26 | } 27 | 28 | @media (min-width: 900px) { 29 | .grid-template-sequence { 30 | grid-template: 31 | "x x" 32 | "foo foo" 33 | "bar bar" 20px 34 | / 1fr 1fr; 35 | } 36 | } 37 | 38 | .foo { 39 | grid-area: foo; 40 | } 41 | 42 | .bar { 43 | grid-area: bar; 44 | } 45 | 46 | .x { 47 | grid-area: x; 48 | } 49 | 50 | @media (max-width: 900px) { 51 | .grid-2 { 52 | grid-template: 53 | "a a" 54 | "a a" 55 | "b b" 20px 56 | / 1fr 1fr; 57 | } 58 | } 59 | 60 | @media (max-width: 400px) { 61 | .grid-2 { 62 | grid-template: 63 | "a a" 64 | "a a" 65 | "b b" 20px 66 | / 1fr 1fr; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/cases/grid-area-media-sequence.out.css: -------------------------------------------------------------------------------- 1 | .grid-template-sequence { 2 | display: -ms-grid; 3 | display: grid; 4 | -ms-grid-rows: auto auto auto; 5 | -ms-grid-columns: 1fr 1fr; 6 | grid-template: 7 | "foo foo" 8 | "bar bar" 9 | "baz baz" 10 | / 1fr 1fr; 11 | } 12 | 13 | @media (min-width: 300px) { 14 | .grid-template-sequence { 15 | -ms-grid-rows: auto 20px; 16 | -ms-grid-columns: 1fr 1fr; 17 | grid-template: 18 | "foo foo" 19 | "bar bar" 20px 20 | / 1fr 1fr; 21 | } 22 | } 23 | 24 | @media (min-width: 600px) { 25 | .grid-template-sequence { 26 | -ms-grid-rows: auto 20px; 27 | -ms-grid-columns: 1fr 1fr; 28 | grid-template: 29 | "foo foo" 30 | "bar bar" 20px 31 | / 1fr 1fr; 32 | } 33 | } 34 | 35 | @media (min-width: 900px) { 36 | .grid-template-sequence { 37 | -ms-grid-rows: auto auto 20px; 38 | -ms-grid-columns: 1fr 1fr; 39 | grid-template: 40 | "x x" 41 | "foo foo" 42 | "bar bar" 20px 43 | / 1fr 1fr; 44 | } 45 | } 46 | 47 | .foo { 48 | -ms-grid-row: 1; 49 | -ms-grid-column: 1; 50 | -ms-grid-column-span: 2; 51 | grid-area: foo; 52 | } 53 | 54 | .bar { 55 | -ms-grid-row: 2; 56 | -ms-grid-column: 1; 57 | -ms-grid-column-span: 2; 58 | grid-area: bar; 59 | } 60 | 61 | .x { 62 | grid-area: x; 63 | } 64 | 65 | @media (min-width: 300px) { 66 | .foo { 67 | -ms-grid-row: 1; 68 | -ms-grid-column: 1; 69 | -ms-grid-column-span: 2; 70 | } 71 | .bar { 72 | -ms-grid-row: 2; 73 | -ms-grid-column: 1; 74 | -ms-grid-column-span: 2; 75 | } 76 | } 77 | 78 | @media (min-width: 600px) { 79 | .foo { 80 | -ms-grid-row: 1; 81 | -ms-grid-column: 1; 82 | -ms-grid-column-span: 2; 83 | } 84 | .bar { 85 | -ms-grid-row: 2; 86 | -ms-grid-column: 1; 87 | -ms-grid-column-span: 2; 88 | } 89 | } 90 | 91 | @media (min-width: 900px) { 92 | .foo { 93 | -ms-grid-row: 2; 94 | -ms-grid-column: 1; 95 | -ms-grid-column-span: 2; 96 | } 97 | .bar { 98 | -ms-grid-row: 3; 99 | -ms-grid-column: 1; 100 | -ms-grid-column-span: 2; 101 | } 102 | .x { 103 | -ms-grid-row: 1; 104 | -ms-grid-column: 1; 105 | -ms-grid-column-span: 2; 106 | } 107 | } 108 | 109 | @media (max-width: 900px) { 110 | .grid-2 { 111 | -ms-grid-rows: auto auto 20px; 112 | -ms-grid-columns: 1fr 1fr; 113 | grid-template: 114 | "a a" 115 | "a a" 116 | "b b" 20px 117 | / 1fr 1fr; 118 | } 119 | } 120 | 121 | @media (max-width: 400px) { 122 | .grid-2 { 123 | -ms-grid-rows: auto auto 20px; 124 | -ms-grid-columns: 1fr 1fr; 125 | grid-template: 126 | "a a" 127 | "a a" 128 | "b b" 20px 129 | / 1fr 1fr; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /test/cases/grid-area.css: -------------------------------------------------------------------------------- 1 | .a { 2 | grid-area: 5 / 1 / span 1 / span 5; 3 | } 4 | 5 | .b { 6 | grid-area: span 1 / span 3 / 4 / 4; 7 | } 8 | 9 | .c { 10 | grid-area: 2 / 2; 11 | } 12 | 13 | .d { 14 | grid-area: "custom-ident"; 15 | } 16 | 17 | .e { 18 | grid-area: 2 / 2 / 3 / 8; 19 | } 20 | 21 | .f { 22 | grid-area: 2 / 2 / 3; 23 | } 24 | 25 | /* emit warning */ 26 | .g { 27 | grid-area: 3 / 1; 28 | grid-row: 1; 29 | grid-column: 2; 30 | } 31 | 32 | .h { 33 | grid-area: 3 / 1; 34 | grid-column: 1 / span 4; 35 | } 36 | 37 | /* there should be no warning */ 38 | .i { 39 | grid-area: 3 / 1; 40 | grid-column-end: span 4; 41 | } 42 | 43 | .j { 44 | grid-area: 3 / 1; 45 | grid-row-end: span 4; 46 | } 47 | -------------------------------------------------------------------------------- /test/cases/grid-area.out.css: -------------------------------------------------------------------------------- 1 | .a { 2 | -ms-grid-row: 5; 3 | -ms-grid-row-span: 1; 4 | -ms-grid-column: 1; 5 | -ms-grid-column-span: 5; 6 | grid-area: 5 / 1 / span 1 / span 5; 7 | } 8 | 9 | .b { 10 | -ms-grid-row: 3; 11 | -ms-grid-row-span: 1; 12 | -ms-grid-column: 1; 13 | -ms-grid-column-span: 3; 14 | grid-area: span 1 / span 3 / 4 / 4; 15 | } 16 | 17 | .c { 18 | -ms-grid-row: 2; 19 | -ms-grid-column: 2; 20 | grid-area: 2 / 2; 21 | } 22 | 23 | .d { 24 | grid-area: "custom-ident"; 25 | } 26 | 27 | .e { 28 | -ms-grid-row: 2; 29 | -ms-grid-row-span: 1; 30 | -ms-grid-column: 2; 31 | -ms-grid-column-span: 6; 32 | grid-area: 2 / 2 / 3 / 8; 33 | } 34 | 35 | .f { 36 | -ms-grid-row: 2; 37 | -ms-grid-row-span: 1; 38 | -ms-grid-column: 2; 39 | grid-area: 2 / 2 / 3; 40 | } 41 | 42 | /* emit warning */ 43 | .g { 44 | -ms-grid-row: 3; 45 | -ms-grid-column: 1; 46 | grid-area: 3 / 1; 47 | grid-row: 1; 48 | grid-column: 2; 49 | } 50 | 51 | .h { 52 | -ms-grid-row: 3; 53 | -ms-grid-column: 1; 54 | grid-area: 3 / 1; 55 | -ms-grid-column-span: 4; 56 | grid-column: 1 / span 4; 57 | } 58 | 59 | /* there should be no warning */ 60 | .i { 61 | -ms-grid-row: 3; 62 | -ms-grid-column: 1; 63 | grid-area: 3 / 1; 64 | -ms-grid-column-span: 4; 65 | grid-column-end: span 4; 66 | } 67 | 68 | .j { 69 | -ms-grid-row: 3; 70 | -ms-grid-column: 1; 71 | grid-area: 3 / 1; 72 | -ms-grid-row-span: 4; 73 | grid-row-end: span 4; 74 | } 75 | -------------------------------------------------------------------------------- /test/cases/grid-areas-duplicate-complex.css: -------------------------------------------------------------------------------- 1 | /*******************************\ 2 | COMPLEX DUPLICATE AREAS TEST 1 3 | \*******************************/ 4 | 5 | #main > #content { 6 | grid-area: content; 7 | } 8 | #main > .sidebar { 9 | grid-area: first-sb; 10 | } 11 | #main > .sidebar.second { 12 | grid-area: second-sb; 13 | } 14 | @media (min-width: 660px) { 15 | #main { 16 | display: grid; 17 | grid-template: "..... ... content ....."/20% 1em 1fr 20%; 18 | } 19 | #main.has-first-sidebar { 20 | grid-template: "..... ... first-sb .... content ....."/20% 1em 300px 30px 1fr 20%; 21 | } 22 | #main.has-second-sidebar { 23 | grid-template: "..... ... content .... second-sb ....."/20% 1em 1fr 30px 300px 20%; 24 | } 25 | #main.has-first-sidebar.has-second-sidebar { 26 | grid-template: "..... ... first-sb .... content .... second-sb ....."/20% 1em 200px 30px 1fr 30px 200px 20%; 27 | } 28 | } 29 | @media (max-width: 1499px) { 30 | #main { 31 | grid-template: "..... ... content ....."/10% 1em 1fr 10%; 32 | } 33 | #main.has-first-sidebar { 34 | grid-template: "..... ... first-sb .... content ....."/10% 1em 250px 30px 1fr 10%; 35 | } 36 | #main.has-second-sidebar { 37 | grid-template: "..... ... content .... second-sb ....."/10% 1em 1fr 30px 250px 10%; 38 | } 39 | #main.has-first-sidebar.has-second-sidebar { 40 | grid-template: "..... ... first-sb .... content .... second-sb ....."/10% 1em 200px 30px 1fr 30px 200px 10%; 41 | } 42 | } 43 | @media (max-width: 959px) { 44 | #main { 45 | grid-template: "..... ... content ....."/2em 1em 1fr 2em; 46 | } 47 | #main.has-first-sidebar { 48 | grid-template: "..... ... first-sb .... content ....."/2em 1em 200px 30px 1fr 2em; 49 | } 50 | #main.has-second-sidebar { 51 | grid-template: "..... ... content .... second-sb ....."/2em 1em 1fr 30px 200px 2em; 52 | } 53 | #main.has-first-sidebar.has-second-sidebar { 54 | grid-template: "..... ... first-sb .... content .... second-sb ....."/2em 1em 150px 30px 1fr 30px 150px 2em; 55 | } 56 | } 57 | 58 | /*******************************\ 59 | COMPLEX DUPLICATE AREAS TEST 2 60 | \*******************************/ 61 | 62 | #main-second { 63 | display: grid; 64 | grid-template: "..... ... content-2 ....."/20% 1em 1fr 20%; 65 | } 66 | #main-second.has-first-sidebar { 67 | grid-template: "..... ... first-sb-2 .... content-2 ....."/20% 1em 300px 30px 1fr 20%; 68 | } 69 | #main-second.has-second-sidebar { 70 | grid-template: "..... ... content-2 .... second-sb-2 ....."/20% 1em 1fr 30px 300px 20%; 71 | } 72 | #main-second.has-first-sidebar.has-second-sidebar { 73 | grid-template: "..... ... first-sb-2 .... content-2 .... second-sb-2 ....."/20% 1em 200px 30px 1fr 30px 200px 20%; 74 | } 75 | 76 | @media (max-width: 960px) { 77 | #main-second { 78 | grid-template: "..... ... content-2 ....."/2em 1em 1fr 2em; 79 | } 80 | #main-second.has-first-sidebar { 81 | grid-template: "..... ... first-sb-2 .... content-2 ....."/2em 1em 200px 30px 1fr 2em; 82 | } 83 | #main-second.has-second-sidebar { 84 | grid-template: "..... ... content-2 .... second-sb-2 ....."/2em 1em 1fr 30px 200px 2em; 85 | } 86 | #main-second.has-first-sidebar.has-second-sidebar { 87 | grid-template: "..... ... first-sb-2 .... content-2 .... second-sb-2 ....."/2em 1em 150px 30px 1fr 30px 150px 2em; 88 | } 89 | } 90 | 91 | .content { 92 | grid-area: content-2; 93 | } 94 | .sidebar { 95 | grid-area: first-sb-2; 96 | } 97 | .sidebar.second { 98 | grid-area: second-sb-2; 99 | } 100 | 101 | /* rule with not found identifier (should not break the compiler) */ 102 | .hello { 103 | grid-area: world; 104 | } 105 | -------------------------------------------------------------------------------- /test/cases/grid-autoplacement.css: -------------------------------------------------------------------------------- 1 | 2 | .grid-basic { 3 | display: grid; 4 | grid-template-columns: 1fr 1fr 1fr; 5 | grid-template-rows: auto; 6 | grid-gap: 30px; 7 | } 8 | 9 | /* Must work with repeat function */ 10 | .grid-repeat { 11 | display: grid; 12 | grid-template-columns: repeat(4, 1fr 2fr); 13 | grid-template-rows: repeat(2, auto); 14 | grid-gap: 30px; 15 | } 16 | 17 | /* Complex case */ 18 | .grid-complex { 19 | display: grid; 20 | grid-template-columns: 1fr repeat(2, minmax(100px, 200px)) 1fr; 21 | grid-template-rows: 100px repeat(2, auto); 22 | grid-gap: 30px 5px; 23 | } 24 | 25 | /* Must work inside media */ 26 | @media (min-width: 400px) { 27 | .grid-media { 28 | display: grid; 29 | grid-template-columns: 1fr 1fr 1fr; 30 | grid-template-rows: auto; 31 | grid-gap: 30px; 32 | } 33 | } 34 | 35 | /* Must consider autoflow value */ 36 | .grid-flow-column { 37 | display: grid; 38 | grid-auto-flow: column; 39 | grid-template-columns: 1fr 1fr 1fr; 40 | grid-template-rows: auto auto; 41 | grid-gap: 30px; 42 | } 43 | 44 | /* Show warning if rows are not defined 45 | and dense value is used */ 46 | .grid-warning-dense { 47 | grid-auto-flow: column dense; 48 | grid-template-columns: 1fr 1fr 1fr; 49 | } 50 | 51 | /* Show warning autoflow used without rows/columns */ 52 | .grid-warning-autoflow { 53 | grid-auto-flow: column; 54 | } 55 | 56 | .grid-autoplace-disabled { 57 | /* autoprefixer grid: no-autoplace */ 58 | grid-template-columns: 1fr 1fr 1fr; 59 | grid-template-rows: auto; 60 | grid-gap: 30px; 61 | } 62 | 63 | .grid-autoplace-gap-warn { 64 | grid-template-columns: 1fr 1fr 1fr; 65 | grid-gap: 30px; 66 | } -------------------------------------------------------------------------------- /test/cases/grid-media-rules.css: -------------------------------------------------------------------------------- 1 | @media (min-width: 30em) { 2 | .wrapper { 3 | display: grid; 4 | grid-template-areas: "a b"; 5 | } 6 | } 7 | 8 | @media (min-width: 60em) { 9 | .wrapper { 10 | grid-template-areas: "a b"; 11 | } 12 | } 13 | 14 | @media (min-width: 30em) { 15 | .a { 16 | grid-area: a; 17 | } 18 | } 19 | 20 | @media (min-width: 30em) { 21 | .b { 22 | grid-area: b; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/cases/grid-media-rules.out.css: -------------------------------------------------------------------------------- 1 | @media (min-width: 30em) { 2 | .wrapper { 3 | display: -ms-grid; 4 | display: grid; 5 | grid-template-areas: "a b"; 6 | } 7 | } 8 | 9 | @media (min-width: 60em) { 10 | .wrapper { 11 | grid-template-areas: "a b"; 12 | } 13 | } 14 | 15 | @media (min-width: 30em) { 16 | .a { 17 | grid-area: a; 18 | } 19 | } 20 | 21 | @media (min-width: 30em) { 22 | .b { 23 | grid-area: b; 24 | } 25 | } 26 | 27 | @media (min-width: 30em) { 28 | .a { 29 | -ms-grid-row: 1; 30 | -ms-grid-column: 1; 31 | } 32 | .b { 33 | -ms-grid-row: 1; 34 | -ms-grid-column: 2; 35 | } 36 | } 37 | 38 | @media (min-width: 60em) { 39 | .a { 40 | -ms-grid-row: 1; 41 | -ms-grid-column: 1; 42 | } 43 | .b { 44 | -ms-grid-row: 1; 45 | -ms-grid-column: 2; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/cases/grid-options.css: -------------------------------------------------------------------------------- 1 | /*****************\ 2 | AREAS ALGORITHM 3 | \*****************/ 4 | 5 | .grid-template-areas { 6 | display: grid; 7 | grid-template-areas: 8 | "a-conflict a-conflict" 9 | "b-conflict b-conflict"; 10 | } 11 | 12 | .grid-template-areas.conflict { 13 | display: grid; 14 | grid-template-areas: 15 | "a-conflict" 16 | "b-conflict"; 17 | } 18 | 19 | .a { 20 | grid-area: a-conflict; 21 | } 22 | 23 | .b { 24 | grid-area: b-conflict; 25 | } 26 | 27 | .grid-template { 28 | grid-template: 29 | [header-left] "head head" 30px [header-right] 30 | [main-left] "nav main" 1fr [main-right] 31 | [footer-left] "nav foot" 30px [footer-right] 32 | / 120px repeat(4, 250px 10px); 33 | } 34 | 35 | @media (min-width: 1000px) { 36 | .grid-template { 37 | grid-template: 38 | [header-left] "head" 30px [header-right] 39 | [nav-left] "nav" auto [nav-right] 40 | [main-left] "main" 1fr [main-right] 41 | [footer-left] "foot" 30px [footer-right] 42 | / 1fr; 43 | } 44 | } 45 | 46 | .grid-template.conflict { 47 | grid-template: 48 | "head head" 49 | "nav main" 20px 50 | "nav foot" 51 | / 1fr 1fr; 52 | } 53 | 54 | .head { 55 | grid-area: head; 56 | } 57 | 58 | .main { 59 | grid-area: main; 60 | } 61 | 62 | .nav { 63 | grid-area: nav; 64 | } 65 | 66 | .foot { 67 | grid-area: foot; 68 | } 69 | 70 | /*********************\ 71 | AUTOPLACE ALGORITHM 72 | \*********************/ 73 | 74 | .grid-autoplace { 75 | display: grid; 76 | grid-template-columns: 1fr 1fr 1fr; 77 | grid-template-rows: auto; 78 | grid-gap: 30px; 79 | } 80 | -------------------------------------------------------------------------------- /test/cases/grid-options.disabled.out.css: -------------------------------------------------------------------------------- 1 | /*****************\ 2 | AREAS ALGORITHM 3 | \*****************/ 4 | 5 | .grid-template-areas { 6 | display: grid; 7 | grid-template-areas: 8 | "a-conflict a-conflict" 9 | "b-conflict b-conflict"; 10 | } 11 | 12 | .grid-template-areas.conflict { 13 | display: grid; 14 | grid-template-areas: 15 | "a-conflict" 16 | "b-conflict"; 17 | } 18 | 19 | .a { 20 | grid-area: a-conflict; 21 | } 22 | 23 | .b { 24 | grid-area: b-conflict; 25 | } 26 | 27 | .grid-template { 28 | grid-template: 29 | [header-left] "head head" 30px [header-right] 30 | [main-left] "nav main" 1fr [main-right] 31 | [footer-left] "nav foot" 30px [footer-right] 32 | / 120px repeat(4, 250px 10px); 33 | } 34 | 35 | @media (min-width: 1000px) { 36 | .grid-template { 37 | grid-template: 38 | [header-left] "head" 30px [header-right] 39 | [nav-left] "nav" auto [nav-right] 40 | [main-left] "main" 1fr [main-right] 41 | [footer-left] "foot" 30px [footer-right] 42 | / 1fr; 43 | } 44 | } 45 | 46 | .grid-template.conflict { 47 | grid-template: 48 | "head head" 49 | "nav main" 20px 50 | "nav foot" 51 | / 1fr 1fr; 52 | } 53 | 54 | .head { 55 | grid-area: head; 56 | } 57 | 58 | .main { 59 | grid-area: main; 60 | } 61 | 62 | .nav { 63 | grid-area: nav; 64 | } 65 | 66 | .foot { 67 | grid-area: foot; 68 | } 69 | 70 | /*********************\ 71 | AUTOPLACE ALGORITHM 72 | \*********************/ 73 | 74 | .grid-autoplace { 75 | display: grid; 76 | grid-template-columns: 1fr 1fr 1fr; 77 | grid-template-rows: auto; 78 | grid-gap: 30px; 79 | } 80 | -------------------------------------------------------------------------------- /test/cases/grid-options.no-autoplace.out.css: -------------------------------------------------------------------------------- 1 | /*****************\ 2 | AREAS ALGORITHM 3 | \*****************/ 4 | 5 | .grid-template-areas { 6 | display: -ms-grid; 7 | display: grid; 8 | grid-template-areas: 9 | "a-conflict a-conflict" 10 | "b-conflict b-conflict"; 11 | } 12 | 13 | .grid-template-areas.conflict { 14 | display: -ms-grid; 15 | display: grid; 16 | grid-template-areas: 17 | "a-conflict" 18 | "b-conflict"; 19 | } 20 | 21 | .a { 22 | -ms-grid-row: 1; 23 | -ms-grid-column: 1; 24 | -ms-grid-column-span: 2; 25 | grid-area: a-conflict; 26 | } 27 | 28 | .grid-template-areas.conflict > .a { 29 | -ms-grid-row: 1; 30 | -ms-grid-column: 1; 31 | -ms-grid-column-span: 1; 32 | } 33 | 34 | .b { 35 | -ms-grid-row: 2; 36 | -ms-grid-column: 1; 37 | -ms-grid-column-span: 2; 38 | grid-area: b-conflict; 39 | } 40 | 41 | .grid-template-areas.conflict > .b { 42 | -ms-grid-row: 2; 43 | -ms-grid-column: 1; 44 | -ms-grid-column-span: 1; 45 | } 46 | 47 | .grid-template { 48 | -ms-grid-rows: 30px 1fr 30px; 49 | -ms-grid-columns: 120px (250px 10px)[4]; 50 | grid-template: 51 | [header-left] "head head" 30px [header-right] 52 | [main-left] "nav main" 1fr [main-right] 53 | [footer-left] "nav foot" 30px [footer-right] 54 | / 120px repeat(4, 250px 10px); 55 | } 56 | 57 | @media (min-width: 1000px) { 58 | .grid-template { 59 | -ms-grid-rows: 30px auto 1fr 30px; 60 | -ms-grid-columns: 1fr; 61 | grid-template: 62 | [header-left] "head" 30px [header-right] 63 | [nav-left] "nav" auto [nav-right] 64 | [main-left] "main" 1fr [main-right] 65 | [footer-left] "foot" 30px [footer-right] 66 | / 1fr; 67 | } 68 | } 69 | 70 | .grid-template.conflict { 71 | -ms-grid-rows: auto 20px auto; 72 | -ms-grid-columns: 1fr 1fr; 73 | grid-template: 74 | "head head" 75 | "nav main" 20px 76 | "nav foot" 77 | / 1fr 1fr; 78 | } 79 | 80 | .head { 81 | -ms-grid-row: 1; 82 | -ms-grid-column: 1; 83 | -ms-grid-column-span: 2; 84 | grid-area: head; 85 | } 86 | 87 | .grid-template.conflict > .head { 88 | -ms-grid-row: 1; 89 | -ms-grid-column: 1; 90 | -ms-grid-column-span: 2; 91 | } 92 | 93 | .main { 94 | -ms-grid-row: 2; 95 | -ms-grid-column: 2; 96 | grid-area: main; 97 | } 98 | 99 | .grid-template.conflict > .main { 100 | -ms-grid-row: 2; 101 | -ms-grid-column: 2; 102 | } 103 | 104 | .nav { 105 | -ms-grid-row: 2; 106 | -ms-grid-row-span: 2; 107 | -ms-grid-column: 1; 108 | grid-area: nav; 109 | } 110 | 111 | .grid-template.conflict > .nav { 112 | -ms-grid-row: 2; 113 | -ms-grid-row-span: 2; 114 | -ms-grid-column: 1; 115 | } 116 | 117 | .foot { 118 | -ms-grid-row: 3; 119 | -ms-grid-column: 2; 120 | grid-area: foot; 121 | } 122 | 123 | .grid-template.conflict > .foot { 124 | -ms-grid-row: 3; 125 | -ms-grid-column: 2; 126 | } 127 | 128 | @media (min-width: 1000px) { 129 | .head { 130 | -ms-grid-row: 1; 131 | -ms-grid-column: 1; 132 | -ms-grid-column-span: 1; 133 | } 134 | .main { 135 | -ms-grid-row: 3; 136 | -ms-grid-column: 1; 137 | } 138 | .nav { 139 | -ms-grid-row: 2; 140 | -ms-grid-row-span: 1; 141 | -ms-grid-column: 1; 142 | } 143 | .foot { 144 | -ms-grid-row: 4; 145 | -ms-grid-column: 1; 146 | } 147 | } 148 | 149 | /*********************\ 150 | AUTOPLACE ALGORITHM 151 | \*********************/ 152 | 153 | .grid-autoplace { 154 | display: -ms-grid; 155 | display: grid; 156 | -ms-grid-columns: 1fr 1fr 1fr; 157 | grid-template-columns: 1fr 1fr 1fr; 158 | -ms-grid-rows: auto; 159 | grid-template-rows: auto; 160 | grid-gap: 30px; 161 | } 162 | -------------------------------------------------------------------------------- /test/cases/grid-status.css: -------------------------------------------------------------------------------- 1 | /* autoprefixer grid: on */ 2 | /* autoprefixer grid: off */ 3 | 4 | .grid-disabled { 5 | /* autoprefixer: off */ 6 | display: grid; 7 | grid-auto-flow: column; 8 | } 9 | 10 | .grid { 11 | display: grid; 12 | grid-template-columns: 1fr 1fr 1fr; 13 | grid-template-rows: 100px 100px; 14 | /* autoprefixer: ignore next */ 15 | grid-auto-flow: column; 16 | } 17 | 18 | .grid-status-off { 19 | /* autoprefixer grid: off */ 20 | /* autoprefixer grid: off */ 21 | display: grid; 22 | grid-template-columns: 1fr 1fr 1fr; 23 | grid-template-rows: 100px 100px; 24 | } 25 | 26 | .grid-2 { 27 | display: grid; 28 | } 29 | 30 | @supports (grid-auto-rows: 100px) { 31 | .disabled { 32 | display: grid; 33 | grid-auto-rows: 100px; 34 | } 35 | } 36 | 37 | .grid-autoplace-enabled { 38 | /* autoprefixer grid: autoplace */ 39 | display: grid; 40 | grid-template-columns: 1fr 1fr 1fr; 41 | grid-template-rows: auto; 42 | grid-gap: 30px; 43 | } 44 | 45 | .grid-autoplace-disabled { 46 | /* autoprefixer grid: off */ 47 | /* autoprefixer grid: autoplace */ 48 | grid-template-columns: 1fr 1fr 1fr; 49 | grid-template-rows: auto; 50 | grid-gap: 30px; 51 | } 52 | 53 | .grid-autoplace-disabled-2 { 54 | /* autoprefixer grid: no-autoplace */ 55 | grid-template-columns: 1fr 1fr 1fr; 56 | grid-template-rows: auto; 57 | } 58 | -------------------------------------------------------------------------------- /test/cases/grid-status.out.css: -------------------------------------------------------------------------------- 1 | /* autoprefixer grid: on */ 2 | /* autoprefixer grid: off */ 3 | 4 | .grid-disabled { 5 | /* autoprefixer: off */ 6 | display: grid; 7 | grid-auto-flow: column; 8 | } 9 | 10 | .grid { 11 | display: -ms-grid; 12 | display: grid; 13 | -ms-grid-columns: 1fr 1fr 1fr; 14 | grid-template-columns: 1fr 1fr 1fr; 15 | -ms-grid-rows: 100px 100px; 16 | grid-template-rows: 100px 100px; 17 | /* autoprefixer: ignore next */ 18 | grid-auto-flow: column; 19 | } 20 | 21 | .grid-status-off { 22 | /* autoprefixer grid: off */ 23 | /* autoprefixer grid: off */ 24 | display: grid; 25 | grid-template-columns: 1fr 1fr 1fr; 26 | grid-template-rows: 100px 100px; 27 | } 28 | 29 | .grid-2 { 30 | display: -ms-grid; 31 | display: grid; 32 | } 33 | 34 | @supports (grid-auto-rows: 100px) { 35 | .disabled { 36 | display: grid; 37 | grid-auto-rows: 100px; 38 | } 39 | } 40 | 41 | .grid-autoplace-enabled { 42 | /* autoprefixer grid: autoplace */ 43 | display: -ms-grid; 44 | display: grid; 45 | -ms-grid-columns: 1fr 30px 1fr 30px 1fr; 46 | grid-template-columns: 1fr 1fr 1fr; 47 | -ms-grid-rows: auto; 48 | grid-template-rows: auto; 49 | grid-gap: 30px; 50 | } 51 | 52 | .grid-autoplace-enabled > *:nth-child(1) { 53 | -ms-grid-row: 1; 54 | -ms-grid-column: 1; 55 | } 56 | 57 | .grid-autoplace-enabled > *:nth-child(2) { 58 | -ms-grid-row: 1; 59 | -ms-grid-column: 3; 60 | } 61 | 62 | .grid-autoplace-enabled > *:nth-child(3) { 63 | -ms-grid-row: 1; 64 | -ms-grid-column: 5; 65 | } 66 | 67 | .grid-autoplace-disabled { 68 | /* autoprefixer grid: off */ 69 | /* autoprefixer grid: autoplace */ 70 | grid-template-columns: 1fr 1fr 1fr; 71 | grid-template-rows: auto; 72 | grid-gap: 30px; 73 | } 74 | 75 | .grid-autoplace-disabled-2 { 76 | /* autoprefixer grid: no-autoplace */ 77 | -ms-grid-columns: 1fr 1fr 1fr; 78 | grid-template-columns: 1fr 1fr 1fr; 79 | -ms-grid-rows: auto; 80 | grid-template-rows: auto; 81 | } 82 | -------------------------------------------------------------------------------- /test/cases/grouping-rule.css: -------------------------------------------------------------------------------- 1 | .grid { 2 | display: grid; 3 | } 4 | 5 | .a, 6 | .b, 7 | .c::selection, 8 | .d:read-only, 9 | .e::placeholder { 10 | color: yellow; 11 | } 12 | 13 | ::selection { 14 | color: red; 15 | } 16 | 17 | :read-only { 18 | color: black; 19 | } 20 | 21 | .f:read-write, .g:read-write { 22 | background: #fff; 23 | } 24 | -------------------------------------------------------------------------------- /test/cases/grouping-rule.out.css: -------------------------------------------------------------------------------- 1 | .grid { 2 | display: -ms-grid; 3 | display: grid; 4 | } 5 | 6 | .c::-moz-selection { 7 | color: yellow; 8 | } 9 | 10 | .e::-webkit-input-placeholder { 11 | color: yellow; 12 | } 13 | 14 | .e:-moz-placeholder { 15 | color: yellow; 16 | } 17 | 18 | .e::-moz-placeholder { 19 | color: yellow; 20 | } 21 | 22 | .e:-ms-input-placeholder { 23 | color: yellow; 24 | } 25 | 26 | .e::-ms-input-placeholder { 27 | color: yellow; 28 | } 29 | 30 | .d:-moz-read-only { 31 | color: yellow; 32 | } 33 | 34 | .a, 35 | .b, 36 | .c::selection, 37 | .d:read-only, 38 | .e::placeholder { 39 | color: yellow; 40 | } 41 | 42 | ::-moz-selection { 43 | color: red; 44 | } 45 | 46 | ::selection { 47 | color: red; 48 | } 49 | 50 | :-moz-read-only { 51 | color: black; 52 | } 53 | 54 | :read-only { 55 | color: black; 56 | } 57 | 58 | .f:-moz-read-write, .g:-moz-read-write { 59 | background: #fff; 60 | } 61 | 62 | .f:read-write, .g:read-write { 63 | background: #fff; 64 | } 65 | -------------------------------------------------------------------------------- /test/cases/ignore-next.css: -------------------------------------------------------------------------------- 1 | .ignore-property { 2 | /* autoprefixer: ignore next */ 3 | mask: none; 4 | background: linear-gradient(to bottom, white, black); 5 | } 6 | 7 | .ignore-property_nospaces-comment { 8 | /* autoprefixer:ignore next */ 9 | mask: none; 10 | background: linear-gradient(to bottom, white, black); 11 | } 12 | 13 | /* autoprefixer: ignore next */ 14 | .ignore-rule::placeholder { 15 | mask: none; 16 | } 17 | 18 | /* autoprefixer: ignore next */ 19 | @media (min-resolution: 2dppx) { 20 | .ignore-at-rule::placeholder { 21 | mask: none; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/cases/ignore-next.out.css: -------------------------------------------------------------------------------- 1 | .ignore-property { 2 | /* autoprefixer: ignore next */ 3 | mask: none; 4 | background: -webkit-linear-gradient(top, white, black); 5 | background: -o-linear-gradient(top, white, black); 6 | background: linear-gradient(to bottom, white, black); 7 | } 8 | 9 | .ignore-property_nospaces-comment { 10 | /* autoprefixer:ignore next */ 11 | mask: none; 12 | background: -webkit-linear-gradient(top, white, black); 13 | background: -o-linear-gradient(top, white, black); 14 | background: linear-gradient(to bottom, white, black); 15 | } 16 | 17 | /* autoprefixer: ignore next */ 18 | .ignore-rule::placeholder { 19 | -webkit-mask: none; 20 | mask: none; 21 | } 22 | 23 | /* autoprefixer: ignore next */ 24 | @media (min-resolution: 2dppx) { 25 | .ignore-at-rule::-webkit-input-placeholder { 26 | -webkit-mask: none; 27 | mask: none; 28 | } 29 | .ignore-at-rule::placeholder { 30 | -webkit-mask: none; 31 | mask: none; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/cases/image-rendering.css: -------------------------------------------------------------------------------- 1 | img { 2 | image-rendering: crisp-edges; 3 | } 4 | 5 | img.other { 6 | image-rendering: pixelated; 7 | } 8 | 9 | img.already { 10 | -ms-interpolation-mode: nearest-neighbor; 11 | display: block; 12 | image-rendering: crisp-edges; 13 | image-rendering: pixelated; 14 | } 15 | -------------------------------------------------------------------------------- /test/cases/image-rendering.out.css: -------------------------------------------------------------------------------- 1 | img { 2 | image-rendering: crisp-edges; 3 | } 4 | 5 | img.other { 6 | -ms-interpolation-mode: nearest-neighbor; 7 | image-rendering: -webkit-optimize-contrast; 8 | image-rendering: -moz-crisp-edges; 9 | image-rendering: -o-pixelated; 10 | image-rendering: pixelated; 11 | } 12 | 13 | img.already { 14 | -ms-interpolation-mode: nearest-neighbor; 15 | display: block; 16 | image-rendering: crisp-edges; 17 | image-rendering: -webkit-optimize-contrast; 18 | image-rendering: -moz-crisp-edges; 19 | image-rendering: -o-pixelated; 20 | image-rendering: pixelated; 21 | } 22 | -------------------------------------------------------------------------------- /test/cases/image-set.css: -------------------------------------------------------------------------------- 1 | a { 2 | background-image: image-set(url(foo@1x.png) 1x, url(foo@2x.png) 2x); 3 | } 4 | 5 | h1 { 6 | background-image: image-set('foo@1x.png' 1x, "foo@2x.png" 2x); 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/image-set.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | background-image: -webkit-image-set(url(foo@1x.png) 1x, url(foo@2x.png) 2x); 3 | background-image: image-set(url(foo@1x.png) 1x, url(foo@2x.png) 2x); 4 | } 5 | 6 | h1 { 7 | background-image: -webkit-image-set(url('foo@1x.png') 1x, url("foo@2x.png") 2x); 8 | background-image: image-set('foo@1x.png' 1x, "foo@2x.png" 2x); 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/intrinsic.css: -------------------------------------------------------------------------------- 1 | a { 2 | width: stretch; 3 | } 4 | 5 | b { 6 | height: max-content; 7 | } 8 | 9 | p { 10 | block-size: min-content; 11 | min-inline-size: fit-content; 12 | } 13 | 14 | .outdated { 15 | width: fill; 16 | } 17 | 18 | .old { 19 | width: fill-available; 20 | } 21 | 22 | .ok { 23 | width: calc(100% - var(--jqx-circular-progress-bar-fill-size)); 24 | } 25 | 26 | .grid { 27 | grid: min-content max-content / fit-content(500px); 28 | } 29 | 30 | .grid-template { 31 | grid-template: min-content / fit-content(10px) max-content; 32 | grid-template: max-content 1fr max-content max-content / max-content 1fr; 33 | } 34 | 35 | .grid-template-columns { 36 | grid-template-columns: minmax(100px, min-content); 37 | } 38 | 39 | .grid-auto-columns { 40 | grid-auto-columns: min-content max-content; 41 | } 42 | 43 | .ignore { 44 | width: -webkit-fill-available; 45 | } 46 | -------------------------------------------------------------------------------- /test/cases/intrinsic.ff.css: -------------------------------------------------------------------------------- 1 | a { 2 | width: -moz-available; 3 | width: stretch; 4 | } 5 | 6 | b { 7 | height: max-content; 8 | } 9 | 10 | p { 11 | block-size: min-content; 12 | min-inline-size: -moz-fit-content; 13 | min-inline-size: fit-content; 14 | } 15 | 16 | .outdated { 17 | width: fill; 18 | } 19 | 20 | .old { 21 | width: fill-available; 22 | } 23 | 24 | .ok { 25 | width: calc(100% - var(--jqx-circular-progress-bar-fill-size)); 26 | } 27 | 28 | .grid { 29 | grid: min-content max-content / fit-content(500px); 30 | } 31 | 32 | .grid-template { 33 | grid-template: min-content / fit-content(10px) max-content; 34 | grid-template: max-content 1fr max-content max-content / max-content 1fr; 35 | } 36 | 37 | .grid-template-columns { 38 | grid-template-columns: minmax(100px, min-content); 39 | } 40 | 41 | .grid-auto-columns { 42 | grid-auto-columns: min-content max-content; 43 | } 44 | 45 | .ignore { 46 | width: -webkit-fill-available; 47 | } 48 | -------------------------------------------------------------------------------- /test/cases/intrinsic.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | width: -webkit-fill-available; 3 | width: -moz-available; 4 | width: stretch; 5 | } 6 | 7 | b { 8 | height: -webkit-max-content; 9 | height: -moz-max-content; 10 | height: max-content; 11 | } 12 | 13 | p { 14 | block-size: -webkit-min-content; 15 | block-size: -moz-min-content; 16 | block-size: min-content; 17 | min-inline-size: -webkit-fit-content; 18 | min-inline-size: -moz-fit-content; 19 | min-inline-size: fit-content; 20 | } 21 | 22 | .outdated { 23 | width: -webkit-fill-available; 24 | width: -moz-available; 25 | width: fill; 26 | } 27 | 28 | .old { 29 | width: -webkit-fill-available; 30 | width: -moz-available; 31 | width: fill-available; 32 | } 33 | 34 | .ok { 35 | width: -webkit-calc(100% - var(--jqx-circular-progress-bar-fill-size)); 36 | width: calc(100% - var(--jqx-circular-progress-bar-fill-size)); 37 | } 38 | 39 | .grid { 40 | grid: -webkit-min-content -webkit-max-content / fit-content(500px); 41 | grid: min-content max-content / fit-content(500px); 42 | } 43 | 44 | .grid-template { 45 | grid-template: -webkit-min-content / fit-content(10px) -webkit-max-content; 46 | grid-template: min-content / fit-content(10px) max-content; 47 | grid-template: -webkit-max-content 1fr -webkit-max-content -webkit-max-content / -webkit-max-content 1fr; 48 | grid-template: max-content 1fr max-content max-content / max-content 1fr; 49 | } 50 | 51 | .grid-template-columns { 52 | grid-template-columns: minmax(100px, -webkit-min-content); 53 | grid-template-columns: minmax(100px, min-content); 54 | } 55 | 56 | .grid-auto-columns { 57 | grid-auto-columns: -webkit-min-content -webkit-max-content; 58 | grid-auto-columns: min-content max-content; 59 | } 60 | 61 | .ignore { 62 | width: -webkit-fill-available; 63 | } 64 | -------------------------------------------------------------------------------- /test/cases/keyframes.css: -------------------------------------------------------------------------------- 1 | @keyframes anim { 2 | from { 3 | top: calc(10% + 10px); 4 | transform: rotate(10deg) 5 | } 6 | 50% { 7 | top: 0; 8 | display: flex 9 | } 10 | to { 11 | top: calc(10%); 12 | transform: rotate(0) 13 | } 14 | } 15 | @media screen { 16 | @keyframes inside {} 17 | } 18 | @keyframes spaces { from { color: black } to { color: white } } 19 | -------------------------------------------------------------------------------- /test/cases/keyframes.out.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes anim { 2 | from { 3 | top: -webkit-calc(10% + 10px); 4 | top: calc(10% + 10px); 5 | -webkit-transform: rotate(10deg); 6 | transform: rotate(10deg) 7 | } 8 | 50% { 9 | top: 0; 10 | display: -webkit-box; 11 | display: -webkit-flex; 12 | display: flex 13 | } 14 | to { 15 | top: -webkit-calc(10%); 16 | top: calc(10%); 17 | -webkit-transform: rotate(0); 18 | transform: rotate(0) 19 | } 20 | } 21 | @-o-keyframes anim { 22 | from { 23 | top: calc(10% + 10px); 24 | -o-transform: rotate(10deg); 25 | transform: rotate(10deg) 26 | } 27 | 50% { 28 | top: 0; 29 | display: flex 30 | } 31 | to { 32 | top: calc(10%); 33 | -o-transform: rotate(0); 34 | transform: rotate(0) 35 | } 36 | } 37 | @keyframes anim { 38 | from { 39 | top: -webkit-calc(10% + 10px); 40 | top: calc(10% + 10px); 41 | -webkit-transform: rotate(10deg); 42 | -o-transform: rotate(10deg); 43 | transform: rotate(10deg) 44 | } 45 | 50% { 46 | top: 0; 47 | display: -webkit-box; 48 | display: -webkit-flex; 49 | display: flex 50 | } 51 | to { 52 | top: -webkit-calc(10%); 53 | top: calc(10%); 54 | -webkit-transform: rotate(0); 55 | -o-transform: rotate(0); 56 | transform: rotate(0) 57 | } 58 | } 59 | @media screen { 60 | @-webkit-keyframes inside {} 61 | @-o-keyframes inside {} 62 | @keyframes inside {} 63 | } 64 | @-webkit-keyframes spaces { from { color: black } to { color: white } } 65 | @-o-keyframes spaces { from { color: black } to { color: white } } 66 | @keyframes spaces { from { color: black } to { color: white } } 67 | -------------------------------------------------------------------------------- /test/cases/logical.css: -------------------------------------------------------------------------------- 1 | a { 2 | margin-block-start: 1px; 3 | margin-inline-start: 1px; 4 | padding-inline-end: 1px; 5 | } 6 | 7 | .border { 8 | border-block-end: 1px; 9 | border-inline-end: 1px; 10 | } 11 | -------------------------------------------------------------------------------- /test/cases/logical.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-margin-before: 1px; 3 | margin-block-start: 1px; 4 | -webkit-margin-start: 1px; 5 | -moz-margin-start: 1px; 6 | margin-inline-start: 1px; 7 | -webkit-padding-end: 1px; 8 | -moz-padding-end: 1px; 9 | padding-inline-end: 1px; 10 | } 11 | 12 | .border { 13 | -webkit-border-after: 1px; 14 | border-block-end: 1px; 15 | -webkit-border-end: 1px; 16 | -moz-border-end: 1px; 17 | border-inline-end: 1px; 18 | } 19 | -------------------------------------------------------------------------------- /test/cases/mask-border.css: -------------------------------------------------------------------------------- 1 | a { 2 | mask-border-source: url(image.png); 3 | mask-border-slice: 50% fill; 4 | mask-border-width: auto 1 50%; 5 | mask-border-outset: 0 1 2; 6 | mask-border-repeat: repeat space; 7 | mask-border: url(#foo) 1 fill; 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/mask-border.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-mask-box-image-source: url(image.png); 3 | mask-border-source: url(image.png); 4 | -webkit-mask-box-image-slice: 50% fill; 5 | mask-border-slice: 50% fill; 6 | -webkit-mask-box-image-width: auto 1 50%; 7 | mask-border-width: auto 1 50%; 8 | -webkit-mask-box-image-outset: 0 1 2; 9 | mask-border-outset: 0 1 2; 10 | -webkit-mask-box-image-repeat: repeat space; 11 | mask-border-repeat: repeat space; 12 | -webkit-mask-box-image: url(#foo) 1 fill; 13 | mask-border: url(#foo) 1 fill; 14 | } 15 | -------------------------------------------------------------------------------- /test/cases/mask-composite.css: -------------------------------------------------------------------------------- 1 | a { 2 | mask: url(add.png) add, url(substract.png); 3 | } 4 | 5 | a { 6 | mask: url(intersect.png) intersect, url(exclude.png); 7 | } 8 | 9 | a { 10 | mask: url(image.png) intersect, url(image.png) add, url(image.png); 11 | } 12 | 13 | a { 14 | mask-composite: add; 15 | } 16 | 17 | a { 18 | mask-composite:; 19 | } 20 | 21 | a { 22 | mask-composite: add, subtract, exclude; 23 | } -------------------------------------------------------------------------------- /test/cases/mask-composite.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-mask: url(add.png), url(substract.png); 3 | -webkit-mask-composite: source-over; 4 | mask: url(add.png) add, url(substract.png); 5 | } 6 | 7 | a { 8 | -webkit-mask: url(intersect.png), url(exclude.png); 9 | -webkit-mask-composite: source-in, xor; 10 | mask: url(intersect.png) intersect, url(exclude.png); 11 | } 12 | 13 | a { 14 | -webkit-mask: url(image.png), url(image.png), url(image.png); 15 | -webkit-mask-composite: source-in, source-over, xor; 16 | mask: url(image.png) intersect, url(image.png) add, url(image.png); 17 | } 18 | 19 | a { 20 | -webkit-mask-composite: source-over; 21 | mask-composite: add; 22 | } 23 | 24 | a { 25 | mask-composite:; 26 | } 27 | 28 | a { 29 | -webkit-mask-composite: source-over, source-out, xor; 30 | mask-composite: add, subtract, exclude; 31 | } -------------------------------------------------------------------------------- /test/cases/mistakes.css: -------------------------------------------------------------------------------- 1 | a { 2 | -ms-transition: all 1s; 3 | transition: all 1s; 4 | 5 | -ms-border-radius: 5px; 6 | border-radius: 5px; 7 | 8 | background: -ms-linear-gradient(white, black); 9 | background: linear-gradient(white, black); 10 | } 11 | -------------------------------------------------------------------------------- /test/cases/mistakes.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | transition: all 1s; 3 | border-radius: 5px; 4 | background: linear-gradient(white, black); 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/multicolumn.css: -------------------------------------------------------------------------------- 1 | .a { 2 | break-inside: auto; 3 | break-before: auto; 4 | break-after: auto; 5 | } 6 | 7 | .b { 8 | break-inside: avoid; 9 | } 10 | 11 | .c { 12 | break-inside: avoid-column; 13 | } 14 | 15 | .d { 16 | break-inside: avoid-page; 17 | } 18 | 19 | .e { 20 | break-inside: avoid-region; 21 | } 22 | 23 | .f { 24 | break-inside: region; 25 | } 26 | -------------------------------------------------------------------------------- /test/cases/multicolumn.out.css: -------------------------------------------------------------------------------- 1 | .a { 2 | -webkit-column-break-inside: auto; 3 | break-inside: auto; 4 | -webkit-column-break-before: auto; 5 | break-before: auto; 6 | -webkit-column-break-after: auto; 7 | break-after: auto; 8 | } 9 | 10 | .b { 11 | -webkit-column-break-inside: avoid; 12 | break-inside: avoid; 13 | } 14 | 15 | .c { 16 | -webkit-column-break-inside: avoid; 17 | break-inside: avoid-column; 18 | } 19 | 20 | .d { 21 | break-inside: avoid-page; 22 | } 23 | 24 | .e { 25 | break-inside: avoid-region; 26 | } 27 | 28 | .f { 29 | break-inside: region; 30 | } 31 | -------------------------------------------------------------------------------- /test/cases/notes.css: -------------------------------------------------------------------------------- 1 | a { 2 | display: flex; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/notes.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | display: -webkit-flex; 3 | display: flex; 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/overscroll-behavior.css: -------------------------------------------------------------------------------- 1 | .none { 2 | overscroll-behavior: none; 3 | } 4 | 5 | .contain { 6 | overscroll-behavior: contain; 7 | } 8 | 9 | .auto { 10 | overscroll-behavior: auto; 11 | } 12 | 13 | .inherit { 14 | overscroll-behavior: inherit; 15 | } 16 | -------------------------------------------------------------------------------- /test/cases/overscroll-behavior.out.css: -------------------------------------------------------------------------------- 1 | .none { 2 | -ms-scroll-chaining: none; 3 | overscroll-behavior: none; 4 | } 5 | 6 | .contain { 7 | -ms-scroll-chaining: none; 8 | overscroll-behavior: contain; 9 | } 10 | 11 | .auto { 12 | -ms-scroll-chaining: chained; 13 | overscroll-behavior: auto; 14 | } 15 | 16 | .inherit { 17 | -ms-scroll-chaining: inherit; 18 | overscroll-behavior: inherit; 19 | } 20 | -------------------------------------------------------------------------------- /test/cases/pie.css: -------------------------------------------------------------------------------- 1 | a { 2 | -pie-background: linear-gradient(white, black) 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/placeholder-shown.css: -------------------------------------------------------------------------------- 1 | :placeholder-shown { 2 | background: #eee 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/placeholder-shown.out.css: -------------------------------------------------------------------------------- 1 | :-ms-input-placeholder { 2 | background: #eee 3 | } 4 | :placeholder-shown { 5 | background: #eee 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/placeholder.css: -------------------------------------------------------------------------------- 1 | ::placeholder { 2 | color: #999 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/placeholder.out.css: -------------------------------------------------------------------------------- 1 | ::-webkit-input-placeholder { 2 | color: #999 3 | } 4 | :-moz-placeholder { 5 | color: #999 6 | } 7 | ::-moz-placeholder { 8 | color: #999 9 | } 10 | :-ms-input-placeholder { 11 | color: #999 12 | } 13 | ::-ms-input-placeholder { 14 | color: #999 15 | } 16 | ::placeholder { 17 | color: #999 18 | } 19 | -------------------------------------------------------------------------------- /test/cases/print-color-adjust.css: -------------------------------------------------------------------------------- 1 | .a { 2 | color-adjust: economy; 3 | } 4 | 5 | .b { 6 | print-color-adjust: exact; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/print-color-adjust.out.css: -------------------------------------------------------------------------------- 1 | .a { 2 | -webkit-print-color-adjust: economy; 3 | color-adjust: economy; 4 | } 5 | 6 | .b { 7 | -webkit-print-color-adjust: exact; 8 | color-adjust: exact; 9 | print-color-adjust: exact; 10 | } 11 | -------------------------------------------------------------------------------- /test/cases/resolution.css: -------------------------------------------------------------------------------- 1 | @media (min-resolution: 2dppx), 2 | (min-resolution: 192dpi) { } 3 | 4 | @media (min-resolution: 2.5dppx) { } 5 | 6 | @media (min-resolution: 144dpi) { } 7 | 8 | @media (min-resolution: 2x) { } 9 | 10 | @media (min-resolution: 120dpi) { } 11 | 12 | @media (min-resolution: 2dppx) { } 13 | 14 | @media only screen and (min-resolution: 124.8dpi) { } 15 | 16 | @media (min-resolution: 113.38dpcm) { } 17 | -------------------------------------------------------------------------------- /test/cases/resolution.out.css: -------------------------------------------------------------------------------- 1 | @media (-webkit-min-device-pixel-ratio: 2), 2 | (min--moz-device-pixel-ratio: 2), 3 | (-o-min-device-pixel-ratio: 2/1), 4 | (min-resolution: 2dppx), 5 | (min-resolution: 192dpi) { } 6 | 7 | @media (-webkit-min-device-pixel-ratio: 2.5), (min--moz-device-pixel-ratio: 2.5), (-o-min-device-pixel-ratio: 5/2), (min-resolution: 2.5dppx) { } 8 | 9 | @media (-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-resolution: 144dpi) { } 10 | 11 | @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2x) { } 12 | 13 | @media (-webkit-min-device-pixel-ratio: 1.25), (min--moz-device-pixel-ratio: 1.25), (-o-min-device-pixel-ratio: 5/4), (min-resolution: 120dpi) { } 14 | 15 | @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx) { } 16 | 17 | @media only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 13/10), only screen and (min-resolution: 124.8dpi) { } 18 | 19 | @media (-webkit-min-device-pixel-ratio: 3), (min--moz-device-pixel-ratio: 3), (-o-min-device-pixel-ratio: 3/1), (min-resolution: 113.38dpcm) { } 20 | -------------------------------------------------------------------------------- /test/cases/scope.css: -------------------------------------------------------------------------------- 1 | a { 2 | /* autoprefixer: off */ 3 | -webkit-border-radius: 4px; 4 | border-radius: 4px; 5 | /* autoprefixer: on */ 6 | mask: none; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/scope.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | /* autoprefixer: off */ 3 | -webkit-border-radius: 4px; 4 | border-radius: 4px; 5 | /* autoprefixer: on */ 6 | mask: none; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/selectors.css: -------------------------------------------------------------------------------- 1 | 2 | [href=:fullscreen] {} 3 | 4 | :fullscreen a { 5 | box-sizing: border-box 6 | } 7 | 8 | :fullscreen a { 9 | color: black 10 | } 11 | 12 | :-moz-full-screen a { 13 | transform: translate3d(0, 0, 0); 14 | } 15 | -------------------------------------------------------------------------------- /test/cases/selectors.out.css: -------------------------------------------------------------------------------- 1 | 2 | [href=:fullscreen] {} 3 | 4 | :-webkit-full-screen a { 5 | box-sizing: border-box 6 | } 7 | 8 | :-moz-full-screen a { 9 | -moz-box-sizing: border-box; 10 | box-sizing: border-box 11 | } 12 | 13 | :fullscreen a { 14 | -moz-box-sizing: border-box; 15 | box-sizing: border-box 16 | } 17 | 18 | :-webkit-full-screen a { 19 | color: black 20 | } 21 | 22 | :-moz-full-screen a { 23 | color: black 24 | } 25 | 26 | :fullscreen a { 27 | color: black 28 | } 29 | 30 | :-moz-full-screen a { 31 | transform: translate3d(0, 0, 0); 32 | } 33 | -------------------------------------------------------------------------------- /test/cases/style.css: -------------------------------------------------------------------------------- 1 | a { 2 | color : black; 3 | display: flex 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/style.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | color : black; 3 | display: -webkit-flex; 4 | display: flex 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/supports.css: -------------------------------------------------------------------------------- 1 | @supports 2 | (cursor: grab) or 3 | not (mask: none) and 4 | (color: black) { 5 | a { 6 | color: black; 7 | } 8 | } 9 | 10 | @supports not (display: flex) { 11 | a { 12 | color: #000; 13 | } 14 | } 15 | 16 | @supports ((perspective: 1px) and (not (-webkit-overflow-scrolling: touch))) { 17 | a { 18 | color: #000; 19 | } 20 | } 21 | 22 | @supports (animation-name: test) and ((animation-timing-function: steps(4, end)) or (animation-timing-function: step-end)) { 23 | animation-name: test; 24 | } 25 | 26 | @supports (transition: color 1s) { 27 | transition: color 1s; 28 | } 29 | -------------------------------------------------------------------------------- /test/cases/supports.out.css: -------------------------------------------------------------------------------- 1 | @supports 2 | ((cursor: -webkit-grab) or (cursor: grab)) or 3 | not ((-webkit-mask: none) or (mask: none)) and 4 | (color: black) { 5 | a { 6 | color: black; 7 | } 8 | } 9 | 10 | @supports not ((display: -webkit-flex) or (display: flex)) { 11 | a { 12 | color: #000; 13 | } 14 | } 15 | 16 | @supports (((-webkit-perspective: 1px) or (perspective: 1px)) and (not (-webkit-overflow-scrolling: touch))) { 17 | a { 18 | color: #000; 19 | } 20 | } 21 | 22 | @supports ((-webkit-animation-name: test) or (animation-name: test)) and (((-webkit-animation-timing-function: steps(4, end)) or (animation-timing-function: steps(4, end))) or ((-webkit-animation-timing-function: step-end) or (animation-timing-function: step-end))) { 23 | -webkit-animation-name: test; 24 | animation-name: test; 25 | } 26 | 27 | @supports (transition: color 1s) { 28 | -webkit-transition: color 1s; 29 | transition: color 1s; 30 | } 31 | -------------------------------------------------------------------------------- /test/cases/syntax.css: -------------------------------------------------------------------------------- 1 | @page { 2 | margin: 0.5cm; 3 | } 4 | 5 | /* comment */ 6 | 7 | @-moz-document url-prefix() { 8 | a { 9 | color: black; 10 | } 11 | } 12 | 13 | a { 14 | /* c */ 15 | color/**/: white; 16 | padding: 0 /*{}*/ 1px /*}*/ 2px; 17 | } 18 | -------------------------------------------------------------------------------- /test/cases/text-decoration.css: -------------------------------------------------------------------------------- 1 | .shorthand { 2 | text-decoration: overline double red; 3 | } 4 | 5 | .shorthand-single-value { 6 | text-decoration: underline; 7 | } 8 | 9 | .full { 10 | text-decoration-color: green; 11 | text-decoration-line: line-through; 12 | text-decoration-style: double; 13 | } 14 | 15 | .old { 16 | text-decoration: underline; 17 | } 18 | 19 | .global { 20 | text-decoration: unset; 21 | } 22 | 23 | .skip { 24 | text-decoration-skip: spaces; 25 | } 26 | 27 | .ink { 28 | text-decoration-skip-ink: auto; 29 | } 30 | 31 | .old-ink { 32 | text-decoration-skip: ink; 33 | } 34 | -------------------------------------------------------------------------------- /test/cases/text-decoration.out.css: -------------------------------------------------------------------------------- 1 | .shorthand { 2 | -webkit-text-decoration: overline double red; 3 | text-decoration: overline double red; 4 | } 5 | 6 | .shorthand-single-value { 7 | text-decoration: underline; 8 | } 9 | 10 | .full { 11 | -webkit-text-decoration-color: green; 12 | -moz-text-decoration-color: green; 13 | text-decoration-color: green; 14 | -webkit-text-decoration-line: line-through; 15 | -moz-text-decoration-line: line-through; 16 | text-decoration-line: line-through; 17 | -webkit-text-decoration-style: double; 18 | -moz-text-decoration-style: double; 19 | text-decoration-style: double; 20 | } 21 | 22 | .old { 23 | text-decoration: underline; 24 | } 25 | 26 | .global { 27 | text-decoration: unset; 28 | } 29 | 30 | .skip { 31 | -webkit-text-decoration-skip: spaces; 32 | text-decoration-skip: spaces; 33 | } 34 | 35 | .ink { 36 | -webkit-text-decoration-skip: ink; 37 | text-decoration-skip-ink: auto; 38 | } 39 | 40 | .old-ink { 41 | -webkit-text-decoration-skip: ink; 42 | text-decoration-skip: ink; 43 | } 44 | -------------------------------------------------------------------------------- /test/cases/text-decoration.shorthand.out.css: -------------------------------------------------------------------------------- 1 | .shorthand { 2 | -webkit-text-decoration: overline double red; 3 | text-decoration: overline double red; 4 | } 5 | 6 | .shorthand-single-value { 7 | text-decoration: underline; 8 | } 9 | 10 | .full { 11 | text-decoration-color: green; 12 | text-decoration-line: line-through; 13 | text-decoration-style: double; 14 | } 15 | 16 | .old { 17 | text-decoration: underline; 18 | } 19 | 20 | .global { 21 | text-decoration: unset; 22 | } 23 | 24 | .skip { 25 | text-decoration-skip: spaces; 26 | } 27 | 28 | .ink { 29 | text-decoration-skip-ink: auto; 30 | } 31 | 32 | .old-ink { 33 | text-decoration-skip: ink; 34 | } 35 | -------------------------------------------------------------------------------- /test/cases/text-emphasis-position.css: -------------------------------------------------------------------------------- 1 | a { 2 | text-emphasis-position: over left; 3 | } 4 | 5 | em { 6 | text-emphasis-position: under right; 7 | } 8 | 9 | .reverse { 10 | text-emphasis-position: left over; 11 | } 12 | 13 | .wrong { 14 | text-emphasis-position: over; 15 | } 16 | -------------------------------------------------------------------------------- /test/cases/text-emphasis-position.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-text-emphasis-position: over; 3 | text-emphasis-position: over left; 4 | } 5 | 6 | em { 7 | -webkit-text-emphasis-position: under; 8 | text-emphasis-position: under right; 9 | } 10 | 11 | .reverse { 12 | -webkit-text-emphasis-position: over; 13 | text-emphasis-position: left over; 14 | } 15 | 16 | .wrong { 17 | -webkit-text-emphasis-position: over; 18 | text-emphasis-position: over; 19 | } 20 | -------------------------------------------------------------------------------- /test/cases/transition-no-warning.css: -------------------------------------------------------------------------------- 1 | .no-warn { 2 | transition-property: color, opacity; 3 | transition-duration: 1s, 2s; 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/transition-no-warning.out.css: -------------------------------------------------------------------------------- 1 | .no-warn { 2 | -webkit-transition-property: color, opacity; 3 | -o-transition-property: color, opacity; 4 | transition-property: color, opacity; 5 | -webkit-transition-duration: 1s, 2s; 6 | -o-transition-duration: 1s, 2s; 7 | transition-duration: 1s, 2s; 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/transition-spec.css: -------------------------------------------------------------------------------- 1 | input[type=range]::-moz-range-thumb { 2 | transition: color 200ms, transform 200ms; 3 | transform: rotate(10deg); 4 | } 5 | 6 | input[type=range]::-webkit-slider-thumb { 7 | transition: none; 8 | transition-property: all; 9 | } 10 | 11 | button::-moz-submit-invalid { 12 | opacity: 1; 13 | transform: translateX(45px); 14 | transition: opacity 0.5s 2s, transform 0.5s 0.5s; 15 | } 16 | 17 | @supports (transition: opacity 0.5s 2s, transform 0.5s 0.5s) { 18 | button::-moz-submit-invalid { 19 | opacity: 1; 20 | transform: translateX(45px); 21 | transition: opacity 0.5s 2s, transform 0.5s 0.5s; 22 | } 23 | 24 | button { 25 | opacity: 1; 26 | transform: translateX(45px); 27 | transition: opacity 0.5s 2s, transform 0.5s 0.5s; 28 | } 29 | } 30 | 31 | button::-webkit-search-cancel-button { 32 | display: flex; 33 | transition: color 200ms, transform 200ms; 34 | transform: rotate(10deg); 35 | } 36 | 37 | button::-webkit-search-cancel-button { 38 | display: -webkit-box; 39 | display: -webkit-flex; 40 | display: flex; 41 | -webkit-transition: color 200ms, -webkit-transform 200ms; 42 | transition: color 200ms, -webkit-transform 200ms; 43 | transition: color 200ms, transform 200ms; 44 | transition: color 200ms, transform 200ms, -webkit-transform 200ms; 45 | -webkit-transform: rotate(10deg); 46 | transform: rotate(10deg); 47 | } 48 | 49 | .a::-webkit-search-cancel-button { 50 | display: flex; 51 | flex-flow: row; 52 | order: 0; 53 | flex: 0 1 2; 54 | transition: flex 200ms; 55 | } 56 | -------------------------------------------------------------------------------- /test/cases/transition-spec.out.css: -------------------------------------------------------------------------------- 1 | input[type=range]::-moz-range-thumb { 2 | -moz-transition: color 200ms, transform 200ms, -moz-transform 200ms; 3 | transition: color 200ms, transform 200ms; 4 | transition: color 200ms, transform 200ms, -moz-transform 200ms; 5 | -moz-transform: rotate(10deg); 6 | transform: rotate(10deg); 7 | } 8 | 9 | input[type=range]::-webkit-slider-thumb { 10 | -webkit-transition: none; 11 | transition: none; 12 | -webkit-transition-property: all; 13 | transition-property: all; 14 | } 15 | 16 | button::-moz-submit-invalid { 17 | opacity: 1; 18 | -moz-transform: translateX(45px); 19 | transform: translateX(45px); 20 | -moz-transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; 21 | transition: opacity 0.5s 2s, transform 0.5s 0.5s; 22 | transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; 23 | } 24 | 25 | @supports (transition: opacity 0.5s 2s, transform 0.5s 0.5s) { 26 | button::-moz-submit-invalid { 27 | opacity: 1; 28 | -moz-transform: translateX(45px); 29 | transform: translateX(45px); 30 | -moz-transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; 31 | transition: opacity 0.5s 2s, transform 0.5s 0.5s; 32 | transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; 33 | } 34 | 35 | button { 36 | opacity: 1; 37 | -webkit-transform: translateX(45px); 38 | -moz-transform: translateX(45px); 39 | -o-transform: translateX(45px); 40 | transform: translateX(45px); 41 | -webkit-transition: opacity 0.5s 2s, -webkit-transform 0.5s 0.5s; 42 | transition: opacity 0.5s 2s, -webkit-transform 0.5s 0.5s; 43 | -o-transition: opacity 0.5s 2s, -o-transform 0.5s 0.5s; 44 | -moz-transition: opacity 0.5s 2s, transform 0.5s 0.5s, -moz-transform 0.5s 0.5s; 45 | transition: opacity 0.5s 2s, transform 0.5s 0.5s; 46 | transition: opacity 0.5s 2s, transform 0.5s 0.5s, -webkit-transform 0.5s 0.5s, -moz-transform 0.5s 0.5s, -o-transform 0.5s 0.5s; 47 | } 48 | } 49 | 50 | button::-webkit-search-cancel-button { 51 | display: -webkit-box; 52 | display: -webkit-flex; 53 | display: flex; 54 | -webkit-transition: color 200ms, -webkit-transform 200ms; 55 | transition: color 200ms, -webkit-transform 200ms; 56 | transition: color 200ms, transform 200ms; 57 | transition: color 200ms, transform 200ms, -webkit-transform 200ms; 58 | -webkit-transform: rotate(10deg); 59 | transform: rotate(10deg); 60 | } 61 | 62 | button::-webkit-search-cancel-button { 63 | display: -webkit-box; 64 | display: -webkit-flex; 65 | display: flex; 66 | -webkit-transition: color 200ms, -webkit-transform 200ms; 67 | transition: color 200ms, -webkit-transform 200ms; 68 | transition: color 200ms, transform 200ms; 69 | transition: color 200ms, transform 200ms, -webkit-transform 200ms; 70 | -webkit-transform: rotate(10deg); 71 | transform: rotate(10deg); 72 | } 73 | 74 | .a::-webkit-search-cancel-button { 75 | display: -webkit-box; 76 | display: -webkit-flex; 77 | display: flex; 78 | -webkit-box-orient: horizontal; 79 | -webkit-box-direction: normal; 80 | -webkit-flex-flow: row; 81 | flex-flow: row; 82 | -webkit-box-ordinal-group: 1; 83 | -webkit-order: 0; 84 | order: 0; 85 | -webkit-box-flex: 0; 86 | -webkit-flex: 0 1 2; 87 | flex: 0 1 2; 88 | -webkit-transition: -webkit-box-flex 200ms, -webkit-flex 200ms; 89 | transition: -webkit-box-flex 200ms, -webkit-flex 200ms; 90 | transition: flex 200ms; 91 | transition: flex 200ms, -webkit-box-flex 200ms, -webkit-flex 200ms; 92 | } 93 | -------------------------------------------------------------------------------- /test/cases/transition.css: -------------------------------------------------------------------------------- 1 | a { 2 | transition: color 200ms, transform 200ms; 3 | transform: rotate(10deg); 4 | } 5 | 6 | div { 7 | transition-property: filter; 8 | animation-name: rotating; 9 | } 10 | 11 | .good { 12 | transition-property: filter; 13 | transition-duration: 1s; 14 | } 15 | 16 | .good2 { 17 | transition-property: color, filter; 18 | transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1); 19 | } 20 | 21 | .bad { 22 | /* safe with different node types */ 23 | transition-property: color, filter; 24 | transition-duration: 1s, 2s; 25 | } 26 | 27 | .revert { 28 | transition: 200ms transform; 29 | } 30 | -------------------------------------------------------------------------------- /test/cases/transition.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-transition: color 200ms, -webkit-transform 200ms; 3 | transition: color 200ms, -webkit-transform 200ms; 4 | -o-transition: color 200ms, -o-transform 200ms; 5 | transition: color 200ms, transform 200ms; 6 | transition: color 200ms, transform 200ms, -webkit-transform 200ms, -o-transform 200ms; 7 | -webkit-transform: rotate(10deg); 8 | -o-transform: rotate(10deg); 9 | transform: rotate(10deg); 10 | } 11 | 12 | div { 13 | -webkit-transition-property: -webkit-filter; 14 | transition-property: -webkit-filter; 15 | -o-transition-property: filter; 16 | transition-property: filter; 17 | transition-property: filter, -webkit-filter; 18 | -webkit-animation-name: rotating; 19 | -o-animation-name: rotating; 20 | animation-name: rotating; 21 | } 22 | 23 | .good { 24 | -webkit-transition-property: -webkit-filter; 25 | transition-property: -webkit-filter; 26 | -o-transition-property: filter; 27 | transition-property: filter; 28 | transition-property: filter, -webkit-filter; 29 | -webkit-transition-duration: 1s; 30 | -o-transition-duration: 1s; 31 | transition-duration: 1s; 32 | } 33 | 34 | .good2 { 35 | -webkit-transition-property: color, -webkit-filter; 36 | transition-property: color, -webkit-filter; 37 | -o-transition-property: color, filter; 38 | transition-property: color, filter; 39 | transition-property: color, filter, -webkit-filter; 40 | -webkit-transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1); 41 | -o-transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1); 42 | transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1); 43 | } 44 | 45 | .bad { 46 | /* safe with different node types */ 47 | -webkit-transition-property: color, -webkit-filter; 48 | transition-property: color, -webkit-filter; 49 | -o-transition-property: color, filter; 50 | transition-property: color, filter; 51 | transition-property: color, filter, -webkit-filter; 52 | -webkit-transition-duration: 1s, 2s; 53 | -o-transition-duration: 1s, 2s; 54 | transition-duration: 1s, 2s; 55 | } 56 | 57 | .revert { 58 | -webkit-transition: 200ms -webkit-transform; 59 | transition: 200ms -webkit-transform; 60 | -o-transition: 200ms -o-transform; 61 | transition: 200ms transform; 62 | transition: 200ms transform, 200ms -webkit-transform, 200ms -o-transform; 63 | } 64 | -------------------------------------------------------------------------------- /test/cases/trim.css: -------------------------------------------------------------------------------- 1 | a { 2 | background: -webkit-linear-gradient(top, #f00 0%, #fff 100%); 3 | background: linear-gradient(to bottom, #f00 0%, #fff 100%); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/uncascade.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-transition: 1s; 3 | -moz-transition: 1s; 4 | transition: 1s 5 | } 6 | 7 | a { 8 | -moz-transition: 1s; 9 | -o-transition: 1s; 10 | transition: 1s 11 | } 12 | 13 | a { 14 | -webkit-border-radius: 4px; 15 | -moz-border-radius: 4px; 16 | border-radius: 4px 17 | } 18 | -------------------------------------------------------------------------------- /test/cases/uncascade.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -moz-transition: 1s; 3 | transition: 1s 4 | } 5 | 6 | a { 7 | -moz-transition: 1s; 8 | transition: 1s 9 | } 10 | 11 | a { 12 | border-radius: 4px 13 | } 14 | -------------------------------------------------------------------------------- /test/cases/user-select.css: -------------------------------------------------------------------------------- 1 | a { 2 | user-select: none; 3 | } 4 | 5 | b { 6 | user-select: contain; 7 | } 8 | 9 | .all { 10 | user-select: all; 11 | } 12 | 13 | .var { 14 | user-select: var(--o-select); 15 | } 16 | -------------------------------------------------------------------------------- /test/cases/user-select.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | -webkit-user-select: none; 3 | -ms-user-select: none; 4 | user-select: none; 5 | } 6 | 7 | b { 8 | -ms-user-select: element; 9 | user-select: contain; 10 | } 11 | 12 | .all { 13 | -webkit-user-select: all; 14 | user-select: all; 15 | } 16 | 17 | .var { 18 | -webkit-user-select: var(--o-select); 19 | -ms-user-select: var(--o-select); 20 | user-select: var(--o-select); 21 | } 22 | -------------------------------------------------------------------------------- /test/cases/value-hack.css: -------------------------------------------------------------------------------- 1 | .not-hack { 2 | width: -webkit-calc(30% + 1px); 3 | width: calc(30% + 1px); 4 | display: -webkit-box; 5 | display: flex; 6 | transition: -webkit-filter 1s, filter 1s; 7 | } 8 | 9 | .not-hack2 { 10 | transition: -webkit-filter 1s; 11 | transition: filter 1s; 12 | } 13 | 14 | .hack { 15 | width: -webkit-calc(30% + 1px); 16 | display: -webkit-box; 17 | } 18 | -------------------------------------------------------------------------------- /test/cases/value-hack.out.css: -------------------------------------------------------------------------------- 1 | .not-hack { 2 | width: calc(30% + 1px); 3 | display: flex; 4 | transition: filter 1s; 5 | } 6 | 7 | .not-hack2 { 8 | transition: filter 1s; 9 | } 10 | 11 | .hack { 12 | width: -webkit-calc(30% + 1px); 13 | display: -webkit-box; 14 | } 15 | -------------------------------------------------------------------------------- /test/cases/values.css: -------------------------------------------------------------------------------- 1 | a { 2 | margin: calc(5% + 5px) calc(10% + 10px); 3 | background: linear-gradient(black, white), radial-gradient(white, black); 4 | content: " linear-gradient(black, white) "; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/values.out.css: -------------------------------------------------------------------------------- 1 | a { 2 | margin: -webkit-calc(5% + 5px) -webkit-calc(10% + 10px); 3 | margin: calc(5% + 5px) calc(10% + 10px); 4 | background: -webkit-linear-gradient(black, white), -webkit-radial-gradient(white, black); 5 | background: -o-linear-gradient(black, white), -o-radial-gradient(white, black); 6 | background: linear-gradient(black, white), radial-gradient(white, black); 7 | content: " linear-gradient(black, white) "; 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/vendor-hack.css: -------------------------------------------------------------------------------- 1 | .a { 2 | -moz-transform: scale(.9999); 3 | transform: scale(.9999); 4 | } 5 | 6 | .b { 7 | -moz-transform: scale(.9999); 8 | } 9 | 10 | .c { 11 | transform: scale(1); 12 | -moz-transform: scale(2); 13 | } 14 | -------------------------------------------------------------------------------- /test/cases/vendor-hack.out.css: -------------------------------------------------------------------------------- 1 | .a { 2 | transform: scale(.9999); 3 | } 4 | 5 | .b { 6 | -moz-transform: scale(.9999); 7 | } 8 | 9 | .c { 10 | transform: scale(1); 11 | -moz-transform: scale(2); 12 | } 13 | -------------------------------------------------------------------------------- /test/cases/viewport.css: -------------------------------------------------------------------------------- 1 | @viewport { 2 | width: device-width; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/viewport.out.css: -------------------------------------------------------------------------------- 1 | @-ms-viewport { 2 | width: device-width; 3 | } 4 | @viewport { 5 | width: device-width; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/webkit-line-clamp.css: -------------------------------------------------------------------------------- 1 | .limit-text { 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | display: -webkit-box; 5 | -webkit-box-orient: vertical; 6 | -webkit-line-clamp: 2; 7 | } 8 | 9 | .simple-clamp { 10 | display: -webkit-box; 11 | -webkit-box-orient: vertical; 12 | -webkit-line-clamp: 2; 13 | } 14 | 15 | .clamp-with-flex-direction { 16 | flex-direction: column; 17 | display: -webkit-box; 18 | -webkit-box-orient: vertical; 19 | -webkit-line-clamp: 2; 20 | } 21 | 22 | .clamp-with-flex-direction-and-box-direction { 23 | flex-direction: column; 24 | display: -webkit-box; 25 | -webkit-box-orient: vertical; 26 | -webkit-box-direction: reverse; 27 | -webkit-line-clamp: 2; 28 | } 29 | 30 | .clamp-display-webkit-box-only { 31 | display: -webkit-box; 32 | -webkit-line-clamp: 2; 33 | } 34 | 35 | .clamp-display-webkit-box-orient-vertical-only { 36 | -webkit-box-orient: vertical; 37 | -webkit-line-clamp: 2; 38 | } 39 | -------------------------------------------------------------------------------- /test/cases/webkit-line-clamp.out.css: -------------------------------------------------------------------------------- 1 | .limit-text { 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | display: -webkit-box; 5 | -webkit-box-orient: vertical; 6 | -webkit-line-clamp: 2; 7 | } 8 | 9 | .simple-clamp { 10 | display: -webkit-box; 11 | -webkit-box-orient: vertical; 12 | -webkit-line-clamp: 2; 13 | } 14 | 15 | .clamp-with-flex-direction { 16 | flex-direction: column; 17 | display: -webkit-box; 18 | -webkit-box-orient: vertical; 19 | -webkit-line-clamp: 2; 20 | } 21 | 22 | .clamp-with-flex-direction-and-box-direction { 23 | flex-direction: column; 24 | display: -webkit-box; 25 | -webkit-box-orient: vertical; 26 | -webkit-line-clamp: 2; 27 | } 28 | 29 | .clamp-display-webkit-box-only { 30 | display: -webkit-box; 31 | -webkit-line-clamp: 2; 32 | } 33 | 34 | .clamp-display-webkit-box-orient-vertical-only { 35 | -webkit-box-orient: vertical; 36 | -webkit-line-clamp: 2; 37 | } 38 | -------------------------------------------------------------------------------- /test/cases/writing-mode.css: -------------------------------------------------------------------------------- 1 | .one { 2 | writing-mode: horizontal-tb; 3 | } 4 | .two { 5 | writing-mode: vertical-rl; 6 | } 7 | .three { 8 | writing-mode: vertical-lr; 9 | } 10 | .rtl-vertical-rl { 11 | writing-mode: vertical-rl; 12 | direction: rtl; 13 | } 14 | .rtl-vertical-lr { 15 | writing-mode: vertical-lr; 16 | direction: rtl; 17 | } 18 | .rtl-horizontal-tb { 19 | writing-mode: horizontal-tb; 20 | direction: rtl; 21 | } 22 | .rtl-horizontal-tb-override-direction { 23 | writing-mode: horizontal-tb; 24 | direction: rtl; 25 | direction: ltr; 26 | } 27 | -------------------------------------------------------------------------------- /test/cases/writing-mode.out.css: -------------------------------------------------------------------------------- 1 | .one { 2 | -webkit-writing-mode: horizontal-tb; 3 | -ms-writing-mode: lr-tb; 4 | writing-mode: horizontal-tb; 5 | } 6 | .two { 7 | -webkit-writing-mode: vertical-rl; 8 | -ms-writing-mode: tb-rl; 9 | writing-mode: vertical-rl; 10 | } 11 | .three { 12 | -webkit-writing-mode: vertical-lr; 13 | -ms-writing-mode: tb-lr; 14 | writing-mode: vertical-lr; 15 | } 16 | .rtl-vertical-rl { 17 | -webkit-writing-mode: vertical-rl; 18 | -ms-writing-mode: bt-rl; 19 | writing-mode: vertical-rl; 20 | direction: rtl; 21 | } 22 | .rtl-vertical-lr { 23 | -webkit-writing-mode: vertical-lr; 24 | -ms-writing-mode: bt-lr; 25 | writing-mode: vertical-lr; 26 | direction: rtl; 27 | } 28 | .rtl-horizontal-tb { 29 | -webkit-writing-mode: horizontal-tb; 30 | -ms-writing-mode: rl-tb; 31 | writing-mode: horizontal-tb; 32 | direction: rtl; 33 | } 34 | .rtl-horizontal-tb-override-direction { 35 | -webkit-writing-mode: horizontal-tb; 36 | -ms-writing-mode: lr-tb; 37 | writing-mode: horizontal-tb; 38 | direction: rtl; 39 | direction: ltr; 40 | } 41 | -------------------------------------------------------------------------------- /test/declaration.test.js: -------------------------------------------------------------------------------- 1 | let { parse } = require('postcss') 2 | let { test } = require('uvu') 3 | let { equal, is } = require('uvu/assert') 4 | 5 | let Declaration = require('../lib/declaration') 6 | let Prefixes = require('../lib/prefixes') 7 | 8 | let prefixes, tabsize 9 | test.before.each(() => { 10 | prefixes = new Prefixes({}, {}) 11 | tabsize = new Declaration('tab-size', ['-moz-', '-ms-'], prefixes) 12 | }) 13 | 14 | test.after.each(() => { 15 | delete prefixes.options.cascade 16 | }) 17 | 18 | test('checks values for other prefixes', () => { 19 | is(tabsize.otherPrefixes('black', '-moz-'), false) 20 | is(tabsize.otherPrefixes('-moz-black', '-moz-'), false) 21 | is(tabsize.otherPrefixes('-dev-black', '-moz-'), false) 22 | is(tabsize.otherPrefixes('-ms-black', '-moz-'), true) 23 | }) 24 | 25 | test('returns true by default', () => { 26 | let css = parse('a {\n tab-size: 4 }') 27 | is(tabsize.needCascade(css.first.first), true) 28 | }) 29 | 30 | test('return false is disabled', () => { 31 | prefixes.options.cascade = false 32 | let css = parse('a {\n tab-size: 4 }') 33 | is(tabsize.needCascade(css.first.first), false) 34 | }) 35 | 36 | test('returns false on declarations in one line', () => { 37 | let css = parse('a { tab-size: 4 } a {\n tab-size: 4 }') 38 | is(tabsize.needCascade(css.first.first), false) 39 | is(tabsize.needCascade(css.last.first), true) 40 | }) 41 | 42 | test('returns max prefix length', () => { 43 | let decl = parse('a { tab-size: 4 }').first.first 44 | let list = ['-webkit-', '-webkit- old', '-moz-'] 45 | equal(tabsize.maxPrefixed(list, decl), 8) 46 | }) 47 | 48 | test('returns before with cascade', () => { 49 | let decl = parse('a { tab-size: 4 }').first.first 50 | let list = ['-webkit-', '-moz- old', '-moz-'] 51 | equal(tabsize.calcBefore(list, decl, '-moz- old'), ' ') 52 | }) 53 | 54 | test('removes cascade', () => { 55 | let css = parse('a {\n' + ' -moz-tab-size: 4;\n' + ' tab-size: 4 }') 56 | let decl = css.first.nodes[1] 57 | tabsize.restoreBefore(decl) 58 | equal(decl.raws.before, '\n ') 59 | }) 60 | 61 | test('returns prefixed property', () => { 62 | let css = parse('a { tab-size: 2 }') 63 | let decl = css.first.first 64 | equal(tabsize.prefixed(decl.prop, '-moz-'), '-moz-tab-size') 65 | }) 66 | 67 | test('returns property name by specification', () => { 68 | equal(tabsize.normalize('tab-size'), 'tab-size') 69 | }) 70 | 71 | test('adds prefixes', () => { 72 | let css = parse('a { -moz-tab-size: 2; tab-size: 2 }') 73 | tabsize.process(css.first.nodes[1]) 74 | equal(css.toString(), 'a { -moz-tab-size: 2; -ms-tab-size: 2; tab-size: 2 }') 75 | }) 76 | 77 | test('checks parents prefix', () => { 78 | let css = parse('::-moz-selection a { tab-size: 2 }') 79 | tabsize.process(css.first.first) 80 | equal(css.toString(), '::-moz-selection a { -moz-tab-size: 2; tab-size: 2 }') 81 | }) 82 | 83 | test('checks value for prefixes', () => { 84 | let css = parse('a { tab-size: -ms-calc(2) }') 85 | tabsize.process(css.first.first) 86 | equal( 87 | css.toString(), 88 | 'a { -ms-tab-size: -ms-calc(2); tab-size: -ms-calc(2) }' 89 | ) 90 | }) 91 | 92 | test('returns list of prefixeds', () => { 93 | equal(tabsize.old('tab-size', '-moz-'), ['-moz-tab-size']) 94 | }) 95 | 96 | test.run() 97 | -------------------------------------------------------------------------------- /test/info.test.js: -------------------------------------------------------------------------------- 1 | let browserslist = require('browserslist') 2 | let { agents } = require('caniuse-lite/dist/unpacker/agents') 3 | let { test } = require('uvu') 4 | let { equal, match } = require('uvu/assert') 5 | 6 | let Browsers = require('../lib/browsers') 7 | let info = require('../lib/info') 8 | let Prefixes = require('../lib/prefixes') 9 | 10 | let data = { 11 | browsers: agents, 12 | prefixes: { 13 | '@keyframes': { 14 | browsers: ['firefox 21'] 15 | }, 16 | 'a': { 17 | browsers: ['firefox 21', 'firefox 20', 'chrome 30'], 18 | transition: true 19 | }, 20 | 'b': { 21 | browsers: ['ie 6', 'firefox 20'], 22 | props: ['a', '*'] 23 | }, 24 | 'c': { 25 | browsers: ['firefox 21'], 26 | props: ['c'] 27 | }, 28 | 'd': { 29 | browsers: ['firefox 21'], 30 | selector: true 31 | }, 32 | 'grid': { 33 | browsers: ['ie 6'], 34 | props: ['display'] 35 | }, 36 | 'grid-row': { 37 | browsers: ['ie 6'] 38 | }, 39 | 'transition': { 40 | browsers: ['firefox 21'] 41 | } 42 | } 43 | } 44 | 45 | test('returns selected browsers and prefixes', () => { 46 | let browsers = new Browsers(data.browsers, [ 47 | 'chrome 30', 48 | 'firefox 21', 49 | 'firefox 20', 50 | 'ie 6' 51 | ]) 52 | let prefixes = new Prefixes(data.prefixes, browsers) 53 | 54 | let coverage = browserslist.coverage([ 55 | 'chrome 30', 56 | 'firefox 21', 57 | 'firefox 20', 58 | 'ie 6' 59 | ]) 60 | let round = Math.round(coverage * 100) / 100.0 61 | 62 | equal( 63 | info(prefixes), 64 | 'Browsers:\n' + 65 | ' Chrome: 30\n' + 66 | ' Firefox: 21, 20\n' + 67 | ' IE: 6\n' + 68 | '\n' + 69 | `These browsers account for ${round}% ` + 70 | 'of all users globally\n' + 71 | '\n' + 72 | 'At-Rules:\n' + 73 | ' @keyframes: moz\n' + 74 | '\n' + 75 | 'Selectors:\n' + 76 | ' d: moz\n' + 77 | '\n' + 78 | 'Properties:\n' + 79 | ' a: webkit, moz\n' + 80 | ' grid-row *: ms\n' + 81 | ' transition: moz\n' + 82 | '\n' + 83 | 'Values:\n' + 84 | ' b: moz, ms\n' + 85 | ' c: moz\n' + 86 | ' grid *: ms\n' + 87 | '\n' + 88 | '* - Prefixes will be added only on grid: true option.\n' 89 | ) 90 | }) 91 | 92 | test('does not show transitions unless they are necessary', () => { 93 | let browsers = new Browsers(data.browsers, ['chrome 30', 'firefox 20']) 94 | let prefixes = new Prefixes(data.prefixes, browsers) 95 | 96 | let coverage = browserslist.coverage(['chrome 30', 'firefox 20']) 97 | let round = Math.round(coverage * 100) / 100.0 98 | 99 | equal( 100 | info(prefixes), 101 | 'Browsers:\n' + 102 | ' Chrome: 30\n' + 103 | ' Firefox: 20\n' + 104 | '\n' + 105 | `These browsers account for ${round}% ` + 106 | 'of all users globally\n' + 107 | '\n' + 108 | 'Properties:\n' + 109 | ' a: webkit, moz\n' + 110 | '\n' + 111 | 'Values:\n' + 112 | ' b: moz\n' 113 | ) 114 | }) 115 | 116 | test('returns string for empty prefixes', () => { 117 | let browsers = new Browsers(data.browsers, ['ie 7']) 118 | let prefixes = new Prefixes(data.prefixes, browsers) 119 | match(info(prefixes), /remove Autoprefixer/) 120 | }) 121 | 122 | test('returns string for empty browsers', () => { 123 | let browsers = new Browsers(data.browsers, []) 124 | let prefixes = new Prefixes(data.prefixes, browsers) 125 | equal(info(prefixes), 'No browsers selected') 126 | }) 127 | 128 | test.run() 129 | -------------------------------------------------------------------------------- /test/old-selector.test.js: -------------------------------------------------------------------------------- 1 | let { parse } = require('postcss') 2 | let { test } = require('uvu') 3 | let { is } = require('uvu/assert') 4 | 5 | let Selector = require('../lib/selector') 6 | 7 | let selector = new Selector('::selection', ['-moz-', '-ms-']) 8 | let old = selector.old('-moz-') 9 | 10 | test('returns true on last rule', () => { 11 | let css = parse('::selection {} ::-moz-selection {}') 12 | is(old.isHack(css.last), true) 13 | }) 14 | 15 | test('stops on another type', () => { 16 | let css = parse('::-moz-selection {} ' + '@keyframes anim {} ::selection {}') 17 | is(old.isHack(css.first), true) 18 | }) 19 | 20 | test('stops on another selector', () => { 21 | let css = parse('::-moz-selection {} a {} ::selection {}') 22 | is(old.isHack(css.first), true) 23 | }) 24 | 25 | test('finds unprefixed selector', () => { 26 | let css = parse('::-moz-selection {} ' + '::-o-selection {} ::selection {}') 27 | is(old.isHack(css.first), false) 28 | }) 29 | 30 | test('finds old selector', () => { 31 | let css = parse('body::-moz-selection {} body::selection {}') 32 | is(old.check(css.first), true) 33 | }) 34 | 35 | test('finds right', () => { 36 | let css = parse('body:::-moz-selection {}') 37 | is(old.check(css.first), false) 38 | }) 39 | 40 | test.run() 41 | -------------------------------------------------------------------------------- /test/old-value.test.js: -------------------------------------------------------------------------------- 1 | let { test } = require('uvu') 2 | let { is } = require('uvu/assert') 3 | 4 | let OldValue = require('../lib/old-value') 5 | 6 | test('checks value in string', () => { 7 | let old = new OldValue('calc', '-o-calc') 8 | is(old.check('1px -o-calc(1px)'), true) 9 | is(old.check('1px calc(1px)'), false) 10 | }) 11 | 12 | test('allows custom checks', () => { 13 | let old = new OldValue('calc', '-o-calc', 'calc', /calc/) 14 | is(old.check('1px calc(1px)'), true) 15 | }) 16 | 17 | test.run() 18 | -------------------------------------------------------------------------------- /test/postcss.test.js: -------------------------------------------------------------------------------- 1 | let postcss = require('postcss') 2 | let { test } = require('uvu') 3 | let { equal } = require('uvu/assert') 4 | 5 | let autoprefixer = require('..') 6 | 7 | test('works with other PostCSS plugins', () => { 8 | let plugin = () => { 9 | return { 10 | AtRule: { 11 | mixin: (atRule, { Declaration }) => { 12 | atRule.replaceWith( 13 | new Declaration({ prop: 'user-select', value: 'none' }) 14 | ) 15 | } 16 | }, 17 | postcssPlugin: 'test', 18 | Rule(rule) { 19 | rule.selector = 'b' 20 | } 21 | } 22 | } 23 | plugin.postcss = true 24 | 25 | let result = postcss([ 26 | plugin(), 27 | autoprefixer({ overrideBrowserslist: 'chrome 40' }) 28 | ]).process('a{ @mixin; }', { 29 | from: 'a.css' 30 | }) 31 | 32 | equal(result.css, 'b{ -webkit-user-select: none; user-select: none; }') 33 | }) 34 | 35 | test.run() 36 | -------------------------------------------------------------------------------- /test/prefixer.test.js: -------------------------------------------------------------------------------- 1 | let { parse } = require('postcss') 2 | let { test } = require('uvu') 3 | let { equal, is, type } = require('uvu/assert') 4 | 5 | let Prefixer = require('../lib/prefixer') 6 | 7 | let css, prefix 8 | test.before.each(() => { 9 | prefix = new Prefixer() 10 | css = parse( 11 | '@-ms-keyframes a { to { } } ' + 12 | ':-moz-full-screen { } a { } ' + 13 | '@-dev-keyframes s { to { } }' 14 | ) 15 | }) 16 | 17 | test('registers hacks for subclasses', () => { 18 | class A extends Prefixer {} 19 | class Hack extends A {} 20 | Hack.names = ['a', 'b'] 21 | 22 | A.hack(Hack) 23 | 24 | equal(A.hacks, { a: Hack, b: Hack }) 25 | type(Prefixer.hacks, 'undefined') 26 | }) 27 | 28 | test('loads hacks', () => { 29 | class A extends Prefixer { 30 | constructor() { 31 | super() 32 | this.klass = 'a' 33 | } 34 | } 35 | class Hack extends A { 36 | constructor() { 37 | super() 38 | this.klass = 'hack' 39 | } 40 | } 41 | A.hacks = { hacked: Hack } 42 | 43 | equal(A.load('hacked').klass, 'hack') 44 | equal(A.load('a').klass, 'a') 45 | }) 46 | 47 | test('cleans custom properties', () => { 48 | let rule = css.first.first 49 | rule._autoprefixerPrefix = '-ms-' 50 | rule._autoprefixerValues = { '-ms-': 1 } 51 | 52 | let cloned = Prefixer.clone(rule, { selector: 'from' }) 53 | equal(cloned.selector, 'from') 54 | 55 | type(cloned._autoprefixerPrefix, 'undefined') 56 | type(cloned._autoprefixerValues, 'undefined') 57 | }) 58 | 59 | test('fixed declaration between', () => { 60 | let parsed = parse('a { color : black }') 61 | let cloned = Prefixer.clone(parsed.first.first) 62 | equal(cloned.raws.between, ' : ') 63 | }) 64 | 65 | test('works with root node', () => { 66 | is(prefix.parentPrefix(css), false) 67 | }) 68 | 69 | test('finds in at-rules', () => { 70 | equal(prefix.parentPrefix(css.first), '-ms-') 71 | }) 72 | 73 | test('finds in selectors', () => { 74 | equal(prefix.parentPrefix(css.nodes[1]), '-moz-') 75 | }) 76 | 77 | test('finds in parents', () => { 78 | let decl = css.first.first 79 | equal(prefix.parentPrefix(decl), '-ms-') 80 | is(prefix.parentPrefix(css.nodes[2]), false) 81 | }) 82 | 83 | test('caches prefix', () => { 84 | prefix.parentPrefix(css.first) 85 | equal(css.first._autoprefixerPrefix, '-ms-') 86 | 87 | css.first._autoprefixerPrefix = false 88 | is(prefix.parentPrefix(css.first), false) 89 | }) 90 | 91 | test('finds only browsers prefixes', () => { 92 | is(prefix.parentPrefix(css.nodes[2]), false) 93 | }) 94 | 95 | test('works with selector contained --', () => { 96 | let parsed = parse(':--a { color: black }') 97 | is(prefix.parentPrefix(parsed.first.first), false) 98 | }) 99 | 100 | test.run() 101 | -------------------------------------------------------------------------------- /test/utils.test.js: -------------------------------------------------------------------------------- 1 | let { test } = require('uvu') 2 | let { equal, is, throws } = require('uvu/assert') 3 | 4 | let utils = require('../lib/utils') 5 | 6 | test('raises an error', () => { 7 | throws(() => { 8 | utils.error('A') 9 | }, 'A') 10 | }) 11 | 12 | test('marks an error', () => { 13 | let error = null 14 | try { 15 | utils.error('A') 16 | } catch (e) { 17 | error = e 18 | } 19 | 20 | is(error.autoprefixer, true) 21 | }) 22 | 23 | test('filters doubles in array', () => { 24 | equal(utils.uniq(['1', '1', '2', '3', '3']), ['1', '2', '3']) 25 | }) 26 | 27 | test('removes note', () => { 28 | equal(utils.removeNote('-webkit- note'), '-webkit-') 29 | equal(utils.removeNote('-webkit-'), '-webkit-') 30 | }) 31 | 32 | test('escapes RegExp symbols', () => { 33 | equal(utils.escapeRegexp('^[()\\]'), '\\^\\[\\(\\)\\\\\\]') 34 | }) 35 | 36 | test('generates RegExp that finds tokens in CSS values', () => { 37 | let regexp = utils.regexp('foo') 38 | function check(string) { 39 | return string.match(regexp) !== null 40 | } 41 | 42 | is(check('foo'), true) 43 | is(check('Foo'), true) 44 | is(check('one, foo, two'), true) 45 | is(check('one(),foo(),two()'), true) 46 | 47 | equal('foo(), a, foo'.replace(regexp, '$1b$2'), 'bfoo(), a, bfoo') 48 | 49 | is(check('foob'), false) 50 | is(check('(foo)'), false) 51 | is(check('-a-foo'), false) 52 | }) 53 | 54 | test('escapes string if needed', () => { 55 | let regexp = utils.regexp('(a|b)') 56 | function check(string) { 57 | return string.match(regexp) !== null 58 | } 59 | 60 | is(check('a'), false) 61 | is(check('(a|b)'), true) 62 | 63 | regexp = utils.regexp('(a|b)', false) 64 | is(check('a'), true) 65 | is(check('b'), true) 66 | }) 67 | 68 | test('does save without changes', () => { 69 | let list = utils.editList('a,\nb, c', parsed => parsed) 70 | equal(list, 'a,\nb, c') 71 | }) 72 | 73 | test('changes list', () => { 74 | let list = utils.editList('a, b', (parsed, edit) => { 75 | equal(parsed, ['a', 'b']) 76 | equal(edit, []) 77 | return ['1', '2'] 78 | }) 79 | equal(list, '1, 2') 80 | }) 81 | 82 | test('saves comma', () => { 83 | let list = utils.editList('a,\nb', () => ['1', '2']) 84 | equal(list, '1,\n2') 85 | }) 86 | 87 | test('parse one value', () => { 88 | let list = utils.editList('1', parsed => [parsed[0], '2']) 89 | equal(list, '1, 2') 90 | }) 91 | 92 | test('splits simple selectors into an array', () => { 93 | let arr1 = utils.splitSelector('#foo.bar') 94 | let arr2 = utils.splitSelector('.foo, .bar') 95 | equal(arr1, [[['#foo', '.bar']]]) 96 | equal(arr2, [[['.foo']], [['.bar']]]) 97 | }) 98 | 99 | test('splits complex selectors into an array', () => { 100 | let arr = utils.splitSelector( 101 | '#foo.bar .child-one.mod .child-two.mod, .baz, .hello' 102 | ) 103 | equal(arr, [ 104 | [ 105 | ['#foo', '.bar'], 106 | ['.child-one', '.mod'], 107 | ['.child-two', '.mod'] 108 | ], 109 | [['.baz']], 110 | [['.hello']] 111 | ]) 112 | }) 113 | 114 | test('detects numbers', () => { 115 | equal(utils.isPureNumber(42), true) 116 | equal(utils.isPureNumber('42'), true) 117 | equal(utils.isPureNumber('autoprefixer'), false) 118 | equal(utils.isPureNumber(''), false) 119 | equal(utils.isPureNumber({}), false) 120 | equal(utils.isPureNumber(undefined), false) 121 | equal(utils.isPureNumber(true), false) 122 | }) 123 | 124 | test.run() 125 | -------------------------------------------------------------------------------- /test/value.test.js: -------------------------------------------------------------------------------- 1 | let { parse } = require('postcss') 2 | let { test } = require('uvu') 3 | let { equal, is, type } = require('uvu/assert') 4 | 5 | let OldValue = require('../lib/old-value') 6 | let Prefixes = require('../lib/prefixes') 7 | let Value = require('../lib/value') 8 | 9 | let prefixes = new Prefixes() 10 | 11 | let calc 12 | test.before.each(() => { 13 | calc = new Value('calc', ['-moz-', '-ms-']) 14 | }) 15 | 16 | test('clones declaration', () => { 17 | let css = parse('a { prop: v }') 18 | let width = css.first.first 19 | 20 | width._autoprefixerValues = { '-ms-': '-ms-v' } 21 | Value.save(prefixes, width) 22 | 23 | equal(css.toString(), 'a { prop: -ms-v; prop: v }') 24 | }) 25 | 26 | test('updates declaration with prefix', () => { 27 | let css = parse('a { -ms-prop: v }') 28 | let width = css.first.first 29 | 30 | width._autoprefixerValues = { '-ms-': '-ms-v' } 31 | Value.save(prefixes, width) 32 | 33 | equal(css.toString(), 'a { -ms-prop: -ms-v }') 34 | }) 35 | 36 | test('ignores on another prefix property', () => { 37 | let css = parse('a { -ms-prop: v; prop: v }') 38 | let width = css.first.last 39 | 40 | width._autoprefixerValues = { '-ms-': '-ms-v' } 41 | Value.save(prefixes, width) 42 | 43 | equal(css.toString(), 'a { -ms-prop: v; prop: v }') 44 | }) 45 | 46 | test('ignores prefixes without changes', () => { 47 | let css = parse('a { prop: v }') 48 | let width = css.first.first 49 | 50 | width._autoprefixerValues = { '-ms-': 'v' } 51 | Value.save(prefixes, width) 52 | 53 | equal(css.toString(), 'a { prop: v }') 54 | }) 55 | 56 | test('checks value in string', () => { 57 | let css = parse( 58 | 'a { 0: calc(1px + 1em); ' + 59 | '1: 1px calc(1px + 1em); ' + 60 | '2: (calc(1px + 1em)); ' + 61 | '3: -ms-calc; ' + 62 | '4: calced; }' 63 | ) 64 | 65 | is(calc.check(css.first.nodes[0]), true) 66 | is(calc.check(css.first.nodes[1]), true) 67 | is(calc.check(css.first.nodes[2]), true) 68 | 69 | is(calc.check(css.first.nodes[3]), false) 70 | is(calc.check(css.first.nodes[4]), false) 71 | }) 72 | 73 | test('check prefixed value', () => { 74 | equal(calc.old('-ms-'), new OldValue('calc', '-ms-calc')) 75 | }) 76 | 77 | test('adds prefix to value', () => { 78 | equal(calc.replace('1px calc(1em)', '-ms-'), '1px -ms-calc(1em)') 79 | equal(calc.replace('1px,calc(1em)', '-ms-'), '1px,-ms-calc(1em)') 80 | }) 81 | 82 | test('adds prefixes', () => { 83 | let css = parse('a { width: calc(1em) calc(1%) }') 84 | let width = css.first.first 85 | 86 | calc.process(width) 87 | equal(width._autoprefixerValues, { 88 | '-moz-': '-moz-calc(1em) -moz-calc(1%)', 89 | '-ms-': '-ms-calc(1em) -ms-calc(1%)' 90 | }) 91 | }) 92 | 93 | test('checks parents prefix', () => { 94 | let css = parse('::-moz-fullscreen a { width: calc(1%) }') 95 | let width = css.first.first 96 | 97 | calc.process(width) 98 | equal(width._autoprefixerValues, { '-moz-': '-moz-calc(1%)' }) 99 | }) 100 | 101 | test('checks property prefix', () => { 102 | let css = parse('a { -moz-width: calc(1%); -o-width: calc(1%) }') 103 | let decls = css.first.nodes 104 | 105 | calc.process(decls[0]) 106 | equal(decls[0]._autoprefixerValues, { 107 | '-moz-': '-moz-calc(1%)' 108 | }) 109 | 110 | calc.process(decls[1]) 111 | type(decls[1]._autoprefixerValues, 'undefined') 112 | }) 113 | 114 | test.run() 115 | --------------------------------------------------------------------------------