What I want from a React UI framework: Intentions and skins in the presentation layer

Published: September 1, 2017

Front-end frameworks like Bootstrap and Foundation are reviled among some front-end developers for creating bloated CSS and for making every site look the same. However, much of this hatred focuses on the use of the use of the framework as a "drop-in" CSS (and JavaScript) file. Customization of SCSS variables can produce amazingly distinctive designs, and it's possible to include only those parts of the framework which are needed, drastically reducing the size of both the CSS and JS assets. To my mind, these frameworks provide two advantages: standardized responsive grid layout systems which work across browsers, and a variety of components (tooltips, dropdown menus, modals etc.) which can be included in a project without being coded from scratch.

At first glance, this "component-based" system would seem to be a good fit for React.js, but that leaves you heavily reliant on jQuery. A new set of projects has sprung up that each provide a library of components that replace the common Bootstrap and Foundation UX patterns that both users and developers are used to. These include Material UI, Semantic UI React and Elemental UI.

One major consideration in choosing between these libraries in the degree to which you will be dependent on inline styles. The argument in favour goes like this: React pushes a component-based architecture, and just as separating form and function between HTML and JavaScript no longer makes sense, it also no longer makes sense to separate content and style by using CSS. Instead, JavaScript manages function, presentation and style. The concerns that need separation are no longer between different layers of a component, but between the components themselves.

Personally, I don't share this outlook. It makes perfect sense for JavaScript to be tightly coupled to HTML in a user-interface library, because the intent of the component depends on the state of that component and its function. But how this intent translates in the styles ("skin") of the component is a different consideration. That is why a <strong> HTML tag and a font-weight CSS property are different things: strong text is the intention, but bold text is the skin that communicates that intention.

You might want to change the skin of a component without changing its function (such as the share of orange or yellow meant to communicate a warning) but you will rarely change the meaning red or orange without changing the function itself. Moreover, if you change the shade of red or orange, you'll likely want to change it across the entire the site or app. The stylesheet is therefore a reference that components make use of, not an embedded part of the components themselves.

For libraries that make heavy use of inline styles, the bottom line is that components are not really reusable, at least not in the way that I would like. I can override the custom styles by providing a custom style object or className to the component (and then write CSS with heavy use of !important declarations), but then I have to do this every time I use that component. The only way to do that in a DRY way to wrap the library component in another component which provides the customizations for a particular project, adding to the depth of nesting and complexity.

Material UI has noted other downsides to inline styles, including performance, and the lack of media queries and pseudo-elements. To be sure, it does provide a method of "customization" through theming, but the number of possible variables is pretty limited compared to Foundation or Bootstrap.

Ideally, here is how I think a framework in this space should work:

  • CSS is produced by compiling SCSS (or similar) with a set of user-provided variables, just like Bootstrap or Foundation
  • Components refer to CSS by class. The user can therefore "override" styles with their own CSS/SCSS.
  • Components with programmatic behaviour are controlled with internal state, and change their own classes or other attributes depending on state. For example, a modal adds a class when it "appears", or better yet, just change its aria-hidden attribute.
  • Components accept props from their parents to notify them of state changes. For example, a modal would accept an onClose prop which is a function that fires when the modal closes. Even better, these functions could override the custom behaviour by returning false.

There are a plethora of these libraries available, but from my research none of them work this way. Feel free to chime in in the comments if anything deserves a second look, if my philosophy is just too wrong and I should get with the inline styles!