amzn / style-dictionary

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

Invalid scss/map-flat when using strings #298

Open siiron opened 5 years ago

siiron commented 5 years ago

New to Style Dictionary here.

I'm trying to convert some font styles from json to scss using scss/map-flat. I'm especially having trouble with font-family where my webfont Montserrat needs to be in single quotes in the finished scss map.

My config is pretty simple:

{
    "source": ["src/properties/**/*.json"],
    "platforms": {
        "scss": {
            "transformGroup": "scss",
            "buildPath": "dist/scss/",
            "files": [              
                {
                    "format": "scss/map-flat",
                    "destination": "_font.map.scss",
                    "filter": {
                        "attributes": {
                            "category": "font"
                        }
                    }
                }
            ]
        }
    }
}

My font.json looks like this:

{
    "font": {
        "family": {
            "brand": { "value": "'Montserrat', Arial, sans-serif" }
        },
        "style": {
            "normal": { "value": "normal" },
            "italic": { "value": "italic" }
        },
        "weight": {
            "regular": { "value": "regular" },
            "medium": { "value": "medium" },
            "bold": { "value": "bold" }
        }
    }
}

After transform I get a _font.map.scss that does not validate since the font-family stack consists of three values not encapsulated in quotes:

$tokens: (
  'font-family-brand': 'Montserrat', Arial, sans-serif,
  'font-style-normal': normal,
  'font-style-italic': italic,
  'font-weight-regular': regular,
  'font-weight-medium': medium,
  'font-weight-bold': bold
);

If I manually encapsulate the values in the map in (), it validates:

$tokens: (
  'font-family-brand': ('Montserrat', Arial, sans-serif),
  'font-style-normal': (normal),
  'font-style-italic': (italic),
  'font-weight-regular': (regular),
  'font-weight-medium': (medium),
  'font-weight-bold': (bold)
);

How can I do this using Style Dictionary? Do I need a custom template or can I somehow escape the font-family string in the json?

dbanksdesign commented 5 years ago

Hmm looks like a bug on our end for not handling font-family properly in that format. I'll do some investigation.. In the meantime, you could override the default sass/map-flat format

const StyleDictionary = require('style-dictionary');
const _ = require('losdash');
const fs = require('fs');

StyleDictionary.registerFormat({
  name: 'scss/map-flat',
  formatter: _.template( fs.readFileSync('pathToTemplate') )
});

StyleDictionary.extend('./config.json').buildAllPlatforms();

Where 'pathToTemplate' is a template file where you copy/paste this code: https://github.com/amzn/style-dictionary/blob/master/lib/common/templates/scss/map-flat.template and change line 37 to add encapsulating parenthesis.

didoo commented 5 years ago

@siiron this is how I am solving a similar problem with Sass maps and quotes for font families:

screenshot_3065

(essentially using Sass interpolation)

Regarding the tokens, and the output of the scss/map-flat format (I am the one that implemented it) I am not sure how we could "fix" it. The SD code doesn't detect the "kind" of property we are formatting, so we can't be sure if something is a font family declaration or not. For this probably what @dbanksdesign suggests (a custom template) is for now the most viable option.

@dbanksdesign do you have any ideas if we can solve it in some generic way, without adding extra complexity? maybe we just add the interpolation whenever the string contains some quotes?

dbanksdesign commented 5 years ago

Is the issue with the quotes or with the commas? We could definitely add a line of logic in the scss/map formats to detect this and encapsulate the value in parenthesis. I would just want to make sure we won't break other cases with the logic and something that we are sure will work. I do want to balance being generic in the built-in formats and not being overly complex, that is what custom formats are for. But at the same time the built-in formats should "just work". I guess that is a long way of saying if this is a general issue with the format and scss language that can be solved in a general way, lets do it.

funyapu commented 4 years ago

Found similar issue here.

If using scss/variables, works ok. But with custom .template, scss/map-flat, or scss/map-deep, values that should be quoted aren't quoted.

7studio commented 4 years ago

I have the same problem in my project with the asset category… and I can't continue without fixing it. I will submit a PR during the day (at least to follow the behaviour of scss/variables) :grinning:

chazzmoney commented 4 years ago

With #394 merged (Thanks @7studio!), what else do we need here to have this fully solved?

chazzmoney commented 3 years ago

@dbanksdesign Is there something we can / should do to solve this? Seems like it is still an issue for some people.

https://github.com/AlaskaAirlines/AuroDesignTokens/pull/39/files

fmal commented 1 year ago

@chazzmoney i've also run into this, would appreciate a fix, even in form of extra user option to scss/map-* formatter that would simply make it wrap the values in parentheses

bimalpaul commented 1 year ago

We're also running into the same issue when we have a list of font-families to account for fallbacks. For the time being, we're using a custom formatter to get around this; but would love for this to be an official fix on the style-dictionary formats as well. Happy to PR if this isn't already being solutioned for.

joshua-andrassy commented 1 month ago

Ran into the same issue, this is our workaround for the time being. I imagine this could be made more generic to inspect a token value and wrap it accordingly.

const originalScssMapFlat = StyleDictionary.format['scss/map-flat'];

// Formats
StyleDictionary.registerFormat({
  name: 'scss/map-flat',
  formatter: (args: FormatterArguments) => {
    const { dictionary } = args;
    const fontFamilyTokens: TransformedToken[] =
      dictionary.allProperties.filter(({ type }) => type === 'fontFamilies');
    fontFamilyTokens.forEach((token) => {
      const { value } = token;
      // encase in interpolation so map value is treated as singular value
      token.value = `#{${value}}`;
    });

    return originalScssMapFlat(args);
  },
});