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

Add support for CSS Modules with explicit filename - [name].module.css #2285

Merged
merged 5 commits into from
Jan 17, 2018

Conversation

ro-savage
Copy link
Contributor

@ro-savage ro-savage commented May 20, 2017

This adds support for CSS Modules using the explicit file naming convention [name].module.css

When using css modules, class names follow a deterministic convention rather than the standard random hash with the covention [directory]__[filename]___[classname].

Given src/components/Button/Button.module.css with a class .primary {}, the generated classname will be src-components-Button__Button-module___primary.

This is done to allow targeting off elements via classname, causes minimal overhead with gzip-enabled, and allows a developer to find component location in the devtools.

See issue #2278 for more details.

Update - 6th Oct 2017

We are currently waiting on version 2 of Create-React-App to add css module support as it is a breaking change. The current release timeframe is unknown.

You may use react-scripts-cssmodules to add cssmodules in the meantime.

This is kept up to date with react-scripts and uses similar version numbers e.g. 1.0.140 equals 1.0.14.

You can use it by running npm uninstall react-scripts and npm install react-scripts-cssmodules. Or in new projects create-react-app my-app --scripts-version react-scripts-cssmodules`

Specific questions or feedback.

  • I have moved PostCSS options into a variable but repeated the rest of the style-loader code. This meaning repeated code but in my opinion is easier to follow. However, it could be changed to use less repeated code?

  • My regex is amateur is there anyway that the incorrect file could be captured by /\.modules.css$/ or the incorrect file excluded by /[^\.modules]\.css$/

I have been using this configuration in production for a couple months with no issues.

Images of working project

React
screen shot 2017-05-20 at 3 02 19 pm

Rendered component. Using both CSS Module stylesheet, and regular stylesheet for animation
screen shot 2017-05-20 at 3 03 41 pm

Rendered HTML
screen shot 2017-05-20 at 3 02 43 pm

Applied styles
screen shot 2017-05-20 at 3 02 52 pm

Todo before merge

@ro-savage
Copy link
Contributor Author

I have spent quiet a while investigating the failing build issue and haven't been able to resolve it.

When I use this branch to create an app and import CSS there is no issues. When I run the e2e locally it works.

$ tasks/e2e-kitchensink.sh

> test-kitchensink@0.1.0 build /private/var/folders/11/rrgwrzvj5lggl1yn4rlz3qyh0000gn/T/tmp.IKl6bVyn/test-kitchensink
> react-scripts build

Creating an optimized production build...
Compiled successfully.

I have even re-forked the repo, redone each change while running e2e tests and made sure no changes broke anything.

Can someone else pull this branch and trying running e2e, see if they get an error and what might be causing it?

@abdulsattar
Copy link

abdulsattar commented May 21, 2017

I tried running it locally. It failed with with the same error as on CI.

The issue I can see here is /[^\.modules]\.css$/.test('style.css') is false. This can be written in a better way if we have negative lookbehind regex in JS. (It works in Ruby: http://rubular.com/r/cAi1KCm98q).

I tried to use a function for test:

test: input => !/\.modules.css$/.test(input),

I still got the same error, though.


```html
<div class="Button-modules__button___1o1Ru"></div>;
}
Copy link

@Skaronator Skaronator May 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This } is wrong and the line above shouldn't have a ;. Otherwise looks good. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@@ -54,6 +54,24 @@ const extractTextPluginOptions = shouldUseRelativeAssetPaths
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};

// Options for PostCSS as we reference these options twice
// Adds vendor prefixing to support IE9 and above
const postCSSLoaderOptions = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A config file might be better since we are copy pasting this on the production and on the dev version.

IMO it would be even better to merge both webpack config files and simply use variables to determine the correct environment. It would also help a lot when you're ejecting it and add/change/remove things but this is just IMHO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was copy and pasted previously as well. For now I think leaving it rather than making another config file is best and it simplifies the code base (at cost of repetition).

The issue should be addressed in #2108 when the target browsers is pulled out into the package.json config.

Thanks for the feedback. Happy to discuss further.

// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .modules.css
{
test: /\.modules.css$/,
Copy link

@alex-pex alex-pex May 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be /\.modules\.css$/ (missing a backslash) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats correct. Was working because it was just allowing [anything]css. Thanks!

@ro-savage
Copy link
Contributor Author

Thanks @abdulsattar you are quite right.

I simplified the regex but just adding an exclude. Now for the regular css loader it simply has a test for all css files /\.css$/ and then excludes anything called .modules.css \.modules\.css$

All the end to end tests are now passing, and ran against my production codebase and works great as well.

@ro-savage
Copy link
Contributor Author

@gaearon now passing all tests, tested in production environment, and reviewed by a few people.

With 18 thumbs up in the issue, and another 3 here, and no dissenting comments. How do you feel and merging / providing feedback?

@cr101
Copy link
Contributor

cr101 commented May 23, 2017

I use Bootstrap 4 SCSS files in my project. Will it be possible to use CSS Modules with SASS?

@ro-savage
Copy link
Contributor Author

@cr101 - this implementation of CSS modules will have no affect on sass files.
If you have it working currently, it will keep working. If its not currently work, it still wont work after either.

See the readme for more info.

@geelen
Copy link

geelen commented May 23, 2017

@ro-savage that's great news—I had thought that Sass support would conflict with this. Sass + CSS Modules has been my preferred set up for a while now, and I think this now represents a pretty good "blessed" version of CSS Modules +@gaearon

@cr101
Copy link
Contributor

cr101 commented May 23, 2017

Is it possible to create a custom build of Bootstrap 4 by composing variables and @importing the css modules that I need into a new bootstrap_core.scss file which would look a little like:

// Overrides Bootstrap default variables
@import "./bootstrap-variables-override";

// Core variables and mixins
@import "bootstrap/scss/custom";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/mixins";

@import "bootstrap/scss/print";

// Core CSS
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
@import "bootstrap/scss/images";
@import "bootstrap/scss/code";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/tables";
@import "bootstrap/scss/forms";
@import "bootstrap/scss/buttons";

// Utility classes
@import "bootstrap/scss/utilities";

Then is it possible to import global and local scoped css like below?

//index.js
import bootstrap from './styles/bootstrap_core.scss';

//dropdown.js
import styles from 'bootstrap/scss/dropdown.scss';

@geelen
Copy link

geelen commented May 24, 2017

I think so, but your dropdown file will need to be called bootstrap/scss/dropdown.modules.scss to be localised.

In general, bootstrap is tough to make work in a local fashion. In fact, even having it globally on the page makes things more difficult, since you have to work against their rules' specificity. Generally, if you have to use bootstrap, leave it global and keep your own components local. If you ever want to apply a bootstrap class to your own component, you can do:

.my-component {
  composes: dropdown from global;
  /* your own css here */
}

This issue probably isn't the place for this discussion though, so if you have further questions maybe open an issue at https://github.com/css-modules/css-modules/issues

@alexeyraspopov
Copy link
Contributor

I recall there was a conversation in Twitter about the naming but I can't find the link. Maybe it's better to use *.module.css (singular form)?

@ro-savage
Copy link
Contributor Author

I am not on twitter, so missed the conversation. For anyone interested its here: https://twitter.com/dan_abramov/status/865895057802571776

Happy to change to .module.css, I personally think the longer name is better than .m.css but thats really just a special preference, the .m.css saves a few bytes and keystrokes on each import.

I deliberately avoided something like .mcss because it breaks compatibility with IDEs and all the other CSS processors that work fine with .css.

@geelen & @markdalgleish have any thoughts? If CRA starts using it, you might find it becomes a defacto standard for CSS Modules.

@gaearon
Copy link
Contributor

gaearon commented May 24, 2017

Let's use .module.css.
Matches Gatsby too: gatsbyjs/gatsby#193.

@gaearon gaearon added this to the 2.0.0 milestone May 24, 2017
@ro-savage ro-savage changed the title Add support for CSS Modules with explicit filename - [name].modules.css Add support for CSS Modules with explicit filename - [name].module.css May 24, 2017
@ro-savage
Copy link
Contributor Author

Updated to now use module.css

Is this a breaking change because someone might currently import a css file called [something].module.css and expecting it to behave like regular CSS?

@gaearon
Copy link
Contributor

gaearon commented May 24, 2017

Is this a breaking change because someone might currently import a css file called [something].module.css and expecting it to behave like regular CSS?

Yes.

describe('css modules inclusion', () => {
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<CssModulesInclusion />, div);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should CSS modules be interpreted by Jest? I would think that http://facebook.github.io/jest/docs/webpack.html#mocking-css-modules is a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added in the recommend identity-obj-proxy method in the jest config. Still need to test properly.

import styles from './assets/style.module.css';

export default () => (
<p className={styles.cssModuleInclusion}>We love useless text.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have an integration test demonstrating that the class name actually gets applied. Can you please add one in the integration folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted to add e2e but got stuck at const doc = await initDOM('css-modules-inclusion');.

I must be missing something fundamental because I could not get to load the correct component or match in the switch statement

Even when I updated the regular css-inclusions import statement to bring in the CSS Module component, it was still just loading the regular css component.

There must be some other way the components are being loaded for the e2e tests outside of integration/webpack.test.js

@taion
Copy link

taion commented May 24, 2017

It feels a bit weird to see .module.css for a CSS module, compared to the future .jsm for a JS module in Node. Granted that it'd require users to tweak tooling a bit more, but .cssm seems more natural, especially once CSS Modules support gets pulled out of css-loader and into cssm-loader.

@taion
Copy link

taion commented May 24, 2017

.mjs and .mcss, rather.

@ro-savage
Copy link
Contributor Author

@Timer - Did you want me to bring it up to date? Or are you happy doing it?

@Timer
Copy link
Contributor

Timer commented Jan 15, 2018

It should merge cleanly in CLI, we just keep rebasing next -- that's why things get funky.

You can feel free if you'd like, or I'll get to it when we're ready to go with this PR.

@andriijas
Copy link
Contributor

@ro-savage looks like its happening! just wanted to thank you for your patience on bringing this feature in. 🥂

@Timer
Copy link
Contributor

Timer commented Jan 17, 2018

Let's see how CI goes. 😄

@Timer
Copy link
Contributor

Timer commented Jan 17, 2018

:shipit:

Thanks for all of your patience and continued involvement, @ro-savage!
Extended thanks to everyone who provided reviews as well!

@Timer Timer merged commit fc7c991 into facebook:next Jan 17, 2018
Copy link
Contributor

@gaearon gaearon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nits

@@ -30,6 +30,20 @@ const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

// Options for PostCSS as we reference these options twice
// Adds vendor prefixing to support IE9 and above
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment outdated (since we don't use a particular config anymore)

@@ -513,6 +514,51 @@ In development, expressing dependencies this way allows your styles to be reload

If you are concerned about using Webpack-specific semantics, you can put all your CSS right into `src/index.css`. It would still be imported from `src/index.js`, but you could always remove that import if you later migrate to a different build tool.

## Adding a CSS Modules stylesheet

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should comment this section out before landing as it would be too confusing.

@Timer
Copy link
Contributor

Timer commented Jan 17, 2018

Addressed nits in a follow-up. Thanks again, all!

minimize: true,
sourceMap: shouldUseSourceMap,
modules: true,
localIdentName: '[path]__[name]___[local]',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the production build expose the app architecture?
Maybe use this property only when shouldUseSourceMap is true

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I always go for '[name]-[local]'. It's very readable and also deterministic, therefore it works well with e2e tests and error reporting tools. The only constraint is that you shouldn't have components with identical names, which for me is something I do anyway. Maybe other people have a different opinion here though as this is can be potential error source.

In regards to @klzns comment, I'd also appreciate not exposing the file path – at least not when shouldUseSourceMap is false.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it need to be deterministic and targetable? kind of kills the whole module thing. Why not recommend people to add additional static class if it needs to be targetable? Im upp for hashes.

Copy link

@sompylasar sompylasar Jan 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it need to be deterministic and targetable? kind of kills the whole module thing. Why not recommend people to add additional static class if it needs to be targetable? Im upp for hashes.

Web marketing people often freak out if they cannot attach the out-of-the-box tools like HeapAnalytics or MouseFlow to specific elements of a website/webapp by id/classname (or if the classnames they attach to are changing with each release).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's another example. Using a hash is be fine by me, and the mentioned use cases can still be addressed with static classes or e.g. data attributes. It's just a personal preference I thought I'll mention in case somebody else finds this interesting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only constraint is that you shouldn't have components with identical names, which for me is something I do anyway.

This sounds like a very artificial constraint. I expect that people will trip over this if it's not enforced.

Copy link

@lnhrdt lnhrdt Feb 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not recommend people to add additional static class if it needs to be targetable?

Great point @andriijas. I agree that we should recommend people decouple styling classes from identifiers for testing or the tools like the ones @sompylasar mentioned. The fact that we used styling classes for so many years as hooks for these tools wasn't because they were actually related concerns, it was just all we had.

Make it easy to do the right thing / hard to do the wrong thing.

@gaearon
Copy link
Contributor

gaearon commented Jan 17, 2018

For any further discussion please create a new issue with your concerns. It is getting hard to follow new threads in this PR.

@Timer
Copy link
Contributor

Timer commented Jan 18, 2018

Support for CSS Modules has been released in the first v2 alpha version. See #3815 for more details!

rubenbp referenced this pull request in biko2/biko-create-react-app-old Jan 30, 2018
* Add css modules with [name].modules.css file convention

* Add e2e for CSS Modules

* Updated based on feedback

* Change css modules class name to be deterministic and fix licences

* Update css modules class naming convention
@facebook facebook locked as resolved and limited conversation to collaborators Feb 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.