I’m using new CSSStyleSheet()
to create a custom stylesheet for a specific document where the styles depend on the content.
Within that stylesheet, I add CSS variables with the root selector. Elements in the project will then be selected by an ID and get specific rules that use those variables. The issue is, that I do not know the IDs before, and as such the styles and the selectors have to be created dynamically.
So far it works flawlessly.
The issue that I facing now is that during some specific events, the value of the CSS variables should change. I tried changing it by using insertRule()
with and without index. I also tried replace()
and replaceSync()
. Nothing achieved the desired result.
The question now is, how can I change the root
for this stylesheet?
const CSS = new CSSStyleSheet();
const rootRule = `:root{
--background: red;
}`;
CSS.insertRule(rootRule, 0);
const bodyRule = `body {
background: var(--background);
}`
CSS.insertRule(bodyRule, 1);
document.adoptedStyleSheets = [CSS];
// change color
BUTTON.addEventListener('click', function() {
console.log('button clicked');
const newRule = `:root {
--background: blue;
}`
CSS.insertRule(newRule, 0);
})
<button id="BUTTON">Change background-color</button>
2
Answers
The issue is due to specificity. The new
--background: blue
rule is being applied, but it’s overridden by the existing rule:The quick and dirty fix to this would be to use
!important
, although this is not ideal:A better aproach would be to remove the original rule, before adding the new one. This can be done using
deleteRule()
. Here’s a working example:You are inserting the rule to the CSSStyleSheet correctly (you can tell by adding
console.log(CSS.cssRules[0].cssText);
to the button click to check whether it’s inserted). The problem you are having is that your code is being inserted at index0
, meaning before your existingrootRule
. This is the default position, but you also specify it withCSS.insertRule(newRule, 0);
. And because rules for specific selectors in CSS get overwritten by later rules of the same selectors, therootRule
is still the one getting applied.Change it to
CSS.insertRule(newRule, 2);
and you will see it work: