vite icon indicating copy to clipboard operation
vite copied to clipboard

CSS `@layer` bundling misses selectors inside media queries

Open argyleink opened this issue 1 year ago • 2 comments

Describe the bug

@import 'layers.css' layer(demo);

where layers.css contains:

h1 {
  color: red;
}

@media (min-width: 100px) {
  h1 {
    color: green;
  }
}

incorrectly produces:

@layer demo {
  h1 {
    color: red;
  }
}

@media (min-width: 100px) {
  h1 {
    color: green;
  }
}

instead of (this is just one correct way):

@layer demo {
  h1 {
    color: red;
  }

  @media (min-width: 100px) {
    h1 {
      color: green;
    }
  }
}

This means any media queries are unlayered, giving them the top most precedence and specificity over styles in layers.

Vanilla Vite example: https://stackblitz.com/edit/vitejs-vite-vlemhc?file=layers.css,style.css Demo: https://vitejs-vite-vlemhc--5173.local.webcontainer.io/

I'd like a way to disable it for now, but don't see one.

Reproduction

https://stackblitz.com/edit/vitejs-vite-vlemhc?file=layers.css,style.css

System Info

System:
    OS: macOS 12.4
    CPU: (16) x64 Intel(R) Xeon(R) W-2140B CPU @ 3.20GHz
    Memory: 799.40 MB / 64.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 18.2.0 - /usr/local/bin/node
    npm: 8.9.0 - /usr/local/bin/npm
  Browsers:
    Chrome: 103.0.5060.134
    Chrome Canary: 106.0.5201.0
    Firefox: 102.0.1
    Firefox Developer Edition: 102.0
    Firefox Nightly: 103.0a1
    Safari: 15.5
    Safari Technology Preview: 15.4
  npmPackages:
    vite: 3.0.2 => 3.0.2

Used Package Manager

npm

Logs

No response

Validations

argyleink avatar Jul 25 '22 20:07 argyleink

I suspect this will be fixed by : https://github.com/postcss/postcss-import/pull/496 Haven't verified this but it seems to be the same issue and vite uses postcss-import.

romainmenke avatar Jul 25 '22 22:07 romainmenke

Currently (v3.0.3), both in a <style> tag or in a linked CSS file the import rule: @import url("https://unpkg.com/open-props/") layer(designsystem); results in @import url("https://unpkg.com/open-props/");

Basically importing without the layer.

dutchcelt avatar Jul 26 '22 13:07 dutchcelt

A fix was released in postcss-import as v15.0.0

https://github.com/postcss/postcss-import/blob/master/CHANGELOG.md#1500--2022-08-30

@sapphi-red Does this require an update in vite before end users can use this updated version?

romainmenke avatar Aug 31 '22 07:08 romainmenke

@romainmenke Thanks! Yes, this requires an update in Vite.

sapphi-red avatar Aug 31 '22 07:08 sapphi-red

@sapphi-red I don't know anything about Vite so maybe this doesn't make sense, but can end users easily pass plugin options to postcss-import or does Vite do all the configuration?

romainmenke avatar Aug 31 '22 11:08 romainmenke

@romainmenke Vite does all the configuration and currently it does not allow any configuration from users. Also Vite is shipped with postcss-import bundled in. So users cannot use the new version until a new release is cut.

sapphi-red avatar Aug 31 '22 11:08 sapphi-red

In that case it is important to set a new plugin option for anonymous cascade layers : nameLayer

postcss-import needs to desugar anonymous layers to uniquely named layers but it doesn't have enough information about how it is used to generate these unique names.

example from the tests : https://github.com/postcss/postcss-import/blob/master/test/layer.js#L67-L80

example from the postcss-preset-env site :

const crypto = require('crypto');
const cwd = process.cwd();

module.exports = ctx => {
	const isProd = ctx.env === 'production';

	return {
		map: !isProd,
		plugins: {
			'postcss-import': {
				nameLayer: hashLayerName,
			},
			'postcss-preset-env': {
				stage: 0,
				preserve: true,
				features: {
					'custom-properties': false,
					'custom-media-queries': {
						preserve: false,
					},
				},
			},
			'cssnano': isProd ? {
				preset: 'default',
			} : false,
		},
	};
};

function hashLayerName(index, rootFilename) {
	if (!rootFilename) {
		return `import-anon-layer-${index}`;
	}

	// A stable, deterministic and unique layer name:
	// - layer index
	// - relative rootFilename to current working directory
	return `import-anon-layer-${crypto
		.createHash('sha256')
		.update(`${index}-${rootFilename.split(cwd)[1]}`)
		.digest('hex')
		.slice(0, 12)}`;
}

romainmenke avatar Aug 31 '22 11:08 romainmenke

@romainmenke I see. Thank you very much for the explanation!

sapphi-red avatar Aug 31 '22 13:08 sapphi-red

@argyleink v3.1.0 was released today. Would be awesome if you could try this version to verify that this is now fully fixed.

romainmenke avatar Sep 05 '22 16:09 romainmenke

bug is fixed 👍🏻

argyleink avatar Sep 06 '22 19:09 argyleink