Show HN: CSS Meta-Language in Javascript

10 points by KeithMajhor ↗ HN
This was an attempt to make css more accessible to javascript. It just happened to solve a lot of the same problems as less and sass as a side-effect. I'll start with a few examples and then break out the code and explain it...

Here's a generic example:

    css // Begin CSS

    ( "selector1" )
    ({
        property1: value,
        property2: value,
        // ...
    })

    ( "selector2" )
    ({
        property1: value,
        property2: value,
        // ...
    })

    ; // End CSS
Some less and sass features (Variables, Mixins, etc.):

    // Variable
    var text_color = "black";

    // Mixin
    var dim = function( w, h ) {
        return {
            width:  w,
            height: h,
        }
    };

    css // Begin CSS

    ( "div" )( dim( "10px", "10px" ) )
    ({
        color: text_color,
    })

    ( "span" )
    ({
        color: text_color,
    })

    ; // End CSS
And finally, since it's leveraging javascript this technique can modify styles dynamically (My goal):

    item_color_input.onsubmit = function( )
    {
        css // Begin CSS

        ( ".item" )
        ({
            background: item_color_input.value,
        })

        ; // End CSS
    }
Frameworks like jQuery provide similar functionality by acting on the DOM. This can be slow (since it has to execute the selector in javascript) and doesn't affect future elements.

The Code: css.js http://keithmajhor.pastebin.com/twCKCcHV

    // ==============================================
    // File:   css.js
    // Author: Keith Majhor <KeithMajhor@gmail.com>
    // ==============================================
    window.css = function( a )
    {
        var decl = { },
            elem = { },
            rule,
            prop;

        css = function( a )
        {
            if ( typeof( a ) === "string" )
            {
                rule = a;
                if ( !decl[rule] )
                {
                    decl[rule] = { };
                    elem[rule] = document.createElement( "style" );
                    document.head.appendChild( elem[rule] );
                }
            }
            else if ( rule )
            {
                for ( prop in a )
                    decl[rule][prop] = a[prop];

                var temp = rule + "{";
                for ( prop in decl[rule] )
                    temp += prop + ":" + decl[rule][prop] + ";";
                elem[rule].innerHTML = temp + "}";
            }

            return css;
        }

        return css( a );
    }
All it does is maintain a hash for each selector and update a <style> tag whenever it's modified. As is it's 500 bytes, minified it's below 300.

I hope this is useful to someone. If anyone has any insight into why this might be a really stupid thing to do, I'd love to hear it.

11 comments

[ 3.1 ms ] story [ 39.3 ms ] thread
This is great. How are you licensing it?

And what do you plan on for the cases where people have disabled JS?

You can use whatever license you want (MIT, BSD, etc.)

This is geared towards applications that wouldn't work without JS anyway. But you could always degrade gracefully by providing some default css in a <noscript> tag.

That makes perfect sense. I really like that it changes the css for the entire class. This would have good applications for a mockup website, you could add elements, alter classes, and add more elements in a consistent manner.
That's sounds a lot like what I had in mind when I wrote it.
jQuery can act on future elements using the .live() function.
I was referencing $( "selector" ).css({ ... }) specifically.

.live works for events. I suppose you could add an event to modify the style of each element as it was added... But then that really feeds back into the previous point which is that it would be slow.

I suppose the trade-off is having a style tag for each selector, which seems wasteful, and I'm not sure of the effect on performance. Any benchmarks for a large-sized stylesheet (1000 rules or so)?
Each time a declaration section is added to a selector the contents of the corresponding tag have to be re-stringified. Having a single tag would require the entire style sheet to be rebuilt whenever a rule was modified.
http://keithmajhor.pastebin.com/xvgVAKve

removed the implicit global

It won't work with that. I'm replacing the (implicit) global window.css with a closure the first time it's invoked. Your modification will cause future css sections to create their own <style> tags even if the they reference the same selectors.

Perhaps this is clearer: http://keithmajhor.pastebin.com/5KA7Ecn0