webpack 4: Create multiple theme css files - css

I need to create multiple theme CSS files using webpack version 4 and "mini CSS extract plugin" in my react project. Depends on a place where webpack will find an import of the SCSS file, it should use loader twice - with different data in sass-loader options.
I found nothing useful in the Internet according this goal. I also have already tried to use such webpack's loaders as: webpack-combine-loaders, multi-loader etc...
here is a part of webpack config
module: {
rules: [
{
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
{
loader: "sass-loader",
options: {
data: '$theme: dark;',
}
},
],
},
{ // the same except data in options
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
{
loader: "sass-loader",
options: {
data: '$theme: white;',
}
},
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'client.white.css',
}),
new MiniCssExtractPlugin({
filename: 'client.dark.css',
}),
],
and in my scss file (button.scss) I use such condition:
$background: #06cc1a;
$color: white;
#if $theme == dark {
$background: white;
$color: black;
}
.button {
background-color: $background;
color: $color;
}
}
as a result, I want to get two CSS files client.white.css where were applied sass variables for the white theme and client.dark.css where were applied variables for the dark theme

We solved this in our project by using multiple entry points, one for each theme, e.g:
entry: {
light: './src/css/light.scss',
dark: './src/css/dark.scss'
}
With the contents of light.scss files being as follows:
$background: #001560;
#import "~base/scss/base.scss";
Webpack will output a single css file for each theme containing all the styles, both base and theme-specific, which is great when optimising for production.
Note though that you will also get a redundant JS file, which you may want to clean up post-build.

I worked on a web app which use multi theme, and we tackle the problem by saving each theme's colors to backend, so we can get the value from API depending from query, and for styling, we use styled-components for that.
I find css-in-js is really useful in this kind of problem. We actually use both styled components and LESS css for our styling. styled-components are used for coloring based on theme, and the rest is on LESS css. Perhaps you can try to use that too, or even a inline css should do the work since JS variable would work on that.
A specific example is to build a ThemeProvider component that engulf the whole application as its child, ThemeProvider will contain the declaration of class with the use of styled-components and that class can be reused throughout application scope.

Related

Vuetify: defining a base color for all text

I need to define a base color for all text in a Vue + Vuetify project. I've tried modifying the theme's primary color on vuetify.ts:
export default new Vuetify({
theme: {
themes: {
light: {
primary: "#E53935",
},
},
},
})
This, however, only applies for some cases; v-text-input labels and text outside of Vuetify components, among others, won't use this value. Is there any good alternative to explicitly styling it via css?
In order to update the variables using css. you need to configure the webpack.config.js as below.
{
test: /\.sass$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'sass-loader',
// Requires sass-loader#^7.0.0
options: {
// This is the path to your variables
data: "#import '#/styles/variables.scss'"
},
// Requires sass-loader#^8.0.0
options: {
// This is the path to your variables
prependData: "#import '#/styles/variables.scss'"
},
},
],
},
Inside, styles folder, variables.scss file keeps variables which needs to be override.
There is a list of variables defined in below link i.e global, component-wise, etc
Vuetify CSS Variables
Hope this helps!

webpack: understanding source maps when working with CSS

Introduction
I have already setup bundling for my Javascript files with webpack in my project. Now I am in the process of adding CSS files to the webpack configuration. So far, I have been including the CSS files manually in the HTML header by adding <link> elements for every CSS file I depend on (e.g. bootstrap, my own css, etc.). Obviously this is not very elegant and using webpack would be much better, so I would like to replace the link elements and bundle them via webpack.
This should be easy, everything is pretty much documented in the webpack documentation. After reading the documentation and experimenting a bit with webpack I have arrived at the configuration below which already works.
Problem
The problem with my current setup is that I would like to have proper source map support and that does not seem to work. By proper, I mean that I expect that when I run a development build with webpack and I inspect some element in Chrome DevTools, that I will see from which file and which line in the file a certain CSS class originated and that I can click on the CSS rules and the browser jumps to that file.
I do not want to have inline styles in the head element, because then the browser will show something like .foobar { <style>..</style>, rather then .foobar { app.css:154.
With my current setup I have all CSS files combined (but not minified) into one app.css file. This means that if I inspect a bootstrap class such as .btn then it appears as .btn { app.css:3003. However, what I want to achieve is that the browser shows it as .btn { bootstrap.css:3003.
So now I am trying to understand how webpack and the different plugins such as css-loader and min-css-extract-plugin apply CSS source maps, and how I can configure them to achieve a proper debugging experience.
I am not sure how relevant this is, but when I navigate in DevTools under Sources to webpack://./bootstrap/dist/css/bootstrap.css I see that it only contains a single line:
// extracted by mini-css-extract-plugin.
Webpack Setup
index.js:
window.jQuery = require('jquery/dist/jquery');
require('bootstrap/dist/css/bootstrap.css');
require('bootstrap/dist/js/bootstrap');
/* other dependencies */
webpack.config.js:
const devMode = process.env.NODE_ENV !== 'production';
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module: {
rules: [
{ /* Javascript rules excluded */ },
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
plugins: [
new UglifyJSPlugin (),
new HtmlWebpackPlugin({
template: 'app/index.tpl.html'
}),
new MiniCssExtractPlugin({ filename: devMode ?
'[name].css' :
'[name].[hash].css'
})
],
Conclusion
It seems I just passed the rubber duck test. While I was writing this I arrived at a solution. I will still publish the question, maybe it can help others.
The problem was that I was also using the mini-css-extract-plugin for development and not just for production. I thought that I needed to do that, because when at first I was using the style-loaded I would get styles included in the header and the browser would show me all styles as .foobar { <style>..</style>.
However, the actual problem seemed to be, that I was not using devtools. So the solution was to add devtool: devMode ? 'cheap-module-eval-source-map' : 'source-map', to the webpack configuration to conditionally use the style-loader plugin during development builds and mini-css-extract-plugin during production builds.
webpack.config.js
{
test: /\.css$/,
use: [
{
- loader: MiniCssExtractPlugin.loader,
+ loader: devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
},
/* ... */
+ devtool: devMode ? 'cheap-module-eval-source-map' : 'source-map',

Global variable in SCSS / SASS with angular components

We are developing a cloud application in angular 5 that will be deployed in a single instance but will be addressed to several customer.
The question concerns the management of themes, each customer wants to have his own theme (mainly colors).
The architecture (simplified) of the application looks like this:
--src
--app
app.component.html
app.component.scss
app.component.ts
-- component1
comp1.component.html
comp1.component.scss
comp1.component.ts
...
--scss
-- current
style.scss
_variables.scsc
-- customer1
style.scss
_variables.scss
-- customer2
style.scss
_variables.scss
...
Currently we are deploying by client, so there is no problem, we copy / paste the style in the current before the build.
Each component.scss imports _variables.scss to exploit the variables.
We would like that when a user logs on to the app, we detect the customer and choose the right style, but that the css is generated at compile time.
Is there a way to define global variables that can be modified in runtime and that impacts sub-components?
The solutions I tested:
Set in angular-cli a scss per customer, build and execute script js to modify the html link to css "href = 'assets / {customer} .bundle.css'". It works for global styles, but variables in subcomponents are not updated.
Use pure css to declare scope variables: root {--color-primary: gray; } and exploit them in the sub-components .test {color: var (- color-primary)}.
Make a JS script that will update all the global variables according to the client.
It works, but no equivalent in SCSS? strange
I do not know if I gave enough detail, thanks for the help.
As there is no proper way to do this; And solution used by Angular theming was not satisfactory to us; we've decided to use custom webpack builder that will compile our style based on the entries we provide. Please note, that we are not using SCSS in angular components explicitly.
"use strict";
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const AutoPrefixer = require("autoprefixer");
module.exports = {
entry: {
"modern_style.application": "./src/styles/modern_style.scss",
"default.application": "./src/styles/default.scss"
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].bundle.css"
})
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
ident: "postcss",
plugins: [AutoPrefixer()],
sourceMap: true
}
},
{
loader: "resolve-url-loader"
},
{
loader: "sass-loader",
options: {
precision: 8,
includePaths: [],
sourceMap: true
}
}
]
}
]
}
};
These entry files will have their variables set & customizations applied in each respective entry file, which looks like this:
// Entry file: modern_style.scss
$someVariableToBeUsedOrOverwritten: red;
#import "common/allModulesAreImportedHere"
.customRule{
//...
}
This generated style, e.g default.bundle.css is then loaded via <link rel="stylesheet" type="text/css" [href]="linkToTheme">
There is no way to pass any variables from ts to scss.
All you need is theming.
So for each customer you need a global body class whith its own set of variables / classes.
Check out angular material theming docs for example https://material.angular.io/guide/theming#defining-a-custom-theme
Mixins will solve your issue.
In the specific customer scss files, you would be holding the specific definition.
In the component.scss, you would be using the mixin, which is specific to the customer and it will resolve your issue on both compile and run time.

How to remove unused CSS using webpack 4?

I am having problem removing unused CSS in webpack 4. It seems that most of the CSS purification plugins are dependent on extract text plugin which is not updated for version 4.
Here's my commands:
node_modules/.bin/webpack --mode=development --env.operation=separateCSS
OR
node_modules/.bin/webpack --mode=development --env.operation=bundleCSS
Here's part of my webpack.config.js:
rules: [
// Loader for SASS files
{
test: /\.s[ac]ss$/,
use: [
// Use IIFE to choose how to emit the CSS: 1. Extract as separate file 2: Bundle with the JS file
(() => {
return env.operation == "separateCSS" ? MiniCssExtractPlugin.loader : 'style-loader';
})(),
{
loader: 'css-loader',
options: {
importLoaders: 1,
url: true
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
// Write future-proof CSS and forget old preprocessor specific syntax.
// Use the latest CSS syntax today with cssnext.
// It transforms CSS specs into more compatible CSS so you don’t need to wait for browser support.
// It parses CSS and add vendor prefixes to CSS rules using values from Can I Use.
// https://github.com/MoOx/postcss-cssnext
require('postcss-cssnext')()
]
}
},
'sass-loader'
]
}
],
plugins: [
new MiniCssExtractPlugin({
filename: "../css/[name].css"
})
]
Does anybody know how can I modify my config file so webpack can remove unused CSS?
Have you considered using something called uncss. There's a webpack plugin for it. It will look through all your CSS and compare it to your HTML, and remove anything that you're not using.
Take a look: WebPack UnCSS

Vue.js + Webpack multiple style tas output

I have several vue.js components, written in single page component format.
For each .vue file, I have less written specific for that page.
After bundling, I have several style tags, which populate the global style space. Thus, some of my classes are overlapping on different pages.
Is this the intended functionality with vue.js and webpack?
This is the default behaviour for vue-loader (which is the main plugin in the vue-webpack template).
However, if you want to you can extract all CSS into one file:
npm install extract-text-webpack-plugin --save-dev
// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")
module.exports = {
// other options...
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue'
},
]
},
vue: {
loaders: {
css: ExtractTextPlugin.extract("css"),
// you can also include <style lang="less"> or other langauges
less: ExtractTextPlugin.extract("css!less")
}
},
plugins: [
new ExtractTextPlugin("style.css")
]
}
Check out the docs of vue-loader regarding extraction.

Resources