111

I have these CSS variables to control the colors of my project so I can do theming.

html {
    --main-background-image: url(../images/starsBackground.jpg);
    --main-text-color: #4CAF50;
    --main-background-color: rgba(0,0,0,.25);
    --beta-background-color: rgba(0,0,0,.85);
}

However no matter how I try to change the attribute(the two commented lines tried separately), the closest I get is returning not a valid attribute.

function loadTheme() {
    var htmlTag = document.getElementsByTagName("html");
    var yourSelect = document.getElementById( "themeSelect" );
    var selectedTheme = ( yourSelect.options[ yourSelect.selectedIndex ].value );
    // htmlTag[0].setAttribute('--main-text-color', '#FFCF40');
    // $("html").css("--main-text-color","#FFCF40");
}

the error message

4
  • broken-links.com/2014/08/28/… Commented Dec 28, 2016 at 22:44
  • 3
    Yes, those CSS variables use a 'native' pre-processor (basically, they eventually get converted into the real property/attribute beforehand). BTW, this feature is not widely supported by all browsers (only FF, from the article. I dunno about other browsers) - in any case, your syntax is incorrect if you want to manipulate them in JS. You need to manipulate properties (not attributes, it seems) - so try htmlTag[0].styles.setProperty('--main-text-color', '#FFCF40'); Commented Dec 28, 2016 at 22:54
  • I copied your answer in and now the error is that it can not set property of undefined. Could I bother you for a fiddle example? Commented Dec 28, 2016 at 23:12
  • Checkout my answer for widely-supported ways of doing something similar OR even better, checkout Brett's answer for how to solve your question (with limited browser support) stackoverflow.com/a/41371037/600486 Commented Dec 28, 2016 at 23:24

8 Answers 8

145

Turns out changing CSS variables is possible using the el.style.cssText property, or el.style.setProperty or el.setAttribute methods. In your code snippets el.setAttribute is incorrectly used, which is causing the error you encountered. Here's the correct way:

document.documentElement.style.cssText = "--main-background-color: red";

or

document.documentElement.style.setProperty("--main-background-color", "green");

or

document.documentElement.setAttribute("style", "--main-background-color: green");

Demo

The following demo defines a background color using a CSS variable, then changes it using the JS snippet 2 seconds after loading.

window.onload = function() {
  setTimeout(function() {
    document.documentElement.style.cssText = "--main-background-color: red";
  }, 2000);
};
html {
    --main-background-image: url(../images/starsBackground.jpg);
    --main-text-color: #4CAF50;
    --main-background-color: rgba(0,0,0,.25);
    --beta-background-color: rgba(0,0,0,.85);
}

body {
  background-color: var(--main-background-color);
}

This will only work in browsers supporting CSS variables obviously.

Sign up to request clarification or add additional context in comments.

3 Comments

This will overwrite the existing inline styles.
yes you need to modify the prop like this: document.querySelector("html").style.setProperty('width', fixed.width + 'px') however I can't get it to work
I'm guessing that html.style.setProperty("--main-background-color", "green"); is the preferred way to do it. Using setAttribute('style', ..) will overwrite any other inline styles and the same goes for style.cssText = ...
44

If you are using :root:

:root {
    --somevar: black;
}

It will be documentElement.

document.documentElement.style.setProperty('--somevar', 'green');

Comments

37

The native solution

The standard methods to get/set CSS3 variables are .setProperty() and .getPropertyValue().

If your Variables are Globals (declared in :root), you can use the following, for getting and setting their values.

// setter
document.documentElement.style.setProperty('--myVariable', 'blue');
// getter
document.documentElement.style.getPropertyValue('--myVariable');

However the getter will only return the value of a var, if has been set, using .setProperty(). If has been set through CSS declaration, will return undefined. Check it in this example:

let c = document.documentElement.style.getPropertyValue('--myVariable');
alert('The value of --myVariable is : ' + (c?c:'undefined'));
:root{ --myVariable : red; }
div{ background-color: var(--myVariable); }
  <div>Red background set by --myVariable</div>

To avoid that unexpected behavior you have to make use of the getComputedStyle()method , before calling .getPropertyValue(). The getter will then , look lik this :

getComputedStyle(document.documentElement,null).getPropertyValue('--myVariable');

In my opinion, accessing CSS variables should be more simple, fast, intuitive and natural...


My personal approach

I've implemented CSSGlobalVariables a tiny (<3kb) javascript module wich automatically detects and packs into an Object, all the active CSS global variables in a document, for easier acces & manipulation.

import {CSSGlobalVariables} from './css-global-variables.js';
let cssVar = new CSSGlobalVariables();
// set the CSS global --myColor value to "green"
cssVar.myColor = "green";

Any change applied to the Object properties, is translated automatically to the CSS variables, and viceversa.

Available in : https://github.com/colxi/css-global-variables

2 Comments

when you say "any change applied" how does that work? do you monitor the object? nice work!
@Tomachi there is a Proxy tracking the changes applied to the object.
18

You can simply use the standard way of setting arbitrary CSS properties: setProperty

document.body.style.setProperty('--background-color', 'blue');
body {
  --background-color: red;
  background-color: var(--background-color);
}

1 Comment

for some reason I can't get this to work when the var is used in a transition like so: left: calc(10% - (var(--width) / 2));
9

For anyone who is struggling with it, if your CSS variable is a sentence you need to wrap it in qoutes.

:root {
  --my-css-var: 'Hello Person!';
}

.selector:after {
    content: var(--my-css-var);
}    

This does not work:

let myVar = 'Hi Person! (doesnt work)';
document.getElementsByTagName('html')[0].style.setProperty('--my-css-var', myVar);

But this does:

let myVar = 'Hi Person! (works)';
document.getElementsByTagName('html')[0].style.setProperty('--my-css-var', '"' + myVar + '"');

1 Comment

this was really helpful
1

You could add something like below (without using class variables)

function loadTheme() {
  var htmlTag = document.getElementById("myDiv");
  var yourSelect = document.getElementById("themeSelect");
  var selectedTheme = (yourSelect.options[yourSelect.selectedIndex].value);
  console.log("selected theme: " + selectedTheme);

  // reset class names
  htmlTag.className = '';
  // add selected theme
  htmlTag.className = 'theme' + selectedTheme;
}
.theme1 {
  color: blue;
}
.theme2 {
  color: red;
}
<div id="myDiv">
  test
</div>
<select id="themeSelect" onChange="loadTheme()">
  <option value="1">Theme 1</option>
  <option value="2">Theme 2</option>
</select>

2 Comments

Downvoted because it doesn't answer the question at all.
@Ced wait! are you saying that my answer does not address the OP's need to do theming? - I am not sure I'd agree with you. Just because I answer in a widely-supported ways (instead of using a limited-browser-support approach) - please read: How To Answer (stackoverflow.com/help/how-to-answer) -> Answers can be "don't do this" but "try this instead".... shrugs
0

It would probably be easier to define classes in your CSS that contain the various theme styles (.theme1 {...}, .theme2 {...}, etc) and then change the class with JS based on the selected value.

2 Comments

This is more of a comment than an answer
I didn't think about that. I'll give that a shot and get back to you
-1

The ::placeholder selector is not available in javascript, however a walk around this, would be to declare a css variable in your style sheet, this variable would be available to the DOM and can be manipulated using javascript.

Example.

let root = document.querySelector(':root');
function setPlaceholderColor(){
root.style.setProperty('--placeholder', yellow);
}
:root{
  --placeholder: red;
}

input::placeholder{
  color: var(--placeholder)
}
<input type="text" placeholder ="Enter Name" onchange="setPlaceholderColor()" />

2 Comments

The code given error : Uncaught ReferenceError: yellow is not defined
need proper testing of code

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.