Как преобразовать json в класс css в словаре стилей?

#css #json #styles #token #style-dictionary

#css #json #стили #токен #словарь стилей

Вопрос:

У меня есть следующая кнопка.json

 {
  "component": {
  "button": {
    "padding": { "value": "{size.padding.medium.value}" },
    "font-size": { "value": 2 },
    "text-align": { "value": "center" },
  
    "primary": {
      "background-color": { "value": "hsl(10, 80, 50)" },
      "color": { "value": "{color.font.inverse.value}" }
    },
  
    "secondary": {
      "background-color": { "value": "{color.background.primary.value}" },
      "color": { "value": "{color.font.link.value}" }
    }
  }
}
 

Я могу генерировать токены стиля, используя словарь стилей Amazon. То, что я хочу сгенерировать, — это полный sass css из json.

Например:

 .component-button {

    padding: " ";
    font-size: " ";

    amp; .primary {
        background-color: "",
        color: ""
    }
}
 

Комментарии:

1. Для этого вы можете использовать пользовательский формат с пользовательским шаблоном. Я могу написать этот код в ответе ниже. Однако здесь я бы использовал только указатели для значений токенов. Таким образом, вы можете управлять их преобразованиями (т. Е. Всеми цветами в значения шестнадцатеричного кода) в более структурированной среде, в отличие от необходимости настраивать button.primary.background-color для преобразования цвета поверх всего остального. Дайте мне знать, если это не имеет смысла.

Ответ №1:

Одним из способов сделать это было бы создать пользовательский формат с пользовательским шаблоном для отображения .scss файла. Я включил базовый код для этого ниже.

Во-первых, вот файл button.json, который я использовал:

 {
  "color": {
    "background": {
      "primary": { "value": "hsl(10, 80, 50)" },
      "secondary": { "value": "hsl(10, 80, 90)" }
    },
    "font": {
      "inverse": { "value": "#fff" },
      "link": { "value": "#00f" }
    }
  },
  "size": {
    "2": { "value": 16 },
    "padding": {
      "medium": { "value": 16 }
    }
  },
  "component": {
    "button": {
      "padding": { "value": "{size.padding.medium.value}" },
      "font-size": { "value": "{size.2.value}" },
      "text-align": { "value": "center" },
      "primary": {
        "background-color": { "value": "{color.background.primary.value}" },
        "color": { "value": "{color.font.inverse.value}" }
      },
      "secondary": {
        "background-color": { "value": "{color.background.secondary.value}" },
        "color": { "value": "{color.font.link.value}" }
      }
    }
  }
}
 

Я добавил дополнительный color size объект and, чтобы указатели могли разрешаться соответствующим образом. Я также сделал некоторые предположения о том, как были структурированы ваши токены (например size.2 , для font-size ), но, надеюсь, общая идея там ясна.

Вот основной код JavaScript. Я включил комментарии, чтобы попытаться все объяснить.

 const _ = require('lodash')

/*
  This is formatted oddly in order to get a nice final shape
  in `button.scss`.

  Essentially what the template is doing is looping through
  the props object and outputting the top-level properties
  as the parent classnames, then for each child props of
  "classname" it looks to see if the child prop is an object,
  if it is, then it outputs the Sass `amp;.` operator with the
  child prop rule as the sub classname and then each child
  prop of the value as the final CSS style rule and value.
  If it's not an object then it outputs the rule and value
  as the CSS style rule and value.
*/
const template = _.template(`<% _.each(props, function(prop, classname) { %>.<%= classname %> {
<% _.each(prop, (value, rule) => { %><% if (typeof value === 'object') { %>  amp;.<%= rule %> {<% _.each(value, (subvalue, subrule) => { %>
    <%= subrule %>: <%= subvalue %>;<% }) %>
  }<% } else { %>  <%= rule %>: <%= value %>;<% } %>
<% }) %><% }) %>}
`)

const StyleDictionary = require('style-dictionary')
  .registerFormat({
    name: 'scss/button',
    formatter: function(dictionary, config) {
      /*
        allProperties is an array containing all the matched
        tokens based on the filter.
      */
      const { allProperties } = dictionary

      /*
        Set up an empty object to hold the final shape to pass
        to the custom template.

        After the allProperties.map(), props will look like this:
        {
          'component-button': {
            padding: '16px',
            'font-size': '16px',
            'text-align': 'center',
            primary: { 'background-color': '#e63c19', color: '#ffffff' },
            secondary: { 'background-color': '#fad8d1', color: '#0000ff' }
          }
        }
      */
      const props = {}

      // go through properties and structure final props object
      allProperties.map(prop => {
        /*
          Extract the attributes object created by the 'attribute/cti'
          transform and the transformed token value.
        */
        const { attributes, value } = prop

        // extract attributes to build custom class and style rules
        const { category, type, item, subitem } = attributes

        // build main classname for .scss file
        const classname = `${category}-${type}`

        /*
          Add to the props object if it doesn't already exist.
          We run the check to see if the classname exists already as an
          object property because in our case, `classname` will be the
          same for each token object in allProperties because each token
          is under the same category and type.
        */
        if (!props.hasOwnProperty(classname)) {
          props[classname] = {}
        }

        /*
          If the token object has a subitem, use the item as the subclass.
          Run the same check to see if this particular subclass (item) has
          been added yet.
        */
        if (subitem) {
          if (!props[classname].hasOwnProperty(item)) {
            props[classname][item] = {}
          }

          // add the subitem and value as final CSS rule
          props[classname][item][subitem] = value
        }
        else {
          // add the item as a CSS rule, not a subclass
          props[classname][item] = value
        }
      })

      /*
        Pass the final `props` object to our custom template to render
        the contents for the final button.scss file.
      */
      return template({ props })
    }
  })
  .extend({
    source: ['button.json'],
    platforms: {
      scss: {
        buildPath: 'build/',
        transforms: [
          'attribute/cti',  // setup attributes object
          'color/css',      // transform color values to hex
          'name/cti/kebab', // prevent name collisions
          'size/px'         // transform size values to px
        ],
        files: [
          {
            destination: 'button.scss',
            format: 'scss/button',
            filter: {
              attributes: {
                category: 'component',
                type: 'button'
              }
            }
          }
        ]
      }
    }
  })

// run Style Dictionary
StyleDictionary.buildAllPlatforms()
 

Если вы запустите это, вы должны получить конечный файл (build/button.scss), который выглядит следующим образом:

 .component-button {
  padding: 16px;
  font-size: 16px;
  text-align: center;
  amp;.primary {
    background-color: #e63c19;
    color: #ffffff;
  }
  amp;.secondary {
    background-color: #fad8d1;
    color: #0000ff;
  }
}
 

Комментарии:

1. Большое спасибо за ваше время и усилия. Я знаю, это заняло бы много времени, по крайней мере, для меня, чтобы написать и протестировать. Недавно я начал использовать словарь стилей.