Webpack + React + Loading CSS of external modules - css

Webpack/CSS Experts!
My NodeJS/React application has the following structure, where I use Webpack 3.8.1 to package & bundle files.
-app
|--actions
|--constants
|--components
|--containers
|--css
|--reducers
|--store
-common
-server
-webpack
|--rules
|--css
|--javascript
|--externals
|--paths
|--resolve
|--webpack.config.js
Everything works as expected in development and production but there is one problem that I have been struggling with: External node modules CSS. When I add a new dependency, which comes with some sort of CSS/SCSS/SASS stylesheets and for that matter makes use of className to style different components, I hit FOUC CSS where none of the external node module's CSS styles has been recognized and therefore are missing from the map that webpack 3 creates once the build process is complete.
As an example, I am using Material-UI as a dependency and so far everything works but when attempting to do the following:
<IconButton>
<FontIcon className="muidocs-icon-action-home"/>
</IconButton>
... and nothing is displayed. This is just one example and I have been facing this issue with other node modules too! So I believe there is something wrong with my webpack configurations or the way I am loading CSS modules. For your reference, I have created a gist of all webpack configuration files.
I have come across with some similar questions like this but am not sure if that will help long-run as my project is becoming bigger.
Your helps & thoughts are greatly appreciated!
[Note] As a side note, and apart from external CSS modules, when I add a new CSS declaration to main.css, I still need to have something like:
import classNames from "classnames/bind";
import styles from "../../../css/components/timeline.css";
const cx = classNames.bind(styles);
within import section and later do something like this:
<div ref="container"
className={cx("calendar-style")}/>
to have CSS styles correctly applied, which again I suspect isn't optimal. As I mentioned earlier, webpack creates a hashmap of all CSS styles inside a .css file under public, whose content look something like this:
._1zvVaiwSh1X6O03wrS2gD- {
height: 100%;
padding: 0;
margin: 0;
}
._2W0FVEAQcsYxEbCbWHcCE3 {
height: 100%;
}...
Therefore, failing to use cx as stated above, will have the same problem as explained earlier and none of my CSS styles are applied. So, a following question would be: how webpack creates these hashes and why? How to control them being generated or not?

Related

Questions about CSS bundling in Rails 7

Rails 7. New App with Bootstrap CSS, JS Bundling, and CSS Bundling. This results in structure of:
app/assets/builds,
app/images/foo.jpg,
app/assets/stylesheets/application.bootstrap.scss
and using Yarn to add Bootstrap to package.json, with ESBuild for JS build and Sass for CSS build.
It all works until I try add a simple CSS class to the application.bootstrap.scss sass file:
.bg {
background-image: url("foo.com");
}
What I really want here, is the asset in app/assets/images/foo.jpg to be referenced properly. It is a scss file, so sass. When I use the sass commands:
background-image: image-url("foo.com");
or
background-image: url(image-path("foo.com"));
nothing works in development, or production, so that my application.css build file is correct. I get errors about syntax ending in a "we found a .jpg but should be (1px 0 solid) or something like that. In other words, the sass compile is not making a valid css build.
What am I supposed to be doing here to make a simple class with an image asset be part of my delivered application.css bundle? Should I be creating a separate css file and adding that to the sprockets manifest? Seems like overkill.
At the moment the only thing that worked was just adding an inline style to my ERB layout, which is totally bogus bad, but all I could do to just move on.
As an extra question, which I know I should not ask here, I also want to reference an image I add to the Rails App, so that https://myapp.com/my-image.png is just available. I used to park this in /public/my-image.png but if I wanted to instead use /app/assets/images/my-image.jpg what would my link be? I guess it would be all fingerprinted and not accessible, but perhaps I am wrong. Is there any point to referencing an asset instead of parking it in /public or am I forced to use /public?
see
https://github.com/rails/cssbundling-rails/issues/102
did you try
.test {
background-image: url("foo.jpg");
}
https://github.com/rails/sprockets-rails#initializer-options
config.assets.resolve_assets_in_css_urls
When this option is enabled, sprockets-rails will register a CSS postprocessor to resolve assets referenced in url() function calls and replace them with the digested paths. Defaults to true.

Global styling vs local styling in VueJS

I'm building a project with .vue files which make it possible to write the CSS (SASS), JS and HTML in the same file.
I've decided to have some global components written in SASS on a assets/styles/app.scss file which will load my grid, variables and mixins.
On top of that, I want to be able to write some local SASS rules depending the component / page I'm on, seems pretty logical to want both in a project ...
Locally it looks like this:
<template>
</template>
<script>
</script>
<style lang="scss">
#import "assets/styles/app";
.my-style {
color: $my-variable;
}
</style>
It actually works, for instance I can use $my-variable in my local .vue file or any mixin I want. The problem is a VueJS project will grow and components will go together to display a page.
I noticed the global styling was loaded on each component, and the same rule is present in 5x, 10x when I open my chrome developer tool. This is still a very small project; all my styles are basically duplicated and loaded by the browser each time I add a component to the same page.
How do you avoid to load multiple times the global styles, while being able to use global SASS code in each components?
I've never worked with local mixed with global styling before, I preferred to just abstract totally the styling into a separated structure, but this is way more convenient to code with everything local in the same place.
What am I doing wrong here?
Detail: I'm on NuxtJS but I believe this issue is more related to VueJS overall.
Basically, every time you do an #import in your components it appends another copy to the main CSS file that Webpack generates.
Assuming you have the Webpack SCSS loader properly configured (which I believe you do since it compiles), you should be able to import the SCSS file once in your app.vue and the SCSS compiler will find it when it appends all other CSS.
For example, getting global fonts and mixins:
<style lang="scss">
#import url('https://fonts.googleapis.com/css?family=Lato:300,400,400i,700,900&subset=latin-ext');
#import "#/scss/mixins.scss";
</style>
Then create your CSS for each component inside the component's <style> section. Just make sure you add the lang="scss" so it all compiles.
You might also want to look into scss-resource-loader for Webpack. I think this is in the newest CLI builds, not sure about Nuxt.
in App.vue
<style lang="scss">
#import "assets/styles/common.scss";
</style>
OR
import compiled sass to css file in main.js
import './assets/styles/common.css';

How to split css and load them only when needed on React+ReactRouter web app (built by WebPack)

I have a web app built by Web app, which takes a lot of time to first loading.
After investigation- I've tried a lot of improvements, also that one of using only critical CSS on server side rendering.
but now' after SSR has finished- React generates a lot more CSS rules- which are not used at this point,
so my question is:
Is that possible to split CSS and load them on demand (i.e.: on route change)?
Any help will be appreciated.
Thanks in advance!
Try using css-modules.
/* style.css */
.className {
color: green;
}
Let's say you have the above styles file. You can use the below code to apply the styles.
import styles from "./style.css";
const ExampleComponent = () => (
<div className={styles.className}>
Hello World!
</div>
);
You should do the webpack config to make importing CSS files to work.
css-modules documentation
Dynamically imported components / Async components react loadable.
I solved the problem by using the amazing package: 'react-universal-component',
you can read about it more here and here.
You can use then the css chunks, or the way I did it-
split css files according to your components, and call them from the component itself, without using a 'wholeCss.css' file or something like this.
This way, you can be sure that no needed css was not loaded.

Angular 2 - Duplicated <style> blocks everywhere

I am using the latest Angular(4) with Angular CLI. I followed the advice found on SO for setting up global scss that is available to components.
Angular-CLI global scss vairables
My structure looks like this
/
styles.scss
/styles
variables.scss
mixins.scss
common.scss
/app
/component1
component1.scss
/component2
component2.scss
The main styles.scss file has the following code
#import './styles/variables.scss';
#import './styles/mixins.scss';
#import './styles/common.scss';
And in my components, I start each component scss file with the statement of
#import '~styles.scss';
I thought that this was the correct way to bring global variables/mixins/common into my component's scss. However, when I started to have components within components, I began to notice that Webpack was actually creating one block per component in the page, and each one of them had all of the global scss written out in them. So there would be one block for component1, with ALL of the variables,mixins,common stuff at the top, and then another block right below that one for the other component2 in the page, with all that information again.
Besides this being extremely inefficient, it means that the global styles are overwritting themselves (can see that in chrome debug) once for each time they are loaded.
Some direction would be very much appreciated.
The <style> tags are normal angular behaviour. Each components SCSS gets written into a <style> element, so there is nothing wrong with that.
The style.scss is for global styles that do not need encapsulation. It also gets written into a <style> element, if you imported it in your angular.json:
"styles": [
    "styles.css"
],
What you are getting wrong is the question you linked (which is still not accepted).
You shouldn't import your already imported styles.scss (apart from variables or mixins) into your components, because this will lead to increasing bundle sizes, as you import the code over and over (which is also the reason for the GitHub issue you mentioned).
You can use the mixins, variables and common.scss simply by including them directly in your components SCSS, just as you need them.
This is basic sass behaviour, you should never import things that result in css several times (sass files imported into component should typically only contain variables, mixins and functions). If you need your import to happen only once, add it to the default styles file.
Look here

Sass with CSS Modules & Webpack

I've been building a project for a while using Webpack, Sass, and CSS modules. Normally in my .scss files, I define a class like:
:local(.button) {
color: white;
}
and in my React components, in the render method, I require the styles:
render = () => {
const styles = require('./MyStyles.scss');
<div className={ styles.button } />
}
and all is good with the world. Everything works as expected.
Now today I was reading through the CSS Modules page and noticed that none of the selectors were encompassed by :local() like mine and furthermore that they were importing the styles like:
import styles from './MyStyles.scss';
And I thought "Wow that looks much nicer, it's easier to see where it's imported, ect. And I'd love not to use :local() and just have things local by default." So I tried that and immediately ran into several problems.
1) `import styles from './MyStyles.scss';
Because I'm using ESLint on my React files, I immediately get an error thrown that MyStyles.scss doesn't have a default export which would normally make sense but the CSS Modules page stated:
When importing the CSS Module from a JS Module, it exports an object with all mappings from local names to global names.
so I naturally expected the default export of the stylesheet to be the object they're referring too.
2) I tried import { button } from './MyStyles.scss';
This passes linting but button logs as undefined.
3) If I revert to the require method of importing my styles, anything not specified with :local is undefined.
For reference, my webpack loader (I'm also including Node-Neat and Node-Bourbon, two awesome libraries):
{ test: /.(scss|css)$/, loader: 'style!css?sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap&includePaths[]=' + encodeURIComponent(require('node-bourbon').includePaths) +
'&includePaths[]=' + encodeURIComponent(require('node-neat').includePaths[1]) + '&includePaths[]=' + path.resolve(__dirname, '..', 'src/client/') }
My questions, following all of this, are:
1) When using CSS Modules with Sass, am I confined to using either :local or :global?
2) Since I'm using webpack, does that also mean I can only require my styles?
Soon after posting, I figured out the solution. The problem, which I thought was quite confusing, was in my Webpack config. Originally my loader looked like:
loader: 'style!css?sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap
which enabled to me to 1) require my Sass and 2) wrap my styles in :local.
However, the css loader was missing the modules option so that it looked like:
loader: 'style!css?modules&sourceMap&localIdentName=[local]___[hash:base64:5]!resolve-url!sass?outputStyle=expanded&sourceMap
Now I can import my styles and I don't have to wrap them in :local (although I presume I still can if I want to).
What I found most interesting about all this is that without the modules option, one can still use CSS Modules-esque features, although somewhat limiting.
EDIT:
Something I noticed, a future warning to whomever looks at this answer, is if you're using the eslint-plugin-import to lint the imports in your javascript code, it will throw an error upon importing styles like:
import styles from './MyStyles.scss';
because of the way CSS Modules exports the resulting styles object. That does mean you'll be required to do require('./MyStyles.scss') to bypass any warnings or errors.

Resources