UNPKG

7.42 kBMarkdownView Raw
1---
2category: packages
3---
4
5## ui-themeable
6
7
8[![npm][npm]][npm-url] 
9[![build-status][build-status]][build-status-url] 
10[![MIT License][license-badge]][LICENSE] 
11[![Code of Conduct][coc-badge]][coc]
12
13
14The [ui-themeable](#ui-themeable) library is meant to be used along with a [babel plugin](#babel-plugin-themeable-styles)
15to import CSS styles and generate theme variables. With this framework, each UI component can be used in
16isolation and support multiple themes, including dynamic themes provided at runtime, while still working within
17a system of components that use a [shared global theme](#canvas).
18
19### Motivation
20
211. Two-tiered theme variable system: system-wide variables + component level variables. With this variable system, components can be themed, tested, and rendered in isolation from the rest of the system, and we can mitigate issues that may arise with system-wide theme updates.
22
232. Runtime theme application and definition: to apply user/account level themes *without using the CSS cascade*.
24
253. Prevent CSS Cascade bugs: All components should specify variants via props or component level theme variables only (no className or style overrides) with a clear API and should not rely on any external styles.
26
274. Theme variables should be accessible in both JS and CSS.
28
295. All component styles and variables should scoped to the component.
30
316. Pre-render/server-side render support (inline critical CSS).
32
33
34### Installation
35
36```sh
37yarn add @instructure/ui-themeable
38```
39
40### Usage
41
42Make a UI component [themeable](#themeable):
43
44```js
45// Button/index.js
46import themeable from '@instructure/ui-themeable'
47
48import styles from 'styles.css'
49import theme from 'theme.js'
50
51class Button extends React.Component {
52 render () {
53 return <button className={styles.root}>{this.props.children}</button>
54 }
55}
56export default themeable(theme, styles)(Example)
57```
58
59Themeable components inject their themed styles into the document when they are mounted.
60
61After the initial mount, a themeable component's theme can be configured explicitly
62via its `theme` prop or passed via React context using the [ApplyTheme](#ApplyTheme) component.
63
64Themeable components register themselves with the [global theme registry](#registry)
65when they are imported into the application, so you will need to be sure to import them
66before you mount your application so that the default themed styles can be generated and injected.
67
68### Defining variables
69
70The themeable component transforms the JS variables defined in the `theme.js` file into CSS custom properties
71that are automatically scoped and applied to the component.
72
73For example, to add a variable for the `hover` state of a `Button` component,
74the `theme.js` file might contain the following:
75
76```js
77// Button/theme.js
78export default function generator ({ colors }) {
79 return (
80 background: colors.backgroundMedium,
81 color: colors.textDarkest,
82
83 hoverColor: colors.textLightest,
84 hoverBackground: colors.backgroundDarkest
85 )
86}
87```
88
89The arguments to the generator function are the [global theme variables](#canvas). In the above example, we've defined
90the default theme for the Button component.
91
92The purpose of the generator function is to take the global variables and apply them as values to the functional
93component level variables. When coming up with names for the component level variables, try to make them describe
94how they are used in the component (vs describing the variable value).
95
96### Supporting multiple themes
97
98If we want to make the Button transform the global theme variables differently with a another theme,
99(e.g. [canvas-high-contrast](#canvas-high-contrast)) we can make a generator for that theme:
100
101```js
102// Button/theme.js
103...
104generator['canvas-high-contrast'] = function ({ colors }) {
105 return {
106 background: colors.backgroundLightest
107 }
108}
109```
110
111This will override the default Button theme and use the global theme variable `colors.textLightest` for the
112value of its `background` theme variable instead of `colors.tiara`.
113
114The rest of the variables will pick up from the default Button theme generator (applying the global theme variables
115from the `canvas-high-contrast` theme).
116
117### Using theme variables in CSS
118
119Note: Don't worry about scoping your CSS variables (the [ui-themeable](#ui-themeable) library will take care of that for you):
120
121```css
122.root {
123 background: var(--background);
124 color: var(--color);
125
126 &:hover {
127 background: var(--hoverBackground);
128 color: var(--hoverColor);
129 }
130}
131```
132
133### Using theme variables in JavaScript
134
135Since the variables are defined in JS you can also access them in your component JS (e.g. `this.theme.hoverColor`) which will give
136you the theme values applied via React context with `ApplyTheme` or the `theme` prop (falling back to the defaults provided in the `theme.js` file).
137
138
139### How it works
140
141The [babel plugin](#babel-plugin-themeable-styles) does a few things:
142
143 1. It uses the [css-modules-require-hook](https://github.com/css-modules/css-modules-require-hook)
144 to namespace the class names (configurable via themeable.config.js).
145 2. It runs [postcss](https://github.com/postcss/postcss) on the contents of the `theme.css` file using plugins defined in postcss.config.js, plus [postcss-themeable-styles](#postcss-themeable-styles).
146 3. It converts the processed CSS string to a function that provides a JS template
147 so that variable values from `theme.js` can be injected into the CSS
148 for browsers that don't support CSS variables.
149
150 The [ui-themeable](#ui-themeable) library will call the theme function and inject the resulting CSS string into the document
151 when the component mounts. If the browser supports CSS variables, it will
152 inject namespaced CSS variables into the CSS before adding it to the document.
153
154 e.g. The following is injected into the document for browsers with CSS var support:
155
156 ```css
157 .list__root {
158 color: var(--list__color);
159 background: var(--list__background);
160 }
161
162 :root {
163 --list__color: #8893A2;
164 --list__background: #FFFFFF;
165 }
166 ```
167
168 Whereas if the browser does not support CSS variables:
169
170 ```css
171 .list__root {
172 color: #8893A2;
173 background: #FFFFFF;
174 }
175 ```
176
177 The [ui-themeable](#ui-themeable) library also supports runtime themes as follows:
178
179 For browsers that support CSS variables, it will add variables via the
180 style attribute on the component root (when the theme is changed, either
181 via the theme property or via React context using the [ApplyTheme](#ApplyTheme) component).
182
183 ```html
184 <div style="--list-background: red">
185 ```
186
187 For browsers that don't support CSS variables it will update the DOM like:
188
189 ```html
190 <div data-theme="XYZ">
191 <style type="text/css">
192 [data-theme="XYZ"].list__root {
193 background: red;
194 }
195 </style>
196 </div>
197 ```
198
199
200[npm]: https://img.shields.io/npm/v/@instructure/ui-themeable.svg
201[npm-url]: https://npmjs.com/package/@instructure/ui-themeable
202
203[build-status]: https://travis-ci.org/instructure/instructure-ui.svg?branch=master
204[build-status-url]: https://travis-ci.org/instructure/instructure-ui "Travis CI"
205
206[license-badge]: https://img.shields.io/npm/l/instructure-ui.svg?style=flat-square
207[license]: https://github.com/instructure/instructure-ui/blob/master/LICENSE
208
209[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
210[coc]: https://github.com/instructure/instructure-ui/blob/master/CODE_OF_CONDUCT.md
211
\No newline at end of file