design-tokens icon indicating copy to clipboard operation
design-tokens copied to clipboard

Cant Export Spacing as Single Value

Open sir-captainmorgan21 opened this issue 3 years ago • 16 comments

We are using Tailwind. For things like spacing, we only need the following json schema to configure it for tailwind.

{
   "4": "4px",
   "8": "8px",
   "16": "16px"
}

and so on.

Right now, the spacing export exports with the assumption we need tokens for top, left, right, bottom. We end up with this after running style dictionary

{
  "4": {"top":4,"bottom":4,"left":4,"right":4},
  "8": {"top":8,"bottom":8,"left":8,"right":8},
  "12": {"top":12,"bottom":12,"left":12,"right":12},
  "16": {"top":16,"bottom":16,"left":16,"right":16},
  "24": {"top":24,"bottom":24,"left":24,"right":24},
  "32": {"top":32,"bottom":32,"left":32,"right":32},
  "40": {"top":40,"bottom":40,"left":40,"right":40},
  "48": {"top":48,"bottom":48,"left":48,"right":48},
  "64": {"top":54,"bottom":54,"left":54,"right":54},
  "96": {"top":96,"bottom":96,"left":96,"right":96}
}

Could there possibly be an option to export spacing with just the spacing value? so instead of...

"4": {
    "description": null,
    "type": "custom-spacing",
    "value": {
      "top": 4,
      "bottom": 4,
      "left": 4,
      "right": 4
    },
    "extensions": {
      "org.lukasoppermann.figmaDesignTokens": {
        "exportKey": "spacing"
      }
    }
  }

We'd be able to get...

"4": {
    "description": null,
    "type": "custom-spacing",
    "value": 4,
    "unit": "px",
    "extensions": {
      "org.lukasoppermann.figmaDesignTokens": {
        "exportKey": "spacing"
      }
    }
  },

sir-captainmorgan21 avatar Feb 05 '22 03:02 sir-captainmorgan21

It also looks like the unit isnt included in the export

sir-captainmorgan21 avatar Feb 05 '22 04:02 sir-captainmorgan21

Hey,

so I think adding some kind of option could be possible. Something like "simplified export" that would merge together all the values.

However you could also easily solve this via transformers for style dictionary. If you are interested we can create the transformers together in PR to the examples folder.

If so, I would suggest creating a new folder tailwind were we can setup everything for tailwind with style dictionary (https://github.com/lukasoppermann/design-tokens/tree/main/examples) I will sort the rest of the example folder afterwards

lukasoppermann avatar Feb 10 '22 09:02 lukasoppermann

@lukasoppermann yea would love to contribute. Do I need to be added as a contributor, or should I just fork?

Are you saying we start to place style-dictionary transformers in the examples folder under /libs? I didn't realize you had those transformers there available. You dont publish via npm do you?!

sir-captainmorgan21 avatar Feb 11 '22 19:02 sir-captainmorgan21

Hey, sorry, somehow this got lost. You can just fork and send a PR.

The transformers are not published via npm, no. But this is a good idea. Maybe a repo with transformers would be really helpful, what do you think?

Did you get around writing any transformers yet?

lukasoppermann avatar Mar 03 '22 08:03 lukasoppermann

Yea, I think a repo of transformers would be nice. That would provide an end to end suite of tools to support the workflow.

sir-captainmorgan21 avatar Mar 07 '22 15:03 sir-captainmorgan21

I have not written any transformers yet, though.

sir-captainmorgan21 avatar Mar 07 '22 15:03 sir-captainmorgan21

@lukasoppermann here is a transformer I wrote:

StyleDictionary.registerFormat({
  name: "json/tailwind-number",
  formatter: ({dictionary}) => {
    let output = '{\n';
    output = output + dictionary.allTokens.map(token => {
      const tokenName = token.name;
      const trimmedValue = tokenName.slice((tokenName.length - (tokenName.indexOf('-') + 1)) * -1);
      return `  "${trimmedValue}": "${trimmedValue}px"`;
    }).join(',\n');
    output = output + '\n}';
    return output;
  }
})

Its meant to covert the tokens exported for radius and spacing, and convert them into the following format for tailwind:

{
  "4": "4px",
  "8": "8px",
  "12": "12px",
  "16": "16px",
  "24": "24px",
  "32": "32px",
  "40": "40px",
  "48": "48px",
  "64": "64px",
  "96": "96px"
}

sir-captainmorgan21 avatar Mar 26 '22 01:03 sir-captainmorgan21

Got another one for tailwind. Generate json thatll match the schema needed to use tailwind's plugin feature to add a utility.

StyleDictionary.registerFormat({
  name: 'json/tw-typography',
  formatter: ({dictionary}) => {
    const typographyTokens = dictionary.tokens.typography
    let output = '{\n';

    Object.keys(typographyTokens).forEach((type, typeIndex, typeArray) => {
      const typographyType = typographyTokens[type];
      Object.keys(typographyType).forEach((size, sizeIndex, sizeArray) => {
        const typeSize = typographyType[size];
        output = output + `  "${type}-${size}": {\n`;
        Object.keys(typeSize).forEach((prop, propIndex, propArray) => {
          const isLastProp = propIndex === propArray.length - 1;
          console.log(typeSize[prop].type);
          console.log(typeSize[prop].value);
          output = output + `    "${prop}": "${typeSize[prop].value}`
            + (typeSize[prop].type === 'dimension' ? 'px"' : '"');
          output = output + (isLastProp ? '\n' : ',\n');
        });
        //${JSON.stringify(typographyTokens[type][size], null, 2)}`;
        const isLastToken = (typeIndex === typeArray.length - 1) && (sizeIndex === sizeArray.length - 1);
        output = output + (isLastToken ? '  }\n' : '  },\n');
      });
    });
    
    return output + '}';
  }
});

Itll format the typography tokens from Figma....

{
  "typography": {
    "display": {
      "100": {
        "fontSize": {
          "type": "dimension",
          "value": 56
        },
        "textDecoration": {
          "type": "string",
          "value": "none"
        },
        "fontFamily": {
          "type": "string",
          "value": "Nunito"
        },
        "fontWeight": {
          "type": "number",
          "value": 800
        },
        "fontStyle": {
          "type": "string",
          "value": "normal"
        },
        "fontStretch": {
          "type": "string",
          "value": "normal"
        },
        "letterSpacing": {
          "type": "dimension",
          "value": -1.12
        },
        "lineHeight": {
          "type": "dimension",
          "value": 64
        },
        "paragraphIndent": {
          "type": "dimension",
          "value": 0
        },
        "paragraphSpacing": {
          "type": "dimension",
          "value": 0
        },
        "textCase": {
          "type": "string",
          "value": "none"
        }
      },
      "200": {
        "fontSize": {
          "type": "dimension",
          "value": 64
        },
        "textDecoration": {
          "type": "string",
          "value": "none"
        },
        "fontFamily": {
          "type": "string",
          "value": "Nunito"
        },
        "fontWeight": {
          "type": "number",
          "value": 800
        },
        "fontStyle": {
          "type": "string",
          "value": "normal"
        },
        "fontStretch": {
          "type": "string",
          "value": "normal"
        },
        "letterSpacing": {
          "type": "dimension",
          "value": -1.28
        },
        "lineHeight": {
          "type": "dimension",
          "value": 72
        },
        "paragraphIndent": {
          "type": "dimension",
          "value": 0
        },
        "paragraphSpacing": {
          "type": "dimension",
          "value": 0
        },
        "textCase": {
          "type": "string",
          "value": "none"
        }
      }
    }
  }
}

like so....

{
  "display-100": {
    "fontSize": "56px",
    "textDecoration": "none",
    "fontFamily": "Nunito",
    "fontWeight": "800",
    "fontStyle": "normal",
    "fontStretch": "normal",
    "letterSpacing": "-1.12px",
    "lineHeight": "64px",
    "paragraphIndent": "0px",
    "paragraphSpacing": "0px",
    "textCase": "none"
  },
  "display-200": {
    "fontSize": "64px",
    "textDecoration": "none",
    "fontFamily": "Nunito",
    "fontWeight": "800",
    "fontStyle": "normal",
    "fontStretch": "normal",
    "letterSpacing": "-1.28px",
    "lineHeight": "72px",
    "paragraphIndent": "0px",
    "paragraphSpacing": "0px",
    "textCase": "none"
  }
}

This one is also making assumptions about how the styles are set up. The depth of the tokens could be an option provided to the formatter. IE... We have tokens where its heading.100.desktop and heading.100.tablet... etc. as opposed to just display.100

sir-captainmorgan21 avatar Mar 27 '22 21:03 sir-captainmorgan21

@lukasoppermann where would you want this style dictionary formatter package to live in the repo?

sir-captainmorgan21 avatar Mar 27 '22 21:03 sir-captainmorgan21

Got an parameterized formatter for typography tokens

StyleDictionary.registerFormat({
  name: 'json/tw-typography',
  formatter: ({dictionary, options}) => {

    const buildTypographyProperties = (obj, path = [], depth = 0, fromLastKey = true) => {
      if (depth !== options.tokenDepth - 1) {
        let json = [];
        Object.keys(obj).forEach((key, index, array) => {
          const isLastKey = index === array.length - 1;
          json.push(buildTypographyProperties(obj[key], path.concat(key), depth + 1, fromLastKey && isLastKey));
        });
        return json.join('');
      } else {
        let json = [`  "${path.join('-').replace(' ', '-')}": { \n`];
        Object.keys(obj).forEach((key, index, array) => {
          const isLastProp = index === array.length - 1;
          json.push(`    "${key}": "${obj[key].value}` + (obj[key].type === 'dimension' ? 'px"' : '"'));
          json.push(isLastProp ? '\n' : ',\n');
        });
        json.push(fromLastKey ? '  }\n' : '  },\n');
        return json.join('');
      }
    };

    const typographyTokens = dictionary.tokens.typography
    let output = '{\n';
    output = output + buildTypographyProperties(typographyTokens);
    return output + '}';
  }
});

sir-captainmorgan21 avatar Mar 28 '22 13:03 sir-captainmorgan21

Nice. I think it would make most sense to create a separate repo so people can download it and use it without having the whole plugin code in their npm. What do you think?

lukasoppermann avatar Mar 29 '22 09:03 lukasoppermann

Agreed

sir-captainmorgan21 avatar Apr 06 '22 14:04 sir-captainmorgan21

Okay, sorry, I am currently extremely at my capacity. 😢

While trying to create a new repo, I thought, why don't we use this: https://github.com/lukasoppermann/design-token-transformer

I try to push it now, but my thoughts are:

  • adding src with subfolders iOS, android, web
  • adding an examples folder with separate build.js files that use the files from src.

Feel free to send a PR to the repo. This is the branch I am working on: https://github.com/lukasoppermann/design-token-transformer/tree/suggested-transformers

And sorry again for the sluggish response.

lukasoppermann avatar Apr 06 '22 15:04 lukasoppermann

No worries! Sounds good. This is perfect!

sir-captainmorgan21 avatar Apr 06 '22 18:04 sir-captainmorgan21

Hey, I merged the changes now. Android is done, so you can use that as an example.

I also added a transform:android script that runs the example.

We still need to add it for iOS and web and of course for your examples. And we need to adjust the documentation on this project and the transformers to tell people about it.

lukasoppermann avatar Apr 06 '22 19:04 lukasoppermann

@lukasoppermann sorry. Been swamped with standing up our design system, but gonna hopefully start on this soon

sir-captainmorgan21 avatar Jun 30 '22 13:06 sir-captainmorgan21