Using External CSS in a Web Component with Webpack

I am making a web component based app – testing my ability – and I seem to have hit a bit of a hurdle: Getting webpack to put the CSS imported in the web-component in the shadow dom.

At present it takes the CSS i import in the component and then sticks it in the overall document’s head. This obviously means that the web component cannot access it.

I feel like I am now tearing my hair out over this and it is taking up way too much time. Does anyone know the solution to my problem or it is it even possible?

My code is as follows:

Webpack common config

                test: /\.css$/,
                use: [
                    {
                        loader: 'style-loader'
                    }, 
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    }
                ]
            }

Webpack dev config:

    mode: 'development',
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html',
            filename: 'index.html'
        })
    ],
    devServer: {
        contentBase: path.join(__dirname, './dist'),
        port: 3000,
        compress: true
    }

Web Component:

const msl = require('making-stuffs-queries'),
    path = require('path');
class IphoneModal extends HTMLElement {
    constructor(){
        super();
        const shadow = this.attachShadow({ mode: 'open' }),
            test = msl.create('h1'),
            link = msl.create('link');
        link.setAttribute('rel', 'stylesheet');
        link.setAttribute('href', 'style.css');
        link.setAttribute('type', 'css');
        test.textContent = this.getAttribute('text');
        shadow.appendChild(link);
        shadow.appendChild(test);
    }
}
customElements.define('iphone-modal', IphoneModal);

The style.css file is in the web component’s root directory.

I’m still learning, but if “style.css” is in the root, should it be link.setAttribute('href', / style.css');

Thanks for the reply!

I solved it yaaayy…

It was basically a matter of telling webpack where to put the CSS when it had processed it. I think I was up late last night. I will update the question now in case anyone else gets stuck.

EDIT: I’ll post the solution here in so I can mark it as the solution.
EDIT 2: Thanks to @snigo for steering me toward a more concise and appropriate solution.

I have removed the original solution as it is not great practice. In order to solve the issue and still enable other stylesheets I followed @snigo’s instruction of prepending a specific selector to the web component’s stylesheet and then created a separate rule for that file type.

I then created an empty style element in my web component and set its innerHTML to the imported stylesheet. My web component is now as follows:

const msl = require('making-stuffs-queries');

import styles from './main.style.css';

class IphoneModal extends HTMLElement {
    constructor(){
        super();

        const shadow = this.attachShadow({ mode: 'open' }),
            stylsheet = msl.create('style'),
            h1 = msl.create('h1');

        h1.classList.add('wrapper');
        stylsheet.innerHTML = styles.toString();
        h1.textContent = this.getAttribute('text');
        shadow.appendChild(stylsheet);
        shadow.appendChild(h1);
    }
}
customElements.define('iphone-modal', IphoneModal);

And my webpack config is now as follows:

.................
            {
                test: /(?<!\.style).css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.style\.css$/,
                use: ['css-loader']
            }
        ]
    },
.................

Hope this helps someone else in the future.

Congrats! and please do post the solution… someone will thank you in 6 months :slight_smile:

Done bud! The reason I love coding is moments like this… You’re pulling your hair out like “why wont it work” then you crack it and it is a moment of pure joy :rofl:

1 Like

I never really worked with Custom Elements, so I might be wrong, but as far as I understand you might want to scope styles within each element separately using templates:

import myElementStyles from './myElement.style.css';

const myElementTemplate = document.createElement('template');
template.style = myElementStyles;
// ...append any html elements you like
// ...attach template to the shadow

So now, you can create 2 rules in your webpack config, one for /.style.css$/ and another one for /(?<!\.style).css$/ (anything that ends with .css but not preceded with .style). The first one will only have css-loader and the second both css- and style-loader.

css-loader will resolve your css but will not append it to the head of the document - that what’s style-loader does.

Oh, yours sounds much better than my solution! I will give this a try as well as mine is somewhat a hack I would guess – If there is CSS elsewhere is will end up in my component’s shadowDOM

I’m afraid in your example you’re suggesting style loader to insert every *.css file css loader will encounter into your custom element, which is not a great idea, yeah! :slight_smile:

1 Like

Hmmm can’t seem to get your solution to work properly, it just gives me the following appended to my shadow root:

<template style="">
#document-fragment
</template>

EDIT: Got it sorted and amended the solution! Thanks for your help, this is a much better way of going about it all!