├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── index.js ├── package.json └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [{package.json,*.js,*.yml}] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | npm-debug.log 5 | index.es5.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.12' 4 | - 'iojs-2' 5 | - 'iojs-1' 6 | after_script: 7 | - npm run coveralls 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # postcss-color-mix 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![Build Status][travis-image]][travis-url] 5 | [![Coveralls Status][coveralls-image]][coveralls-url] 6 | [![Dependency Status][depstat-image]][depstat-url] 7 | 8 | > Mix two colors together in [PostCSS][PostCSS] 9 | 10 | Mixes two colors together. Specifically, takes the average 11 | of each of the RGB components, optionally weighted by the given percentage. 12 | The opacity of the colors is also considered when weighting the components. 13 | [I borrowed implementation from Sass][sass]. 14 | 15 | The weight specifies the amount of the first color that should be included 16 | in the returned color. The default, 50%, means that half the first color 17 | and half the second color should be used. 25% means that a quarter 18 | of the first color and three quarters of the second color should be used. 19 | 20 | ## Install 21 | 22 | npm install --save postcss-color-mix 23 | 24 | ## Usage 25 | 26 | postcss([ require('postcss-color-mix') ]) 27 | 28 | See [PostCSS] docs for examples for your environment. 29 | 30 | ```css 31 | .foo { 32 | color: mix(#f00, #00f); /* #800080 */ 33 | color: mix(#f00, #00f, 25%); /* #4000BF */ 34 | color: mix(#f00, #00f, .25); /* #4000BF */ 35 | color: mix(rgba(255, 0, 0, 0.5), #00f); /* rgba(64, 0, 191, 0.75) */ 36 | } 37 | ``` 38 | 39 | ## License 40 | 41 | MIT © [Vladimir Starkov](https://iamstarkov.com/) 42 | 43 | 44 | [sass]: http://sass-lang.com/documentation/Sass/Script/Functions.html#mix-instance_method 45 | [PostCSS]: https://github.com/postcss/postcss 46 | 47 | [npm-url]: https://npmjs.org/package/postcss-color-mix 48 | [npm-image]: https://img.shields.io/npm/v/postcss-color-mix.svg?style=flat-square 49 | 50 | [travis-url]: https://travis-ci.org/iamstarkov/postcss-color-mix 51 | [travis-image]: https://img.shields.io/travis/iamstarkov/postcss-color-mix.svg?style=flat-square 52 | 53 | [coveralls-url]: https://coveralls.io/r/iamstarkov/postcss-color-mix 54 | [coveralls-image]: https://img.shields.io/coveralls/iamstarkov/postcss-color-mix.svg?style=flat-square 55 | 56 | [depstat-url]: https://david-dm.org/iamstarkov/postcss-color-mix 57 | [depstat-image]: https://david-dm.org/iamstarkov/postcss-color-mix.svg?style=flat-square 58 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss'; 2 | import balanced from 'balanced-match'; 3 | import Color from 'color'; 4 | import { try as postcssTry } from 'postcss-message-helpers'; 5 | 6 | const mix = (c1, c2, w='') => { 7 | const weight = w.endsWith('%') ? w.replace('%', '') : w * 100; 8 | const mixed = Color(c1).mix(Color(c2), weight); 9 | return mixed.alpha() < 1 ? mixed.rgbaString() : mixed.hexString(); 10 | }; 11 | 12 | const transformColor = (string, source) => { 13 | if (string.indexOf('mix(') === -1) { 14 | return string; 15 | } 16 | 17 | const value = balanced('(', ')', string).body; 18 | 19 | if (!value) { throw new Error(`Missing closing parentheses in "${string}"`, source); } 20 | 21 | return mix.apply(null, value.split(/,\s*(?![^()]*\))/)); 22 | }; 23 | 24 | const transformDecl = (decl) => { 25 | if (!decl.value || decl.value.indexOf('mix(') === -1) { 26 | return; 27 | } 28 | 29 | decl.value = postcssTry(() => transformColor(decl.value, decl.source), decl.source ) 30 | }; 31 | 32 | export default postcss.plugin('postcss-color-mix', () => 33 | (style) => { style.walkDecls(transformDecl); } 34 | ); 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-color-mix", 3 | "version": "1.1.0", 4 | "description": "Mix two colors together in PostCSS", 5 | "main": "index.es5.js", 6 | "scripts": { 7 | "coverage": "isparta cover _mocha index.js -- --require babel/register", 8 | "precoveralls": "npm run coverage", 9 | "coveralls": "coveralls < coverage/lcov.info", 10 | "test": "mocha --require babel/register", 11 | "tdd": "npm test -- --watch", 12 | "transpile": "babel index.js > index.es5.js", 13 | "prepublish": "npm run transpile", 14 | "clean": "trash index.es5.js", 15 | "push": "git push --follow-tags", 16 | "postpublish": "npm-run-all clean push" 17 | }, 18 | "dependencies": { 19 | "balanced-match": "^0.2.0", 20 | "color": "^0.9.0", 21 | "postcss": "^5.0.2", 22 | "postcss-message-helpers": "^2.0.0" 23 | }, 24 | "devDependencies": { 25 | "assert": "*", 26 | "babel": "*", 27 | "coveralls": "*", 28 | "isparta": "*", 29 | "mocha": "*", 30 | "npm-run-all": "*", 31 | "trash": "*" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/iamstarkov/postcss-color-mix.git" 36 | }, 37 | "keywords": [ 38 | "postcss", 39 | "css", 40 | "postcss-plugin", 41 | "color", 42 | "mix" 43 | ], 44 | "author": "Vladimir Starkov (https://iamstarkov.com/)", 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/iamstarkov/postcss-color-mix/issues" 48 | }, 49 | "homepage": "https://github.com/iamstarkov/postcss-color-mix#readme" 50 | } 51 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import { equal } from 'assert'; 2 | import postcss from 'postcss'; 3 | import mix from './index'; 4 | 5 | const verify = function(input, expected, done) { 6 | postcss([ mix() ]) 7 | .process(input) 8 | .then((result) => { 9 | equal(result.css, expected); 10 | equal(result.warnings().length, 0); 11 | done(); 12 | }).catch((error) => { done(error); }); 13 | }; 14 | 15 | it('mix two hex colors without weight', (done)=> { 16 | verify( 17 | `a { color: mix(#f00, #00f); }`, 18 | `a { color: #800080; }`, 19 | done); 20 | }); 21 | 22 | it('mix two hex colors with weight as percentage', (done)=> { 23 | verify( 24 | `a { color: mix(#f00, #00f, 25%); }`, 25 | `a { color: #4000BF; }`, 26 | done); 27 | }); 28 | 29 | it('mix two hex colors with weight as rational number', (done)=> { 30 | verify( 31 | `a { color: mix(#f00, #00f, 0.25); }`, 32 | `a { color: #4000BF; }`, 33 | done); 34 | }); 35 | 36 | it('mix hex color with rgba one with weight', (done)=> { 37 | verify( 38 | `a { color: mix(rgba(255, 0, 0, 0.5), #00f); }`, 39 | `a { color: rgba(64, 0, 191, 0.75); }`, 40 | done); 41 | }); 42 | --------------------------------------------------------------------------------