Search code examples
csswebpackwebpack-style-loader

Webpack can't fix CSS override issue and bundle <style> elements in <head>


In my app, lets say I have two JS pages A and B, and each import a different stylesheet (import '../style/<A or B.css>').
Both stylesheets have identical classnames but but different properties.

I run yarn run dev ==> dev: webpack-dev-server --inline --hot ==> webpack -p

This is what my html <head> looks like
https://i.sstatic.net/cqpBR.jpg
page A stylesheet is loaded first, then page B css style is loaded after

When I go to Page B, the css is correct
When I go to Page A, the css is mixed up and some class styles are overriden by page B.css.

My project structure is like

public/
 bundle.js
 index.html
src/
 components/
 pages/
 style/
 App.js
 index.js
 package.json
 webpack.config.js

my webpack.config.js is

const path = require('path');

var config = {
    entry: path.resolve(__dirname, 'src', 'index.js'),
    output: {
        path: path.resolve(__dirname, 'public'),
        filename: 'bundle.js'
    },
    devServer: {
        contentBase: path.resolve(__dirname, 'src'),
        publicPath: path.resolve(__dirname, 'public')
    },
    module: {
        rules: [
        {
            test: /\.js$/,
            exclude: /(node_modules|bower_components)/,
            use: [
                { loader: 'babel-loader',
                options: { presets: ['react','env'] } }
            ]
        },
        {
            test: /\.css$/,
            use: [
                { loader: "style-loader?singleton", 
                  options: 
                  { singleton: true } 
                },
                { loader: "css-loader" }
            ]
        }
        ]
    }
};
module.exports = config;

I want Webpack to merge the multiple elements and fix the css override issue
In Webpack, I have tried style-loader?singleton and { singleton: true } but it didnt work.

EDIT 1: looking into extract-text-webpack-plugin

EDIT 2:

import movieStyle from '../style/MovieDetail.css' 
... 
return (
<div id="CellDetail_right" className={ movieStyle['cell-detail-right'] }>...</div>
) 

Ok, I added options: { modules: true } and it didnt work. My classNames are hyphenated and after compiling the browser renders the components WITHOUT any style or classes. Div on browser looks like <div id="CellDetail_right">...<div>


Solution

  • One solution is to enable local scoped css to avoid styles bleeding/overrides. Update your css-loader options to include modules: true

    {
        test: /\.css$/,
        use: [
            {
                loader: "style-loader", 
                options: { singleton: true }
            },
            {
                loader: "css-loader",
                options: {
                    modules: true,
                    camelCase: 'dashes',
                    localIdentName: '[path][name]__[local]'
                }
            }
        ]
    }
    

    Then, using in your components as:

    import styles from '../style/MovieDetail.css';
    
    function MyComponent() {
        return (
            <div className={styles.container}>
                <div className={styles.cellDetailRight}>Some Content</div>
            </div>
        );
    }
    

    This ensures that despite you have more .container rules defined in other css files, this particular rule becomes to something like ._-path-to-component__container.

    Using camelCase: 'dashes' in options transform your hyphenated rules.

    dashes in class names will be camelized

    You can also review my webpack-demo project which includes configs to handle your scenario. Check the webpack configurations

    Read more on css-loader options


    enter image description here