workday/canvas-kit/sana-canvas-kit-accessibility
Claude Code and agent skill by workday/canvas-kit
- community
Checking that the SKILL.md file is still in the repository.
Build accessible UI with Canvas Kit. Use when adding ARIA, labels, keyboard support, focus management, popups/portals, live regions, forms accessibility, or reviewing a11y. Prefer semantic HTML over hand-added ARIA. For custom component factories see /sana-canvas-kit-builder; for deprecated props like disabled vs aria-disabled see /sana-canvas-kit-component-selection.
Install
npx vskill@latest install workday/canvas-kit/sana-canvas-kit-accessibilitySKILL.md
Canvas Kit Accessibility
Accessibility is not a follow-up pass — implement it while you build, not only when writing tests.
Guiding principle: No ARIA is better than Bad ARIA. Prefer semantic HTML and native keyboard behavior. Only add ARIA that a pattern actually requires.
Building custom components → /sana-canvas-kit-builder. Deprecated a11y-related props (disabled on MenuItem, etc.) → /sana-canvas-kit-component-selection.
When to apply
- Adding or reviewing interactive UI (buttons, links, inputs, menus, dialogs, tabs)
- Choosing Modal vs Popup vs Menu vs Dialog
- Wiring labels, errors, hints, or live announcements
- Focus on open/close, keyboard traps, Escape dismissal
- User asks for accessible, screen-reader-friendly, or keyboard-navigable UI
For deeper docs, call MCP get-accessibility-guidelines (see MCP lookup). Scenario and component slug tables: references.md.
Core rules
- Semantic HTML first —
<button>,<a href>,<input>,<label>beforeroleon a<div>. - Every interactive element needs an accessible name — see Naming.
- Keyboard before mouse-only — Tab/Shift+Tab at minimum; APG patterns often need arrow keys,
Escape, Enter/Space.
- Visible focus by default — use
:focus-visibleso keyboard users get a focus ring while
pointer clicks rely on browser heuristics. Never remove focus outlines unconditionally (see Focus).
- Follow APG exactly — if you add ARIA, match the
WAI-ARIA APG pattern for that widget. Don't invent role/state combinations.
- When in doubt, ask — bad ARIA is worse than none.
Automated checks (cy.checkA11y(), axe) catch a fraction of real issues. They don't verify that labels make sense, keyboard flow is logical, or focus lands somewhere sensible after an action.
Accessible names
Priority order (highest first):
aria-labelledby— reference to an existing visible element'sidaria-label— string when no visible label exists- Visible text / native
<label htmlFor={id}>on the control
Do not use title or placeholder as the only label — screen readers treat them inconsistently.
Descriptions (helper text, errors) use aria-describedby. Errors often also need aria-invalid.
Use useUniqueId() from @workday/canvas-kit-react/common when wiring id / aria-labelledby / aria-controls across compound subcomponents.
Roles and native elements
| Need | Prefer | Not | |
|---|---|---|---|
| Click action | <button type="button"> | <div onClick> | |
| Navigation | <a href="..."> | <span onClick> | |
| Text input | <input> / Canvas TextInput | contentEditable div | |
| Toggle | Checkbox / Switch | custom div + role="checkbox" | |
| Expand/collapse | Expandable, Disclosure | hand-rolled aria-expanded on div |
Reach for Canvas Kit components that already implement APG behavior (Menu, Modal, Tabs, Select, FormField) before rebuilding patterns.
Keyboard navigation
| Pattern | Keys (typical) | Canvas Kit starting point | |
|---|---|---|---|
| Button/link | Enter, Space (button) | PrimaryButton, Hyperlink | |
| Menu / listbox | Arrows, Enter, Escape, typeahead | Menu, Select, MultiSelect | |
| Tabs | Arrow keys between tabs, Tab into panel | Tabs (Preview) | |
| Dialog/modal | Tab cycle, Escape to close | Modal, Dialog | |
| Tooltip | Escape (if open), focus/hover on target | Tooltip | |
| Radio group | Arrows within group | Radio / RadioGroup (Preview) |
Tab/Shift+Tab alone is not enough for composite widgets — check the APG for the pattern you're implementing.
Deprecated: disabled on MenuItem — use aria-disabled (/sana-canvas-kit-component-selection).
Focus indicators
Required by default. Canvas Kit uses `:focus-visible` — browser heuristics decide when focus styles apply (keyboard navigation shows the ring; most pointer clicks do not). Do not use data-whatinput or InputProvider; those were removed in v14.
When extending stencils or adding custom focus styles:
- `:focus-visible` — real keyboard/focus navigation
- `&.focus` — static state twin for visual testing (Chromatic
StaticStates); pair with
:focus-visible the way Canvas Kit components do
Do not remove focus outlines globally (outline: none on :focus without a :focus-visible replacement). Canvas Kit components already follow this pattern — preserve it when extending stencils. hideMouseFocus / mouseFocusBehavior are deprecated — use :focus-visible instead.
Popups, portals, and focus
Popups add reading-order and focus problems. Pick the right primitive:
| Need | Component | Notes | |
|---|---|---|---|
| Modal overlay + focus trap | Modal | Blocking dialog; use for true modals | |
Dialog with aria-owns reading order | Dialog | Non-blocking or custom dialog patterns | |
| Lightweight popover, no trap | Popup | See inline portal / aria-owns patterns | |
| Action list | Menu | Menu.Item vs Menu.Option for selection | |
| Hover/focus supplementary text | Tooltip | Target must be focusable |
Reading order ≠ focus order. Moving focus into a popup helps announcements but does not fix DOM order when content is portaled to document.body.
Patterns (detail in modules/docs/mdx/accessibility/Popups.mdx):
- Inline / no portal —
portal={false}onPopup.Popper; DOM order matches reading order - Portaled + `aria-owns` —
Dialogpattern; support varies; tab order still follows real DOM - Initial focus —
useInitialFocusso screen readers announce new content on open - Return focus —
useReturnFocusto the trigger on close
Theme: portaled popups inherit CSS variables from <html>. Set data-theme="sana-canvas" on <html> or use CanvasProvider theme forwarding — see v16 upgrade guide.
Before custom dismissal logic, read modules/docs/mdx/accessibility/Popups.mdx.
Forms
Use FormField compound API for labels, hints, and errors:
- Associate errors with
aria-invalidand visible error text - Group related radios/checkboxes with
FormField+role="group"where appropriate - Don't rely on color alone for error/caution — use text and icons
- Checked-state contrast: custom
brand.successthemes must maintain 3:1 against white checkmarks
(v16 Checkbox/Radio/Switch)
- Which color pair to use for error/warning/success text and surfaces →
/sana-canvas-kit-tokens
(accessible color pairings section). This skill covers that color can't be the only signal; /sana-canvas-kit-tokens covers which token pair guarantees contrast.
MCP: get-accessibility-guidelines with {"scenario": "forms"} or {"component": "form-field"}.
Live regions
Use AriaLiveRegion from @workday/canvas-kit-react/common for dynamic status text — not for interactive content.
Rules:
- Plain text only — no links, buttons, or images inside live regions
- Polite by default —
assertiveonly when interruption is truly required - Announce once — users cannot replay; debounce noisy updates (e.g. character counts)
- Don't overuse — frequent announcements are disruptive
Hide visual-only live text with AccessibleHide or as={AccessibleHide}.
Read modules/docs/mdx/accessibility/AriaLiveRegions.mdx before inventing announcement logic.
Tables
- Use
Table/BaseTablewith proper header cells (scopewhere needed) - For filterable/sortable/expandable tables, see
modules/docs/mdx/accessibility/tables/ - Name tables via
<caption>,aria-label, oraria-labelledby(e.g. heading +useUniqueId)
MCP: {"scenario": "tables"} or {"component": "table"}.
Page structure
- One logical
<main>per view; useHeadinglevels in order (don't skip levels for styling) - Landmarks:
header,nav,main, complementary regions — seePageStructure.mdx - Reflow: avoid
min-width> 320px at small breakpoints; test at 1280×1024 @ 400% zoom
Windows High Contrast
Canvas Kit components include forced-colors / high-contrast fallbacks where gradients or shadows would disappear. Don't remove borders that exist for WHCM visibility (e.g. Skeleton placeholders).
See WindowsHighContrastThemes.mdx.
Workflow
MCP lookup
get-accessibility-guidelines returns guidance links — it does not scan code or certify WCAG.
At least one of component or scenario is required:
Also returns docs://examples/{component} and docs://examples/{component}/accessibility when available. Prefer component accessibility sections for implementation-specific rules.
Common scenarios: overview, forms, popups, tables, aria-live, page-structure, headers, side-panel, windows-high-contrast, color-contrast.
Full slug lists: references.md.
Anti-patterns
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/sana-canvas-kit-accessibility/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/workday/canvas-kit/sana-canvas-kit-accessibility)