Modern CSS Techniques | Entering the 21-st Century by Learning React

11 Aug

Last time we learned what State Lifting is in a React application. Also we dabbled around with some styles, but not a lot. Today I’ll mess up with the styles a bit more. I want to make them easier to maintain in the long run. I’ve heard there’s this technique in React, called CSS Modules and want to try it out. If it’s not too hard perhaps I can dig in a bit more. Let’s begin!

How are the styles working right now?

Currently all the styles are in two separate files, the App.css and the LoginForm.css. These files are in the main directory, which isn’t really scalable and they are simply imported in the App.js file, which can go out of hand at some point (probably really fast). In these files we have the standard styles of a React application, which are generated from the beginning (in App.css) and the custom styles for the login form in the LoginForm.css, which I created in the previous article: Entering the 21-st Century by Learning React: Closing the Popup.

Let’s start with the work then.

Switching to CSS Modules

Now, currently what I do is just import the CSS file into the App.js and use its name in the “className” property of the JSX element. This works fine. I think there is some magic, which makes the Javascript not break, when importing a CSS file into it, but this is not what I am concerned with. What I think is going to be a problem is overwriting styles, because different files define the same class.

I’ve read on the Internet, that if I import the CSS file using the “import … from …” syntax, I can actually distinguish between classes defined in different files. Let’s see if this would work.

Last time I defined the .LoginPopup class in the LoginForm.css file. Then I used it in the JSX root element of the LoginForm:

render() {
  return (
    <div className="LoginPopup"> // HERE!
      <button 
        className="ClosePopup"
        type="close"
        name="CloseLogin"
        onClick={this.props.onClose}
      >
        <img alt="closeButton" href="#"/>
      </button>
      <div className="LoginForm"> 
        <input className="LoginInput" type="text" name="username" id="username" placeholder="Username" onChange={this.change.bind(this)} />
        <input className="LoginInput" type="password" name="password"  id="password" placeholder="Password" onChange={this.change.bind(this)} />
        <button
          className="LoginButton"
          type="submit"
          name="login"
          onClick={this.onSubmit.bind(this)}>
            Log In
        </button>
      </div>
    </div>
  );
}

I also import the CSS file like that:

import './LoginForm.css';

What would happen, if I import it like this:

import formStyles from './LoginForm.css';

and change the className value too:

<div className={formStyles.LoginPopup}> // HERE!

It seems I am missing something. The background of the popup is back to the default of the application. The style doesn’t load. I need to debug a bit.

Huh? The HTML element doesn’t have a class at all. It just doesn’t exist. There’s only the <div> there. What’s up with the formStyles variable? It’s not defined! I guess the things I changed are not enough. I’ll browse the web for a bit.

Side note: I saw a thing, which makes me test what happens, if I import the CSS into a variable, but I only use the name of the styles, instead of referencing it from the variable, it actually still works. I don’t think this is the Modules working though.

The problem

Here we go! I’ve read about the CSS Modules technique. It seems this is something related to webpack, not really React. So it’s not native behaviour for a React app. It also looks like it needs to be set up, to start working. To be honest I didn’t really think we are already using webpack, but I guess it comes included in the package, when you start with React. Anyhow, as we could see, the formStyles variable is not defined. I guess what I need to fix is to add a webpack module, which would translate the css into a javascript variable.

Ok, then, let’s set it up! First I need to install the mini-css-extract-plugin:

>npm install --save-dev mini-css-extract-plugin

Then I need to set up the plugin in the webpack.config.js. This starts to get hard very quickly! I’m still not panicking though. Let’s open that file and add the smallest amount of configuration as possible. Wait, I still don’t have such a file! Perfect! That makes it much easier! I’ll create one with the config I found on the internet:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
      ignoreOrder: false,
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
              hmr: process.env.NODE_ENV === 'development',
            },
          },
          'css-loader',
        ],
      },
    ],
  },
};

Now let’s run the server again. Perfect! It works! We have CSS modules, people!

Multi-browser support

You know what’s the worst nightmare of a front-end developer? Browsers! Every single of them has a very opinionated philosophy how something should work. I tried out the CSS Modules on a Firefox, instead the usual Chromium and I noticed, that the LoginPopup class isn’t set to the div! I’m going to update the browsers and see if this helps. Now Chromium is also messing it up!

Alright, I think I’ll change the source I’m using to set this up. The new source doesn’t put that much config into the webpack.config.js, but actually uses an npm command:

>npm run eject

I’ll run it and see what happens. Hey, it tells me I don’t neet to run this command to make CSS Modules work. It spits out a link: https://reactjs.org/blog/2018/10/01/create-react-app-v2.html, where a blog post says everything should work out of the box. Didn’t I try that? I’ll check again. Oh, and I’ll remove the webpack.config.js file for now.

The modern setup

Well, I checked again if the CSS modules work, but the situation is still dire. But I read that maybe my file should be with name matching the “.*.module.css” pattern. I’ll try that. Does it help? Maybe, but the server is complaining there is no file with the name LoginForm.css. I guess I need to restart it. And now it cannot find the ‘./LoginForm.css’ module. I think we have a breakthrough, people!

Did I do stupid things in the App.js file? It seems so! Look at this import:

import formStyles from './LoginForm.css'

Let’s fix the name of the file over there: ‘./LoginForm.module.css’. There you go!

What we have

Finally we have a React application, which can use CSS Modules. This means that all CSS classes are now local by default and would not overwrite styles defined in other CSS files. Isn’t this awesome! Of course, most of our classes are following the age-old ways of global styles, which shows up really easily when you change the wrong thing in the code. So there’s a lot to clean up! I’ll leave it there until I learn React properly. Don’t do that at work!

What’s next?

I think, next time I need to apply this “State Lifting” technique again and fix the behaviour of the submit button. Right now, once you click it, you cannot open the form again! Outrageous!

Anyhow, until then:

Happy Coding!

What are you still doing here?

Well, if you are so interested in this article, I guess you can look at the previous parts of this series. It starts here: Entering the 21-st century: Learning React.

Or maybe, in case you’re from the future, look at the next chapter: State Management and Bugfixing – Learning React.