Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a function for accessing variable values. #85

Closed
Shepard opened this issue Aug 6, 2019 · 1 comment
Closed

Provide a function for accessing variable values. #85

Shepard opened this issue Aug 6, 2019 · 1 comment
Labels

Comments

@Shepard
Copy link

Shepard commented Aug 6, 2019

Hi,

I use your library to allow custom styling of a page at runtime. It works great for that, thanks for your work!

In my CSS I define some variables for color values in the :root scope and they contain default colors. Then in the JS I check with the server if different colors have been set by the user and if so I override the variable values with those values using a simple call like this:

cssVars({
	variables: {
		'key-color': keyColor,
		...
	}
});

There's some dependencies between the different color values though so if color X is not set it will fall back to color Y etc. To make this work I need the default values of the CSS variables in my JS code and then apply some logic to calculate the new values depending on which colors the user set.

So far I just duplicate the default values that are set in the CSS in my JS code. However it would be nicer if I could access them before the cssVars call to override them. Maybe something like:
const defaultKeyColor = cssVars.getValue('key-color', document.documentElement);

In modern browsers this would basically just do a call like this:
element.style.getPropertyValue("--" + var); or like this: getComputedStyle(element).getPropertyValue("--" + var); (not entirely sure which one is correct here).
In legacy browsers it would have to do the usual CSS loading and parsing step I guess.

Is this a feature that you would deem generally useful and would be willing to implement?

Regards,
Simon

@jhildenbiddle
Copy link
Owner

jhildenbiddle commented Aug 15, 2019

Hi @Shepard --

Good to hear your finding the ponyfill useful!

It's unlikely I would add a separate method for getting custom property names/values because this information is already available using the options.onComplete callback:

cssVars({
  onComplete(cssText, styleElms, cssVariables, benchmark) {
    console.log(cssVariables);
  }
});

// => { "--mycolor": "red" }

The red flag in the scenario you describe is the possibility of generating legacy-compatible CSS twice: once for "default" values, then again if "custom" values are returned from the server. You should avoid doing this by using one of the following approaches:

Option 1: Separate Stylesheets

  1. Store your default custom property values in a separate stylesheet
  2. Call the ponyfill, but only include the stylesheet with default custom property values
  3. In the onComplete callback, check the server for custom values
  4. Run the ponyfill again within the onComplete callback with whatever options you need

By limiting the CSS being processed to a stylesheet containing only custom property values in :root selectors (and no other CSS), the ponyfill is able to return custom property values without doing any extra work. Essentially, steps 1+2 serve as the "function for accessing variable values" you are looking for, but you had to help out by isolating your custom property values in a separate stylesheet. Here's a sample implementation:

<!-- index.html -->

<link href="variables.css" rel="stylesheet">
<link href="main.css" rel="stylesheet">
/* variables.css */

:root {
  --key-color: red;
}
/* script.js */

// 1st call: Get custom property names & values
cssVars({
  include: 'link[href="variables.css"]',
  onComplete(cssText, styleElms, cssVariables, benchmark) {
    const customValues = getValuesFromServer();

    if (customValues) {
      // Do stuff...
    }

    // 2nd call: Transform CSS
    cssVars({
      variables: (customValues || {})
    });
  }
});

Option 2: Parse :root-level declarations with onSuccess callback

If you can't or don't wan to store your default custom property values in a separate stylesheet, you can parse only the :root-level declarations in all stylesheets using the options.onSuccess callback. This callback fires after each stylesheets is read and allows you to modify the CSS content processed by the ponyfill. With this approach, using both the onSuccess and onComplete callbacks provides the "function for accessing variable values" you are looking for.

// Matches all `:root`-level declarations in CSS
const reCSSRootDecls = /(?::root(?![.:#])[\s,]*[^{]*{\s*[^}]*})/gm;

// 1st call: Get custom property names & values
cssVars({
  updateDOM: false,
  onSuccess(cssText, elm, url) {
    const cssRootDecls = cssText.match(reCSSRootDecls);

    if (cssRootDecls) {
      return cssRootDecls.join('\n');
    }
    else {
      return false;
    }
  },
  onComplete(cssText, styleElms, cssVariables, benchmark) {
    const customValues = getValuesFromServer();

    if (customValues) {
      // Do stuff...
    }

    // 2nd call: Transform CSS
    cssVars({
      variables: (customValues || {})
    });
  }
});

Of the two options above, the first is preferable because it limits the number of stylesheets that the ponyfill has to process on the the first call. The second is preferable if (as states) you can't or do not want to isolate your default custom properties into a separate file.

Hope this helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants