diff --git a/README.md b/README.md
index 1c0905b0..bf483fab 100644
--- a/README.md
+++ b/README.md
@@ -911,6 +911,150 @@ module.exports = {
};
```
+### Multiple Themes
+
+**webpack.config.js**
+
+```js
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+
+module.exports = {
+ entry: "./src/index.js",
+ module: {
+ rules: [
+ {
+ test: /\.s[ac]ss$/i,
+ oneOf: [
+ {
+ resourceQuery: "?dark",
+ use: [
+ Self.loader,
+ "css-loader",
+ {
+ loader: "sass-loader",
+ options: {
+ additionalData: `@use 'dark-theme/vars' as vars;`,
+ },
+ },
+ ],
+ },
+ {
+ use: [
+ Self.loader,
+ "css-loader",
+ {
+ loader: "sass-loader",
+ options: {
+ additionalData: `@use 'light-theme/vars' as vars;`,
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ plugins: [
+ new Self({
+ filename: "[name].css",
+ attributes: {
+ id: "theme",
+ },
+ }),
+ ],
+};
+```
+
+**src/index.js**
+
+```js
+import "./style.scss";
+
+let theme = "light";
+const themes = {};
+
+themes[theme] = document.querySelector("#theme");
+
+async function loadTheme(newTheme) {
+ // eslint-disable-next-line no-console
+ console.log(`CHANGE THEME - ${newTheme}`);
+
+ const themeElement = document.querySelector("#theme");
+
+ if (themeElement) {
+ themeElement.remove();
+ }
+
+ if (themes[newTheme]) {
+ // eslint-disable-next-line no-console
+ console.log(`THEME ALREADY LOADED - ${newTheme}`);
+
+ document.head.appendChild(themes[newTheme]);
+
+ return;
+ }
+
+ if (newTheme === "dark") {
+ // eslint-disable-next-line no-console
+ console.log(`LOADING THEME - ${newTheme}`);
+
+ import(/* webpackChunkName: "dark" */ "./style.scss?dark").then(() => {
+ themes[newTheme] = document.querySelector("#theme");
+
+ // eslint-disable-next-line no-console
+ console.log(`LOADED - ${newTheme}`);
+ });
+ }
+}
+
+document.onclick = () => {
+ if (theme === "light") {
+ theme = "dark";
+ } else {
+ theme = "light";
+ }
+
+ loadTheme(theme);
+};
+```
+
+**src/dark-theme/\_vars.scss**
+
+```scss
+$background: black;
+```
+
+**src/light-theme/\_vars.scss**
+
+```scss
+$background: white;
+```
+
+**src/styles.scss**
+
+```scss
+body {
+ background-color: vars.$background;
+}
+```
+
+**public/index.html**
+
+```html
+
+
+
+
+
+ Document
+
+
+
+
+
+
+```
+
### Media Query Plugin
If you'd like to extract the media queries from the extracted CSS (so mobile users don't need to load desktop or tablet specific CSS anymore) you should use one of the following plugins:
diff --git a/package-lock.json b/package-lock.json
index 25ab70db..6a8cb0cf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,6 +36,8 @@
"memfs": "^3.0.2",
"npm-run-all": "^4.1.5",
"prettier": "^2.3.2",
+ "sass": "^1.39.0",
+ "sass-loader": "^12.1.0",
"standard-version": "^9.3.0",
"webpack": "^5.48.0",
"webpack-cli": "^4.7.2",
@@ -11633,6 +11635,15 @@
"node": ">=6"
}
},
+ "node_modules/klona": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz",
+ "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -14776,6 +14787,55 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
+ "node_modules/sass": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.39.0.tgz",
+ "integrity": "sha512-F4o+RhJkNOIG0b6QudYU8c78ZADKZjKDk5cyrf8XTKWfrgbtyVVXImFstJrc+1pkQDCggyidIOytq6gS4gCCZg==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/sass-loader": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz",
+ "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==",
+ "dev": true,
+ "dependencies": {
+ "klona": "^2.0.4",
+ "neo-async": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "fibers": ">= 3.1.0",
+ "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0",
+ "sass": "^1.3.0",
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "fibers": {
+ "optional": true
+ },
+ "node-sass": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
"node_modules/saxes": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
@@ -26188,6 +26248,12 @@
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
"dev": true
},
+ "klona": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz",
+ "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
+ "dev": true
+ },
"leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -28557,6 +28623,25 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
+ "sass": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.39.0.tgz",
+ "integrity": "sha512-F4o+RhJkNOIG0b6QudYU8c78ZADKZjKDk5cyrf8XTKWfrgbtyVVXImFstJrc+1pkQDCggyidIOytq6gS4gCCZg==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0"
+ }
+ },
+ "sass-loader": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz",
+ "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==",
+ "dev": true,
+ "requires": {
+ "klona": "^2.0.4",
+ "neo-async": "^2.6.2"
+ }
+ },
"saxes": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
diff --git a/package.json b/package.json
index 947fc76a..a6715cb9 100644
--- a/package.json
+++ b/package.json
@@ -71,6 +71,8 @@
"memfs": "^3.0.2",
"npm-run-all": "^4.1.5",
"prettier": "^2.3.2",
+ "sass": "^1.39.0",
+ "sass-loader": "^12.1.0",
"standard-version": "^9.3.0",
"webpack": "^5.48.0",
"webpack-cli": "^4.7.2",
diff --git a/test/cases/multiple-themes-async-loading-with-default-light/expected/dark.css b/test/cases/multiple-themes-async-loading-with-default-light/expected/dark.css
new file mode 100644
index 00000000..c4d554e0
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading-with-default-light/expected/dark.css
@@ -0,0 +1,3 @@
+body {
+ background-color: black;
+}
diff --git a/test/cases/multiple-themes-async-loading-with-default-light/expected/main.css b/test/cases/multiple-themes-async-loading-with-default-light/expected/main.css
new file mode 100644
index 00000000..cdf802a3
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading-with-default-light/expected/main.css
@@ -0,0 +1,3 @@
+body {
+ background-color: white;
+}
diff --git a/test/cases/multiple-themes-async-loading-with-default-light/public/index.html b/test/cases/multiple-themes-async-loading-with-default-light/public/index.html
new file mode 100644
index 00000000..061216f2
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading-with-default-light/public/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Document
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/cases/multiple-themes-async-loading-with-default-light/src/dark-theme/_vars.scss b/test/cases/multiple-themes-async-loading-with-default-light/src/dark-theme/_vars.scss
new file mode 100644
index 00000000..2abe50c6
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading-with-default-light/src/dark-theme/_vars.scss
@@ -0,0 +1 @@
+$background: black;
diff --git a/test/cases/multiple-themes-async-loading-with-default-light/src/index.js b/test/cases/multiple-themes-async-loading-with-default-light/src/index.js
new file mode 100644
index 00000000..cce9cc7c
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading-with-default-light/src/index.js
@@ -0,0 +1,50 @@
+/* eslint-env browser */
+import "./style.scss";
+
+let theme = "light";
+const themes = {};
+
+themes[theme] = document.querySelector("#theme");
+
+async function loadTheme(newTheme) {
+ // eslint-disable-next-line no-console
+ console.log(`CHANGE THEME - ${newTheme}`);
+
+ const themeElement = document.querySelector("#theme");
+
+ if (themeElement) {
+ themeElement.remove();
+ }
+
+ if (themes[newTheme]) {
+ // eslint-disable-next-line no-console
+ console.log(`THEME ALREADY LOADED - ${newTheme}`);
+
+ document.head.appendChild(themes[newTheme]);
+
+ return;
+ }
+
+ if (newTheme === "dark") {
+ // eslint-disable-next-line no-console
+ console.log(`LOADING THEME - ${newTheme}`);
+
+ // eslint-disable-next-line import/no-unresolved
+ import(/* webpackChunkName: "dark" */ "./style.scss?dark").then(() => {
+ themes[newTheme] = document.querySelector("#theme");
+
+ // eslint-disable-next-line no-console
+ console.log(`LOADED - ${newTheme}`);
+ });
+ }
+}
+
+document.onclick = () => {
+ if (theme === "light") {
+ theme = "dark";
+ } else {
+ theme = "light";
+ }
+
+ loadTheme(theme);
+};
diff --git a/test/cases/multiple-themes-async-loading-with-default-light/src/light-theme/_vars.scss b/test/cases/multiple-themes-async-loading-with-default-light/src/light-theme/_vars.scss
new file mode 100644
index 00000000..6a1fb84b
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading-with-default-light/src/light-theme/_vars.scss
@@ -0,0 +1 @@
+$background: white;
diff --git a/test/cases/multiple-themes-async-loading-with-default-light/src/style.scss b/test/cases/multiple-themes-async-loading-with-default-light/src/style.scss
new file mode 100644
index 00000000..fb281175
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading-with-default-light/src/style.scss
@@ -0,0 +1,3 @@
+body {
+ background-color: vars.$background;
+}
diff --git a/test/cases/multiple-themes-async-loading-with-default-light/webpack.config.js b/test/cases/multiple-themes-async-loading-with-default-light/webpack.config.js
new file mode 100644
index 00000000..58c979cd
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading-with-default-light/webpack.config.js
@@ -0,0 +1,47 @@
+import Self from "../../../src";
+
+module.exports = {
+ entry: "./src/index.js",
+ module: {
+ rules: [
+ {
+ test: /\.s[ac]ss$/i,
+ oneOf: [
+ {
+ resourceQuery: "?dark",
+ use: [
+ Self.loader,
+ "css-loader",
+ {
+ loader: "sass-loader",
+ options: {
+ additionalData: `@use 'dark-theme/vars' as vars;`,
+ },
+ },
+ ],
+ },
+ {
+ use: [
+ Self.loader,
+ "css-loader",
+ {
+ loader: "sass-loader",
+ options: {
+ additionalData: `@use 'light-theme/vars' as vars;`,
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ plugins: [
+ new Self({
+ filename: "[name].css",
+ attributes: {
+ id: "theme",
+ },
+ }),
+ ],
+};
diff --git a/test/cases/multiple-themes-async-loading/expected/dark.css b/test/cases/multiple-themes-async-loading/expected/dark.css
new file mode 100644
index 00000000..c4d554e0
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading/expected/dark.css
@@ -0,0 +1,3 @@
+body {
+ background-color: black;
+}
diff --git a/test/cases/multiple-themes-async-loading/expected/light.css b/test/cases/multiple-themes-async-loading/expected/light.css
new file mode 100644
index 00000000..cdf802a3
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading/expected/light.css
@@ -0,0 +1,3 @@
+body {
+ background-color: white;
+}
diff --git a/test/cases/multiple-themes-async-loading/public/index.html b/test/cases/multiple-themes-async-loading/public/index.html
new file mode 100644
index 00000000..c51cce96
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading/public/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ Document
+
+
+
+
+
\ No newline at end of file
diff --git a/test/cases/multiple-themes-async-loading/src/dark-theme/_vars.scss b/test/cases/multiple-themes-async-loading/src/dark-theme/_vars.scss
new file mode 100644
index 00000000..2abe50c6
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading/src/dark-theme/_vars.scss
@@ -0,0 +1 @@
+$background: black;
diff --git a/test/cases/multiple-themes-async-loading/src/index.js b/test/cases/multiple-themes-async-loading/src/index.js
new file mode 100644
index 00000000..8c016077
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading/src/index.js
@@ -0,0 +1,58 @@
+/* eslint-env browser */
+
+let theme = "light";
+
+const themes = {};
+
+async function loadTheme(newTheme) {
+ // eslint-disable-next-line no-console
+ console.log(`CHANGE THEME - ${newTheme}`);
+
+ const themeElement = document.querySelector("#theme");
+
+ if (themeElement) {
+ themeElement.remove();
+ }
+
+ if (themes[newTheme]) {
+ // eslint-disable-next-line no-console
+ // eslint-disable-next-line no-console
+ console.log(`THEME ALREADY LOADED - ${newTheme}`);
+
+ document.head.appendChild(themes[newTheme]);
+
+ return;
+ }
+
+ // eslint-disable-next-line no-console
+ console.log(`LOADING THEME - ${newTheme}`);
+
+ if (newTheme === "light") {
+ import(/* webpackChunkName: "light" */ "./style.scss").then(() => {
+ themes[newTheme] = document.querySelector("#theme");
+
+ // eslint-disable-next-line no-console
+ console.log(`LOADED - ${newTheme}`);
+ });
+ } else {
+ // eslint-disable-next-line import/no-unresolved
+ import(/* webpackChunkName: "dark" */ "./style.scss?dark").then(() => {
+ themes[newTheme] = document.querySelector("#theme");
+
+ // eslint-disable-next-line no-console
+ console.log(`LOADED - ${newTheme}`);
+ });
+ }
+}
+
+document.onclick = () => {
+ if (theme === "light") {
+ theme = "dark";
+ } else {
+ theme = "light";
+ }
+
+ loadTheme(theme);
+};
+
+loadTheme(theme);
diff --git a/test/cases/multiple-themes-async-loading/src/light-theme/_vars.scss b/test/cases/multiple-themes-async-loading/src/light-theme/_vars.scss
new file mode 100644
index 00000000..6a1fb84b
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading/src/light-theme/_vars.scss
@@ -0,0 +1 @@
+$background: white;
diff --git a/test/cases/multiple-themes-async-loading/src/style.scss b/test/cases/multiple-themes-async-loading/src/style.scss
new file mode 100644
index 00000000..fb281175
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading/src/style.scss
@@ -0,0 +1,3 @@
+body {
+ background-color: vars.$background;
+}
diff --git a/test/cases/multiple-themes-async-loading/webpack.config.js b/test/cases/multiple-themes-async-loading/webpack.config.js
new file mode 100644
index 00000000..58c979cd
--- /dev/null
+++ b/test/cases/multiple-themes-async-loading/webpack.config.js
@@ -0,0 +1,47 @@
+import Self from "../../../src";
+
+module.exports = {
+ entry: "./src/index.js",
+ module: {
+ rules: [
+ {
+ test: /\.s[ac]ss$/i,
+ oneOf: [
+ {
+ resourceQuery: "?dark",
+ use: [
+ Self.loader,
+ "css-loader",
+ {
+ loader: "sass-loader",
+ options: {
+ additionalData: `@use 'dark-theme/vars' as vars;`,
+ },
+ },
+ ],
+ },
+ {
+ use: [
+ Self.loader,
+ "css-loader",
+ {
+ loader: "sass-loader",
+ options: {
+ additionalData: `@use 'light-theme/vars' as vars;`,
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ plugins: [
+ new Self({
+ filename: "[name].css",
+ attributes: {
+ id: "theme",
+ },
+ }),
+ ],
+};