amzn / style-dictionary

A build system for creating cross-platform styles.
https://styledictionary.com
Apache License 2.0
3.9k stars 545 forks source link

Feat: Include simple arithmetic in JSON #365

Closed anthonytranDev closed 3 years ago

anthonytranDev commented 4 years ago

As a user when I have following JSON file:

{
    "base": {
        "value": 8
    },
    "spacing-unit": {
        "magnitude": "3",
        "value": "{base.value} * {spacing-unit.magnitude.value}"
    }
}

I would like the result:

{
    "base": {
        "value: 8
    },
    "spacing-unit": {
        "magnitude: "3"
        "value: 24
    }
}

Reading through CONTRIBUTING.md, it's noted "Do not mutate property names or values in a format."

Let me know if this feature is possible. Else, any chance of myself or the style-dictionary time including some sort of transformer, for this behaviour

dbanksdesign commented 4 years ago

This is something we have discussed before pretty early on. You can take a look at this issue for context if you want: https://github.com/amzn/style-dictionary/issues/59

There are ways to achieve what you want right now, but in different ways than you propose. For example, as long as you export a simple object, you can use node modules as your token files. This allows you to do any arithmetic or other mutations you'd like in Javascript. Here is an example of that:

https://github.com/amzn/style-dictionary/tree/master/examples/advanced/node-modules-as-config-and-properties

and a node module performing computations and exporting a simple object:

https://github.com/amzn/style-dictionary/blob/master/examples/advanced/node-modules-as-config-and-properties/properties/color/core.js

Let me know if that helps, and happy to keep the discussion open if we feel this is an important feature to add.

danilovaz commented 4 years ago

@dbanksdesign can I use this?

"value": "calc(var(--size-icon-small) * var(--size-icon-proportion))"

to have this?

calc(16 * 10)

tonyjwalt commented 4 years ago

@danilovaz, your question references CSS custom properties.

Are those also tokens? Do you want the references maintained in your output?

It seems like what you want is this:

{
    "test": {
        "value": "calc({size.icon.small.value} * {size.icon.proportion.value})"
    }
}

which, assuming those references are also tokens, should generate this token:

test: calc(16*10)

or with a CSS-deep format (does not come with style dictionary currently, but could be extended) you could get this token:

test: calc(var(--size-icon-small) * var(--size-icon-proportion));
showered commented 4 years ago

Perhaps consider using Javascript instead of JSON. It gives you some flexibility in your code rather than having to rely on style-dictionary to do the magic.

For example:

const buttonBaseSize = 10;

module.exports = {
  size: {
    button: {
      base: {
        height: { value: buttonBaseSize },
      },
      large: {
        height: { value: buttonBaseSize * 2 },
      }
    }
  }
}

You might run in to issues with brand/platform overrides, but this is the best I can suggest right now!

chazzmoney commented 3 years ago

We will not be adding this for now. The Javascript method demonstrated by @showered is the best path forward for these techniques at this time.

Amrender-Singh commented 2 years ago

@dbanksdesign @chazzmoney I have got something similar in one of my projects, wherein we get the tokens in JSON( this cannot be changed). And we need to perform similar mathematical operation, like we have font-size: “0.5*{base.value}”. Is such operation possible using style dictionary. Also I read the contribution doc is there any specific reason for not recommending mutation of tokens in format . Thanks

dbanksdesign commented 2 years ago

The difficulty is do we just 'eval' the string or do we try to perform the arithmetic ourselves by parsing the string? What you could do right now is create a custom transform that does the specific arithmetic you are looking for. It will need to be a transitive transform to work on references. I'm away from my computer, but can get an example later if you need.

Amrender-Singh commented 2 years ago

@dbanksdesign Thanks for getting back. I tried to used custom transform, but I am facing some problems with it. My Design token JSON structure is like:

{
    "sizing-scale": {
      "adaptive": {
            "100": {
              "value": "{sizing-scale.adaptive.base}",
              "type": "sizing"
            },
            "0.25": {
              "value": "0.25*{sizing-scale.adaptive.base}",
              "type": "sizing"
            },
        "base": {
              "value": "16",
              "type": "sizing"
            }
       }
    },
    "border-radius": {
        "circle": {
          "value": "50%",
          "type": "borderRadius"
        },
        "rounded-100": {
          "value": "10*{sizing-scale.adaptive.025}",
          "type": "borderRadius"
        }
    }
}

As we are using size-scaling.adavtive.0.25 reference in border radius too, the problem is if I calculate and convert adaptive.0.25 to rem and add unit rem to it, then when i calculate borderRadius then sizing-scale.adaptive.025 has value in rem(for ex: 0.625 rem) and then if i eval this statement it gives me error, so i again have to extract the number perform calculation and then convert it to rem. Is there any way, using which i can perform a transformation on all the tokens in one go, for example performing all calculations and then once it is completed, i can convert everything to rem and append rem to them. Currently If i pass in an array of transform, all the transform are performed sequentially at one token at a time, so even if i have a different transform to convert token to rem I still face the same problem.

Amrender-Singh commented 2 years ago

@dbanksdesign any suggestion regarding this?

dbanksdesign commented 2 years ago

Hey @Amrender-Singh I made a quick example using the example JSON you sent over: https://stackblitz.com/edit/node-fdkelt?file=sd.config.js

A few things:

  1. The token name "0.25" won't work because of how style dictionary walks the object tree, so I changed the name to "025"
  2. I did a very naive implementation that only works for multiplication *, but you can expand it to fit your needs. Hope this helps