WithAI.Design

5 min read

UIUX必修课!基于令牌token的UI设计

UIUX必修课!基于令牌token的UI设计

摘要

本文深入探讨了设计令牌(Design Tokens)在构建可扩展、一致且易于维护的 UI 架构中的核心作用。设计令牌作为一种将设计决策表示为数据的形式,为跨学科团队(设计师、开发人员、产品经理)提供了一种通用的语言,实现了设计和开发之间的无缝协作。文章强调了分层组织设计令牌的重要性,将令牌分为选项令牌(定义可使用的基本样式)、决策令牌(基于情境应用样式)和组件令牌(将样式映射到 UI 组件)。这种分层架构有助于设计师在适当的抽象层级思考,同时使开发人员能够有条理地应用样式。

文章还讨论了自动化设计令牌分发流程的重要性,包括如何使用工具和 CI/CD 管道来确保及时、准确的更新。通过版本控制和适当的工具(如 Style Dictionary), 设计令牌的更新可以高效地融入到开发流程中,从而减少人工错误和工作量。对于团队工作来说,理解令牌的范围也是非常重要的,合理地暴露公共令牌能确保开发的灵活性,保护内部令牌有助于维护系统的稳定性。

本文强调了设计令牌的价值不仅在于减少重复工作和提高效率,还在于提升设计的一致性和可维护性。对于 UI/UX 设计师和前端开发人员而言,熟练掌握设计令牌的概念和实践至关重要。这种掌握可以使我们构建更加模块化、可扩展、和可靠的 UI 系统,无论项目的规模和复杂性如何,都能保持一致的设计语言和良好的用户体验。 最终的目的是构建一个更高效、合作、并能持续交付的开发模式。

正文

设计令牌,也称为“tokens”,是以数据形式呈现的基本设计决策。它们是设计系统的基础构建模块。

自从 2022 年设计令牌规范的第二版编辑草案发布,以及号召工具开发者开始实施并提供反馈以来,设计令牌工具的生态系统得到了快速发展。代码生成器、文档系统和用户界面(UI)设计软件等工具现在能够更好地支持设计令牌,这凸显了它们在现代 UI 架构中日益增长的重要性。

在本文中,我将解释什么是设计令牌,它们在何时有用,以及如何有效地应用它们。我们将重点关注那些日后通常难以更改的关键架构决策,其中包括:

  1. 如何分层组织设计令牌,以平衡可伸缩性、可维护性和开发人员体验。
  2. 是否应向产品团队提供所有设计令牌,还是仅提供一部分。
  3. 如何实现跨团队设计令牌分发的自动化。

设计令牌的作用


在 2017 年左右,我参与了一个使用微前端架构来扩展开发团队的大型项目。在这种架构中,不同的团队负责用户界面的不同部分,甚至可能在同一页面上。每个团队都可以独立部署其微前端。

在一些场景中,组件会发生重叠显示(例如,对话框或 Toast 消息会出现在内容区域之上),而这些组件可能并不属于同一个微前端。团队使用 CSS 属性 z-index 来控制堆叠顺序,通常依赖于“魔术数字”——即那些未经记录或标准化的任意值。这种方法随着项目规模的扩大而变得难以维护。它导致了一些需要跨团队协作才能解决的问题。

这个问题最终通过设计令牌得到解决,我认为这是一个引入该概念的典型例子。相应的令牌文件可能类似于这样:

{
  "z-index": {
    "$type": "number",
    "default": {
      "$value": 1
    },
    "sticky": {
      "$value": 100
    },
    "navigation": {
      "$value": 200
    },
    "spinner": {
      "$value": 300
    },
    "toast": {
      "$value": 400
    },
    "modal": {
      "$value": 500
    }
  }
}

上面的设计令牌代表了可以在应用程序中使用的 z-index 值集合,其名称也为开发人员提供了在哪里使用它们的清晰指示。这样一个令牌文件可以集成到设计师的工作流中,也可以用来生成符合各团队需求的代码。例如,在本例中,令牌文件可能被用来生成 CSS 或 SCSS 变量:

  :root {
    --z-index-default: 1;
    --z-index-sticky: 100;
    --z-index-navigation: 200;
    --z-index-spinner: 300;
    --z-index-toast: 400;
    --z-index-modal: 500;
  }
  $z-index-default: 1;
  $z-index-sticky: 100;
  $z-index-navigation: 200;
  $z-index-spinner: 300;
  $z-index-toast: 400;
  $z-index-modal: 500;

什么是设计令牌?

Salesforce 最初引入设计令牌,旨在简化对多个平台的设计更新。

“设计令牌社区组” 将设计令牌定义为“一种以平台无关的方式表达设计决策的方法,从而可以在不同的学科工具技术之间共享这些决策。”

让我们仔细分析一下:

  • **跨学科协作:**设计令牌充当一种通用语言,使设计师、开发人员、产品经理和其他专业人员保持一致。通过为设计决策提供一个单一的事实来源,确保所有参与产品生命周期的人员都能保持在同一频道,从而提高工作效率。
  • **工具集成:**设计令牌可以集成到各种设计和开发工具中,包括 UI 设计软件、令牌编辑器、转换工具(代码生成器)和文档系统。这使得设计更新能够快速反映在代码库中,并在团队之间保持同步。
  • **技术适应性:**设计令牌可以转换成不同的技术形式,例如用于 Web 的 CSS、SASS 和 JavaScript,甚至可以用于 Android 和 iOS 等原生平台。这种灵活性保证了在各种平台和设备上设计的一致性。

建立单一事实来源

设计令牌的一个关键优势在于,它们能够为设计和工程团队提供一个单一的事实来源。这有助于确保多个产品或服务在视觉和功能上保持一致性。

转换工具会将一个或多个设计令牌文件作为输入,并生成特定于平台的代码作为输出。一些转换工具还可以生成设计令牌的 HTML 文档。在撰写本文时,流行的转换工具包括 Style Dictionary, Theo, DiezSpecify App

自动化设计令牌分发


在本节中,我们将探讨如何实现向产品团队自动化分发设计令牌。

假设我们的目标是,在设计师做出更改之后,立即为团队提供更新的、针对特定技术的设计令牌。为了实现这一目标,我们可以使用设计令牌的部署管道来自动化转换和分发过程。除了特定于平台的代码产物(如用于 Web 的 CSS,用于 Android 的 XML 等)之外,管道还可能部署设计令牌的文档。

一个至关重要的要求是,要将设计令牌置于版本控制之下。幸运的是,像 Figma 这样的常用设计工具的插件已经与 GitHub 等 Git 提供商集成了。最佳实践是将 Git 存储库作为设计令牌的单一事实来源,而不是设计工具本身。不过,这要求插件支持存储库和设计工具之间的双向同步,而并非所有插件都支持。目前,Tokens Studio 插件支持这种双向同步。关于将 Tokens Studio 与不同 Git 提供商集成的详细指导,请参阅其文档。该工具允许您配置目标分支,并支持基于主干和基于拉取请求的工作流。

一旦令牌纳入版本控制,我们就可以设置部署管道,构建并部署产品团队所需的产物,包括特定于平台的源代码和文档。源代码通常被打包为库,并通过构件(artifact)注册表分发。这种方法使产品团队能够控制升级周期。他们只需更新其依赖项即可采用更新的样式。这些更新也可以通过更新使用基于令牌样式的组件库间接应用。

图 2:自动化设计令牌分发

在 Thoughtworks,这个整体设置使得团队可以在一天之内,在多个前端和团队中推出较小的设计更改。

完全自动化管道

设计管道最直接的方式是采用完全自动化的、基于主干的工作流。在这种设置中,只要所有更改都通过自动化质量检查,对主分支的任何更改都将立即部署。

这样的管道可能包含以下作业:

  1. 检查: 使用设计令牌验证器或 JSON 验证器来验证设计令牌文件。
  2. 构建: 使用 Style Dictionary 等转换工具,将设计令牌文件转换为特定于平台的格式。此作业还可以使用转换工具或集成专门的文档工具来构建文档。
  3. 测试: 此作业高度依赖于测试策略。虽然可以直接使用设计令牌文件来进行一些测试(例如,检查颜色对比度),但一种常见的方法是使用诸如 Storybook 等文档工具来测试生成的代码。Storybook 为可视化回归测试、可访问性测试、交互测试和其他类型的测试提供了出色的测试支持
  4. 发布: 将更新的令牌发布到包管理器(如 npm)。发布过程和版本控制可以使用基于 Conventional Commits(约定式提交)的包发布工具(如semantic-release)进行完全自动化。Semantic-release 还允许将软件包部署到多个平台。发布作业还可以部署设计令牌的文档。
  5. 通知: 通过电子邮件或聊天通知团队新的令牌版本,以便他们可以更新其依赖项。

好的,这是文章的剩余部分,经过同样的翻译和优化流程后的结果:

图 3:完全自动化的部署管道

包含手动审批的管道

有时,完全自动化的质量检查可能不足。如果在发布之前需要人工评审,一种常见的方法是将包含最新设计令牌的文档的更新版本部署到预览环境(临时环境)。

如果使用像 Storybook 这样的工具,这个预览环境可能不仅包含设计令牌,还可以展示它们与应用程序中使用的组件的集成。

审批流程可以通过拉取请求工作流来实现。或者,它可以作为管道中的一个手动审批/部署步骤。

图 4:包含手动审批的部署管道

分层组织令牌


如前所述,设计令牌将设计决策表示为数据。然而,并非所有决策都在相同的细节层次上运作。相反,理想情况下,通用的设计决策会引导更为具体的决策。将令牌(或设计决策)分层组织,可以使设计师在适当的抽象层级上做出决策,从而支持一致性和可伸缩性。

例如,为每个新组件单独设置颜色选择是不切实际的。相反,定义一个基础调色板,然后决定如何以及在何处应用这些颜色会更有效率。这种方法减少了决策的数量,同时保持了统一的外观和感觉。

设计令牌用于三种关键类型的设计决策。它们彼此层叠构建:

  • 什么? 有哪些可用的设计选项?
  • 如何? 这些样式在整个用户界面中如何应用?
  • 哪里? 这些样式具体应用在哪些组件中?

这三种类型的令牌有不同的名称(通常,命名是一个难点)。在本文中,我们将使用 Samantha Gordashko 提出的术语:选项令牌(option tokens)、决策令牌(decision tokens)和组件令牌(component tokens)。

让我们用颜色示例来说明设计令牌如何解答上述三个问题。

选项令牌:定义可用的设计选项

选项令牌 (也称为 原始令牌基础令牌、_核心令牌_或 参考令牌)定义了在应用程序中可以使用的 什么 样式。它们定义了诸如调色板、间距/尺寸比例或字体系列之类的元素。并非所有选项都需要在应用程序中使用,但它们代表了合理的选择。

以我们的示例为例,假设我们有一个调色板,每种颜色有 9 种色调,从很浅到高度饱和。在下面,我们将蓝色调和灰色调定义为选项令牌:

{
  "color": {
    "$type": "color",
    "options": {
      "blue-100": {"$value": "#e0f2ff"},
      "blue-200": {"$value": "#cae8ff"},
      "blue-300": {"$value": "#b5deff"},
      "blue-400": {"$value": "#96cefd"},
      "blue-500": {"$value": "#78bbfa"},
      "blue-600": {"$value": "#59a7f6"},
      "blue-700": {"$value": "#3892f3"},
      "blue-800": {"$value": "#147af3"},
      "blue-900": {"$value": "#0265dc"},
      "grey-100": {"$value": "#f8f8f8"},
      "grey-200": {"$value": "#e6e6e6"},
      "grey-300": {"$value": "#d5d5d5"},
      "grey-400": {"$value": "#b1b1b1"},
      "grey-500": {"$value": "#909090"},
      "grey-600": {"$value": "#6d6d6d"},
      "grey-700": {"$value": "#464646"},
      "grey-800": {"$value": "#222222"},
      "grey-900": {"$value": "#000000"},
      "white": {"$value": "#ffffff"}
    }
  }
}

虽然提供合理的选项非常有用,但选项令牌本身不足以指导开发人员如何以及在何处应用这些选项。

决策令牌:定义样式的应用方式

决策令牌(也称为 语义令牌系统令牌)指定了在用户界面中,在上下文中如何应用这些样式选项。

在我们的颜色示例中,它们可能包括如下决策:

  • grey-100 用作表面颜色。
  • grey-200 用于禁用元素的背景。
  • grey-400 用于禁用元素的文字。
  • grey-900 用作文字的默认颜色。
  • blue-900 用作强调色。
  • 白色用于强调色背景上的文本。

相应的决策令牌文件可能如下所示:

{
  "color": {
    "$type": "color",
    "decisions": {
      "surface": {
        "$value": "{color.options.grey-100}",
        "description": "用作表面颜色。"
      },
      "background-disabled": {
        "$value": "{color.options.grey-200}",
        "description":"用于禁用元素的背景。"
      },
      "text-disabled": {
        "$value": "{color.options.grey-400}",
        "description": "用于禁用元素的文字。"
      },
      "text": {
        "$value": "{color.options.grey-900}",
        "description": "用作默认文字颜色。"
      },
      "accent": {
        "$value": "{color.options.blue-900}",
        "description": "用作强调色。"
      },
      "text-on-accent": {
        "$value": "{color.options.white}",
        "description": "用于强调色背景上的文字。"
      }
    }
  }
}

作为一名开发人员,我主要关注的是决策,而不是选项。例如,颜色令牌通常包含很长的选项列表(调色板),但实际上只有少数选项在应用程序中使用。在决定应用哪些样式时,真正相关的令牌通常是决策令牌。

决策令牌使用对选项令牌的引用。我将这种组织令牌的方式视为分层架构。在其他文章中,我经常看到使用“层级”(tier)一词,但我认为 “层”(layer)是更好的词,因为它没有隐含任何物理分隔。下图可视化了我们到目前为止讨论的两层:

图 5:双层模式

组件令牌:定义样式的应用位置

组件令牌(或 组件特定令牌)将 决策令牌 映射到 UI 的特定部分。它们显示了样式 被应用在哪里

在设计令牌的上下文中,术语“组件”并不总是与技术术语“组件”相对应。例如,在一些应用程序中,按钮可能被实现为 UI 组件,而在另一些应用程序中,可能只是使用 HTML 的 button 元素。 _组件令牌_在这两种情况下都可以使用。

组件令牌可以被组织成一个 ,该 引用了多个决策令牌。在我们的示例中,此引用可能包括不同变体(主要、次要)按钮以及禁用按钮的文本和背景颜色。它们还可以包括对其他类型令牌(间距/尺寸、边框等)的引用,在以下示例中我将省略这些引用:

{
  "button": {
    "primary": {
      "background": {
        "$value": "{color.decisions.accent}"
      },
      "text": {
        "$value": "{color.decisions.text-on-accent}"
      }
    },
    "secondary": {
      "background": {
        "$value": "{color.decisions.surface}"
      },
      "text": {
        "$value": "{color.decisions.text}"
      }
    },
    "background-disabled": {
      "$value": "{color.decisions.background-disabled}"
    },
    "text-disabled": {
      "$value": "{color.decisions.text-disabled}"
    }
  }
}

在某种程度上,组件令牌只是将决策应用到特定组件的结果。但是,如此示例所示,此过程并非总是那么简单,尤其对于那些没有设计经验的开发人员而言。虽然决策令牌可以大致了解在给定上下文中应使用哪些样式,但组件令牌可以提供额外的清晰度。

图 6:三层模式

注意: 可能会出现“雪花”情况,其中某些层会被跳过。例如,可能无法为每个组件令牌定义通用的决策,或者这些决策可能尚未制定(例如在项目开始时)。

令牌范围


我已经提到,尽管选项令牌对设计师非常有用,但它们可能与使用特定于平台的代码产物的应用程序开发人员无关。应用程序开发人员通常对决策令牌和组件令牌更感兴趣。

尽管设计令牌规范中尚未包含令牌范围,但一些设计系统已经将令牌分成了私有(也称为 内部)和公共(也称为 全局)令牌。例如,Salesforce Lightning 设计系统为每个令牌引入了一个标志。有很多理由说明这是一个好主意:

  • 它指导开发人员使用哪些令牌
  • 更少的选项可以提供更好的开发人员体验
  • 它可以减少文件大小,因为不必包含所有令牌
  • 私有/内部令牌可以在不引发重大更改的情况下更改或删除

将选项令牌设为私有的一个缺点是,开发人员将依赖设计师始终将这些样式作为决策或组件令牌提供。如果设计师的可用性有限,或者并非所有决策都可用(例如在项目开始时),则可能会出现问题。

不幸的是,目前尚没有用于实现设计令牌范围的标准化解决方案。因此,具体方法取决于项目的工具链,并且很可能需要一些自定义代码。

基于文件的作用域

使用 Style Dictionary,我们可以使用 过滤器 来仅公开公共令牌。最直接的方法是基于文件扩展名进行过滤。如果我们对组件、决策和选项令牌使用不同的文件扩展名,我们可以在文件路径上使用过滤器,例如将选项令牌层设为私有。

Style Dictionary 配置示例:

const styleDictionary = new StyleDictionary({
  "source": \["color.options.json", "color.decisions.json"\],
  "platforms": {
    "css": {
      "transformGroup": "css",
      "files": \[
        {
          "destination": "variables.css",
          "filter": token => !token.filePath.endsWith('options.json'),
          "format": "css/variables"
        }
      \]
    }
  }
});

生成的 CSS 变量将仅包含决策令牌,而不包含选项令牌。

生成的 CSS 变量示例:

:root {
  --color-decisions-surface: #f8f8f8;
  --color-decisions-background-disabled: #e6e6e6;
  --color-decisions-text-disabled: #b1b1b1;
  --color-decisions-text: #000000;
  --color-decisions-accent: #0265dc;
  --color-decisions-text-on-accent: #ffffff;
}

更灵活的方法

如果需要更高的灵活性,最好为每个令牌添加一个范围标志,并基于此标志进行过滤:

Style Dictionary 配置示例:

const styleDictionary = new StyleDictionary({
  "source": \["color.options.json", "color.decisions.json"\],
  "platforms": {
    "css": {
      "transformGroup": "css",
      "files": \[
          {
            "destination": "variables.css",
            "filter": {
              "public": true
            },
            "format": "css/variables"
          }
        \]
    }
  }
});

如果我们然后将该标志添加到决策令牌,则生成的 CSS 将与上面的示例相同:

带有范围标志的令牌示例:

  {
    "color": {
      "$type": "color",
      "decisions": {
        "surface": {
          "$value": "{color.options.grey-100}",
          "description": "用作表面颜色。",
          "public": true
        },
        "background-disabled": {
          "$value": "{color.options.grey-200}",
          "description":"用于禁用元素的背景。",
          "public": true
        },
        "text-disabled": {
          "$value": "{color.options.grey-400}",
          "description": "用于禁用元素的文字。",
          "public": true
        },
        "text": {
          "$value": "{color.options.grey-900}",
          "description": "用作默认文字颜色。",
          "public": true
        },
        "accent": {
          "$value": "{color.options.blue-900}",
          "description": "用作强调色。",
          "public": true
        },
        "text-on-accent": {
          "$value": "{color.options.white}",
          "description": "用于强调色背景上的文字。",
          "public": true
        }
      }
    }
  }

生成的 CSS 变量示例:

:root {
  --color-decisions-surface: #f8f8f8;
  --color-decisions-background-disabled: #e6e6e6;
  --color-decisions-text-disabled: #b1b1b1;
  --color-decisions-text: #000000;
  --color-decisions-accent: #0265dc;
  --color-decisions-text-on-accent: #ffffff;
}

现在,也可以通过 Figma UI 设置这些标志(如果使用 Figma 变量作为设计令牌的来源)。它可以通过插件 API 作为 hiddenFromPublishing 标志进行访问。

我应该使用设计令牌吗 ?


设计令牌为现代 UI 架构提供了显著的好处,但它们可能并不适用于所有项目。

优点 包括:

  • 缩短设计变更的交付周期
  • 跨平台和技术保持一致的设计语言和 UI 架构
  • 从实现角度来看,设计令牌相对来说比较轻量级

缺点 包括:

  • 自动化所需的初始工作
  • 设计师可能需要在一定程度上与 Git 进行交互
  • 标准化仍在进行中

在决定是否采用设计令牌时,请考虑以下因素:

何时使用设计令牌

  1. 多平台或多应用程序环境: 当跨多个平台(Web,iOS,Android 等)工作,或者需要维护多个应用程序或前端时,设计令牌能确保在所有平台中使用一致的设计语言。
  2. 频繁的设计变更: 对于需要定期更新设计的环境来说,设计令牌提供了一种结构化的方式来高效管理和传播更改。
  3. 大型团队: 对于设计师和开发人员数量众多的团队,设计令牌有助于协作。
  4. 自动化工作流: 如果您熟悉 CI/CD 管道,那么添加一个设计令牌的管道的成本相对较低。而且目前也有商业化的解决方案。

何时不一定需要设计令牌

  1. 小型项目: 对于范围有限且设计复杂性较低的小型项目,管理设计令牌的开销可能不值得。
  2. 设计变更不是问题: 如果设计变更的速度、设计一致性以及设计和工程团队之间的协作不是问题,那么你可能也就不需要使用设计令牌。

更多 AI 辅助设计和设计灵感趋势,请关注公众号(设计小站):sjxz 00。

原文:https://martinfowler.com/articles/design-token-based-ui-architecture.html