Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically editing Less (css) code with JQuery-like selector syntax?

It's possible to use libraries in less.js to dynamically regenerate css from less files within the browser. If there was an easy way to modify less code, this would be an extremely powerful method of dynamically updating a site's css.

Imagine you had a colour that was used 100 times throughout a large site. If you wanted to change that color dynamically just using javascript, you would need to update every bit of css that had that colour (perhaps 50 lines of code).

With what I'm imagining all you would need to write is something like this:

$('@mainColour').value('#F04');

I'm thinking of having a go at this myself, but it sounds like a huge project and I wonder if someone has already started something like this?

edit: to clarify, ideally what I want to be able to do is take a string of Less code, programatically edit it (perhaps using a jquery-like selector syntax) and then spit it out as modified Less. Ideally the code is in Javascript (but not necessarily client side) The example I give above is one possible application but maybe not a good one (where there might be better more common ways of achieving it).

like image 473
Michael Bylstra Avatar asked Apr 23 '12 02:04

Michael Bylstra


6 Answers

it is definitely possible, but i had to modify the less sourcecode a bit (which i think is fine, considering it's really not meant to be done :) )

i suppose everyone wants to see a demo first, click here: http://jsfiddle.net/kritzikratzi/BRJXU/1/ (script-window contains only the modified less.js-source, everything of interest is in the html window)

kk, i'll first explain my patch, usage is at the end.

patch consists of three parts

add a utility function

less.Overrides = {}; 
less.Override = function( variableName, value ){
    if( !value ){
        delete less.Overrides[variableName]; 
    }
    else{
        less.Overrides[variableName] = value; 
    }
    less.refreshStyles(); 
}; 

save the property into an object and tell less to update it's styles.

modify the parse function

   function parse(str, callback ){
        ... 

        var overrides = "\n\n"; 
        for( var key in less.Overrides ){
            overrides += key + ": " + less.Overrides[key] + ";\n"; 
        }

        str += overrides; 

all we do here is serialize the overridden properties and add them to the end of every file that is parsed.

modify the loadStyles function

    if (styles[i].type.match(typePattern) || styles[i].hasAttribute( "lessText" )) {
        var lessText; 
        if( styles[i].hasAttribute( "lessText" ) ){
            lessText = styles[i].getAttribute( "lessText" );
        }
        else{
            lessText = styles[i].innerHTML || ''; 
            styles[i].setAttribute( "lessText", lessText );
        }
    ....

by default less will replace the type parameter from <style type='text/less'> to type='text/css' and forgot about the less-source. to prevent this the original less-source is stored and loaded.

usage and conclusion

<style type="text/less">
    @color: green; 

    #header{ color: @color; }
</style>
<div id="header">i'm the header!</div>
<a href="#" onclick="less.Override('@color', 'red');">make it red</a> 

this works just fine on my computer and i have to admit it looks very neat. i haven't tested external less files, if they don't work it should be easy to fix.

i still think it's not the best idea to use this in a production environment (for reasons mentioned by others already).

like image 200
kritzikratzi Avatar answered Nov 04 '22 04:11

kritzikratzi


While I agree with @Baz1inga that in general this would be easier to do by adding and removing classes, I also know there are certain cases where LESS-style variables work much better (e.g. if the color is sometimes foreground, sometimes background, or is lightened in certain places). This is definitely do-able; in fact, here's some tested code that will do it (minus the jQuery-style syntax; any particular reason for needing that?):

function update_css(newcss) {
    var id = "styleholder";
    if ((css = document.getElementById(id)) === null) {
        css = document.createElement('style');
        css.type = 'text/css';
        css.id = id;
        document.getElementsByTagName('head')[0].appendChild(css);
    }
    if (css.styleSheet) { // IE
        try {
            css.styleSheet.cssText = newcss;
        } catch (e) {
            throw new(Error)("Couldn't reassign styleSheet.cssText.");
        }
    } else {
        (function (node) {
            if (css.childNodes.length > 0) {
                if (css.firstChild.nodeValue !== node.nodeValue) {
                    css.replaceChild(node, css.firstChild);
                }
            } else {
                css.appendChild(node);
            }
        })(document.createTextNode(newcss));
    }
}

lessvars = {mycolor: "red"};

maincode = "div { color: @mycolor; }"; // this would be a long string, loaded via AJAX from a LESS file on the server

function compile_less(variables) {
    var variable_less = "";
    for (var variable in variables) {
        variable_less += "@" + variable + ": " + variables[variable] + ";";
    }
    new(less.Parser)({
        optimization: less.optimization
    }).parse(variable_less + maincode, function (e, root) {
        update_css(root.toCSS());
    });
}

compile_less(lessvars);

function set_less_var(name, value) {
    lessvars[name] = value;
    compile_less(lessvars);
}

The "update_css" function above is derived from the "createCSS" function in less.js; the rest I wrote. You can now, at any time, do something like this, to change the color and havethe effects appear immediately in the site content:

set_less_var("mycolor", "green");

(Note that, of course, your "maincode" should probably be loaded from .less files in the background -- I just assigned them here to variables for simplicity.)

Just for fun (as I don't recommend it) -- and to show you that I think my code does what you want -- here's a function that allows you to use the above code to do $("@mycolor").value("black");:

function $(varname) {
    return {
        value: function(val) {
            set_less_var(varname.slice(1), val);
        }
    }
}
like image 30
jamalex Avatar answered Nov 04 '22 04:11

jamalex


If you use the less compiler locally in your browser, there is now a feature to modify less variables:

less.modifyVars({
 '@buttonFace': '#5B83AD',
 '@buttonText': '#D9EEF2'
});
like image 26
Peter Avatar answered Nov 04 '22 02:11

Peter


first of all javascript can't write to a file. The best you'll be able to do is get Javascript to read and edit the XML then post that data to a server-side script to write to file.

well in general people use a different class to address this issue and replace the existing class with the new class rather than go edit the css file itself, which sounds pretty weird to me..

I stumbled upon this blogpost may be this is what you are looking for.. he shows different ways to get news stylesheets based on your requirement.

like image 22
Baz1nga Avatar answered Nov 04 '22 03:11

Baz1nga


If you can do c# and want to do this server-side, the port dotless supports plugins where you implement a visitor pattern to programmatically alter the less ast before it is spit out...

like image 1
Luke Page Avatar answered Nov 04 '22 02:11

Luke Page


it might, just might be a good idéer but if your css / html is right this shouldn't be necessary at all, you just have to css in the right way you could stack your classes if you have alot of "a" tags. If you have very big websites your customers can be quite picky about some little changes like font then its good to customize your outcome and then it is very easy to just css your way out of it, than to make more variables to make your outcome

If you wanna change 1 color your just use your find tool and use find and replace.

please just use some css & comon knowlegde to get your result the more scripts manipulating your website the more load time.

Best regards

SP

like image 1
Simon Dragsbæk Avatar answered Nov 04 '22 04:11

Simon Dragsbæk