Skip to content

Instantly share code, notes, and snippets.

@robindotis
Last active March 3, 2023 08:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robindotis/54786199b5d86f5085e5c55337d6e0d0 to your computer and use it in GitHub Desktop.
Save robindotis/54786199b5d86f5085e5c55337d6e0d0 to your computer and use it in GitHub Desktop.
Theme picker, using object oriented code
/*
* OORSSS: Object oriented really simple stylesheet switcher
*
* This script will generate buttons to switch stylesheets.
* It will use localstorage to store the users choice between sessions.
*
* STEP 1
* ======
* To get started you need to create a new themePicker, providing:
* - the id of the element within which the picker will be created [Required]
* - provide the path to the stylesheets [optional]
*
* eg
* let themeSwitch = new themePicker("themeSwitch","/assets/css/");
*
* STEP 2
* ======
* Add the themes you wish to make available to the theme picker, providing:
* - the theme name (will be stored in localstorage) [Required]
* - the theme's css file [Required]
* - whether this is the site's default theme [optional, default false]
*
* eg
* themeSwitch.addTheme(new theme("Default","default.css",true));
*
* STEP 3
* ======
* Instantiate the picker:
* themeSwitch.createPicker();
*
* This will create:
* - the HTML buttons within the defined HTML element
* - the non default stylesheet links with rel "alternate stylesheet"
*
* Note: The system assumes the default stylesheet already exists.
* This ensures the site will render in the default styles
* without JavaScript.
*
*/
class theme {
constructor(name, css, dflt = false) {
this.name = name;
this.css = css;
this.dflt = dflt;
}
createButton(){
/*Create the button element*/
const btn = document.createElement("button");
btn.setAttribute('type','button');
btn.setAttribute('onclick',"themeSwitch.setTheme('" + this.name + "')");
btn.appendChild(document.createTextNode(this.name));
return btn;
}
createCssLink(){
/*Create the link element*/
const lnk = document.createElement("link");
lnk.setAttribute('rel','alternate stylesheet');
lnk.setAttribute('href',this.css);
lnk.setAttribute('media','all');
return lnk;
}
}
class themePicker {
#CSS_NAKED_THEME = "Naked"; //Also used for css file, lowercase. eg "Naked" => naked.css
#CSS_NAKED_DAY_OF_MONTH = 9;
#CSS_NAKED_MONTH = 3; //3 = April
#themes = [];
#cssFiles = [];
defaultTheme;
currentTheme;
constructor(id, path, text = "", forceNakedDay = false){
this.id = id;
this.path = path;
this.text = text;
const today = new Date();
if (forceNakedDay && (today.getMonth() == this.#CSS_NAKED_MONTH && today.getDate() == this.#CSS_NAKED_DAY_OF_MONTH)) {
this.forceNakedDay = forceNakedDay;
}
}
addNakedTheme() {
themeSwitch.addTheme(new theme(this.#CSS_NAKED_THEME, this.#CSS_NAKED_THEME.toLocaleLowerCase() + ".css"));
}
addTheme(theme) {
this.#cssFiles.push(theme.css);
theme.css = this.path + theme.css;
if(theme.dflt){
/* Default theme is also current theme unless set later */
this.defaultTheme = this.currentTheme = theme;
}
if(theme.name == localStorage.getItem('theme')) {
this.currentTheme = theme;
}
this.#themes.push(theme);
}
setTheme(themeName){
const styles = document.querySelectorAll('link[rel~="stylesheet"]');
/* set current theme */
for(let i=0; i<this.#themes.length; i++){
if(this.#themes[i].name == themeName) {
this.currentTheme = this.#themes[i];
break;
}
}
/* insert correct rel attribute (alternate for inactive styles) */
for (let i = 0; i < styles.length; ++i) {
/* returns true if the href contains any of the strings in the themes array */
/* this if statement ensures we only alter stylesheets used by the themes*/
if(this.#cssFiles.some(v => styles[i].href.includes(v))) {
if(styles[i].href.endsWith(this.currentTheme.css)) {
styles[i].rel = "stylesheet";
}
else {
styles[i].rel = "alternate stylesheet";
}
}
//if naked style, remove all fixed stylesheets by setting them to alternate
//otherwise, make sure they added by removing the alternate setting
if(styles[i].href.endsWith("fonts.css") ||
styles[i].href.endsWith("structure.css") ||
styles[i].href.endsWith("style.css")) {
if(themeName == this.#CSS_NAKED_THEME || this.forceNakedDay) {
styles[i].rel = "alternate stylesheet";
}
else {
styles[i].rel = "stylesheet";
}
}
}
/* Disable the button for the currently selected style */
const btns = document.getElementById(this.id).querySelectorAll('button');
for (let i = 0; i < btns.length; ++i) {
if(btns[i].onclick.toString().includes(this.currentTheme.name)) {
btns[i].setAttribute("disabled","");
}
else {
btns[i].removeAttribute("disabled");
}
}
if (!this.forceNakedDay) { //don't store theme if forced
localStorage.setItem("theme",this.currentTheme.name);
}
}
toogleButtonState(btnName, disabled = false) {
const btn = document.querySelector('button[onclick*="' + btnName + '"]');
btn.setAttribute("disabled","");
}
createPicker(){
const dfltLink = document.querySelector('link[href*="' + this.defaultTheme.css + '"]');
if (this.forceNakedDay) {
this.setTheme(this.#CSS_NAKED_THEME);
}
else {
if(this.text.length > 0) {
const spn = document.createElement("span");
spn.appendChild(document.createTextNode(this.text));
document.getElementById(this.id).appendChild(spn);
}
for(let i=0; i<this.#themes.length; i++){
/* Add all buttons into the HTML element with the theme id */
document.getElementById(this.id).appendChild(this.#themes[i].createButton());
/* Create all the HTML links, except for the default one */
if(this.defaultTheme.css != this.#themes[i].css) {
const lnk = document.createElement("link");
lnk.setAttribute('rel','alternate stylesheet');
lnk.setAttribute('href',this.#themes[i].css);
lnk.setAttribute('media','all');
dfltLink.after(lnk);
}
}
if(localStorage.getItem('theme') === null) {
this.toogleButtonState(this.defaultTheme.name, true);
}
else {
this.setTheme(localStorage.getItem('theme'));
}
}
}
}
let themeSwitch = new themePicker("themeSwitch","/assets/css/","Choose a style:", true, true);
themeSwitch.addTheme(new theme("Default","default.css",true));
themeSwitch.addTheme(new theme("High Contrast","high.css"));
themeSwitch.addTheme(new theme("Miami","miami.css"));
themeSwitch.addTheme(new theme("Grey","olden.css"));
themeSwitch.addTheme(new theme("Plain","plain.css"));
themeSwitch.addNakedTheme();
themeSwitch.createPicker();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment