30
Jun

React and CSS: The Solutions Majisti Way

Starting React, most tutorials or guides, or call them what you want, will skip one important part of a React application: Styles. Almost none talk about how to handle CSS once your view is divided into React components, and it does bring many problems.

  1. How do you make a component reusable? If you use that component in some other project and it relies on specific CSS classes to be properly styled, the functionality will be there sure, but without styles, that component isn’t really usable.
  2. How do you make your component easy to distribute? If you have some CSS classes that are required for a component to be used, then distributing the component becomes difficult.There are two ways to distribute a style for your component.
    1. With CSS (or a preprocessor): You need to give a CSS file for that component with the right class(es), but then how do you prevent classes from conflicting with the global CSS? Or even other components. Sure, you can use some variant of BEM (should it be called CEM?) and end up with class names like UserAccountSummary-username-label. That gets very troublesome very fast, and then you can have the problem of multiple component with the same name for different contexts, and refactoring becomes an issue, etc.
    2. With inline styles: Just put the styles in the component with inline styles. No additional file to distribute and you are sure there won’t be conflicts with other styles. This is less than ideal not only because it is slower than using CSS, it brings with it other problems. What about media queries and pseudo-selectors? How do people using your component customize its style? Overriding the component’s style with CSS will require the use of !important. Not only that, but how to you ensure some sort of consistent style when each component defines its own little styles?

For those reasons, when Solutions Majisti was experimenting with React, we were already looking for solutions to this CSS problem. The two solutions I found (and liked) were Radium and CSS Modules. I know I said I didn’t like inline styles, and I’m cheating a bit with Radium, but the way they do inline styles feels OK to me somehow. Spoiler alert though, we didn’t go with Radium. Steven really wasn’t a fan of it and CSS modules felt alright to us when we hacked to test them out.

What Are CSS Modules

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default.

This is a simple as we can get with this. They are basically a CSS file, dedicated to one piece of your view, a React component in our case. CSS Modules aren’t really anything, they’re just a way to organize and think about your CSS. They do, however, require that you use Webpack to load and “compile” your CSS Modules. It’s really not so bad though, Webpack is an amazing tool and I do recommend its use even if you’re not using CSS Modules.

Why We Prefer CSS Modules

First, a disclaimer: We have not used Radium. Like I said before, we tested CSS modules and liked it enough to just go with it.

Ultimately, it’s a pretty simple reason. We just prefer letting CSS do the styling and Javascript do the logic. The argument could be made that React components already handle the view so it might as well also style it, but to us it’s not the same at all. React is a smarter way to do what everyone was doing with jQuery a while back: a dynamic data-driven application view without having to do DOM manipulation. How that view is presented is controlled by CSS even if React decides which boxes get displayed and where.

CSS modules let us have one CSS file per React component, with the minimum number of styles required to display it and it plugs in into our global CSS which we also like. Since it’s just CSS, it’s easy to override if the application’s design requires it without having to play with the component itself and we just need to package the CSS with the component. Awesome!

CSS Modules with React

At this point, you could already use CSS modules without too much trouble.

import React from 'react';
import styles from './user-profile.css';

export default class UserProfile extends React.Component {
    render () {
        return (
            <div className={styles.container}>
                <div className={styles.name}>John Doe</div>
                <div className={styles.role}>Captain</div>
            </div>
        );
    }
}

There are just a few limits you would encounter, based on Webpack’s css-loader. The main one being you are limited in how you name your classes and as you can see, you need to use a styles object to fetch your classes. It’s far from the end of the world, but you know, it’s troublesome. You will also not get any warning for a missing class, ouch!

Enter react-css-modules

I love the open source community. This library streamlines the inclusion of CSS modules into a react component and handles a few things in a nicer way. I will shamelessly copy from the README:

  • You are not forced to use the camelCase naming convention.
  • You do not need to refer to the styles object every time you use a CSS Module.
  • There is clear distinction between global CSS and CSS Modules, e.g.: <div className='global-css' styleName='local-module'></div>
  • You are warned when styleName refers to an undefined CSS Module (errorWhenNotFound option).
  • You can enforce use of a single CSS module per ReactElement (allowMultiple option).

All in all, this is pretty sexy stuff, but there’s still one thing to solve before you’re ready to go crazy with this: how to we organize our files?

Organizing our CSS

css-modules-directories-v02Note: We’re using TypeScript (yes, after all my ranting) and SASS here. It won’t change anything in your implementation since you need to be using Webpack anyway.

Our final stack for CSS is composed of many ideas from many people. In the end, we have what we find is a really flexible structure for our styles that allows us to really quickly build a visual for our applications since we never have to stop and ask ourselves, “Where should this style go?”.

Global SASS

Even if you do have components, you still need to style the rest of your website.

A quick note on how we organize SASS files: In every folder, we have a _all.scss file that will load all of that folder’s files. This is done to simplify imports in screen.scss. Assume that this file is always present.

  • base: Base is used for, well, the base configuration of the website’s styles. We always have exactly 5 files.
    • _configuration: This is used to configure libraries we use, font-awesome, mappy-breakpoints and susy being the most common.
    • _normalize: This is very self-explanatory. We don’t really use any normalizing library either, we just add what we need for our projects, no more.
    • _palette: Color variables for our theme. This is also where we would override colors for frameworks like Bootstrap if we used them.
    • _typography: Used to define font-family and font-size on elements, we also define variables for font-family to be used by others.
    • _variables: Any variable that does not fit one of the previous files is defined here.
  • helpers: That’s where we define functions, mixins and placeholders. Each divided in its own files, of course.
    • We’re still not sure about that one, but for now it has served us well.
  • layout: There’s only one required file here, _global.scss. The rest is left to the developer to decide, but we usually end up with _header.scss, _footer.scss, and _global.scss at least.
  • libraries: Any library with an installer of sorts will get installed in that directory, Bourbon being a good example if you’re not using the npm version. There’s also a _priority-libraries.scss file in there that will specify certain libraries that need to be loaded first in screen.scss. Things like Susy and Bourbon, where we want the utility functions in all of our SASS, are good examples.
  • libraries-extensions: If you need to add to Bootstrap, that’s where you do it.
  • pages: Every page that needs to define specific styles only for that page, will have a file here.

We used to have a component directory, but React components took over that role now. With all that, this is what our screen.scss looks like:

@import "helpers/all";
@import "libraries/priority-libraries";
@import "base/all";
@import "libraries/all";
@import "libraries-extensions/all";
@import "layout/all";
@import "pages/all";

React Components and SASS

This will be a lot faster. After understanding CSS modules and seeing react-css-module, it’s pretty much just that. As you can see from the picture above, we have our component ChatBubble.tsx in it own ChatBubble directory and right next to it, ChatBubble.scss. Then, in ChatBubble.tsx we could have something like:

import * as React from 'react'
const CSSModules = require('react-css-modules') as any
const styles = require("./ChatBubble.scss") as any

@CSSModules(styles)
export default class ChatBubble extends React.Component<any, any>
{
    render():JSX.Element
    {
        return (
            <div className='bubble' styleName='chat-bubble'>
                <img styleName='profile-picture'
                {children}
                <p styleName='timestamp'></p>
            </div>
        );
    }
}

Even though we could technically remove the .scss extension from the import with Webpack, we decided against it. It’s a nice and clean indicator that this is not something we can actually use in our code, it’s just our component’s style.

Putting It All Together

With all that done, all that is left is compiling it and this is done with Webpack. Our production configuration for Webpack looks like this:

var path = require('path');
var webpack = require('webpack');
var path = require('path');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var root = __dirname + '/../../';

module.exports = {
    module: {
        loaders: [{
            test: /\.scss|\.sass|\.css$/,
            loaders: [
                'style',
                'css?-url&modules&importLoaders=1&localIdentName=[name].[local].[hash:base64:5]',
                'resolve-url',
                'sass'
            ],
            include: path.join(root, 'src/ts'),
        }, {
            test: path.resolve(root, "src/sass/screen.scss"),
            loader: ExtractTextPlugin.extract(
                "style",
                "css?-url!resolve-url!sass"
            ),
            include: path.join(root, 'src/sass'),
        },
    },
    plugins: [
        new ExtractTextPlugin('[name].css', {
            allChunks: true
        }),
    ],
    sassLoader: {
        includePaths: [
            path.resolve(root, "src/sass"),
            path.resolve(root, "src/js/components"),
        ]
    }
};

Development environment would just add source maps and ignore the ExtractTextPlugin.

It’s basically just loader configurations for css-loader and react-css-modules. We also use the ExtractTextPlugin to concatenate all our component’s CSS into a single file, and configure sass-loader to know where to look when we @import in a sass file. This post is already pretty long and I feel like it’s pretty simple. However, if you need more details, don’t hesitate to comment, and I will add to this example.

Conclusion

Phew. That looks like a lot of work, but it’s actually pretty simple and once it’s done, you have reusable React component that can be easily distributed, and a very solid CSS workflow thanks to CSS modules. Of course, I’m sure this structure will keep getting better as time goes on, but it’s served us well and we feel like it’s a very solid foundation to build upon.