webpack/css-loader

`@import` with media query produces incorrect output when the imported file is a entry point

Closed this issue ยท 27 comments

Feature Proposal

Support @import list-of-media-queries syntax. Currently the plugin ignores and strips the media query from the output, making runtime-conditional import impossible without additional plugins.

Feature Use Case

@import "./dark.css" (prefers-color-scheme: dark);

Currently the plugin outputs the content of dark.css file without any media query condition, so the CSS is applied unconditionally at runtime:

/* content of dark.css */

A better output would be:

@media (prefers-color-scheme: dark) {
  /* content of dark.css */
}

Seems I found a solution by adding postcss-loader with thepostcss-import plugin. I still wonder if this feature would be considered for inclusion in css-loader as I feel like this should be possible without postcss baggage.

Are you sure you are using the latest version? It was implement a long time ago, we supports @layer too

#1504

exports[`"import" option should work and output media: result 1`] = `
"@media (prefers-color-scheme: dark) {a {
  color: white;
}
}a {
  color: black;
}
"
`;

Anyway feel free to feedback, and check style-loader and mini-css-extract-plugin version too

This was the setup where I observed it:

https://github.com/go-gitea/gitea/blob/202803fc69d21763a06d8d0f5a4c46509c18f6f1/webpack.config.js#L138-L153

No style-loader, mini-css-extract-plugin very slightly out of date by one patch release. The import was not filtered. I guess I have to retry with a clean setup, after seeing your tests.

As you can see we have another result... I can provie history when it was implemented and can provide usage in other repos

Sure thanks, that's more than I expected from this issue. Now it's on me to debug further :)

Hm, you have vue-loader, and they are registering own style-loader (which I think doesn't support it), where do you use this styles?

Can you try to create simple CSS file and do it, like I done in tests

I really want to help, but as you can see we don't have problem inside css-loader

Just this plain CSS file, so .vue loader is not involved:

https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/theme-auto.css

Yes, I will try to make a minimal webpack repro.

Just this plain CSS file, so .vue loader is not involved:

https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/theme-auto.css

Vue plugin can register a loader on css under the hood (I don't know how it is now, but before it was), so you can think that style-loader used, but no

Okay, let me see what happens if I remove vue-loader.

Yeah, let's try, it is the first place where we potentially can have a probem

Unfortunately, no amount of plugin removal has any effect, the imported CSS is inlined without any @media wrapping.

image

So I think I will make a fresh repo next.

Yeah, I will keep open, feel free to ping me

Just fyi, the minimal repro so far does not reproduce it, so it's definitely something else in that config interfering.

https://github.com/silverwind/media-url

It correctly outputs

@media (prefers-color-scheme: dark) {
body { background: black }

}

Will check further what else in the full config could be a cause.

To be honest, I don't see anything criminal in the configuration...

Added the original CSS files to https://github.com/silverwind/media-url, now it reproduces and the @media is gone in output. It seems it has something to do with the file that is being imported already being present in another entrypoint.

$ git clone https://github.com/silverwind/media-url && cd media-url
$ npm install
$ npx webpack
$ head -5 dist/theme-auto.css
/*!***************************************************************************************************!*\
  !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[0].use[1]!./css/chroma/base.css ***!
  \***************************************************************************************************/
.chroma {
  background-color: var(--color-code-bg);

Trying to reduce this further now...

Yep, as soon as I remove the separate entry point for the file that is being imported, the @media is emitted correctly.

I see...give me time to think how I can fix it and there is a problem (I think in MiniCssExtractPlugin.loader, but need check)

https://github.com/silverwind/media-url is minimal again.

With the two entry points, it fails to emit @media in index:

    dark: fileURLToPath(new URL('dark.css', import.meta.url)),
    index: fileURLToPath(new URL('index.css', import.meta.url)),

If I remove the dark entry point, the @media in index is back again.

Yes, I was right, the problem is in the mini-css-extract-plugin, a fix is simple:

identifier() {
  return `css|${this._identifier}|${this._identifierIndex}|${this.media}|${this.supports}|${this.layer}`;
}

https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/src/index.js#L168

I am very tired today because I moved to a new country and will send a PR and will do release tomorrow, but if you want to send a PR yourself, feel free you have the fix and the test case, in this case I will do just release ๐Ÿ˜„

No hurry, we have a workaround via postcss until then. But I'll be very happy to rip it out again after this fix is in ๐Ÿ˜„.

Should work ๐Ÿ‘

Confirmed, thanks for the quick fix! ๐ŸŽ‰