How to inject the ERPNext theme switcher

I have created a custom switch theme feature that is dynamic so that users can add themes with one color. Here, I am having difficulty injecting the theme_switcher belonging to erpnext.
The following is my CSS code, Should I include root:?

body {
  background-color: var(--bg-color);
  color: var(--text-color);
}

.sidebar {
  background-color: var(--sidebar-bg);
  color: var(--sidebar-text);
}

.sidebar a:hover {
  background-color: var(--sidebar-hover-bg);
  color: var(--sidebar-hover-text);
}

.navbar {
  background-color: var(--navbar-bg);
  color: var(--navbar-text);
}

.card {
  background-color: var(--card-bg);
  border: 1px solid var(--card-border);
  box-shadow: 0 2px 4px var(--card-shadow);
}

.btn-primary {
  background-color: var(--btn-primary-bg);
  color: var(--btn-primary-color);
  border: 1px solid var(--btn-primary-border);
}

.btn-primary:hover {
  background-color: var(--btn-primary-hover-bg);
}

This is my JS code for performing the injection.

function inject_theme_style() {
    const $body = $("body");

    if ($body.data("custom-theme-injected")) {
        console.log(" Theme already injected.");
        return;
    }

    $body.data("custom-theme-injected", true);
    console.log("inject_theme_style: start injecting");

    frappe.call({
        method: "custom_ui.custom_ui.doctype.ui_theme.ui_theme.get_active_theme",
        callback: function (r) {
            if (!r.message || !r.message.variables) return;

            let vars = r.message.variables;

            if (typeof vars === "string") {
                try {
                    vars = JSON.parse(vars);
                } catch (e) {
                    console.warn(" Parsing error. Using empty vars");
                    vars = {};
                }
            }

            const $style = $("<style id='custom-theme-style'></style>");

            let cssVars = ":root {\n";
            for (const key in vars) {
                if (Object.hasOwn(vars, key)) {
                    cssVars += `  ${key}: ${vars[key]};\n`;
                }
            }
            cssVars += "}";

            $style.text(cssVars);
            $("head").append($style);

            console.log(`Tema '${r.message.theme_name}' berhasil diinject!`);
        }
    });
}

window.reload_custom_theme_style = function () {
    $("#custom-theme-style").remove();
    $("body").removeData("custom-theme-injected");
    inject_theme_style();
};

frappe.realtime.on("custom_theme_updated", () => {
    console.log("Theme updated. Reloading...");
    window.reload_custom_theme_style();
});

frappe.after_ajax(() => {
    setTimeout(() => {
        const interval = setInterval(() => {
            if ($("body").length) {
                clearInterval(interval);
                console.log("DOM ready, injecting theme...");
                inject_theme_style();
            }
        }, 200);
    }, 500);
});

frappe.router.on("change", () => {
    if (frappe.get_route()[0] === "app" || frappe.get_route()[0] === "desk") {
        window.reload_custom_theme_style();
    }
});