amzn / style-dictionary

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

Composition tokens & scss variables #947

Open argdesign opened 1 year ago

argdesign commented 1 year ago

I'm working in a project with multiple composition tokens, I was using registerTransform to create the scss variables of each one of them. But I was looking for a more practical way to transform composition tokens into scss variables. I found this amazing example to transform composition tokens , I made some little adjustments but the output is not exactly what I'm needing.

Expected Output

$heading-level-1: (
 font-weight: 600,
 line-height: 48,
 font-size: 20px,
 letter-spacing: 0%,
 paragraph-spacing: 0,
 text-decoration: none,
 text-case: none;
),

registerTransform

StyleDictionary.registerTransform({
    type: 'value',
    transitive: true,
    name: 'scss/test',
    matcher: ({ type }) => {
        return ['typography', 'composition'].includes(type);
    },
    transformer: ({ value, name, type }) => {
        if (!value) return;

        const entries = Object.entries(value);

        const flattendedValue = entries.reduce(
            (acc, [key, v], index) => 
       `${acc ? `${acc}\n ` : '' }${StyleDictionary.transform['name/cti/kebab'].transformer(
        { path: [key] },
        { prefix: '' },
      )}: ${v}${index + 1 === entries.length ? '' : ','}` ,
            ``,
        );

        return flattendedValue
    },
 });

Output , The output is creating a ';' at the end of the last css property

$heading-level-1: font-family: Roboto,
 font-weight: 600,
 line-height: 48,
 font-size: 20px,
 letter-spacing: 0%,
 paragraph-spacing: 0,
 text-decoration: none,
 text-case: none ;

json

    "heading": {
      "level-1": {
        "value": {
          "fontFamily": "Roboto",
          "fontWeight": "600",
          "lineHeight": "48",
          "fontSize": "20px",
          "letterSpacing": "0%",
          "paragraphSpacing": "0",
          "textDecoration": "none",
          "textCase": "none"
        },
        "type": "typography",
        "description": "Heading 1"
      }
    },

Thanks in advance

Sidnioulz commented 1 year ago

Hi,

The format expected by Storybook would be:

{
  "heading": {
    "level-1": {
      "fontFamily": {
        "value": "Roboto"
      },
      "fontWeight": {
        "value": 600
      },
      "lineHeight": {
        "value": 48
      },
      "fontSize": {
        "value": "20px"
      },
      "letterSpacing": {
        "value": "0%"
      },
      "paragraphSpacing": {
        "value": 0
      },
      "textDecoration": {
        "value": "none"
      },
      "textCase": {
        "value": "none"
      }
    }
  }
}

There are a bunch of reasons behind this.

Firstly, SD supports multiple output platforms, not just web. What you use for letter-spacing could be called tracking in other platforms' vocabulary. Line-height could be leading. And there could be concepts like font-display that apply only to Web and not other platforms, and vice versa.

So, it SD was opinionated and enforced that specific keys of a value output a specific CSS property / SCSS variable and a specific value format, it would be much, much less flexible than it is today.

Secondly, separating the different bits into their own keys allows you to inject additional attributes, e.g. to ensure your size transforms are used for a specific value, or a comment injected in inline doc:

    "lineHeight": {
      "value": 48,
      "comment": "We don't use relative line heights at Foo Corp",
      "attributes": {
        "category": "size"
      }
    }