├── test ├── scale.css ├── rotate.css ├── translate.css ├── scale.expect.css ├── rotate.expect.css ├── translate.expect.css ├── combo.css └── combo.expect.css ├── .travis.yml ├── .gitignore ├── .tape.js ├── CHANGELOG.md ├── package.json ├── CONTRIBUTING.md ├── index.js ├── README.md └── LICENSE.md /test/scale.css: -------------------------------------------------------------------------------- 1 | .scale { 2 | scale: .5 .5; 3 | } 4 | -------------------------------------------------------------------------------- /test/rotate.css: -------------------------------------------------------------------------------- 1 | .rotate { 2 | rotate: 180deg; 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | test/*.result.css 4 | -------------------------------------------------------------------------------- /test/translate.css: -------------------------------------------------------------------------------- 1 | .translate { 2 | translate: 10px 10px; 3 | } 4 | -------------------------------------------------------------------------------- /test/scale.expect.css: -------------------------------------------------------------------------------- 1 | .scale { 2 | transform: scale3d(.5,.5,1); 3 | } 4 | -------------------------------------------------------------------------------- /test/rotate.expect.css: -------------------------------------------------------------------------------- 1 | .rotate { 2 | transform: rotate3d(0,0,1,180deg); 3 | } 4 | -------------------------------------------------------------------------------- /test/translate.expect.css: -------------------------------------------------------------------------------- 1 | .translate { 2 | transform: translate3d(10px,10px,0); 3 | } 4 | -------------------------------------------------------------------------------- /test/combo.css: -------------------------------------------------------------------------------- 1 | .combo { 2 | transform: rotate(90deg); 3 | rotate: 180deg; 4 | scale: 0.5 0.5; 5 | translate: 10px 10px; 6 | } 7 | -------------------------------------------------------------------------------- /test/combo.expect.css: -------------------------------------------------------------------------------- 1 | .combo { 2 | transform: rotate(90deg) rotate3d(0,0,1,180deg) scale3d(0.5,0.5,1) translate3d(10px,10px,0); 3 | } 4 | -------------------------------------------------------------------------------- /.tape.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'postcss-transform-shortcut': { 3 | 'rotate': { 4 | message: 'supports rotate property' 5 | }, 6 | 'scale': { 7 | message: 'supports scale property' 8 | }, 9 | 'translate': { 10 | message: 'supports translate property' 11 | }, 12 | 'combo': { 13 | message: 'supports multiple shortcut properties' 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changes to Transform Shortcut 2 | 3 | ### 2.0.1 (December 8, 2016) 4 | 5 | - Updated: Use destructing assignment on plugin options 6 | - Updated: Use template literals 7 | 8 | ### 2.0.0 (December 6, 2016) 9 | 10 | - Updated: Correct `rotate` assignment 11 | - Updated: boilerplate conventions (Node v6.9.1 LTS) 12 | 13 | ### 1.0.0 (July 18, 2015) 14 | 15 | Updated: PostCSS 5 16 | Updated: Develop dependencies 17 | Updated: ESLint configuration 18 | 19 | ### 0.1.0 (July 18, 2015) 20 | 21 | Added: Initial release 22 | Added: Reference to specification 23 | Added: Support for `.process` method 24 | Updated: Functionality to match specification 25 | Updated: Tests 26 | 27 | ### 0.0.1 (July 18, 2015) 28 | 29 | Added: Pre-release 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-transform-shortcut", 3 | "version": "2.0.1", 4 | "description": "Use shorthand transform properties in CSS", 5 | "author": "Jonathan Neal ", 6 | "license": "CC0-1.0", 7 | "repository": "jonathantneal/postcss-transform-shortcut", 8 | "homepage": "https://github.com/jonathantneal/postcss-transform-shortcut#readme", 9 | "bugs": "https://github.com/jonathantneal/postcss-transform-shortcut/issues", 10 | "main": "index.js", 11 | "files": [ 12 | "index.js" 13 | ], 14 | "scripts": { 15 | "lint": "echint && eslint index.js && jscs index.js", 16 | "prepublish": "npm test", 17 | "tape": "postcss-tape", 18 | "test": "npm run lint && postcss-tape" 19 | }, 20 | "engines": { 21 | "node": ">=6.9.1" 22 | }, 23 | "dependencies": { 24 | "postcss": "^5.2.6" 25 | }, 26 | "devDependencies": { 27 | "echint": "^2.1.0", 28 | "echint-config-dev": "1.0.0", 29 | "eslint": "^3.12.1", 30 | "eslint-config-dev": "1.0.0", 31 | "jscs": "^3.0.7", 32 | "jscs-config-dev": "1.0.1", 33 | "postcss-tape": "1.3.0" 34 | }, 35 | "echint": { 36 | "extends": "dev" 37 | }, 38 | "eslintConfig": { 39 | "extends": "dev", 40 | "parserOptions": { 41 | "sourceType": "module" 42 | } 43 | }, 44 | "jscsConfig": { 45 | "preset": "dev" 46 | }, 47 | "keywords": [ 48 | "postcss", 49 | "css", 50 | "postcss-plugin", 51 | "transforms", 52 | "translate3d", 53 | "translate", 54 | "rotate", 55 | "scale" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Transform Shortcut 2 | 3 | You want to help? You rock! Now, take a moment to be sure your contributions 4 | make sense to everyone else. 5 | 6 | ## Reporting Issues 7 | 8 | Found a problem? Want a new feature? 9 | 10 | - See if your issue or idea has [already been reported]. 11 | - Provide a [reduced test case] or a [live example]. 12 | 13 | Remember, a bug is a _demonstrable problem_ caused by _our_ code. 14 | 15 | ## Submitting Pull Requests 16 | 17 | Pull requests are the greatest contributions, so be sure they are focused in 18 | scope, and do avoid unrelated commits. 19 | 20 | 1. To begin, [fork this project], clone your fork, and add our upstream. 21 | ```bash 22 | # Clone your fork of the repo into the current directory 23 | git clone https://github.com//postcss-transform-shortcut 24 | # Navigate to the newly cloned directory 25 | cd postcss-transform-shortcut 26 | # Assign the original repo to a remote called "upstream" 27 | git remote add upstream https://github.com/jonathantneal/postcss-transform-shortcut 28 | # Install the tools necessary for development 29 | npm install 30 | ``` 31 | 32 | 2. Create a branch for your feature or fix: 33 | ```bash 34 | # Move into a new branch for a feature 35 | git checkout -b feature/thing 36 | ``` 37 | ```bash 38 | # Move into a new branch for a fix 39 | git checkout -b fix/something 40 | ``` 41 | 42 | 3. Be sure your code follows our practices. 43 | ```bash 44 | # Test current code 45 | npm run test 46 | ``` 47 | 48 | 4. Push your branch up to your fork: 49 | ```bash 50 | # Push a feature branch 51 | git push origin feature/thing 52 | ``` 53 | ```bash 54 | # Push a fix branch 55 | git push origin fix/something 56 | ``` 57 | 58 | 5. Now [open a pull request] with a clear title and description. 59 | 60 | [already been reported]: issues 61 | [fork this project]: fork 62 | [live example]: http://codepen.io/pen 63 | [open a pull request]: https://help.github.com/articles/using-pull-requests/ 64 | [reduced test case]: https://css-tricks.com/reduced-test-cases/ 65 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // tooling 2 | const postcss = require('postcss'); 3 | 4 | // transform defaults 5 | const defaults = { 6 | rotate: [0, 0, 1, '0deg'], 7 | scale: [1, 1, 1], 8 | translate: [0, 0, 0] 9 | }; 10 | 11 | // plugin 12 | module.exports = postcss.plugin('postcss-transform-shortcut', () => (css) => { 13 | // walk each rule 14 | css.walkRules((rule) => { 15 | // recent transform declaration 16 | let transform; 17 | 18 | // recent transform values 19 | let values = []; 20 | 21 | rule.nodes.slice(0).forEach((decl) => { 22 | // whether the declaration is a transform 23 | if (decl.prop === 'transform') { 24 | // reset recent transform declaration 25 | transform = decl; 26 | 27 | // reset recent transform values 28 | values = postcss.list.space(decl.value); 29 | } else if (/^(rotate|scale|translate)?$/.test(decl.prop)) { 30 | // whether a transform declaration does not exist 31 | if (!transform) { 32 | // recent transform declaration is a modified clone of the shorthand 33 | transform = decl.cloneBefore({ 34 | prop: 'transform' 35 | }); 36 | } 37 | 38 | // new values from defaults 39 | const newValues = defaults[decl.prop].slice(0); 40 | 41 | // current values from the current property 42 | const currentValues = postcss.list.space(decl.value); 43 | 44 | // whether the property is rotate with one value 45 | if (decl.prop === 'rotate' && currentValues.length === 1) { 46 | // update the values 47 | newValues.splice(-1, 1, ...currentValues); 48 | } else { 49 | // update the values 50 | newValues.splice(0, currentValues.length, ...currentValues); 51 | } 52 | 53 | // push the shorthand to transform values 54 | values.push(decl.prop + `3d(${ newValues.join(',') })`); 55 | 56 | // remove the shorthand 57 | decl.remove(); 58 | } 59 | }); 60 | 61 | // whether there are transform values 62 | if (values.length) { 63 | // assign the transform values 64 | transform.value = values.join(' '); 65 | } 66 | }); 67 | }); 68 | 69 | // override plugin#process 70 | module.exports.process = function (cssString, pluginOptions, processOptions) { 71 | return postcss([ 72 | 0 in arguments ? module.exports(pluginOptions) : module.exports() 73 | ]).process(cssString, processOptions); 74 | }; 75 | 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Transform Shortcut PostCSS Logo 2 | 3 | [![NPM Version][npm-img]][npm-url] 4 | [![Build Status][cli-img]][cli-url] 5 | [![Licensing][lic-image]][lic-url] 6 | [![Changelog][log-image]][log-url] 7 | [![Gitter Chat][git-image]][git-url] 8 | 9 | [Transform Shortcut] lets you use shorthand transform properties in CSS, following the [CSS Transform Module Level 2 Specification]. 10 | 11 | ```css 12 | /* before */ 13 | 14 | .transform { 15 | transform: skewX(25deg); 16 | rotate: 180deg; 17 | scale: 2 2; 18 | translate: 10px 10px; 19 | } 20 | 21 | /* after */ 22 | 23 | .transform { 24 | transform: skewX(25deg) rotate3d(180deg,0,1) scale3d(2,2,1) translate3d(10px,10px,0px); 25 | } 26 | 27 | ``` 28 | 29 | The `translate`, `rotate`, and `scale` properties allow authors to specify simple transforms independently, in a way that maps to typical user interface usage, rather than having to remember the order in transform that keeps the actions of `transform()`, `rotate()` and `scale()` independent and acting in screen coordinates. 30 | 31 | The `rotate` property accepts an angle to rotate an element, and optionally an axis to rotate it around, specified as the X, Y, and Z lengths of an origin-anchored vector. If the axis is unspecified, it defaults to `0 0 1`, causing a "2d rotation" in the plane of the screen. 32 | 33 | The `scale` property accepts 1-3 values, each specifying a scale along one axis, in order X, Y, then Z. Unspecified scales default to `1`. 34 | 35 | The `translate` property accepts 1-3 values, each specifying a translation against one axis, in the order X, Y, then Z. Unspecified translations default to `0px`. 36 | 37 | ## Caveat 38 | 39 | Once these new properties are supported natively, you can also use them to style transforms across multiple rules without overwriting a previous rule’s transforms. Unfortunately, I cannot predict how your CSS rules will be inherited without also knowing your DOM. Therefore, this particularly useful feature cannot be simulated at this time. 40 | 41 | ```css 42 | .button { 43 | rotate: 45deg; 44 | } 45 | 46 | .button--warn { 47 | scale: 2; 48 | } 49 | ``` 50 | 51 | ## Usage 52 | 53 | Add [Transform Shortcut] to your build tool: 54 | 55 | ```bash 56 | npm install jonathantneal/postcss-transform-shortcut --save-dev 57 | ``` 58 | 59 | #### Node 60 | 61 | ```js 62 | require('postcss-transform-shortcut').process(YOUR_CSS, { /* options */ }); 63 | ``` 64 | 65 | #### PostCSS 66 | 67 | Add [PostCSS] to your build tool: 68 | 69 | ```bash 70 | npm install postcss --save-dev 71 | ``` 72 | 73 | Load [Transform Shortcut] as a PostCSS plugin: 74 | 75 | ```js 76 | postcss([ 77 | require('postcss-transform-shortcut')({ /* options */ }) 78 | ]).process(YOUR_CSS, /* options */); 79 | ``` 80 | 81 | #### Gulp 82 | 83 | Add [Gulp PostCSS] to your build tool: 84 | 85 | ```bash 86 | npm install gulp-postcss --save-dev 87 | ``` 88 | 89 | Enable [Transform Shortcut] within your Gulpfile: 90 | 91 | ```js 92 | var postcss = require('gulp-postcss'); 93 | 94 | gulp.task('css', function () { 95 | return gulp.src('./src/*.css').pipe( 96 | postcss([ 97 | require('postcss-transform-shortcut')({ /* options */ }) 98 | ]) 99 | ).pipe( 100 | gulp.dest('.') 101 | ); 102 | }); 103 | ``` 104 | 105 | #### Grunt 106 | 107 | Add [Grunt PostCSS] to your build tool: 108 | 109 | ```bash 110 | npm install grunt-postcss --save-dev 111 | ``` 112 | 113 | Enable [Transform Shortcut] within your Gruntfile: 114 | 115 | ```js 116 | grunt.loadNpmTasks('grunt-postcss'); 117 | 118 | grunt.initConfig({ 119 | postcss: { 120 | options: { 121 | use: [ 122 | require('postcss-transform-shortcut')({ /* options */ }) 123 | ] 124 | }, 125 | dist: { 126 | src: '*.css' 127 | } 128 | } 129 | }); 130 | ``` 131 | 132 | [npm-url]: https://www.npmjs.com/package/postcss-transform-shortcut 133 | [npm-img]: https://img.shields.io/npm/v/postcss-transform-shortcut.svg 134 | [cli-url]: https://travis-ci.org/jonathantneal/postcss-transform-shortcut 135 | [cli-img]: https://img.shields.io/travis/jonathantneal/postcss-transform-shortcut.svg 136 | [lic-url]: LICENSE.md 137 | [lic-image]: https://img.shields.io/npm/l/postcss-transform-shortcut.svg 138 | [log-url]: CHANGELOG.md 139 | [log-image]: https://img.shields.io/badge/changelog-md-blue.svg 140 | [git-url]: https://gitter.im/postcss/postcss 141 | [git-image]: https://img.shields.io/badge/chat-gitter-blue.svg 142 | 143 | [Transform Shortcut]: https://github.com/jonathantneal/postcss-transform-shortcut 144 | [PostCSS]: https://github.com/postcss/postcss 145 | [Gulp PostCSS]: https://github.com/postcss/gulp-postcss 146 | [Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss 147 | 148 | [CSS Transform Module Level 2 Specification]: https://drafts.csswg.org/css-transforms-2/ 149 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # CC0 1.0 Universal 2 | 3 | ## Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an “owner”) of an original work of 8 | authorship and/or a database (each, a “Work”). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific works 12 | (“Commons”) that the public can reliably and without fear of later claims of 13 | infringement build upon, modify, incorporate in other works, reuse and 14 | redistribute as freely as possible in any form whatsoever and for any purposes, 15 | including without limitation commercial purposes. These owners may contribute 16 | to the Commons to promote the ideal of a free culture and the further 17 | production of creative, cultural and scientific works, or to gain reputation or 18 | greater distribution for their Work in part through the use and efforts of 19 | others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation of 22 | additional consideration or compensation, the person associating CC0 with a 23 | Work (the “Affirmer”), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and 25 | publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights (“Copyright and 31 | Related Rights”). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 1. the right to reproduce, adapt, distribute, perform, display, 34 | communicate, and translate a Work; 35 | 2. moral rights retained by the original author(s) and/or performer(s); 36 | 3. publicity and privacy rights pertaining to a person’s image or likeness 37 | depicted in a Work; 38 | 4. rights protecting against unfair competition in regards to a Work, 39 | subject to the limitations in paragraph 4(i), below; 40 | 5. rights protecting the extraction, dissemination, use and reuse of data 41 | in a Work; 42 | 6. database rights (such as those arising under Directive 96/9/EC of the 43 | European Parliament and of the Council of 11 March 1996 on the legal 44 | protection of databases, and under any national implementation thereof, 45 | including any amended or successor version of such directive); and 46 | 7. other similar, equivalent or corresponding rights throughout the world 47 | based on applicable law or treaty, and any national implementations 48 | thereof. 49 | 50 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 51 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 52 | unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright 53 | and Related Rights and associated claims and causes of action, whether now 54 | known or unknown (including existing as well as future claims and causes of 55 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 56 | duration provided by applicable law or treaty (including future time 57 | extensions), (iii) in any current or future medium and for any number of 58 | copies, and (iv) for any purpose whatsoever, including without limitation 59 | commercial, advertising or promotional purposes (the “Waiver”). Affirmer makes 60 | the Waiver for the benefit of each member of the public at large and to the 61 | detriment of Affirmer’s heirs and successors, fully intending that such Waiver 62 | shall not be subject to revocation, rescission, cancellation, termination, or 63 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 64 | by the public as contemplated by Affirmer’s express Statement of Purpose. 65 | 66 | 3. Public License Fallback. Should any part of the Waiver for any reason be 67 | judged legally invalid or ineffective under applicable law, then the Waiver 68 | shall be preserved to the maximum extent permitted taking into account 69 | Affirmer’s express Statement of Purpose. In addition, to the extent the Waiver 70 | is so judged Affirmer hereby grants to each affected person a royalty-free, non 71 | transferable, non sublicensable, non exclusive, irrevocable and unconditional 72 | license to exercise Affirmer’s Copyright and Related Rights in the Work (i) in 73 | all territories worldwide, (ii) for the maximum duration provided by applicable 74 | law or treaty (including future time extensions), (iii) in any current or 75 | future medium and for any number of copies, and (iv) for any purpose 76 | whatsoever, including without limitation commercial, advertising or promotional 77 | purposes (the “License”). The License shall be deemed effective as of the date 78 | CC0 was applied by Affirmer to the Work. Should any part of the License for any 79 | reason be judged legally invalid or ineffective under applicable law, such 80 | partial invalidity or ineffectiveness shall not invalidate the remainder of the 81 | License, and in such case Affirmer hereby affirms that he or she will not (i) 82 | exercise any of his or her remaining Copyright and Related Rights in the Work 83 | or (ii) assert any associated claims and causes of action with respect to the 84 | Work, in either case contrary to Affirmer’s express Statement of Purpose. 85 | 86 | 4. Limitations and Disclaimers. 87 | 1. No trademark or patent rights held by Affirmer are waived, abandoned, 88 | surrendered, licensed or otherwise affected by this document. 89 | 2. Affirmer offers the Work as-is and makes no representations or 90 | warranties of any kind concerning the Work, express, implied, statutory 91 | or otherwise, including without limitation warranties of title, 92 | merchantability, fitness for a particular purpose, non infringement, or 93 | the absence of latent or other defects, accuracy, or the present or 94 | absence of errors, whether or not discoverable, all to the greatest 95 | extent permissible under applicable law. 96 | 3. Affirmer disclaims responsibility for clearing rights of other persons 97 | that may apply to the Work or any use thereof, including without 98 | limitation any person’s Copyright and Related Rights in the Work. 99 | Further, Affirmer disclaims responsibility for obtaining any necessary 100 | consents, permissions or other rights required for any use of the Work. 101 | 4. Affirmer understands and acknowledges that Creative Commons is not a 102 | party to this document and has no duty or obligation with respect to 103 | this CC0 or use of the Work. 104 | 105 | For more information, please see 106 | http://creativecommons.org/publicdomain/zero/1.0/. 107 | --------------------------------------------------------------------------------