@workday/canvas-kit-docs 14.0.0-alpha.1149-next.0 → 14.0.0-alpha.1153-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.
@@ -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" />
@@ -0,0 +1,254 @@
1
+ import {InformationHighlight} from '@workday/canvas-kit-preview-react/information-highlight';
2
+ import {Hyperlink} from '@workday/canvas-kit-react/button';
3
+ import {system} from '@workday/canvas-tokens-web'
4
+
5
+
6
+ # Canvas Kit Styling
7
+
8
+ ## Introduction
9
+
10
+ Canvas Kit styling is a custom CSS-in-JS solution that provides both a runtime for development and a
11
+ static parsing process for build time. This system offers several key benefits:
12
+
13
+ - TypeScript autocomplete for enhanced developer experience
14
+ - Low runtime overhead for better performance
15
+ - Static CSS compilation for optimized builds
16
+ - Dynamic styling with CSS Variables for flexible design
17
+
18
+ The motivation behind this custom styling solution stems from the need to move beyond IE11 support
19
+ and implement performance improvements using static styling methods. For more details, refer to the
20
+ [Why Canvas Kit Styling](https://workday.github.io/canvas-kit/?path=/docs/styling-why-canvas-styling--docs)
21
+ section.
22
+
23
+ ## Overview
24
+
25
+ The Canvas Kit styling system consists of two main packages:
26
+
27
+ - `@workday/canvas-kit-styling` - Core styling utilities for runtime use
28
+ - `@workday/canvas-kit-styling-transform` - Build-time optimization tools
29
+
30
+ These packages work together to provide a CSS-in-JS experience during development while enabling optimized static CSS in production.
31
+
32
+ ## Installation
33
+
34
+ ```sh
35
+ yarn add @workday/canvas-kit-styling
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ```tsx
41
+ import React from 'react';
42
+ import {createRoot} from 'react-dom/client';
43
+
44
+ import {createStyles} from '@workday/canvas-kit-styling';
45
+
46
+ const myStyles = createStyles({
47
+ backgroundColor: 'red',
48
+ }); // returns the CSS class name created for this style
49
+
50
+ myStyles; // something like "css-{hash}"
51
+
52
+ const domNode = document.getElementById('root');
53
+ const root = createRoot(domNode);
54
+
55
+ root.render(<div className={myStyles}>Hello!</div>);
56
+ ```
57
+
58
+ ## Development
59
+
60
+ Canvas Kit Styling comes with a runtime that doesn't need anything special for developement. The
61
+ runtime uses `@emotion/css` to include your styles on the page.
62
+
63
+ ## Production
64
+
65
+ If you wish to use the static compilation, you must use the `@workday/canvas-kit-styling-transform`
66
+ package. Add the following to your project's `tsconfig.json` file:
67
+
68
+ ```json
69
+ {
70
+ "compilerOptions": {
71
+ // other options
72
+ "plugins": [
73
+ {
74
+ "transform": "@workday/canvas-kit-styling-transform",
75
+ "prefix": "css",
76
+ "fallbackFiles": [""]
77
+ }
78
+ ]
79
+ }
80
+ }
81
+ ```
82
+
83
+ This adds a list of plugins to use when transforming TypeScript files into JavaScript files. The
84
+ [ts-patch](https://www.npmjs.com/package/ts-patch) projects uses the `plugins` when running
85
+ transforms.
86
+
87
+ ### Webpack
88
+
89
+ You will need to transform TypeScript files using the `ts-patch` which is the same as the TypeScript
90
+ tanspiler except it uses TypeScript's
91
+ [transform API](https://levelup.gitconnected.com/writing-a-custom-typescript-ast-transformer-731e2b0b66e6)
92
+ to transform code during compilation.
93
+
94
+ In your webpack config, you add the following:
95
+
96
+ ```js
97
+ {
98
+ rules: [
99
+ //...
100
+ {
101
+ test: /.\.tsx?$/,
102
+ loaders: [
103
+ // ts-loader
104
+ {
105
+ loader: require.resolve('ts-loader'),
106
+ options: {
107
+ compiler: 'ts-patch/compiler',
108
+ },
109
+ },
110
+ // OR awesome-typescript-loader
111
+ {
112
+ loader: require.resolve('awesome-typescript-loader'),
113
+ },
114
+ ],
115
+ },
116
+ ];
117
+ }
118
+ ```
119
+
120
+ ## Core Styling Approaches for Static Styling
121
+ For proper static styling there's two methods that you can use to apply styles.
122
+ 1. Using `createStyles` for simple object base styles.
123
+ 2. Using `createStencil` for dynamic styles and reusable components.
124
+
125
+ Both approaches are intended to be used in tandem with the `cs` prop when applying styles to our components.
126
+
127
+ ### `cs` Prop
128
+
129
+ The `cs` prop takes in a single, or an array of values that are created by the `cs` function, a
130
+ string representing a CSS class name, or the return of the `createVars` function. It merges
131
+ everything together and applies `className` and `style` attributes to a React element. Most of our
132
+ components extend the `cs` prop so that you can statically apply styles to them.
133
+
134
+ > **Important**: While the `cs` prop accepts a style object, **this will not** be considered
135
+ > statically styling an element and you will lose the performance benefits. We plan on providing a babel plugin to extract these styles statically in a future version.
136
+
137
+ ```tsx
138
+ import {system} from '@workday/canvas-tokens-webs';
139
+ import {PrimaryButton} from '@workday/canvas-kit-react/button';
140
+
141
+ const styles = createStyles({color: system.color.static.red.default});
142
+
143
+ function MyComponent() {
144
+ return <PrimaryButton cs={styles}>Text</PrimaryButton>;
145
+ }
146
+ ```
147
+
148
+ ### `createStyles`
149
+
150
+ The primary utility function is the `createStyles` function. It makes a call to the `css` function
151
+ from `@emotion/css`. Emotion still does most of the heavy lifting by handling the serialization,
152
+ hashing, caching, and style injection.
153
+
154
+ ```tsx
155
+ // Bad example (inside render function)
156
+ import {system} from '@workday/canvas-tokens-webs';
157
+ import {PrimaryButton} from '@workday/canvas-kit-react/button';
158
+
159
+ function MyComponent() {
160
+ const styles = createStyles({color: system.color.static.red.default}); // Don't do this
161
+ return <PrimaryButton cs={styles}>Text</PrimaryButton>;
162
+ }
163
+
164
+ // Good example (outside render function)
165
+ import {system} from '@workday/canvas-tokens-webs';
166
+ import {PrimaryButton} from '@workday/canvas-kit-react/button';
167
+
168
+ const styles = createStyles({color: system.color.static.red.default});
169
+
170
+ function MyComponent() {
171
+ return <PrimaryButton cs={styles}>Text</PrimaryButton>;
172
+ }
173
+ ```
174
+
175
+ Most of our components support using the `cs` prop to apply the static styles. It merges
176
+ everything together and applies `className` and `style` attributes to a React element.
177
+
178
+ <InformationHighlight className="sb-unstyled" cs={{marginBlock: system.space.x4,}}>
179
+ <InformationHighlight.Icon />
180
+ <InformationHighlight.Heading>Information</InformationHighlight.Heading>
181
+ <InformationHighlight.Body>
182
+ For a more in depth overview, please view our <Hyperlink src="https://workday.github.io/canvas-kit/?path=/docs/styling-getting-started-create-styles--docs">Create Styles</Hyperlink> docs.
183
+ </InformationHighlight.Body>
184
+ </InformationHighlight>
185
+
186
+ ### `createStencil`
187
+
188
+ `createStencil` is a function for creating reusable, complex component styling systems. It manages
189
+ `base` styles, `parts`, `modifiers`, `variables`, and `compound` modifiers. Most of our components
190
+ also export their own Stencil that might expose CSS variables in order to modify the component.
191
+
192
+ In the example below, we leverage `parts`, `vars`, `base` and `modifiers` to create a reusable
193
+ `Card` component. The Stencil allows us to dynamic style the component based on the props.
194
+
195
+ ```tsx
196
+ import {createStencil}from '@workday/canvas-kit-styling';
197
+ import {Card} from '@workday/canvas-kit-react/card';
198
+ import {system} from '@workday/canvas-tokens-webs';
199
+
200
+ const themedCardStencil = createStencil({
201
+ vars: {
202
+ // Create CSS variables for the color of the header
203
+ headerColor: ''
204
+ },
205
+ parts: {
206
+ // Allows for styling a sub element of the component that may not be exposed through the API
207
+ header: 'themed-card-header'
208
+ },
209
+ base: ({headerPart, headerColor}) => ({
210
+ padding: system.space.x4,
211
+ boxShadow: system.depth[2],
212
+ backgroundColor: system.color.bg.default,
213
+ color: system.color.text.default,
214
+ // Targets the header part via [data-part="themed-card-header"]"]
215
+ [headerPart]: {
216
+ color: headerColor
217
+ }
218
+ }),
219
+ modifiers: {
220
+ isDarkTheme: {
221
+ // If the prop `isDarkTheme` is true, style the component and it's parts
222
+ true: ({headerPart}) => ({
223
+ backgroundColor: system.color.bg.contrast.default,
224
+ color: system.color.text.inverse
225
+ [headerPart]: {
226
+ color: system.color.text.inverse
227
+ }
228
+ })
229
+ }
230
+ }
231
+ })
232
+
233
+ const ThemedCard = ({isDarkTheme, headerColor, elemProps}) => {
234
+ return (
235
+ /* Use the `cs` prop to apply the stencil and pass it the dynamic properties it needs to style accordingly */
236
+ <Card cs={themedCardStencil({isDarkTheme, headerColor})} {...elemProps}>
237
+ /* Apply the data part selector to the header */
238
+ <Card.Heading {...themedCardStencil.parts.header}>Canvas Supreme</Card.Heading>
239
+ <Card.Body>
240
+ Our house special supreme pizza includes pepperoni, sausage, bell peppers, mushrooms,
241
+ onions, and oregano.
242
+ </Card.Body>
243
+ </Card>
244
+ );
245
+ };
246
+ ```
247
+
248
+ <InformationHighlight className="sb-unstyled" cs={{marginBlock: system.space.x4,}}>
249
+ <InformationHighlight.Icon />
250
+ <InformationHighlight.Heading>Information</InformationHighlight.Heading>
251
+ <InformationHighlight.Body>
252
+ For a more in depth overview, please view our <Hyperlink src="https://workday.github.io/canvas-kit/?path=/docs/styling-getting-started-stencils--docs">Create Stencil</Hyperlink> docs.
253
+ </InformationHighlight.Body>
254
+ </InformationHighlight>