@workday/canvas-kit-docs 14.0.0-alpha.1148-next.0 → 14.0.0-alpha.1151-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es6/lib/docs.js +0 -550
- package/dist/es6/lib/stackblitzFiles/packageJSONFile.js +5 -5
- package/dist/es6/lib/stackblitzFiles/packageJSONFile.ts +5 -5
- package/dist/mdx/14.0-UPGRADE-GUIDE.mdx +19 -2
- package/dist/mdx/react/card/card.mdx +4 -5
- package/dist/mdx/styling/mdx/CreateStyles.mdx +111 -0
- package/dist/mdx/styling/mdx/CustomizingStyles.mdx +179 -0
- package/dist/mdx/styling/mdx/FromEmotion.mdx +178 -0
- package/dist/mdx/styling/mdx/MergingStyles.mdx +164 -0
- package/dist/mdx/styling/mdx/Overview.mdx +254 -0
- package/dist/mdx/styling/mdx/Stencils.mdx +459 -0
- package/dist/mdx/styling/mdx/Utilities.mdx +246 -0
- package/dist/mdx/styling/mdx/WhyCanvasStyling.mdx +136 -0
- package/dist/mdx/styling/mdx/examples/CSProp.tsx +36 -0
- package/dist/mdx/styling/mdx/examples/CreateModifiers.tsx +27 -0
- package/dist/mdx/styling/mdx/examples/CreateStencil.tsx +63 -0
- package/dist/mdx/styling/mdx/examples/CreateStyles.tsx +13 -0
- package/dist/mdx/styling/mdx/examples/CreateVars.tsx +20 -0
- package/dist/mdx/styling/mdx/examples/CustomButton.tsx +69 -0
- package/dist/mdx/styling/mdx/examples/CustomIcon.tsx +23 -0
- package/dist/mdx/styling/mdx/examples/EmotionButton.tsx +111 -0
- package/dist/mdx/styling/mdx/examples/ManualStylesButton.tsx +107 -0
- package/dist/mdx/styling/mdx/examples/StyledButton.tsx +31 -0
- package/dist/mdx/styling/mdx/examples/StylingButton.tsx +107 -0
- package/dist/mdx/styling/mdx/examples/StylingOverrides.tsx +158 -0
- package/package.json +6 -6
- package/dist/mdx/preview-react/menu/Menu.mdx +0 -105
- package/dist/mdx/preview-react/menu/examples/Basic.tsx +0 -74
- package/dist/mdx/preview-react/menu/examples/ContextMenu.tsx +0 -67
- package/dist/mdx/preview-react/menu/examples/CustomMenuItem.tsx +0 -15
- package/dist/mdx/preview-react/menu/examples/Headers.tsx +0 -32
- package/dist/mdx/preview-react/menu/examples/Icons.tsx +0 -26
- package/dist/mdx/preview-react/menu/examples/ManyItems.tsx +0 -15
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {ExampleCodeBlock} from '@workday/canvas-kit-docs';
|
|
2
|
+
import {InformationHighlight} from '@workday/canvas-kit-preview-react/information-highlight';
|
|
3
|
+
import {system} from '@workday/canvas-tokens-web'
|
|
4
|
+
|
|
5
|
+
import CreateStyles from './examples/CreateStyles';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Create Styles
|
|
9
|
+
|
|
10
|
+
The primary utility function is the `createStyles` function. It makes a call to the `css` function
|
|
11
|
+
from `@emotion/css`. Emotion still does most of the heavy lifting by handling the serialization,
|
|
12
|
+
hashing, caching, and style injection.
|
|
13
|
+
|
|
14
|
+
## Basic Example
|
|
15
|
+
|
|
16
|
+
In this example, the HTML will look like:
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<div class="css-m39zwu"></div>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The CSS will look like this:
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
.css-m39zwu {
|
|
26
|
+
background: var(--cnvs-sys-color-bg-primary-default);
|
|
27
|
+
color: var(--cnvs-sys-color-text-inverse);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
<InformationHighlight className="sb-unstyled" cs={{marginBlock: system.space.x4,}}>
|
|
31
|
+
<InformationHighlight.Icon />
|
|
32
|
+
<InformationHighlight.Heading> Note</InformationHighlight.Heading>
|
|
33
|
+
<InformationHighlight.Body>
|
|
34
|
+
The `createStyles` function handles wrapping our Tokens in `var(--${token})`.
|
|
35
|
+
</InformationHighlight.Body>
|
|
36
|
+
</InformationHighlight>
|
|
37
|
+
|
|
38
|
+
We're using `className` for simplicity here.
|
|
39
|
+
|
|
40
|
+
<ExampleCodeBlock code={CreateStyles} />
|
|
41
|
+
|
|
42
|
+
<InformationHighlight className="sb-unstyled" variant="caution" cs={{marginBlock: system.space.x4,}}>
|
|
43
|
+
<InformationHighlight.Icon />
|
|
44
|
+
<InformationHighlight.Heading> Caution: Performance Hit</InformationHighlight.Heading>
|
|
45
|
+
<InformationHighlight.Body>
|
|
46
|
+
Do not inline the call to `createStyles` in the render function of a component. This will cause performance issues as a new style is inserted into the browser on every render.
|
|
47
|
+
</InformationHighlight.Body>
|
|
48
|
+
</InformationHighlight>
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
// Bad example (inside render function)
|
|
52
|
+
import {system} from '@workday/canvas-tokens-webs';
|
|
53
|
+
import {PrimaryButton} from '@workday/canvas-kit-react/button';
|
|
54
|
+
|
|
55
|
+
function MyComponent() {
|
|
56
|
+
const styles = createStyles({color: system.color.static.red.default}); // Don't do this
|
|
57
|
+
return <PrimaryButton className={createStyles({color: system.color.static.red.default})}>Text</PrimaryButton>;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## When to Use `createStyles`
|
|
62
|
+
|
|
63
|
+
`createStyles` is a great way to generate static styles when styling our components that don't rely
|
|
64
|
+
on dynamic styles. Use `createStyles` if you want to create re useable styles or need to apply
|
|
65
|
+
simple style overrides to our components.
|
|
66
|
+
|
|
67
|
+
## When to Use Something Else
|
|
68
|
+
|
|
69
|
+
You should use [stencils](/docs/styling-getting-started-stencils--docs) when styling our components
|
|
70
|
+
that have complex styles and dynamic properties.
|
|
71
|
+
|
|
72
|
+
## Proper Usage
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
// Bad example (inside render function)
|
|
76
|
+
import {system} from '@workday/canvas-tokens-webs';
|
|
77
|
+
import {PrimaryButton} from '@workday/canvas-kit-react/button';
|
|
78
|
+
|
|
79
|
+
function MyComponent() {
|
|
80
|
+
const styles = createStyles({color: system.color.static.red.default}); // Don't do this
|
|
81
|
+
return <PrimaryButton cs={styles}>Text</PrimaryButton>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Good example (outside render function)
|
|
85
|
+
import {system} from '@workday/canvas-tokens-webs';
|
|
86
|
+
import {PrimaryButton} from '@workday/canvas-kit-react/button';
|
|
87
|
+
|
|
88
|
+
const styles = createStyles({color: system.color.static.red.default});
|
|
89
|
+
|
|
90
|
+
function MyComponent() {
|
|
91
|
+
return <PrimaryButton cs={styles}>Text</PrimaryButton>;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
<InformationHighlight className="sb-unstyled" cs={{marginBlock: system.space.x4,}}>
|
|
96
|
+
<InformationHighlight.Icon />
|
|
97
|
+
<InformationHighlight.Heading>Note</InformationHighlight.Heading>
|
|
98
|
+
<InformationHighlight.Body>
|
|
99
|
+
Most of our components support using the `cs` prop to apply the static styles. It merges everything together and applies `className` and `style` attributes to a React element
|
|
100
|
+
</InformationHighlight.Body>
|
|
101
|
+
</InformationHighlight>
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
## Performance Benefits
|
|
106
|
+
|
|
107
|
+
`createStyles` is performant because:
|
|
108
|
+
|
|
109
|
+
- Styles are statically evaluated when styles are defined outside the render function
|
|
110
|
+
- No new StyleSheets are injected during render
|
|
111
|
+
- It works well with the browser's selector cache
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# How To Customize Styles
|
|
2
|
+
|
|
3
|
+
There are multiple ways to customize styles for components within Canvas Kit. The approach you
|
|
4
|
+
choose will depend on your use case. Ranging from some simple overrides to fully custom solutions,
|
|
5
|
+
here are the following options:
|
|
6
|
+
|
|
7
|
+
- [Create Styles](#createstyles)
|
|
8
|
+
- [Stencils](#stencils)
|
|
9
|
+
|
|
10
|
+
## Create Styles
|
|
11
|
+
|
|
12
|
+
### Using `createStyles` with `cs` prop
|
|
13
|
+
|
|
14
|
+
Use `createStyles` in tandem with `cs` prop when you're overriding static styles and making small modifications to an
|
|
15
|
+
existing Canvas Kit component like padding, color and flex properties. Take our `Text` component as
|
|
16
|
+
an example.
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import {createStyles} from '@Workday/canvas-kit-styling';
|
|
20
|
+
import {system} from '@Workday/canvas-tokens-web';
|
|
21
|
+
import {Text} from '@Workday/canvas-kit-react/text';
|
|
22
|
+
|
|
23
|
+
const uppercaseTextStyles = createStyles({
|
|
24
|
+
textTransform: 'uppercase',
|
|
25
|
+
margin: system.space.x4
|
|
26
|
+
})
|
|
27
|
+
//...
|
|
28
|
+
<Text cs={uppercaseTextStyles}>My uppercased text</Text>;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
> **Note:** `createStyles` handles wrapping our token variables in `var(--${token})`
|
|
32
|
+
|
|
33
|
+
You can also apply styles created via `createStyles` via `className`.
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import {createStyles} from '@Workday/canvas-kit-styling';
|
|
37
|
+
import {system} from '@Workday/canvas-tokens-web';
|
|
38
|
+
import {Text} from '@Workday/canvas-kit-react/text';
|
|
39
|
+
|
|
40
|
+
const uppercaseTextStyles = createStyles({
|
|
41
|
+
textTransform: 'uppercase',
|
|
42
|
+
margin: system.space.x4
|
|
43
|
+
})
|
|
44
|
+
//...
|
|
45
|
+
<Text className={uppercaseTextStyles}>My uppercased text</Text>;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If you need to dynamically apply styles based on some state or prop, use [Stencils](#stencils) instead.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## Stencils
|
|
52
|
+
|
|
53
|
+
Stencils can be useful when applying dynamic styles or building your own reusable component.
|
|
54
|
+
|
|
55
|
+
### Extending Stencils
|
|
56
|
+
|
|
57
|
+
[Stencils](https://workday.github.io/canvas-kit/?path=/docs/styling-basics--create-stencil) help you
|
|
58
|
+
organize the styling of reusable components into base styles, modifiers, and variables. The
|
|
59
|
+
organization makes it more natural to produce static and clean CSS with optional extraction into CSS
|
|
60
|
+
files.
|
|
61
|
+
|
|
62
|
+
Stencils that define variables, modifiers and base styles can be extended to create your own
|
|
63
|
+
reusable component using Canvas Kit styles.
|
|
64
|
+
|
|
65
|
+
If we take `SystemIcon` component as an example, it defines `systemIconStencil` which defines styles
|
|
66
|
+
for an icon. This stencil can be extended to build a custom icon component for your use case.
|
|
67
|
+
|
|
68
|
+
**Before v11** you'd have to use `systemIconStyles` function to overwrite styles for an icon:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
// Before v11
|
|
72
|
+
import {systemIconStyles} from '@workday/canvas-kit-react';
|
|
73
|
+
import {space} from '@workday/canvas-kit-react/tokens'; // old tokens
|
|
74
|
+
|
|
75
|
+
// old way of styling with Emotion styled
|
|
76
|
+
const StyledNavIcon = styled('span')(({size, iconStyles}){
|
|
77
|
+
display: 'inline-flex',
|
|
78
|
+
pointerEvents: 'unset',
|
|
79
|
+
margin: `${space.xxxs} ${space.xxxs} 0 0`,
|
|
80
|
+
padding: '0',
|
|
81
|
+
'svg': {
|
|
82
|
+
...iconStyles,
|
|
83
|
+
width: size,
|
|
84
|
+
height: size,
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const NavIcon = ({iconColor, iconHover, iconBackground, iconBackgroundHover, icon, size}) => {
|
|
89
|
+
// old way of styling with systemIconStyles function
|
|
90
|
+
// systemIconStyles is deprecated in v11
|
|
91
|
+
const iconStyles = systemIconStyles({
|
|
92
|
+
fill: iconColor,
|
|
93
|
+
fillHover: iconHover,
|
|
94
|
+
background: iconBackground,
|
|
95
|
+
backgroundHover: iconBackgroundHover,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// insert icon function used by platform or any other functionality here
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<StyledNavIcon
|
|
102
|
+
icon={icon}
|
|
103
|
+
size={size}
|
|
104
|
+
iconStyles={iconStyles}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**In v11** you'd extend `systemIconStencil` to reuse its styles:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
// v11
|
|
114
|
+
import {createStencil} from '@workday/canvas-kit-styling';
|
|
115
|
+
import {system} from '@workday/canvas-tokens-web';
|
|
116
|
+
import {systemIconStencil} from '@workday/canvas-kit-react/icon';
|
|
117
|
+
|
|
118
|
+
const navIconStencil = createStencil({
|
|
119
|
+
// We extend `systemIconStencil` to inherit it's base styles, modifiers and variables so that we can customize it
|
|
120
|
+
extends: systemIconStencil,
|
|
121
|
+
vars: {
|
|
122
|
+
// These variables support our styling iconHover and iconBackgroundHover
|
|
123
|
+
// they can be removed later and overwritten by `cs`.
|
|
124
|
+
// Also note the variables have no value. This allows for cascading styles.
|
|
125
|
+
fillHover: '',
|
|
126
|
+
backgroundHover: '',
|
|
127
|
+
},
|
|
128
|
+
base: ({fillHover, backgroundHover}) => ({
|
|
129
|
+
display: 'inline-flex',
|
|
130
|
+
pointerEvents: 'unset',
|
|
131
|
+
// instead of using our old tokens it's better to use our new system tokens
|
|
132
|
+
margin: `${system.space.x1} ${system.space.x1} 0 0`,
|
|
133
|
+
padding: '0',
|
|
134
|
+
'&:hover, &.hover': {
|
|
135
|
+
// systemIconStencil doesn't have hover specific variables
|
|
136
|
+
// so we reassigned color and backgroundColor variables using pseudo-selector
|
|
137
|
+
[systemIconStencil.vars.color]: fillHover,
|
|
138
|
+
[systemIconStencil.vars.backgroundColor]: backgroundHover,
|
|
139
|
+
},
|
|
140
|
+
}),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Your reusable NavIcon component using Stencils
|
|
144
|
+
const NavIcon = ({
|
|
145
|
+
iconColor,
|
|
146
|
+
iconHover,
|
|
147
|
+
iconBackground,
|
|
148
|
+
iconBackgroundHover,
|
|
149
|
+
icon,
|
|
150
|
+
size,
|
|
151
|
+
...elemProps
|
|
152
|
+
}) => {
|
|
153
|
+
// insert icon function used by platform or any other functionality here
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<span
|
|
157
|
+
icon={icon}
|
|
158
|
+
{...handleCsProp(
|
|
159
|
+
elemProps,
|
|
160
|
+
navIconStencil({
|
|
161
|
+
// Because we're extending systemIconStencil, it already has a size prop and applies size to the svg's width and height
|
|
162
|
+
// so we don't need to set these variables in our navIconStencil
|
|
163
|
+
size,
|
|
164
|
+
// systemIconStencil already has color (for icon fill) and backgroundColor variables
|
|
165
|
+
// so we assigned them to our prop values
|
|
166
|
+
color: iconColor,
|
|
167
|
+
backgroundColor: iconBackground,
|
|
168
|
+
fillHover: iconHover,
|
|
169
|
+
backgroundHover: iconBackgroundHover,
|
|
170
|
+
})
|
|
171
|
+
)}
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Another example of Stencil extension and customization is our
|
|
178
|
+
[CustomButton](https://workday.github.io/canvas-kit/?path=/story/components-buttons--custom-styles)
|
|
179
|
+
example. This example highlights the power of inheritance that you get from extending stencils.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import {ExampleCodeBlock} from '@workday/canvas-kit-docs';
|
|
2
|
+
import EmotionButton from './examples/EmotionButton';
|
|
3
|
+
import ManualStylesButton from './examples/ManualStylesButton';
|
|
4
|
+
import StylingButton from './examples/StylingButton';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Converting from @emotion/styled
|
|
8
|
+
|
|
9
|
+
The most difficult part of understanding styling without Emotion's runtime is the mindset shift. You
|
|
10
|
+
are using CSS to merge properties instead of JavaScript. This is essential to remove the runtime of
|
|
11
|
+
Emotion. We'll use a contrived button example using `@emotion/styled` and our styling solution to
|
|
12
|
+
step through the differences.
|
|
13
|
+
|
|
14
|
+
## Button using `@emotion/styled`
|
|
15
|
+
|
|
16
|
+
<ExampleCodeBlock code={EmotionButton} />
|
|
17
|
+
|
|
18
|
+
If we inspect each button, we'll notice each has a different class name. They all look like
|
|
19
|
+
`css-{hash}`:
|
|
20
|
+
|
|
21
|
+
For example, the Primary buttons:
|
|
22
|
+
|
|
23
|
+
- Primary Large: `css-oqv33j`
|
|
24
|
+
- Primary Medium: `css-1nhzlx`
|
|
25
|
+
- Primary Small: `css-1ygk6q`
|
|
26
|
+
|
|
27
|
+
This means each button is a unique style sheet insert by Emotion. If we render each permutation at
|
|
28
|
+
once, there will only be one expensive
|
|
29
|
+
[style recalculation](https://microsoftedge.github.io/DevTools/explainers/StyleTracing/explainer.html)
|
|
30
|
+
|
|
31
|
+
Converting to use the Canvas Kit Styling solution means organizing a little different. In our
|
|
32
|
+
example, it is already organized well, but conditionals might be anywhere in the style functions and
|
|
33
|
+
will need to be organized in groups.
|
|
34
|
+
|
|
35
|
+
## Button using only `createStyles`
|
|
36
|
+
|
|
37
|
+
What are we really trying to accomplish? [BEM](https://getbem.com/introduction) fits well with
|
|
38
|
+
compound components. BEM stands for Block, Element, Modifer. In compound components, "Block" refers
|
|
39
|
+
to a container component while "Element" refers to subcomponets. The "Modifer" refers to changing
|
|
40
|
+
the appearance of a block.
|
|
41
|
+
|
|
42
|
+
In our example, all styles that are common to all appearances of our button. It might be
|
|
43
|
+
`borderRadius`, `fontFamily`. We can use `createStyles` to define these styles:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const baseStyles = createStyles({
|
|
47
|
+
fontSize: '1rem',
|
|
48
|
+
display: 'flex',
|
|
49
|
+
borderRadius: '1rem',
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The `variant` modifiers use a variable prop called `backgroundColor` which cannot be variable at
|
|
54
|
+
runtime. We need to use a CSS Variable for this.
|
|
55
|
+
|
|
56
|
+
We can create modifers using `createStyles` and organize them in an object:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const modifierStyles = {
|
|
60
|
+
variant: {
|
|
61
|
+
primary: createStyles({
|
|
62
|
+
background: `var(--background-color-button, blue)`,
|
|
63
|
+
color: 'white',
|
|
64
|
+
}),
|
|
65
|
+
secondary: createStyles({
|
|
66
|
+
background: `var(--background-color-button, gray)`,
|
|
67
|
+
}),
|
|
68
|
+
danger: createStyles({
|
|
69
|
+
background: `var(--background-color-button, red)`,
|
|
70
|
+
}),
|
|
71
|
+
},
|
|
72
|
+
size: {
|
|
73
|
+
large: createStyles({
|
|
74
|
+
fontSize: '1.4rem',
|
|
75
|
+
height: '2rem',
|
|
76
|
+
}),
|
|
77
|
+
medium: createStyles({
|
|
78
|
+
fontSize: '1rem',
|
|
79
|
+
height: '1.5rem',
|
|
80
|
+
}),
|
|
81
|
+
small: createStyles({
|
|
82
|
+
fontSize: '0.8rem',
|
|
83
|
+
height: '1.2rem',
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Each modifier value uses `createStyles` which returns a different class name. This means we can
|
|
90
|
+
create a "Primary Large" button by applying these modifiers to the `className` prop of a React
|
|
91
|
+
element:
|
|
92
|
+
|
|
93
|
+
```jsx
|
|
94
|
+
<button className={`${baseStyles} ${modifierStyles.variant.primary} ${modifierStyles.size.large}`}>
|
|
95
|
+
Primary Large
|
|
96
|
+
</button>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This will create a button with 3 separate class names applied. `@emotion/styled` only applies a
|
|
100
|
+
single css class name.
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<!-- @emotion/styled -->
|
|
104
|
+
<button class="css-108wq52">Primary Large</button>
|
|
105
|
+
|
|
106
|
+
<!-- createStyles -->
|
|
107
|
+
<button class="css-puxv12 css-puxv13 css-puxv16">Primary Large</button>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If you want to change the background color, you'll have to pass it using `style`:
|
|
111
|
+
|
|
112
|
+
```jsx
|
|
113
|
+
<button
|
|
114
|
+
className={`${baseStyles} ${modifierStyles.size.large}`}
|
|
115
|
+
style={{'--color-background-button': 'orange'}}
|
|
116
|
+
>
|
|
117
|
+
Orange Large
|
|
118
|
+
</button>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The output HTML will look like:
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<button class="css-puxv12 css-puxv16" style="--color-background-button: orange;">
|
|
125
|
+
Orange Large
|
|
126
|
+
</button>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This works because CSS Custom Properties cascade values. The `style` attribute defines styles on the
|
|
130
|
+
element directly. This is a runtime in React that allows us to change a style without a new style
|
|
131
|
+
block - the styles can be static, but we can still have variable property values.
|
|
132
|
+
|
|
133
|
+
<ExampleCodeBlock code={ManualStylesButton} />
|
|
134
|
+
|
|
135
|
+
## Button using all utilities
|
|
136
|
+
|
|
137
|
+
If we want variables that are hashed and make it easier to define and use, we have `createVars`.
|
|
138
|
+
There are also edge cases for modifiers like allowing `undefined`, so we made a `createModifiers`
|
|
139
|
+
function as well. Both `createModifiers` and `createVars` return a function that makes it easier to
|
|
140
|
+
call with inputs and will return the correct output.
|
|
141
|
+
|
|
142
|
+
For example, `createModifiers`:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
const myModifiers = createModifiers({
|
|
146
|
+
size: {
|
|
147
|
+
large: 'button-large',
|
|
148
|
+
small: 'button-small'
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
myModifiers.size.large // 'button-large'
|
|
153
|
+
|
|
154
|
+
// the function knows what config can be passed
|
|
155
|
+
// and what restrictions each value has
|
|
156
|
+
myModifiers({size: 'large'}) // 'button-large'
|
|
157
|
+
myModifiers({size: 'small'}) // 'button-small'
|
|
158
|
+
myModifiers() // ''
|
|
159
|
+
|
|
160
|
+
// in a component
|
|
161
|
+
<div className={myModifiers({size: 'large'})} /> // <div class="button-large" />
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`createVars`:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
const myVars = createVars('background', 'color')
|
|
168
|
+
|
|
169
|
+
myVars.color // something like `--color-{hash}`
|
|
170
|
+
|
|
171
|
+
// the function knows what keys are allowed
|
|
172
|
+
myVars({color: 'red'}) // {'--color-{hash}': 'red'}
|
|
173
|
+
|
|
174
|
+
// in a component
|
|
175
|
+
<div style={myVars({color: 'red'})} /> // <div style="--color-{hash}: red;">
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
<ExampleCodeBlock code={StylingButton} />
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {ExampleCodeBlock, SymbolDoc} from '@workday/canvas-kit-docs';
|
|
2
|
+
import {InformationHighlight} from '@workday/canvas-kit-preview-react/information-highlight';
|
|
3
|
+
import {system} from '@workday/canvas-tokens-web';
|
|
4
|
+
import StylingOverrides from './examples/StylingOverrides';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Merging Styles
|
|
8
|
+
|
|
9
|
+
## mergeStyles
|
|
10
|
+
|
|
11
|
+
In v9, we used `@emotion/styled` or `@emotion/react` for all styling which is a runtime styling
|
|
12
|
+
solution. Starting in v10, we're migrating our styling to a more static solution using
|
|
13
|
+
`createStyles` and the `cs` prop.
|
|
14
|
+
|
|
15
|
+
For a transition period, we're opting for backwards compatibility. If style props are present,
|
|
16
|
+
[styled components](https://emotion.sh/docs/styled) are used, or the
|
|
17
|
+
[css prop](https://emotion.sh/docs/css-prop) is used in a component, Emotion's style merging will be
|
|
18
|
+
invoked to make sure the following style precedence:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
createStyles > CSS Prop > Styled Component > Style props
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This will mean that any `css` prop or use of `styled` within the component tree _per element_ will
|
|
25
|
+
cause style class merging. For example:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import styled from '@emotion/styled';
|
|
29
|
+
import {createStyles} from '@workday/canvas-kit-styling';
|
|
30
|
+
import {mergeStyles} from '@workday/canvas-kit-react/layout';
|
|
31
|
+
|
|
32
|
+
const styles1 = createStyles({
|
|
33
|
+
padding: 4,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const styles2 = createStyles({
|
|
37
|
+
padding: 12,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const Component1 = props => {
|
|
41
|
+
return <div {...mergeStyles(props, [styles1])} />;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const Component2 = props => {
|
|
45
|
+
return <Component1 cs={styles2} />;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const Component3 = styled(Component1)({
|
|
49
|
+
padding: 8,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const Component4 = props => {
|
|
53
|
+
return <Component3 cs={styles2} />;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default () => (
|
|
57
|
+
<>
|
|
58
|
+
<Component1 />
|
|
59
|
+
<Component2 />
|
|
60
|
+
<Component3 />
|
|
61
|
+
<Component4 />
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `styled` component API is forcing `mergeStyles` to go into Emotion merge mode, which removes the
|
|
67
|
+
`style1` class name and creates a new class based on all the merged style properties. So
|
|
68
|
+
`.component3` is a new class created by Emotion at render time that merges `.style1` and
|
|
69
|
+
`{padding: 8px}`. `Component4` renders `Component3` with a `cs` prop, but `Component3` is already in
|
|
70
|
+
merge mode and so `Component4` will also merge all styles into a new class name of `.component4`
|
|
71
|
+
that has the styles from `.style1`, `.component3`, and `{padding: 12px}`:
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<head>
|
|
75
|
+
<style>
|
|
76
|
+
.styles1 {
|
|
77
|
+
padding: 4px;
|
|
78
|
+
}
|
|
79
|
+
.styles2 {
|
|
80
|
+
padding: 8px;
|
|
81
|
+
}
|
|
82
|
+
.component3 {
|
|
83
|
+
padding: 4px;
|
|
84
|
+
padding: 8px;
|
|
85
|
+
}
|
|
86
|
+
.component4 {
|
|
87
|
+
padding: 4px;
|
|
88
|
+
padding: 8px;
|
|
89
|
+
padding: 12px;
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
92
|
+
</head>
|
|
93
|
+
|
|
94
|
+
<div class="styles1"></div>
|
|
95
|
+
<div class="styles1 styles2"></div>
|
|
96
|
+
<div class="component3"></div>
|
|
97
|
+
<div class="component4"></div>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The `css` prop and `styled` component APIs will rewrite the `className` React prop by iterating over
|
|
101
|
+
all class names and seeing if any exist within the cache. If a class name does exist in the cache,
|
|
102
|
+
the CSS properties are copied to a new style property map until all the class names are evaluated
|
|
103
|
+
and removed from the `className` prop. Emotion will then combine all the CSS properties and inject a
|
|
104
|
+
new `StyleSheet` with a new class name and add that class name to the element.
|
|
105
|
+
|
|
106
|
+
The following example shows this style merging.
|
|
107
|
+
|
|
108
|
+
<ExampleCodeBlock code={StylingOverrides} />
|
|
109
|
+
|
|
110
|
+
CSS style property merging works by
|
|
111
|
+
[CSS specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity). If two matching
|
|
112
|
+
selectors have the same specificity, the last defined property wins. Stencils take advantage of this
|
|
113
|
+
by making all styles have the same specificity of `0-1-0` and inserting `base` styles, then
|
|
114
|
+
`modifiers` (in order), then `compound` modifiers (in order). This means if a property was defined
|
|
115
|
+
in `base`, a `modifier`, and a `compound` modifier, the `compound` modifier would win because it is
|
|
116
|
+
the last defined. This should be the expected order.
|
|
117
|
+
|
|
118
|
+
<InformationHighlight className="sb-unstyled" variant="caution" cs={{marginBlock: system.space.x4}}>
|
|
119
|
+
<InformationHighlight.Icon />
|
|
120
|
+
<InformationHighlight.Heading>Caution</InformationHighlight.Heading>
|
|
121
|
+
<InformationHighlight.Body>
|
|
122
|
+
While we support `mergeStyles` we'd advise against using this in your components so that users
|
|
123
|
+
can get the performance benefit of static styling using utilities like `createStyles` and
|
|
124
|
+
`createStencil` in tandem with the `cs` prop.
|
|
125
|
+
</InformationHighlight.Body>
|
|
126
|
+
</InformationHighlight>
|
|
127
|
+
|
|
128
|
+
### mergeStyles API
|
|
129
|
+
|
|
130
|
+
<SymbolDoc hideHeading name="mergeStyles" />
|
|
131
|
+
|
|
132
|
+
## handleCsProp
|
|
133
|
+
|
|
134
|
+
But what about when using components that use `@emotion/react` or `@emotion/styled`? Those libraries
|
|
135
|
+
use a different approach. Instead of multiple class names, they use a single, merged class name.
|
|
136
|
+
|
|
137
|
+
`handleCsProp` was created to handle integration with existing components that use the `css` prop
|
|
138
|
+
from `@emotion/react` or the `styled` components from `@emotion/styled`. If a class name from one of
|
|
139
|
+
those libraries is detected, style merging will follow the same rules as those libraries. Instead of
|
|
140
|
+
multiple class names, a single class name with all matching properties is created. The
|
|
141
|
+
`handleCsProp` also takes care of merging `style` props, `className` props, and can handle the `cs`
|
|
142
|
+
prop:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
const myStencil = createStencil({
|
|
146
|
+
// ...
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const MyComponent = elemProps => {
|
|
150
|
+
return <div {...handleProps(elemProps, myStencil({}))} />;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// All props will be merged for you
|
|
154
|
+
<MyComponent style={{color: 'red'}} className="my-classname" cs={{position: 'relative'}} />;
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`handleCsProp` will make sure the `style` prop is passed to the `div` and that the `my-classname`
|
|
158
|
+
CSS class name appears on the `div`'s class list. Also the `cs` prop will add the appropriate styles
|
|
159
|
+
to the element via a CSS class name. If your component needs to handle being passed a `className`,
|
|
160
|
+
`style`, or `cs` prop, use `handleCsProp`.
|
|
161
|
+
|
|
162
|
+
### handleCsProp API
|
|
163
|
+
|
|
164
|
+
<SymbolDoc hideHeading name="handleCsProp" />
|