1 | # Radium API
|
2 |
|
3 | **Table of Contents**
|
4 |
|
5 | - [Sample Style Object](#sample-style-object)
|
6 | - [Radium](#radium)
|
7 | - [config.matchMedia](#configmatchmedia)
|
8 | - [config.plugins](#configplugins)
|
9 | - [config.userAgent](#configuseragent)
|
10 | - [getState](#getstate)
|
11 | - [keyframes](#keyframes)
|
12 | - [Plugins](#plugins)
|
13 | - [Style Component](#style-component)
|
14 | - [StyleRoot Component](#styleroot-component)
|
15 | - [TestMode](#testmode)
|
16 |
|
17 |
|
18 | ## Sample Style Object
|
19 |
|
20 | ```jsx
|
21 | var styles = {
|
22 | base: {
|
23 | backgroundColor: '#0074d9',
|
24 | border: 0,
|
25 | borderRadius: '0.3em',
|
26 | color: '#fff',
|
27 | cursor: 'pointer',
|
28 | fontSize: 16,
|
29 | outline: 'none',
|
30 | padding: '0.4em 1em',
|
31 |
|
32 | ':hover': {
|
33 | backgroundColor: '#0088FF'
|
34 | },
|
35 |
|
36 | ':focus': {
|
37 | backgroundColor: '#0088FF'
|
38 | },
|
39 |
|
40 | ':active': {
|
41 | backgroundColor: '#005299',
|
42 | transform: 'translateY(2px)',
|
43 | },
|
44 |
|
45 | // Media queries must start with @media, and follow the same syntax as CSS
|
46 | '@media (min-width: 992px)': {
|
47 | padding: '0.6em 1.2em'
|
48 | },
|
49 |
|
50 | '@media (min-width: 1200px)': {
|
51 | padding: '0.8em 1.5em',
|
52 |
|
53 | // Media queries can also have nested :hover, :focus, or :active states
|
54 | ':hover': {
|
55 | backgroundColor: '#329FFF'
|
56 | }
|
57 | }
|
58 | },
|
59 |
|
60 | red: {
|
61 | backgroundColor: '#d90000',
|
62 |
|
63 | ':hover': {
|
64 | backgroundColor: '#FF0000'
|
65 | },
|
66 |
|
67 | ':focus': {
|
68 | backgroundColor: '#FF0000'
|
69 | },
|
70 |
|
71 | ':active': {
|
72 | backgroundColor: '#990000'
|
73 | }
|
74 | }
|
75 | };
|
76 | ```
|
77 |
|
78 | ## Radium
|
79 |
|
80 | `Radium` itself is a higher-order component, whose job is to:
|
81 | - Provide initial state
|
82 | - Process the `style` attribute after `render()`
|
83 | - Clean up any resources when the component unmounts
|
84 |
|
85 | Usage with `class` and ES7 decorators:
|
86 |
|
87 | ```jsx
|
88 | @Radium
|
89 | class MyComponent extends React.Component { ... }
|
90 | ```
|
91 |
|
92 | Usage with `createClass`:
|
93 |
|
94 | ```jsx
|
95 | var MyComponent = React.createClass({ ... });
|
96 | module.exports = Radium(MyComponent);
|
97 | ```
|
98 |
|
99 | `Radium`'s primary job is to apply interactive or media query styles, but even if you are not using any special styles, the higher order component will still:
|
100 | - Merge arrays of styles passed as the `style` attribute
|
101 | - Automatically vendor prefix the `style` object
|
102 |
|
103 | You can also pass a configuration object to `@Radium`:
|
104 |
|
105 | ```jsx
|
106 | @Radium({matchMedia: mockMatchMedia})
|
107 | class MyComponent extends React.Component { ... }
|
108 |
|
109 | // or with createClass
|
110 |
|
111 | var MyComponent = React.createClass({ ... });
|
112 | module.exports = Radium({matchMedia: mockMatchMedia})(MyComponent);
|
113 | ```
|
114 |
|
115 | You may want to have project-wide Radium settings. Simply create a function that
|
116 | wraps Radium, and use it instead of `@Radium`:
|
117 |
|
118 | ```jsx
|
119 | function ConfiguredRadium(component) {
|
120 | return Radium(config)(component);
|
121 | }
|
122 |
|
123 | // Usage
|
124 | @ConfiguredRadium
|
125 | class MyComponent extends React.Component { ... }
|
126 | ```
|
127 |
|
128 | Radium can be called any number of times with a config object, and later configs
|
129 | will be merged with and overwrite previous configs. That way, you can still
|
130 | override settings on a per-component basis:
|
131 |
|
132 | ```jsx
|
133 | @ConfiguredRadium(config)
|
134 | class MySpecialComponent extends React.Component { ... }
|
135 | ```
|
136 |
|
137 | Alternatively, if the config value can change every time the component is rendered (userAgent, for example), you can pass configuration to any component wrapped in `Radium` using the `radiumConfig` prop:
|
138 |
|
139 | ```jsx
|
140 | <App radiumConfig={{userAgent: req.headers['user-agent']}} />
|
141 | ```
|
142 |
|
143 | The config will be passed down via [context](https://facebook.github.io/react/docs/context.html) to all child components. Fields in the `radiumConfig` prop or context will override those passed into the `Radium()` function.
|
144 |
|
145 | Possible configuration values:
|
146 | - [`matchMedia`](#configmatchmedia)
|
147 | - [`plugins`](#configplugins)
|
148 | - [`userAgent`](#configuseragent)
|
149 |
|
150 | ### config.matchMedia
|
151 |
|
152 | Allows you to replace the `matchMedia` function that Radium uses. The default is `window.matchMedia`, and the primary use case for replacing it is to use media queries on the server. You'll have to send the width and height of the page to the server somehow, and then use a [mock for match media](https://github.com/azazdeaz/match-media-mock) that implements the [`window.matchMedia` API](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia). Your code could look like this:
|
153 |
|
154 | **Server**
|
155 |
|
156 | ```jsx
|
157 | var ConfiguredRadium = require('./configured-radium');
|
158 | var matchMediaMock = require('match-media-mock').create();
|
159 | ConfiguredRadium.setMatchMedia(matchMediaMock);
|
160 |
|
161 | app.get('/app/:width/:height', function(req, res) {
|
162 | matchMediaMock.setConfig({
|
163 | type: 'screen',
|
164 | width: req.params.width,
|
165 | height: req.params.height,
|
166 | });
|
167 |
|
168 | // Your application code uses `@ConfiguredRadium` instead of `@Radium`
|
169 | var html = React.renderToString(<RadiumApp />);
|
170 |
|
171 | res.end(html);
|
172 | });
|
173 | ```
|
174 |
|
175 | **ConfiguredRadium.js**
|
176 |
|
177 | ```jsx
|
178 | var Radium = require('radium');
|
179 |
|
180 | var _matchMedia = null;
|
181 |
|
182 | function ConfiguredRadium(component) {
|
183 | return Radium({
|
184 | matchMedia: _matchMedia
|
185 | })(component);
|
186 | }
|
187 |
|
188 | ConfiguredRadium.setMatchMedia = function (matchMedia) {
|
189 | _matchMedia = matchMedia;
|
190 | };
|
191 |
|
192 | module.exports = ConfiguredRadium;
|
193 | ```
|
194 |
|
195 | **MyComponent.js**
|
196 |
|
197 | ```jsx
|
198 | var ConfiguredRadium = require('./configured-radium');
|
199 |
|
200 | @ConfiguredRadium
|
201 | class MyComponent extends React.Component { ... }
|
202 | ```
|
203 |
|
204 | See [#146](https://github.com/FormidableLabs/radium/pull/146) for more info.
|
205 |
|
206 | ### config.plugins
|
207 | **Array<Plugin>**
|
208 |
|
209 | Replaces all plugins with the provided set. See [Plugins](#plugins) for more information.
|
210 |
|
211 | Because the `plugins` config replaces all plugins, you have to provide the built-ins if you want to keep the default Radium functionality. A simple example of creating and adding a `styleLogger` plugin:
|
212 |
|
213 | ```jsx
|
214 | function styleLogger({componentName, style}) {
|
215 | console.log('Name: ' + componentName, style);
|
216 | }
|
217 |
|
218 | function ConfiguredRadium(component) {
|
219 | return Radium({
|
220 | plugins: [
|
221 | Radium.Plugins.mergeStyleArray,
|
222 | Radium.Plugins.checkProps,
|
223 | Radium.Plugins.resolveMediaQueries,
|
224 | Radium.Plugins.resolveInteractionStyles,
|
225 | Radium.Plugins.keyframes,
|
226 | Radium.Plugins.visited,
|
227 | Radium.Plugins.removeNestedStyles,
|
228 | Radium.Plugins.prefix,
|
229 | styleLogger,
|
230 | Radium.Plugins.checkProps,
|
231 | ],
|
232 | })(component);
|
233 | }
|
234 |
|
235 | // Usage
|
236 | @ConfiguredRadium
|
237 | class MyComponent extends React.Component { ... }
|
238 | ```
|
239 |
|
240 | You will typically want to put plugins before the final `checkProps` so that you can still benefit from the checks it provides. If your plugin might produce other pseudo-style blocks, like `@media` consumed by `resolveMediaQueries` or `:hover` consumed by `resolveInteractionStyles`, you would want to have your plugin run before those plugins.
|
241 |
|
242 | You can of course omit any or all of the built-in plugins, and replace them with your own version. For example, you may want to omit `Radium.Plugins.prefix` entirely if you aren't using vendor prefixes or are using a [compile-time solution](https://github.com/UXtemple/babel-plugin-react-autoprefix) instead.
|
243 |
|
244 | ### config.userAgent
|
245 | **string**
|
246 |
|
247 | Set the user agent passed to [inline-style-prefixer](https://github.com/rofrischmann/inline-style-prefixer) to perform prefixing on style objects. Mainly used during server rendering, passed in via the `radiumConfig` prop. Using express:
|
248 |
|
249 | ```jsx
|
250 | <App radiumConfig={{userAgent: req.headers['user-agent']}} />
|
251 | ```
|
252 |
|
253 | For a complete example, see [examples/server.js](https://github.com/FormidableLabs/radium/blob/master/examples/server.js).
|
254 |
|
255 | ## getState
|
256 |
|
257 | **Radium.getState(state, elementKey, value)**
|
258 |
|
259 | _Note: `getState` will not work in a stateless component, because even though Radium maintains the state internally, the stateless component does not have access to it, by definition_
|
260 |
|
261 | Query Radium's knowledge of the browser state for a given element key. This is particularly useful if you would like to set styles for one element when another element is in a particular state, e.g. show a message when a button is hovered.
|
262 |
|
263 | Note that the target element specified by `elementKey` must have the state you'd like to check defined in its style object so that Radium knows to add the handlers. It can be empty, e.g. `':hover': {}`.
|
264 |
|
265 | Parameters:
|
266 |
|
267 | - **state** - you'll usually pass `this.state`, but sometimes you may want to pass a previous state, like in `shouldComponentUpdate`, `componentWillUpdate`, and `componentDidUpdate`
|
268 | - **elementKey** - if you used multiple elements, pass the same `key=""` or `ref=""`. If you only have one element, you can leave it blank (`'main'` will be inferred)
|
269 | - **value** - one of the following: `:active`, `:focus`, and `:hover`
|
270 | - **returns** `true` or `false`
|
271 |
|
272 | Usage:
|
273 |
|
274 | ```jsx
|
275 | Radium.getState(this.state, 'button', ':hover')
|
276 | ```
|
277 |
|
278 | ## keyframes
|
279 |
|
280 | **Radium.keyframes(keyframes, [name])**
|
281 |
|
282 | Create a keyframes animation for use in an inline style. `keyframes` returns an opaque object you must assign to the `animationName` property. `Plugins.keyframes` detects the object and adds CSS to the Radium root's style sheet. Radium will automatically apply vendor prefixing to keyframe styles. In order to use `keyframes`, you must wrap your application in the [`StyleRoot component`](#styleroot-component).
|
283 |
|
284 | `keyframes` takes an optional second parameter, a `name` to prepend to the animation's name to aid in debugging.
|
285 |
|
286 | ```jsx
|
287 | @Radium
|
288 | class Spinner extends React.Component {
|
289 | render () {
|
290 | return (
|
291 | <div>
|
292 | <div style={styles.inner} />
|
293 | </div>
|
294 | );
|
295 | }
|
296 | }
|
297 |
|
298 | var pulseKeyframes = Radium.keyframes({
|
299 | '0%': {width: '10%'},
|
300 | '50%': {width: '50%'},
|
301 | '100%': {width: '10%'},
|
302 | }, 'pulse');
|
303 |
|
304 | var styles = {
|
305 | inner: {
|
306 | // Use a placeholder animation name in `animation`
|
307 | animation: 'x 3s ease 0s infinite',
|
308 | // Assign the result of `keyframes` to `animationName`
|
309 | animationName: pulseKeyframes,
|
310 | background: 'blue',
|
311 | height: '4px',
|
312 | margin: '0 auto',
|
313 | }
|
314 | };
|
315 | ```
|
316 |
|
317 | ## Plugins
|
318 |
|
319 | ### Built-in Plugins
|
320 |
|
321 | Almost everything that Radium does, except iteration, is implemented as a plugin. Radium ships with a base set of plugins, all of which can be accessed via `Radium.Plugins.pluginName`. They are called in the following order:
|
322 |
|
323 | - `mergeStyleArray` - If the `style` attribute is an array, intelligently merge each style object in the array. Deep merges nested style objects, such as `:hover`.
|
324 | - `checkProps` - Performs basic correctness checks, such as ensuring you do not mix longhand and shorthand properties.
|
325 | - `resolveMediaQueries` - Handles style entries like `'@media (...)': { ... }`, applying them only when the appropriate media query is hit. Can be configured using [config.matchMedia](#configmatchmedia).
|
326 | - `resolveInteractionStyles` - Handles `':hover'`, `':focus'`, and `':active'` styles.
|
327 | - `prefix` - Uses in-browser detection and a small mapping to add vendor prefixes to CSS properties and values.
|
328 | - `checkProps` - Same as above, just run after everything else.
|
329 |
|
330 | ### Plugin Interface
|
331 |
|
332 | All plugins are functions accept a PluginConfig, and return a PluginResult. The annotated flow types follow. A plugin is called once for every *rendered element* that has a `style` attribute, for example the `div` and `span` in `return <div style={...}><span style={...} /></div>;`.
|
333 |
|
334 | **PluginConfig**
|
335 | ```jsx
|
336 | type PluginConfig = {
|
337 | // Adds a chunk of css to the root style sheet
|
338 | addCSS: (css: string) => {remove: () => void},
|
339 |
|
340 | // Helper function when adding CSS
|
341 | appendImportantToEachValue: (style: Object) => Object;
|
342 |
|
343 | // May not be readable if code has been minified
|
344 | componentName: string,
|
345 |
|
346 | // The Radium configuration
|
347 | config: Config,
|
348 |
|
349 | // Converts an object of CSS rules to a string, for use with addCSS
|
350 | cssRuleSetToString: (
|
351 | selector: string,
|
352 | rules: Object,
|
353 | userAgent: ?string,
|
354 | ) => string,
|
355 |
|
356 | // Retrieve the value of a field on the component
|
357 | getComponentField: (key: string) => any,
|
358 |
|
359 | // Retrieve the value of a field global to the Radium module
|
360 | // Used so that tests can easily clear global state.
|
361 | getGlobalState: (key: string) => any,
|
362 |
|
363 | // Retrieve the value of some state specific to the rendered element.
|
364 | // Requires the element to have a unique key or ref or for an element key
|
365 | // to be passed in.
|
366 | getState: (stateKey: string, elementKey?: string) => any,
|
367 |
|
368 | // Helper function when adding CSS
|
369 | hash: (data: string) => string,
|
370 |
|
371 | // Returns true if the value is a nested style object
|
372 | isNestedStyle: (value: any) => bool,
|
373 |
|
374 | // Access to the mergeStyles utility
|
375 | mergeStyles: (styles: Array<Object>) => Object,
|
376 |
|
377 | // The props of the rendered element. This can be changed by each plugin,
|
378 | // and successive plugins will see the result of previous plugins.
|
379 | props: Object,
|
380 |
|
381 | // Calls setState on the component with the given key and value.
|
382 | // By default this is specific to the rendered element, but you can override
|
383 | // by passing in the `elementKey` parameter.
|
384 | setState: (stateKey: string, value: any, elementKey?: string) => void,
|
385 |
|
386 | // The style prop of the rendered element. This can be changed by each plugin,
|
387 | // and successive plugins will see the result of previous plugins. Kept
|
388 | // separate from `props` for ease of use.
|
389 | style: Object,
|
390 |
|
391 | // uses the exenv npm module
|
392 | ExecutionEnvironment: {
|
393 | canUseEventListeners: bool,
|
394 | canUseDOM: bool,
|
395 | }
|
396 | };
|
397 | ```
|
398 |
|
399 | **PluginResult**
|
400 |
|
401 | ```jsx
|
402 | type PluginResult = ?{
|
403 | // Merged into the component directly. Useful for storing things for which you
|
404 | // don't need to re-render, event subscriptions, for instance.
|
405 | componentFields?: Object,
|
406 |
|
407 | // Merged into a Radium controlled global state object. Use this instead of
|
408 | // module level state for ease of clearing state between tests.
|
409 | globalState?: Object,
|
410 |
|
411 | // Merged into the rendered element's props.
|
412 | props?: Object,
|
413 |
|
414 | // Replaces (not merged into) the rendered element's style property.
|
415 | style?: Object,
|
416 | };
|
417 | ```
|
418 |
|
419 | If your plugin consumes custom style blocks, it should merge any applicable style blocks and strip any others out of the style object before returning to avoid errors farther down. For example, a hypothetical `enumPropResolver` might know how to resolve keys of the form `'propName=value'`, such that if `this.props.propName === 'value'`, it will merge in that style object. `enumPropResolver` should then also strip any other keys that will not be merged. Thus, if this style object is passed to `enumPropResolver`, and `this.props.type === 'error'`:
|
420 |
|
421 | ```jsx
|
422 | {
|
423 | 'type=success': {color: 'blue'},
|
424 | 'type=error': {color: 'red'},
|
425 | 'type=warning': {color: 'yellow'},
|
426 | }
|
427 | ```
|
428 |
|
429 | `enumPropResolver` should then return an object with the style property equal to:
|
430 |
|
431 | ```jsx
|
432 | {
|
433 | color: 'red'
|
434 | }
|
435 | ```
|
436 |
|
437 | ## Style Component
|
438 |
|
439 | The `<Style>` component renders an HTML `<style>` tag containing a set of CSS rules. Using it, you can define an optional `scopeSelector` that all selectors in the resulting `<style>` element will include.
|
440 |
|
441 | Without the `<Style>` component, it is prohibitively difficult to write a `<style>` element in React. To write a normal `<style>` element, you need to write your CSS as a multiline string inside of the element. `<Style>` simplifies this process, and adds prefixing and the ability to scope selectors.
|
442 |
|
443 | If you include a `scopeSelector`, you can include CSS rules that should apply to that selector as well as any nested selectors. For example, the following
|
444 |
|
445 | ```
|
446 | <Style
|
447 | scopeSelector=".scoping-class"
|
448 | rules={{
|
449 | color: 'blue',
|
450 | span: {
|
451 | fontFamily: 'Lucida Console, Monaco, monospace'
|
452 | }
|
453 | }}
|
454 | />
|
455 | ```
|
456 |
|
457 | will return:
|
458 |
|
459 | ```
|
460 | <style>
|
461 | .scoping-class {
|
462 | color: 'blue';
|
463 | }
|
464 | .scoping-class span {
|
465 | font-family: 'Lucida Console, Monaco, monospace'
|
466 | }
|
467 | </style>
|
468 | ```
|
469 |
|
470 | ### Props
|
471 |
|
472 | #### rules
|
473 |
|
474 | An object of CSS rules to render. Each key of the rules object is a CSS selector and the value is an object of styles. If rules is empty, the component will render nothing.
|
475 |
|
476 | ```jsx
|
477 | var Radium = require('radium');
|
478 | var Style = Radium.Style;
|
479 |
|
480 | // or
|
481 | import Radium, { Style } from 'radium'
|
482 |
|
483 | <Style rules={{
|
484 | body: {
|
485 | margin: 0,
|
486 | fontFamily: 'Helvetica Neue, Helvetica, Arial, sans-serif'
|
487 | },
|
488 | html: {
|
489 | background: '#ccc',
|
490 | fontSize: '100%'
|
491 | },
|
492 | mediaQueries: {
|
493 | '(min-width: 550px)': {
|
494 | html: {
|
495 | fontSize: '120%'
|
496 | }
|
497 | },
|
498 | '(min-width: 1200px)': {
|
499 | html: {
|
500 | fontSize: '140%'
|
501 | }
|
502 | }
|
503 | },
|
504 | 'h1, h2, h3': {
|
505 | fontWeight: 'bold'
|
506 | }
|
507 | }} />
|
508 | ```
|
509 |
|
510 |
|
511 | #### scopeSelector
|
512 |
|
513 | A string that any included selectors in `rules` will be appended to. Use to scope styles in the component to a particular element. A good use case might be to generate a unique ID for a component to scope any styles to the particular component that owns the `<Style>` component instance.
|
514 |
|
515 | ```jsx
|
516 | <div className="TestClass">
|
517 | <Style
|
518 | scopeSelector=".TestClass"
|
519 | rules={{
|
520 | h1: {
|
521 | fontSize: '2em'
|
522 | }
|
523 | }}
|
524 | />
|
525 | </div>
|
526 | ```
|
527 |
|
528 | ### Notes
|
529 |
|
530 | Some style properties, like [`content`](https://developer.mozilla.org/en-US/docs/Web/CSS/content), allow quoted strings or keywords as values. Because all non-numerical property values are written
|
531 | as strings in Radium style objects, you must explicitly add quotes to string value for these properties: `content: "'Hello World!'"`.
|
532 |
|
533 | ## StyleRoot Component
|
534 |
|
535 | _Props: Accepts all props valid on `div` and optional `radiumConfig`_
|
536 |
|
537 | Usually wrapped around your top-level App component. StyleRoot wraps its children in a plain div followed by the root style sheet. Radium plugins, like keyframes and media queries, use this style sheet to inject CSS at runtime. Because the style sheet appears after your rendered elements, it is populated correctly during a server render.
|
538 |
|
539 | StyleRoot transfers all of its props to the rendered `div`, and is itself wrapped in Radium, so you can pass it inline styles or `radiumConfig`.
|
540 |
|
541 | ```jsx
|
542 | import {StyleRoot} from 'radium';
|
543 |
|
544 | class App extends React.Component {
|
545 | render() {
|
546 | return (
|
547 | <StyleRoot>
|
548 | ... rest of your app ...
|
549 | </StyleRoot>
|
550 | );
|
551 | }
|
552 | }
|
553 | ```
|
554 |
|
555 | **Note:** StyleRoot passes the style-keeper (the object where styles are collected) down to other Radium components via context. Because of this, you cannot use keyframes or media queries in *direct children* of the `<StyleRoot>`, e.g.
|
556 |
|
557 | ```jsx
|
558 | // COUNTEREXAMPLE, DOES NOT WORK
|
559 | <StyleRoot>
|
560 | <div style={{'@media print': {color: 'black'}}} />
|
561 | </StyleRoot>
|
562 | ```
|
563 |
|
564 | You'll have to break out that piece into a proper component:
|
565 |
|
566 | ```jsx
|
567 | class BodyText extends React.Component {
|
568 | render() {
|
569 | return <div style={{'@media print': {color: 'black'}}} />;
|
570 | }
|
571 | }
|
572 |
|
573 | class App extends React.Component {
|
574 | render() {
|
575 | return (
|
576 | <StyleRoot>
|
577 | <BodyText>...</BodyText>
|
578 | </StyleRoot>
|
579 | );
|
580 | }
|
581 | }
|
582 | ```
|
583 |
|
584 | ## TestMode
|
585 |
|
586 | Directly off the main Radium object you can access `TestMode`, used to control internal Radium state and behavior during tests. It is only available in non-production builds.
|
587 |
|
588 | - `Radium.TestMode.clearState()` - clears the global Radium state, currently only the cache of media query listeners.
|
589 | - `Radium.TestMode.enable()` - enables "test mode", which doesn’t throw or warn as much. Currently it just doesn’t throw when using addCSS without StyleRoot.
|
590 | - `Radium.TestMode.disable()` - disables "test mode"
|