Reputation: 662
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.
Upvotes: 4
Views: 143
Reputation: 1854
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).
.blueBorder
, .redBorder
etc and using JavaScript, add and remove classes on elements as needed.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?
Upvotes: 1
Reputation: 9
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.
i.e.
/***
RED THEME
***/
.red-theme .button {}
.red-theme a {}
.red-theme #footer {}
/***
END RED THEME
***/
Upvotes: -1