0

I'm trying to get a list of all active CSS variables on a root element. I've set a couple CSS variables in my code and would like to overwrite these variables with different values in certain cases.

Here is my CSS variable setup:

:root {
  --simpleResponse-background-color: #000;
  --simpleResponse-text-color: #fff;
  --simpleResponse-text-font: 'Arial';
  --simpleResponse-text-font-weight: 'none';
  --simpleResponse-text-font-size: 10
}

.simpleResponse {
  background-color: var(--simpleResponse-background-color);
}

.paragraph {
  color: var(--simpleResponse-text-color);
  font-family: var(--simpleResponse-text-font);
  font-weight: var(--simpleResponse-text-font-weight);
  font-size: var(--simpleResponse-text-font-size);
  margin: 0;
}

I would like to overwrite these variables using document.documentElement.style.setProperty('--variableName', newValue);, but without having to add a line for each variable.

Therefor, I'm looking for a way to retrieve all the set CSS variables from my page, so I can update each variable accordingly using a loop. I've tried using document.documentElement.style and getComputedStyle(root), but none of these options return the variables that are set.

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

  if (!root) {
    throw new Error('Cannot find root.')
  }

  const cssProperties = getComputedStyle(root);

enter image description here

Is it possible to retrieve these values from the page?

Jordi
  • 3,041
  • 5
  • 17
  • 36
  • Are you trying to switch your application theme by changing these variables ? – moolsbytheway Aug 23 '22 at 13:25
  • Yes I am, though the theme I switch into is customisable by the client. So I'm not switching between two static themes. – Jordi Aug 23 '22 at 13:28
  • But you a have defined theme name ? – moolsbytheway Aug 23 '22 at 13:34
  • Do you mean that these variables are persisted in a database and change per user ? – moolsbytheway Aug 23 '22 at 13:36
  • I don't have a specific implementation for that yet, for now its only important that I can switch between using the variables set in the CSS variables or overwrite those with new values using `document.documentElement.style.setProperty`. The switch won't change per user, it should be the same for all. – Jordi Aug 23 '22 at 13:43
  • Are you trying to get the variables set for a specific element, as you say, or all the set variables from your page, which you also say? – A Haworth Aug 23 '22 at 17:01
  • All the variables set on the root of the page is enough. I've updated the question. – Jordi Aug 23 '22 at 17:17

2 Answers2

0

I understand that you are trying to implement theming in your application, but the colors are defined in the user profile.

So i assume that you have a user who can change the theme data from his profile page and the data is persisted in the database.

And i assume that you want the theme to be applied on the page load when user is connected.

Note: this is scratch code, not tested:

You should have an API endpoint that returns to you the user theme data through a GET request

GET /api/v1/user/1001/theme

the theme file should be as follows

{
    "bgColor": "#000",
    "textColor": "#FFF",
    "textFont": "Arial",
    "textFontWeight": "none",
    "textFontSize": 10
}

On the page load fetch the user theme data and inject the variables in you page


// Listen to the page load event
window.addEventListener('load', (event) => {
    // ... and then apply them data
    setThemeData();
});

function setThemeData() { 
    // fetch the theme data from your API
    var themeData = userApi.getThemeData(); // async call so you should handle this

    // create a "style" html tag to inject your css
    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = `:root {
      --simpleResponse-background-color: '${themeData}';
      --simpleResponse-text-color: '${themeData.textColor}';
      --simpleResponse-text-font: '${themeData.textFont}';
      --simpleResponse-text-font-weight: '${themeData.textFontWeight}';
      --simpleResponse-text-font-size: ${themeData.textFontSize}
    }`;

    // Append the style tag to the document HEAD
    document.getElementsByTagName('head')[0].appendChild(style);
}

Your css should be be now applied to the page.

moolsbytheway
  • 1,152
  • 13
  • 20
  • 1
    Hi Moulaye, thanks for your response. Sadly this doesn't really answer my question as I only asked about a way to retrieve the CSS variables, I didn't mention a DB or API in my question. – Jordi Aug 23 '22 at 14:07
  • It's a proposition :) – moolsbytheway Aug 24 '22 at 09:54
0

Found the answer thanks to a post on SO. It reads the :root property from the stylesheet and loops through the properties looking for the -- keyword.

I wasn't a fan of all the filter function chaining, so I'll leave my own solution here.

/**
* Get all simpleResponse CSS variables on the root of the chat widget.
*/
function getCustomizableProperties(): string[] {

  // Find the css sheet that includes the simpleResponse CSS settings.
  const simpleResponseStylesheet = getSimpleResponseStyleSheet();

  if (!simpleResponseStylesheet) {
    console.debug('No customizable properties found. Skipping custom theme rendering.')
    return [];
  }

  // Once found, collect the CSS settings and put them into an array.
  const properties = getSimpleResponseStyleProperties(simpleResponseStylesheet);
  return properties;
}

function getSimpleResponseStyleSheet(): CSSStyleSheet | undefined {
  const styleSheets = Array.from(document.styleSheets);
  const simpleResponseStylesheet = styleSheets.find(styleSheet => {

  if (styleSheet.href === null) {
    const cssRules = Array.from(styleSheet.cssRules);
  
    return cssRules.find(rule => rule.cssText.includes('--simpleResponse'))
  }
  return undefined;
});

return simpleResponseStylesheet;
}

function getSimpleResponseStyleProperties(styleSheet: CSSStyleSheet): string[] {
  const cssRules =  Array.from(styleSheet.cssRules);

  // Casting to any to access properties missing from typing.
  const rootStyleRule: any = cssRules.find((cssRule) => {
    const rule = cssRule as any;
    return rule.selectorText === ':root';
  })

 const rootStyleProperties = Array.from(rootStyleRule.style) as string[];

 return rootStyleProperties.filter(prop => prop.includes('--simpleResponse'));
}

This returns an array of CSS variables

0: "--simpleResponse-background-color"
1: "--simpleResponse-text-color"
2: "--simpleResponse-text-font"
3: "--simpleResponse-text-font-weight"
4: "--simpleResponse-text-font-size"
Jordi
  • 3,041
  • 5
  • 17
  • 36