Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generated CSS is always suffixed with :not(#\#) #370

Closed
vinceau opened this issue Jan 22, 2024 · 4 comments
Closed

Generated CSS is always suffixed with :not(#\#) #370

vinceau opened this issue Jan 22, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@vinceau
Copy link

vinceau commented Jan 22, 2024

Describe the issue

I'm migrating a project to use StyleX, and I'm defining styles as per usual like so:

const styles = stylex.create({
  container: {
    marginBottom: "20px",
  },
  dateInfo: {
    marginRight: "auto",
    marginLeft: "5px",
    opacity: 0.6,
    fontSize: "15px",
  },
  fixedCardHeight: {
    height: "200px",
  },
  markdownContainer: {
    color: "#ccc",
    maxWidth: "700px",
  },
});

However, when I inspect the generated CSS, every selector is appended with a trailing :not(#\#).

.x1qk1tsj:not(#\#) {
	color: #ccc
}

.x1jvydc1:not(#\#) {
	font-size: 15px
}

.x197sbye:not(#\#) {
	opacity: .6
}

.x1m3v4wt:not(#\#):not(#\#) {
	height: 200px
}

.xieb3on:not(#\#):not(#\#) {
	margin-bottom: 20px
}

.xd8780z:not(#\#):not(#\#) {
	margin-left: 5px
}

.xack27t:not(#\#):not(#\#) {
	margin-right: auto
}

.xrgej4m:not(#\#):not(#\#) {
	max-width: 700px
}

The more components that I migrate to use StyleX, the longer the :not(#\#) chain is for some selectors, with some selectors having 3 or 4. e.g.

.x1n2onr6:not(#\#):not(#\#):not(#\#) {
	position: relative
}

.x1m3v4wt:not(#\#):not(#\#):not(#\#):not(#\#) {
	height: 200px
}

I initially thought it was some form of disambiguation, but surely the randomly generated CSS selectors should be sufficient for that.

Expected behavior

I would assume the expected generated CSS to just be like:

.xrgej4m {
	max-width: 700px
}

.x1cnzs8 {
	padding-top: 20px
}

.x1dmp6jm {
	width: 80px
}

Steps to reproduce

I'm using the following packages:

{
    "@stylexjs/webpack-plugin": "^0.4.1",
    "@stylexjs/stylex": "^0.4.1"
}

but I was initially using v0.3.0 which also had this issue.

with the Webpack configuration:

    new StylexPlugin({
      dev: isDevelopment,
      unstable_moduleResolution: {
        type: "commonJS",
        rootDir: webpackPaths.rootPath,
      },
      appendTo: "style.css",
    }),

Additional comments

Why does it do this and is it by design? It makes all the CSS selector names really long and also greatly increases the size of the resulting CSS file.

@vinceau vinceau added the bug Something isn't working label Jan 22, 2024
@Daniel15
Copy link
Member

Daniel15 commented Jan 23, 2024

Looks like this was added to adjust the specificity of the CSS rule when @layer support (added in 0.4.1) is not being used: https://github.com/facebook/stylex/blame/7d9d3b61cbd5f2d649d84ca0f2531791107365f3/packages/babel-plugin/src/index.js#L398

I haven't seen this internally at Meta so I wonder if some of the logic is specific to the open-source version.

@vinceau
Copy link
Author

vinceau commented Jan 23, 2024

Is that polyfill logic supposed to create really long :not(#\#) chains like so?

.x1m3v4wt:not(#\#):not(#\#):not(#\#):not(#\#) {
	height: 200px
}

and does it actually change in behaviour when compared with just a single one:

.x1m3v4wt:not(#\#) {
	height: 200px
}

@Daniel15
Copy link
Member

does it actually change in behaviour when compared with just a single one

Each :not(#\#) increases the specificity of the rule, meaning rules that have more of them will override the rules that have fewer. Repeating the class name (e.g. .x1m3v4wt.x1m3v4wt) would do that too. I suspect stylex is doing this to ensure that the classes are consistently applied in the same order, but I'm not sure. I'll leave that up to the stylex devs to comment on - I'm just a user of stylex. 😄

@nmn
Copy link
Contributor

nmn commented Jan 23, 2024

@Daniel15 is correct with the explainations. Using :not(#\#) is a polyfill for CSS Layers. Each :not(#\#) bumps the specificity by one ID selector. Repeating classNames wouldn't work because they don't bump the specificity enough. (.foo.foo has the same specificity as .bar:hover)

You can configure the Webpack plugin with useCSSLayers: true to generate CSS layers instead. This is supported in all modern browsers, but your styles may be broken in older browsers as a result.

@nmn nmn closed this as completed Jan 23, 2024
vinceau added a commit to project-slippi/slippi-launcher that referenced this issue Jan 23, 2024
vinceau added a commit to project-slippi/slippi-launcher that referenced this issue Jan 24, 2024
* wip

* remove declaration override

* append stylex styles

* fix things

* fix bottom margin

* remove dev runtime

* revert thing

* revert

* use stylex for keyframe animations

* pass logo url via function

* update stylex version

* Fix generated CSS containing a bunch of `:not(#\#)`s.

See [this issue](facebook/stylex#370) for more info.

* add eslint plugin to validate stylex styles

* use numbers that default to px

* use numbers more
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants