01 Workday Canvasデザインシステムのインターフェースプレビュー 講座名を絞り込み、講座を一度だけ追加し、選択を解除して、合計時間をリアルタイムで比較できます。
Canvasのインタラクションパターンと、抑制の効いたエンタープライズ向け階層を独自に解釈したオフライン版HTMLです。配色とレイアウトはアプリケーション側の選択であり、Sanaのテナント表示を再現したものではありません。 ·
実装リファレンス 02
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.smfont-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.LabelConnected label element入力欄との生成された関連付け。 FormField.Input as={TextInput}Polymorphic input slot以前のlabelプロパティAPIではありません。 FormField.HintLinked helper content入力内容に期待される形式を説明します。 orientationvertical / horizontalStart / horizontalEnd現在の配置値を使用してください。 FormField.FieldInput and hint alignment wrapper横並びレイアウトで便利です。
名前 / ルール 値 / 設定 用途 grow100% width when true画面幅に応じて入力欄を拡張。 widthnumber or string任意の上書き設定。 minimum width fallbackpx2rem(280)幅の狭い親レイアウトで確認してください。 errorerror / caution stylingバリデーションはアプリケーション側で行います。 transition0.2s box-shadow, 0.2s border-colorソースで実際に使われている状態遷移。 font size rolesystem.legacy.fontSize.subtext.lgコンポーネント内部の実装を、無関係なタイトル用の型に置き換えないでください。
名前 / ルール 値 / 設定 用途 Backgroundbrand.action.base, with legacy accent fallbackテナントのブランド設定。 Hoverbrand.action.dark or colorSpace.hover状態はアクションカラーから導きます。 Pressedbrand.action.darkest or colorSpace.pressed押下時に異なるスタイルを適用。 variant="inverse"Inverse surface and foreground roles適切なコントラストが得られる背景で使用してください。 Border radiussystem.legacy.shape.fullソースでは現在も旧仕様との互換性が維持されています。
移行時のリファレンスであり、現在のトークン名ではありません ソース 名前 / ルール 値 / 設定 用途 Former space.s → system.space.x41rem in 2023 migration table現在のソースでは、パディングと間隔が分かれています。 Former space.m → system.space.x61.5rem in 2023 migration table以前の名称が引き続き推奨されているとは限りません。 Former borderRadius.m → system.shape.x10.25rem in 2023 migration table現在のソースでは、形状サイズに意味のある名称を使用しています。
03 Workday Canvasデザインシステム完全ガイド 実際のインターフェースで、このデザインシステムを選び、適用し、レビューするための実践ガイドです。
DESIGN-workday-canvas.md 詳しいガイダンス、実装の出発点、ソースへのリンクをまとめています。
ガイドをダウンロード Workday Canvasデザインシステム:移行例をコピーする前に、現在のソースを確認してください スペーシングは用途ごとに定義されています Canvasのスペーシングは、canvas-kit-react/tokensのTシャツサイズ定数から、番号付きのsystem.spaceトークンを経て、paddingやgapなどのソースカテゴリへと変化してきました。現在のsys.jsonでは、これらの値は基本トークンへの参照として表されています。このリファレンスでは、単語を連結してCSS変数を独自に作るのではなく、実際のパスをそのまま記載しています。
以前のアプリケーションを移行する際は、移行テーブルが今も役立ちます。以前のspace.sが1 remに相当していた理由を確認できます。ただし、これは過去の情報であり、旧エクスポート名がすべて現在も推奨されていることを示すものではありません。プレビューでは、アプリケーション側で16pxと24pxの間隔を明示的に選択しています。ライブラリのトークン名については、引き続きソース内のテーブルを参照してください。
関連する仕様を確認する
Workday Canvasデザインシステム:カラーはテナントとコンポーネントの状態に応じて決まります プライマリボタンは、単に青い色見本を固定したものではありません PrimaryButtonは、まずbrand.action.baseを参照し、値がなければ従来のプライマリアクセントにフォールバックします。ホバー時と押下時のスタイルでもブランドのアクションロールを参照し、その後colorSpaceヘルパーを使って状態に応じた色を導きます。そのため、固定の背景色を一つ変更するだけでは、コンポーネントのテーマ仕様を再現できません。
学習プレビューは、落ち着いた青系のアプリケーション配色を採用した独自のHTML版であることを明示しています。特定の顧客テナントや、新しいSana Canvasテーマを再現したものではありません。完全なネイティブ実装例では、インストール済みのトークンレイヤーによってコンポーネントの色が適用されます。
関連する仕様を確認する
Workday Canvasデザインシステム:複合ロールで行間と太さを維持 サイズのエイリアスだけではスタイル全体を表せません 現在のbody.smタイポグラフィオブジェクトには、フォントファミリー、normalのウェイト、本文サイズ、本文の行送りが含まれています。見出しロールではboldのウェイトを使用します。以前のスクリーンショットからピクセルサイズだけを選ぶと、こうした関連性が失われます。ソースではnormalという名称を使っており、以前の移行資料に登場するregularキーとは異なります。
Canvas Kitコンポーネントパッケージにはフォントが含まれていません。フォントの読み込みは別途、連携方法として決めてください。オフラインのサンプルはシステムフォントにフォールバックし、RobotoやSanaフォントが同梱されているとはしていません。
関連する仕様を確認する
Workday Canvasデザインシステム:FormFieldは複合コンポーネントです フィールドにラベル、入力欄、ヒントをまとめて構成させる ソースではFormField.Label、FormField.Input、FormField.Hintを使用できます。Inputのasプロパティを介してTextInputを渡すと、モデルがアクセシビリティ上必要な関連付けを生成します。これは、外側のフィールドにlabelプロパティを設定し、ラップしていない入力欄を直接配置する以前の例とは異なります。
横並びのフォームでは、現在の配置値としてhorizontalStartとhorizontalEndを使用します。FormField.Fieldは、その配置の中で入力欄とヒントをまとめます。このフィルターは、説明文をラベルと入力コントロールのすぐ下に置くため、縦並びにしています。
FormField.Labelに、画面上に表示される実際のラベルを設定します。 複合コンポーネントのInputスロットにTextInputを渡します。 フィルターの適用範囲にはHintを使用します。 制約を追加する場合も、バリデーションロジックはアプリケーション側に置いてください。 関連する仕様を確認する
Workday Canvasデザインシステム:選択状態は表示中の検索結果とは独立して保持されます 講座には一意のIDを使い、合計は導出します 各講座にはID、タイトル、所要時間があります。選択リストにはIDを保存し、表示する合計時間は講座データから導出します。カタログを絞り込んでも、選択済みの項目は削除されません。項目を削除すると、すぐに追加操作が再び使えるようになり、合計時間も減少します。
これは独自に作成したローカルの学習プラン例です。Workday APIの呼び出し、従業員の受講登録、学習記録の変更は行いません。この境界を明示することで、プロトタイプが実際の業務処理を行うかのような誤解を防ぎます。
カタログ表示のみを絞り込みます。 コースIDを一度だけ追加します。 コースIDから選択中のコースと合計時間(分)を算出します。 検索条件をリセットせずに選択を解除します。 関連する仕様を確認する
Workday Canvas デザインシステム テーマ付きコンポーネントを使う前にトークンを読み込む ProviderとCSSレイヤーは役割が異なります 現在のスタートガイドの例では、アプリケーションのエントリーポイントでベース、ブランド、コンポーネント、システム変数のスタイルを読み込み、その後CanvasProviderでインターフェースをラップしています。Providerだけでは、これらのCSS定義の代わりにはなりません。ここで示すコードはこの構成に従い、完全なReactルートを用意しています。
Sana Canvasはオプトイン方式です。変数はシステム変数の後に読み込まれ、文書化されたhtmlのdata-themeによって選択されます。ドキュメントルートを制御できない埋め込みアプリケーション向けに、ポップアップへテーマを伝播する専用のプロバイダーテーマオプションがあります。パッケージのアップグレード時に、既存のテナントへ誤ってこのテーマを追加しないでください。
関連する仕様を確認する
Workday Canvas デザインシステム コンポーネントの例 完全なスタンドアロンのインタラクティブ見本 ドキュメント全体をindex.htmlとして保存し、ブラウザーで開いてください。Canvasの操作パターンと、抑制の効いたエンタープライズ向けの情報階層を独自にオフラインで再現したHTMLです。配色とレイアウトはアプリケーション側で決めたものであり、Sanaテナントの表示ではありません。
html コードをコピー
<!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と複合FormFieldAPIを別々に紹介します。登録処理は送信されません。 複合フィールドと選択可能な学習項目 FormFieldが入力との関連付けを生成し、TextInputが元のコースデータを絞り込みます。各PrimaryButtonで項目を一度だけ追加できます。選択解除すると、合計時間が再計算されます。
jsx コードをコピー
// 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はコース項目ごとに独立して配置されています。プラン全体に、誤解を招く受講登録アクションはありません。 削除には、アプリケーション側で明示的に用意したネイティブボタンを使用します。 追加済みの項目は無効状態にして重複を防ぎ、ライブカウントで変更を確認できるようにします。 04
Workday Canvas デザインシステム エージェントのセットアップ 実装の出発点 バンドルされたアプリケーション向けのReact構成全体。現在のCanvasProviderとトークンCSSのセットアップ、元のコースデータ、ネイティブの削除コントロールを含みます。リモートの受講登録機能はありません。ソースを確認済みですが、このワークスペースではパッケージのコンパイルを行っていません。
jsx サンプルをコピー
// 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>); あなたのプロジェクト向けプロンプト AI エージェント用プロンプト プロンプトをコピー
現在のCanvas FormFieldの構成とトークンのCSSレイヤーを使用します。コースの絞り込みと選択状態を分離し、重複を防ぎ、合計時間を算出してください。セマンティックロールを使ってテナントのテーマ設定を維持し、過去の番号付きスペース名を現在推奨されるソースカテゴリと取り違えないでください。 05
Workday Canvas デザインシステムのソースと対象範囲 独立したOpenDesignの参考資料です。従業員の受講登録やWorkdayのAPIは関係ありません。現在のソース名と以前の移行用名称を明確に区別しています。