├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── index.test.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## May 22, 2017 [v 0.1.0] 2 | 3 | - Repo created. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2016 TheSisb 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 Theme Shorthand [![Build Status][ci-img]][ci] 2 | 3 | [PostCSS] plugin that allows the use of "light-" and "dark-" property prefixes for themeing. 4 | 5 | [PostCSS]: https://github.com/postcss/postcss 6 | [ci-img]: https://travis-ci.org/hammerandchisel/postcss-theme-shorthand.svg 7 | [ci]: https://travis-ci.org/hammerandchisel/postcss-theme-shorthand 8 | 9 | ```css 10 | .thing { 11 | light-color: white; 12 | dark-color: black; 13 | color: red; 14 | light-background: black; 15 | dark-background: white; 16 | } 17 | 18 | .ballon, .garage { 19 | dark-color: red; 20 | light-color: blue; 21 | } 22 | ``` 23 | 24 | ```css 25 | :global(.theme-light) .thing { 26 | color: white; 27 | background: black; 28 | } 29 | 30 | :global(.theme-dark) .thing { 31 | color: black; 32 | background: white; 33 | } 34 | 35 | .thing { 36 | color: red; 37 | } 38 | 39 | :global(.theme-dark) .ballon, :global(.theme-dark) .garage { 40 | color: red; 41 | } 42 | 43 | :global(.theme-light) .ballon, :global(.theme-light) .garage { 44 | color: blue; 45 | } 46 | 47 | .ballon, .garage { 48 | } 49 | ``` 50 | 51 | ## Usage 52 | 53 | ```js 54 | postcss([ require('postcss-theme-shorthand') ]) 55 | ``` 56 | 57 | See [PostCSS] docs for examples for your environment. 58 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const postcss = require('postcss'); 2 | 3 | module.exports = postcss.plugin('postcss-theme-shorthand', () => { 4 | const ThemedPropRegExp = /^(light|dark)-/; 5 | const LightThemeRegExp = /^light-/g; 6 | const DarkThemeRegExp = /^dark-/g; 7 | const LIGHT_THEME = '.theme-light'; 8 | const DARK_THEME = '.theme-dark'; 9 | 10 | // Holds a reference to the new css node for themeing, 11 | // in case multiple light-/dark- props in a single selector 12 | let currentLightNode; 13 | let currentDarkNode; 14 | 15 | function createNewNode(parent, selector) { 16 | return parent.cloneBefore({ selector }).removeAll(); 17 | } 18 | 19 | function addLightThemedStyle({ parent, prop, value, source }) { 20 | const ruleSelectors = parent.selectors 21 | .map(ruleSelector => `:global(${LIGHT_THEME}) ${ruleSelector}`) 22 | .join(', '); 23 | 24 | if (!currentLightNode || currentLightNode.selector !== ruleSelectors) { 25 | currentLightNode = createNewNode(parent, ruleSelectors); 26 | } 27 | 28 | currentLightNode.append({ 29 | prop: prop.replace(LightThemeRegExp, ''), 30 | value, 31 | source 32 | }); 33 | } 34 | 35 | function addDarkThemedStyle({ parent, prop, value, source }) { 36 | const ruleSelectors = parent.selectors 37 | .map(ruleSelector => `:global(${DARK_THEME}) ${ruleSelector}`) 38 | .join(', '); 39 | 40 | if (!currentDarkNode || currentDarkNode.selector !== ruleSelectors) { 41 | currentDarkNode = createNewNode(parent, ruleSelectors); 42 | } 43 | 44 | currentDarkNode.append({ 45 | prop: prop.replace(DarkThemeRegExp, ''), 46 | value, 47 | source 48 | }); 49 | } 50 | 51 | function ruleHandler(decl) { 52 | const { prop } = decl; 53 | 54 | if (prop.match(LightThemeRegExp) !== null) { 55 | addLightThemedStyle(decl); 56 | } else if (prop.match(DarkThemeRegExp) !== null) { 57 | addDarkThemedStyle(decl); 58 | } 59 | 60 | decl.remove(); 61 | } 62 | 63 | return function (css) { 64 | css.walkDecls(ThemedPropRegExp, ruleHandler); 65 | }; 66 | }); 67 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | const postcss = require('postcss'); 2 | const plugin = require('./'); 3 | 4 | function run(input, output, opts) { 5 | return postcss([ plugin(opts) ]).process(input) 6 | .then(result => { 7 | expect(result.css).toEqual(output); 8 | expect(result.warnings().length).toBe(0); 9 | }); 10 | } 11 | 12 | 13 | it('Handles light and dark theme property prefixes', () => { 14 | return run(` 15 | .thing { 16 | margin: 10px; 17 | light-color: $black; 18 | } 19 | `, 20 | ` 21 | :global(.theme-light) .thing { 22 | color: $black; 23 | } 24 | .thing { 25 | margin: 10px; 26 | } 27 | `, {}); 28 | }); 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-theme-shorthand", 3 | "version": "1.0.0", 4 | "description": "PostCSS plugin that allows the use of 'light-' and 'dark-' property prefixes for themeing.", 5 | "keywords": [ 6 | "postcss", 7 | "css", 8 | "postcss-plugin", 9 | "theme" 10 | ], 11 | "author": "TheSisb ", 12 | "license": "MIT", 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/hammerandchisel/postcss-theme-shorthand.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/hammerandchisel/postcss-theme-shorthand/issues" 19 | }, 20 | "homepage": "https://github.com/hammerandchisel/postcss-theme-shorthand", 21 | "dependencies": { 22 | "postcss": "6.0.5" 23 | }, 24 | "devDependencies": { 25 | "eslint": "^3.12.2", 26 | "eslint-config-postcss": "^2.0.2", 27 | "jest": "^18.0.0" 28 | }, 29 | "scripts": { 30 | "test": "jest && eslint *.js" 31 | }, 32 | "eslintConfig": { 33 | "extends": "eslint-config-postcss", 34 | "env": { 35 | "jest": true 36 | } 37 | }, 38 | "main": "index.js" 39 | } 40 | --------------------------------------------------------------------------------