UNPKG

19 kBMarkdownView Raw
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
21var 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
85Usage with `class` and ES7 decorators:
86
87```jsx
88@Radium
89class MyComponent extends React.Component { ... }
90```
91
92Usage with `createClass`:
93
94```jsx
95var MyComponent = React.createClass({ ... });
96module.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
103You can also pass a configuration object to `@Radium`:
104
105```jsx
106@Radium({matchMedia: mockMatchMedia})
107class MyComponent extends React.Component { ... }
108
109// or with createClass
110
111var MyComponent = React.createClass({ ... });
112module.exports = Radium({matchMedia: mockMatchMedia})(MyComponent);
113```
114
115You may want to have project-wide Radium settings. Simply create a function that
116wraps Radium, and use it instead of `@Radium`:
117
118```jsx
119function ConfiguredRadium(component) {
120 return Radium(config)(component);
121}
122
123// Usage
124@ConfiguredRadium
125class MyComponent extends React.Component { ... }
126```
127
128Radium can be called any number of times with a config object, and later configs
129will be merged with and overwrite previous configs. That way, you can still
130override settings on a per-component basis:
131
132```jsx
133@ConfiguredRadium(config)
134class MySpecialComponent extends React.Component { ... }
135```
136
137Alternatively, 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
143The 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
145Possible configuration values:
146- [`matchMedia`](#configmatchmedia)
147- [`plugins`](#configplugins)
148- [`userAgent`](#configuseragent)
149
150### config.matchMedia
151
152Allows 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
157var ConfiguredRadium = require('./configured-radium');
158var matchMediaMock = require('match-media-mock').create();
159ConfiguredRadium.setMatchMedia(matchMediaMock);
160
161app.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
178var Radium = require('radium');
179
180var _matchMedia = null;
181
182function ConfiguredRadium(component) {
183 return Radium({
184 matchMedia: _matchMedia
185 })(component);
186}
187
188ConfiguredRadium.setMatchMedia = function (matchMedia) {
189 _matchMedia = matchMedia;
190};
191
192module.exports = ConfiguredRadium;
193```
194
195**MyComponent.js**
196
197```jsx
198var ConfiguredRadium = require('./configured-radium');
199
200@ConfiguredRadium
201class MyComponent extends React.Component { ... }
202```
203
204See [#146](https://github.com/FormidableLabs/radium/pull/146) for more info.
205
206### config.plugins
207**Array&lt;Plugin&gt;**
208
209Replaces all plugins with the provided set. See [Plugins](#plugins) for more information.
210
211Because 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
214function styleLogger({componentName, style}) {
215 console.log('Name: ' + componentName, style);
216}
217
218function 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
237class MyComponent extends React.Component { ... }
238```
239
240You 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
242You 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
247Set 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
253For 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
261Query 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
263Note 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
265Parameters:
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
272Usage:
273
274```jsx
275Radium.getState(this.state, 'button', ':hover')
276```
277
278## keyframes
279
280**Radium.keyframes(keyframes, [name])**
281
282Create 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
288class Spinner extends React.Component {
289 render () {
290 return (
291 <div>
292 <div style={styles.inner} />
293 </div>
294 );
295 }
296}
297
298var pulseKeyframes = Radium.keyframes({
299 '0%': {width: '10%'},
300 '50%': {width: '50%'},
301 '100%': {width: '10%'},
302}, 'pulse');
303
304var 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
321Almost 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
332All 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
336type 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
402type 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
419If 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
439The `<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
441Without 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
443If 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
457will 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
474An 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
477var Radium = require('radium');
478var Style = Radium.Style;
479
480// or
481import 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
513A 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
530Some 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
531as 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
537Usually 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
539StyleRoot 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
542import {StyleRoot} from 'radium';
543
544class 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
564You'll have to break out that piece into a proper component:
565
566```jsx
567class BodyText extends React.Component {
568 render() {
569 return <div style={{'@media print': {color: 'black'}}} />;
570 }
571}
572
573class App extends React.Component {
574 render() {
575 return (
576 <StyleRoot>
577 <BodyText>...</BodyText>
578 </StyleRoot>
579 );
580 }
581}
582```
583
584## TestMode
585
586Directly 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"