Skip to content

Latest commit

 

History

History
427 lines (317 loc) · 12.3 KB

File metadata and controls

427 lines (317 loc) · 12.3 KB

import {Meta} from '@storybook/addon-docs';

import {DownloadLLMFile, StorybookStatusIndicator} from '@workday/canvas-kit-docs'; import {InformationHighlight} from '@workday/canvas-kit-react/information-highlight';

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 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

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

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>

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

// `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 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.

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 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

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 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

<PrimaryButton cs={[baseStyles, variantStyles]} />

❌ Don’t

<PrimaryButton className={`${baseStyles} ${variantStyles}`} />

Use Variables for Dynamic Values

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 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

const customIconStencil = createStencil({
  extends: systemIconStencil,
  base: {
    margin: system.gap.sm,
  },
});

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

const badgeStencil = createStencil({
  modifiers: {
    status: {
      success: {background: system.color.accent.success},
      error: {background: system.color.brand.accent.critical},
    },
  },
});

Use Compound Modifiers for Complex Conditions

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},
    },
  ],
});

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

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 for more information.

✅ Do

color: system.color.fg.default;
margin: system.gap.md;

❌ Don’t

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

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

const StyledButton = styled(Button)(styles);
<StyledButton cs={createStyles({padding: 8})} />;

Migration Example

Style Props

Before

import {Flex} from '@workday/canvas-kit-react/layout';

<Flex depth={1} marginX={10} background="frenchVanilla100" />;

After

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,
  }}
/>;

Emotion styled

Before (Emotion)

const StyledButton = styled('button')({
  backgroundColor: 'blue',
  color: 'white',
});

After (Canvas Kit Styling)

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>;