├── test ├── mocha.opts ├── fixtures │ ├── modern-notation.css │ ├── modern-notation.expected.css │ ├── shorthand.css │ ├── multiple.css │ ├── shorthand.expected.css │ ├── important.css │ ├── multiple.expected.css │ ├── important.expected.css │ ├── standard.css │ ├── standard.expected.css │ ├── complex.css │ └── complex.expected.css └── test.js ├── .npmignore ├── .gitignore ├── .travis.yml ├── .eslintrc ├── package.json ├── LICENSE ├── README.md ├── index.js └── CHANGELOG.md /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter nyan 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | .eslintrc 3 | .travis.yml 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Deps 2 | node_modules 3 | 4 | # Runtime 5 | *.log 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /test/fixtures/modern-notation.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: rgba(#ffffff / 50%); 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/modern-notation.expected.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: rgba(255 255 255 / 50%); 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "14" 5 | - "12" 6 | - "10" 7 | -------------------------------------------------------------------------------- /test/fixtures/shorthand.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: rgba(#fff, 0.5); 3 | background: rgba(#000, .8); 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/multiple.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | border: 1px solid rgba(#f00, 0.2); 3 | border: rgba(#f00,.2) 1px solid; 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/shorthand.expected.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: rgba(255,255,255, 0.5); 3 | background: rgba(0,0,0, .8); 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/important.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | border: 1px solid rgba(#f00, 0.2) !important; 3 | color: rgba(#f00,.2) !important; 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/multiple.expected.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | border: 1px solid rgba(255,0,0, 0.2); 3 | border: rgba(255,0,0,.2) 1px solid; 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/important.expected.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | border: 1px solid rgba(255,0,0, 0.2) !important; 3 | color: rgba(255,0,0,.2) !important; 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/standard.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: rgba(#ffffff, 0.5); 3 | background: rgba(#00b2ff,.8); 4 | border: rgba( #fff, .2 ) 1px solid; 5 | border-bottom: rgba( #fff , .2 ) 1px solid; 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/standard.expected.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: rgba(255,255,255, 0.5); 3 | background: rgba(0,178,255,.8); 4 | border: rgba( 255,255,255, .2 ) 1px solid; 5 | border-bottom: rgba( 255,255,255 , .2 ) 1px solid; 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/complex.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | background: linear-gradient(rgba(#0fab53,.1), rgba(0, 0, 0, 0.2), rgba(#000, 0.2)); 3 | background-image: linear-gradient(#f00, rgba(#f00, 0.2)), 4 | linear-gradient(#0f0, rgba(#0f0, 0.2)); 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/complex.expected.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | background: linear-gradient(rgba(15,171,83,.1), rgba(0, 0, 0, 0.2), rgba(0,0,0, 0.2)); 3 | background-image: linear-gradient(#f00, rgba(255,0,0, 0.2)), 4 | linear-gradient(#0f0, rgba(0,255,0, 0.2)); 5 | } 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "mocha": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "rules": { 9 | "indent": [ 10 | "error", 11 | 2, 12 | { "VariableDeclarator": 2 } 13 | ], 14 | "linebreak-style": [ 15 | "error", 16 | "unix" 17 | ], 18 | "quotes": [ 19 | "error", 20 | "single" 21 | ], 22 | "semi": [ 23 | "error", 24 | "always" 25 | ] 26 | } 27 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-hexrgba", 3 | "version": "2.1.0", 4 | "description": "PostCSS plugin that adds shorthand hex methods to rgba() values", 5 | "keywords": [ 6 | "postcss", 7 | "css", 8 | "postcss-plugin", 9 | "hex", 10 | "rgba" 11 | ], 12 | "license": "MIT", 13 | "repository": "seaneking/postcss-hexrgba", 14 | "author": "Sean King ", 15 | "maintainers": [ 16 | { 17 | "name": "Sean King", 18 | "email": "sean@simpla.io", 19 | "web": "http://simpla.io" 20 | } 21 | ], 22 | "dependencies": { 23 | "postcss-value-parser": "^4.1.0" 24 | }, 25 | "peerDependencies": { 26 | "postcss": "^8.1.4" 27 | }, 28 | "devDependencies": { 29 | "chai": "^4.2.0", 30 | "eslint": "^5.16.0", 31 | "mocha": "^6.1.1", 32 | "postcss": "^8.1.4" 33 | }, 34 | "scripts": { 35 | "test": "mocha test", 36 | "posttest": "eslint ." 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2017 Sean King 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PostCSS HexRGBA 2 | [![NPM version][npm-badge]][npm-url] [![Downloads][downloads-badge]][npm-url] [![Build Status][travis-badge]][travis-url] 3 | 4 | [PostCSS][PostCSS] plugin that adds shorthand hex methods to rgba() values. 5 | 6 | _Part of [Rucksack - CSS Superpowers](https://www.rucksackcss.org/)_ 7 | 8 | **Input** 9 | 10 | ```css 11 | .foo { 12 | color: rgba(#0fab53, 0.8) 13 | } 14 | 15 | .bar { 16 | background: linear-gradient(rgba(#fff, .1), rgba(#fff, .2)); 17 | } 18 | ``` 19 | 20 | **Output** 21 | 22 | ```css 23 | .foo { 24 | color: rgba(15,171,83, 0.8) 25 | } 26 | 27 | .bar { 28 | background: linear-gradient(rgba(255,255,255, .1), rgba(255,255,255, .2)); 29 | } 30 | ``` 31 | 32 | ## Usage 33 | 34 | ```js 35 | postcss([ require('postcss-hexrgba') ]) 36 | ``` 37 | 38 | See [PostCSS][PostCSS] docs for examples for your environment. 39 | 40 | *** 41 | 42 | MIT © [Sean King](https://twitter.com/seaneking) 43 | 44 | [npm-badge]: https://badge.fury.io/js/postcss-hexrgba.svg 45 | [npm-url]: https://npmjs.org/package/postcss-hexrgba 46 | [downloads-badge]: https://img.shields.io/npm/dm/postcss-hexrgba.svg 47 | [travis-badge]: https://travis-ci.org/seaneking/postcss-hexrgba.svg?branch=master 48 | [travis-url]: https://travis-ci.org/seaneking/postcss-hexrgba 49 | [PostCSS]: https://github.com/postcss/postcss 50 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const postcss = require('postcss'); 4 | const expect = require('chai').expect; 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const plugin = require('../'); 8 | 9 | function test(fixture, opts, done) { 10 | let input = fixture + '.css', 11 | expected = fixture + '.expected.css'; 12 | 13 | input = fs.readFileSync(path.join(__dirname, 'fixtures', input), 'utf8'); 14 | expected = fs.readFileSync(path.join(__dirname, 'fixtures', expected), 'utf8'); 15 | 16 | postcss([ plugin(opts) ]) 17 | .process(input, {from: undefined}) 18 | .then(result => { 19 | expect(result.css).to.eql(expected); 20 | expect(result.warnings()).to.be.empty; 21 | done(); 22 | }).catch(error => done(error)); 23 | } 24 | 25 | describe('postcss-hexrgba', () => { 26 | 27 | it('handles standard hex', done => test('standard', {}, done)); 28 | 29 | it('handles shorthand hex', done => test('shorthand', {}, done)); 30 | 31 | it('handles hex in multiple attributes', done => test('multiple', {}, done)); 32 | 33 | it('handles complex statements', done => test('complex', {}, done)); 34 | 35 | it('preservers !important', done => test('important', {}, done)); 36 | 37 | it('modern color function notation', done => test('modern-notation', { notation: 'modern' }, done)); 38 | }); 39 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const valueParser = require('postcss-value-parser'); 4 | 5 | const rgbShorthandRegex = /^([a-f\d])([a-f\d])([a-f\d])$/i; 6 | const rgbRegex = /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; 7 | 8 | /** 9 | * Hex to RGB converter 10 | * @param {string} hex hexadecimal string without # 11 | * @return {array} RGB values 12 | */ 13 | function hexRgb(hex){ 14 | hex = hex.replace(rgbShorthandRegex, (m, r, g, b) => { 15 | return r + r + g + g + b + b; 16 | }); 17 | 18 | const rgb = hex.match(rgbRegex); 19 | 20 | // Convert it 21 | return rgb ? [ 22 | parseInt(rgb[1], 16), 23 | parseInt(rgb[2], 16), 24 | parseInt(rgb[3], 16) 25 | ] : false; 26 | } 27 | 28 | /** 29 | * @typedef {{ colorFunctionNotation: 'legacy' | 'modern' }} Options 30 | */ 31 | 32 | /** 33 | * CSS rule handler 34 | * @param {string} decl CSS declaration 35 | * @param {any} result PostCSS result 36 | * @param {Options} options 37 | */ 38 | function ruleHandler(decl, result, options) { 39 | const value = valueParser(decl.value).walk(node => { 40 | if (node.type !== 'function' || node.value !== 'rgba') { 41 | return; 42 | } 43 | 44 | const nodes = node.nodes; 45 | // Check for the hex value 46 | if (nodes[0].type !== 'word' || nodes[0].value.indexOf('#') !== 0) { 47 | return; 48 | } 49 | 50 | const hex = nodes[0].value.replace('#', ''); 51 | const rgb = hexRgb(hex); 52 | 53 | // If conversion fails, emit a warning 54 | if (!rgb) { 55 | result.warn('not a valid hex', { node: decl }); 56 | return; 57 | } 58 | 59 | // Replace hex value with rgb 60 | nodes[0].value = options.colorFunctionNotation === 'modern' ? rgb.join(' ') : rgb.join(','); 61 | }).toString(); 62 | 63 | decl.value = value; 64 | } 65 | 66 | /** 67 | * @param {Options} [options] 68 | */ 69 | module.exports = (options = {}) => { 70 | const colorFunctionNotation = options.colorFunctionNotation || 'legacy'; 71 | 72 | return { 73 | postcssPlugin: 'postcss-hexrgba', 74 | 75 | Declaration(decl, { result }) { 76 | if (!decl.value.includes('rgba')) { 77 | return; 78 | } 79 | 80 | ruleHandler(decl, result, { colorFunctionNotation }); 81 | }, 82 | }; 83 | }; 84 | 85 | module.exports.postcss = true; 86 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v1.0.1](https://github.com/seaneking/postcss-hexrgba/tree/v1.0.1) (2018-04-30) 4 | [Full Changelog](https://github.com/seaneking/postcss-hexrgba/compare/v1.0.0...v1.0.1) 5 | 6 | **Closed issues:** 7 | 8 | - !important gets removed [\#9](https://github.com/seaneking/postcss-hexrgba/issues/9) 9 | - Plugin not working with specific linear-gradient background [\#6](https://github.com/seaneking/postcss-hexrgba/issues/6) 10 | 11 | **Merged pull requests:** 12 | 13 | - directly substitute the hex in the replace callback [\#12](https://github.com/seaneking/postcss-hexrgba/pull/12) ([schelmo](https://github.com/schelmo)) 14 | - add a test for !important \( \#10 \) [\#11](https://github.com/seaneking/postcss-hexrgba/pull/11) ([schelmo](https://github.com/schelmo)) 15 | - do not omit !important [\#10](https://github.com/seaneking/postcss-hexrgba/pull/10) ([schelmo](https://github.com/schelmo)) 16 | 17 | ## [v1.0.0](https://github.com/seaneking/postcss-hexrgba/tree/v1.0.0) (2017-07-26) 18 | [Full Changelog](https://github.com/seaneking/postcss-hexrgba/compare/v0.2.1...v1.0.0) 19 | 20 | **Closed issues:** 21 | 22 | - Plugin is not working in Postcss ember [\#7](https://github.com/seaneking/postcss-hexrgba/issues/7) 23 | 24 | **Merged pull requests:** 25 | 26 | - Upgrade to postcss@6 and dev deps [\#8](https://github.com/seaneking/postcss-hexrgba/pull/8) ([perrin4869](https://github.com/perrin4869)) 27 | 28 | ## [v0.2.1](https://github.com/seaneking/postcss-hexrgba/tree/v0.2.1) (2016-09-19) 29 | [Full Changelog](https://github.com/seaneking/postcss-hexrgba/compare/v0.2.0...v0.2.1) 30 | 31 | **Merged pull requests:** 32 | 33 | - BUGFIX: hexrgba breaks on undefined values [\#5](https://github.com/seaneking/postcss-hexrgba/pull/5) ([dimaip](https://github.com/dimaip)) 34 | 35 | ## [v0.2.0](https://github.com/seaneking/postcss-hexrgba/tree/v0.2.0) (2015-09-06) 36 | [Full Changelog](https://github.com/seaneking/postcss-hexrgba/compare/v0.1.2...v0.2.0) 37 | 38 | **Closed issues:** 39 | 40 | - Move to PostCSS 5.0 [\#4](https://github.com/seaneking/postcss-hexrgba/issues/4) 41 | 42 | ## [v0.1.2](https://github.com/seaneking/postcss-hexrgba/tree/v0.1.2) (2015-07-25) 43 | [Full Changelog](https://github.com/seaneking/postcss-hexrgba/compare/v0.1.1...v0.1.2) 44 | 45 | **Closed issues:** 46 | 47 | - Errors out if declaration value is complex [\#3](https://github.com/seaneking/postcss-hexrgba/issues/3) 48 | 49 | **Merged pull requests:** 50 | 51 | - Add declaration with multiple attributes including rgba to be converted correctly [\#2](https://github.com/seaneking/postcss-hexrgba/pull/2) ([simonprev](https://github.com/simonprev)) 52 | 53 | ## [v0.1.1](https://github.com/seaneking/postcss-hexrgba/tree/v0.1.1) (2015-07-23) 54 | [Full Changelog](https://github.com/seaneking/postcss-hexrgba/compare/v0.1.0...v0.1.1) 55 | 56 | ## [v0.1.0](https://github.com/seaneking/postcss-hexrgba/tree/v0.1.0) (2015-06-17) 57 | 58 | 59 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* --------------------------------------------------------------------------------