1 | import { code, md } from '@kalamazoo/docs';
|
2 |
|
3 | export default md`
|
4 |
|
5 | ## Theming
|
6 |
|
7 | Button now supports the updated Theming API. With this API, components can be passed a custom theming function which has access to all of Button's props, as well as the to the original ADG theme.
|
8 |
|
9 | This means that the custom theme can change appearance based on different props, and even ignore the ADG styling completely. Custom
|
10 | themes can modify any CSS prop on the button body or the loading spinner.
|
11 |
|
12 | #### ⚡️ Theming API TLDR:
|
13 | - **\`Button\`** can take a **\`theme\`** prop, a function called by the theming API to generate and return custom styling. It recieves all of Button's props, as well as the built-in ADG theme for button.
|
14 | - The **\`theme\` function** can choose to ignore the ADG theme completely, or spread custom styling on top.
|
15 | - How the custom styling is retrieved is left up to the user, but a common approach is to define the styling in an **object structure** and fetch using custom logic.
|
16 | - In use, buttons can be wrapped by a **\`ThemeProvider\`** that has the \`theme\` function passed in, or a component that wraps \`Button\` can be used in place of Button.
|
17 |
|
18 | ### Building a Custom Themed Button
|
19 |
|
20 | There are two approaches to defining a custom button. The first is to wrap buttons in a ThemeProvider:
|
21 |
|
22 | ${code`
|
23 | // CustomButton.js
|
24 | import React from 'react';
|
25 | import Button, { Theme as ButtonTheme } from '@kalamazoo/button';
|
26 |
|
27 | <ButtonTheme.Provider value={customTheme}>
|
28 | <Button> both of these buttons </Button>
|
29 | <Button> will recieve custom styling </Button>
|
30 | </ButtonTheme.Provider>
|
31 | `}
|
32 |
|
33 | The second approach is to create a wrapped Button component that passes in all existing props, as well as a custom \`theme\` prop:
|
34 |
|
35 | ${code`
|
36 | // CustomButton.js
|
37 | import React from 'react';
|
38 | import Button from '@kalamazoo/button';
|
39 |
|
40 | export default (props) => (
|
41 | <Button
|
42 | {...props}
|
43 | theme={customTheme}
|
44 | }}
|
45 | />
|
46 | );
|
47 | `}
|
48 |
|
49 | ### Building the theming function
|
50 |
|
51 | In both cases, Button's \`theme\` prop expects a **theming function**, which is called by the theming API to style Button. \`theme\` should have the following footprint:
|
52 |
|
53 | ${code`
|
54 | customTheme = (adgTheme, props) => styling
|
55 | `}
|
56 |
|
57 | The output of the function, \`styling\`, is an object containing a pair of style objects:
|
58 | - **\`ButtonStyles\`**, which contains styles for the body of Button.
|
59 | - **\`SpinnerStyles\`**, which contains styles for the loading spinner.
|
60 |
|
61 | Button applies these styles directly using Emotion's \`css\` prop - so you have complete control over how these components in Button are rendered.
|
62 |
|
63 | An example of how the theming function can be implemented is below:
|
64 |
|
65 | ${code`
|
66 | customTheme = (currentTheme, themeProps) => {
|
67 | const { buttonStyles, spinnerStyles } = currentTheme(themeProps);
|
68 | return {
|
69 | buttonStyles: {
|
70 | ...buttonStyles,
|
71 | ...extract(buttonTheme, themeProps),
|
72 | },
|
73 | spinnerStyles,
|
74 | };
|
75 | `}
|
76 |
|
77 | In most cases, the props of interest will include \`appearance\`, \`state\` and \`mode\` - though any prop in Button can be used (including custom props, if you want to add any to your Button wrapper.).
|
78 |
|
79 | The \`extract\` function is an arbitrary function that takes Button's props, and generates the required styling. This is up to what your implementation requires.
|
80 |
|
81 | ### Creating styling for Button with an object structure
|
82 |
|
83 | How the custom styles are determined based on Button's props can vary - for simple changes to Button, the \`theme\` function can return a static object. If defining multiple appearances, or changing something dependent on state, the styles will need to be computed in some function (\`extract\` in this example).
|
84 |
|
85 | When programmatically defining the styling, a common approach is to use an object structure similar to that listed below:
|
86 |
|
87 | ${code`
|
88 | import { colors } from '@kalamazoo/theme';
|
89 |
|
90 | const buttonTheme = {
|
91 | toolbar: {
|
92 | background: {
|
93 | default: { light: 'transparent' },
|
94 | hover: { light: colors.DN60 },
|
95 | active: { light: colors.B75 },
|
96 | },
|
97 | boxShadowColor: {
|
98 | focus: { light: colors.B75 },
|
99 | },
|
100 | color: {
|
101 | default: { light: colors.DN400 },
|
102 | hover: { light: colors.DN400 },
|
103 | active: { light: colors.B400 },
|
104 | disabled: { light: colors.DN100 },
|
105 | },
|
106 | },
|
107 | primary: {
|
108 | background: {
|
109 | default: { light: colors.B100 },
|
110 | hover: { light: colors.B75 },
|
111 | active: { light: colors.B200 },
|
112 | disabled: { light: colors.DN70 },
|
113 | },
|
114 | boxShadowColor: {
|
115 | focus: { light: colors.B75 },
|
116 | },
|
117 | color: {
|
118 | default: { light: colors.DN30 },
|
119 | },
|
120 | },
|
121 | };`}
|
122 |
|
123 | #### Parsing the object structure
|
124 |
|
125 | An example of how this structure can be traversed to get the styles for the current combination of props is shown below:
|
126 |
|
127 | ${code`
|
128 | function extract(newTheme, appearance, state, mode) {
|
129 | if (!newTheme[appearance]) return;
|
130 | const root = newTheme[appearance];
|
131 | return Object.keys(root).reduce((acc, val) => {
|
132 | let node = root;
|
133 | [val, state, mode].forEach(item => {
|
134 | if (!node[item]) return;
|
135 | if (typeof node[item] !== 'object') {
|
136 | acc[val] = node[item];
|
137 | return;
|
138 | }
|
139 | node = node[item];
|
140 | return;
|
141 | });
|
142 | return acc;
|
143 | }, {});
|
144 | }`}
|
145 |
|
146 | This example function will explore the tree, and return all styling props with their correct values for the props provided. Note that if a value is not found, no value is returned for that styling prop - and therefore the ADG styling, or no styling, depending on how the \`theme\` function is defined.
|
147 |
|
148 | ## Other theming guidelines
|
149 | ### Dark Mode support
|
150 |
|
151 | Dark mode support can be added using the API as follows:
|
152 |
|
153 | ${code`
|
154 | <Button theme={(theme, props) => theme({ ...props, mode: 'dark' })}
|
155 | Dark Button
|
156 | </Button>
|
157 | `}
|
158 |
|
159 | ### Styled-components support
|
160 |
|
161 | With the shift to Emotion in \`v12\`, styled-components' \`styled\` function is no longer supported. Emotion's \‘styled\’ function can be used in-place, but as this is not explicitly supported by Button, we recommend using the new theming API instead.
|
162 |
|
163 | More details on migration can be found in the \`v11\` to \`v12\` upgrade guide.
|
164 |
|
165 | `;
|