Crie um plano de aprendizagem local com o FormField composto, o TextInput controlado e o PrimaryButton do Canvas Kit, diferenciando as fontes atuais de tokens semânticos dos exemplos antigos de migração de space.x4.
Interpretação original e offline HTML dos padrões de interação do Canvas e de uma hierarquia empresarial discreta. A paleta e o layout são escolhas da aplicação, não uma renderização de um tenant do Sana. · Referência de implementação
02
Referência de design do sistema de design Workday Canvas
Código-fonte atual do Canvas Kit e sys.json de tokens do Canvas. Os nomes JSON brutos abaixo são caminhos do código-fonte, não nomes inventados de variáveis CSS geradas. Valores que são aliases de tokens base são apresentados como aliases; a referência de migração datada é identificada explicitamente como histórica.
Sistema de design Workday Canvas: consulte a fonte atual antes de copiar um exemplo de migração
O espaçamento agora distingue finalidades
O Canvas evoluiu das constantes de espaço no estilo de tamanhos de camiseta em canvas-kit-react/tokens para os tokens numerados system.space e, depois, para categorias da fonte, como padding e gap. O sys.json atual expressa os valores subjacentes como referências a tokens base. Esta referência preserva os caminhos reais, em vez de inventar uma variável CSS pela concatenação de palavras.
A tabela de migração continua sendo útil para converter uma aplicação antiga: ela explica por que space.s correspondia a um rem. É um registro histórico, não uma garantia de que toda exportação antiga seja a interface atual preferencial. A prévia usa intervalos de aplicação escolhidos explicitamente, de 16 e 24px; a tabela da fonte continua sendo a referência oficial para os nomes dos tokens da biblioteca.
Sistema de design Workday Canvas: as cores pertencem ao tenant e ao estado do componente
Um botão primário é mais do que uma amostra fixa de azul
PrimaryButton primeiro consulta brand.action.base e, se não estiver disponível, usa o destaque primário legado. Os estilos de hover e pressionado também consultam as funções de ação da marca antes de derivar os estados com os auxiliares colorSpace. Por isso, alterar apenas uma cor de fundo fixa não reproduz o contrato de tema do componente.
A prévia de aprendizagem original usa uma paleta de aplicação azul e discreta e se identifica como uma interpretação HTML. Ela não representa a renderização de um tenant específico de cliente nem o tema mais recente do Sana Canvas. O exemplo nativo completo permite que as camadas de tokens instaladas forneçam as cores dos componentes.
Sistema de design Workday Canvas: estilos compostos preservam a altura da linha e o peso
Um alias de tamanho não define o estilo inteiro
O objeto de tipografia body.sm atual combina família tipográfica, peso normal, tamanho de texto de corpo e entrelinha do corpo. Os estilos de título usam o peso negrito. Escolher apenas um tamanho em pixels de uma captura de tela antiga eliminaria essas relações. A fonte usa normal, em vez da chave regular que aparece em textos de migração mais antigos.
As fontes não estão incluídas no pacote de componentes do Canvas Kit. Mantenha o carregamento de fontes como uma decisão de integração separada; o exemplo offline usa uma fonte substituta do sistema e não afirma incluir Roboto nem uma fonte do Sana.
Sistema de design Workday Canvas: FormField é um componente composto
Deixe o campo compor o rótulo, a entrada e a dica
A fonte disponibiliza FormField.Label, FormField.Input e FormField.Hint. Passar TextInput pela propriedade as de Input permite que o modelo gere as associações de acessibilidade. Isso difere dos exemplos antigos, que definiam uma propriedade label no campo externo e colocavam uma entrada sem encapsulamento diretamente dentro dele.
Em formulários horizontais, horizontalStart e horizontalEnd são os valores atuais de orientação. FormField.Field agrupa a entrada e a dica dentro desse alinhamento. Nosso filtro permanece vertical porque sua explicação deve ficar logo abaixo do rótulo e do controle.
Coloque um rótulo visível em FormField.Label.
Passe TextInput pelo slot composto Input.
Use Hint para delimitar o escopo do filtro.
Ao introduzir restrições, mantenha a lógica de validação na aplicação.
Sistema de design Workday Canvas: um campo de entrada expansível ainda precisa de um contêiner flexível
Considere a largura mínima
TextInput tem uma largura mínima substituta de 280px, expressa por meio de px2rem na fonte. grow solicita largura total; isso não faz com que um contêiner rígido de duas colunas se ajuste magicamente a uma janela de visualização estreita. O catálogo offline empilha a barra lateral do plano abaixo dos cartões de cursos em dispositivos móveis e permite que cada coluna do layout encolha.
O valor da busca é um estado React comum. A aplicação faz uma correspondência de substring sem diferenciar maiúsculas de minúsculas nos títulos dos três cursos e exibe explicitamente quando não há resultados. O filtro não limpa os cursos selecionados, pois visibilidade e inclusão no plano são estados diferentes.
Sistema de design Workday Canvas: a seleção persiste independentemente dos resultados visíveis
Use IDs estáveis para os cursos e derive os totais
Cada curso original tem um ID, um título e uma duração. A lista de seleção armazena IDs, enquanto o total exibido é derivado dos registros dos cursos. Filtrar o catálogo não remove um item selecionado. Remover um item restaura imediatamente a ação Adicionar e reduz o total.
Este é um cenário original de planejamento local. Ele não chama APIs do Workday, não matricula um funcionário nem modifica um registro de aprendizagem. Deixar esse limite explícito evita que um protótipo sugira uma transação comercial real.
Filtre apenas a visualização do catálogo.
Adicione cada ID de curso apenas uma vez.
Derive os cursos selecionados e o total de minutos a partir desses IDs.
Sistema de design Workday Canvas: carregue os tokens antes de esperar que os componentes tenham um tema
Provider e camadas CSS têm responsabilidades diferentes
O exemplo atual de introdução importa os estilos de variáveis base, de marca, de componentes e do sistema no ponto de entrada da aplicação e, em seguida, envolve a interface com CanvasProvider. Um provider não substitui essas definições CSS. O código aqui segue essa composição e fornece uma raiz React completa.
Sana Canvas é opcional: suas variáveis são carregadas após as variáveis do sistema, e o atributo documentado html data-theme seleciona esse tema. Aplicações incorporadas que não podem controlar a raiz do documento contam com uma opção de tema dedicada no provider para propagar o tema aos pop-ups. Não adicione esse tema por engano a um tenant existente ao atualizar pacotes.
Workday Canvas Exemplos de componentes do sistema de design
Exemplo interativo completo e independente
Salve o documento completo como index.html e abra-o em um navegador. Interpretação HTML original e offline dos padrões de interação do Canvas e de uma hierarquia corporativa discreta. A paleta e o layout são escolhas da aplicação, não a renderização de um tenant do 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>
Adicionar um curso altera o plano de aprendizagem de exemplo. A entrada nativa demonstra separadamente CanvasProvider e o FormField composto API; nenhuma matrícula é enviada.
Campo composto e itens de aprendizagem selecionáveis
FormField gera a associação da entrada, TextInput filtra os dados originais dos cursos, e cada PrimaryButton adiciona um item uma única vez. A remoção recalcula o total selecionado.
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 pertence a cada item de curso independente; o plano inteiro não tem uma ação de matrícula enganosa.
Remover usa um botão nativo explicitamente controlado pela aplicação.
O estado Adicionado desativado evita duplicações, enquanto uma contagem dinâmica confirma a alteração.
04
Sistema de design Workday Canvas Configuração do agente de IA
Ponto de partida para implementação
Composição React completa para uma aplicação empacotada. Configuração atual de CanvasProvider e das camadas de tokens CSS; dados originais dos cursos e controles nativos de remoção. Sem matrícula remota. Fonte verificada, mas o pacote não foi compilado neste ambiente de trabalho.
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>);
Um prompt para seu projeto
Prompt para agente de IA
Use a composição atual de FormField do Canvas e as camadas de tokens CSS. Mantenha a filtragem dos cursos separada do estado de seleção, evite duplicações e derive o total de minutos. Preserve a tematização do tenant por meio de funções semânticas; não confunda nomes históricos numerados de espaço com as categorias preferenciais atuais da fonte.
05
Workday Canvas Fontes e escopo do sistema de design
Referência OpenDesign independente. Não há matrícula de funcionários nem uso de API do Workday. Os nomes atuais da fonte e os nomes antigos de migração são diferenciados explicitamente.