Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Allow importing CSS modules from other NPM packages #13282

Closed
StarpTech opened this issue May 23, 2020 · 14 comments
Closed

Allow importing CSS modules from other NPM packages #13282

StarpTech opened this issue May 23, 2020 · 14 comments

Comments

@StarpTech
Copy link
Contributor

StarpTech commented May 23, 2020

Feature request

Allow importing CSS modules from other NPM packages.

Is your feature request related to a problem? Please describe.

An NPM package is the primitive of code sharing. Therefore monorepo is very common to split the domain into separate NPM packages. It feels very wrong when we can't import React components.

It should be possible to import CSS files from packages that follow the CSS modules convention *.module.(css|scss) because Next.js ensures already that CSS Modules are side-effect free.

The current solution forces me:

  • Handle my private packages as "global-CSS"
  • Pre-compile all my components to not have a CSS import statement.

Current error:

error - ./node_modules/react-modern-calendar/dist/esm/components/Calendar/index.module.css
CSS Modules cannot be imported from within node_modules.
Read more: https://err.sh/next.js/css-modules-npm

Additional context

Next.js: 9.4.2

@arnaudriegert
Copy link

Have you tried next-transpile-modules?

@StarpTech
Copy link
Contributor Author

Hi @arnaudpeich thanks for the workaround. With this issue I'd like to request first-class support in Next.js. next-transpile-modules is a great example that the decision to block any css,scss isn't the right way.

@mattcarlotta
Copy link
Contributor

mattcarlotta commented May 24, 2020

The three biggest problems I see are:

  • The .modules.css is not an enforced naming pattern -- while it is in use by the create-react-app, the CRA is an opinionated boilerplate, not an enforced standard. So if the third-party library utilizes a different naming convention for modules -- like treating example.css as a local stylesheet instead of a global stylesheet -- then node_modules CSS modules still won't work in a Next project.
  • Most libraries use global CSS to easily manipulate a component's appearance when it's nested within another component -- for example, importing an optional wrapping parent component that contains CSS to change any of its children components based upon a user's screen size. If the library were to use CSS modules, then it makes styling nested elements very difficult due to the dynamic module naming convention (the localIdentName will change for each compilation).
  • There's the assumption that because a component uses CSS modules, then it means it's sideEffect free, which won't always be the case.

So, allowing CSS modules within node_modules will really only cover one very ridge scenario for a third party library:

  • It uses the .module.css naming convention
  • It doesn't utilize/contain components that have nested CSS selectors
  • It doesn't have any internal sideEffects
  • It expects the components won't be restyled by a user-defined stylesheet (otherwise, you may run into the CSS import order problem)

@StarpTech
Copy link
Contributor Author

StarpTech commented May 25, 2020

The .modules.css is not an enforced naming pattern -- while it is in use by the create-react-app, the CRA is an opinionated boilerplate, not an enforced standard. So if the third-party library utilizes a different naming convention for modules -- like treating example.css as a local stylesheet instead of a global stylesheet -- then node_modules CSS modules still won't work in a Next project.

I don't see it as a problem. That convention is one way to communicate that the styles are coupled to the local component. It's good to have one way.

So if the third-party library utilizes a different naming convention for modules -- like treating example.css as a local stylesheet instead of a global stylesheet -- then node_modules CSS modules still won't work in a Next project.

This could be a use case for a further issue. For now, it's great to include your own UI components in the build pipeline.

Most libraries use global CSS to easily manipulate a component that may be nested within another component -- for example, importing an optional wrapping parent component that contains CSS to change any of its children components based upon a user's screen size. If the library were to use CSS modules, then it makes styling nested elements very difficult due to the dynamic module naming convention (the localIdentName will change for each compilation).

This is not related to this issue. You can still use global CSS in _app.js

There's the assumption that because a component uses CSS modules, then it means it's sideEffect free, which won't always be the case.

The reference is not correct. We are talking about CSS-Modules not Javascript. In pure CSS-Modules and "pure" is the magic keyword here. Pure CSS-Modules can't produce side-effects like :global(). They are already enforced in Next.js

@mattcarlotta
Copy link
Contributor

mattcarlotta commented May 25, 2020

Playing devils advocate here (I have a custom Webpack config to import from node_modules, but it's only within _app.js, so I understand where you're coming from) ...

I don't see it as a problem. That convention is one way to communicate that the styles are coupled to the local component. It's good to have one way.

Javascript has many frameworks (Vue, Angular, Nuxt, Nest, etc... or just plain Javascript) , therefore encouraging any UI library to conform to a naming convention established by a React boilerplate seems a bit of an overreach. Even if the UI library is React-based, the naming convention hasn't ever been enforced and would require adoption across-the-board; again a bit of an overreach.

This could be a use case for a further issue. For now, it's great to include your own UI components in the build pipeline.

I understand that building your own UI components is an option, but I thought we're primarily focusing on node_modules UI components that import their own CSS modules, where next would have to handle the component-level css imports.

This is not related to this issue. You can still use global CSS in _app.js

The issue is related in that it's not very easy/practical to rewrite an entire UI library for CSS modules due to a Next limitation. Therefore asking a library that utilizes global CSS to switch to local CSS to avoid this limitation may not be possible nor practical (CSS modules are locally scoped by design and it makes CSS sharing/nested CSS selectors implausible).

The reference is not correct. We are talking about CSS-Modules not Javascript. In pure CSS-Modules and "pure" is the magic keyword here. Pure CSS-Modules can't produce side-effects like :global(). They are already enforced in Next.js.

Oops! My apologies for assuming CSS sideEffects impacted Webpack's JS code-splitting. That said, there's still a glaring issue that because these stylesheets are very restrictive (pure) that it becomes very difficult to restyle them within a project because of the local naming convention and their scoping. If a developer were to try to utilize their own CSS stylesheet to override the libraries' stylesheets, then I believe this results in the import order issue reappearing.

That said, I'm on board for allowing global node_module CSS imports within an _app.js. That way, you don't have to copy/paste a UI library's CSS manually, and maintaining the import order is very easy and straight-forward. Unfortunately, the downside would be that there will still be quite a bit of dead-code from importing the entire bundled CSS.

@StarpTech
Copy link
Contributor Author

StarpTech commented May 25, 2020

Javascript has many frameworks (Vue, Angular, Nuxt, Nest, etc... or just plain Javascript) , therefore encouraging any UI library to conform to a naming convention established by a React boilerplate seems a bit of an overreach. Even if the UI library is React-based, the naming convention hasn't ever been enforced and would require adoption across-the-board; again a bit of an overreach.

This is not the ECMAScript repository 😄 I'm looking for a pragmatic solution. CSS Modules have been adopted across the ecosystem. Parcel, Gatsby, Next.js, Webpack, React just to name a few.

That said, there's still a glaring issue that because these stylesheets are very restrictive (pure) that it becomes very difficult to restyle them within a project because of the local naming convention and their scoping.

Not necessarily, it depends on the localIdent pattern. You can create ID's without a hash and use the folder name or component name as id. In that way CSS-Modules can be still overridden.

That said, I'm on board for allowing global node_module CSS imports within an _app.js.

I think you mean to allow CSS-Modules from node_modules ? My proposal is to allow them to bundle in the Next.js pipeline. I don't want to pre-build all my UI components when I build a UI framework.

@mattcarlotta
Copy link
Contributor

mattcarlotta commented May 25, 2020

think you mean to allow CSS-Modules from node_modules ? My proposal is to allow them to bundle in the Next.js pipeline. I don't want to pre-build all my UI components when I build a UI framework.

I incorrectly thought node_modules CSS imports were being excluded from being imported in an _app.js and were excluded from being imported within a SCSS file outside of _app.js, but still being imported into _app.js. Upon closer inspection, they don't appear to be excluded.

This is not the ECMAScript repository smile I'm looking for a pragmatic solution. CSS Modules have been adopted across the ecosystem. Parcel, Gatsby, Next.js, Webpack, React just to name a few.

Again, the .module.css and a static localIdent naming conventions are not standardized regardless of the eco-system. Therefore, expecting any UI libraries to adopt both to mitigate a NextJS limitation is a bit narrow-focused. While I understand your intention, the amount of work required to adhere to one framework's CSS import quirk seems a bit overreaching. If it works for 99% of the frameworks/bundlers out there, why refactor the entire library for the 1%? Also, what about the libraries that use LESS over CSS/SASS? Should they also rewrite/refactor to include CSS modules that work with Next?

My point is that since most UI libraries don't use CSS modules (because of the reasons mentioned above), this feature has very limited usage (as of now).

I think a better feature request would be for Next to handle CSS imports like the rest, which I'm aware of is an easier said than done task (or implausible); but significantly better than expecting all libraries to adhere to NextJS's own limitations.

In short, don't add a work-around, fix the import order problem! 😉

@StarpTech
Copy link
Contributor Author

StarpTech commented May 25, 2020

Again, the .module.css convention has been already established in Next.js and this feature is about to extend it. CSS Modules were introduced because they can provide a kind of isolation. We can archive the same isolation in other NPM packages because they use CSS-Modules. The order problem can't be fixed because the order can't be guaranteed. That is the whole point why CSS Modules were introduced. Please open a different issue if you strive for something bigger.

@mattcarlotta
Copy link
Contributor

The CSS module naming convention may be established by Next's Webpack configuration, but it's likely not in use by the majority of 3rd party libraries. CSS modules are a work-around to a problem, but a very opinionated and ridged one at best. If a library doesn't adapt to this ridged convention, then it still won't work in a Next project. Therefore, the usage of this feature is very limited in focus, functionality, and compatibility.

@Timer Timer added type: next and removed css labels Jun 3, 2020
@arshaw
Copy link

arshaw commented Jul 30, 2020

I would very much like Next to support css modules for third-party libs, but I'll hold off on expressing why. I just want to comment on the .module.css naming issue for now.

This sort of reminds me of the .js versus .mjs debate... how a file extension for essentially the same format changes the way in which others interpret it. .mjs was at least championed by the Node.js team, but as for interpreting a CSS file as either a local module or global, I can't imagine any organization declaring a standard. It's more a responsibility of the bundlers, and if any bundler was going to create a defacto standard for this, it would be Webpack...

...well it turns out Webpack already seems to have settled upon the .module.css extension. Take a look at the docs for the css-loader: https://webpack.js.org/loaders/css-loader/#auto . If you configure modules:true the modules{auto:true} is implied, which results in any filenames matching /\.module\.\w+$/i being interpreted as a local CSS modules.

I would kindly ask the Next team to consider going along with this standard! It would make my life as a third-party UI-based CSS-enabled package maintainer much easier!

@arshaw
Copy link

arshaw commented Jul 30, 2020

The postcss plugin for rollup does the same: https://www.npmjs.com/package/rollup-plugin-postcss#automodules

@apdrsn
Copy link

apdrsn commented Oct 6, 2020

Hi :)
I got a ui library in a mono repo along with a next app consuming components from the ui-lib.
Each component import their own import styles from "component.module.scss";
When using a component in the next app I will get the styles for all the components printed in <style> tags.
However when building the app I would expect next to add them to the a static css-file. Is there something I need to do?

Let me know if this is unrelated to this issue and should open a new one.

@lauriskuznecovs
Copy link

I really appreciate that many things are new bundle in Next.js by default, but there are some cases which really blocks development.

Referring to this documentation, it should work if we import css from our component, instead of dependency importing it inside node_modules

Example:

  • My next app > Component A > Dependency A (react-bootstrap) - WORKS
// _app.js
import 'bootstrap/dist/css/bootstrap.css'
// Component A
import Button from 'react-bootstrap/Button';
  • My next app > Component A > Dependency A > Dependency B (react-bootstrap) - DOES NOT WORK
// _app.js
import 'Dependency-A/styles.css'
// Component A
import Button from 'my-custom-ui-lib/Button';
// Dependency A
import Button from 'react-bootstrap/Button';
import 'bootstrap/dist/css/bootstrap.css'
// My custom logic which makes some new component out of Bootstrap Buttons

@danoc
Copy link

danoc commented Aug 16, 2021

Here's a related RFC: #27953

In particular:

Note: We'll likely not add support for .module.css as we did not find packages using that. Please provide some examples and we'll have a look if it makes sense.

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

9 participants