diff --git a/package.json b/package.json
index db0f7c7f7b9f5c34728b306427b915a1f02a5c8c..f490dd68555e3e8d338e4f7ab4e053803fa896a9 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
     "internal:eslint": "eslint --cache --max-warnings 0 --report-unused-disable-directives --ext .js,.vue,.graphql",
     "internal:stylelint": "stylelint -q --rd '{ee/,}app/assets/stylesheets/**/*.{css,scss}'",
     "prejest": "yarn check-dependencies",
-    "build:css": "node ./scripts/frontend/compile_css.mjs",
+    "build:css": "node scripts/frontend/build_css.mjs",
     "jest": "jest --config jest.config.js",
     "jest-debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
     "jest:ci": "jest --config jest.config.js --ci --coverage --testSequencer ./scripts/frontend/parallel_ci_sequencer.js",
diff --git a/scripts/frontend/build_css.mjs b/scripts/frontend/build_css.mjs
new file mode 100755
index 0000000000000000000000000000000000000000..bc07362c3f95aeea0b258287bce6ae6ea3bcf050
--- /dev/null
+++ b/scripts/frontend/build_css.mjs
@@ -0,0 +1,13 @@
+#!/usr/bin/env node
+
+import process from 'node:process';
+/* eslint-disable import/extensions */
+import { compileAllStyles } from './lib/compile_css.mjs';
+/* eslint-enable import/extensions */
+
+const fileWatcher = await compileAllStyles({ shouldWatch: process.argv?.includes('--watch') });
+
+process.on('SIGTERM', () => {
+  console.info('SIGTERM signal received.');
+  fileWatcher?.close();
+});
diff --git a/scripts/frontend/compile_css.mjs b/scripts/frontend/lib/compile_css.mjs
old mode 100755
new mode 100644
similarity index 76%
rename from scripts/frontend/compile_css.mjs
rename to scripts/frontend/lib/compile_css.mjs
index 433c9117f8189503ed5c60f3da0757d05814ab73..64d694c4371a00025d88d40829942a9e65c33f27
--- a/scripts/frontend/compile_css.mjs
+++ b/scripts/frontend/lib/compile_css.mjs
@@ -1,18 +1,16 @@
-#!/usr/bin/env node
-
-import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
+import { mkdir, writeFile } from 'node:fs/promises';
 import { fileURLToPath } from 'node:url';
 import path from 'node:path';
-import { argv } from 'node:process';
+import { env } from 'node:process';
 import { compile, Logger } from 'sass';
 import glob from 'glob';
 /* eslint-disable import/extensions */
-import IS_EE from '../../config/helpers/is_ee_env.js';
-import IS_JH from '../../config/helpers/is_jh_env.js';
+import IS_EE from '../../../config/helpers/is_ee_env.js';
+import IS_JH from '../../../config/helpers/is_jh_env.js';
 /* eslint-enable import/extensions */
 
 // Note, in node > 21.2 we could replace the below with import.meta.dirname
-const ROOT_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../');
+const ROOT_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../');
 const OUTPUT_PATH = path.join(ROOT_PATH, 'app/assets/builds/');
 
 const BASE_PATH = 'app/assets/stylesheets';
@@ -166,14 +164,7 @@ function resolveCompilationTargets() {
   return Object.fromEntries([...result.entries()].map((entry) => entry.reverse()));
 }
 
-function writeContentToFile(content, srcFile, outputFile) {
-  if (!existsSync(path.dirname(outputFile))) {
-    mkdirSync(path.dirname(outputFile), { recursive: true });
-  }
-  writeFileSync(outputFile, content.css);
-}
-
-async function compileAllStyles() {
+export async function compileAllStyles({ shouldWatch = false }) {
   const reverseDependencies = {};
 
   const compilationTargets = resolveCompilationTargets();
@@ -184,38 +175,81 @@ async function compileAllStyles() {
   };
 
   let fileWatcher = null;
-  if (argv?.includes('--watch')) {
+  if (shouldWatch) {
     const { watch } = await import('chokidar');
     fileWatcher = watch([]);
   }
 
-  function compileSCSSFile(source, dest) {
+  async function compileSCSSFile(source, dest) {
     console.log(`\tcompiling source ${source} to ${dest}`);
     const content = compile(source, sassCompilerOptions);
     if (fileWatcher) {
       for (const dependency of content.loadedUrls) {
         if (dependency.protocol === 'file:') {
-          reverseDependencies[dependency.pathname] ||= new Set();
-          reverseDependencies[dependency.pathname].add(source);
-          fileWatcher.add(dependency.pathname);
+          const dependencyPath = fileURLToPath(dependency);
+          reverseDependencies[dependencyPath] ||= new Set();
+          reverseDependencies[dependencyPath].add(source);
+          fileWatcher.add(dependencyPath);
         }
       }
     }
-    writeContentToFile(content, source, dest);
+    // Create target folder if it doesn't exist
+    await mkdir(path.dirname(dest), { recursive: true });
+    return writeFile(dest, content.css, 'utf-8');
   }
 
   if (fileWatcher) {
-    fileWatcher.on('change', (changedFile) => {
+    fileWatcher.on('change', async (changedFile) => {
       console.warn(`${changedFile} changed, recompiling`);
+      const recompile = [];
       for (const source of reverseDependencies[changedFile]) {
-        compileSCSSFile(source, compilationTargets[source]);
+        recompile.push(compileSCSSFile(source, compilationTargets[source]));
       }
+      await Promise.all(recompile);
     });
   }
 
-  for (const [source, dest] of Object.entries(compilationTargets)) {
-    compileSCSSFile(source, dest);
+  const initialCompile = Object.entries(compilationTargets).map(([source, dest]) =>
+    compileSCSSFile(source, dest),
+  );
+
+  await Promise.all(initialCompile);
+
+  return fileWatcher;
+}
+
+function shouldUseNewPipeline() {
+  return /^(true|t|yes|y|1|on)$/i.test(`${env.USE_NEW_CSS_PIPELINE}`);
+}
+
+export function viteCSSCompilerPlugin() {
+  if (!shouldUseNewPipeline()) {
+    return null;
   }
+  let fileWatcher = null;
+  return {
+    name: 'gitlab-css-compiler',
+    async configureServer() {
+      fileWatcher = await compileAllStyles({ shouldWatch: true });
+    },
+    buildEnd() {
+      return fileWatcher?.close();
+    },
+  };
 }
 
-await compileAllStyles();
+export function simplePluginForNodemon({ shouldWatch = true }) {
+  if (!shouldUseNewPipeline()) {
+    return null;
+  }
+  let fileWatcher = null;
+  return {
+    async start() {
+      await fileWatcher?.close();
+      fileWatcher = await compileAllStyles({ shouldWatch });
+    },
+    stop() {
+      return fileWatcher?.close();
+    },
+  };
+}
diff --git a/scripts/frontend/webpack_dev_server.js b/scripts/frontend/webpack_dev_server.js
index ae73c14b501c8a52b4cb0d1711dfcac84c6aeaf5..296d0058b1ae6471db0a3866629f496f9b682f0d 100755
--- a/scripts/frontend/webpack_dev_server.js
+++ b/scripts/frontend/webpack_dev_server.js
@@ -47,6 +47,8 @@ else {
   });
 }
 
+let plugin = false;
+
 // print useful messages for nodemon events
 nodemon
   .on('start', () => {
@@ -56,11 +58,20 @@ nodemon
       console.log('The JavaScript assets are recompiled only if they change');
       console.log('If you change them often, you might want to unset DEV_SERVER_STATIC');
     }
+    /* eslint-disable import/extensions, promise/catch-or-return */
+    import('./lib/compile_css.mjs').then(({ simplePluginForNodemon }) => {
+      plugin = simplePluginForNodemon({ shouldWatch: !STATIC_MODE });
+      return plugin?.start();
+    });
+    /* eslint-enable import/extensions, promise/catch-or-return */
   })
   .on('quit', () => {
+    console.log('Shutting down CSS compilation process');
+    plugin?.stop();
     console.log('Shutting down webpack process');
     process.exit();
   })
   .on('restart', (files) => {
     console.log('Restarting webpack process due to: ', files);
+    plugin?.start();
   });
diff --git a/vite.config.js b/vite.config.js
index 007706a195578d17ad4e871c0c607028e301d60e..2a395b5acb10adf7bc1e949fae50c40f4c19f7a6 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -14,6 +14,9 @@ import {
   SOURCEGRAPH_PUBLIC_PATH,
   GITLAB_WEB_IDE_PUBLIC_PATH,
 } from './config/webpack.constants';
+/* eslint-disable import/extensions */
+import { viteCSSCompilerPlugin } from './scripts/frontend/lib/compile_css.mjs';
+/* eslint-enable import/extensions */
 
 let viteGDKConfig;
 try {
@@ -104,6 +107,7 @@ export default defineConfig({
     ],
   },
   plugins: [
+    viteCSSCompilerPlugin(),
     viteGDKConfig.enabled ? autoRestartPlugin : null,
     fixedRubyPlugin,
     vue({