Using css-loader without React - css

I'm building a small web app with Webpack-enabled CSS modules (via css-loader) and without React. My goal is to get the benefits of short, obfuscated CSS class names (as I'm currently using lengthy BEM class names) in my HTML by using the localIdentName: '[hash:base64:8]' option on css-loader. Since I'm not using React, I'm working with raw HTML that is not being programmatically generated through JSX file or document.write.
I've used css-loader with React plenty before, and it ultimately works because you're able to import the style class names in the React file and refer to them using the human-readable names, regardless of whatever the class name gets changed to by Webpack.
However, I'm confused how to deal with this when using raw HTML; I can't just import the styles in since it's not a JS file. If I have a class called photo__caption--large referenced in my HTML, and webpack converts the class name to d87sj, the CSS file will say d87sj but the HTML will still say photo__caption--large.
Is there some kind of loader for HTML files that's able to edit class names in the file to their Webpackified equivalents? Or should I really just be using React with CSS modules?

This github code might help you.
https://github.com/moorthy-g/css-module-for-raw-html
A bit of complicated setup needed. We need to wire the following packages together.
- postcss-loader
- postcss-modules
- posthtml-css-modules
- posthtml-loader
The following postcss configuration creates modules dump file (./tmp/module-data.json).
// postcss.config.js
module.exports = {
plugins: {
'postcss-modules': {
getJSON: function(cssFileName, json) {
const path = require('path'), fs = require('fs');
const moduleFile = path.resolve('./tmp/module-data.json');
const cssName = path.basename(cssFileName, '.css');
const moduleData = fs.existsSync(moduleFile) ? require(moduleFile) : {};
moduleData[cssName] = json;
fs.existsSync('./tmp') || fs.mkdirSync('./tmp');
fs.writeFileSync(moduleFile, JSON.stringify(moduleData));
},
generateScopedName: '[local]_[hash:base64:5]'
}
}
}
And the following webpack rule links html file to modules dump file.
{
test: /\.html$/,
use: [
{ loader: 'html-loader' },
{
loader: 'posthtml-loader',
options: {
plugins: [
require('posthtml-css-modules')('./tmp/module-data.json')
]
}
}
]
}
Finally, HTML uses css-module attribute instead of class
<div css-module="main.container">
<h2 css-module="main.title">Title for the page</h2>
</div>
Let me know if you have any issues

My understanding of Webpack, CSS Modules, & CSS-Loader is that the entire point is to use javascript to generate the files.
This enables all the name translation and what not. What's your goal with Webpack if you're writing static HTML?
There are several static site generators for Webpack that will allow you to get the result you want, BUT they're still building the HTML files.
Alternately you could look at tools similar to React (Vue or Angular) that allow you to write all your "templates" in straight HTML. In Vue for example, you can write only HTML (to be compiled from javascript) without needing to use any of its data binding or routing.

Related

How to handle css modules with ?module suffix in Jest

I'm building a react app that mixes global css with css modules using Symfony's webpack encore. To avoid global CSS issues I've settled on using import 'app.css' for global styles and import styles from 'component.css?module' in my components. This is working as expected, however Jest is not pruning the ?module from the css module import and cannot find the file, giving me errors like Cannot find module './login.module.css?module' from 'assets/pages/Login/index.jsx'.
Does anyone know how to workaround this?
I managed to fix this myself. If anyone is interested, I enabled css modules in webpack.config.js by adding
.configureCssLoader((options) => {
options.modules = true;
})
However this then applies css module loading to all .css files and breaks global css rules.
So I modified the webpack config manually using the following at the end of webpack.config.js
const config = Encore.getWebpackConfig();
// only include files ending in module.css in the module css loader
config.module.rules[1].include = /\.module\.css$/;
// add another css loader without modules enabled to parse global css files
config.module.rules.push({
test: /\.css$/,
use: ["style-loader", "css-loader"],
exclude: /\.module\.css$/,
});
module.exports = config;
Not sure if this is the best way, but seems to be working ok at the moment!
I had some similar issue and fixed it with something like that (with Webpack Encore) :
.configureCssLoader(options => {
options.modules = {
auto: /\.module\.\w+$/i
}})
And now you can create a file with '.module.scss' suffix, and you can import it in a ts file. (working with jest, unlike ?module)

How to make module css works with typescript in a gatsby application?

I have an GastbyJS application, and I'm trying to add typescript on it. I solve most of the issues, but I'm not able to make the css module work with it.
I have this import in my file, that works fine:
import styles from "./card.module.css"
But when I added the typescript config it says that Cannot find module './card.module.css'.ts(2307)
I tried to use some gatsby plugins, but they didn`t work as expected.
The whole code is here: github
I had the same problem and after trying all the various plugins I came across this solution which worked for me.
Create a css.d.ts file with:
declare module '*.css' {
const content: { [className: string]: string };
export default content;
}
Add these lines to your tsconfig.json file:
"esModuleInterop": true,
"files": ["./src/typings/css.d.ts"]
The "files" path needs to match where your type definition file is.
https://github.com/thetrevorharmon/gatsby-starter-typescript-sass/issues/1#issuecomment-436748732

Build additional CSS file in angular build

Background
#angular-builders/custom-webpack allows us to customize the webpack config of angular build, tutorial here. I am using it to build the additional scripts for web extension (Chrome/Firefox).
Here is the extra.webpack.config.js that I have included in angular.json
const { resolve } = require('path');
const scriptsDir = __dirname;
module.exports = {
entry: {
"background-script": resolve(scriptsDir, 'background-script/boot.ts'),
"fill-manager": [resolve(scriptsDir, 'fill-manager/boot.ts')],
"site-bridge": resolve(scriptsDir, 'site-bridge/boot.ts')
}
};
As expected it outputs background-script.js, fill-manager.js and site-bridge.js alongside angular build artifacts. As this webpack config is merged with the angular's webpack config, we can control all the optimizations, hashing, source maps etc from a single angular.json file.
Problem
I also want to bundle additional css files that would be used with extension scripts and be controlled by angular build.
I might be able to add specific rules, loaders etc to extra.webpack.config.js but I do not want to deal with postcss, autoprefixer and sass loaders etc as its already being done inside angular.
Just like script files, simply adding css entry inside extra.webpack.config.js does not produce css file i.e.
module.exports = {
entry: {
...
"fill-manager": [resolve(scriptsDir, 'fill-manager/boot.ts'), resolve(scriptsDir, 'fill-manager/main.css')],
...
}
};
Is there a way where I can specify a css/scss file entry in extra.webpack.config.js and it just output a bundled css file using configuration based on angular?

with webpack, why should one import CSS files from JS source code and not build CSS in isolation like the tradition did?

webpack doc for loading css:
https://webpack.js.org/guides/asset-management/#loading-css1
The webpack doc asks to import the CSS files from the Javascript code and to then run extract-text-webpack-plugin to extract the CSS.
--> with webpack, why should one import CSS files from JS source code and not build CSS in isolation like the tradition did?
By not importing CSS from Javascript, I mean that the webpack config for CSS has no ".js" extension, and it parses the CSS/SCSS files directly.
There are benefits of not importing CSS from javascript:
(objective fact). If one wants to build only CSS, it will be faster if the bundler does not need to parse the Javascript source code. Besides, one can run the bundlers for CSS and Javscript in parallel by using parallel-webpack.
(subjective, based on tradition, but most important in my opinion). SASS building in isolation has been the tradition for years. Therefore, we can achieve better HTML semantics and maintainability. Importing CSS from JS is virtual and may lead to neglect the generated separate CSS bundle.
(objective fact) One can split more the config files for CSS and Javascript, for more clarity.
I'm more a traditionalist than most as i've been doing this for over 15 years, but I would say the new way to separate concerns is better than the traditional way.
In the old thinking we use to separate layout from styling from functionality (html from css from js). This is more 'vertical' and made knowing where files are easy, but made finding specific code to do with 'features' hard. I.e. a button might be made up of a small button section within /src/shop-front.html, a few lines of code within /src/css/shop-front.css and then the enhanced functionality would live somewhere in /src/js/shop-front.js
The new way of thinking is to separate concerns by components. So now you would have your shop-front, but this would be made from /src/components/button/ and all files will live in the same place. Including the js file which includes the css as mentioned.
The benefit being, if you decide to swap button for fancy-button all you do is change the shop-front import button from 'button' to import button from 'fancy-button'. all old code will be removed automatically. no need to try to digest and change code in multiple places.
To address your concerns:
true, node-sass is fast, but I would say the difference between that and going via webpack is negligible and the improvement in dev experience (as mentioned above) is worth any extra ms
This point does apply as the way the css/sass is built does not change just because we require it from a js file. I guess you mean if you were to use css modules, but this is a choice, you don't have to. I use sass and apply class names as i would normally.
In webpack config, you could split the js config from the css config if you wanted to. I wouldn't argue that this would make things any clearer though. the config is so small, it's not worth worrying about.
My folder structure is nice and easy to reason with:
This is a basic config for simple scss imports
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { SRC, DIST } = require('./src/config/paths');
module.exports = {
devtool: 'source-map',
cache: true,
context: SRC,
entry: {
app: [`${SRC}/client-entry.js`]
},
output: {
path: DIST,
filename: '[name]-[chunkhash].js',
publicPath: '/'
},
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
],
resolve: {
modules: ['node_modules', SRC],
extensions: ['.js', '.jsx', '.scss']
},
module: {
rules: [
{
test: /\.jsx?$/,
include: [/src/],
loader: 'babel-loader',
options: {
cacheDirectory: true
}
},
{
test: /\.s?css$/,
include: [/src/],
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'postcss-loader', 'sass-loader']
})
}
]
}
};

materializecss in messes with existing classes. How can I add a prefix?

I want to use materializecss in my chrome extension.
The extension adds some functionality to gmail. Unfortunately the materializecss seems to interfere with the gmail stylings. Is it possible to isolate the scope of the materialize css or to add a custom prefix to the classes?
I currently import the css in my manifest.json
"content_scripts": [
{
"matches": ["*://mail.google.com/*"],
"css": [
"vendor/materializecss/materialize.min.css"
],
...
Not without rewriting the whole framework (the scss files) or using scoped stlyes.
There is gulp-class-prefix plugin to prefix CSS classes. Here is the configuration file
var gulp = require('gulp'),
classPrefix = require('gulp-class-prefix');
gulp.task('prefix', function () {
return gulp.src('SOURCE_OF_CSS_FILE')
.pipe(classPrefix('YOUR_PREFIX'))
.pipe(gulp.dest('DESTINASTION_FOLDER'));
});
gulp.task('default', ['prefix']);
but this will only prefix only CSS file and will work only for the components that do not need JS inclusion. For dynamic components, you need to make changes in materiazecss.js file.
Update
I got this python script. This script is designed for bootstrap. But anyhow this works(70%-80%) for materializecss too. You just need to change the file name in the main function of it ie replace bootstrap with materializecss.

Resources