j2c
A small JavaScript object to CSS compiler. ~820 bytes mingzipped.
Think SASS, but in JSONish syntax.
Inspired by restlye.js and JSS, but smaller :-).
Table of Contents
TOC generated with DocToc
Why?
- Send small, compact, SASS-like data down the line
- Simplify your asset pipeline
- Use the full power of JavaScript to create mixins, variables and macros
- Stop worrying about vendor prefixes
- Good fit for virtual DOM frameworks like React or Mithril
- I like writing compilers :-).
Usage
For building a style sheet
j2c.vendors = []
r = j2c.sheet("ul.my_root_class")
r.add({
"@media condition": {
color: "red"
},
font: {
size: "2em",
family: "sans-serif"
},
background_color: "#44f",
" li": {
padding:{
left: "5px"
top: "10px"
}
}
})
console.log(r.toString())
Output:
@media condition{
ul.my_root_class {
color:red;
}
}
ul.my_root_class li{
padding-left:5px;
padding-top:10px;
}
ul.my_root_class {
font-size:2em;
font-family:sans-serif;
background-color:#44f;
}
-vendor-prefixes
If you don't truncate the vendors list as I did in the example above, you'll get each property prefixed for each vendor.
Most of the resulting combinations don't make any sense (-moz-color
FTW), and they are simply ignored by browsers. That's the price to pay for the small code size.
root selector
If no root selector is provided, J2C
creates one (a unique class).
r = j2c.sheet()
r.prefix
Telling selectors and properties apart
j2c
considers that object keys matching /^[-_0-9A-Za-z]+$/
as properties, and everything else as (sub-)selectors.
Selectors are concatenated as is, while properties are concatenated with hyphens. {" ul": {" li": {padding: {left:10}}}}
becomes ul li{padding-left:10px;}
. {" p":{".foo":{color:"red"}}}
, is translated to p.foo:{color:red;}
.
Overloading properties
r = j2c.sheet("ul.my_root_class")
r.add({
"font-size": ["2em", "2rem"]
})
console.log(r.toString())
becomes
.foo {
font-size:2em;
font-size:2rem;
}
Alternatively
r = j2c.sheet("ul.my_root_class")
r.add([
{
"font-size": "2em"
},
{
"font-size": "2rem"
}
])
console.log(r.toString())
becomes
ul.my_root_class {
font-size:2em;
}
ul.my_root_class {
font-size:2rem;
}
At-rules
Most At-rules are handled out of the box by sheet.add()
. However, @font-face
and @keyframes
have are not covered and they are implemented respectively by sheet.font(definitions)
and sheet.keyframes(name, definitions)
. The latter automatically generates browser-specific @-vendor-keyframes
blocks.
Combining multiple selectors
Here's a excerpt from the j2c
port of the PocketGrid.
j2c.sheet("").add({
".block,.blockgroup":{
",:before,:after":{
"box-sizing":"border-box"
}
}
}
Nesting ",:before,:after"
inside the ".block,.blockgroup"
block combines [".block", ".blockgroup"]
with ["", ":before", ":after"]
, giving
.block,.block:before,.block:after,.blockgroup,.blockgroup:before,.blockgroup:after{
box-sizing:border-box
}
Mathy folks call this as a Cartesian product.
CSS Hacks
Since sheet.add
only accepts property names that match /^[-_0-9A-Za-z]+$/
, it is not possible to express CSS hacks using objects. You can, however, work around the issue by using arrays and strings instead.
Here's another modified excerpt from the PocketGrid port:
j2c.sheet("").add({
".blockgroup": [
"*zoom: 1; /* hack */",
{
"list-style-type":"none",
padding:0,
margin:0
}
]
})
Array elements are inserted in sequence, and string literals are treated as a list of properties, and inserted as is.
Result:
.blockgroup{
*zoom: 1;
}
.blockgroup{
list-style-type:none;
padding:0;
margin:0;
}
For building inline styles
console.log(j2c.inline({
float:"left";
}));
... outputs ...
-o-float:left;
-ms-float:left;
-moz-float:left;
-webkit-float:left;
float:left;
API Reference
j2c
object
j2c.inline(props:(Object|Array|String)) : String
: returns a declaration list suitable for inline stylesj2c.sheet([root:String]) : Sheet
: Creates a Sheet object.j2c.vendors = ["o", "ms", "moz", "webkit"]
(r/w): list of vendor prefixes.
Sheet
methods
sheet.add(statements:(Object|Array|String)) : Sheet
: add a series of statements to the style sheet. Returns the Sheet
for chaining.sheet.font(definitions:(Object|Array|String)) : Sheet
: creates a @font-face
block. Returns the Sheet
for chaining.sheet.keyframes(name:String, statements:(Object|Array|String)) : Sheet
: creates a @keyframes
block. Returns the Sheet
for chaining.sheet.toString() : String
: the stylesheet in string form.
Limitations
Selectors and properties order
j2c
relies on JS objects to define selectors and properties. As a consequence, the source order cannot be guaranteed to be respected in the output.
j2c(".hello").add({
foo:"bar",
baz:"qux"
}).toString()
This may produce either .hello{foo:bar;baz:qux;}
or .hello{baz:qux;foo:bar;}
.
If you need some elements to happen in order, use an array of objects.
j2c(".hello").add([
{foo:"bar"},
{baz:"qux"}
]).toString()
This will always yield .hello{foo:bar;}.hello{baz:qux;}
.
No input validation
j2c
knows the bare minimum to output a valid stylesheet when provided with valid input. It will hapily accept invalid selectors, properties and values, and could in that case produce a broken stylesheet.
I may get around and write a validator companion, but I'm not there yet :-).
License: MIT