Search code examples
webpack-4css-loader

css modules query breaks css rules with latest css-loader


With css-loader

{
        test: /\.s?css$/,
        use: [
          { loader: 'style-loader' },
          { loader: 'css-loader',
            query: {
              modules: true,
              localIdentName: '[name]-[local]-[hash:base64:8]'
            }
          },
          { loader: 'sass-loader'}
        ]
      }

configured that way the css-loader seems to not find css rules under class names. The css rules listed under div.profile doesn't get applied on the screen. The css-loader ver. 1.0.0 in my code runs with Node 10.x. Switching modules: false gets the desired styling to show.

The code is posted below.

main.js:

require('babel-runtime/regenerator');
require('babel-register');                   
require('webpack-hot-middleware/client?reload=true');
require('./index.html');

index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Webpack 4</title>
  </head>
  <body>
    <div class="profile">
      <img src="./images/400.jpg" alt="">
      <h1>Hello Webpack 4</h1>
      <div id="react-root"></div>
    </div>
    <script src="/main-bundle.js"></script>
  </body>
</html>

app.js:

import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './counter';
import { AppContainer } from 'react-hot-loader';

const render = (Component) => {
  ReactDOM.render(
    <AppContainer>
      <Component />
    </AppContainer>,
    document.getElementById('react-root')
  );
};

render(Counter);

if (module.hot) {
  module.hot.accept('./counter', () => {
    render(require('./counter'));
  });
}

counter.js:

import React, { Component } from 'react';
import { hot } from 'react-hot-loader';
import { css } from 'emotion';
import styled from 'react-emotion';
import styles from './main.scss';

const Fancy = styled('h1')`
  color: ${props => props.wild ? 'hotpink' : 'gold'}
`;

const red = '#f00';

const className = css`
  color: ${red};
  font-size: 3rem;
`;

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.addCount = this.addCount.bind(this);
  }

  addCount() {
    this.setState(() => ({ count: this.state.count + 1 }));
  }

  render() {
    const isWild = this.state.count % 2 === 0;
    return (
      <div className={styles.counter}>
        <h1 onClick={this.addCount} className={className}>Count: {this.state.count}</h1>
        <Fancy wild={isWild}>react-emotion lib allows to hook styles to component names</Fancy>
      </div>
    );
  }
}

export default hot(module)(Counter);

main.scss:

body {
  background-color: #a1b2c3;
}

.profile {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100vh;

  img {
    border-radius: 50%;
    box-shadow: 0 0 20px #000;
  }

  h1 {
    font-family: 'source-code-pro', 'sans-serif';
    font-weight: 400;
  }
}

.counter {
  border: 3px solid green;
}

Solution

  • The reason was the .profile class name in index.html is outside the counter.js scope. The css modules produce class names by the localIdentName pattern but the .profile class name was hard coded in index.html before css modules in counter.js came into play.

    In counter.js

    import styles from './main.scss';
    console.log('styles:', styles);
    

    outputs

    styles: Object { profile: "main-profile-2P-yNf0J", counter: "main-counter-Pmp5YERO" }
    

    How to get the main-profile-2P-yNf0J class name to index.html remains unclear for me.