├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── ChangeLog.md ├── LICENSE ├── README.md ├── gulpfile.js ├── index.js ├── package.json └── test ├── fixtures ├── nonsense-values.css ├── nonsense-values.expected.css ├── nonsense.css ├── nonsense.expected.css ├── null.css ├── null.expected.css ├── scale.css ├── scale.expected.css ├── shorthand.css └── shorthand.expected.css └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{json,yml}] 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "space-before-function-paren": [2, { "named": "never" }], 4 | "no-shadow-restricted-names": [2], 5 | "computed-property-spacing": [2], 6 | "no-empty-character-class": [2], 7 | "no-irregular-whitespace": [2], 8 | "no-unexpected-multiline": [2], 9 | "no-multiple-empty-lines": [2], 10 | "space-return-throw-case": [2], 11 | "no-constant-condition": [2], 12 | "no-extra-boolean-cast": [2], 13 | "no-inner-declarations": [2], 14 | "no-this-before-super": [2], 15 | "no-use-before-define": [2], 16 | "no-array-constructor": [2], 17 | "object-curly-spacing": [2, "always"], 18 | "no-floating-decimal": [2], 19 | "no-warning-comments": [2], 20 | "handle-callback-err": [2], 21 | "no-unneeded-ternary": [2], 22 | "operator-assignment": [2], 23 | "space-before-blocks": [2], 24 | "no-native-reassign": [2], 25 | "no-trailing-spaces": [2], 26 | "operator-linebreak": [2, "after"], 27 | "consistent-return": [2], 28 | "no-duplicate-case": [2], 29 | "no-invalid-regexp": [2], 30 | "no-negated-in-lhs": [2], 31 | "constructor-super": [2], 32 | "no-nested-ternary": [2], 33 | "no-extend-native": [2], 34 | "block-scoped-var": [2], 35 | "no-control-regex": [2], 36 | "no-sparse-arrays": [2], 37 | "no-throw-literal": [2], 38 | "no-return-assign": [2], 39 | "no-const-assign": [2], 40 | "no-class-assign": [2], 41 | "no-extra-parens": [2], 42 | "no-regex-spaces": [2], 43 | "no-implied-eval": [2], 44 | "no-useless-call": [2], 45 | "no-self-compare": [2], 46 | "no-octal-escape": [2], 47 | "no-new-wrappers": [2], 48 | "no-process-exit": [2], 49 | "no-catch-shadow": [2], 50 | "linebreak-style": [2], 51 | "space-infix-ops": [2], 52 | "space-unary-ops": [2], 53 | "no-cond-assign": [2], 54 | "no-func-assign": [2], 55 | "no-unreachable": [2], 56 | "accessor-pairs": [2], 57 | "no-empty-label": [2], 58 | "no-fallthrough": [2], 59 | "no-path-concat": [2], 60 | "no-new-require": [2], 61 | "no-spaced-func": [2], 62 | "no-unused-vars": [2], 63 | "spaced-comment": [2], 64 | "no-delete-var": [2], 65 | "comma-spacing": [2], 66 | "no-extra-semi": [2], 67 | "no-extra-bind": [2], 68 | "arrow-spacing": [2], 69 | "prefer-spread": [2], 70 | "no-new-object": [2], 71 | "no-multi-str": [2], 72 | "semi-spacing": [2], 73 | "no-lonely-if": [2], 74 | "dot-notation": [2], 75 | "dot-location": [2, "property"], 76 | "comma-dangle": [2, "never"], 77 | "no-dupe-args": [2], 78 | "no-dupe-keys": [2], 79 | "no-ex-assign": [2], 80 | "no-obj-calls": [2], 81 | "valid-typeof": [2], 82 | "default-case": [2], 83 | "no-redeclare": [2], 84 | "no-div-regex": [2], 85 | "no-sequences": [2], 86 | "no-label-var": [2], 87 | "comma-style": [2], 88 | "brace-style": [2], 89 | "no-debugger": [2], 90 | "quote-props": [2, "consistent-as-needed"], 91 | "no-iterator": [2], 92 | "no-new-func": [2], 93 | "key-spacing": [2, { "align": "value" }], 94 | "complexity": [2], 95 | "new-parens": [2], 96 | "no-eq-null": [2], 97 | "no-bitwise": [2], 98 | "wrap-iife": [2], 99 | "no-caller": [2], 100 | "use-isnan": [2], 101 | "no-labels": [2], 102 | "no-shadow": [2], 103 | "camelcase": [2], 104 | "eol-last": [2], 105 | "no-octal": [2], 106 | "no-empty": [2], 107 | "no-alert": [2], 108 | "no-proto": [2], 109 | "no-undef": [2], 110 | "no-eval": [2], 111 | "no-with": [2], 112 | "no-void": [2], 113 | "max-len": [2, 80], 114 | "new-cap": [2], 115 | "eqeqeq": [2], 116 | "no-new": [2], 117 | "quotes": [2, "single"], 118 | "indent": [2, 4], 119 | "semi": [2, "always"], 120 | "yoda": [2, "never"] 121 | }, 122 | "env": { 123 | "mocha": true, 124 | "node": true 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | 3 | node_modules/ 4 | npm-debug.log 5 | 6 | test/ 7 | .travis.yml 8 | 9 | gulpfile.js 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - stable 5 | - "0.12" 6 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoferjoseph/postcss-modular-scale/5a50d20763f220a3bcd548389d02df89731a4767/ChangeLog.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2015 Kristofer Joseph 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 Modular Scale 2 | ===================== 3 | 4 | A plugin to provide a modular scale function in your styles. 5 | 6 | Install 7 | ------- 8 | 9 | `npm install postcss-modular-scale` 10 | 11 | Usage 12 | ----- 13 | 14 | > To generate relative values use target / base font size 15 | 16 | Example: 17 | 18 | 1 = 16px ( target ) / 16px ( base font size ) 19 | 0.75 = 12px ( target ) / 16px ( base font size ) 20 | Generates `em` or `rem` relative values 21 | 22 | ### Input 23 | 24 | ```css 25 | :root { 26 | --ms-bases: 1, 0.75; 27 | --ms-ratios: 2; 28 | } 29 | 30 | .header { 31 | font-size: ms(4)rem; 32 | } 33 | 34 | ``` 35 | 36 | ### Output 37 | 38 | ```css 39 | :root { 40 | --ms-bases: 1, 0.75; 41 | --ms-ratios: 2; 42 | } 43 | 44 | .header { 45 | font-size: 3rem; 46 | } 47 | ``` 48 | Mad props 49 | -------- 50 | 51 | Tons of thanks to Scott Kellum @scottkellum & Tim Brown @nicewebtype for making [Modular Scale](http://www.modularscale.com) and the libraries that make this plugin possible. 52 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | var files = ['index.js', 'test/*.js', 'gulpfile.js']; 4 | 5 | gulp.task('test', function () { 6 | var mocha = require('gulp-mocha'); 7 | return gulp.src('test/*.js', { read: false }) 8 | .pipe(mocha()); 9 | }); 10 | 11 | gulp.task('default', ['test']); 12 | 13 | gulp.task('watch', function () { 14 | gulp.watch(files, ['test']); 15 | }); 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var postcss = require('postcss'); 2 | var ModularScale = require('modular-scale'); 3 | var valueParser = require('postcss-value-parser'); 4 | 5 | var pluginName = 'postcss-modular-scale'; 6 | 7 | module.exports = postcss.plugin(pluginName, function(opts) { 8 | opts = opts || {}; 9 | var ratios = opts.ratios; 10 | var bases = opts.bases; 11 | 12 | return function(css, result) { 13 | var declarations = []; 14 | var ms = null; 15 | 16 | css.walkDecls(function(decl) { 17 | 18 | if (!decl.value) { 19 | return; 20 | } 21 | 22 | if (decl.parent.selector === ':root') { 23 | 24 | if (decl.prop === '--ms-ratios') { 25 | ratios = decl.value.split(','); 26 | result.messages.push({ 27 | type: 'modular-scale-ratios', 28 | plugin: pluginName, 29 | text: 'Modular scale ratios: ' + ratios 30 | }); 31 | } 32 | 33 | if (decl.prop === '--ms-bases') { 34 | bases = decl.value.split(','); 35 | result.messages.push({ 36 | type: 'modular-scale-bases', 37 | plugin: pluginName, 38 | text: 'Modular scale bases: ' + bases 39 | }); 40 | } 41 | } 42 | 43 | if (decl.value.indexOf('ms(') !== -1) { 44 | declarations.push(decl); 45 | } 46 | 47 | }); 48 | 49 | ms = new ModularScale({ 50 | ratios: ratios, 51 | bases: bases 52 | }); 53 | 54 | declarations.forEach(function(decl) { 55 | var parsedValue = valueParser(decl.value); 56 | 57 | parsedValue.walk(function (node) { 58 | if (node.type === 'function' && node.value === 'ms') { 59 | if (node.nodes.length === 1 && node.nodes[0].type === 'word') { 60 | var value = parseFloat(node.nodes[0].value); 61 | 62 | if (!isNaN(value)) { 63 | var newValue = ms(value); 64 | 65 | node.type = 'word'; 66 | node.value = newValue; 67 | 68 | result.messages.push({ 69 | type: 'modular-scale-result', 70 | plugin: pluginName, 71 | text: 'Modular scale for ' + decl.value + ' is ' + newValue 72 | }); 73 | } else { 74 | throw decl.error('Modular scale value should be a number', { plugin: pluginName }); 75 | } 76 | } else { 77 | throw decl.error('Modular scale value should be a number', { plugin: pluginName }); 78 | } 79 | } 80 | }); 81 | 82 | decl.value = parsedValue.toString(); 83 | }); 84 | }; 85 | }); 86 | 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-modular-scale", 3 | "version": "2.5.2", 4 | "description": "PostCSS plugin for incorporating modular scale units in your styles.", 5 | "keywords": [ 6 | "postcss", 7 | "css", 8 | "postcss-plugin", 9 | "modular scale", 10 | "modular", 11 | "scale" 12 | ], 13 | "author": "Kristofer Joseph ", 14 | "license": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/kristoferjoseph/postcss-modular-scale.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/kristoferjoseph/postcss-modular-scale/issues" 21 | }, 22 | "homepage": "https://github.com/kristoferjoseph/postcss-modular-scale", 23 | "dependencies": { 24 | "modular-scale": "^4.4.1", 25 | "postcss": "^5.0.2", 26 | "postcss-value-parser": "^3.3.0" 27 | }, 28 | "devDependencies": { 29 | "chai": "^3.2.0", 30 | "gulp": "^3.9.0", 31 | "gulp-eslint": "^1.0.0", 32 | "gulp-mocha": "^2.1.3" 33 | }, 34 | "scripts": { 35 | "test": "gulp" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/fixtures/nonsense-values.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ms-bases: 1, 0.75; 3 | --ms-ratios: 2; 4 | } 5 | 6 | .header { 7 | font-size: ms(rem)rem; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/nonsense-values.expected.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristoferjoseph/postcss-modular-scale/5a50d20763f220a3bcd548389d02df89731a4767/test/fixtures/nonsense-values.expected.css -------------------------------------------------------------------------------- /test/fixtures/nonsense.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ms-bases: 'chewbacca', 'happiness'; 3 | --ms-ratios: 'octopus'; 4 | } 5 | 6 | .header { 7 | padding: ms(4)rem; 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/nonsense.expected.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ms-bases: 'chewbacca', 'happiness'; 3 | --ms-ratios: 'octopus'; 4 | } 5 | 6 | .header { 7 | padding: 6.854rem; 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/null.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --not-ms: #f00; 3 | } 4 | 5 | .header { 6 | padding: ms(4)rem; 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/null.expected.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --not-ms: #f00; 3 | } 4 | 5 | .header { 6 | padding: 6.854rem; 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/scale.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ms-bases: 1, 0.75; 3 | --ms-ratios: 2; 4 | } 5 | 6 | .header { 7 | font-size: ms(4)rem; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/scale.expected.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ms-bases: 1, 0.75; 3 | --ms-ratios: 2; 4 | } 5 | 6 | .header { 7 | font-size: 3rem; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/shorthand.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ms-bases: 1; 3 | --ms-ratios: 2; 4 | } 5 | 6 | .header { 7 | padding: ms(1)rem ms(2)rem ms(3)rem ms(4)rem; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/shorthand.expected.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ms-bases: 1; 3 | --ms-ratios: 2; 4 | } 5 | 6 | .header { 7 | padding: 2rem 4rem 8rem 16rem; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var postcss = require('postcss'); 3 | var expect = require('chai').expect; 4 | 5 | var plugin = require('../'); 6 | 7 | var test = function (input, output, opts, done) { 8 | postcss([ plugin(opts) ]).process(input).then(function (result) { 9 | expect(result.css).to.eql(output); 10 | expect(result.warnings()).to.be.empty; 11 | done(); 12 | }).catch(function (error) { 13 | done(error); 14 | }); 15 | }; 16 | 17 | var failTest = function (input, output, opts, done) { 18 | postcss([ plugin(opts) ]).process(input).then(function (result) { 19 | done(new Error('Parsing should fail.')); 20 | }).catch(function (error) { 21 | done(); 22 | }); 23 | }; 24 | 25 | describe('postcss-modular-scale', function () { 26 | 27 | it('should modular scale', function (done) { 28 | var input = fs.readFileSync( 29 | 'test/fixtures/scale.css', 30 | 'utf8' 31 | ); 32 | var output = fs.readFileSync( 33 | 'test/fixtures/scale.expected.css', 34 | 'utf8' 35 | ); 36 | test(input, output, {}, done); 37 | }); 38 | 39 | it('should not bomb on nothing', function (done) { 40 | var input = fs.readFileSync( 41 | 'test/fixtures/null.css', 42 | 'utf8' 43 | ); 44 | var output = fs.readFileSync( 45 | 'test/fixtures/null.expected.css', 46 | 'utf8' 47 | ); 48 | test(input, output, {}, done); 49 | }); 50 | 51 | it('should not bomb on nonsense', function (done) { 52 | var input = fs.readFileSync( 53 | 'test/fixtures/nonsense.css', 54 | 'utf8' 55 | ); 56 | var output = fs.readFileSync( 57 | 'test/fixtures/nonsense.expected.css', 58 | 'utf8' 59 | ); 60 | test(input, output, {}, done); 61 | }); 62 | 63 | it('should handle shorthand properties', function (done) { 64 | var input = fs.readFileSync( 65 | 'test/fixtures/shorthand.css', 66 | 'utf8' 67 | ); 68 | var output = fs.readFileSync( 69 | 'test/fixtures/shorthand.expected.css', 70 | 'utf8' 71 | ); 72 | test(input, output, {}, done); 73 | }); 74 | 75 | it('should report an error on incorrect values', function (done) { 76 | var input = fs.readFileSync( 77 | 'test/fixtures/nonsense-values.css', 78 | 'utf8' 79 | ); 80 | var output = fs.readFileSync( 81 | 'test/fixtures/nonsense-values.expected.css', 82 | 'utf8' 83 | ); 84 | failTest(input, output, {}, done); 85 | }); 86 | }); 87 | --------------------------------------------------------------------------------