Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How does this project compare to jss? #2

Open
serapath opened this issue Jun 10, 2015 · 8 comments
Open

How does this project compare to jss? #2

serapath opened this issue Jun 10, 2015 · 8 comments
Labels

Comments

@serapath
Copy link

https://github.com/jsstyles/jss

@pygy
Copy link
Member

pygy commented Jun 11, 2015

tl;dr: j2c is dumber API-wise, but more powerful out of the box, language-wise. It doesn't support plugins (yet). It is smaller (<1k mingzipped). It has a larger test/code ratio.

Currently

API

JSS has an API to attach/detach style sheets to the DOM and to add/modify the properties of a given "rule".

j2c outputs strings, and it's up to you to insert them in the DOM, either as inline styles of full style sheets. If you want to update styles dynamically, you can either add/remove classes, use inline styles, or create and append a new stylesheet for that selector.

Both support "local" class names, but the JSS local names may clash if JSS is included more than once in a page. The probability of that happening with j2c is infinitesimal.

Features/plugins

j2c supports all at-rules, optionally nested if it makes sense (@media and @supports).

j2c allows to fix the order of appearance of properties and selectors.

Out of the box, j2c provides the equivalent of the nested, extend and debug plugins for JSS. No CamelCase, but instead j2c converts underscores to dashes in property names. The rationale for underscores is in the FAQ at the bottom of the home page.

There's no smart auto-prefixing at the moment. You can either prefix all properties in bulk or do it manually using the $combiner:

_o$_ms$_moz$_webkit$: {
    box_shadow: "0 0 10px rgba(0,0,0,0.6)",
    transform: "rotate(-45deg) scaleX(0.85)",
    user_select: "none"
}

This adds the prefixes in order, ending with the unprefixed property names thanks to the final $ sign. I should proably improve the documentation of that operator.

@keyframes rules automatically get a @-webkit-keyframes counterpart with properties prefixed in bulk. That's AFAIK the only at-rule that needs a prefix in practice.

In the works:

In the next branch:

  • j2c.scoped() is folded j2c.sheet(), and all classes are mangled into local names by default. You can declare some names as globals, or go full global, optionally. Nested class names are also localized.

(edit: actually, in the latest version everything is folded into a single j2c function, but it may not last).

  • Animation names are mangled too, by default. You can thus name your animations however you like without the fear of a name clash.
  • There's a post-process hook that can be tapped into for smart auto-prefix. The auto-prefix code has yet to be written, and will be provided as an add-on.

All of it is already functional and tested, but I need to fine tune the API and document it.

@pygy pygy added the question label Jun 11, 2015
@serapath
Copy link
Author

tl;dr: i'm confused how to improve the way i do components and everytime i try to explain that, I end up with a relatively unstructured braindump - sry for that.

Sounds great.
I'm following jss for a while now and still don't know how to integrate it.
#68 and #56

I'm also watching css-modulesify.
The project I'm currently using in my projects a lot is atomify becase it helps me to create components

Atomify is using

  • npm require for javascript, thus require & module.exports + "main":"entry.js" in package.json.
  • rework-css for css and @import "moduleName"; + "style":"entry.css" in package.json

You can pass in variables to imported stylesheets using:

:root {
  --variablename: #fff;
}

and later in a css property: body: var(--variablename) and combine that with BEM conventions to get local scope for css, but it requires some discipline - no automation here.
Atomify is brainstorming about switching from rework to postcss and already did a first step by including autoprefixer which uses postcss

A presentation about atomify is here and the slides in the end show how to do themeing across css components

In the issue links above i write some code of how my frameworkless components in javascript approximately look like and how i'm trying to think about how to use all that stuff - ideally to have somehow the same awesome features i get from the node javascript require(..) function.

Currently: css classes is the glue in components. They are uses in

  • css as selectors
  • html as element classes
  • javascript as .querySelectorAll(...) argument

If they will be autoprefixed to make the application of BEM conventions simpler across many composed modules, it's hard for me how to write my css, html and javascript and have the glue mentioned above work in a complex project.

@pygy
Copy link
Member

pygy commented Jun 11, 2015

The use case I had in mind when writing j2c was components for client-side frameworks, that handle the DOM for you and provide you elements when needed without having to resort to querySelectorAll(). Think componentDidMount in React, or config in Mitrhil.

I think that it could accommodate your server-based use case, but we'd have to rethink the gluing process...

I'll read your slides. I had to clean them up a bit, the backgrounds and animations were distracting. Thankfully you host them here, which means I could just fork your repo and get rid of the noise :-).

A first thought is about BEM. It is mostly a way to namespace your CSS classes to avoid name clashes. You can use the same design philosophy in j2c without having to use the dashes and underscores.

Using this example:

https://css-tricks.com/bem-101/

/* Block component */
.btn {}

/* Element that depends upon the block */ 
.btn__price {}

/* Modifier that changes the style of the block */
.btn--orange {} 
.btn--big {}
<a class="btn btn--big btn--orange" href="http://css-tricks.com">
  <span class="btn__price">$9.99</span>
  <span class="btn__text">Subscribe</span>
</a>

With the next branch (the penultimate commit actually, I'll probably revert the last commit).

css = j2c.sheet({
    ".btn": {
        ...,

        "& .price": {...},

        "&.orange": {...},
        "&.big": {...}
    }
})
.btn_j2c_330681706_1434027505456_3{
    ...
}
.btn_j2c_330681706_1434027505456_3.big_j2c_330681706_1434027505456_3{
    ...
}
.btn_j2c_330681706_1434027505456_3.orange_j2c_330681706_1434027505456_3{
    ...
}
.btn_j2c_330681706_1434027505456_3 .price_j2c_330681706_1434027505456_3{
    ...
}
<a class="{css.btn} {css.big} {css.orange}" href="http://css-tricks.com">
  <span class="{css.price}">$9.99</span>
  <span class="{css.text}">Subscribe</span>
</a>

That's the idea for a client-side framework.

For your server-based use case you could use normal classes in the source (class=""btn") then walk the DOM tree to "localize" the class names based on the properties of the object returned by j2c (either on the server or in the browser, it would be trivial to do recursively) and also find a way to reach the elements for attaching handlers.

You could either use .querySelectorAll('.' + css.btn), rely on custom attributes .querySelectorAll([data-my-bnt]) for your selectors, or rely on the DOM tree structure.

The last two options are not very good (stream of thoughts here). Custom attributes mean you fall back to the global namespace, and relying on the tree structure is brittle.

I'll read your slides and add more later.

@serapath
Copy link
Author

serapath commented Sep 7, 2015

Actually, I was talking about client side updates.
I did investigate css-modules a bit, and jss (which might solve my problems, but seems to be a bit verbose), and smart-css.

I like j2c but it doesn't seem to support runtime updates (e.g. change color or animations or any kind of styling based on sensor input (light sensor?) or user input (mouse movement?))

Once my component rendered, i'd like to attach event listeners that change a components styling.
The example below does not solve the problem and i made it in a comment on stilr here, but maybe it helps to clarify the point

// BUTTON USAGE
var messageBus       = require('./messageBus.js')
var buttonComponent  = require('button-component')
var specialColorCode = '#a339d2'

var buttonStyle = buttonComponent(document.body, messageBus)

messageBus.on('requestUpdates', function () {
  messageBus.emit('newUpdates', {
    data: prompt('What is the new data?')
  })
  buttonStyle.create({ Info__content: { color: specialColorCode } })
})
// BUTTON IMPLEMENTATION
var buttonStyle = require('./buttonStyle')

module.exports = function ButtonComponent (parentDOM, eventBus) {

  parentDOM.innerHTML = '<div class="Info">' +
    'Info: <input class="Info__content" type="text" name="info"><br>' +
    '<button class="Info__button">Refresh</button>' +
    '</div>'

  var Info       = parentDOM.querySelector('.Info')
  var __content  = parentDOM.querySelector('.Info__content')
  var __button   = parentDOM.querySelector('.Info__button')

  __button.addEventListener('click',  function onclick (event) {
    // check for updates
    Info.setAttribute('class', 'Info Info--loading')
    eventBus.emit('requestUpdates')
  })

  eventBus.on('newUpdates', function (update) {
    Info.setAttribute('class', 'Info')
    if (update && update.data) {
      __content.value = update.data
    }
  })

  buttonStyle.on('update', function () {
    var newCSS = buttonStyle.render()
    console.log(newCSS)
  })
  return buttonStyle
}

@pygy
Copy link
Member

pygy commented Sep 7, 2015

Indeed, j2c itself doesn't support runtime updates. It takes JS objects and outputs strings ready for inclusion in the DOM.

JSS is probably a better solution since it supports style sheet mutations after DOM insertion (which are handledd automatically under the hood.

That being said, for your use case, you probably need per component instance updates rather than the per selector updates that JSS provides...

Maybe you could, using JSS, create one class per component instance for the styles that need to be updated, and operate on that in your event handlers...

j2c is more static. You'd have to regenerate a stylesheet string, update the <style> tag, and have the browser parse it then render the modifications. It would be wasteful IMO. That being said, I don't know how JSS handles updates under the hood... @kof, does that sound like a reasonable use of JSS?

Edit: also, yikes! yet another competitor! Hello stylr. (and hello grandad JSS, if you only took notice just now).

Edit2: You could also create per component instance <style> tags using j2c and manually update it using the CSSOM like I suppose JSS does under the hood (see here for an example). What you'd probably want in that case is a lib that does virtual CSSOM diffing... An interesting project in itself...

@kof
Copy link

kof commented Sep 8, 2015

but the JSS local names may clash if JSS is included more than once in a page.

This is arleady solved. You just create own Jss instances if this is an issue for you.

j2c is dumber API-wise, but more powerful out of the box

Don't see the power, see a simpler version of jss)

@pygy
Copy link
Member

pygy commented Sep 8, 2015

Thanks for passing by @kof.

Don't see the power, see a simpler version of jss)

Look better:

mixin = {
    " .child": {
        float:"left"
    },
    ".modifier": {
        color:"green"
    },
    font_size: "10px"
}

j2c.sheet({"@global":{
    ".foo":{
        color: "#f00",
        "@media bar": {
            ".firefox &": {margin: "33px"},
            " .baz": [
                mixin,
                {
                    "@supports qux": {
                        border: [
                            '3px solid green',
                            {left$right: '1px solid red'}
                        ]
                    }
                }
            ]
        }
    }
}})
.foo{
    color:#f00;
}
@media bar{
    .firefox .foo{
        margin:33px;
    }
    .foo .baz .child{
        float:left;
    }
    .foo .baz.modifier{
        color:green;
    }
    .foo .baz{
        font-size:10px;
    }
    @supports qux{
        .foo .baz{
            border:3px solid green;
            border-left:1px solid red;
            border-right:1px solid red;
        }
    }
}

See now ;-)?

This is out of the box, no plugin required (I indented the result manually for readability).

I'm using the next branch that drops the JSS-like j2c.scoped({pseudoclass:{...}}) method and automatically localizes all class and animation names unless you use @global{} or :global(...), a bit like PostCSS does.

Sadly I had to break the 1KB barrier, and I'm now at 1031 bytes mingzipped for the CommonJS version. /humblebrag ;-)

BTW, for @serapath, I was looking for your input on this:

That being said, for your use case, you probably need per component instance updates rather than the per selector updates that JSS provides...

Maybe you could, using JSS, create one class per component instance for the styles that need to be updated, and operate on that in your event handlers...

j2c is more static. You'd have to regenerate a stylesheet string, update the <style> tag, and have the browser parse it then render the modifications. It would be wasteful IMO. That being said, I don't know how JSS handles updates under the hood... @kof, does that sound like a reasonable use of JSS?

Is it reasonable to update properties continuously (say, on mouse movement or light/tilt sensor events) using JSS?

@kof
Copy link

kof commented Sep 9, 2015

Well, scoped selectors are by default in jss too, nested at-rules is something I never used and though not implemented ... but if I get some requests on this I am open to support it too.

The weird thing is (same in postcss) that you actually treat selectors as namespaces to access them later from javascript. In jss I strongly differentiate namespaces for rules and selectors. Selectors defined by user are only possible in global style mode. ({named: false} option)

As for plugins I didn't put all of them into core to make core smaller and faster and allow people to use just plugins they need. Core is also platform agnostic.

Dynamic props is the only part of jss core which is browser dependent. It makes sense to change rules dynamically when the rule is applied to lots of elements. So that updating styles on every element would be less performant than changing it on the rule.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants