import {Meta} from '@storybook/addon-docs';
import {DownloadLLMFile, StorybookStatusIndicator} from '@workday/canvas-kit-docs'; import {InformationHighlight} from '@workday/canvas-kit-react/information-highlight';
As part of the Canvas Kit’s modernization process, we’re moving away from Emotion’s runtime styling
and promoting a custom CSS-in-JS solution: @workday/canvas-kit-styling. This change improves
performance, consistency, and maintainability across our codebase. For more information,
view our Future of discussion.
- Reduce runtime overhead by removing Emotion’s runtime from
@emotion/react - Promote prescriptive, opinionated styling across Workday
- Enable static CSS compilation for faster load times and smaller bundles
- Support new design tokens and CSS Variables for scalable theming
- Ensure proper style merging and stable selector behavior
- Support advanced styling patterns like compound styles, modifiers, and
data-parts
Emotion dynamically injects styles at runtime, causing costly re-renders and cache invalidations. The new system statically compiles styles at build time for optimal performance.
- Deprecation introduced: Canvas Kit v14.1
- Removal: Not immediate — style props and
styled()will continue to function in upcoming releases - Migration timeline: Gradual; no immediate codebase-wide update required
We've provided an LLM migration mapping file (llm-style-props-migration.txt) specifically
designed for use with LLM-based code assistants such as Cursor. It
contains a compiled LLM consumption version of this v14 Upgrade Guide. It is not intended for direct
human reference or team documentation, but rather as structured input for LLMs to automate and
assist with your migration process.
Important: LLMs can make mistakes. Please verify changes using this Migration Guide.
How to use:
- View raw file: Open the file in a new tab to see the complete migration mapping
- Download LLM File: Save the file locally to upload or paste into your LLM/code assistant
- Use with LLM: Provide the raw content to your LLM/code assistant as context for automated migration
Use the new Canvas Kit Styling utilities:
| Old API | New API | Purpose |
|---|---|---|
styled() |
createStyles / createStencil |
Define static or component-level styles |
Inline style props, like background or padding |
cs prop |
Safely merge class names and styles |
| Dynamic values | createVars |
Manage CSS variables for runtime overrides |
| Emotion modifiers | modifiers, compound |
Define consistent appearance variants |
<InformationHighlight className="sb-unstyled" cs={{p: {marginBlock: 0}}}> <InformationHighlight.Icon /> <InformationHighlight.Heading>Canvas Kit Styling Docs</InformationHighlight.Heading> For a detailed overview of our styling approach, view our styling docs. <InformationHighlight.Link href="https://workday.github.io/canvas-kit/?path=/docs/styling-getting-started-overview--docs"> Read more </InformationHighlight.Link>
Canvas Kit’s styling utilities are built for static CSS generation, token integration, and predictable composition.
createStyles— define reusable, static CSS objects.createStencil— define reusable, dynamic component styles with parts, vars, and modifierscsprop — apply multiple styles and handle merges consistently to Canvas Kit components
These best practices ensure your components remain performant, consistent, and maintainable under the new Canvas Kit Styling system.
Always declare styles at the module level. Creating styles inside the render or component function will trigger component re-render.
✅ Do
// `createStyles` returns a string of className
const buttonStyles = createStyles({
backgroundColor: system.color.brand.accent.primary,
color: system.color.fg.inverse,
});
export const MyButton = () => <button className={buttonStyles}>Click me</button>;❌ Don’t
export const MyButton = () => {
const buttonStyles = createStyles({backgroundColor: 'red'}); // bad
return <button cs={buttonStyles}>Click me</button>;
};Use createStyles for simple, reusable style objects that do not depend on dynamic data or
props.
✅ Ideal for:
- Defining base styles
- Applying static overrides
- Styling tokens-based components
createStyles returns a string of className that can be applied to a React element. If you're
applying the class to a Canvas Kit component, you can use the cs prop.
import {BaseButton} from '@workday/canvas-kit-react/button';
import {createStyles} from '@workday/canvas-kit-styling';
// `createStyles` returns a string of className
const buttonStyles = createStyles({
backgroundColor: system.color.brand.accent.primary,
color: system.color.fg.inverse,
});
export const MyButton = () => <BaseButton cs={buttonStyles}>Click me</button>;Use createStencil when styles depend on props, variants, or component parts.
Examples:
- Size or color variants (
primary,secondary) - Compound state combinations (
size=small,iconPosition=end) - Multi-part components (e.g.
Button,Card,MenuItem)
✅ Do
const buttonStencil = createStencil({
vars: {color: '', background: ''},
base: ({color, backgroundColor}) => ({
color: cssVar(color, system.color.fg.default),
backgroundColor: cssVar(backgroundColor, system.color.bg.default),
}),
modifiers: {
variant: {
primary: {background: system.color.brand.accent.primary},
secondary: {background: system.color.accent.muted.default},
},
},
});- vars: If you initialize the variable with an empty string, it will allow the variable to cascade and be defined.
const customButtonStencil = createStencil({
base: {
// Set the color variable to the primary color
[buttonStencil.vars.color]: system.color.brand.fg.primary.default,
},
});- cssVar: The
cssVarfunction is used when you want to add a default value if the CSS Variable is not defined. - modifiers: The
modifiersproperty is used to define the styles for the different variants of the component.
The cs prop merges className and style attributes safely and consistently. Use this over using
style props or className concatenation.
✅ Do
<PrimaryButton cs={[baseStyles, variantStyles]} />❌ Don’t
<PrimaryButton className={`${baseStyles} ${variantStyles}`} />Instead of inline styles or runtime calculations, use stencil variables.
✅ Do
const primaryButtonStencil = createStencil({
base: {
// Use the buttonStencil variable to set the background color
[buttonStencil.vars.background]: 'orange',
}
})
<PrimaryButton cs={primaryButtonStencil} />❌ Don’t
<PrimaryButton cs={{backgroundColor: 'orange'}} /> // breaks static optimizationWhen modifying Canvas Kit components, extend the provided Stencil instead of creating your own
from scratch.
✅ Do
const customIconStencil = createStencil({
extends: systemIconStencil,
base: {
margin: system.gap.sm,
},
});This will inherit both the styles and variables from the systemIconStencil.
Define component variations (size, color, emphasis) using modifiers rather than conditional logic.
✅ Do
const badgeStencil = createStencil({
modifiers: {
status: {
success: {background: system.color.accent.success},
error: {background: system.color.brand.accent.critical},
},
},
});When two or more modifiers combine to produce a new style, define a compound modifier.
✅ Do
const myCustomStencil = createStencil({
base: {
//base styles
},
modifiers: {
variant: {
primary: {
// primary variant styles
},
},
size: {
large: {
// large size styles
},
},
},
compound: [
{
// apply styles when the variant is primary AND the size is large
modifiers: {variant: 'primary', size: 'large'},
styles: {paddingInline: system.padding.xl},
},
],
});Each Stencil should map to one semantic component. Nested stencils can increase CSS specificity and complexity. Use parts instead of deep nesting.
✅ Do
const cardStencil = createStencil({
parts: {header: 'card-header', body: 'card-body'},
base: ({headerPart}) => ({
[headerPart]: {
fontWeight: 'bold',
},
}),
});
<Card cs={cardStencil}>
<Card.Heading {...cardStencil.parts.header}>Card Title</Card.Heading>
<Card.Body {...cardStencil.parts.body}>Card Body</Card.Body>
</Card>;Always use design tokens (system) for spacing, colors, typography, etc., instead of raw values.
View our System Tokens
docs for
more information.
✅ Do
color: system.color.fg.default;
margin: system.gap.md;❌ Don’t
color: '#333';
margin: '8px';- Enable static compilation during development to catch type or value errors early.
- Use
as constfor static objects to ensure values are type-locked for the compiler.
✅ Do
const reusableStyles = {
position: 'absolute',
} as const;Avoid combining Emotion’s styled or css with createStyles or createStencil. It reintroduces
runtime style recalculations and negates static benefits.
❌ Don’t
const StyledButton = styled(Button)(styles);
<StyledButton cs={createStyles({padding: 8})} />;import {Flex} from '@workday/canvas-kit-react/layout';
<Flex depth={1} marginX={10} background="frenchVanilla100" />;import {Flex} from '@workday/canvas-kit-react/layout';
import {system} from '@workday/canvas-tokens-web';
import {px2rem} from '@workday/canvas-kit-styling';
<Flex
cs={{
boxShadow: system.depth[1],
marginInline: px2rem(10),
background: system.color.bg.default,
}}
/>;- px2rem: The
px2remfunction is used to convert a pixel value to a rem value. - Use [CSS logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values
- Use
systemtokens overbasetokens for better theming support.
const StyledButton = styled('button')({
backgroundColor: 'blue',
color: 'white',
});import {PrimaryButton} from '@workday/canvas-kit-react/button';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
const primaryButtonStyles = createStyles({
backgroundColor: system.color.brand.accent.primary,
color: system.color.fg.inverse,
});
<PrimaryButton cs={primaryButtonStyles}>Click me</PrimaryButton>;