Inline Styles are the Future


Radium is a set of tools to manage inline styles on React elements.

It gives you powerful styling capabilities without CSS, and was built over about 2 months at Formidable Labs in response to our excitement about a talk by Christopher Chedeau (and, more broadly, the mindshifting way that Facebook is building production web applications right now).

In this post, we'll cover the implications of the new technology, and some of the first questions the community asked.

Modularization

var styleGlobals = require("styleGlobals");

var styles = {
   color: styleGlobals.fooBlue,
   backgroundColor: styleGlobals.barRed,
}

What's it mean? No more Cascade and no more Style Sheets: all of the style properties that would have previously been defined globally in CSS are scoped to JS objects. "Globals" such as fonts and colors can be defined explicitly and changed programmatically, and are available only where they are required. If you've used @import to modularize Sass or LESS, or simply included <link> tags in the wrong order, you know that source order matters.

CommonJS modules handle the dependency tree under the hood – just modularize your styles as you would your JS and require them in the JS modules that need them. And, since they're modules, there are no more CSS global variables, overrides or inheritance. It isn't actually all that different from CSS. In CSS, you use class="foo" to reference class .foo in a stylesheet. Here, we're using style.foo to reference a modularized (or not, if only one component consumes it) JavaScript object, but we gain the full expressive power of the language between the definition of the style and the reference to it on the element.

Computation & State:

See the Pen Radium State by David Davidson (@david-davidson) on CodePen. What's it mean? You can now use JS – either inline or required from an external module – to compute a style based on the state of your application (or anything you like!). Since the inline style is subject to diffing in React, this is extremely performant. Without Radium & React, you have to use JS to touch the DOM and/or the stylesheet. Looking at a stylesheet, it's a small research project to figure out when styles might be manipulated somewhere in the JS.

Explicit Modifiers

See the Pen Radium Modifiers by David Davidson (@david-davidson) on CodePen. What's it mean? .btn-default .btn-primary .btn-warning are no longer necessary, since Radium allows you to define modifiers explicitly as React props for child components.

Support for pseudo elements and browser states

See the Pen Radium Browser States by David Davidson (@david-davidson) on CodePen.

Support for media queries

Yes! See: https://github.com/FormidableLabs/radium/blob/master/docs/guides/media-queries.md

Progressive Enhancement

Logic!

display: Modernizr.flexbox ? flex : block

Even more logic!

var someFlexboxStyles = require("../styles/someFlexboxStyles");
var someOtherStyles = require("../styles/someOtherStyles");

var styles = Modernizr.flexbox ? someFlexboxStyles : someOtherStyles;

What's it mean? It's now possible to make cross browser & cross device style logic as complex as you need to and keep it clean. As demonstrated in the second example, you can inject different styles at any point, based on any condition, in the same way you could do:

var alertStyles = this.state.isValid ? coolBlueAlert : brightRedAlert;

Dead code elimination & Minification

If your build system includes real dead code elimination (like webpack), unused Radium styles (for unused components, for instance) won't be included, which is a definite benefit over standard CSS, where it's extremely difficult to remove styles after they've been added.

Large teams / maintainability

This will be expanded on in another blog post on React in general, the benefits of not having thousands of lines of CSS are myriad. For starters, it's more obvious what each view (in this case component) consumes. In BackboneJS, for instance, you would have to check all of the classes in the template, plus scan the document for any classes inserted with JS, then go check the rules in the CSS files. Conversely, another developer may unwittingly change the contents of a class without realizing that your view is downstream of it.

Questions asked by the community

How would you style child elements?

Suppose all <li>'s that are children of a given <div> Scenario 1: You do have access to the <li>'s you need to style (note ES6 fat arrow function, helpful for access to this.props inside of callbacks):

render: function () {
var parentStyles = {...};
var listItemStyles = {...};
var builtListStyles = this.buildStyles(listItemStyles);

var listNodes = _.each(this.collection, (item) => {
  return <li style={builtListStyles}> {item.title} </li>
})

return (
<div this.buildStyles(parentStyles)>
 <ul>
  {listNodes}
 </ul>
</div>
)
}

Scenario 2: You do not have access to the elements you need to style. In that case, use Radium's <Style/> component. The API is still being finalized but should look something like this once it's released:

render: function () {
var styleContent = {
  h1: {
    fontSize: 20
  }
};

return (
  <Style scope={true} content={styleContent} />
);
}

Can I get a converter for my old CSS projects?

See: https://github.com/FormidableLabs/css-to-radium

What about a browser prefixing solution?

See: https://github.com/FormidableLabs/radium/issues/11

How does Radium handle CSS animations?

You can't define CSS keyframe animations in inline styles, so you can't define them in Radium JS objects. You can, however, reference a CSS animation you defined in a stylesheet (and soon a CSS animation you define in a <Style/> tag) in an inline style:

<Style scope={true} content={someKeyframeAnimationThatDefinesMyAnimationFoo} />
{
 animation: "my-animation-foo 0.3s ease forwards"
}

A note on <Style/>

Inserting lots of <style> ... </style> tags throughout the DOM to define CSS is not performant, so use it judiciously. The <Style/> component will be released soon, but isn't available quite yet. See the above examples for functionality.

How does Radium handle server side rendering?

Two Radium features don't work completely with server side rendering yet:

  1. Media queries in their present state can't be evaluated until the app is hydrated with JS, meaning that the server-side render will not include proper media query styles.
  2. Browser states aren't functional until the app is hydrated with JS, meaning that elements will not have hover, focus, or active styles until JS is loaded.

We're working hard to quickly resolve these two issues – they are fundamentally unacceptable to us.

Perf?

We'll write a post dedicated to perf sometime soon. Stay tuned!

The short story: Radium is highly performant because React diffs the inline styles against the previous inline styles and updates only what has changed. There is also a small conversation starting on the repo: https://github.com/FormidableLabs/radium/issues/58.

In Conclusion

Happy hacking from @formidablelabs!


We Are Formidable

Formidable is a Seattle-based consultancy and open source shop, with an emphasis on Node.js and React.js. We deploy a mixture of consulting, staff augmentation, and training to level up teams and solve engineering problems. Whether it’s transitioning walmart.com to React, moving speedtest.net off Flash, or helping a startup build and scale an MVP, we’re ready to help teams of any size.

Interested in hiring or working for us? Get in touch or view our careers page.