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

output multi references in one value e.g. --c-hsl: hsl(var(--c-h), var(--c-s), 11%);? #565

Closed
uptonking opened this issue Mar 9, 2021 · 3 comments

Comments

@uptonking
Copy link

uptonking commented Mar 9, 2021

  • some property value may consist of several parts, such as border, padding.
  • how can we combine outputReferences and transitive transform to output a value referencing multiple css variables
  • my goal is to output a css variable like --color-btn-hsl: hsl(var(--color-btn-h), var(--color-btn-s), 11%);

tokens source:

{
  "color": {    
    "btn": {
      "h": { "value": 10, "outputAsItIs": true },
      "s": { "value": "12%", "outputAsItIs": true },
      "l": { "value": "12%", "outputAsItIs": true },
      "hsl": {
        "value": {
          "h": "color.btn.h",
          "s": "color.btn.s",
          "l": "color.btn.l"
        }
      }
    }
  }
}
  • problem is simplified here:
    • only output css files with css variables, without considering other platform formats
    • with outputAsItIs option, color/css transform will skip the h, s, l prop value.

expected output:

:root {
  --color-btn-h: 10;
  --color-btn-s: 12%;
  --color-btn-l: 12%;
  --color-btn-hsl: hsl(var(--color-btn-h), var(--color-btn-s), 11%);
}
@uptonking
Copy link
Author

uptonking commented Mar 10, 2021

🙃 It took me the whole day to solve this problem 🙃

a possible custom formatter is here (works but not elegant) :

StyleDictionary.registerFormat({
  name: 'css/variables',
  formatter: function ({ dictionary, options }) {
    return `${this.selectorName} {
      ${dictionary.allProperties
        .map((prop) => {
          let value = prop.value;
          const ov = prop.original.value;

          const propIsColorHsl =
            prop.attributes.category === 'color' &&
            ov.hasOwnProperty('h') &&
            ov.hasOwnProperty('s') &&
            ov.hasOwnProperty('l');

          function hslPropUsesReference(p) {
            return (
              dictionary.getReference(p.original.value.h.toString()) ||
              dictionary.getReference(p.original.value.s) ||
              dictionary.getReference(p.original.value.l)
            );
          }

         function outputHslProp(valStr, fallback) {
            const fbOut = fallback !== undefined ? `, ${fallback}` : '';
            // if (valStr.startsWith('{')) {
            if (dictionary.getReference(valStr.toString())) {
              return `var(--${dictionary.getReference(valStr).name}${fbOut})`;
            }
            return valStr;
          }

          if (propIsColorHsl && hslPropUsesReference(prop)) {
            const hslStrBeforeHex = `hsl(${value.h},${value.s},${value.l})`;
            value = hslToHex(hslStrBeforeHex);
          }

          if (options.outputReferences) {
            if (propIsColorHsl) {
              if (!hslPropUsesReference(prop)) {

                return `  --${prop.name}: ${value};`;
              }

              const hOut = outputHslProp(prop.original.value.h, prop.value.h);
              const sOut = outputHslProp(prop.original.value.s, prop.value.s);
              const lOut = outputHslProp(prop.original.value.l, prop.value.l);

              // e.g. --color-hsl: hsl(var(--color-btn-h), var(--color-btn-s), 11%);
              return `  --${prop.name}: hsl(${hOut}, ${sOut}, ${lOut});`;
            }

			// if (prop.original.value is not hsl && prop.value is hsl)

            if (dictionary.usesReference(prop.original.value)) {
                 const reference = dictionary.getReference(prop.original.value);

              value = reference.name;
      
              return `  --${prop.name}: var(--${value}, ${prop.value});`;
            }
          }
          return `  --${prop.name}: ${prop.value};`;
        })
        .join('\n')}
    }`;
  },
});

any better idea?

@uptonking uptonking changed the title How to output multi references in one value, e.g. output -color-btn-hsl: hsl(var(--color-btn-h), var(--color-btn-s), 11%); output multi references in one value e.g. --c-hsl: hsl(var(--c-h), var(--c-s), 11%);? Mar 10, 2021
@dbanksdesign
Copy link
Member

I think what you have makes sense. I started writing out an example, but it would be fairly similar to what you have.

A few minor notes:

  • I don't think you need the hslPropUsesReference function. dictionary.usesReference() handles if the value is an object, so you could just pass the original value: dictionary.usesReference(prop.original.value)
  • I like how you have fallbacks when you use the reference!
  • I think in your source example, you are missing curly braces in the references

@uptonking
Copy link
Author

uptonking commented Mar 12, 2021

@dbanksdesign

I have used this solution in my project, but a few more problems occurred.

  • a closely related bugfix to the code above:
    • if a new token value references a special token whose value is hsl containing references, then a more if should be added as in the comment(I have updated the comment in the code above)
    • if (prop.original.value is not hsl && prop.value is hsl) output calculated color hex value

  • when I write more and more tokens with css vars, my most frequent use case is to output original str and replace referenced part {} with var()
  • for example
    • source: { "size": { "shadow": { "value": "0 0.2rem 0.4rem {color.shadow}" } } }
    • output: --size-shadow: 0 0.2rem 0.4rem var(--color-shadow);
  • I think a utility function should be provided by style-dictionary!!!
  • this use case is so frequent;
  • but I haven't found a good solution for utility function;
  • my solution is to write a custom formatter for every name:
  • if(prop.name === size-shadow/...) , prop.original.value.replace({color.shadow/...} , var(--${dictionary.getReference(prop.original.value).name}))
  • this is just a simple use case for only one referenced value, what abou 2,3...?

  • it seems impossible to write a general formatter even for color tokens output; we have to write a new one every time.
    • practical use cases are so flexible, for example, a new formatter need to be written for hsla()/rgb/rgba/hex...
    • for borders/paddings/shadows, I have to write even more...

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

No branches or pull requests

2 participants