├── LICENSE ├── README.md ├── lib └── ccss.js ├── package.json └── src └── ccss.coffee /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 James Campos 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CoffeeScript CSS 2 | 3 | despite the name, CoffeeScript is not required; it just makes writing objects 4 | (as well as everything else) easier. You could create a JSON string in another 5 | language, parse it in node, and render it with ccss. 6 | 7 | install: `npm install ccss` 8 | 9 | main.coffee: 10 | 11 | ```coffee 12 | ccss = require 'ccss' 13 | 14 | template = require './template.coffee' 15 | css = ccss.compile template 16 | require('fs').writeFileSync 'main.css', css 17 | 18 | #or all at once: ccss.compileFile './template.coffee', 'main.css' 19 | ``` 20 | 21 | template.coffee: 22 | 23 | ```coffee 24 | borderRadius = (str) -> 25 | WebkitBorderRadius: str 26 | MozBorderRadius: str 27 | borderRadius: str 28 | 29 | boxShadow = (str) -> 30 | WebkitBoxShadow: str 31 | MozBoxShadow: str 32 | boxShadow: str 33 | 34 | module.exports = 35 | form: 36 | input: 37 | padding: '5px' 38 | border: '1px solid' 39 | mixins: borderRadius '5px' 40 | '#id .className': do -> 41 | opaque = 1 42 | translucent = opaque / 2 43 | img: 44 | mixins: [ 45 | borderRadius '5px' 46 | boxShadow '5px' 47 | ] 48 | opacity: translucent 49 | 'img:hover': 50 | opacity: opaque 51 | ``` 52 | 53 | main.css: 54 | 55 | ```css 56 | form input { 57 | padding: 5px; 58 | border: 1px solid; 59 | -webkit-border-radius: 5px; 60 | -moz-border-radius: 5px; 61 | border-radius: 5px; 62 | } 63 | #id .className img { 64 | opacity: 0.5; 65 | -webkit-border-radius: 5px; 66 | -moz-border-radius: 5px; 67 | border-radius: 5px; 68 | -webkit-box-shadow: 5px; 69 | -moz-box-shadow: 5px; 70 | box-shadow: 5px; 71 | } 72 | #id .className img:hover { 73 | opacity: 1; 74 | } 75 | ``` 76 | 77 | the core of the compiler is simply this: 78 | 79 | iterate over the key / values of an object; if the value is another object, 80 | append the key to the current selector, and recurse; else generate css. 81 | 82 | to reduce the amount of quoting, if a css property has a capital letter C, 83 | it will be transformed into -c; selectors are not touched. 84 | 85 | # Related 86 | 87 | [CoffeeKup](http://coffeekup.org/) - CoffeeScript to HTML 88 | 89 | [ckup](http://satyr.github.com/ckup/) - [Coco](http://satyr.github.com/coco/) to both HTML and CSS 90 | 91 | [CoffeeStylesheets](https://github.com/mikesmullin/coffee-stylesheets) - Another CoffeeScript to CSS approach 92 | -------------------------------------------------------------------------------- /lib/ccss.js: -------------------------------------------------------------------------------- 1 | var extend, fs; 2 | fs = require('fs'); 3 | extend = function(object, properties) { 4 | var key, value; 5 | for (key in properties) { 6 | value = properties[key]; 7 | object[key] = value; 8 | } 9 | return object; 10 | }; 11 | this.compile = function(rules) { 12 | var child, children, css, declarations, key, mixin, mixins, nested, pairs, selector, split, value, _i, _j, _len, _len2, _ref; 13 | css = ''; 14 | for (selector in rules) { 15 | pairs = rules[selector]; 16 | declarations = ''; 17 | nested = {}; 18 | if (mixins = pairs.mixins, pairs) { 19 | delete pairs.mixins; 20 | _ref = [].concat(mixins); 21 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 22 | mixin = _ref[_i]; 23 | extend(pairs, mixin); 24 | } 25 | } 26 | for (key in pairs) { 27 | value = pairs[key]; 28 | if (typeof value === 'object') { 29 | children = []; 30 | split = key.split(/\s*,\s*/); 31 | for (_j = 0, _len2 = split.length; _j < _len2; _j++) { 32 | child = split[_j]; 33 | children.push("" + selector + " " + child); 34 | } 35 | nested[children.join(',')] = value; 36 | } else { 37 | key = key.replace(/[A-Z]/g, function(s) { 38 | return '-' + s.toLowerCase(); 39 | }); 40 | declarations += " " + key + ": " + value + ";\n"; 41 | } 42 | } 43 | declarations && (css += "" + selector + " {\n" + declarations + "}\n"); 44 | css += this.compile(nested); 45 | } 46 | return css; 47 | }; 48 | this.compileFile = function(infile, outfile) { 49 | var css, rules; 50 | rules = require(process.cwd() + '/' + infile); 51 | css = this.compile(rules); 52 | outfile || (outfile = infile.replace(/coffee$/, 'css')); 53 | return fs.writeFileSync(outfile, css); 54 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ccss", 3 | "description": "CoffeeScript CSS", 4 | "version": "0.1.2-1", 5 | "keywords": ["css"], 6 | "licenses": [{ 7 | "type": "MIT", 8 | "url": "https://github.com/aeosynth/ccss/raw/master/LICENSE" 9 | }], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/aeosynth/ccss.git" 13 | }, 14 | "author": "James Campos ", 15 | "main": "lib/ccss", 16 | "engines": { 17 | "node": "*" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ccss.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | 3 | extend = (object, properties) -> 4 | for key, value of properties 5 | object[key] = value 6 | object 7 | 8 | @compile = (rules) -> 9 | css = '' 10 | 11 | for selector, pairs of rules 12 | declarations = '' 13 | nested = {} 14 | 15 | #add mixins to the current level 16 | if {mixins} = pairs 17 | delete pairs.mixins 18 | for mixin in [].concat mixins 19 | extend pairs, mixin 20 | 21 | #a pair is either a css declaration, or a nested rule 22 | for key, value of pairs 23 | if typeof value is 'object' 24 | children = [] 25 | split = key.split /\s*,\s*/ 26 | children.push "#{selector} #{child}" for child in split 27 | nested[children.join ','] = value 28 | else 29 | #borderRadius -> border-radius 30 | key = key.replace /[A-Z]/g, (s) -> '-' + s.toLowerCase() 31 | declarations += " #{key}: #{value};\n" 32 | 33 | declarations and css += "#{selector} {\n#{declarations}}\n" 34 | 35 | css += @compile nested 36 | 37 | css 38 | 39 | @compileFile = (infile, outfile) -> 40 | rules = require process.cwd() + '/' + infile 41 | css = @compile rules 42 | outfile or= infile.replace /coffee$/, 'css' 43 | fs.writeFileSync outfile, css 44 | --------------------------------------------------------------------------------