diff --git a/Cargo.lock b/Cargo.lock
index ae469d23..9edd20bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -552,6 +552,12 @@ dependencies = [
  "stacker",
 ]
 
+[[package]]
+name = "codemap"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24"
+
 [[package]]
 name = "concurrent-queue"
 version = "2.5.0"
@@ -1231,6 +1237,19 @@ dependencies = [
  "spinning_top",
 ]
 
+[[package]]
+name = "grass_compiler"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d9e3df7f0222ce5184154973d247c591d9aadc28ce7a73c6cd31100c9facff6"
+dependencies = [
+ "codemap",
+ "indexmap",
+ "lasso",
+ "once_cell",
+ "phf",
+]
+
 [[package]]
 name = "h2"
 version = "0.3.26"
@@ -1886,6 +1905,15 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "lasso"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb"
+dependencies = [
+ "hashbrown 0.14.5",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.5.0"
@@ -2484,6 +2512,7 @@ version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
 dependencies = [
+ "phf_macros",
  "phf_shared",
 ]
 
@@ -2507,6 +2536,19 @@ dependencies = [
  "rand",
 ]
 
+[[package]]
+name = "phf_macros"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "phf_shared"
 version = "0.11.2"
@@ -4056,6 +4098,7 @@ dependencies = [
  "fern",
  "futures",
  "governor",
+ "grass_compiler",
  "handlebars",
  "hickory-resolver",
  "html5gum",
diff --git a/Cargo.toml b/Cargo.toml
index 78a1a4b5..150b3b9d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -163,6 +163,9 @@ argon2 = "0.5.3"
 # Reading a password from the cli for generating the Argon2id ADMIN_TOKEN
 rpassword = "7.3.1"
 
+# Loading a dynamic CSS Stylesheet
+grass_compiler = { version = "0.13.4", default-features = false }
+
 # Strip debuginfo from the release builds
 # The symbols are the provide better panic traces
 # Also enable fat LTO and use 1 codegen unit for optimizations
diff --git a/src/api/web.rs b/src/api/web.rs
index 6983719b..a96d7e2a 100644
--- a/src/api/web.rs
+++ b/src/api/web.rs
@@ -1,13 +1,20 @@
+use once_cell::sync::Lazy;
 use std::path::{Path, PathBuf};
 
-use rocket::{fs::NamedFile, http::ContentType, response::content::RawHtml as Html, serde::json::Json, Catcher, Route};
+use rocket::{
+    fs::NamedFile,
+    http::ContentType,
+    response::{content::RawCss as Css, content::RawHtml as Html, Redirect},
+    serde::json::Json,
+    Catcher, Route,
+};
 use serde_json::Value;
 
 use crate::{
     api::{core::now, ApiResult, EmptyResult},
     auth::decode_file_download,
     error::Error,
-    util::{Cached, SafeString},
+    util::{get_web_vault_version, Cached, SafeString},
     CONFIG,
 };
 
@@ -16,7 +23,7 @@ pub fn routes() -> Vec<Route> {
     // crate::utils::LOGGED_ROUTES to make sure they appear in the log
     let mut routes = routes![attachments, alive, alive_head, static_files];
     if CONFIG.web_vault_enabled() {
-        routes.append(&mut routes![web_index, web_index_head, app_id, web_files]);
+        routes.append(&mut routes![web_index, web_index_direct, web_index_head, app_id, web_files, vaultwarden_css]);
     }
 
     #[cfg(debug_assertions)]
@@ -45,11 +52,101 @@ fn not_found() -> ApiResult<Html<String>> {
     Ok(Html(text))
 }
 
+#[get("/css/vaultwarden.css")]
+fn vaultwarden_css() -> Cached<Css<String>> {
+    // Configure the web-vault version as an integer so it can be used as a comparison smaller or greater then.
+    // The default is based upon the version since this feature is added.
+    static WEB_VAULT_VERSION: Lazy<u32> = Lazy::new(|| {
+        let re = regex::Regex::new(r"(\d{4})\.(\d{1,2})\.(\d{1,2})").unwrap();
+        let vault_version = get_web_vault_version();
+
+        let (major, minor, patch) = match re.captures(&vault_version) {
+            Some(c) if c.len() == 4 => (
+                c.get(1).unwrap().as_str().parse().unwrap(),
+                c.get(2).unwrap().as_str().parse().unwrap(),
+                c.get(3).unwrap().as_str().parse().unwrap(),
+            ),
+            _ => (2024, 6, 2),
+        };
+        format!("{major}{minor:02}{patch:02}").parse::<u32>().unwrap()
+    });
+
+    // Configure the Vaultwarden version as an integer so it can be used as a comparison smaller or greater then.
+    // The default is based upon the version since this feature is added.
+    static VW_VERSION: Lazy<u32> = Lazy::new(|| {
+        let re = regex::Regex::new(r"(\d{1})\.(\d{1,2})\.(\d{1,2})").unwrap();
+        let vw_version = crate::VERSION.unwrap_or("1.32.1");
+
+        let (major, minor, patch) = match re.captures(vw_version) {
+            Some(c) if c.len() == 4 => (
+                c.get(1).unwrap().as_str().parse().unwrap(),
+                c.get(2).unwrap().as_str().parse().unwrap(),
+                c.get(3).unwrap().as_str().parse().unwrap(),
+            ),
+            _ => (1, 32, 1),
+        };
+        format!("{major}{minor:02}{patch:02}").parse::<u32>().unwrap()
+    });
+
+    let css_options = json!({
+        "web_vault_version": *WEB_VAULT_VERSION,
+        "vw_version": *VW_VERSION,
+        "signup_disabled": !CONFIG.signups_allowed() && CONFIG.signups_domains_whitelist().is_empty(),
+        "mail_enabled": CONFIG.mail_enabled(),
+        "yubico_enabled": CONFIG._enable_yubico() && (CONFIG.yubico_client_id().is_some() == CONFIG.yubico_secret_key().is_some()),
+        "emergency_access_allowed": CONFIG.emergency_access_allowed(),
+        "sends_allowed": CONFIG.sends_allowed(),
+        "load_user_scss": true,
+    });
+
+    let scss = match CONFIG.render_template("scss/vaultwarden.scss", &css_options) {
+        Ok(t) => t,
+        Err(e) => {
+            // Something went wrong loading the template. Use the fallback
+            warn!("Loading scss/vaultwarden.scss.hbs or scss/user.vaultwarden.scss.hbs failed. {e}");
+            CONFIG
+                .render_fallback_template("scss/vaultwarden.scss", &css_options)
+                .expect("Fallback scss/vaultwarden.scss.hbs to render")
+        }
+    };
+
+    let css = match grass_compiler::from_string(
+        scss,
+        &grass_compiler::Options::default().style(grass_compiler::OutputStyle::Compressed),
+    ) {
+        Ok(css) => css,
+        Err(e) => {
+            // Something went wrong compiling the scss. Use the fallback
+            warn!("Compiling the Vaultwarden SCSS styles failed. {e}");
+            let mut css_options = css_options;
+            css_options["load_user_scss"] = json!(false);
+            let scss = CONFIG
+                .render_fallback_template("scss/vaultwarden.scss", &css_options)
+                .expect("Fallback scss/vaultwarden.scss.hbs to render");
+            grass_compiler::from_string(
+                scss,
+                &grass_compiler::Options::default().style(grass_compiler::OutputStyle::Compressed),
+            )
+            .expect("SCSS to compile")
+        }
+    };
+
+    // Cache for one day should be enough and not too much
+    Cached::ttl(Css(css), 86_400, false)
+}
+
 #[get("/")]
 async fn web_index() -> Cached<Option<NamedFile>> {
     Cached::short(NamedFile::open(Path::new(&CONFIG.web_vault_folder()).join("index.html")).await.ok(), false)
 }
 
+// Make sure that `/index.html` redirect to actual domain path.
+// If not, this might cause issues with the web-vault
+#[get("/index.html")]
+fn web_index_direct() -> Redirect {
+    Redirect::to(format!("{}/", CONFIG.domain_path()))
+}
+
 #[head("/")]
 fn web_index_head() -> EmptyResult {
     // Add an explicit HEAD route to prevent uptime monitoring services from
diff --git a/src/config.rs b/src/config.rs
index aa6b1145..61a47b76 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1269,11 +1269,16 @@ impl Config {
             let hb = load_templates(CONFIG.templates_folder());
             hb.render(name, data).map_err(Into::into)
         } else {
-            let hb = &CONFIG.inner.read().unwrap().templates;
+            let hb = &self.inner.read().unwrap().templates;
             hb.render(name, data).map_err(Into::into)
         }
     }
 
+    pub fn render_fallback_template<T: serde::ser::Serialize>(&self, name: &str, data: &T) -> Result<String, Error> {
+        let hb = &self.inner.read().unwrap().templates;
+        hb.render(&format!("fallback_{name}"), data).map_err(Into::into)
+    }
+
     pub fn set_rocket_shutdown_handle(&self, handle: rocket::Shutdown) {
         self.inner.write().unwrap().rocket_shutdown_handle = Some(handle);
     }
@@ -1312,6 +1317,11 @@ where
             reg!($name);
             reg!(concat!($name, $ext));
         }};
+        (@withfallback $name:expr) => {{
+            let template = include_str!(concat!("static/templates/", $name, ".hbs"));
+            hb.register_template_string($name, template).unwrap();
+            hb.register_template_string(concat!("fallback_", $name), template).unwrap();
+        }};
     }
 
     // First register default templates here
@@ -1355,6 +1365,9 @@ where
 
     reg!("404");
 
+    reg!(@withfallback "scss/vaultwarden.scss");
+    reg!("scss/user.vaultwarden.scss");
+
     // And then load user templates to overwrite the defaults
     // Use .hbs extension for the files
     // Templates get registered with their relative name
diff --git a/src/static/templates/scss/user.vaultwarden.scss.hbs b/src/static/templates/scss/user.vaultwarden.scss.hbs
new file mode 100644
index 00000000..c0b8ed2a
--- /dev/null
+++ b/src/static/templates/scss/user.vaultwarden.scss.hbs
@@ -0,0 +1 @@
+/* See the wiki for examples and details: https://github.com/dani-garcia/vaultwarden/wiki/Customize-Vaultwarden-CSS */
diff --git a/src/static/templates/scss/vaultwarden.scss.hbs b/src/static/templates/scss/vaultwarden.scss.hbs
new file mode 100644
index 00000000..3fc3e70e
--- /dev/null
+++ b/src/static/templates/scss/vaultwarden.scss.hbs
@@ -0,0 +1,105 @@
+/**** START Static Vaultwarden changes ****/
+/* This combines all selectors extending it into one */
+%vw-hide {
+  display: none !important;
+}
+
+/* This allows searching for the combined style in the browsers dev-tools (look into the head tag) */
+.vw-hide,
+head {
+  @extend %vw-hide;
+}
+
+/* Hide the Subscription Page tab */
+bit-nav-item[route="settings/subscription"] {
+  @extend %vw-hide;
+}
+
+/* Hide any link pointing to Free Bitwarden Families */
+a[href$="/settings/sponsored-families"] {
+  @extend %vw-hide;
+}
+
+/* Hide the `Enterprise Single Sign-On` button on the login page */
+a[routerlink="/sso"] {
+  @extend %vw-hide;
+}
+
+/* Hide Two-Factor menu in Organization settings */
+bit-nav-item[route="settings/two-factor"],
+a[href$="/settings/two-factor"] {
+  @extend %vw-hide;
+}
+
+/* Hide Business Owned checkbox */
+app-org-info > form:nth-child(1) > div:nth-child(3) {
+  @extend %vw-hide;
+}
+
+/* Hide the `This account is owned by a business` checkbox and label */
+#ownedBusiness,
+label[for^="ownedBusiness"] {
+  @extend %vw-hide;
+}
+
+/* Hide the radio button and label for the `Custom` org user type */
+#userTypeCustom,
+label[for^="userTypeCustom"] {
+  @extend %vw-hide;
+}
+
+/* Hide Business Name */
+app-org-account form div bit-form-field.tw-block:nth-child(3) {
+  @extend %vw-hide;
+}
+
+/* Hide organization plans */
+app-organization-plans > form > bit-section:nth-child(2) {
+  @extend %vw-hide;
+}
+
+/* Hide Device Verification form at the Two Step Login screen */
+app-security > app-two-factor-setup > form {
+  @extend %vw-hide;
+}
+/**** END Static Vaultwarden Changes ****/
+/**** START Dynamic Vaultwarden Changes ****/
+{{#if signup_disabled}}
+/* Hide the register link on the login screen */
+app-frontend-layout > app-login > form > div > div > div > p {
+  @extend %vw-hide;
+}
+{{/if}}
+
+/* Hide `Email` 2FA if mail is not enabled */
+{{#unless mail_enabled}}
+app-two-factor-setup ul.list-group.list-group-2fa li.list-group-item:nth-child(5) {
+  @extend %vw-hide;
+}
+{{/unless}}
+
+/* Hide `YubiKey OTP security key` 2FA if it is not enabled */
+{{#unless yubico_enabled}}
+app-two-factor-setup ul.list-group.list-group-2fa li.list-group-item:nth-child(2) {
+  @extend %vw-hide;
+}
+{{/unless}}
+
+/* Hide Emergency Access if not allowed */
+{{#unless emergency_access_allowed}}
+bit-nav-item[route="settings/emergency-access"] {
+  @extend %vw-hide;
+}
+{{/unless}}
+
+/* Hide Sends if not allowed */
+{{#unless sends_allowed}}
+bit-nav-item[route="sends"] {
+  @extend %vw-hide;
+}
+{{/unless}}
+/**** End Dynamic Vaultwarden Changes ****/
+/**** Include a special user stylesheet for custom changes ****/
+{{#if load_user_scss}}
+{{> scss/user.vaultwarden.scss }}
+{{/if}}