Security News
JSR Working Group Kicks Off with Ambitious Roadmap and Plans for Open Governance
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
See HISTORY.md for a high level overview of the differences.
A lean (2.0KB), no hassle CSS in JS solution.
j2c
:
<script src="j2c.global.js">
to isomorphic apps.gulp
/babel
/browserify
/webpack
).For styling components, mostly. Especially if you plan to publish them standalone (your users won't have to worry about importing the style sheets, and you won't tie your lib to any build system).
j2c
supports building either inline styles of full style sheets (arbitrary CSS).
In sheet
mode, j2c
follows a 'local by default' approach to make it easier to write components without having to worry about class and animation names clashes.
Like SASS, LESS and Stylus, j2c
supports:
@composes
, an @extends
-like mechanism inspired by @tivac's Modular CSSAll standard CSS at-rules are available out of the box, most importantly:
@media
and @supports
, which can be nested anywhere in the sheet, SASS-style.@keyframes
@font-face
j2c
...... with the j2c
importer.
The home page has a few interactive demos.
j2c
is mostly done.At this point, the core (this very repo) is considered feature complete and should not evolve much if at all. We will add new at-rules as they are standardized, and will fix any bugs that are reported.
Thanks to
@compose
which I stole from his Modular CSS project.$ npm install j2c
then
var j2c = require('j2c')
There are also separate builds for AMD
, ES6
and a global window.j2c
in the dist
directory.
j2c
can be used to either assemble inline declarations or full style sheets with, by default, locally unique class names.
Here's an example of locallized class names (as pioneered AFAIK by JSS):
sheet = j2c.sheet({
".title": {
font_size: "3rem",
"&:before": {
color: "#00b",
content: "'#'"
}
},
".content": {
line_height: "1.6em",
padding: "2rem"
}
});
Unique class names are generated automatically for title
and content
:
.content_j2c_fvp6zc2gdj35evhsl73ffzq_0 {
line-height: 1.6em;
padding: 2rem;
}
.title_j2c_fvp6zc2gdj35evhsl73ffzq_0 {
font-size: 3rem;
}
.title_j2c_fvp6zc2gdj35evhsl73ffzq_0:before {
content: '#';
color: #888;
}
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):
<div>
<style>{sheet}</style>
<h3 class="{sheet.title}">Hello</h3>
<div class="{sheet.content}">Foo bar baz...</div>
</div>
The <style>{sheet}</style>
construct works in modernish browsers (ie9+). For older IE, see below.
Animation names are also "localized" by default, font names are left untouched.
j2c.inline(declarations)
The j2c
function takes in JS objects and builds a property:value;
list out of it.
j2c.inline({
backgroundColor:"red",
border: {
top$left: {
width: "1px",
color: "white"
}
}
})
Outputs, as you could expect (white space added for readability):
background-color: red;
border-top-color: white;
border-top-width: 1px;
border-left-color: white;
border-left-width: 1px;
CamelCase
and _snake_case
names are turned into -dash-case
, so that property names can be left unquoted in the source.
Combine (sub)properties who share the same value using $
as a separator. It is useful to specify vendor prefixes.
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.
You can sneak in arrays anywhere in the source tree. It enables many advanced techniques, like:
If you want to overload a property by using an array at the value level
j2c.inline({
border_color: ["#33e", "rgba(64,64,255,0.8)"],
})
becomes
border-color:#33e;
border-color:rgba(64,64,255,0.8);
Alternatively:
j2c.inline([
{ border_color: "#33e"},
{ border_color: "rgba(64,64,255,0.8)"}
])
and
j2c.inline({
border:[
{color: "#33e"},
{color: "rgba(64,64,255,0.8)"}
]
})
will give the same result.
You can mix in properties by using a function call in an array:
function mixin(color) {
return {
border_color: color,
color: color
}
}
j2c.inline([
mixin("red"),
{
font_size:"2em"
}
])
'color:red;
border-color:red;
font-size:2em;'
The mixin could also be a plain JS object if it doesn't need to be customized.
j2c.sheet(rules)
Everything found in the inline
section applies here too, I recommend you read it first.
To give you a taste of what can be done in j2c, here's a first, rather advanced example.
s = j2c.sheet({
"ul.foo": {
"@media condition": {
color: "red"
},
// properties for the main ul.my_root_class elements
font: {
size: "2em",
family: "sans-serif"
},
// underscores in property names are converted to dashes.
background_color: "#44f",
// CamelCase is also automatically handled.
borderRadius:"2px",
// sub-selector for children element, notice the mandatory initial space
// signifying a child element.
" li": {
padding:{
left: "5px",
top: "10px"
},
// convenient $ shortcut.
border: {left$right: {width: "2px"}}
}
}
})
Output (after indentation):
ul.foo_j2c_fgdl0s2a5fmle5g56rbuax71_0 li{
padding-left:5px;
padding-top:10px;
border-left-width:2px;
border-right-width:2px;
}
ul.foo_j2c_fgdl0s2a5fmle5g56rbuax71_0{
font-size:2em;
font-family:sans-serif;
background-color:#44f;
}
@media condition{
ul.foo_j2c_fgdl0s2a5fmle5g56rbuax71_0{
color:red;
}
}
Were s.foo === "foo_j2c_fgdl0s2a5fmle5g56rbuax71_0 "
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:
s = j2c.sheet({
"@global": {
"ul.foo": {
font_size: "2em",
}
},
"p:global(.bar)" :{
color:"#f00",
animation_name: ":global(baz)"
},
"@keyframes :global(baz)": {
// define the global "baz" animation here.
}
})
@global
blocks also globalize animation names (not shown above).
TODO: refactor this section to mention the SASS-like &
placeholder (at any arbitrary position).
Here's a excerpt from the j2c
port of the PocketGrid.
j2c.sheet({"@global": {
".block,.blockgroup":{
",:before,:after":{ // Notice the initial coma.
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.
j2c
handles @-rules out of the box, including nested ones.
j2c.sheet({
"@media screen": {
" p": {
foo:"bar",
"@media (orientation: landscape)": {
baz:"qux"
}
}
}
})
becomes
@media screen {
p {
foo: bar;
}
@media (orientation: landscape) {
p {
baz: qux;
}
}
}
For @keyframes
rules, a @-webkit-keyframes
block is automatically created with auto-prefixed property names.
@coposes
Mixins and @composes
make j2c
sheets composable. Both techniques can be combined.
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 to create mixins, that can return either at-rules, selectors, properties or a mix thereof.
@composes
j2c
also supports @composes
, which works a bit like the SASS@extend
, more powerful in some regards, but more limited in others.
The limitation is that it can only deal with classes. Specifically:
sheet = j2c.sheet({
'.red': {
color: '#f00'
},
'.great': {
fontSize: '3em'
},
// `scarlet` here is the target of the composition, `great` and `red` are the sources.
'.scarlet': {
'@composes': ['.great', '.red'] // you can also pass a single class
}
})
sheet.scarlet
is now defined as 'great__j2c-xxx red__j2c-xxx scarlet__j2c-xxx'
(class names truncated for readability).
The extra power comes from the fact that you can inherit from arbitrary classes, not just j2c-defined ones:
sheet = j2c.sheet(namespace, {
'.myButton': {
'@composes': ':global(.button)', // coming, say, form Bootstrap
color: theme.highlight
}
})
Here, sheet.myButton
is 'button myButton_j2c...'
.
While the @composes
sources can be arbitrary classes, the target must be a local one. It will not work in global context.
@composes
doesn't support nested selectors, and doesn't work in conditional at rules. Its target must lie at the first nesting level.
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.
Here's another modified excerpt from the PocketGrid port:
j2c.sheet({
".blockgroup": [
"*zoom: 1; /* hackety hackery */",
{
"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; /* hackety hackery */
}
.blockgroup{
list-style-type:none;
padding:0;
margin:0;
}
You can also pass th result of j2c.inline
which is less picky about property names.
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).
You can specify the prefixes by hand using the "$" operator where needed:
j2c.inline({
// Notice the trailing dollar, required for the unprefixed property.
_o$_ms$_moz$_webkit$: {foo: "bar"},
hello: "world"
});
Compiles to
p {
-o-foo:bar;
-ms-foo:bar;
-moz-foo:bar;
-webkit-foo:bar;
foo:bar;
hello:world;
}
/!\
This will be replaced by a plugin in a future version.
To prefix values, you can use j2c.prefix
:
j2c.inline({
background_image:j2c.prefix(
"linear-gradient(90deg, #f00, #ff0)",
['moz','webkit']
)
})
background-image: -moz-linear-gradient(90deg, #f00, #ff0);
background-image: -webkit-linear-gradient(90deg, #f00, #ff0);
background-image: linear-gradient(90deg, #f00, #ff0);
There's no support for prefixing a list multiple values (e.g. "linear-gradient(90deg, #f00, #ff0),linear-gradient(90deg, #f00, #ff0)"
).
@-webkit-keyframes
/!\
This will be replaced by a plugin in a future version.
@keyframes
blocks automatically produce their @-webkit-keyframes
counterparts, even in the absence of a vendor list argument.
Foreword: Please note that the following is based on research on the Web, but not effectively tested in Internet explorer at the moment.
Add a text node to a new style
element.
var style = document.createElement('style');
style.type = 'text/css'; // my not even be needed
style.appendChild(document.createTextNode(sheet));
In frameworks:
<style>{sheet}</style>
Sweet, innit?
As above, but with a link
element and a data URI.
<link rel="stylesheet" itemprop="stylesheet" href="{'data:,' + encodeURIComponent(sheet)}" />
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.
function stylize(element, sheet){
element.type = 'text/css';
if (element.styleSheet){
element.styleSheet.cssText = sheet;
} else {
element.appendChild(document.createTextNode(sheet));
}
return element;
}
var el = document.createElement('style')
var sheet = j2c.sheet(...)
stylize(el, sheet);
document.head.appendChild(el);
For this to work in client-side frameworks, you need to grab a handle on the actual <style>
DOM node. This means that you must create a custom component/directive.
Here are a few examples:
var j2cComponent = {
render: function(){
return <style />
}
componentDidMount: function(){
stylize(React.findDOMNode(this), this.prop.sheet)
}
}
var j2cComponent = {
view: function(ctrl, args) {
return m("style", {
sheet: args.sheet
config: function(el, isinit, vdom) {
if(!isinit) {
stylize(el, vdom.attrs.sheet);
}
}
})
}
}
<style>
tags).module.directive('j2cSheet', function() {
return {
restrict: 'A',
link: function link(scope, element, attrs) {
if (element.tagName.toUpperCase() !== "STYLE") throw 'j2cSheet expects a <style> element';
stylize(element[0], attrs.j2cSheet);
}
};
});
module.directive('j2cInline', function() {
return {
restrict: 'A',
link: function link(scope, element, attrs) {
element[0].style += j2c.inline(attrs.j2cInline);
}
};
});
Since j2c
relies on the view library/framework for DOM insertion, it supports the isomorphic scenarios the same way your view solution does (see the previous section). Caveat: local class names will be regenerated on hydration, which means that styles will have to be computed twice by the browser.
j2c
at this point does little validation. When errors are encountered, it has no way to determine where in the source code the error occurred. To make it easier to pinpoint issues, the errors are inserted in the sheet as, for example @-error-bad-at-rule "@medis";
or :bad-sub-selector-foo
. This way you get the broader context of where the error occureed in the source.
j2c
relies on JS objects to define selectors and properties. The iteration order of object properties is officially undefined, but in practice it only differs in situations that do not apply to j2c
. As long as we're using non-numeric keys and we don't delete then re-add object properties, the source object order is respected in the output.
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.
At this point, it will
I may get around and write a validator companion, but I'm not there yet :-).
j2c
puts each selector list and properties on their own lines, but doesn't indent or add other white space.
For debugging purposes, I recommend that you pipe j2c
's output through a [be au ti fier] of your choice.
j2c
doesn't provide any facility to auto-prefix a list of values. It is relevant in the context of multiple gradient backgrounds and transition
/transition-property
values.
FAQs
A tiny CSS in JS solution.
We found that j2c demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
Security News
Research
An advanced npm supply chain attack is leveraging Ethereum smart contracts for decentralized, persistent malware control, evading traditional defenses.
Security News
Research
Attackers are impersonating Sindre Sorhus on npm with a fake 'chalk-node' package containing a malicious backdoor to compromise developers' projects.