My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Saturday, February 08, 2014

restyle.js - a simplified CSS approach

Before you think about "yet another CSS preprocessor", I'd like to inform you that I've asked around to few common, well known, CSS or general web developers and it looks like this little script was still missing ... once you'll realize what is this about, you'll probably wonder yourself "how come nobody has done this already?".
My idea is that somebody probably did but I am not sure in 0.8KB minzipped and compatible with both server and client down to IE6 ... so here I am talking about restyle.

update Published object specifications explaining how the object is parsed and what will produce.
P.S. yes, absurd.js inspired somehow restyle except absurd.js won't work in your browser at runtime (yet?) does way more in about 26KB (~9KB gzipped) while restyle does not implement any magic.
Please read F.A.Q. to know more or check the restyle VS absurd page with basic features compared.

restyle.js

restyle is a function that provided a JS object creates and place in the DOM its CSS representation, using by default all possible prefixes. i.e.
var addedStyle = restyle({
  body: {
    color: '#FFF',
    background: {
      image: 'url(bla.bla)',
      color: '#000'
    }
  }
});
will result in this CSS, the string only in node.js world, a whole object in the DOM/browser world.
body {
  color: #FFF;
  background-image: url(bla.bla);
  background-color: #000;
}
to remove this new style at any time in DOM/browsers:
addedStyle.remove();
this is nothing impressive with standard styles but it becomes very handy when prefixes are in place.
restyle({
  'div > button:first-child': {
    transform: 'rotate(30deg)'
  }
}, ['moz', 'webkit']);
will become
div > button:first-child {
  -webkit-transform: rotate(30deg);
  -moz-transform: rotate(30deg);
  transform: rotate(30deg);
}
while the following
restyle({
  'body > div': {
    animation: {
      name: 'spin',
      duration: '4s'
    }
  },
  '@keyframes spin': {
    from: {
      transform: 'rotate(0deg)'
    },
    to: {
      transform: 'rotate(360deg)'
    }
  }
});
will become this
body > div{
  -webkit-animation-name:spin;
  -moz-animation-name:spin;
  -ms-animation-name:spin;
  -o-animation-name:spin;
  animation-name:spin;
  -webkit-animation-duration:4s;
  -moz-animation-duration:4s;
  -ms-animation-duration:4s;
  -o-animation-duration:4s;
  animation-duration:4s;
}
@-webkit-keyframes spin{
  from{
    -webkit-transform:rotate(0deg);
    transform:rotate(0deg);
  }
  to{
    -webkit-transform:rotate(360deg);
    transform:rotate(360deg);
  }
}
@-moz-keyframes spin{
  from{
    -moz-transform:rotate(0deg);
    transform:rotate(0deg);
  }
  to{
    -moz-transform:rotate(360deg);
    transform:rotate(360deg);
  }
}
@-ms-keyframes spin{
  from{
    -ms-transform:rotate(0deg);
    transform:rotate(0deg);
  }
  to{
    -ms-transform:rotate(360deg);
    transform:rotate(360deg);
  }
}
@-o-keyframes spin{
  from{
    -o-transform:rotate(0deg);
    transform:rotate(0deg);
  }
  to{
    -o-transform:rotate(360deg);
    transform:rotate(360deg);
  }
}
@keyframes spin{
  from{
    -webkit-transform:rotate(0deg);
    -moz-transform:rotate(0deg);
    -ms-transform:rotate(0deg);
    -o-transform:rotate(0deg);
    transform:rotate(0deg);
  }
  to{
    -webkit-transform:rotate(360deg);
    -moz-transform:rotate(360deg);
    -ms-transform:rotate(360deg);
    -o-transform:rotate(360deg);
    transform:rotate(360deg);
  }
}
and so on ... this has few advantages if done at runtime:
  • it simplifies prefixes hell effortless
  • it confines all new changes in one style element that can be dropped at any time
  • it can associate or create complex styles inline for custom components preserving size
  • it is compatible with runtime/dynamic properties for any sort of screen/case/device
Used without prefixes on the server side, it can be a pre-processor of the pre-processor, able to use variables at runtime in a JS way, instead of using SASS and company.

Moar Info and Conclusions

Everything open sourced in github, restyle is also an npm and a bower module.
If you think you got everything already, feel free to test some syntax or verify if it works for your browser at runtime ... and that's correct, it should work even in IE6 ;-)
enjoy

following

there are experiments in place able to make restyle magic, follow up in the next post if you want to know more.

update on why the DOM result has so many prefixes?

Every time we write CSS for other browsers vendors too we are creating useless noise in the file for the current browser. This is because we don't serve the CSS file accordingly with the current browser, we simply define all prefixes hoping one will be used from the current visiting browser.
Since no concrete size will be compromise when used at runtime, restyle, by default, has exactly the same careless behavior: it just create prefixes for everything simply trusting the normal browser behavior that what should work and be understood via prefix will work, everything else will be ignored, where last non prefixed version should be prevalent.
There is no way to feature detect properly weird or early CSS behaviors per each vendor, it will blow the code size and complicate the logic resulting in a non so present, past, and future proof behavior as the current one.
That said, you can pass the Array you want if at runtime you think all those prefixes are pointless, restyle purpose is to keep it simple and work ...
Although, if you have any hint on how to do this properly everywhere without adding more than 3 lines of code to the source :P I'll be happy to listen to you ;)
In node.js case, you should probably use a more complex preprocessor after creating CSS via restyle with the benefit that used logic can be applied eventually at runtime too.

11 comments:

David F Kaye said...

THIS IS EXCELLENT STUFF!

Have a look at the AbsurdJS css-preprocessor which does some of the same stuff but reduces rulesets.

http://krasimir.github.io/absurd/

Blerik said...

absurd.js

Charles Bryant said...

Brilliant!

Andrea Giammarchi said...

updated here and there the fact indeed restyle has been inspired by absurd.js but instead of being a full preprocessor it is something useful at runtime for the browsers too … and can be used upfront or with probably similar objects on the server side too.

Daniele Polencic said...

Rework from visionmedia is very similar to restyle:

https://github.com/reworkcss/rework

Krasimir said...

Just to mention that Absurd.js is ported for client side usage and could produce CSS at runtime.

fibric said...

Hmm if its useful for a browser 'at runtime' why vendor prefixes are applied for all mentioned browser but not the one which is executing this js?

does it make sense to create -moz prefix when my chrome is doing magic?

Andrea Giammarchi said...

@Daniele rework seems different and way closer to absurd, but it starts from reading CSS, so basically the opposite of what `restyle` does.

@Krasimir thanks for the update, although 30KB will do much more than `restyle`. I'd like to know your opinion about `restyle` design choice, in terms of object are shaped, thanks.

@fibric does it make sense to serve CSS files that contain vendor prefixes your browser won't use? Yes? restyle does the same. No? restyle does the same … do you have any specific problem with that at runtime since you'll never even see those CSS that will be simply ignored/not used ? If so, please file a bug … if no, how can I help you? :-)

Andrea Giammarchi said...

@fibric another solution would be that you find out which prefix is needed and you wrap `restyle` sending that Array with just one prefix so that you will be virtually happy about used prefixes behind the runtime scene ;-)

there is a solution, if that somehow disturbs you ... but unless there are evidences current approach is problematic, I don't think it make sense increase size and logic because of some philosophy, you know what I mean.

Take care

João Cunha said...

Hey pal, I've updated my v-unit.js to use the same head tag selection as your restyle. We happened to share the CSS node technique, though. Check it out: https://github.com/joaocunha/v-unit

Thanks

Andrea Giammarchi said...

nice one, although the viewport size calculation is a bit more problematic than clientHeight and clientWidth, specially on mobile.

How about you have a look to `display` module too ?

it does the "observing" in a non so aggressive way and you can add your own listener via `display.on('change', yourCallback)` it will provide width and height of the container, not the one of the html section ;-)

display