简体   繁体   中英

Maintainable CSS for large number of themes

I have to implement themes for a web app, which the user will be able to switch between on the fly. The designers want about 20 different font colors and background colors. Is there a way to do this without creating 20 different .css files? That would be a maintainability nightmare.

I'm thinking JavaScript will probably have to be used. I'm currently planning on appending the .css file as a tag to the DOM, and then doing some string replacement on the color codes when the user changes the Theme. I was hoping to find a better solution, as this seems like a pretty bad hack.

With SASS you can use variables in CSS, you'll just have to change it in 1 file and it compiles with updated variables you should probably try that.

Cheers

Broadly speaking, I know of two ways to go about changing a site's style while using one css source (which may or not require multiple files).

  1. Define lots of classes .blueBorder , .redBorder etc and using JavaScript, add and remove classes on elements as needed.
  2. Or define classes and using JavaScript again, change the definition of those classes.

It is possible to use a mixture of both approaches, though I'm not sure why one would want to do that.

Here's a JSFIDDLE using the 2nd approach.

Rather than using jQuery, which would make the coding somewhat simpler (I guess) due to the power of its selectors, I decided to use a pure JavaScript solution. The meat of which, however, I did not write. The function getCSSRule , by Patrick Hunlock, can be found here . Each line of the function is commented. However, I've removed the comments in the Fiddle only because of wrapping issues.

The function returns a pointer to a CSS rule which then can be easily manipulated. For example:

    // get a class rule (in production code check return value for valid result)
    var r = getCSSRule('.primaryColor');
    // change its definition
    r.style.backgroundColor = "#f00";

All elements which have the class primaryColor assigned to them will have their background color change to red (#f00) at the point the 2 above lines execute. There is nothing else required.

NOTE the names of the nodes in the style sheet are not exactly the same as the CSS rule ( backgroundColor vs. background-color ). I know a lot of folks here do not like w3Schools.com site, but when looking for a style object reference, that's where I found one. You can find it here

And here is the code:

Starting CSS Styles:

    <style type="text/css">

        #box1 {width: 50%; height: 200px; margin: 40px auto;  padding-top: 20px;}
        #box2 {width: 50%; height: 120px; margin: 20px auto 20px; padding: 10px;}
        .primaryColor {background-color: #f00;}
        .primaryBorder {border: 10px solid #000;}
        .secondaryColor {background-color: #ff0;}
        .secondaryBorder {border: 5px solid #fff;}
        .t {color: #f00;}
    </style>

HTML:

<div id="box1" class="primaryColor primaryBorder">
    <div id="box2" class="secondaryColor secondaryBorder"><p class="t">Theme Demonstration</p>
    </div>
</div>

<form style="margin: 40px auto; width:50%">
    <div role="radio" style="text-align:center" aria-checked="false">
    <input type="radio" name="theme" CHECKED value="theme1" onClick="setThemeOne()" >Theme 1
    <input type="radio" name="theme" value="theme2" onClick="setThemeTwo()" >Theme 2
    <input type="radio" name="theme" value="theme3" onClick="setThemeThree()">Theme 3
    </div>
</form>

And the good stuff, JavaScript:

function getCSSRule(ruleName, deleteFlag) {
     ruleName=ruleName.toLowerCase();
     if (document.styleSheets) {
            for (var i=0; i<document.styleSheets.length; i++) {
                 var styleSheet=document.styleSheets[i];
                 var ii=0;
                 var cssRule=false;
                 do {
                        if (styleSheet.cssRules) {
                             cssRule = styleSheet.cssRules[ii];
                        } else {
                             cssRule = styleSheet.rules[ii];
                        }
                        if (cssRule)  {
                             if (cssRule.selectorText.toLowerCase()==ruleName) {
                                    if (deleteFlag=='delete') {
                                         if (styleSheet.cssRules) {
                                                styleSheet.deleteRule(ii);
                                         } else {
                                                styleSheet.removeRule(ii);
                                         }
                                         return true;
                                    } else {
                                         return cssRule;
                                    }
                             }
                        }
                        ii++;
                 } while (cssRule)
            }
     }
     return false;
}

function setThemeOne() {
    var r = getCSSRule('.primaryColor');
    r.style.backgroundColor = "#f00";
    r = getCSSRule('.primaryBorder');
    r.style.border = "10px solid #000;";
    r = getCSSRule('.secondaryColor');
    r.style.backgroundColor = "#ff0";
    r = getCSSRule('.secondaryBorder');
    r.style.border = "5px solid #fff";
    r = getCSSRule('.t');
    r.style.color = "#000";
};


function setThemeTwo() {
    var r = getCSSRule('.primaryColor');
    r.style.backgroundColor = "#ff0";
    r = getCSSRule('.primaryBorder');
    r.style.border = "10px solid #ccc;";
    r = getCSSRule('.secondaryColor');
    r.style.backgroundColor = "#f00";
    r = getCSSRule('.secondaryBorder');
    r.style.border = "5px solid #000";
    r = getCSSRule('.t');
    r.style.color = "#ccc";

};


function setThemeThree() {
    var r = getCSSRule('.primaryColor');
    r.style.backgroundColor = "#ccc";
    r = getCSSRule('.primaryBorder');
    r.style.border = "10px solid #000;";
    r = getCSSRule('.secondaryColor');
    r.style.backgroundColor = "#000";
    r = getCSSRule('.secondaryBorder');
    r.style.border = "5px solid #fff";
    r = getCSSRule('.t');
    r.style.color = "#fff";

};

Note about compatibility

This specific example I've tested in IE11 and current version of Chrome . However, I've had similar code deployed on a site since about 2011 and at that time the site supported browsers back to IE7 or IE8 (don't recall) and no one ever reported an issue. But I see now that I did patch the getCSSRule function for Chrome . (I did not have to do that for the current version.) Here's the patch:

 if (cssRule){  //If we found a rule...
   // [KT] 04/24/2012 - added condition to check for undefined selector for Chrome
if ((cssRule.selectorText != undefined) && cssRule.selectorText.toLowerCase()==ruleName)){//match rule Name?

CSS preprocessors always win here.
I would avoid a separate stylesheet with css and just define all your colours in one place with good commenting. Its going to get messy with css and that amount of colour schemes. ie

/***
RED THEME
***/
.red-theme .button {}
.red-theme a {}
.red-theme #footer {}
/***
END RED THEME
***/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM