Sass with CSS Modules & Webpack - css

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.

Related

How to import part of an scss file

For example I am trying to import .navbar-nav from bootstrap's _navbar.scss and not the whole _navbar.scss file to my compiled css file. Is there a way to do it?
Sorry if this was asked before.
You can try doing an extend:
.your-class{
#extend .navbar-nav;
}
However, this would only work if you had imported the _navbar.scss somewhere else or the bootstrap.scss.
Additional
// main.scss
#import ../wherever bootstrap file is/_navbar.scss;
#import _custom.scss;
// _custom.scss
.your-class{
#extend .navbar-nav;
}
One of the way to import .scss in javascript is
import { navbar-nav } from '_navbar.scss'
When using in your component you can do.
<div className={navbar-nav} />
if you want to import it in your .scss file then you can do.
#import '_navbar.scss'
.class {
#extend .navbar-nav
}
As you are learning Sass here are some explanations which may help:
Better wording helps ...
At first some wording to get a correct understandable communication here and anywhere else you are talking about coding:
SASS don't minify a given CSS, it writes the CSS. Minify means the process that a given CSS code is compressed by a postprocessor to a shorter way to write it, - i.e. comments and spaces will be removed ... But yes: as SASS writes CSS it is able to write code in a minified format.
What you mean is to 'reduce code' or 'avoid not needed code' as you only try to import, use and write! the only needed parts of a given module which is a good practice.
.navbar is a CSS class. SASS don't load CSS classes, it writes CSS classes. It doesn't matter if you 'write the code on your own to a SCSS file' or 'get the code from a framework/module' ... SASS writes the however prepared CSS classes to your CSS file.
What you mean is the SASS includes/imports files with code from a framework/module to write that code/classes to css. So yes: maybe you can say you 'load' that module/scss-file ... but you don't load as css class. (This is as important as 'classes' in coding allways means a special construct of excutable code which does something in your programm. CSS classes don't execute anything, in SASS they are content you want to write/output to css.)
Please: these wordings are important to understand each other and to understand the mechanic of the process how SASS works is going on as well.
Reducing code by importing only selected file is good practice
So, I am not sure if I did understand your question right:
No. You are not able to include/import/load a part of the code of a single scss-file only. If you do #import 'somefile.scss' you always get the whole code of the whole file.
Yes. you are able to include/import/load parts of a given framework/module as you are able to load only the special FILES(!) of a framework/module you need for your project.
Yes. That is a really good practice.
As you mentioned Bootstrap indeed is developed and allows you to do that. But head up. If you import i.e. the part navbar.scss (or other selected elements) it only works if you also load the other files navbar.scss depends on. That are almost variables, functions, mixins and sometimes needed JS components to this element as well. Please note, that importing the files the elements are based on (i.e. vars, functions, mixins) has to be done BEFORE you load the element (i.e. like navbars, grid,...) itself.
A way to organize your project
Yes. A good way to organize your project is to have a single(!!!) file which brings all the code together you write in other partial files yourself or which you import from other framework/modules.
In case of Bootstrap this can be (simplified example):
// ###> file: your 'custom.scss'
// Note: file is without leading underscore
// as this files GENERATES/WRITE the css to custom.css
// Files with underscore as _partial-footer-styling.scss
// are not compiled to write css on their own
// that files are only compiled to css when they are imported to files without underscore
#import 'path/your-own-vars';
// Note: technique importing files
// you don't need to write underscore and '.scss'
// Note: function of this file
// the file '_your-own-vars.scss' is to organize you needed vars special to your project
// it includes your own vars and bootstrap vars as well
// --> the Bootstrap vars in this file will overwrite the vars of Bootstrap which will be included next
#import 'bootstrap-path/functions';
#import 'bootstrap-path/variables';
#import 'bootstrap-path/mixins';
#import 'bootstrap-path/your-selected-component-1';
#import 'bootstrap-path/your-selected-component-2';
#import 'bootstrap-path/your-selected-component-3';
...
#import 'path/partial-your-own-additional-css-special-section';
#import 'path/partial-your-own-additional-css-footer-settings';
....
A detailed explanation how to include and use Bootstrap (partly if you like to do so) to your project is here: https://getbootstrap.com/docs/4.6/getting-started/theming/

How to use SASS to rewrite custom CSS methods from an external Vue module?

I'm using a third-party module in our Vue project called vue-cal, which is for rendering a calendar. You can customise how your date cells look and such, using their custom css like this.
<style>
.vuecal--month-view .vuecal__cell-content {justify-content: flex-start;}
.vuecal__cell.selected {background-color: red;}
</style>
However, we use sass for our project, and I tried rewriting it like below but now the <style> isn't being applied at all. How can I rewrite this in sass correctly, or is there no way to rewrite external libraries' custom methods like these in non-css syntax?
<style lang="sass" scoped>
.vuecal--month-view
.vuecal__cell-content
justify-content: flex-start
.vuecal__cell.selected
background-color: red
</style>
Sorry if this is a basic question - still a beginner to Vue, CSS and front-end in general.
[EDIT] Forgot to mention an important detail. We already use sass-loader#7.1.0, and I've already written some other code in sass for other components. Those are being rendered fine. That's why I'm wondering if it has to do with vue-cal-specific methods.
You need to use vue-loader to pre-process the SASS into native CSS
Step 1:
npm install -D sass-loader sass
Step 2, in your webpack config file, make sure to have:
module.exports = {
module: {
rules: [
// ... other rules omitted
// this will apply to both plain `.scss` files
// AND `<style lang="scss">` blocks in `.vue` files
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
}
]
},
// plugin omitted
}
This is the barebones needed. For further information see:
vue-loader documentation
[SOLVED]
It turned out that the problem was not sass, but the fact that the style was scoped. Scoped styles are only applied to the component itself, but vue-cal is an external module so the styles weren't applied. Removing scoped immediately fixed the issue.

Webpack + React + Loading CSS of external modules

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?

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

Accessing global sass variables and bootstrap in .vue files

I need my Vue components to inherit styling and variables from other "global" imports.
I do not want to import variables and bootstrap in every single component, this seems very redundant and not best practice.
It is basic usage so Vue must have made the functionality. I have already attempted to create a base component, an "app.vue" component if you will, that has its own styling that includes the variables and bootstrap, but my components can still not access it.
Any idea?
You can do this with webpack's sass loader options that name is 'prependData'. This code will be added top of every .vue file that consist of scss style. I know this is ugly but It works. As bootstrap documentation said. You have two choices about this. One of these is adding whole bootstrap files another one is this technique.
You can check here bootstrap documentation.
// webpack.config.js
loader: 'sass-loader',
options: {
prependData: "#import '~/node_modules/bootstrap/scss/_functions.scss'; #import '~/node_modules/bootstrap/scss/_variables.scss'; #import '~/node_modules/bootstrap/scss/_mixins.scss';"
}
I know of no other way than #import the variables file in each component. Much like we have to import/require the npm libs we use in each component.

Resources