You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2023/08/23 15:47:56 UTC

[openmeetings] branch master updated: [OPENMEETINGS-2232] CSS is being built by sass

This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new 09dcd5be5 [OPENMEETINGS-2232] CSS is being built by sass
09dcd5be5 is described below

commit 09dcd5be5705601d04367df514c0d7a7095e2a5a
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Wed Aug 23 22:47:31 2023 +0700

    [OPENMEETINGS-2232] CSS is being built by sass
---
 .../src/main/assembly/components/all.xml           |   2 +
 openmeetings-web/pom.xml                           |  28 +--
 openmeetings-web/src/main/front/package.json       |  10 +-
 .../src/css/_activities.scss}                      |   0
 .../raw-admin.css => front/src/css/_admin.scss}    |   0
 .../src/css/_calendar.scss}                        |   0
 .../css/raw-chat.css => front/src/css/_chat.scss}  |   2 +
 .../src/css/_cssemoticons.scss}                    |   0
 .../src/css/_general.scss}                         | 241 ++++++++-------------
 .../src/main/front/src/css/_install.scss           |  44 ++++
 .../css/raw-menu.css => front/src/css/_menu.scss}  |   0
 .../src/css/_nettest.scss}                         |   0
 .../src/css/_responsive.scss}                      |   0
 .../css/raw-room.css => front/src/css/_room.scss}  |   0
 .../src/main/front/src/css/_signin.scss            |  32 +++
 .../css/raw-tree.css => front/src/css/_tree.scss}  |   0
 .../src/css/_variables.scss}                       |  14 ++
 .../css/raw-wb.css => front/src/css/_wb.scss}      |   0
 openmeetings-web/src/main/front/src/css/style.scss |  17 ++
 openmeetings-web/src/main/front/src/index.js       |   4 +-
 .../src/main/front/webpack-css.common.js           |  33 +++
 openmeetings-web/src/main/front/webpack-css.dev.js |  21 ++
 .../src/main/front/webpack-css.prod.js             |  20 ++
 openmeetings-web/src/main/front/webpack.common.js  |   2 +-
 .../apache/openmeetings/web/pages/BasePage.java    |   4 +-
 25 files changed, 285 insertions(+), 189 deletions(-)

diff --git a/openmeetings-server/src/main/assembly/components/all.xml b/openmeetings-server/src/main/assembly/components/all.xml
index b9edc04f2..a53285863 100644
--- a/openmeetings-server/src/main/assembly/components/all.xml
+++ b/openmeetings-server/src/main/assembly/components/all.xml
@@ -109,6 +109,8 @@
 			<outputDirectory>${om.webapp}</outputDirectory>
 			<excludes>
 				<exclude>WEB-INF/classes/**</exclude>
+				<exclude>js/*.txt</exclude>
+				<exclude>css/*.js</exclude>
 			</excludes>
 		</fileSet>
 		<fileSet>
diff --git a/openmeetings-web/pom.xml b/openmeetings-web/pom.xml
index dcd092161..2934a0201 100644
--- a/openmeetings-web/pom.xml
+++ b/openmeetings-web/pom.xml
@@ -94,7 +94,7 @@
 							<arguments>run build</arguments>
 							<workingDirectory>src/main/front</workingDirectory>
 							<environmentVariables>
-								<outDir>${project.build.directory}/${project.build.finalName}/js/</outDir>
+								<outDir>${project.build.directory}/${project.build.finalName}/</outDir>
 							</environmentVariables>
 						</configuration>
 					</execution>
@@ -104,32 +104,6 @@
 				<groupId>com.samaxes.maven</groupId>
 				<artifactId>minify-maven-plugin</artifactId>
 				<executions>
-					<execution>
-						<id>theme-minify</id>
-						<goals>
-							<goal>minify</goal>
-						</goals>
-						<configuration>
-							<charset>UTF-8</charset>
-							<cssSourceFiles>
-								<cssSourceFile>raw-variables.css</cssSourceFile>
-								<cssSourceFile>raw-nettest.css</cssSourceFile>
-								<cssSourceFile>raw-general.css</cssSourceFile>
-								<cssSourceFile>raw-activities.css</cssSourceFile>
-								<cssSourceFile>raw-admin.css</cssSourceFile>
-								<cssSourceFile>raw-calendar.css</cssSourceFile>
-								<cssSourceFile>raw-cssemoticons.css</cssSourceFile>
-								<cssSourceFile>raw-chat.css</cssSourceFile>
-								<cssSourceFile>raw-menu.css</cssSourceFile>
-								<cssSourceFile>raw-room.css</cssSourceFile>
-								<cssSourceFile>raw-tree.css</cssSourceFile>
-								<cssSourceFile>raw-wb.css</cssSourceFile>
-								<cssSourceFile>raw-responsive.css</cssSourceFile>
-							</cssSourceFiles>
-							<skipMinify>true</skipMinify>
-							<cssFinalFile>theme.css</cssFinalFile>
-						</configuration>
-					</execution>
 					<execution>
 						<id>nettest-js</id>
 						<goals>
diff --git a/openmeetings-web/src/main/front/package.json b/openmeetings-web/src/main/front/package.json
index eb4bf82a0..ca0ff4c31 100644
--- a/openmeetings-web/src/main/front/package.json
+++ b/openmeetings-web/src/main/front/package.json
@@ -4,14 +4,20 @@
 	"description": "",
 	"main": "./src/index.js",
 	"scripts": {
-		"build-dev": "webpack --config webpack.dev.js",
-		"build-prod": "webpack --config webpack.prod.js",
+		"build-dev": "webpack --config webpack.dev.js && webpack --config webpack-css.dev.js",
+		"build-prod": "webpack --config webpack.prod.js && webpack --config webpack-css.prod.js",
 		"build": "npm run build-dev && npm run build-prod"
 	},
 	"author": "",
 	"license": "Apache-2.0",
 	"rat-license": "Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0",
 	"devDependencies": {
+		"sass-loader": "^13.3.2",
+		"style-loader": "^3.3.3",
+		"css-loader": "^6.8.1",
+		"ignore-loader": "^0.1.2",
+		"mini-css-extract-plugin": "^2.7.6",
+		"node-sass": "^9.0.0",
 		"circular-dependency-plugin": "^5.2.2",
 		"webpack": "^5.88.2",
 		"webpack-cli": "^5.1.4",
diff --git a/openmeetings-web/src/main/webapp/css/raw-activities.css b/openmeetings-web/src/main/front/src/css/_activities.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-activities.css
rename to openmeetings-web/src/main/front/src/css/_activities.scss
diff --git a/openmeetings-web/src/main/webapp/css/raw-admin.css b/openmeetings-web/src/main/front/src/css/_admin.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-admin.css
rename to openmeetings-web/src/main/front/src/css/_admin.scss
diff --git a/openmeetings-web/src/main/webapp/css/raw-calendar.css b/openmeetings-web/src/main/front/src/css/_calendar.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-calendar.css
rename to openmeetings-web/src/main/front/src/css/_calendar.scss
diff --git a/openmeetings-web/src/main/webapp/css/raw-chat.css b/openmeetings-web/src/main/front/src/css/_chat.scss
similarity index 99%
rename from openmeetings-web/src/main/webapp/css/raw-chat.css
rename to openmeetings-web/src/main/front/src/css/_chat.scss
index 68417de27..a57aff0a5 100644
--- a/openmeetings-web/src/main/webapp/css/raw-chat.css
+++ b/openmeetings-web/src/main/front/src/css/_chat.scss
@@ -194,3 +194,5 @@
 .chat-delete.confirmation {
 	z-index: calc(var(--chat-zindex) + 1);
 }
+
+@import "cssemoticons";
diff --git a/openmeetings-web/src/main/webapp/css/raw-cssemoticons.css b/openmeetings-web/src/main/front/src/css/_cssemoticons.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-cssemoticons.css
rename to openmeetings-web/src/main/front/src/css/_cssemoticons.scss
diff --git a/openmeetings-web/src/main/webapp/css/raw-general.css b/openmeetings-web/src/main/front/src/css/_general.scss
similarity index 77%
rename from openmeetings-web/src/main/webapp/css/raw-general.css
rename to openmeetings-web/src/main/front/src/css/_general.scss
index 8bb974732..8055dfdf4 100644
--- a/openmeetings-web/src/main/webapp/css/raw-general.css
+++ b/openmeetings-web/src/main/front/src/css/_general.scss
@@ -1,7 +1,6 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
-:root {
-	--app-height: 100%;
-}
+@import "variables";
+
 html, body {
 	width: 100vw;
 	height: var(--app-height);
@@ -10,18 +9,6 @@ html, body {
 	font-family: Arial, Helvetica, sans-serif;
 	overflow-y: hidden;
 }
-body {
-	--text-align-start: left;
-	--text-align-end: right;
-	--background-start: left;
-	--background-end: right;
-}
-html[dir="rtl"] body {
-	--text-align-start: right;
-	--text-align-end: left;
-	--background-start: right;
-	--background-end: left;
-}
 
 .main-loader {
 	height: 100%;
@@ -32,33 +19,6 @@ html[dir="rtl"] body {
 .main {
 	height: 100%;
 }
-.signin .oauth-section {
-	padding-bottom: 20px;
-}
-.signin .oauth-section .provider {
-	background-position: var(--background-start) 0;
-	background-size: 24px;
-	background-repeat: no-repeat;
-	height: 24px;
-	display: inline-block;
-	padding-inline-start: 20px;
-	vertical-align: bottom;
-}
-.signin .or-seperator {
-	margin-top: 20px;
-	text-align: center;
-	border-top: 1px solid #cccccc;
-}
-.signin .or-seperator i {
-	padding: 0 10px;
-	background: #ffffff;
-	position: relative;
-	top: -11px;
-	z-index: 1;
-}
-.signin-forget .form-check-label {
-	padding-inline-end: 10px;
-}
 #header {
 	font-size: 28px;
 	line-height: 40px;
@@ -66,25 +26,25 @@ html[dir="rtl"] body {
 	padding-inline-start: 80px;
 	margin-inline-start: 20px;
 	height: var(--header-height);
-}
-#header .logo {
-	display: inline-block;
-	background-image: url(images/logo.png);
-	background-repeat: no-repeat;
-	height: var(--header-height);
-	width: 80px;
+
+	.logo {
+		display: inline-block;
+		background-image: url(images/logo.png);
+		background-repeat: no-repeat;
+		height: var(--header-height);
+		width: 80px;
+	}
 }
 #topLinks {
 	--bs-border-width: 1px;
 	--bs-border-style: solid;
 	--bs-border-color: black;
+	span {
+		padding-right: 5px;
+		padding-left: 5px;
+		white-space: nowrap;
+	}
 }
-#topLinks span {
-	padding-right: 5px;
-	padding-left: 5px;
-	white-space: nowrap;
-}
-
 #busy-indicator {
 	display: none;
 	z-index: 1000;
@@ -112,47 +72,50 @@ html[dir="rtl"] body {
 	margin-inline-start: 2px;
 	display: inline-block;
 }
-.pagedEntityListPanel .pagination {
-	display: inline-block;
+.pagedEntityListPanel {
+	.pagination {
+		display: inline-block;
+	}
+	select {
+		padding: 0px 0px;
+		border: solid 1px #aacfe4;
+		width: 60px;
+		font-size: 12px;
+		display: inline;
+		float: none;
+	}
+	input {
+		font-size: 14px;
+		padding: 0px 0px;
+		border: solid 1px #aacfe4;
+		width: 100px;
+		height: 20px;
+		margin: 2px 2px;
+		display: inline;
+		float: none;
+	}
 }
 .searchForm {
 	display: inline-block;
 	vertical-align: top;
-}
-.searchForm input[type="button"] {
-	padding: .2em .5em;
-	font-size: smaller;
-}
-.pagedEntityListPanel select, .adminForm .pagedEntityListPanel select {
-	padding: 0px 0px;
-	border: solid 1px #aacfe4;
-	width: 60px;
-	font-size: 12px;
-	display: inline;
-	float: none;
-}
-.searchForm input[type="text"], .adminForm .searchForm input[type="text"] {
-	width: 140px;
-	float: none;
-	display: inline-block;
-}
-.searchForm input[type="submit"], .adminForm .searchForm input[type="submit"] {
-	width: auto;
-	float: none;
-	display: inline-block;
-	border: 2px outset buttonface;
-	padding: 1px 6px;
-	font-size: 12px;
-}
-.pagedEntityListPanel input, .adminForm .pagedEntityListPanel input {
-	font-size: 14px;
-	padding: 0px 0px;
-	border: solid 1px #aacfe4;
-	width: 100px;
-	height: 20px;
-	margin: 2px 2px;
-	display: inline;
-	float: none;
+
+	input[type="button"] {
+		padding: .2em .5em;
+		font-size: smaller;
+	}
+	input[type="text"] {
+		width: 140px;
+		float: none;
+		display: inline-block;
+	}
+	input[type="submit"] {
+		width: auto;
+		float: none;
+		display: inline-block;
+		border: 2px outset buttonface;
+		padding: 1px 6px;
+		font-size: 12px;
+	}
 }
 .centered {
 	text-align: center;
@@ -209,33 +172,35 @@ html[dir="rtl"] body {
 	margin-top: 2px;
 	display: inline-block;
 	position: relative;
-}
-.om-icon.big {
-	width: 34px;
-	height: 34px;
-}
-.om-icon::before {
-	font-family: 'Font Awesome 6 Free';
-	font-weight: 900;
-	color: var(--bs-secondary);
-	font-size: 1.2em;
-	vertical-align: text-bottom;
-	line-height: 1em;
-}
-.om-icon.big::before {
-	font-size: 2.0em;
-	line-height: 1.2em;
-}
-.add.om-icon::before {
-	content: '\f055';
-}
-.online.om-icon::before {
-	color: var(--bs-success);
-	content: '\f111';
-}
-.offline.om-icon::before {
-	color: var(--bs-danger);
-	content: '\f111';
+
+	&.big {
+		width: 34px;
+		height: 34px;
+
+		&::before {
+			font-size: 2.0em;
+			line-height: 1.2em;
+		}
+	}
+	&::before {
+		font-family: 'Font Awesome 6 Free';
+		font-weight: 900;
+		color: var(--bs-secondary);
+		font-size: 1.2em;
+		vertical-align: text-bottom;
+		line-height: 1em;
+	}
+	&.add::before {
+		content: '\f055';
+	}
+	&.online::before {
+		color: var(--bs-success);
+		content: '\f111';
+	}
+	&.offline::before {
+		color: var(--bs-danger);
+		content: '\f111';
+	}
 }
 .message {
 	margin: 50px;
@@ -478,7 +443,7 @@ select.messages.selector {
 	font-size: 12px;
 }
 .profile .remove {
-	border: 1px solid #cccc;
+	border: 1px solid #cccccc;
 	padding: 0px 7px;
 }
 .room.list.container {
@@ -638,16 +603,6 @@ select.messages.selector {
 .captcha-img {
 	vertical-align: bottom;
 }
-.installer-note {
-	margin-top: 20px;
-	padding: 0.7em;
-}
-.installer-important {
-	font-size: 1.2em;
-}
-.installer-less-important {
-	font-size: 1.1em;
-}
 .main.privacy {
 	max-width: 800px;
 	padding: 20px;
@@ -685,28 +640,6 @@ select.messages.selector {
 .private-message .ui-autocomplete {
 	z-index: 999 !important;
 }
-.abstractWizard .adminForm div.formelement {
-	max-width: 600px;
-}
-.abstractWizard .adminForm label {
-	width: 240px;
-}
-.abstractWizard ul.paramList {
-	list-style-type: none;
-}
-.abstractWizard ul.paramList li {
-	padding-top: 5px;
-}
-.abstractWizard ul.paramList label {
-	width: 350px;
-	display: inline-block;
-}
-.abstractWizard input, .abstractWizard select {
-	width: 280px;
-}
-.abstractWizard input[type=checkbox], .abstractWizard input[type=radio] {
-	width: auto;
-}
 .img-upload .btn.btn-file {
 	width: 120px;
 	padding: 0;
@@ -720,10 +653,6 @@ select.messages.selector {
 .popover.confirmation.show {
 	z-index: 3000;
 }
-.installer {
-	overflow-y: auto;
-	height: calc(100% - var(--header-height));
-}
 .overflow-break-word {
 	overflow-wrap: break-word;
 }
diff --git a/openmeetings-web/src/main/front/src/css/_install.scss b/openmeetings-web/src/main/front/src/css/_install.scss
new file mode 100644
index 000000000..579551a32
--- /dev/null
+++ b/openmeetings-web/src/main/front/src/css/_install.scss
@@ -0,0 +1,44 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+.abstractWizard {
+	.adminForm {
+		div.formelement {
+			max-width: 600px;
+		}
+
+		label {
+			width: 240px;
+		}
+	}
+	ul.paramList {
+		list-style-type: none;
+
+		li {
+			padding-top: 5px;
+		}
+		label {
+			width: 350px;
+			display: inline-block;
+		}
+	}
+	& input, & select {
+		width: 280px;
+	}
+	& input[type=checkbox], & input[type=radio] {
+		width: auto;
+	}
+}
+
+.installer-note {
+	margin-top: 20px;
+	padding: 0.7em;
+}
+.installer-important {
+	font-size: 1.2em;
+}
+.installer-less-important {
+	font-size: 1.1em;
+}
+.installer {
+	overflow-y: auto;
+	height: calc(100% - var(--header-height));
+}
diff --git a/openmeetings-web/src/main/webapp/css/raw-menu.css b/openmeetings-web/src/main/front/src/css/_menu.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-menu.css
rename to openmeetings-web/src/main/front/src/css/_menu.scss
diff --git a/openmeetings-web/src/main/webapp/css/raw-nettest.css b/openmeetings-web/src/main/front/src/css/_nettest.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-nettest.css
rename to openmeetings-web/src/main/front/src/css/_nettest.scss
diff --git a/openmeetings-web/src/main/webapp/css/raw-responsive.css b/openmeetings-web/src/main/front/src/css/_responsive.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-responsive.css
rename to openmeetings-web/src/main/front/src/css/_responsive.scss
diff --git a/openmeetings-web/src/main/webapp/css/raw-room.css b/openmeetings-web/src/main/front/src/css/_room.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-room.css
rename to openmeetings-web/src/main/front/src/css/_room.scss
diff --git a/openmeetings-web/src/main/front/src/css/_signin.scss b/openmeetings-web/src/main/front/src/css/_signin.scss
new file mode 100644
index 000000000..3af28875d
--- /dev/null
+++ b/openmeetings-web/src/main/front/src/css/_signin.scss
@@ -0,0 +1,32 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+.signin {
+	.oauth-section {
+		padding-bottom: 20px;
+
+		.provider {
+			background-position: var(--background-start) 0;
+			background-size: 24px;
+			background-repeat: no-repeat;
+			height: 24px;
+			display: inline-block;
+			padding-inline-start: 20px;
+			vertical-align: bottom;
+		}
+	}
+	.or-seperator {
+		margin-top: 20px;
+		text-align: center;
+		border-top: 1px solid #cccccc;
+
+		i {
+			padding: 0 10px;
+			background: #ffffff;
+			position: relative;
+			top: -11px;
+			z-index: 1;
+		}
+	}
+}
+.signin-forget .form-check-label {
+	padding-inline-end: 10px;
+}
diff --git a/openmeetings-web/src/main/webapp/css/raw-tree.css b/openmeetings-web/src/main/front/src/css/_tree.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-tree.css
rename to openmeetings-web/src/main/front/src/css/_tree.scss
diff --git a/openmeetings-web/src/main/webapp/css/raw-variables.css b/openmeetings-web/src/main/front/src/css/_variables.scss
similarity index 85%
rename from openmeetings-web/src/main/webapp/css/raw-variables.css
rename to openmeetings-web/src/main/front/src/css/_variables.scss
index 28b87036f..127c27831 100644
--- a/openmeetings-web/src/main/webapp/css/raw-variables.css
+++ b/openmeetings-web/src/main/front/src/css/_variables.scss
@@ -1,4 +1,7 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+:root {
+	--app-height: 100%;
+}
 body {
 	--header-height: 50px;
 	--menu-height: 36px;
@@ -15,6 +18,17 @@ body {
 	--chat-msg-height: 80px;
 	--chat-send-width: 32px;
 	--chat-zindex: 2000;
+
+	--text-align-start: left;
+	--text-align-end: right;
+	--background-start: left;
+	--background-end: right;
+}
+html[dir="rtl"] body {
+	--text-align-start: right;
+	--text-align-end: left;
+	--background-start: right;
+	--background-end: left;
 }
 body.no-header {
 	--header-height: 0px;
diff --git a/openmeetings-web/src/main/webapp/css/raw-wb.css b/openmeetings-web/src/main/front/src/css/_wb.scss
similarity index 100%
rename from openmeetings-web/src/main/webapp/css/raw-wb.css
rename to openmeetings-web/src/main/front/src/css/_wb.scss
diff --git a/openmeetings-web/src/main/front/src/css/style.scss b/openmeetings-web/src/main/front/src/css/style.scss
new file mode 100644
index 000000000..5eb3fa29b
--- /dev/null
+++ b/openmeetings-web/src/main/front/src/css/style.scss
@@ -0,0 +1,17 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+
+@import "general";
+@import "install";
+@import "signin";
+@import "admin";
+@import "activities";
+@import "calendar";
+@import "chat";
+@import "menu";
+@import "nettest";
+@import "room";
+@import "tree";
+@import "wb";
+
+/* last one */
+@import "responsive";
diff --git a/openmeetings-web/src/main/front/src/index.js b/openmeetings-web/src/main/front/src/index.js
index 41fb9a013..cbad11af7 100644
--- a/openmeetings-web/src/main/front/src/index.js
+++ b/openmeetings-web/src/main/front/src/index.js
@@ -1,3 +1,5 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
 
-// designed to be empty
\ No newline at end of file
+import './css/style.scss';
+
+// designed to have no code but import
\ No newline at end of file
diff --git a/openmeetings-web/src/main/front/webpack-css.common.js b/openmeetings-web/src/main/front/webpack-css.common.js
new file mode 100644
index 000000000..a24ec1bc7
--- /dev/null
+++ b/openmeetings-web/src/main/front/webpack-css.common.js
@@ -0,0 +1,33 @@
+// Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+
+module.exports = {
+	module: {
+		rules: [{
+			test: /\.png$/i,
+			use: [
+				'ignore-loader',
+			],
+		},{
+			test: /\.s[ac]ss$/i,
+			use: [
+				MiniCssExtractPlugin.loader,
+				{
+					loader: 'css-loader',
+					options: {
+						url: false,
+					},
+				},
+				"sass-loader",
+			],
+		}],
+	},
+	entry: {
+		_theme: {
+			import: './src/index.js'
+		}
+	},
+	output: {
+		path: `${process.env.outDir}/css/`,
+	},
+};
diff --git a/openmeetings-web/src/main/front/webpack-css.dev.js b/openmeetings-web/src/main/front/webpack-css.dev.js
new file mode 100644
index 000000000..be2ef73f2
--- /dev/null
+++ b/openmeetings-web/src/main/front/webpack-css.dev.js
@@ -0,0 +1,21 @@
+// Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0
+
+const { merge } = require('webpack-merge');
+const common = require('./webpack-css.common.js');
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+
+module.exports = merge(common, {
+	mode: 'development',
+	devtool: 'inline-source-map',
+	plugins: [
+		new MiniCssExtractPlugin({
+			// Options similar to the same options in webpackOptions.output
+			// both options are optional
+			filename: 'theme.css',
+			chunkFilename: '[id].css',
+		}),
+	],
+	output: {
+		filename: '[name].js',
+	},
+});
diff --git a/openmeetings-web/src/main/front/webpack-css.prod.js b/openmeetings-web/src/main/front/webpack-css.prod.js
new file mode 100644
index 000000000..64f73dfca
--- /dev/null
+++ b/openmeetings-web/src/main/front/webpack-css.prod.js
@@ -0,0 +1,20 @@
+// Licensed under the Apache License, Version 2.0 (the \"License\") http://www.apache.org/licenses/LICENSE-2.0
+
+const { merge } = require('webpack-merge');
+const common = require('./webpack-css.common.js');
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+
+module.exports = merge(common, {
+	mode: 'production',
+	plugins: [
+		new MiniCssExtractPlugin({
+			// Options similar to the same options in webpackOptions.output
+			// both options are optional
+			filename: 'theme.min.css',
+			chunkFilename: '[id].css',
+		}),
+	],
+	output: {
+		filename: '[name].min.js',
+	},
+});
diff --git a/openmeetings-web/src/main/front/webpack.common.js b/openmeetings-web/src/main/front/webpack.common.js
index 3413ae443..eb4c83b4e 100644
--- a/openmeetings-web/src/main/front/webpack.common.js
+++ b/openmeetings-web/src/main/front/webpack.common.js
@@ -37,6 +37,6 @@ module.exports = {
 		}),
 	],
 	output: {
-		path: `${process.env.outDir}`,
+		path: `${process.env.outDir}/js/`,
 	},
 };
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/BasePage.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/BasePage.java
index 67ce15035..189e7a182 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/BasePage.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/pages/BasePage.java
@@ -131,8 +131,8 @@ public abstract class BasePage extends AsyncUrlFragmentAwarePage {
 		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(Application.get().getJavaScriptLibrarySettings().getJQueryReference())));
 		super.renderHead(response);
 		final String suffix = DEVELOPMENT == getApplication().getConfigurationType() ? "" : ".min";
-		response.render(CssHeaderItem.forUrl(String.format("css/theme_om/jquery-ui%s.css", suffix)));
-		response.render(CssHeaderItem.forUrl("css/theme.css"));
+		response.render(CssHeaderItem.forUrl("css/theme_om/jquery-ui" + suffix + ".css"));
+		response.render(CssHeaderItem.forUrl("css/theme" + suffix + ".css"));
 		if (!Strings.isEmpty(getGaCode())) {
 			response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(BasePage.class, "om-ga.js") {
 				private static final long serialVersionUID = 1L;