# Workday Canvas 设计系统

## Workday Canvas 设计系统界面预览

筛选课程标题、添加课程（每门仅限一次）、移除已选课程，并查看实时总时长。

## Workday Canvas 设计系统设计参考

当前 Canvas Kit 源码和 Canvas 设计令牌 sys.json。下方的原始 JSON 名称是源码路径，并非臆造的生成式 CSS 变量名称。与基础设计令牌别名对应的值会标注为别名；带日期的迁移参考明确标注为历史内容。

### 当前间距语义

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| sys.padding.xs | {base.size.100} | 输入框的水平内边距角色。 |
| sys.padding.md | {base.size.200} | 组件默认内边距。 |
| sys.padding.xl | {base.size.300} | 卡片内边距。 |
| sys.gap.sm | {base.size.100} | 紧凑布局中的相邻元素间距。 |
| sys.gap.md | {base.size.200} | 默认行内间距。 |
| sys.gap.xl | {base.size.400} | 卡片内侧留白。 |
| sys.gap.xxl | {base.size.800} | 区块间距。 |
### 当前字体与形状

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| sys.type.body.sm | font-size body.sm + line-height body.sm + normal weight | 复合正文文本角色。 |
| sys.font-size.body.sm | {base.size.200} | 正文基础字号别名。 |
| sys.line-height.body.sm | {base.size.300} | 正文行高别名。 |
| sys.font-weight.normal | {base.font-weight.400} | 当前名称为 normal，而非 regular。 |
| sys.shape.sm | {base.size.50} | 小圆角。 |
| sys.shape.md | {base.size.100} | 输入框圆角。 |
| sys.shape.full | {base.size.75} × 100 | 全圆角。 |
### FormField 组合方式

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| FormField.Label | Connected label element | 与输入框的生成式关联。 |
| FormField.Input as={TextInput} | Polymorphic input slot | 这不是旧版的 label 属性 API。 |
| FormField.Hint | Linked helper content | 说明输入框的预期内容。 |
| orientation | vertical / horizontalStart / horizontalEnd | 使用当前支持的对齐方式。 |
| FormField.Field | Input and hint alignment wrapper | 适用于水平布局。 |
### TextInput 实现

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| grow | 100% width when true | 输入框的响应式扩展。 |
| width | number or string | 可选的覆盖值。 |
| minimum width fallback | px2rem(280) | 检查狭窄父容器中的布局。 |
| error | error / caution styling | 由应用提供验证逻辑。 |
| transition | 0.2s box-shadow, 0.2s border-color | 源码中的实际转换。 |
| font size role | system.legacy.fontSize.subtext.lg | 不要用无关的标题类型替换组件内部实现。 |
### PrimaryButton 实现

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| Background | brand.action.base, with legacy accent fallback | 租户品牌样式。 |
| Hover | brand.action.dark or colorSpace.hover | 状态颜色根据操作颜色派生。 |
| Pressed | brand.action.darkest or colorSpace.pressed | 明显的按压态样式。 |
| variant="inverse" | Inverse surface and foreground roles | 适用于对比度合适的背景。 |
| Border radius | system.legacy.shape.full | 当前源码仍保留旧版兼容性。 |
### 迁移参考，不是当前设计令牌名称

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| Former space.s → system.space.x4 | 1rem in 2023 migration table | 当前源码将内边距与间距区分开来。 |
| Former space.m → system.space.x6 | 1.5rem in 2023 migration table | 不要假定旧名称仍是首选。 |
| Former borderRadius.m → system.shape.x1 | 0.25rem in 2023 migration table | 当前源码使用语义化的形状尺寸。 |
## Workday Canvas 设计系统完整指南

实用指南，介绍如何为真实界面选择、应用和评审此系统。

完整指导、实现起点和源代码链接。

## Workday Canvas 设计系统：复制迁移示例前，先阅读当前源码

### 间距现在按用途区分

> Canvas 的间距体系从 canvas-kit-react/tokens 中的 T 恤尺码间距常量，演进到带编号的 system.space 设计令牌，再发展为 padding 和 gap 等源码分类。当前 sys.json 通过基础设计令牌引用来表达这些分类对应的底层值。本参考保留了实际路径，而不是通过拼接单词臆造 CSS 变量。

在转换旧版应用时，迁移表仍然很有参考价值：它解释了为什么过去的 space.s 对应 1rem。不过，这只是历史依据，并不意味着所有旧版导出项都是当前首选接口。预览中使用的是应用自行选定的 16 和 24px 间隔；设计令牌名称应以源码表为准。

阅读配套规范

## Workday Canvas 设计系统：颜色由租户和组件状态决定

### 主按钮不只是一个固定的蓝色色块

PrimaryButton 会先读取 brand.action.base；如果没有该值，则回退到旧版主强调色。悬停态和按压态样式也会先查询 brand 操作颜色角色，再通过 colorSpace 辅助函数派生状态颜色。因此，仅更改一个硬编码的背景色无法复现组件的主题约定。

原始学习预览采用克制的蓝色应用配色，并明确标注为一种 HTML 解读。它并非某个特定客户租户或新版 Sana Canvas 主题的渲染效果。完整的原生示例会使用已安装的设计令牌层来提供组件颜色。

阅读配套规范

## Workday Canvas 设计系统：复合角色保留行高和字重

### 字号别名并不等于完整的文本样式

当前的 body.sm 文本对象组合了字体系列、normal 字重、正文字号和正文行高。标题角色使用粗体字重。若只从旧截图中挑选一个像素字号，这些关联就会丢失。源码使用 normal，而不是旧版迁移说明中出现的 regular 键。

Canvas Kit 组件包不包含字体。字体加载应作为独立的集成决策；离线示例使用系统备用字体，不会声称内置了 Roboto 或 Sana 字体。

阅读配套规范

## Workday Canvas 设计系统的 FormField 是一个复合组件

### 让字段组合标签、输入框和提示

源代码提供 FormField.Label、FormField.Input 和 FormField.Hint。将 TextInput 作为 prop 传给 Input，可让模型生成无障碍关联。这与旧示例不同：旧示例会在外层字段上设置 label prop，并直接放入未封装的输入框。

对于水平表单，当前的方向值为 horizontalStart 和 horizontalEnd。FormField.Field 会在该对齐方式下将输入框与提示组合在一起。我们的筛选器仍采用垂直布局，因为说明文字应紧跟在标签和控件下方。

- 在 FormField.Label 中放置清晰可见的标签。

- 通过复合组件的 Input 插槽传入 TextInput。

- 使用 Hint 说明筛选范围。

- 添加约束时，将验证逻辑保留在应用中。

阅读配套规范

## Workday Canvas 设计系统中，可扩展的输入框仍需要灵活的父容器

### 考虑最小宽度

TextInput 的备用最小宽度为 280px，在源代码中通过 px2rem 表示。grow 会请求使用完整宽度，但不能让僵硬的双列父容器自动适应窄视口。离线目录会在移动设备上将计划侧栏移至课程卡片下方，并允许各布局列收缩。

搜索值是普通的 React 状态。应用会对三个课程标题执行不区分大小写的子串匹配，并明确显示空结果。筛选器不会清除已选课程，因为目录可见性与计划成员资格是不同的状态。

阅读配套规范

## Workday Canvas 设计系统中，选择状态独立于可见结果而保留

### 使用稳定的课程 ID 并推导总时长

每门原始课程都有 ID、标题和时长。选中列表保存 ID，显示的总时长则根据课程记录推导得出。筛选目录不会移除已选项目。移除项目后，其“添加”操作会立即恢复，且总时长会相应减少。

这是一个原创的本地规划场景。它不会调用 Workday API、为员工办理注册，也不会修改学习记录。明确这一界限，可避免原型被误认为会执行真实的业务交易。

1. 仅筛选目录视图。

2. 每个课程 ID 仅添加一次。

3. 根据这些 ID 推导已选课程和总分钟数。

4. 移除已选项目，但不重置搜索。

阅读配套规范

## Workday Canvas 设计系统中，使用主题组件前应先加载令牌

### Provider 与 CSS 层各司其职

当前的入门示例会在应用入口处导入基础、品牌、组件和系统变量样式，然后使用 CanvasProvider 包裹界面。仅使用 provider 不能替代这些 CSS 定义。此处的代码遵循这一组合方式，并提供完整的 React 根节点。

Sana Canvas 为可选项：其变量会在系统变量之后加载，文档中所述的 html data-theme 属性用于选择该主题。无法控制文档根节点的嵌入式应用，可通过专用的 provider 主题选项将主题传递到弹出窗口。升级软件包时，不要意外将该主题添加到现有租户。

阅读配套规范

## Workday Canvas 设计系统组件示例

### 完整的独立交互示例

将完整文档保存为 index.html，然后在浏览器中打开。这是对 Canvas 交互模式和克制的企业级层级结构的原创离线 HTML 解读。配色和布局由应用自行决定，并非 Sana 租户的实际渲染效果。

```
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Canvas learning plan interpretation</title><style>*{box-sizing:border-box}body{margin:0;background:#f3f5f7;color:#20272f;font:16px/1.5 system-ui,sans-serif}button,input{font:inherit}header{background:white;border-bottom:1px solid #d5dbe0;padding:16px 24px;display:flex;gap:12px;align-items:center}.mark{width:32px;height:32px;border:4px solid #ed9b28;border-bottom:0;border-radius:50% 50% 0 0;height:16px;margin-right:8px}header span{font-size:14px;color:#59616b;margin-left:auto}main{max-width:1100px;margin:32px auto;padding:0 24px}h1{font-size:28px;line-height:36px;margin:0 0 8px}p{margin:0 0 24px}label{display:block;font-size:14px;font-weight:600;margin-bottom:8px}#search{width:100%;max-width:580px;height:40px;border:1px solid #89949f;border-radius:8px;padding:8px 12px;background:white}.hint{font-size:12px;color:#59616b;margin:8px 0 24px}.layout{display:grid;grid-template-columns:1fr 290px;gap:24px;align-items:start}.course{display:grid;grid-template-columns:96px 1fr;gap:24px;background:white;padding:24px;border-radius:8px;border:1px solid #d5dbe0;margin-bottom:16px;min-width:0}.art{height:96px;display:grid;place-items:center;border-radius:8px;font-size:40px;color:#005cb9;background:#e7f1fd}.art.orange{background:#fff0d6;color:#805200}.art.green{background:#e6f4ed;color:#236443}.category{font-size:12px;color:#59616b;text-transform:uppercase;letter-spacing:1px}h2{font-size:20px;line-height:28px;margin:4px 0 8px}.course p{font-size:14px;margin:0 0 16px;color:#59616b}.row{display:flex;justify-content:space-between;align-items:center;gap:16px}.row span{font-size:14px;color:#59616b}button{border:0;border-radius:999px;background:#005cb9;color:white;font-size:14px;font-weight:600;padding:8px 16px;cursor:pointer}button:disabled{opacity:.5;cursor:default}button:focus-visible,input:focus-visible{outline:3px solid #005cb9;outline-offset:3px}.plan{padding:24px;background:white;border:1px solid #d5dbe0;border-radius:8px}.plan h2{margin:0 0 16px}.number{font-size:36px;font-weight:700;line-height:1.2}.plan-total{padding-bottom:20px;border-bottom:1px solid #d5dbe0;margin-bottom:20px}.plan-total span{font-size:14px}.plan ul{padding:0;list-style:none;margin:0}.plan li{border-bottom:1px solid #e6e9ed;padding:0 0 16px;margin:0 0 16px;font-size:14px}.remove{background:transparent;color:#005cb9;padding:4px 0;display:block;border-radius:3px;text-decoration:underline;font-weight:400}.empty{font-size:14px;color:#59616b}.boundary{font-size:12px;color:#59616b;margin-top:24px}.no-results{background:white;border:1px solid #d5dbe0;padding:24px;border-radius:8px}@media(max-width:720px){main{padding:0 16px;margin:24px auto}.layout{grid-template-columns:1fr}.course{padding:16px;grid-template-columns:60px 1fr;gap:16px}.art{height:60px;font-size:28px}header span{display:none}.row{align-items:flex-start;flex-direction:column;gap:8px}.plan{padding:20px}}
</style><header><div class="mark" aria-hidden="true"></div><strong>Learning workspace</strong><span>Personal development · Local plan</span></header><main><h1>Make room to grow</h1><p>Choose a few focused lessons for your learning plan.</p><label for="search">Find a course</label><input id="search" type="search" placeholder="Search course titles" aria-describedby="hint"><p id="hint" class="hint">Search filters the catalog; your selected courses stay in the plan.</p><div class="layout"><section id="catalog" aria-label="Course catalog"></section><aside class="plan"><h2>Your local plan</h2><div class="plan-total" role="status"><div class="number" id="minutes">0</div><span>minutes · <span id="count">0</span> <span id="course-word">courses</span></span></div><ul id="selected"></ul><p class="empty" id="empty">Add a course to start your plan.</p><small>No enrollment is submitted.</small></aside></div><p class="boundary">Independent HTML interpretation of Canvas composition patterns. Original palette, course illustrations and content; no Workday account or enrollment service is connected.</p></main><script>const courses=[{id:'feedback',name:'Giving useful feedback',minutes:25,category:'Communication',description:'Practice clear observations and constructive next steps.',art:'◎',color:''},{id:'planning',name:'Planning a focused week',minutes:20,category:'Personal effectiveness',description:'Protect attention and organize a realistic working week.',art:'▤',color:'orange'},{id:'meetings',name:'Running inclusive meetings',minutes:30,category:'Teamwork',description:'Make space for different voices and useful decisions.',art:'◌',color:'green'}];let chosen=new Set;const catalog=document.querySelector('#catalog');function render(){let matches=courses.filter(c=>c.name.toLowerCase().includes(document.querySelector('#search').value.toLowerCase()));catalog.innerHTML=matches.length?matches.map(c=>`<article class="course"><div class="art ${c.color}" aria-hidden="true">${c.art}</div><div><div class="category">${c.category}</div><h2>${c.name}</h2><p>${c.description}</p><div class="row"><span>${c.minutes} minutes</span><button data-add="${c.id}" ${chosen.has(c.id)?'disabled':''}>${chosen.has(c.id)?'Added to plan':'Add to plan'}</button></div></div></article>`).join(''):'<div class="no-results">No matching courses. Try “feedback” or clear your search.</div>';let selected=courses.filter(c=>chosen.has(c.id));document.querySelector('#minutes').textContent=selected.reduce((sum,c)=>sum+c.minutes,0);document.querySelector('#count').textContent=selected.length;document.querySelector('#course-word').textContent=selected.length===1?'course':'courses';document.querySelector('#empty').hidden=selected.length>0;document.querySelector('#selected').innerHTML=selected.map(c=>`<li>${c.name}<button class="remove" data-remove="${c.id}" aria-label="Remove ${c.name}">Remove</button></li>`).join('');document.querySelectorAll('[data-add]').forEach(b=>b.onclick=()=>{let id=b.dataset.add;chosen.add(id);render();document.querySelector('[data-remove="'+id+'"]').focus()});document.querySelectorAll('[data-remove]').forEach(b=>b.onclick=()=>{let id=b.dataset.remove;chosen.delete(id);render();let add=document.querySelector('[data-add="'+id+'"]');(add||document.querySelector('#search')).focus()})}document.querySelector('#search').oninput=render;render();</script></html>
```

代码参考

- 添加课程会更改示例学习计划。原生示例还会单独展示 CanvasProvider 和复合 FormField API；不会提交注册操作。

### 复合字段与可选学习项目

FormField 会生成输入框关联关系，TextInput 会筛选原创课程数据，每个 PrimaryButton 只会添加一次对应项目。移除项目后，所选课程总时长会重新计算。

```
// In a React 18+ application with a bundler:
// npm install @workday/canvas-kit-react @workday/canvas-tokens-web @workday/canvas-kit-styling @emotion/react @emotion/styled
import React,{useState} from 'react';
import {createRoot} from 'react-dom/client';
import {CanvasProvider} from '@workday/canvas-kit-react/common';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
import {PrimaryButton} from '@workday/canvas-kit-react/button';
import {system} from '@workday/canvas-tokens-web';
import {cssVar} from '@workday/canvas-kit-styling';
import '@workday/canvas-tokens-web/css/base/_variables.css';
import '@workday/canvas-tokens-web/css/brand/_variables.css';
import '@workday/canvas-tokens-web/css/component/_variables.css';
import '@workday/canvas-tokens-web/css/system/_variables.css';
// Default theme example; do not silently add a Sana theme to an existing tenant.
const courses=[{id:'feedback',name:'Giving useful feedback',minutes:25},{id:'planning',name:'Planning a focused week',minutes:20},{id:'meetings',name:'Running inclusive meetings',minutes:30}];
function LearningPlan(){
 const [query,setQuery]=useState(''),[chosen,setChosen]=useState([]);
 const visible=courses.filter(c=>c.name.toLowerCase().includes(query.toLowerCase()));
 const selected=courses.filter(c=>chosen.includes(c.id));
 return <main style={{fontFamily:cssVar(system.fontFamily.default),maxWidth:900,margin:'auto',padding:24}}>
 <h1>Build a learning plan</h1>
 <FormField><FormField.Label>Find a course</FormField.Label>
  <FormField.Input as={TextInput} grow value={query} onChange={e=>setQuery(e.target.value)}/>
  <FormField.Hint>Filter the three example course titles.</FormField.Hint>
 </FormField>
 <ul>{visible.map(c=><li key={c.id} style={{margin:'24px 0'}}><h2>{c.name}</h2><p>{c.minutes} minutes</p>
  <PrimaryButton type="button" disabled={chosen.includes(c.id)} onClick={()=>setChosen(old=>[...old,c.id])}>
   {chosen.includes(c.id)?'Added to plan':'Add to plan'}
  </PrimaryButton></li>)}</ul>
 {!visible.length&&<p>No matching courses.</p>}
 <section aria-label="Learning plan"><h2>Your local plan</h2><p role="status">{selected.length} courses · {selected.reduce((sum,c)=>sum+c.minutes,0)} minutes</p>
 <ul>{selected.map(c=><li key={c.id}>{c.name} <button type="button" onClick={()=>setChosen(old=>old.filter(id=>id!==c.id))}>Remove {c.name}</button></li>)}</ul>
 <p>No enrollment is submitted.</p></section></main>;
}
createRoot(document.getElementById('root')).render(<CanvasProvider><LearningPlan/></CanvasProvider>);
```

代码参考

- 每个独立课程项目都配有 PrimaryButton；整个计划不会提供容易造成误导的注册操作。

- 移除操作使用明确由应用负责的原生按钮。

- 禁用的“已添加”状态可防止重复添加，动态计数则会确认更改。

## Workday Canvas 设计系统 AI Agent 配置

### 实现起点

适用于打包应用的完整 React 组合。当前的 CanvasProvider 和令牌 CSS 配置；原创课程数据与原生移除控件。不执行远程注册。已检查源代码，但未在此工作区中编译软件包。

```
// In a React 18+ application with a bundler:
// npm install @workday/canvas-kit-react @workday/canvas-tokens-web @workday/canvas-kit-styling @emotion/react @emotion/styled
import React,{useState} from 'react';
import {createRoot} from 'react-dom/client';
import {CanvasProvider} from '@workday/canvas-kit-react/common';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
import {PrimaryButton} from '@workday/canvas-kit-react/button';
import {system} from '@workday/canvas-tokens-web';
import {cssVar} from '@workday/canvas-kit-styling';
import '@workday/canvas-tokens-web/css/base/_variables.css';
import '@workday/canvas-tokens-web/css/brand/_variables.css';
import '@workday/canvas-tokens-web/css/component/_variables.css';
import '@workday/canvas-tokens-web/css/system/_variables.css';
// Default theme example; do not silently add a Sana theme to an existing tenant.
const courses=[{id:'feedback',name:'Giving useful feedback',minutes:25},{id:'planning',name:'Planning a focused week',minutes:20},{id:'meetings',name:'Running inclusive meetings',minutes:30}];
function LearningPlan(){
 const [query,setQuery]=useState(''),[chosen,setChosen]=useState([]);
 const visible=courses.filter(c=>c.name.toLowerCase().includes(query.toLowerCase()));
 const selected=courses.filter(c=>chosen.includes(c.id));
 return <main style={{fontFamily:cssVar(system.fontFamily.default),maxWidth:900,margin:'auto',padding:24}}>
 <h1>Build a learning plan</h1>
 <FormField><FormField.Label>Find a course</FormField.Label>
  <FormField.Input as={TextInput} grow value={query} onChange={e=>setQuery(e.target.value)}/>
  <FormField.Hint>Filter the three example course titles.</FormField.Hint>
 </FormField>
 <ul>{visible.map(c=><li key={c.id} style={{margin:'24px 0'}}><h2>{c.name}</h2><p>{c.minutes} minutes</p>
  <PrimaryButton type="button" disabled={chosen.includes(c.id)} onClick={()=>setChosen(old=>[...old,c.id])}>
   {chosen.includes(c.id)?'Added to plan':'Add to plan'}
  </PrimaryButton></li>)}</ul>
 {!visible.length&&<p>No matching courses.</p>}
 <section aria-label="Learning plan"><h2>Your local plan</h2><p role="status">{selected.length} courses · {selected.reduce((sum,c)=>sum+c.minutes,0)} minutes</p>
 <ul>{selected.map(c=><li key={c.id}>{c.name} <button type="button" onClick={()=>setChosen(old=>old.filter(id=>id!==c.id))}>Remove {c.name}</button></li>)}</ul>
 <p>No enrollment is submitted.</p></section></main>;
}
createRoot(document.getElementById('root')).render(<CanvasProvider><LearningPlan/></CanvasProvider>);
```

### 适用于你项目的提示词

```
使用当前的 Canvas FormField 组合方式和令牌 CSS 层级。课程筛选与选择状态应彼此独立，避免重复添加，并根据课程计算总分钟数。通过语义角色保留租户主题；不要将历史编号的间距名称误认为当前推荐的来源类别。
```

## Workday Canvas 设计系统来源与范围

独立的 OpenDesign 参考。此处不涉及员工报名或 Workday API。明确区分当前来源名称与较早的迁移名称。

- Canvas Kit 设置 当前导入项、Provider 与 Sana 边界。

- 当前系统令牌来源 间距类别、排版与形状别名。

- FormField 来源 复合组件 API 与方向。

- TextInput 来源 尺寸与状态样式。

- PrimaryButton 来源 适配品牌的状态角色。

- Button 来源 继承行为与图标方向。

- 历史迁移 较早的名称仅作为迁移参考保留。

## Workday Canvas 设计系统相关资源

### Zendesk Garden

面向支持工作的 Garden 指南，采用真实的主题别名、字体和圆角比例，配备完整的回复编辑器、根据可见状态保存的本地草稿，以及字段恢复功能。

### Adeo Mozaic

使用 Mozaic 的 16px 魔法单位、尺寸匹配的 48px 控件、实际 SCSS 导入和 BEM 类，构建实用的数量控件组合。货架规划器可直观呈现取值范围、输入错误和本地材料汇总。

### Adobe Spectrum

通过可选择的创意素材表格和导出预设检查器，了解 React Spectrum v3。掌握 Provider 主题和输入缩放、受支持的样式属性、带键集合，以及 v3 与较新 Spectrum 2 实现之间的界限。


- [实现参考](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/form-field/lib/FormField.tsx)
- [来源](https://raw.githubusercontent.com/Workday/canvas-tokens/main/packages/canvas-tokens/tokens/web/sys.json)
- [来源](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/form-field/lib/FormField.tsx)
- [来源](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/text-input/lib/TextInput.tsx)
- [来源](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/button/lib/PrimaryButton.tsx)
- [来源](https://github.com/Workday/canvas-tokens/discussions/77)
- [阅读配套规范](https://raw.githubusercontent.com/Workday/canvas-tokens/main/packages/canvas-tokens/tokens/web/sys.json)
- [阅读配套规范](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/button/lib/PrimaryButton.tsx)
- [阅读配套规范](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/form-field/lib/FormField.tsx)
- [阅读配套规范](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/text-input/lib/TextInput.tsx)
- [阅读配套规范](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/button/lib/Button.tsx)
- [阅读配套规范](https://github.com/Workday/canvas-kit)
- [代码参考](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/form-field/lib/FormField.tsx)
- [Canvas Kit 设置 当前导入项、Provider 与 Sana 边界。](https://github.com/Workday/canvas-kit)
- [当前系统令牌来源 间距类别、排版与形状别名。](https://raw.githubusercontent.com/Workday/canvas-tokens/main/packages/canvas-tokens/tokens/web/sys.json)
- [FormField 来源 复合组件 API 与方向。](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/form-field/lib/FormField.tsx)
- [TextInput 来源 尺寸与状态样式。](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/text-input/lib/TextInput.tsx)
- [PrimaryButton 来源 适配品牌的状态角色。](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/button/lib/PrimaryButton.tsx)
- [Button 来源 继承行为与图标方向。](https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/react/button/lib/Button.tsx)
- [历史迁移 较早的名称仅作为迁移参考保留。](https://github.com/Workday/canvas-tokens/discussions/77)
