├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── INSTALL.md ├── LICENSE ├── README.md ├── index.js ├── index.test.js ├── package.json └── yarn.lock /.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 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | yarn-error.log 4 | coverage/ 5 | 6 | *.swp 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | 3 | node_modules/ 4 | npm-debug.log 5 | yarn-error.log 6 | yarn.lock 7 | 8 | coverage/ 9 | test/ 10 | .travis.yml 11 | .idea 12 | 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | node_js: 4 | - node 5 | install: 6 | - YARN_IGNORE_ENGINES=true yarn 7 | - npm install -g codecov 8 | script: 9 | - yarn test 10 | after_success: 11 | - codecov -e TRAVIS_NODE_VERSION -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | This project adheres to [Semantic Versioning](http://semver.org/). 3 | 4 | ## 4.1.0 5 | 6 | * Improving clamp replacement (Implemented by https://github.com/Antonio-Laguna) 7 | 8 | ## 4.0.0 9 | 10 | * Fix bug that clamp polyfill doesn't work in grid-template context (Fix by https://github.com/Antonio-Laguna) 11 | 12 | ## 3.0.0 13 | 14 | * Add preserve option (Implemented by https://github.com/Antonio-Laguna) 15 | 16 | ## 2.0.0 17 | 18 | * Rewrite package to PostCSS 8 (#2) 19 | 20 | ## 1.1.0 21 | 22 | * Simplify regex, refactor indexOf. Update dependecies 23 | 24 | ## 1.0.0 25 | 26 | * Add precalculation for parameters of clamp 27 | 28 | ## 0.2.0 29 | 30 | * Handle functions in `clamp` function 31 | * Update README and add INSTALL guide 32 | 33 | ## 0.1.0 34 | 35 | * Initial release. Handle simple transform clamp to min/max 36 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installing PostCSS Clamp 2 | 3 | [PostCSS Clamp] runs in all Node environments, with special instructions for: 4 | 5 | | [Node](#node) | [PostCSS CLI](#postcss-cli) | [Webpack](#webpack) | [Create React App](#create-react-app) | [Gulp](#gulp) | [Grunt](#grunt) | 6 | | --- | --- | --- | --- | --- | --- | 7 | 8 | ## Node 9 | 10 | Add [PostCSS Clamp] to your project: 11 | 12 | ```bash 13 | $ npm install postcss-clamp --save-dev 14 | ``` 15 | 16 | Use [PostCSS Clamp] as a [PostCSS] plugin: 17 | 18 | ```js 19 | const postcss = require('postcss'); 20 | const postcssClamp = require('postcss-clamp'); 21 | 22 | postcss([ 23 | postcssClamp(/* pluginOptions */) 24 | ]).process(YOUR_CSS /*, processOptions */); 25 | ``` 26 | 27 | ## PostCSS CLI 28 | 29 | Add [PostCSS CLI] to your project: 30 | 31 | ```bash 32 | npm install postcss-cli --save-dev 33 | ``` 34 | 35 | Use **PostCSS Clamp** in your `postcss.config.js` configuration file: 36 | 37 | ```js 38 | const postcssClamp = require('postcss-clamp'); 39 | 40 | module.exports = { 41 | plugins: [ 42 | postcssClamp(/* pluginOptions */) 43 | ] 44 | } 45 | ``` 46 | 47 | ## Webpack 48 | 49 | Add [PostCSS Loader] to your project: 50 | 51 | ```bash 52 | npm install postcss-loader --save-dev 53 | ``` 54 | 55 | Use **PostCSS Clamp** in your Webpack configuration: 56 | 57 | ```js 58 | const postcssClamp = require('postcss-clamp'); 59 | 60 | module.exports = { 61 | module: { 62 | rules: [ 63 | { 64 | test: /\.css$/, 65 | use: [ 66 | 'style-loader', 67 | { loader: 'css-loader', options: { importLoaders: 1 } }, 68 | { loader: 'postcss-loader', options: { 69 | ident: 'postcss', 70 | plugins: () => [ 71 | postcssClamp(/* pluginOptions */) 72 | ] 73 | } } 74 | ] 75 | } 76 | ] 77 | } 78 | } 79 | ``` 80 | 81 | ## Create React App 82 | 83 | Add [React App Rewired] and [React App Rewire PostCSS] to your project: 84 | 85 | ```bash 86 | npm install react-app-rewired react-app-rewire-postcss --save-dev 87 | ``` 88 | 89 | Use **React App Rewire PostCSS** and **PostCSS Clamp** in your 90 | `config-overrides.js` file: 91 | 92 | ```js 93 | const reactAppRewirePostcss = require('react-app-rewire-postcss'); 94 | const postcssClamp = require('postcss-clamp'); 95 | 96 | module.exports = config => reactAppRewirePostcss(config, { 97 | plugins: () => [ 98 | postcssClamp(/* pluginOptions */) 99 | ] 100 | }); 101 | ``` 102 | 103 | ## Gulp 104 | 105 | Add [Gulp PostCSS] to your project: 106 | 107 | ```bash 108 | npm install gulp-postcss --save-dev 109 | ``` 110 | 111 | Use **PostCSS Clamp** in your Gulpfile: 112 | 113 | ```js 114 | const postcss = require('gulp-postcss'); 115 | const postcssClamp = require('postcss-clamp'); 116 | 117 | gulp.task('css', () => gulp.src('./src/*.css').pipe( 118 | postcss([ 119 | postcssClamp(/* pluginOptions */) 120 | ]) 121 | ).pipe( 122 | gulp.dest('.') 123 | )); 124 | ``` 125 | 126 | ## Grunt 127 | 128 | Add [Grunt PostCSS] to your project: 129 | 130 | ```bash 131 | npm install grunt-postcss --save-dev 132 | ``` 133 | 134 | Use **PostCSS Clamp** in your Gruntfile: 135 | 136 | ```js 137 | const postcssClamp = require('postcss-clamp'); 138 | 139 | grunt.loadNpmTasks('grunt-postcss'); 140 | 141 | grunt.initConfig({ 142 | postcss: { 143 | options: { 144 | use: [ 145 | postcssClamp(/* pluginOptions */) 146 | ] 147 | }, 148 | dist: { 149 | src: '*.css' 150 | } 151 | } 152 | }); 153 | ``` 154 | 155 | [Gulp PostCSS]: https://github.com/postcss/gulp-postcss 156 | [Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss 157 | [PostCSS]: https://github.com/postcss/postcss 158 | [PostCSS CLI]: https://github.com/postcss/postcss-cli 159 | [PostCSS Loader]: https://github.com/postcss/postcss-loader 160 | [PostCSS Clamp]: https://github.com/polemius/postcss-clamp 161 | [React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss 162 | [React App Rewired]: https://github.com/timarney/react-app-rewired 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2020 Ivan Menshykov 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 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PostCSS Clamp 2 | [![Build Status][ci-img]][ci] [![codecov.io][cov-img]][cov] 3 | 4 | [PostCSS] plugin to transform `clamp()` to combination of `min/max`. 5 | 6 | [PostCSS]: https://github.com/postcss/postcss 7 | [ci-img]: https://travis-ci.com/polemius/postcss-clamp.svg?branch=master 8 | [ci]: https://travis-ci.com/polemius/postcss-clamp 9 | [cov-img]: https://codecov.io/github/polemius/postcss-clamp/coverage.svg?branch=master 10 | [cov]: https://codecov.io/github/polemius/postcss-clamp?branch=master 11 | 12 | This plugin transform this css: 13 | 14 | ```css 15 | .foo { 16 | width: clamp(10px, 4em, 80px); 17 | } 18 | ``` 19 | 20 | into this: 21 | 22 | ```css 23 | .foo { 24 | width: max(10px, min(4em, 80px)); 25 | } 26 | ``` 27 | 28 | Or with enabled options `precalculate`: 29 | 30 | ```css 31 | .foo { 32 | width: clamp(10em, 4px, 10px); 33 | } 34 | 35 | /* becomes */ 36 | 37 | .foo { 38 | width: max(10em, 14px); 39 | } 40 | ``` 41 | 42 | [!['Can I use' table](https://caniuse.bitsofco.de/image/css-math-functions.png)](https://caniuse.com/#feat=css-math-functions) 43 | 44 | ## Instalation 45 | 46 | ```bash 47 | $ npm install postcss postcss-clamp --save-dev 48 | or 49 | $ yarn add --dev postcss postcss-clamp 50 | ``` 51 | 52 | ## Usage 53 | 54 | Use [PostCSS Clamp] as a [PostCSS] plugin: 55 | 56 | ```js 57 | const postcss = require('postcss'); 58 | const postcssClamp = require('postcss-clamp'); 59 | 60 | postcss([ 61 | postcssClamp(/* pluginOptions */) 62 | ]).process(YOUR_CSS /*, processOptions */); 63 | ``` 64 | 65 | [PostCSS Clamp] runs in all Node environments, with special instructions for: 66 | 67 | | [Node](INSTALL.md#node) | [PostCSS CLI](INSTALL.md#postcss-cli) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) | 68 | | --- | --- | --- | --- | --- | --- | 69 | 70 | See [PostCSS] docs for examples for your environment. 71 | 72 | ## Options 73 | 74 | ### precalculate 75 | 76 | The `precalculate` option determines whether values with the same unit 77 | should be precalculated. By default, these are not precalculation. 78 | 79 | ```js 80 | postcssColorHexAlpha({ 81 | precalculate: true 82 | }); 83 | ``` 84 | 85 | The second and third value has the same unit (`px`): 86 | 87 | ```css 88 | .foo { 89 | width: clamp(10em, 4px, 10px); 90 | } 91 | 92 | /* becomes */ 93 | 94 | .foo { 95 | width: max(10em, 14px); 96 | } 97 | ``` 98 | 99 | Here all values have the same unit: 100 | 101 | ```css 102 | .foo { 103 | width: clamp(10px, 4px, 10px); 104 | } 105 | 106 | /* becomes */ 107 | 108 | .foo { 109 | width: 24px; 110 | } 111 | ``` 112 | 113 | ## LICENSE 114 | 115 | See [LICENSE](LICENSE) 116 | 117 | [PostCSS]: https://github.com/postcss/postcss 118 | [PostCSS Clamp]: https://github.com/polemius/postcss-clamp 119 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | let valueParser = require('postcss-value-parser') 2 | 3 | function parseValue (value) { 4 | let parsed = value.match(/([\d.-]+)(.*)/) 5 | if (!parsed || !parsed[1] || !parsed[2] || isNaN(parsed[1])) { 6 | return undefined 7 | } 8 | return [parseFloat(parsed[1]), parsed[2]] 9 | } 10 | 11 | function compose (first, second, third) { 12 | if (first && second && third) { 13 | return `max(${first}, min(${second}, ${third}))` 14 | } 15 | if (first && second) { 16 | return `max(${first}, ${second})` 17 | } 18 | 19 | return first 20 | } 21 | 22 | function updateValue (declaration, value, preserve) { 23 | let newValue = value 24 | let newValueAst = valueParser(value) 25 | let valueAST = valueParser(declaration.value) 26 | // Walk can't be interrupted, so we only care about first 27 | let foundClamp = false 28 | 29 | valueAST.walk((node, index, nodes) => { 30 | let isClamp = node.type === 'function' && node.value === 'clamp' 31 | 32 | if (!isClamp || foundClamp) { 33 | return 34 | } 35 | 36 | foundClamp = true 37 | nodes[index] = newValueAst 38 | }) 39 | 40 | if (foundClamp) { 41 | newValue = valueAST.toString() 42 | } 43 | 44 | if (preserve) { 45 | declaration.cloneBefore({ value: newValue }) 46 | } else { 47 | declaration.value = newValue 48 | } 49 | } 50 | 51 | module.exports = opts => { 52 | opts = opts || {} 53 | let precalculate = opts.precalculate ? Boolean(opts.precalculate) : false 54 | let preserve = opts.preserve ? Boolean(opts.preserve) : false 55 | 56 | return { 57 | postcssPlugin: 'postcss-clamp', 58 | Declaration (decl) { 59 | if (!decl || !decl.value.includes('clamp')) { 60 | return 61 | } 62 | valueParser(decl.value).walk(node => { 63 | let nodes = node.nodes 64 | if ( 65 | node.type !== 'function' || 66 | node.value !== 'clamp' || 67 | nodes.length !== 5 68 | ) { 69 | return 70 | } 71 | let first = nodes[0] 72 | let second = nodes[2] 73 | let third = nodes[4] 74 | let naive = compose( 75 | valueParser.stringify(first), 76 | valueParser.stringify(second), 77 | valueParser.stringify(third) 78 | ) 79 | if (!precalculate || second.type !== 'word' || third.type !== 'word') { 80 | updateValue(decl, naive, preserve) 81 | return 82 | } 83 | let parsedSecond = parseValue(second.value) 84 | let parsedThird = parseValue(third.value) 85 | if (parsedSecond === undefined || parsedThird === undefined) { 86 | updateValue(decl, naive, preserve) 87 | return 88 | } 89 | let [secondValue, secondUnit] = parsedSecond 90 | let [thirdValue, thirdUnit] = parsedThird 91 | if (secondUnit !== thirdUnit) { 92 | updateValue(decl, naive, preserve) 93 | return 94 | } 95 | let parsedFirst = parseValue(first.value) 96 | if (parsedFirst === undefined) { 97 | let secondThirdValue = `${secondValue + thirdValue}${secondUnit}` 98 | updateValue( 99 | decl, 100 | compose(valueParser.stringify(first), secondThirdValue), 101 | preserve 102 | ) 103 | return 104 | } 105 | let [firstValue, firstUnit] = parsedFirst 106 | if (firstUnit !== secondUnit) { 107 | let secondThirdValue = `${secondValue + thirdValue}${secondUnit}` 108 | updateValue( 109 | decl, 110 | compose(valueParser.stringify(first), secondThirdValue), 111 | preserve 112 | ) 113 | return 114 | } 115 | 116 | updateValue( 117 | decl, 118 | compose(`${firstValue + secondValue + thirdValue}${secondUnit}`), 119 | preserve 120 | ) 121 | }) 122 | } 123 | } 124 | } 125 | 126 | module.exports.postcss = true 127 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | let postcss = require('postcss') 2 | 3 | let clamp = require('./') 4 | 5 | async function run (input, output, opts) { 6 | let result = await postcss([clamp(opts)]).process(input, { 7 | from: '/test.css' 8 | }) 9 | expect(result.css).toEqual(output) 10 | expect(result.warnings()).toHaveLength(0) 11 | return result 12 | } 13 | 14 | it('handle simple transformation (only values)', async () => { 15 | await run( 16 | 'a{ width: clamp(10px, 64px, 80px); }', 17 | 'a{ width: max(10px, min(64px, 80px)); }' 18 | ) 19 | }) 20 | 21 | it('handle simple transformation (only values) with preserve', async () => { 22 | await run( 23 | 'a{ width: clamp(10px, 64px, 80px); }', 24 | 'a{ width: max(10px, min(64px, 80px)); width: clamp(10px, 64px, 80px); }', 25 | { preserve: true } 26 | ) 27 | }) 28 | 29 | it('handle transformation with functions', async () => { 30 | await run( 31 | 'a{ width: clamp(calc(100% - 10px), min(10px, 100%), max(40px, 4em)); }', 32 | 'a{ width: max(calc(100% - 10px), min(min(10px, 100%), max(40px, 4em))); }' 33 | ) 34 | }) 35 | 36 | it('handle transformation with functions with preserve', async () => { 37 | await run( 38 | 'a{ width: clamp(calc(100% - 10px), min(10px, 100%), max(40px, 4em)); }', 39 | 'a{ width: max(calc(100% - 10px), min(min(10px, 100%), max(40px, 4em))); ' + 40 | 'width: clamp(calc(100% - 10px), min(10px, 100%), max(40px, 4em)); }', 41 | { preserve: true } 42 | ) 43 | }) 44 | 45 | it('handle transformation with different units', async () => { 46 | await run( 47 | 'a{ width: clamp(10%, 2px, 4rem); }', 48 | 'a{ width: max(10%, min(2px, 4rem)); }' 49 | ) 50 | }) 51 | 52 | it('handle transformation with different units and preserve', async () => { 53 | await run( 54 | 'a{ width: clamp(10%, 2px, 4rem); }', 55 | 'a{ width: max(10%, min(2px, 4rem)); width: clamp(10%, 2px, 4rem); }', 56 | { preserve: true } 57 | ) 58 | }) 59 | 60 | it('transform only function with 3 parameters', async () => { 61 | await run( 62 | 'a{ width: clamp(10%, 2px, 4rem);' + 63 | '\nheight: clamp(10px, 20px, 30px, 40px); }', 64 | 'a{ width: max(10%, min(2px, 4rem));' + 65 | '\nheight: clamp(10px, 20px, 30px, 40px); }' 66 | ) 67 | }) 68 | 69 | it('transform only clamp function', async () => { 70 | await run( 71 | 'a{ width: clamp(10%, 2px, 4rem);\nheight: calc(10px + 100%); }', 72 | 'a{ width: max(10%, min(2px, 4rem));\nheight: calc(10px + 100%); }' 73 | ) 74 | }) 75 | 76 | it('precalculate second and third with the same unit (int values)', async () => { 77 | await run('a{ width: clamp(10%, 2px, 5px); }', 'a{ width: max(10%, 7px); }', { 78 | precalculate: true 79 | }) 80 | }) 81 | 82 | it('precalculate second and third with the same unit (float values)', async () => { 83 | await run( 84 | 'a{ width: clamp(10%, 2.5px, 5.1px); }', 85 | 'a{ width: max(10%, 7.6px); }', 86 | { precalculate: true } 87 | ) 88 | }) 89 | 90 | it('precalculate second and third with the same unit (float and int values)', async () => { 91 | await run( 92 | 'a{ width: clamp(10%, 2.5px, 5px); }', 93 | 'a{ width: max(10%, 7.5px); }', 94 | { precalculate: true } 95 | ) 96 | }) 97 | 98 | it('precalculate 2nd & 3rd with the same unit (float and int vals) & preserve', async () => { 99 | await run( 100 | 'a{ width: clamp(10%, 2.5px, 5px); }', 101 | 'a{ width: max(10%, 7.5px); width: clamp(10%, 2.5px, 5px); }', 102 | { precalculate: true, preserve: true } 103 | ) 104 | }) 105 | 106 | it('precalculate all values with the same unit (int values)', async () => { 107 | await run('a{ width: clamp(10px, 2px, 5px); }', 'a{ width: 17px; }', { 108 | precalculate: true 109 | }) 110 | }) 111 | 112 | it('precalculate all values with the same unit (float values)', async () => { 113 | await run( 114 | 'a{ width: clamp(10.4px, 2.11px, 5.9px); }', 115 | 'a{ width: 18.41px; }', 116 | { precalculate: true } 117 | ) 118 | }) 119 | 120 | it('precalculate all values with the same unit (int and float values)', async () => { 121 | await run('a{ width: clamp(10.4px, 2px, 5.9px); }', 'a{ width: 18.3px; }', { 122 | precalculate: true 123 | }) 124 | }) 125 | 126 | it('handle function with enable precalculation as third', async () => { 127 | await run( 128 | 'a{ width: clamp(10px, 2px, calc(10px + 100%)); }', 129 | 'a{ width: max(10px, min(2px, calc(10px + 100%))); }', 130 | { precalculate: true } 131 | ) 132 | }) 133 | 134 | it('handle function with enable precalculation as second', async () => { 135 | await run( 136 | 'a{ width: clamp(10px, calc(10px + 100%), 2px); }', 137 | 'a{ width: max(10px, min(calc(10px + 100%), 2px)); }', 138 | { precalculate: true } 139 | ) 140 | }) 141 | 142 | it('handle function with enable precalculation as first', async () => { 143 | await run( 144 | 'a{ width: clamp(calc(10px + 100%), 10px, 2px); }', 145 | 'a{ width: max(calc(10px + 100%), 12px); }', 146 | { precalculate: true } 147 | ) 148 | }) 149 | 150 | it('handle function with enable precalculation as all', async () => { 151 | await run( 152 | 'a{ width: clamp(calc(10px + 100%), calc(10rem + 200%), 10px); }', 153 | 'a{ width: max(calc(10px + 100%), min(calc(10rem + 200%), 10px)); }', 154 | { precalculate: true } 155 | ) 156 | }) 157 | 158 | it('handle not valid values', async () => { 159 | await run('a{ width: clamp(a, b, c); }', 'a{ width: max(a, min(b, c)); }', { 160 | precalculate: true 161 | }) 162 | }) 163 | 164 | it('handle not valid values with preserve', async () => { 165 | await run( 166 | 'a{ width: clamp(a, b, c); }', 167 | 'a{ width: max(a, min(b, c)); width: clamp(a, b, c); }', 168 | { precalculate: true, preserve: true } 169 | ) 170 | }) 171 | 172 | it('handle not valid values mixed with valid', async () => { 173 | await run( 174 | 'a{ width: clamp(a, 1px, 2em); }', 175 | 'a{ width: max(a, min(1px, 2em)); }', 176 | { precalculate: true } 177 | ) 178 | }) 179 | 180 | it('handle not valid values mixed with valid and preserve', async () => { 181 | await run( 182 | 'a{ width: clamp(a, 1px, 2em); }', 183 | 'a{ width: max(a, min(1px, 2em)); width: clamp(a, 1px, 2em); }', 184 | { precalculate: true, preserve: true } 185 | ) 186 | }) 187 | 188 | it('handle complex values', async () => { 189 | await run( 190 | 'a{ grid-template-columns: clamp(22rem, 40%, 32rem) minmax(0, 1fr); }', 191 | 'a{ grid-template-columns: max(22rem, min(40%, 32rem)) minmax(0, 1fr); }' 192 | ) 193 | }) 194 | 195 | it('handle multiple complex values', async () => { 196 | await run( 197 | 'a{ margin: clamp(1rem, 2%, 3rem) 4px clamp(5rem, 6%, 7rem) 8rem; }', 198 | 'a{ margin: max(1rem, min(2%, 3rem)) 4px max(5rem, min(6%, 7rem)) 8rem; }' 199 | ) 200 | }) 201 | 202 | it('handle calc', async () => { 203 | await run( 204 | 'a{ margin: 0 40px 0 calc(-1 * clamp(32px, 16vw, 64px)); }', 205 | 'a{ margin: 0 40px 0 calc(-1 * max(32px, min(16vw, 64px))); }' 206 | ) 207 | }) 208 | 209 | it('handle multiple calc', async () => { 210 | await run( 211 | 'a{ margin: calc(-1 * clamp(1px, 2vw, 3px)) calc(-1 * clamp(4px, 5vw, 6px)); }', 212 | 'a{ margin: calc(-1 * max(1px, min(2vw, 3px))) calc(-1 * max(4px, min(5vw, 6px))); }' 213 | ) 214 | }) 215 | 216 | it('handle nested clamp', async () => { 217 | await run( 218 | 'a{ font-size: clamp(clamp(1rem, 2vw, 3rem), 4vw, 5rem); }', 219 | 'a{ font-size: max(max(1rem, min(2vw, 3rem)), min(4vw, 5rem)); }' 220 | ) 221 | }) 222 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-clamp", 3 | "version": "4.1.0", 4 | "description": "PostCSS plugin to transform clamp() to combination of min/max", 5 | "keywords": [ 6 | "postcss", 7 | "css", 8 | "postcss-plugin", 9 | "clamp", 10 | "min", 11 | "max" 12 | ], 13 | "main": "index.js", 14 | "repository": "polemius/postcss-clamp", 15 | "author": "Ivan Menshykov ", 16 | "license": "MIT", 17 | "dependencies": { 18 | "postcss-value-parser": "^4.2.0" 19 | }, 20 | "devDependencies": { 21 | "@logux/eslint-config": "^44.2.0", 22 | "clean-publish": "^4.0.0", 23 | "eslint": "^7.32.0", 24 | "eslint-ci": "^1.0.0", 25 | "eslint-config-postcss": "^4.0.0", 26 | "eslint-config-standard": "^16.0.3", 27 | "eslint-plugin-es5": "^1.5.0", 28 | "eslint-plugin-import": "^2.25.4", 29 | "eslint-plugin-jest": "^24.7.0", 30 | "eslint-plugin-node": "^11.1.0", 31 | "eslint-plugin-prefer-let": "^1.1.0", 32 | "eslint-plugin-prettierx": "^0.18.0", 33 | "eslint-plugin-promise": "^4.3.1", 34 | "eslint-plugin-security": "^1.4.0", 35 | "eslint-plugin-standard": "^4.1.0", 36 | "eslint-plugin-unicorn": "^28.0.2", 37 | "jest": "^27.5.1", 38 | "postcss": "^8.4.6" 39 | }, 40 | "peerDependencies": { 41 | "postcss": "^8.4.6" 42 | }, 43 | "scripts": { 44 | "test": "jest && eslint-ci *.js" 45 | }, 46 | "jest": { 47 | "testEnvironment": "node", 48 | "collectCoverage": true, 49 | "coverageDirectory": "./coverage/" 50 | }, 51 | "eslintConfig": { 52 | "extends": "eslint-config-postcss", 53 | "rules": { 54 | "no-useless-escape": 0 55 | } 56 | }, 57 | "engines": { 58 | "node": ">=7.6.0" 59 | } 60 | } 61 | --------------------------------------------------------------------------------