├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | 3 | node_modules/ 4 | 5 | test/ 6 | .travis.yml 7 | 8 | gulpfile.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - iojs 5 | - "0.12" 6 | - "0.10" 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.0.0 - 2015-09-19 2 | * Update PostCSS dependency 3 | * Add legacy option 4 | 5 | # 1.0.2 - 2015-07-24 6 | * Fix for zero-value options 7 | 8 | # 1.0.1 - 2015-06-02 9 | * Add source map support 10 | * Update package.json with postcss-plugin tag 11 | 12 | # 1.0.0 - 2015-06-02 13 | * Initial release 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2015 Andy Jansson 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # postcss-grid [![Build Status][ci-img]][ci] 2 | 3 | A semantic grid system for [PostCSS] 4 | 5 | [PostCSS]: https://github.com/postcss/postcss 6 | [ci-img]: https://travis-ci.org/andyjansson/postcss-grid.svg 7 | [ci]: https://travis-ci.org/andyjansson/postcss-grid 8 | 9 | ## Installation 10 | 11 | ```js 12 | npm install postcss-grid 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```js 18 | var fs = require('fs'); 19 | var postcss = require('postcss'); 20 | var grid = require('postcss-grid'); 21 | 22 | var css = fs.readFileSync('input.css', 'utf8'); 23 | 24 | var options = { 25 | columns: 12, // the number of columns in the grid 26 | maxWidth: 960, // the maximum width of the grid (in px) 27 | gutter: 20, // the width of the gutter (in px) 28 | legacy: false // fixes the double-margin bug in older browsers. Defaults to false 29 | }; 30 | 31 | var output = postcss() 32 | .use(grid(options)) 33 | .process(css) 34 | .css; 35 | ``` 36 | 37 | ### Columns 38 | 39 | Columns are created by using the `grid-column` declaration and passing a `/`-delimited value. This value contains the number of columns the element should span, separated by the total number of columns in the element's container. 40 | 41 | 42 | **Example**: 43 | 44 | ```css 45 | .element { 46 | grid-column: 1/12; 47 | } 48 | ``` 49 | 50 | Turns into: 51 | 52 | ```css 53 | .element{ 54 | float: left; 55 | width: 6.42361%; 56 | margin-right: 2.08333%; 57 | } 58 | ``` 59 | 60 | You can also use it in conjunction with the `!last` declaration to make sure that the last element of the row doesn't allocate a gutter, pushing itself to the next row. 61 | 62 | **Example**: 63 | 64 | ```css 65 | .element { 66 | grid-column: 1/2 !last; 67 | } 68 | ``` 69 | 70 | Turns into: 71 | 72 | ```css 73 | .element{ 74 | float: left; 75 | width: 6.42361%; 76 | } 77 | ``` 78 | 79 | ### Offsetting elements 80 | 81 | Elements can be offset to the left and the right by using `grid-pull` and `grid-push`. 82 | 83 | **Example**: 84 | 85 | ```css 86 | .push { 87 | grid-push: 1/12; 88 | } 89 | .pull { 90 | grid-pull: 1/12; 91 | } 92 | ``` 93 | 94 | Turns into: 95 | 96 | ```css 97 | .push { 98 | margin-left: 8.50694%; 99 | } 100 | .pull { 101 | margin-right: 8.50694%; 102 | } 103 | ``` 104 | 105 | ### Width and gutter values 106 | 107 | The width and gutter values can be retrieved by calling `grid-width(...)` and `grid-gutter(...)` from a declaration. 108 | 109 | **Example**: 110 | 111 | ```css 112 | .element { 113 | width: grid-width(1/12); 114 | margin-left: grid-gutter(12); 115 | } 116 | ``` 117 | 118 | Turns into: 119 | 120 | ```css 121 | .element { 122 | width: 6.42361%; 123 | margin-left: 2.08333%; 124 | } 125 | ``` 126 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var postcss = require('postcss'), 2 | functionCall = require('reduce-function-call'), 3 | extend = require('util')._extend; 4 | 5 | module.exports = postcss.plugin('postcss-grid', function (opts) { 6 | opts = extend({ 7 | columns: 12, 8 | maxWidth: 960, 9 | gutter: 20, 10 | legacy: false 11 | }, opts); 12 | 13 | var columnWidth = (opts.maxWidth - ((opts.columns -1 ) * opts.gutter)) / opts.columns; 14 | 15 | var gridWidth = function (span, cols) { 16 | var width = span * columnWidth + (span -1 ) * opts.gutter; 17 | var container = cols * columnWidth + (cols -1) * opts.gutter; 18 | return ((width / container) * 100).toFixed(5) * 1; 19 | }; 20 | 21 | var gutterWidth = function (cols) { 22 | var width = cols * columnWidth + (cols - 1) * opts.gutter; 23 | return ((opts.gutter / width) * 100).toFixed(5) * 1; 24 | }; 25 | 26 | var value = /\s*(\d+)\s*\/\s*(\d+)\s*/; 27 | var isLast = /\s*!last\s*$/; 28 | 29 | return function (css) { 30 | css.walkDecls(function (decl) { 31 | if (decl.value.indexOf('grid-width(') !== -1) { 32 | decl.value = functionCall(decl.value, "grid-width", function (body) { 33 | var match; 34 | 35 | if (match = value.exec(body)) { 36 | var span = match[1]; 37 | var columns = match[2]; 38 | return gridWidth(span, columns) + '%' 39 | } 40 | else { 41 | throw decl.error('Invalid declaration', { plugin: 'postcss-grid' }); 42 | } 43 | }); 44 | } 45 | if (decl.value.indexOf('grid-gutter(') !== -1) { 46 | decl.value = functionCall(decl.value, "grid-gutter", function (body) { 47 | return gutterWidth(body) + '%' 48 | }); 49 | } 50 | 51 | if (decl.prop === 'grid-column') { 52 | var match; 53 | 54 | if (match = value.exec(decl.value)) { 55 | var span = match[1]; 56 | var columns = match[2]; 57 | 58 | decl.parent.append({prop: 'float', value: 'left'}).source = decl.source; 59 | decl.parent.append({prop: 'width', value: gridWidth(span, columns) + '%'}).source = decl.source; 60 | 61 | if (!(decl.value.match(isLast))) { 62 | if (opts.legacy) decl.parent.append({prop: 'display', value: 'inline'}).source = decl.source; 63 | decl.parent.append({prop: 'margin-right', value: gutterWidth(columns) + '%'}).source = decl.source; 64 | } 65 | decl.remove(); 66 | } 67 | else { 68 | throw decl.error('Invalid declaration', { plugin: 'postcss-grid' }); 69 | } 70 | } 71 | if (decl.prop === 'grid-push' || decl.prop === 'grid-pull') { 72 | var match; 73 | 74 | if (match = value.exec(decl.value)) { 75 | var span = match[1]; 76 | var columns = match[2]; 77 | var width = span * gridWidth(1, columns) + span * gutterWidth(columns); 78 | decl.parent.append({ 79 | prop: decl.prop === 'grid-push' ? 'margin-left' : 'margin-right', 80 | value: width + '%' 81 | }).source = decl.source; 82 | decl.remove(); 83 | } 84 | else { 85 | throw decl.error('Invalid declaration', { plugin: 'postcss-grid' }); 86 | } 87 | } 88 | }); 89 | }; 90 | }); 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-grid", 3 | "version": "2.0.0", 4 | "description": "A semantic grid system for PostCSS", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "node test/test.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/andyjansson/postcss-grid.git" 15 | }, 16 | "keywords": [ 17 | "postcss", 18 | "grid", 19 | "css", 20 | "postcss-plugin" 21 | ], 22 | "author": "Andy Jansson", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/andyjansson/postcss-grid/issues" 26 | }, 27 | "homepage": "https://github.com/andyjansson/postcss-grid#readme", 28 | "devDependencies": {}, 29 | "dependencies": { 30 | "postcss": "^5.0.5", 31 | "reduce-function-call": "^1.0.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var postcss = require('postcss'), 2 | grid = require('../'), 3 | assert = require('assert'), 4 | extend = require('util')._extend; 5 | 6 | var settings = { 7 | columns: 12, 8 | maxWidth: 960, 9 | gutter: 20 10 | }; 11 | 12 | var test = function (opts, input, output) { 13 | assert.equal(postcss(grid(opts)).process(input).css, output); 14 | }; 15 | 16 | test(settings, '.element{grid-column: 1/12;}', '.element{float: left;width: 6.42361%;margin-right: 2.08333%;}'); 17 | test(extend({ legacy: true }, settings), '.element{grid-column: 1/12;}', '.element{float: left;width: 6.42361%;display: inline;margin-right: 2.08333%;}'); 18 | test(settings, '.element{grid-column: 1/12 !last;}', '.element{float: left;width: 6.42361%;}'); 19 | test(settings, '.element{grid-column: 6/12;}', '.element{float: left;width: 48.95833%;margin-right: 2.08333%;}'); 20 | test(settings, '.element{grid-column: 3/6;}', '.element{float: left;width: 47.87234%;margin-right: 4.25532%;}'); 21 | test(settings, '.element{grid-column: 12/12 !last;}', '.element{float: left;width: 100%;}'); 22 | test(settings, '.element{grid-push: 1/12;}', '.element{margin-left: 8.50694%;}'); 23 | test(settings, '.element{grid-pull: 1/12;}', '.element{margin-right: 8.50694%;}'); 24 | test(settings, '.element{width: grid-width(1/12);}', '.element{width: 6.42361%;}'); 25 | test(settings, '.element{margin-left: grid-gutter(12);}', '.element{margin-left: 2.08333%;}'); 26 | --------------------------------------------------------------------------------