style-dictionary icon indicating copy to clipboard operation
style-dictionary copied to clipboard

[Feature] (Transformer to) allow splitting tokens

Open lukasoppermann opened this issue 3 years ago • 9 comments

Hey @dbanksdesign and @chazzmoney I think especially considering the upcoming composite tokens this could be very helpful.

I am running into this problem currently with font styles. My font styles (exported from figma) has letter-spacing. Sadly in css letter-spacing is not part of the font property but a separate attribute.

My need is to transform this input:

'font': {
  type: 'font-style',
  value: {
    fontSize: 16,
    fontWeight: 700,
    //...
    letterSpacing: 0.136
  }
}

Into this css:

font: 700 16px /*...*/;
letter-spacing: 0.136px;

While this is technically possible in a format I feel that is not the correct place for it, as it has not so much to do with the formatting.

lukasoppermann avatar Sep 14 '21 08:09 lukasoppermann

This would be a bit more difficult, but nothing is impossible. The difficulty in this approach is the end result for the generated code for each platform would mean splitting up a single token in different ways. Style Dictionary has the assumption that a design token is a name and a value and like you pointed out, the format should just format how a given language like CSS writes that declaration, [name] : [value] ; for example. This works fine in cases where a composite token can still be represented as a single declaration, for example having a border token that can be represented like [border width] [border style] [border color] in CSS because a transform can take an object and turn it into that string. While it doesn't fit with the spirit of what the format is supposed to do, that is the only way I can think of to split a single token into multiple tokens in an output. A better place would be if Style Dictionary had a pre-format step where you could mutate the dictionary object, but that sounds a bit weird too.

Another option would be to have a token group rather than a composite token and then split each font property into its own token inside the group. This might be a bit cleaner to implement a format for since it wouldn't involve splitting a token.

Can you zoom out a bit, what is the full output you want to have? A single text style in figma outputs to a helper class in CSS?

dbanksdesign avatar Sep 14 '21 23:09 dbanksdesign

Hey @dbanksdesign,

I totally understand this. Actually I find a pre-format step very good, I just thought it was more complicated. This pre-format step would have to be run on a per platform or per file basis (better would be to allow both).

In the w3c specs it is stated that groups are not supposed to have any meaning, it's just an ordering system, however a font-style needs to be grouped. It is actually part of the same token. This is why I think a composite token is better.

Zooming out

I actually translate the tokens into css variables:

 --background: #ffffffff;
 --breakpoints-xl: 1792px;
 --radius-round: 5px 10px 5px 5px;
 --typography-title1: 700 114/131.1 "RTL United Text", sans-serif;
 --typography-title1-letter-spacing: 0.135px; /* <-- this does not work yet */
 --gradients-image-overlay: linear-gradient(180deg, rgb(0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%);
 --elevation-small: 0px 1px 0px 0px rgba(23, 24, 26, 0.1);

The composite token for fonts looks like this (plugin output):

{
      name: 'title 1',
      description: 'a font token',
      type: 'custom-fontStyle',
      value: {
        fontSize: 114,
        textDecoration: 'underline',
        fontFamily: 'Helvetica',
        fontWeight: 700,
        fontStyle: 'italic',
        fontStretch: 'normal',
        letterSpacing: 1.2,
        lineHeight: 131.1,
        paragraphIndent: 0,
        paragraphSpacing: 12,
        textCase: 'none'
      }
}

lukasoppermann avatar Sep 15 '21 07:09 lukasoppermann

I'm bumping up against the same problem with JSON exported from Figma (via Figma-tokens plugin). Is there a workaround that others have used? Or is this feature in consideration?

BrettJephson avatar Jan 20 '22 14:01 BrettJephson

BTW, Figma tokens plugin supports splitting typography tokens into separate ones during export. You should check "Expand Typography" field in plugin UI.

Screenshot 2022-03-11 at 10 47 43

So the result will be:

"fontFamily": {
  "value": "{typography.font-family.sans-serif}",
  "type": "fontFamily"
},
"fontWeight": {
  "value": "{typography.font-weight.normal}",
  "type": "fontWeight"
},
"lineHeight": {
  "value": "{typography.line-height.32px}",
  "type": "lineHeight"
},
"fontSize": {
  "value": "{typography.font-size.24px}",
  "type": "fontSize"
},
"letterSpacing": {
  "value": "{typography.letter-spacing.02%}",
  "type": "letterSpacing"
}

juwain avatar Mar 11 '22 07:03 juwain

This would be a bit more difficult, but nothing is impossible. The difficulty in this approach is the end result for the generated code for each platform would mean splitting up a single token in different ways. Style Dictionary has the assumption that a design token is a name and a value and like you pointed out, the format should just format how a given language like CSS writes that declaration, [name] : [value] ; for example. This works fine in cases where a composite token can still be represented as a single declaration, for example having a border token that can be represented like [border width] [border style] [border color] in CSS because a transform can take an object and turn it into that string. While it doesn't fit with the spirit of what the format is supposed to do, that is the only way I can think of to split a single token into multiple tokens in an output. A better place would be if Style Dictionary had a pre-format step where you could mutate the dictionary object, but that sounds a bit weird too.

Another option would be to have a token group rather than a composite token and then split each font property into its own token inside the group. This might be a bit cleaner to implement a format for since it wouldn't involve splitting a token.

Can you zoom out a bit, what is the full output you want to have? A single text style in figma outputs to a helper class in CSS?

Hey @dbanksdesign, just stumbled upon this issue. I actually chimed in on #848 with a similar idea (mutating the dictionary object by expanding composite tokens into separate tokens). While this might seem a bit weird it has the benefit of making a lot of formats just work out of the box (e.g. css/variables, scss/map-flat, scss/variables).

I also vented the idea of a pre-format step - and if run per platform and/or file as suggested, that would not tie it directly with the format and could be reused for different files and formats within that platform. Maybe 2 brains having the same thought is hinting at something? 🤔 😅

jakobe avatar Sep 09 '22 08:09 jakobe

This would be very helpful for colours, as well:

ideally, style-dictionary would let us write a transform to automatically split single colour inputs to multiple values, for example hex to rgb, hsl:

IN:

color:
  blue:
    500:
      $value: '#0066cc'

OUT:

:root {
  --rh-color-blue-500: #0066cc;
  --rh-color-blue-500-rgb: 0, 102, 204;
  --rh-color-blue-500-hsl: 210 100% 40%;
}

USAGE:

#play-button {
  background-color: hsl(var(--rh-color-blue-500-hsl) / var(--rh-opacity-50));
}

bennypowers avatar Jan 11 '23 08:01 bennypowers

Here's a gist. It has a self contained class, which can be used with a formatter to expand a token.

yringler avatar Jul 20 '23 14:07 yringler

Adding a case for things like https://seek-oss.github.io/capsize/

Input:

text: {
  regular: {
    size: { $value: '14px', $type: 'dimension' },
    lineheight: { $value: 1.4, $type: 'number' },
  }
}

Output:

text: {
  regular: {
    size: { $value: '14px', $type: 'dimension' },
    lineheight: { $value: 1.4, $type: 'number' },
    baselineTrim: { $value: '-0.3364em', $type: 'dimension' },
    capHeightTrim: { $value: '-0.3364em', $type: 'dimension' },
    capHeight: { $value: '10.1818px', $type: 'dimension' },
  }
}

jossmac avatar Aug 03 '23 03:08 jossmac