├── .bithoundrc
├── .eslintrc.js
├── .gitignore
├── .travis.yml
├── HISTORY.md
├── README.md
├── dist
├── README.md
├── inline
│ └── README.md
├── j2c.amd.js
├── j2c.commonjs.js
├── j2c.commonjs.min.js
├── j2c.es6.js
├── j2c.global.js
├── j2c.global.min.js
└── j2c.global.min.js.gz
├── example
├── pocketgrid.js
└── pocketgrid.mixin.js
├── package.json
├── scripts
├── build.js
├── filesize.js
└── regexps.js
├── src
├── at-rules.js
├── declarations.js
├── helpers.js
├── main.js
└── sheet.js
└── test
└── test.js
/.bithoundrc:
--------------------------------------------------------------------------------
1 | {
2 | "ignore": [
3 | "dist/*.js",
4 | "example/*.js"
5 | ]
6 | }
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | indent: [
4 | 2, 2
5 | ],
6 | quotes: [
7 | 2,
8 | "single",
9 | "avoid-escape"
10 | ],
11 | "linebreak-style": [
12 | 2,
13 | "unix"
14 | ],
15 | semi: [
16 | 2,
17 | "never"
18 | ],
19 | "no-trailing-spaces": [2]
20 | },
21 | parserOptions: {
22 | ecmaVersion: 6,
23 | sourceType: "module"
24 | },
25 | ecmaFeatures: {
26 | modules: true
27 | },
28 | sourceType: "module",
29 | env: {
30 | es6: true,
31 | node: true,
32 | browser: true
33 | },
34 | extends: "eslint:recommended"
35 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | gh_pages
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '0.11'
5 | - '5.0'
6 | script: npm run cover
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | # Change log
2 |
3 | ## v0.11.1 (2016-03-8) and v0.11.2
4 |
5 | - Patch bump to fix what `npm` installs by default (I erronously published `v1.0.0-x` on without `--tag next`, twice) Note to self, don't publish in the
6 | wee hours.
7 |
8 | ## v0.11.0
9 |
10 | - Refactor the innards so that the source order is completely respected in the output, including conditional at-rules.
11 | - Speed ++
12 | - Autoprefix all animation-* and transition.
13 | - Some error messages are now inserted in the result as -error-prefixed at-rules and :pseudo-classes.
14 |
15 | ## v0.10.0
16 |
17 | - At long last allow element selectors without a prepended space at the top level
18 | - Autoprefix animation and animation-name with -webkit- too like we do for `@keyframes`.
19 | - Tweaks and cleanup
20 |
21 | ## v0.9.0
22 |
23 | - Added @extend
24 | - Internal change: switched to rollup for build.
25 | - Dropped the inline-only version for now.
26 |
27 | ## v0.8.3
28 |
29 | - Cosmetic release that fixed typos in the docs
30 |
31 | ## v0.8.2
32 |
33 | - cleanup
34 | - improve test suite
35 | - update docs to reflect move to the `j2css` Github organization.
36 |
37 | ## v0.8.0
38 |
39 | - docs cleanup (version bump for npmjs.com dislpay).
40 |
41 | ## v0.8.0
42 |
43 | - classes and animations/@keyframes names are now localized by default in `j2c.sheet()`.
44 | - introduced `@global{}` and `:global()` to reach global names
45 | - dropped `j2c.scoped()` which is obsoleted by the above.
46 | - dropped bulk auto-prefixing.
47 | - better at-rules handling.
48 | - support for autoDeCamelization of property-names.
49 | - New signature for `j2c.sheet([namespace1, namespace2, ] source)` where `namespace` is an object
50 | with plain -> localized names for classes and animations.
51 | - allow to create custom j2c instances that can be extended with plugins *[needs docs]*.
52 | - Hardened the test suite.
53 | - Bug fix: the source order is now respected in the output, with the caveat that, for nested selector, the children appear in source before the parent.
54 |
55 | ## v0.7.3
56 |
57 | - Identical to `v0.7.2`. Somehow a bad commit ended up on NPM for that version.
58 |
59 | ## v0.7.2
60 |
61 | - Fix regression: Vendor names were not in the corrrect order.
62 |
63 | ## pre-v0.7.2
64 |
65 | Uncharted territory.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # j2c [![npm][npm_img]][npm_url] ![.min.gz][size_img]
2 |
3 | [![Join the chat at https://gitter.im/j2css/j2c][gitter_img]][gitter_url]
4 | [![Build Status][trav_img]][trav_url]
5 | [![Coverage Status][cov_img]][cov_url]
6 | [![Dependency Status][deps_img]][deps_url]
7 | [![bitHound Score][bithound_img]][bithound_url]
8 | [![downloads][monthly_img]][monthly_url]
9 |
10 |
11 | A lean, no hassle CSS in JS solution.
12 |
13 | Scales from standalone use to isomorphic apps. Compatible with any framework/view library.
14 |
15 | Supports building either inline styles of full style sheets.
16 |
17 | In `sheet` mode, `j2c` follows a [**'local by default'**](https://medium.com/seek-ui-engineering/the-end-of-global-css-90d2a4a06284) approach to make it easier to write components without having to worry about class and animation names clashes.
18 |
19 | Like SASS, LESS and Stylus, `j2c` supports:
20 |
21 | - mixins
22 | - `@extend`
23 | - nested selectors (in `sheet` mode)
24 |
25 | All standard CSS at-rules are available out of the box, most importantly:
26 |
27 | - `@media` and `@supports` can be nested anywhere in the sheet
28 | - `@keyframes` (with automatic generation of `@-webkit-keyframes`)
29 | - `@font-face`
30 |
31 | The [home page](http://j2c.py.gy) has a few interactive demos.
32 |
33 | [trav_img]: https://travis-ci.org/j2css/j2c.svg?branch=master
34 | [trav_url]: https://travis-ci.org/j2css/j2c
35 | [cov_img]: https://coveralls.io/repos/j2css/j2c/badge.svg?branch=master
36 | [cov_url]: https://coveralls.io/r/j2css/j2c?branch=master
37 | [npm_img]: https://img.shields.io/npm/v/j2c.svg
38 | [npm_url]: https://npmjs.com/package/j2c
39 | [monthly_url]: http://npm-stat.com/charts.html?package=j2c
40 | [monthly_img]: https://img.shields.io/npm/dm/j2c.svg
41 | [size_img]: https://badges.herokuapp.com/size/npm/j2c/dist/j2c.global.min.js.gz?label=.min.gz
42 | [deps_img]: https://david-dm.org/j2css/j2c.svg
43 | [deps_url]: https://david-dm.org/j2css/j2c
44 | [bithound_img]: https://www.bithound.io/github/j2css/j2c/badges/score.svg
45 | [bithound_url]: https://www.bithound.io/github/j2css/j2c/
46 | [gitter_img]: https://badges.gitter.im/Join%20Chat.svg
47 | [gitter_url]: https://gitter.im/j2css/j2c
48 |
49 | ## Table of Contents
50 |
51 | - [Installation](#installation)
52 | - [Usage](#usage)
53 | - [For inline declarations: `j2c.inline(declarations)`](#for-inline-declarations-j2cinlinedeclarations)
54 | - [Arrays](#arrays)
55 | - [Overloading properties](#overloading-properties)
56 | - [Mixins](#mixins)
57 | - [Vendor prefixes:](#vendor-prefixes)
58 | - [For building a style sheet: `j2c.sheet(rules)`](#for-building-a-style-sheet-j2csheetrules)
59 | - [Combining multiple selectors](#combining-multiple-selectors)
60 | - [At-rules](#at-rules)
61 | - [Mixins and @extend](#mixins-and-extend)
62 | - [CSS Hacks](#css-hacks)
63 | - [Inserting a stylesheet in a document](#inserting-the-stylesheet-in-the-document)
64 | - [Isomorphic app support](#isomorphic-app-support)
65 | - [Limitations](#limitations)
66 | - [TODO](#todo)
67 | - [License: MIT](#license-mit)
68 |
69 | ----
70 |
71 | ## Installation
72 |
73 | ```Bash
74 | $ npm install j2c
75 | ```
76 |
77 | then
78 |
79 | ```JavaScript
80 | var j2c = require('j2c')
81 | ```
82 |
83 | There are also separate builds for `AMD`, `ES6` and a global `window.j2c` in the `dist` directory.
84 |
85 | ## Usage
86 |
87 | `j2c` can be used to either assemble inline declarations or full style sheets with, by default, locally unique class names.
88 |
89 | Here's an example of locallized class names (as pioneered AFAIK by [JSS](https://github.com/jsstyles/jss)):
90 |
91 | ```JavaScript
92 | sheet = j2c.sheet({
93 | ".title": {
94 | font_size: "3rem",
95 | "&:before": {
96 | color: "#00b",
97 | content: "'#'"
98 | }
99 | },
100 | ".content": {
101 | line_height: "1.6em",
102 | padding: "2rem"
103 | }
104 | });
105 | ```
106 |
107 | Unique class names are generated automatically for `title` and `content`:
108 |
109 | ```CSS
110 | .content_j2c_fvp6zc2gdj35evhsl73ffzq_0 {
111 | line-height: 1.6em;
112 | padding: 2rem;
113 | }
114 |
115 | .title_j2c_fvp6zc2gdj35evhsl73ffzq_0 {
116 | font-size: 3rem;
117 | }
118 |
119 | .title_j2c_fvp6zc2gdj35evhsl73ffzq_0:before {
120 | content: '#';
121 | color: #888;
122 | }
123 | ```
124 |
125 | `sheet` is now a `String` object with a `title` and `content` properties that hold the unique class names. It can be used like this in your view, either on the server, in the browser of for isomorphic apps (let's say this is part of a React view):
126 |
127 | ```Haml
128 |
129 |
130 |
Hello
131 |
Foo bar baz...
132 |
133 | ```
134 |
135 | The `` construct works in modernish browsers (ie9+). For older IE, see [below](#inserting-the-stylesheet-in-the-document).
136 |
137 | Animation names are also "localized" by default, font names are left untouched.
138 |
139 | ### For inline declarations: `j2c.inline(declarations)`
140 |
141 | The `j2c` function takes in JS objects and builds a `property:value;` list out of it.
142 |
143 | ```JavaScript
144 | j2c.inline({
145 | backgroundColor:"red",
146 | border: {
147 | top$left: {
148 | width: "1px",
149 | color: "white"
150 | }
151 | }
152 | })
153 | ```
154 |
155 | Outputs, as you could expect (white space added for readability):
156 |
157 | ```CSS
158 | background-color: red;
159 | border-top-color: white;
160 | border-top-width: 1px;
161 | border-left-color: white;
162 | border-left-width: 1px;
163 | ```
164 |
165 | `CamelCase` and `_snake_case` names are turned into `-dash-case`, so that property names can be left unquoted in the source.
166 |
167 | Combine (sub)properties who share the same value by using `$` as a separator. It is useful to specify vendor prefixes.
168 |
169 | #### Property ordering
170 |
171 | Provided you don't delete and re-add properties to your objects, the properties will end up in the CSS sheet in the source order.
172 |
173 | #### Arrays for value overloading and mixins
174 |
175 | You can sneak in arrays anywhere in the source tree. It enables many advanced techniques, like:
176 |
177 | ##### Overloading properties
178 |
179 | If you want to overload a property by using an array at the value level
180 |
181 | ```JavaScript
182 | j2c.inline({
183 | border_color: ["#33e", "rgba(64,64,255,0.8)"],
184 | })
185 | ```
186 |
187 | becomes
188 |
189 | ```CSS
190 | border-color:#33e;
191 | border-color:rgba(64,64,255,0.8);
192 | ```
193 |
194 | Alternatively:
195 |
196 | ```JavaScript
197 | j2c.inline([
198 | { border_color: "#33e"},
199 | { border_color: "rgba(64,64,255,0.8)"}
200 | ])
201 | ```
202 |
203 | and
204 |
205 | ```JavaScript
206 | j2c.inline({
207 | border:[
208 | {color: "#33e"},
209 | {color: "rgba(64,64,255,0.8)"}
210 | ]
211 | })
212 | ```
213 |
214 | will give the same result.
215 |
216 | ##### Mixins
217 |
218 | You can mix in properties by using a function call in an array:
219 |
220 | ```JavaScript
221 | function mixin(color) {
222 | return {
223 | border_color: color,
224 | color: color
225 | }
226 | }
227 |
228 | j2c.inline([
229 | mixin("red"),
230 | {
231 | font_size:"2em"
232 | }
233 | ])
234 | ```
235 |
236 | ```CSS
237 | 'color:red;
238 | border-color:red;
239 | font-size:2em;'
240 | ```
241 |
242 | The mixin could also be a plain JS object if it doesn't need to be customized.
243 |
244 | ### For building a style sheet: `j2c.sheet(rules)`
245 |
246 | Everything found in the `inline` section applies here too, I recommend you read it first.
247 |
248 | To give you a taste of what can be done in j2c, here's a first, rather advanced example.
249 |
250 | ```JavaScript
251 | s = j2c.sheet({
252 | "ul.foo": {
253 | "@media condition": {
254 | color: "red"
255 | },
256 | // properties for the main ul.my_root_class elements
257 | font: {
258 | size: "2em",
259 | family: "sans-serif"
260 | },
261 | // underscores in property names are converted to dashes.
262 | background_color: "#44f",
263 | // CamelCase is also automatically handled.
264 | borderRadius:"2px",
265 |
266 | // sub-selector for children element, notice the mandatory initial space
267 | // signifying a child element.
268 | " li": {
269 | padding:{
270 | left: "5px",
271 | top: "10px"
272 | },
273 | // convenient $ shortcut.
274 | border: {left$right: {width: "2px"}}
275 | }
276 | }
277 | })
278 | ```
279 |
280 | Output (after indentation):
281 |
282 | ```CSS
283 | ul.foo_j2c_fgdl0s2a5fmle5g56rbuax71_0 li{
284 | padding-left:5px;
285 | padding-top:10px;
286 | border-left-width:2px;
287 | border-right-width:2px;
288 | }
289 | ul.foo_j2c_fgdl0s2a5fmle5g56rbuax71_0{
290 | font-size:2em;
291 | font-family:sans-serif;
292 | background-color:#44f;
293 | }
294 | @media condition{
295 | ul.foo_j2c_fgdl0s2a5fmle5g56rbuax71_0{
296 | color:red;
297 | }
298 | }
299 | ```
300 |
301 | Were `s.foo === "foo_j2c_fgdl0s2a5fmle5g56rbuax71_0 "`
302 |
303 | #### Global class and animation names.
304 |
305 | You can define or refer to global names using the `@global{}` pseudo at-rule, and the `:global()` function. This will thus preserve the `.foo`, `.bar` and `baz` names:
306 |
307 | ```JavaScript
308 | s = j2c.sheet({
309 | "@global": {
310 | "ul.foo": {
311 | font_size: "2em",
312 | }
313 | },
314 | "p:global(.bar)" :{
315 | color:"#f00",
316 | animation_name: ":global(baz)"
317 | },
318 | "@keyframes :global(baz)": {
319 | // define the global "baz" animation here.
320 | }
321 | })
322 | ```
323 |
324 | `@global` blocks also globalize animation names (not shown above).
325 |
326 | #### Combining multiple selectors
327 |
328 | TODO: refactor this section to mention the SASS-like `&` placeholder (at any arbitrary position).
329 |
330 | Here's a excerpt from the `j2c` port of the [PocketGrid](https://github.com/arnaudleray/pocketgrid/blob/44aa1154a56b11a852f7252943f265028c28f056/pocketgrid.css).
331 |
332 | ```JavaScript
333 | j2c.sheet({"@global": {
334 | ".block,.blockgroup":{
335 | ",:before,:after":{ // Notice the initial comma.
336 | box_sizing:"border-box"
337 | }
338 | }
339 | }})
340 | ```
341 |
342 | Nesting `",:before,:after"` inside the `".block,.blockgroup"` block combines `[".block", ".blockgroup"]` with `["", ":before", ":after"]`, giving
343 |
344 | ```CSS
345 | .block,.block:before,.block:after,.blockgroup,.blockgroup:before,.blockgroup:after{
346 | box-sizing:border-box;
347 | }
348 | ```
349 |
350 | Mathy folks call this as a Cartesian product.
351 |
352 | #### At-rules
353 |
354 | `j2c` handles @-rules out of the box, including nested ones.
355 |
356 | ```JavaScript
357 | j2c.sheet({
358 | "@media screen": {
359 | " p": {
360 | foo:"bar",
361 | "@media (orientation: landscape)": {
362 | baz:"qux"
363 | }
364 | }
365 | }
366 | })
367 | ```
368 |
369 | becomes
370 |
371 | ```CSS
372 | @media screen {
373 | p {
374 | foo: bar;
375 | }
376 | @media (orientation: landscape) {
377 | p {
378 | baz: qux;
379 | }
380 | }
381 | }
382 | ```
383 |
384 | For `@keyframes` rules, a `@-webkit-keyframes` block is automatically created with auto-prefixed property names.
385 |
386 | #### Mixins and `@extend`
387 |
388 | Mixins and `@extend` make `j2c` sheets composable. Both techniques can be combined.
389 |
390 | ##### Mixins and source objects composition
391 |
392 | For mixins, arrays works the same way at the selector level as they do at the property/value one. You can therefore use the [method described in the "inline" section](#mixins) to create mixins, that can return either at-rules, selectors, properties or a mix thereof.
393 |
394 | ##### `@extend`
395 |
396 | `j2c` also supports a SASS-like `@extend`, more powerful in some regards, but more limited in others.
397 |
398 | The limitation is that it can only deal with classes. Specifically:
399 |
400 | ```JS
401 | namespace = j2c.sheet({
402 | '.red': {color: '#f00'}
403 | })
404 |
405 | sheet = j2c.sheet(namespace, {
406 | '.great': {
407 | fontSize: '3em'
408 | },
409 | '.greatRed': {
410 | '@extend': ['.great', '.red'] // you can also pass a single class
411 | }
412 | })
413 | ```
414 |
415 | `sheet.greatRed` is now defined as `'great_j2c... red_j2c... greatRed_j2c...'` (class names truncated for readability).
416 |
417 | The extra power comes from the fact that you can inherit from arbitrary classes, not just j2c-defined ones:
418 |
419 | ```JS
420 | sheet = j2c.sheet(namespace, {
421 | '.myButton': {
422 | '@extend': ':global(.button)', // coming, say, form Bootstrap
423 | color: theme.highlight
424 | }
425 | })
426 | ```
427 |
428 | Here, `sheet.myButton` is `'button myButton_j2c...'`.
429 |
430 | While `@extend` can import from arbitrary classes, it only imports into local ones.
431 |
432 | `@extend` works fine with nested selectors. If there are more than one class in a selector, `@extend` applies to the last (right-most) one.
433 |
434 | ###### Invalid uses
435 |
436 | If the last or only selector is a `:global(.klass)`, in `@global` context, or in the absence of a class in the selector, `@extend` is turned into a `at-extend` property and inserted as-is in the sheet.
437 |
438 | #### CSS Hacks
439 |
440 | Since `j2c.sheet` 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.
441 |
442 | Here's another modified excerpt from the PocketGrid port:
443 |
444 | ```JavaScript
445 | j2c.sheet({
446 | ".blockgroup": [
447 | "*zoom: 1; /* hackety hackery */",
448 | {
449 | "list-style-type":"none",
450 | padding:0,
451 | margin:0
452 | }
453 | ]
454 | })
455 | ```
456 |
457 | Array elements are inserted in sequence, and string literals are treated as a list of properties, and inserted as is.
458 |
459 | Result:
460 |
461 | ```CSS
462 | .blockgroup{
463 | *zoom: 1; /* hackety hackery */
464 | }
465 | .blockgroup{
466 | list-style-type:none;
467 | padding:0;
468 | margin:0;
469 | }
470 | ```
471 |
472 | You can also pass th result of `j2c.inline` which is less picky about property names.
473 |
474 | ## Vendor prefixes:
475 |
476 | _Note: The prefix story in `j2c` is currently sub-optimal. I hope at some point to port prefixfree as a plugin. It is already small, and half of it isn't needed for `j2c` (the half that deals with finding and updating style elements in the DOM)._
477 |
478 | ### Prefixing property names
479 |
480 | You can specify the prefixes by hand using the "$" operator where needed:
481 |
482 | ```JavaScript
483 | j2c.inline({
484 | // Notice the trailing dollar, required for the unprefixed property.
485 | _o$_ms$_moz$_webkit$: {foo: "bar"},
486 | hello: "world"
487 | });
488 | ```
489 |
490 | Compiles to
491 |
492 | ```CSS
493 | p {
494 | -o-foo:bar;
495 | -ms-foo:bar;
496 | -moz-foo:bar;
497 | -webkit-foo:bar;
498 | foo:bar;
499 | hello:world;
500 | }
501 | ```
502 |
503 |
504 | ### Prefixing values
505 |
506 | `/!\` This will be replaced by a plugin in a future version.
507 |
508 | To prefix values, you can use `j2c.prefix`:
509 |
510 | ```JavaScript
511 | j2c.inline({
512 | background_image:j2c.prefix(
513 | "linear-gradient(90deg, #f00, #ff0)",
514 | ['moz','webkit']
515 | )
516 | })
517 | ```
518 |
519 | ```CSS
520 | background-image: -moz-linear-gradient(90deg, #f00, #ff0);
521 | background-image: -webkit-linear-gradient(90deg, #f00, #ff0);
522 | background-image: linear-gradient(90deg, #f00, #ff0);
523 | ```
524 |
525 | There's no support for prefixing a list multiple values (e.g. `"linear-gradient(90deg, #f00, #ff0),linear-gradient(90deg, #f00, #ff0)"`).
526 |
527 | ### `@-webkit-keyframes`
528 |
529 | `/!\` This will be replaced by a plugin in a future version.
530 |
531 | `@keyframes` blocks automatically produce their `@-webkit-keyframes` counterparts, even in the absence of a vendor list argument.
532 |
533 | ## Inserting the stylesheet in the document
534 |
535 | Foreword: Please note that the following is based on research on the Web, but not effectively tested in Internet explorer at the moment.
536 |
537 | ### ie9+
538 |
539 | Add a text node to a new `style` element.
540 |
541 | ```JavaScript
542 | var style = document.createElement('style');
543 | style.type = 'text/css'; // my not even be needed
544 | style.appendChild(document.createTextNode(sheet));
545 | ```
546 |
547 | In frameworks:
548 |
549 | ```Handlebars
550 |
551 | ```
552 |
553 | Sweet, innit?
554 |
555 | ### ie8+ (sheets up to 32k in ie8)
556 |
557 | As above, but with a `link` element and a data URI.
558 |
559 | ```Handlebars
560 |
561 | ```
562 |
563 | Note that ie8 has a 32k limit on the length of data URIs. It supports base 64 in data URIs, but doesn't provide `btoa`, which would not be useful in this context anyway, since base 64 encoded sheets are larger than URI encoded ones.
564 |
565 | ### ie6+ (unlimited sheet size)
566 |
567 |
568 | ```JavaScript
569 | function stylize(element, sheet){
570 | element.type = 'text/css';
571 | if (element.styleSheet){
572 | element.styleSheet.cssText = sheet;
573 | } else {
574 | element.appendChild(document.createTextNode(sheet));
575 | }
576 | return element;
577 | }
578 | var el = document.createElement('style')
579 | var sheet = j2c.sheet(...)
580 | stylize(el, sheet);
581 | document.head.appendChild(el);
582 | ```
583 |
584 | For this to work in client-side frameworks, you need to grab a handle on the actual `