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 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

  • Adding CSS Modules agreed and approved in #2278
  • Update readme
  • Confirm regex
  • Fix build
  • Add end2end test for .modules.css
  • Up to date with master
@ro-savage

This comment has been minimized.

Copy link
Contributor Author

ro-savage commented May 20, 2017

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

This comment has been minimized.

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>;
}

This comment has been minimized.

@Skaronator

Skaronator May 21, 2017

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

This comment has been minimized.

@ro-savage

ro-savage May 23, 2017

Author Contributor

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 = {

This comment has been minimized.

@Skaronator

Skaronator May 21, 2017

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.

This comment has been minimized.

@ro-savage

ro-savage May 23, 2017

Author Contributor

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$/,

This comment has been minimized.

@alex-pex

alex-pex May 23, 2017

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

This comment has been minimized.

@ro-savage

ro-savage May 23, 2017

Author Contributor

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

@abdulsattar abdulsattar referenced this pull request May 23, 2017

Closed

Add css modules #2341

@ro-savage ro-savage force-pushed the ro-savage:add-css-modules branch from 5516ef1 to eab529b May 23, 2017

@ro-savage

This comment has been minimized.

Copy link
Contributor Author

ro-savage commented May 23, 2017

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

This comment has been minimized.

Copy link
Contributor Author

ro-savage commented May 23, 2017

@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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Contributor Author

ro-savage commented May 23, 2017

@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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Contributor

alexeyraspopov commented May 24, 2017

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

This comment has been minimized.

Copy link
Contributor Author

ro-savage commented May 24, 2017

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

This comment has been minimized.

Copy link
Member

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 force-pushed the ro-savage:add-css-modules branch from eab529b to e59831a 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

This comment has been minimized.

Copy link
Contributor Author

ro-savage commented May 24, 2017

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

This comment has been minimized.

Copy link
Member

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);

This comment has been minimized.

@gaearon

gaearon May 24, 2017

Member

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.

This comment has been minimized.

@ro-savage

ro-savage May 24, 2017

Author Contributor

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>

This comment has been minimized.

@gaearon

gaearon May 24, 2017

Member

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

This comment has been minimized.

@ro-savage

ro-savage May 24, 2017

Author Contributor

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

This comment has been minimized.

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

This comment has been minimized.

Copy link

taion commented May 24, 2017

.mjs and .mcss, rather.

@gaearon gaearon force-pushed the facebook:next branch 3 times, most recently from 75f8e7d to 7e4657b Jan 14, 2018

@Timer Timer force-pushed the ro-savage:add-css-modules branch from 38a7a14 to 6ca7052 Jan 14, 2018

@gaearon gaearon force-pushed the facebook:next branch from 727f487 to 43bbaf0 Jan 14, 2018

@Timer Timer force-pushed the facebook:next branch from 43bbaf0 to 2e59c54 Jan 15, 2018

@ro-savage

This comment has been minimized.

Copy link
Contributor Author

ro-savage commented Jan 15, 2018

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

@Timer

This comment has been minimized.

Copy link
Collaborator

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

This comment has been minimized.

Copy link
Collaborator

andriijas commented Jan 17, 2018

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

@Timer Timer force-pushed the ro-savage:add-css-modules branch from 6ca7052 to 8d52f0d Jan 17, 2018

@Timer

This comment has been minimized.

Copy link
Collaborator

Timer commented Jan 17, 2018

Let's see how CI goes. 😄

@Timer

This comment has been minimized.

Copy link
Collaborator

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

1 of 2 checks passed

continuous-integration/travis-ci/pr The Travis CI build is in progress
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details

2.0 automation moved this from Needs Decision to Ready Jan 17, 2018

@gaearon
Copy link
Member

gaearon left a comment

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

This comment has been minimized.

@gaearon

gaearon Jan 17, 2018

Member

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

This comment has been minimized.

@gaearon

gaearon Jan 17, 2018

Member

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

@Timer

This comment has been minimized.

Copy link
Collaborator

Timer commented Jan 17, 2018

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

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

This comment has been minimized.

@klzns

klzns Jan 17, 2018

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

This comment has been minimized.

@amannn

amannn Jan 17, 2018

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.

This comment has been minimized.

@andriijas

andriijas Jan 17, 2018

Collaborator

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.

This comment has been minimized.

@sompylasar

sompylasar Jan 17, 2018

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).

This comment has been minimized.

@amannn

amannn Jan 17, 2018

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.

This comment has been minimized.

@gaearon

gaearon Jan 17, 2018

Member

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.

This comment has been minimized.

@lnhrdt

lnhrdt Feb 3, 2018

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

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Collaborator

Timer commented Jan 18, 2018

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

@flobacher flobacher referenced this pull request Jan 23, 2018

Merged

css module support #13

rubenbp referenced this pull request in biko2/biko-create-react-app-old Jan 30, 2018

Add support for CSS Modules with explicit filename (#2285)
* 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

@lnhrdt lnhrdt referenced this pull request Feb 3, 2018

Closed

localIdentName #3

@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.