You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Style Dictionary配置求助:如何导出关联式设计令牌?

Style Dictionary 保留令牌层级关联引用的配置方案

我有三层设计令牌(原始层、语义层、特定层),使用Style Dictionary编译到不同平台时,所有令牌都被替换成了原始值,无法保留JSON源文件中的关联引用关系。之前尝试用GPT建议的build.js定义转换规则,但构建时出现报错。以下是我的相关文件和编译结果:

参考JSON文件

{
  "token": {
    "spacer": {
      "value": "12px",
      "type": "dimension"
    },
    "gap": {
      "value": "{token.spacer}",
      "type": "dimension"
    },
    "button-gap": {
      "value": "{token.gap}",
      "type": "dimension"
    }
  }
}

原配置文件

{
    "source": ["tokens/**/*.json"],
    "platforms": {
      "web": {
        "transformGroup": "scss",
        "buildPath": "build/web/",
        "files": [
          {
            "destination": "variables.scss",
            "format": "scss/variables"
          }
        ]
      },
      "ios": {
        "transformGroup": "ios",
        "buildPath": "build/ios/",
        "files": [
          {
            "destination": "tokens.h",
            "format": "ios/macros"
          }
        ]
      },
      "android": {
        "transformGroup": "android",
        "buildPath": "build/android/",
        "files": [
          {
            "destination": "tokens.xml",
            "format": "android/resources"
          }
        ]
      }
    }
  }

当前编译结果

Web(variables.scss)

$token-spacer: 12px;
$token-gap: 12px;
$token-button-gap: 12px;

Android(tokens.xml)

<resources>
  <dimen name="token_spacer">192.00dp</dimen>
  <dimen name="token_gap">192.00dp</dimen>
  <dimen name="token_button_gap">192.00dp</dimen>
</resources>

iOS(tokens.h)

#define TokenSpacer 192.00f
#define TokenGap 192.00f
#define TokenButtonGap 192.00f

解决方案:保留关联引用的配置与自定义格式器

核心思路是禁用Style Dictionary默认的令牌值解析替换,同时编写自定义格式器,将令牌引用转换为对应平台支持的变量/宏引用语法。

1. 修改配置文件,禁用默认值解析

在每个平台的配置中添加"resolveReferences": false,阻止Style Dictionary自动替换引用的令牌值;同时指定自定义格式器:

{
  "source": ["tokens/**/*.json"],
  "platforms": {
    "web": {
      "transformGroup": "scss",
      "resolveReferences": false,
      "buildPath": "build/web/",
      "files": [
        {
          "destination": "variables.scss",
          "format": "custom/scss-references"
        }
      ]
    },
    "ios": {
      "transformGroup": "ios",
      "resolveReferences": false,
      "buildPath": "build/ios/",
      "files": [
        {
          "destination": "tokens.h",
          "format": "custom/ios-macros-references"
        }
      ]
    },
    "android": {
      "transformGroup": "android",
      "resolveReferences": false,
      "buildPath": "build/android/",
      "files": [
        {
          "destination": "tokens.xml",
          "format": "custom/android-resources-references"
        }
      ]
    }
  }
}

2. 创建自定义格式器脚本(build.js)

编写build.js注册自定义格式器,处理令牌引用转换与单位转换逻辑:

const StyleDictionary = require('style-dictionary');

// 自定义SCSS格式:将令牌引用转为SCSS变量
StyleDictionary.registerFormat({
  name: 'custom/scss-references',
  formatter: function({ dictionary }) {
    return dictionary.allTokens.map(token => {
      let value = token.value;
      // 替换{token.spacer}为$token-spacer
      if (value.startsWith('{') && value.endsWith('}')) {
        const refTokenName = value.slice(1, -1).replace(/\./g, '-');
        value = `$${refTokenName}`;
      }
      return `$${token.name}: ${value};`;
    }).join('\n');
  }
});

// 自定义iOS宏格式:将令牌引用转为宏名称
StyleDictionary.registerFormat({
  name: 'custom/ios-macros-references',
  formatter: function({ dictionary }) {
    return dictionary.allTokens.map(token => {
      let value = token.value;
      // 替换{token.spacer}为TokenSpacer
      if (value.startsWith('{') && value.endsWith('}')) {
        const refTokenPath = value.slice(1, -1).split('.');
        const refTokenName = refTokenPath.map(part => 
          part.charAt(0).toUpperCase() + part.slice(1)
        ).join('');
        value = refTokenName;
      }
      // 处理px转iOS pt单位(1px=16pt)
      if (typeof value === 'string' && value.endsWith('px')) {
        value = `${parseFloat(value) * 16}.00f`;
      }
      return `#define ${token.name} ${value}`;
    }).join('\n');
  }
});

// 自定义Android资源格式:将令牌引用转为@dimen/xxx
StyleDictionary.registerFormat({
  name: 'custom/android-resources-references',
  formatter: function({ dictionary }) {
    const tokens = dictionary.allTokens.map(token => {
      let value = token.value;
      // 替换{token.spacer}为@dimen/token_spacer
      if (value.startsWith('{') && value.endsWith('}')) {
        const refTokenName = value.slice(1, -1).replace(/\./g, '_');
        value = `@dimen/${refTokenName}`;
      }
      // 处理px转Android dp单位(1px=16dp)
      if (typeof value === 'string' && value.endsWith('px')) {
        value = `${parseFloat(value) * 16}.00dp`;
      }
      return `  <dimen name="${token.name}">${value}</dimen>`;
    }).join('\n');
    return `<resources>\n${tokens}\n</resources>`;
  }
});

// 执行构建
StyleDictionary.extend('config.json').buildAllPlatforms();

3. 运行构建命令

替换原有的Style Dictionary构建命令,执行自定义脚本:

node build.js

预期编译结果

Web(variables.scss)

$token-spacer: 12px;
$token-gap: $token-spacer;
$token-button-gap: $token-gap;

iOS(tokens.h)

#define TokenSpacer 192.00f
#define TokenGap TokenSpacer
#define TokenButtonGap TokenGap

Android(tokens.xml)

<resources>
  <dimen name="token_spacer">192.00dp</dimen>
  <dimen name="token_gap">@dimen/token_spacer</dimen>
  <dimen name="token_button_gap">@dimen/token_gap</dimen>
</resources>

注意事项

  • 自定义格式器中的引用替换逻辑需与你的令牌命名规则匹配,若令牌结构调整需同步修改正则或字符串处理逻辑
  • 若存在其他类型令牌(如颜色、字体),需扩展格式器的处理分支,适配对应平台的语法
  • 禁用resolveReferences后,原transformGroup中的单位转换逻辑需手动在自定义格式器中实现,确保单位转换正确

内容的提问来源于stack exchange,提问作者vizin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 16:12:09