组件属性因设计文件和编码框架而异。我们如何翻译和调整它们,以帮助设计师和开发人员更好地合作?
假同源词是看起来相似但含义不同的词;异义词是拼写相同但发音不同的单词。
语言既是沟通的障碍,也是沟通的催化剂。正确的词可以准确地描述一种感觉,并推动围绕一个想法的一致性。同时,听起来相似的单词在不同语言中可能具有截然不同的含义,或者在同一语言中具有不同的发音和含义——尽管拼写相同。当然,有效沟通不仅关乎我们使用的词语,还关乎我们带来的观点和我们分享(或不分享)的背景。
设计和工程语言也是如此。作为一名工程师,我使用许多与设计同行相同的术语,但我们不同的环境和上下文往往会导致我们对这些术语的解释方式存在差异。这是一件好事——我们应该利用彼此独特的专业知识。问题是,当我们认为我们在谈论同一件事时,我们不是。
当使用或实现组件时,它被视为组件的实例。这些实例是通过修改组件属性的值来执行该表达式的。
在深入探讨之前,让我们先从组件及其属性的一些松散定义开始。首先是组件。无论您是在 Figma 中进行设计,还是在代码库中将设计变为现实,您都熟悉这个概念。组件使构建和维护过程更加高效,并允许您在规模上保持一致。更具体地说,它们是可重用的元素,具有描述如何表达它们的规则。这些规则在组件中被定义为属性(或“props”,因为它们被亲切地称为)。道具描述了组件可能的行为和外观的各种方式,例如变体或交互状态。
“Boolean”(真/假值)是设计人员和开发人员如何从不同角度处理共享概念的一个很好的例子。这是开发人员刚开始时学习的工程概念;通常,设计师是通过与开发人员一起使用 Figma 来了解这个想法的。
正是在这种独特的框架下,我真诚地问:什么是财产?设计师和开发人员对道具很熟悉,但要通过他们独特的视角来看待它们。对于 Figma 的设计师来说,道具几乎完全用于标记视觉差异。Figma 道具有不同的类型,如变体、布尔值、实例交换和文本。开发人员共享很多这些概念,但可能会使用其他术语来描述它们;他们可能会使用相同的语言来描述微妙不同的东西。开发人员还具有非可视化属性,如事件处理程序或与数据相关的属性。从这个意义上说,他们对道具的目的的看法非常不同,即使对道具作为一个概念有共同的理解。也许这是显而易见的,也许这令人沮丧,但这是事实!让我们谈谈为什么。
按钮案例研究
在各种环境中混淆语言太容易了,尤其是在描述共享概念(如按钮)时。我们一直看到按钮。它们看起来很简单。但实际上,Figma 中的按钮与代码库中的按钮组件不是一回事。虽然我们有很多关于按钮视觉外观的语言,但设计人员正在考虑视觉一致性以及如何在文件中实现它们,而开发人员正在考虑交互性以及如何用代码呈现它们。相同的概念,不同的考虑。
Material UI 是一个基于 Google 的 Material Design 的开源项目。
在加入 Figma 之前,我曾在 React 和 TypeScript Web 应用程序上担任软件工程师,在他们迁移到与设计系统兼容的前端时,我就加入了他们。他们建立在 Material UI (MUI) 之上;虽然 MUI 提升了前端的层次,但其约定经常与专用设计系统的约定相冲突。像这样的迁移暴露了语言和理解方面的这些差异,并提供了通过有效沟通缩小这些差距的机会。它们还展示了像按钮这样简单的东西的想法是多么复杂。
这项工作背后的驱动力是构建易于全栈工程师实现的组件,他们的主要重点是将前端连接到后端。我们希望我们的工程师能够发现组件库直观且轻量级。我们的目标是创建一个易于访问且设计良好的前端,从而减少他们需要考虑风格和前端问题的数量。
我们在 Figma 中有两个按钮组件:Button 和 IconButton。这些 Figma 组件不像在代码库中那样共享原始组件。Figma 中不存在这种方式的组件继承。你甚至可能会争辩说,一个 Figma 按钮组件就足够了,我不会不同意。在我们的案例中,它们是分开的,因为这是从设计角度对我们的想法进行建模的最佳方式。一些道具(如尺寸和颜色变体)存在冗余,但我们对此感到满意。保持同步很容易,而且不会威胁到一致性。
除了 Figma 中的这两个按钮组件之外,我们的代码库中还有五个。五.Figma 中的 Button 和 IconButton 组件与代码库中的同名组件共享一个名称和概念,但它们与代码库中的对应组件没有相同的用途或包含三分之一的道具。在 Figma 中以设计人员身份实现组件不同于在代码库中以开发人员身份实现组件。当您针对组件的开发人员或设计人员体验进行优化时,该组件是针对该特定目的量身定制的,即使它与另一个环境中的组件共享名称也是如此。如果它的目的不同,它的模式也不同。我们不能将组件简化为一个名称。上下文至关重要。
你可能熟悉 JavaScript 框架的概念,或者你可能与框架无关(我看到并羡慕你!您可以为 Web 以外的平台构建。你可能也不知道我在说什么(👋你也属于这里)。无论如何,和我一起呆一会儿。粗略地说,这个组件库对代码库中的按钮概念具有以下组件继承:
这五个组件之一 MuiButtonBase 是一个现有组件,我们利用它来获取许多初始核心属性定义,这些定义在后台非常有用。这些都是不容易证明构建整个方法论的合理性,当你可以从盒子里拿出一个。ButtonPrimitive 是我们的自定义基础组件,它充当意识形态层,保护工程师实现的组件 (Button) 免受我们为自定义核心 MUI 基础组件所做的所有障碍跳跃。
对我来说,一个尤里卡的时刻是,我可以将可访问性要求融入到这些道具定义中。例如,和按钮的最佳做法是仅在按钮内容不描述操作时才提供这些标签。没有文本而只有一个图标的按钮不具有描述性,因此我们在 true 时强制执行为必需的道具。如果它为 false 的实例是可选的。我们添加了有关何时为此可选方案添加细微差别的注释和文档,以便开发人员在实现按钮时可以在编辑器中弹出该信息。这个原始层使我们能够在组件库级别强制执行和教育可访问性,这是一个巨大的胜利。aria-label
aria-label
button.iconOnly
aria-label
aria-label
其他三个组件(Button、IconButton 和 SpecialtyButton)是 ButtonPrimitive 的有限表达式,其明确用途是易于实现。它们可能共享一些 ButtonPrimitive 属性定义(大小变体、颜色变体、onClick 等),但某些道具是硬编码的。例如,当 ButtonPrimitive 被 Button 组件包装时,该逻辑被硬编码为幕后逻辑,而 IconButton 组件被硬编码为 .实施工程师不需要知道这些!这些类型只是工作,这些组件可以以最少的开销实现,让开发人员在功能开发工作过程中实现按钮。他们所知道的就是这一点,并且是他们实现 IconButton 时需要的道具。iconOnly
iconOnly={false}
iconOnly={true}
aria-label
icon
代码库中的组件具有非常特定的用途,超出了可视层。像按钮这样简单的东西可以有许多组件。是的,Figma 中有组件,代码库中有组件。是的,在设计系统的情况下,它们可以描述相同的用户界面。不,这并不意味着它们是相同的组件,不,它们不需要使用完全相同的语言。这并不是说我们需要在所有上下文中都使用统一的语言;相反,我们只需要足够的背景信息来让每个人都在同一页面上。
我们共同的词汇
在实践中,我们什么时候应该使用不同的词——一个在设计中,一个在开发中——什么时候可以使用相同的语言?变体组件属性(如 OR)是最容易对齐的,并且通常包含开发人员在设计中实现组件时需要了解的大部分内容。然而,有趣的是,单词大小写的差异经常在这里成为不必要的障碍。一个快速的胜利是同意将你的道具和变体选项命名为同样的东西。与我合作过的许多设计师都擅长命名,开发人员通常对格式有要求(答案几乎总是 camelCase)。发挥彼此的长处。size: "small" | "medium" | "large"
variant: "primary" | "secondary" | "basic" | "danger" | "success"
即使是这种对齐语言的“简单”案例也并非没有警告。一个障碍是,在 Figma 中对组件的交互式默认、悬停、焦点和按下“状态”进行建模的唯一方法是使用变体。状态不是 Web 世界中的变体属性;这是主题变体中的一种风格(“主要”、“危险”等)。我们在这里做什么?我们可以在 Figma 属性定义中添加一个前缀,如“:state”或“*state”,表示该属性不是代码库中的属性。
我们可以做的另一件事是对齐这些状态的术语,以反映平台中的相应术语。就网络而言,我们可能更喜欢“初始的、焦点可见的、活动的”,而不是“默认的、焦点的、按下的”。CSS 和 之间的差异远比在此设计变体级别上更有意义。当然,你要在多大程度上调整这些是由你来决定的。将CSS实现的细微差别引入组件语言可能会弊大于利。有机会在两个方向上保持一致,更清楚地了解对方的需求将有助于你们共同做出这些让步。:focus
:focus-visible
哦,当一个按钮被禁用时呢?设计人员可能会将“disabled”添加到前面提到的“:state”变体中,因为它与其他状态是互斥的。这很有道理。然而,在代码中,除了单独的样式之外,它通常还有自己的道具。在这种情况下,最好在 Figma 中将其保留为状态变体选项,但也在代码库中将其表示为禁用的布尔属性。有时,在描述具有属性的组件的方式上会有所不同。那也没关系。您仍然可以在“disabled”一词的大小写上对齐。disabled
在 Figma,我们不断改进我们的产品,以满足人们的需求,这总是需要一定程度的采用实现模式。但是,我们还需要像工程师考虑开发人员人体工程学一样考虑设计师的人体工程学。我们的目标之一是尽可能加快产品开发过程,而要做到这一点,就需要完全采用设计工具中的设计体验,就像我们在开发工具中采用开发人员体验一样。
随之而来的是语言的差异,因为环境的差异。今天,最重要的是,我们学会识别这些差异,并专注于如何共同努力,用我们面前的工具定义的语言来描述我们正在构建的内容。否则,我们最终会试图强行从众,这将不可避免地使某些需求得不到满足,并且总是落后。
具体细节会根据您正在构建的产品而变化,但让我们同意在可能的情况下进行调整,并在这种调整导致摩擦时进行对话。最好的前进道路始终是相对于你的独特方案的优先级组合。因此,如果我们不能一直使用完全相同的语言,我们如何更好地跨上下文和框架进行翻译?
Component Inspector 插件的幕后花絮
我构建了一个插件来回答这个确切的问题。当开发人员浏览包含 UI 组件的设计文件时,除了组件名称之外,他们主要关心一些特定属性的值。我想为专注于属性语言而不是视觉样式的组件生成代码。这个 Component Inspector 插件就是这样做的,它为不同组件框架(Angular、React、Vue 和 Web Components)中的组件定义和实例显示代码。它主动忽略了 Figma 端的一些属性语言,以及代码端一些不重叠的属性语言,目的是生成足够的代码来帮助开发人员以精确的语言翻译视觉样式。创建这个插件是我接受这种共享语言理念的最平易近人的方式——反映我认为在实践中最有用的版本。
我去年发布了这个插件,当时我们推出了 Dev Mode,这是 Figma 中开发人员的专用工作区。现在开发模式已经上线,我构建了一个部分来显示 Figma 中插件的代码。
Component Inspector 插件生成描述组件属性的代码。
我要做的第一件事是指定插件在开发模式下以不同的方式运行。我想暂时在 Figma 中保留相同的功能。我更新了我的manifest.json以告诉Figma这也适用于开发模式,并且启用了代码生成。我还可以在此处指定它生成的语言。
codegen 的 Component Inspector manifest.json 中的相关部分。查看 GitHub 存储库中的完整文件。
Json格式
{ "name": "Component Inspector", "editorType": ["dev", "figma"], "capabilities": ["codegen"], "codegenLanguages": [ { "label": "Angular", "value": "angular" }, { "label": "React", "value": "react" }, { "label": "Vue: Composition API", "value": "vue-composition" }, { "label": "Vue: Options API", "value": "vue-options" }, { "label": "Web Components", "value": "web" }, { "label": "JSON", "value": "json" } ], "codegenPreferences": [ { "itemType": "select", "propertyName": "boolean", "label": "Boolean properties on instances", "options": [ { "label": "Implicit", "value": "implicit", "isDefault": true }, { "label": "Explicit", "value": "explicit" } ] }, { "itemType": "select", "propertyName": "comments", "label": "Comment generation in definitions", "options": [ { "label": "Disabled", "value": "disabled", "isDefault": true }, { "label": "Enabled", "value": "enabled" } ] }, { "itemType": "select", "propertyName": "defaults", "label": "Default values on instances", "options": [ { "label": "Shown", "value": "shown", "isDefault": true }, { "label": "Hidden", "value": "hidden" } ] }, { "itemType": "action", "propertyName": "settings", "label": "More Settings" } ] }
然后在 codegen 模式下,每当 codegen 事件触发时,我都会返回代码。
示例 codegen 插件“generate”事件处理程序。在 GitHub 上查看我们的插件示例存储库,获取示例 codegen 插件。
JavaScript的
if (figma.mode === "codegen") { figma.codegen.on("generate", async (event) => { const { node, language } = event; if (language === "html") { return [ { title: `My HTML`, code: `<p>${node.name}</p>`, language: "HTML", }, ]; } else if (language === "css") { return [ { title: `My CSS`, code: `.${node.name} { color: red; }`, language: "CSS", }, ]; } }); }
该插件一直包含我们在这里介绍的一些概念。您可以选择要忽略的属性前缀(例如,“:state”中的“:”)。这样,开发人员就只能通过隐藏其余组件属性来查看相关组件属性的代码。它还允许您将文本属性配置为“插槽”,因此您甚至可以指定要在该插槽中呈现的元素类型。
“可选”元素属性是代码框架中的常见模式。当您有类似“图标”的东西并且它可能不存在时,就会发生这种情况。在 Figma 中,您可以将其建模为两个属性——“hasIcon”(布尔值)控制“icon”(实例交换)的可见性。在代码中,这只是一个可选的图标属性。当图标属性为“undefined”时,隐含“has icon”。为了将两个 Figma 属性转换为一个属性,插件必须检测实例交换 (“icon”) 属性的可见性是否引用了布尔属性 (“hasIcon”)。在这种情况下,它知道要设为可选,而不是为“hasIcon”属性生成代码。icon
另一种“可选”模式是变体可以未定义。在 Figma 中没有很好的方法来显示这一点。因此,该插件允许用户指定一个关键字来命名默认的变体值(类似于“undefined”)。然后,插件将检测到这种情况,并在该情况下将该变体属性视为可选属性。codegen 插件可以在弹出窗口中显示用户特定的设置,也可以直接在 UI 的首选项菜单中显示这些设置。我将此菜单用于组件检查器设置:“实例上的隐式或显式布尔属性”和“显示或隐藏实例的默认值”。
这个插件并不适合所有人,这就是重点。开发人员有非常具体的需求。我们知道这一点。插件(尤其是面向所有组织的私有插件)是根据特定代码库需求定制设计语言翻译的最佳方式。
如果您对构建插件感兴趣,请查看这些资源,并随时发送有关我们 API 的反馈:
组件检查器源代码
插件示例:
存储 库
Codegen 示例和开发模式示例
变量的导入/导出和样式
插件文档
Figma 工程师 Sawyer Hood 的 Config 谈论如何为开发模式构建插件
设计师和开发人员之间没有完美的翻译,但当我们接受环境中的差异并理解我们如何谈论它们的细微差别时,我们就可以更好、更共同、更高效地构建。但要做到这一点,我们必须修改我们对变量、组件和属性等术语的严格定义,以带来精确性,而不是失真。