How to change CSS :root color variables in JavaScript

JavascriptHtmlCss

Javascript Problem Overview


Alright, I'm creating a system for my webpage that allows users to change the theme. How I want to accomplish this is by having all the colors as variables, and the colors are set in the :root part of the CSS.

What I want to do is change those colors via JavaScript. I looked up how to do it, but nothing that I attempted actually worked properly. Here's my current code:

CSS:

:root {
  --main-color: #317EEB;
  --hover-color: #2764BA;
  --body-color: #E0E0E0;
  --box-color: white;
}

JS:

(Code to set the theme, it's ran on the click of a button) - I didn't bother adding the :root change to the other 2 themes since it doesn't work on the Dark theme

function setTheme(theme) {
  if (theme == 'Dark') {
    localStorage.setItem('panelTheme', theme);
    $('#current-theme').text(theme);
    $(':root').css('--main-color', '#000000');
  }
  if (theme == 'Blue') {
    localStorage.setItem('panelTheme',  'Blue');
    $('#current-theme').text('Blue');
    alert("Blue");
  }
  if (theme == 'Green') {
    localStorage.setItem('panelTheme', 'Green');
    $('#current-theme').text('Green');
    alert("Green");
  }
}

(Code that is ran when the html is loaded)

function loadTheme() {
  //Add this to body onload, gets the current theme. If panelTheme is empty, defaults to blue.
  if (localStorage.getItem('panelTheme') == '') {
    setTheme('Blue');
  } else {
    setTheme(localStorage.getItem('panelTheme'));
    $('#current-theme').text(localStorage.getItem('panelTheme'));
  }
}

It shows the alert, but does not actually change anything. Can someone point me in the right direction?

Javascript Solutions


Solution 1 - Javascript

Thank you @pvg for providing the link. I had to stare at it for a little to understand what was going on, but I finally figured it out.

The magical line I was looking for was this:

document.documentElement.style.setProperty('--your-variable', '#YOURCOLOR');

That did exactly what I wanted it to do, thank you very much!

Solution 2 - Javascript

For those who want to modify the actual style sheet the following works:

var sheet = document.styleSheets[0];
sheet.insertRule(":root{--blue:#4444FF}");

More info at here: <https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule>

Solution 3 - Javascript

To use the values of custom properties in JavaScript, it is just like standard properties.

// get variable from inline style
element.style.getPropertyValue("--my-variable");

// get variable from wherever
getComputedStyle(element).getPropertyValue("--my-variable");

// set variable on inline style
element.style.setProperty("--my-variable", 4);

Solution 4 - Javascript

I came here looking how to toggle the :root color-scheme with JavaScript, which sets the browser to dark mode (including the scroll bars) like this:

:root {
    color-scheme: dark;
}

using the @Daedalus answer above, this is how I implemented my dark mode detection from user preference:

	const userPrefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
	const preferredTheme = userPrefersDarkMode ? 'dark' : 'light';
	document.documentElement.style.setProperty("color-scheme", preferredTheme);

or with saved toggle:

	const savedTheme = localStorage.getItem('theme');
	if (savedTheme == 'dark') {
		thisTheme = 'light'
	}
	else {
		thisTheme = 'dark'; // the default when never saved is dark
    }
	document.documentElement.style.setProperty("color-scheme", thisTheme);
	localStorage.setItem('theme', thisTheme);

see also the optional meta tag in the header:

<meta name="color-scheme" content="dark light">

Solution 5 - Javascript

old jquery magic still working too

$('#yourStyleTagId').html(':root {' +
    '--your-var: #COLOR;' +
'}');

Solution 6 - Javascript

Read only, retrieve all CSS --root rules in an array, without using .getComputedStyle().

This may allow to retrieve values before full DOM content load, to create modules that use global root theme variables, but not via CSS. (canvas context...)

/* Retrieve all --root CSS variables
*  rules into an array
*  Without using getComputedStyle (read string only)
*  On this example only the first style-sheet
*  of the document is parsed
*/
console.log(

  [...document.styleSheets[0].rules]
   .map(a => a.cssText.split(" ")[0] === ":root" ?
    a.cssText.split("{")[1].split("}")[0].split("--") : null)
   .filter(a => a !== null)[0]
   .map(a => "--"+a)
   .slice(1)

)

:root {
    --gold: hsl(48,100%,50%);
    --gold-lighter: hsl(48,22%,30%);
    --gold-darker: hsl(45,100%,47%);
    --silver: hsl(210,6%,72%);
    --silver-lighter: hsl(0,0%,26%);
    --silver-darker: hsl(210,3%,61%);
    --bronze: hsl(28,38%,67%);
    --bronze-lighter: hsl(28,13%,27%);
    --bronze-darker: hsl(28,31%,52%);
}

Solution 7 - Javascript

TL;DR

A solution to the problem could be the below code:

const headTag = document.getElementsByTagName('head')[0];
const styleTag = document.createElement("style");

styleTag.innerHTML = `
:root {
    --main-color: #317EEB;
    --hover-color: #2764BA;
    --body-color: #E0E0E0;
    --box-color: white;
}
`;
headTag.appendChild(styleTag);

Explanation:

Although @Daedalus answer with document.documentElement does the job pretty well, a slightly better approach is to add the styling into a <style> HTML tag (solution proposed).

If you add document.documentElement.style then all the CSS variables are added into the html tag and they are not hidden:

CSS style inline to html

On the other hand, with the proposed code:

const headTag = document.getElementsByTagName('head')[0];
const styleTag = document.createElement("style");

styleTag.innerHTML = `
:root {
    --main-color: #317EEB;
    --hover-color: #2764BA;
    --body-color: #E0E0E0;
    --box-color: white;
}
`;
headTag.appendChild(styleTag);

the HTML tag will be cleaner and if you inspect the HTML tag you can also see the :root styling as well.

HTML tag without styles

Solution 8 - Javascript

I think this is cleaner and easier to remember. to set/get css variables to/from :root

const root = document.querySelector(':root');

// set css variable
root.style.setProperty('--my-color', 'blue');

// to get css variable from :root
const color = getComputedStyle(root).getPropertyValue('--my-color'); // blue

Example: setting multiple variables all at once

const setVariables = vars => Object.entries(vars).forEach(v => root.style.setProperty(v[0], v[1]));
const myVariables = {
  '--color-primary-50': '#eff6ff',
  '--color-primary-100': '#dbeafe',
  '--color-primary-200': '#bfdbfe',
  '--color-primary-300': '#93c5fd',
  '--color-primary-400': '#60a5fa',
  '--color-primary-500': '#3b82f6',
  '--color-primary-600': '#2563eb',
  '--color-primary-700': '#1d4ed8',
  '--color-primary-800': '#1e40af',
  '--color-primary-900': '#1e3a8a',
};
setVariables(myVariables);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDaedalusView Question on Stackoverflow
Solution 1 - JavascriptDaedalusView Answer on Stackoverflow
Solution 2 - JavascriptMichaelView Answer on Stackoverflow
Solution 3 - JavascriptiProDevView Answer on Stackoverflow
Solution 4 - JavascriptgojimmypiView Answer on Stackoverflow
Solution 5 - Javascriptv2pView Answer on Stackoverflow
Solution 6 - JavascriptNVRMView Answer on Stackoverflow
Solution 7 - JavascriptJohn SkoumbourdisView Answer on Stackoverflow
Solution 8 - JavascriptPakpoom TiwakornkitView Answer on Stackoverflow