@workday/canvas-kit-docs
Version:
Documentation components of Canvas Kit components
424 lines (315 loc) • 12.5 kB
text/mdx
import {InformationHighlight} from '@workday/canvas-kit-preview-react/information-highlight';
import {StorybookStatusIndicator, DownloadLLMFile} from '@workday/canvas-kit-docs';
# Style Props Deprecation Overview
### Purpose
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](https://github.com/Workday/canvas-kit/discussions/2265) discussion.
### Goals
- **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.
### Timeline
- **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
## LLM Assisted Migration <StorybookStatusIndicator type="ai" />
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](https://www.cursor.so/). 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
<DownloadLLMFile
rawFileLink="https://raw.githubusercontent.com/Workday/canvas-kit/master/modules/docs/llm/llm-style-props-migration.txt"
filename="llm-style-props-migration.txt"
/>
## Changes Overview
### Replacements
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 |
## Canvas Kit Styling
<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>
</InformationHighlight>
Canvas Kit’s styling utilities are built for **static CSS generation**, **token integration**, and
**predictable composition**.
### Core APIs
- **`createStyles`** — define reusable, static CSS objects.
- **`createStencil`** — define reusable, dynamic component styles with parts, vars, and modifiers
- **`cs` prop** — apply multiple styles and handle merges consistently to Canvas Kit components
### Best Practices
These best practices ensure your components remain **performant**, **consistent**, and
**maintainable** under the new Canvas Kit Styling system.
#### Define Styles Outside the Render Function
Always declare styles at the module level. Creating styles inside the render or component function
will trigger component re-render.
✅ **Do**
```tsx
// `createStyles` returns a string of className
const buttonStyles = createStyles({
backgroundColor: system.color.bg.primary.default,
color: system.color.text.inverse,
});
export const MyButton = () => <button className={buttonStyles}>Click me</button>;
```
❌ **Don’t**
```tsx
export const MyButton = () => {
const buttonStyles = createStyles({backgroundColor: 'red'}); // bad
return <button cs={buttonStyles}>Click me</button>;
};
```
#### Use `createStyles` for Static Styling
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.
```tsx
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.bg.primary.default,
color: system.color.text.inverse,
});
export const MyButton = () => <BaseButton cs={buttonStyles}>Click me</button>;
```
#### Use `createStencil` for Dynamic or Complex Styling
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**
```tsx
const buttonStencil = createStencil({
vars: {color: '', background: ''},
base: ({color, backgroundColor}) => ({
color: cssVar(color, system.color.text.default),
backgroundColor: cssVar(backgroundColor, system.color.bg.default),
}),
modifiers: {
variant: {
primary: {background: system.color.bg.primary.default},
secondary: {background: system.color.bg.muted.default},
},
},
});
```
- **vars**: If you initialize the variable with an empty string, it will allow the variable to
cascade and be defined.
```tsx
const customButtonStencil = createStencil({
base: {
// Set the color variable to the primary color
[buttonStencil.vars.color]: system.color.fg.primary.default,
},
});
```
- **cssVar**: The `cssVar` function is used when you want to add a default value if the CSS Variable
is not defined.
- **modifiers**: The `modifiers` property is used to define the styles for the different variants of
the component.
#### Use `cs` Prop to Merge Styles
The `cs` prop merges `className` and `style` attributes safely and consistently. Use this over using
style props or className concatenation.
✅ **Do**
```tsx
<PrimaryButton cs={[baseStyles, variantStyles]} />
```
❌ **Don’t**
```tsx
<PrimaryButton className={`${baseStyles} ${variantStyles}`} />
```
#### Use Variables for Dynamic Values
Instead of inline styles or runtime calculations, use stencil variables.
✅ **Do**
```tsx
const primaryButtonStencil = createStencil({
base: {
// Use the buttonStencil variable to set the background color
[buttonStencil.vars.background]: 'orange',
}
})
<PrimaryButton cs={primaryButtonStencil} />
```
❌ **Don’t**
```tsx
<PrimaryButton cs={{backgroundColor: 'orange'}} /> // breaks static optimization
```
#### Extend Existing Stencils Instead of Overriding Styles
When modifying Canvas Kit components, extend the provided `Stencil` instead of creating your own
from scratch.
✅ **Do**
```tsx
const customIconStencil = createStencil({
extends: systemIconStencil,
base: {
margin: system.space.x2,
},
});
```
This will inherit both the styles and variables from the `systemIconStencil`.
#### Use Modifiers for Variants and States
Define component variations (size, color, emphasis) using **modifiers** rather than conditional
logic.
✅ **Do**
```tsx
const badgeStencil = createStencil({
modifiers: {
status: {
success: {background: system.color.bg.success.default},
error: {background: system.color.bg.negative.default},
},
},
});
```
#### Use Compound Modifiers for Complex Conditions
When two or more modifiers combine to produce a new style, define a **compound modifier**.
✅ **Do**
```tsx
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.space.x5},
},
],
});
```
#### Avoid Nested Stencils Unless Necessary
Each Stencil should map to one semantic component. Nested stencils can increase CSS specificity and
complexity. Use **parts** instead of deep nesting.
✅ **Do**
```tsx
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>;
```
#### Prefer Tokens and System Variables
Always use design tokens (`system`) for spacing, colors, typography, etc., instead of raw values.
View our System Tokens
[docs](https://workday.github.io/canvas-tokens/?path=/docs/docs-system-tokens-overview--docs) for
more information.
✅ **Do**
```tsx
color: system.color.text.default;
margin: system.space.x2;
```
❌ **Don’t**
```tsx
color: '#333';
margin: '8px';
```
#### Debugging and Static Compilation
- Enable static compilation during development to catch type or value errors early.
- Use `as const` for static objects to ensure values are type-locked for the compiler.
✅ **Do**
```tsx
const reusableStyles = {
position: 'absolute',
} as const;
```
#### Don’t Mix Emotion and Static Styling
Avoid combining Emotion’s `styled` or `css` with `createStyles` or `createStencil`. It reintroduces
runtime style recalculations and negates static benefits.
❌ **Don’t**
```tsx
const StyledButton = styled(Button)(styles);
<StyledButton cs={createStyles({padding: 8})} />;
```
## Migration Example
### Style Props
#### Before
```typescript
import {Flex} from '@workday/canvas-kit-react/layout';
<Flex depth={1} marginX={10} background="frenchVanilla100" />;
```
#### After
```typescript
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 `px2rem` function 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 `system` tokens over `base` tokens for better theming support.
### Emotion styled
#### Before (Emotion)
```tsx
const StyledButton = styled('button')({
backgroundColor: 'blue',
color: 'white',
});
```
#### After (Canvas Kit Styling)
```tsx
import {createStyles} from '@workday/canvas-kit-styling';
import {PrimaryButton} from '@workday/canvas-kit-react/button';
import {system} from '@workday/canvas-tokens-web';
const primaryButtonStyles = createStyles({
backgroundColor: system.color.bg.primary.default,
color: system.color.text.inverse,
});
<PrimaryButton cs={primaryButtonStyles}>Click me</PrimaryButton>;
```