feat: add environment-based CSS theming with configurable colors and branding

This commit is contained in:
2026-04-26 16:47:48 +09:30
parent 37e5de7f92
commit d8828d64f2
16 changed files with 262 additions and 39 deletions
+19 -1
View File
@@ -1 +1,19 @@
VITE_API_BASE_URL=http://localhost:5000 # ===========================================
# Secuird UI Configuration
# ===========================================
# Copy this file to .env.local for local development
# or use mode-specific env files (.env.development, .env.staging, .env.production)
# API Configuration
VITE_API_BASE_URL=https://api.gatehouse.local/api/v1
# Theme Configuration
# Options: default (teal), dev (red), preprod (purple)
VITE_THEME=default
# Optional: Override app name and favicon
# VITE_APP_NAME=Secuird
# VITE_FAVICON=/favicon.svg
# Host Configuration
VITE_ALLOWED_HOSTS=ui.gatehouse.local,localhost
+15 -15
View File
@@ -5,37 +5,37 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Primary Meta Tags --> <!-- Primary Meta Tags -->
<title>Secuird — Enterprise Identity & Access Management</title> <title>%VITE_APP_NAME% — Enterprise Identity & Access Management</title>
<meta name="title" content="Secuird — Enterprise Identity & Access Management" /> <meta name="title" content="%VITE_APP_NAME% — Enterprise Identity & Access Management" />
<meta name="description" content="Secuird unifies social logins, MFA, and SSH certificate management in one platform. Enable enterprise SSO with Microsoft 365, Google Workspace, and GitHub without complex federation. Eliminate SSH key chaos with short-lived certificates tied to verified identities." /> <meta name="description" content="%VITE_APP_NAME% unifies social logins, MFA, and SSH certificate management in one platform. Enable enterprise SSO with Microsoft 365, Google Workspace, and GitHub without complex federation. Eliminate SSH key chaos with short-lived certificates tied to verified identities." />
<meta name="keywords" content="identity management, access management, SSO, single sign-on, SSH certificates, MFA, multi-factor authentication, OIDC, enterprise security, Microsoft 365, Google Workspace, GitHub authentication" /> <meta name="keywords" content="identity management, access management, SSO, single sign-on, SSH certificates, MFA, multi-factor authentication, OIDC, enterprise security, Microsoft 365, Google Workspace, GitHub authentication" />
<meta name="author" content="Secuird" /> <meta name="author" content="%VITE_APP_NAME%" />
<meta name="robots" content="index, follow" /> <meta name="robots" content="index, follow" />
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="%VITE_FAVICON%" />
<link rel="apple-touch-icon" href="/gatehouse-logo.svg" /> <link rel="apple-touch-icon" href="%VITE_FAVICON%" />
<!-- Open Graph / Facebook --> <!-- Open Graph / Facebook -->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://securd.com/" /> <meta property="og:url" content="https://securd.com/" />
<meta property="og:title" content="Secuird — Enterprise Identity & Access Management" /> <meta property="og:title" content="%VITE_APP_NAME% — Enterprise Identity & Access Management" />
<meta property="og:description" content="Secuird unifies social logins, MFA, and SSH certificate management in one platform. Enable enterprise SSO without complex federation. Eliminate SSH key chaos with short-lived certificates." /> <meta property="og:description" content="%VITE_APP_NAME% unifies social logins, MFA, and SSH certificate management in one platform. Enable enterprise SSO without complex federation. Eliminate SSH key chaos with short-lived certificates." />
<meta property="og:image" content="/og-image.png" /> <meta property="og:image" content="/og-image.png" />
<meta property="og:site_name" content="Secuird" /> <meta property="og:site_name" content="%VITE_APP_NAME%" />
<meta property="og:locale" content="en_US" /> <meta property="og:locale" content="en_US" />
<!-- Twitter --> <!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content="https://securd.com/" /> <meta name="twitter:url" content="https://securd.com/" />
<meta name="twitter:title" content="Secuird — Enterprise Identity & Access Management" /> <meta name="twitter:title" content="%VITE_APP_NAME% — Enterprise Identity & Access Management" />
<meta name="twitter:description" content="Secuird unifies social logins, MFA, and SSH certificate management in one platform. Enable enterprise SSO without complex federation. Eliminate SSH key chaos with short-lived certificates." /> <meta name="twitter:description" content="%VITE_APP_NAME% unifies social logins, MFA, and SSH certificate management in one platform. Enable enterprise SSO without complex federation. Eliminate SSH key chaos with short-lived certificates." />
<meta name="twitter:image" content="/og-image.png" /> <meta name="twitter:image" content="/og-image.png" />
<meta name="twitter:site" content="@securd" /> <meta name="twitter:site" content="@securd" />
<!-- Theme color --> <!-- Theme color -->
<meta name="theme-color" content="#36b9a6" /> <meta name="theme-color" content="%VITE_THEME_COLOR%" />
<meta name="msapplication-TileColor" content="#36b9a6" /> <meta name="msapplication-TileColor" content="%VITE_THEME_COLOR%" />
<!-- Canonical URL --> <!-- Canonical URL -->
<link rel="canonical" href="https://securd.com/" /> <link rel="canonical" href="https://securd.com/" />
@@ -45,7 +45,7 @@
{ {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "SoftwareApplication", "@type": "SoftwareApplication",
"name": "Secuird", "name": "%VITE_APP_NAME%",
"applicationCategory": "SecurityApplication", "applicationCategory": "SecurityApplication",
"operatingSystem": "Web", "operatingSystem": "Web",
"description": "Enterprise identity and access management platform providing secure authentication, organization-level security policy, SSH certificate management, and OIDC-based Single Sign-On.", "description": "Enterprise identity and access management platform providing secure authentication, organization-level security policy, SSH certificate management, and OIDC-based Single Sign-On.",
@@ -74,7 +74,7 @@
{ {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "Organization", "@type": "Organization",
"name": "Secuird", "name": "%VITE_APP_NAME%",
"url": "https://securd.com", "url": "https://securd.com",
"logo": "https://securd.com/gatehouse-logo.svg", "logo": "https://securd.com/gatehouse-logo.svg",
"sameAs": [ "sameAs": [
+16
View File
@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<!-- Background - Dev theme color (Red #ef4444) -->
<rect width="24" height="24" rx="3" fill="#ef4444"/>
<!-- Left pillar -->
<path d="M4 4h3v16H4V4z" fill="#ffffff"/>
<!-- Right pillar -->
<path d="M17 4h3v16h-3V4z" fill="#ffffff"/>
<!-- Archway -->
<path d="M7 4h10v3H7V4z" fill="#ffffff" opacity="0.7"/>
<!-- Keyhole -->
<circle cx="12" cy="14" r="2" fill="#ffffff" opacity="0.5"/>
</svg>

After

Width:  |  Height:  |  Size: 496 B

+16
View File
@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<!-- Background - Preprod theme color (Purple #a855f7) -->
<rect width="24" height="24" rx="3" fill="#a855f7"/>
<!-- Left pillar -->
<path d="M4 4h3v16H4V4z" fill="#ffffff"/>
<!-- Right pillar -->
<path d="M17 4h3v16h-3V4z" fill="#ffffff"/>
<!-- Archway -->
<path d="M7 4h10v3H7V4z" fill="#ffffff" opacity="0.7"/>
<!-- Keyhole -->
<circle cx="12" cy="14" r="2" fill="#ffffff" opacity="0.5"/>
</svg>

After

Width:  |  Height:  |  Size: 503 B

+3 -2
View File
@@ -1,6 +1,7 @@
import { Link, Outlet, useLocation } from "react-router-dom"; import { Link, Outlet, useLocation } from "react-router-dom";
import { SecuirdLogo as GatehouseLogo } from "@/components/branding/SecuirdLogo"; import { SecuirdLogo as GatehouseLogo } from "@/components/branding/SecuirdLogo";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { config } from "@/config";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { import {
Shield, Shield,
@@ -35,7 +36,7 @@ return (
{/* Logo */} {/* Logo */}
<Link to="/" className="flex items-center gap-2.5"> <Link to="/" className="flex items-center gap-2.5">
<GatehouseLogo size="md" /> <GatehouseLogo size="md" />
<span className="text-xl font-semibold text-foreground tracking-tight">Secuird</span> <span className="text-xl font-semibold text-foreground tracking-tight">{config.app.name}</span>
</Link> </Link>
{/* Desktop Navigation */} {/* Desktop Navigation */}
@@ -130,7 +131,7 @@ return (
<div className="col-span-2 lg:col-span-1"> <div className="col-span-2 lg:col-span-1">
<Link to="/" className="flex items-center gap-2.5"> <Link to="/" className="flex items-center gap-2.5">
<GatehouseLogo size="sm" /> <GatehouseLogo size="sm" />
<span className="text-lg font-semibold text-foreground tracking-tight">Secuird</span> <span className="text-lg font-semibold text-foreground tracking-tight">{config.app.name}</span>
</Link> </Link>
<p className="mt-4 text-sm text-muted-foreground max-w-xs"> <p className="mt-4 text-sm text-muted-foreground max-w-xs">
Enterprise identity and access management. Secure by design, simple by choice. Enterprise identity and access management. Secure by design, simple by choice.
@@ -4,6 +4,7 @@ import { Shield, Smartphone, Fingerprint, AlertTriangle, CheckCircle, Loader2 }
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { useAuth } from '@/contexts/AuthContext'; import { useAuth } from '@/contexts/AuthContext';
import { config } from '@/config';
import { AddPasskeyWizard } from '@/components/security/AddPasskeyWizard'; import { AddPasskeyWizard } from '@/components/security/AddPasskeyWizard';
import { TotpEnrollmentWizard } from '@/components/security/TotpEnrollmentWizard'; import { TotpEnrollmentWizard } from '@/components/security/TotpEnrollmentWizard';
import { api } from '@/lib/api'; import { api } from '@/lib/api';
@@ -98,7 +99,7 @@ export default function MfaEnforcementLayout() {
<header className="h-14 border-b border-border bg-card flex items-center justify-between px-4 flex-shrink-0"> <header className="h-14 border-b border-border bg-card flex items-center justify-between px-4 flex-shrink-0">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Shield className="w-5 h-5 text-primary" /> <Shield className="w-5 h-5 text-primary" />
<span className="font-semibold text-foreground">Secuird</span> <span className="font-semibold text-foreground">{config.app.name}</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
+3 -2
View File
@@ -1,5 +1,6 @@
import { Outlet, Link } from "react-router-dom"; import { Outlet, Link } from "react-router-dom";
import { SecuirdLogo } from "@/components/branding/SecuirdLogo"; import { SecuirdLogo } from "@/components/branding/SecuirdLogo";
import { config } from "@/config";
export default function PublicLayout() { export default function PublicLayout() {
return ( return (
@@ -12,7 +13,7 @@ export default function PublicLayout() {
<div className="max-w-md mx-auto"> <div className="max-w-md mx-auto">
<Link to="/" className="flex items-center gap-2.5 justify-center"> <Link to="/" className="flex items-center gap-2.5 justify-center">
<SecuirdLogo size="md" /> <SecuirdLogo size="md" />
<span className="text-xl font-semibold text-foreground tracking-tight">Secuird</span> <span className="text-xl font-semibold text-foreground tracking-tight">{config.app.name}</span>
</Link> </Link>
</div> </div>
</header> </header>
@@ -28,7 +29,7 @@ export default function PublicLayout() {
<footer className="relative z-10 py-6 px-4"> <footer className="relative z-10 py-6 px-4">
<div className="max-w-md mx-auto text-center"> <div className="max-w-md mx-auto text-center">
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
© {new Date().getFullYear()} Secuird. Identity & Access. © {new Date().getFullYear()} {config.app.name}. Identity & Access.
</p> </p>
</div> </div>
</footer> </footer>
+3 -2
View File
@@ -23,6 +23,7 @@ import { SecuirdLogo } from "@/components/branding/SecuirdLogo";
import { NavLink } from "@/components/NavLink"; import { NavLink } from "@/components/NavLink";
import { useAuth } from "@/contexts/AuthContext"; import { useAuth } from "@/contexts/AuthContext";
import { useOrg } from "@/contexts/OrgContext"; import { useOrg } from "@/contexts/OrgContext";
import { config } from "@/config";
import { import {
Sidebar, Sidebar,
SidebarContent, SidebarContent,
@@ -104,7 +105,7 @@ export function AppSidebar() {
<SecuirdLogo size="sm" variant="light" /> <SecuirdLogo size="sm" variant="light" />
{!collapsed && ( {!collapsed && (
<span className="text-lg font-semibold text-sidebar-foreground tracking-tight"> <span className="text-lg font-semibold text-sidebar-foreground tracking-tight">
Secuird {config.app.name}
</span> </span>
)} )}
</div> </div>
@@ -221,7 +222,7 @@ export function AppSidebar() {
<SidebarFooter className="p-4 border-t border-sidebar-border"> <SidebarFooter className="p-4 border-t border-sidebar-border">
{!collapsed && ( {!collapsed && (
<div className="text-xs text-sidebar-muted"> <div className="text-xs text-sidebar-muted">
{import.meta.env.VITE_APP_VERSION ?? 'Secuird'} {import.meta.env.VITE_APP_VERSION ?? config.app.name}
</div> </div>
)} )}
</SidebarFooter> </SidebarFooter>
+63 -2
View File
@@ -4,6 +4,60 @@
// Base URL without /api/v1 suffix - used for CLI sign URL // Base URL without /api/v1 suffix - used for CLI sign URL
const BASE_URL = import.meta.env.VITE_API_BASE_URL || "http://192.168.64.7:8888"; const BASE_URL = import.meta.env.VITE_API_BASE_URL || "http://192.168.64.7:8888";
// Theme configuration from environment
export type ThemeName = "default" | "dev" | "preprod";
interface ThemeColors {
primary: string;
primaryForeground: string;
sidebarPrimary: string;
sidebarPrimaryForeground: string;
ring: string;
}
const THEME_COLORS: Record<ThemeName, ThemeColors> = {
default: {
primary: "173 65% 36%", // teal
primaryForeground: "0 0% 100%",
sidebarPrimary: "173 65% 36%",
sidebarPrimaryForeground: "0 0% 100%",
ring: "173 65% 36%",
},
dev: {
primary: "0 72% 51%", // red
primaryForeground: "0 0% 100%",
sidebarPrimary: "0 72% 51%",
sidebarPrimaryForeground: "0 0% 100%",
ring: "0 72% 51%",
},
preprod: {
primary: "270 70% 55%", // purple
primaryForeground: "0 0% 100%",
sidebarPrimary: "270 70% 55%",
sidebarPrimaryForeground: "0 0% 100%",
ring: "270 70% 55%",
},
};
const THEME_DEFAULTS: Record<ThemeName, { appName: string; favicon: string }> = {
default: {
appName: "Secuird",
favicon: "/favicon.svg",
},
dev: {
appName: "Secuird Dev",
favicon: "/favicon-dev.svg",
},
preprod: {
appName: "Secuird Staging",
favicon: "/favicon-staging.svg",
},
};
const themeName = (import.meta.env.VITE_THEME || "default") as ThemeName;
const themeColors = THEME_COLORS[themeName] || THEME_COLORS.default;
const themeDefaults = THEME_DEFAULTS[themeName] || THEME_DEFAULTS.default;
export const config = { export const config = {
// API Configuration // API Configuration
api: { api: {
@@ -13,10 +67,17 @@ export const config = {
// Sign URL for CLI (same as base URL, no /api/v1) // Sign URL for CLI (same as base URL, no /api/v1)
signUrl: BASE_URL, signUrl: BASE_URL,
// App metadata // App metadata - can be overridden per-theme or via env
app: { app: {
name: "Secuird", name: import.meta.env.VITE_APP_NAME || themeDefaults.appName,
description: "Identity & Access Platform", description: "Identity & Access Platform",
favicon: import.meta.env.VITE_FAVICON || themeDefaults.favicon,
},
// Theme configuration for CSS variable injection
theme: {
name: themeName,
colors: themeColors,
}, },
// Feature flags // Feature flags
+15 -13
View File
@@ -1,5 +1,7 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
@import './styles/theme-vars.css';
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@@ -7,7 +9,7 @@
/* Secuird Design System - Enterprise Identity & Access Platform /* Secuird Design System - Enterprise Identity & Access Platform
Authoritative, infrastructure-grade aesthetic with slate/charcoal/muted blue palette Authoritative, infrastructure-grade aesthetic with slate/charcoal/muted blue palette
Colors are HSL for theming flexibility Colors are HSL for theming flexibility
*/ */
@layer base { @layer base {
:root { :root {
@@ -21,9 +23,9 @@
--popover: 0 0% 100%; --popover: 0 0% 100%;
--popover-foreground: 222 47% 9%; --popover-foreground: 222 47% 9%;
/* Primary — teal, fully saturated, dark enough to read on white */ /* Primary — theme-configurable (teal default, red for dev, purple for staging) */
--primary: 173 65% 36%; --primary: var(--theme-primary);
--primary-foreground: 0 0% 100%; --primary-foreground: var(--theme-primary-foreground);
/* Secondary — cool blue-gray, clearly darker than bg */ /* Secondary — cool blue-gray, clearly darker than bg */
--secondary: 216 20% 91%; --secondary: 216 20% 91%;
@@ -33,9 +35,9 @@
--muted: 216 18% 88%; --muted: 216 18% 88%;
--muted-foreground: 222 18% 42%; --muted-foreground: 222 18% 42%;
/* Accent — same teal as primary */ /* Accent — same as primary */
--accent: 173 65% 36%; --accent: var(--theme-primary);
--accent-foreground: 0 0% 100%; --accent-foreground: var(--theme-primary-foreground);
/* Semantic */ /* Semantic */
--destructive: 0 72% 48%; --destructive: 0 72% 48%;
@@ -53,24 +55,24 @@
/* UI chrome */ /* UI chrome */
--border: 216 18% 84%; /* clearly visible on white card */ --border: 216 18% 84%; /* clearly visible on white card */
--input: 216 18% 92%; --input: 216 18% 92%;
--ring: 173 65% 36%; --ring: var(--theme-ring);
--radius: 0.5rem; --radius: 0.5rem;
/* Sidebar */ /* Sidebar */
--sidebar-background: 222 30% 95%; --sidebar-background: 222 30% 95%;
--sidebar-foreground: 222 47% 18%; --sidebar-foreground: 222 47% 18%;
--sidebar-primary: 173 65% 36%; --sidebar-primary: var(--theme-sidebar-primary);
--sidebar-primary-foreground: 0 0% 100%; --sidebar-primary-foreground: var(--theme-sidebar-primary-foreground);
--sidebar-accent: 216 20% 88%; --sidebar-accent: 216 20% 88%;
--sidebar-accent-foreground: 222 47% 9%; --sidebar-accent-foreground: 222 47% 9%;
--sidebar-border: 216 18% 84%; --sidebar-border: 216 18% 84%;
--sidebar-ring: 173 65% 36%; --sidebar-ring: var(--theme-ring);
--sidebar-muted: 222 20% 48%; --sidebar-muted: 222 20% 48%;
/* Gradients */ /* Gradients */
--gradient-brand: linear-gradient(135deg, hsl(173 65% 36%), hsl(173 65% 28%)); --gradient-brand: linear-gradient(135deg, hsl(var(--theme-primary)), hsl(var(--theme-primary) / 0.8));
--gradient-accent: linear-gradient(135deg, hsl(173 65% 36%), hsl(173 65% 28%)); --gradient-accent: linear-gradient(135deg, hsl(var(--theme-primary)), hsl(var(--theme-primary) / 0.8));
--gradient-subtle: linear-gradient(135deg, hsl(216 28% 97%), hsl(216 18% 93%)); --gradient-subtle: linear-gradient(135deg, hsl(216 28% 97%), hsl(216 18% 93%));
/* Shadows — stronger alpha so cards lift off the bg */ /* Shadows — stronger alpha so cards lift off the bg */
+8
View File
@@ -0,0 +1,8 @@
/* Default theme - Secuird (Teal) */
@theme {
--color-primary: hsl(173 65% 36%);
--color-primary-foreground: hsl(0 0% 100%);
--color-sidebar-primary: hsl(173 65% 36%);
--color-sidebar-primary-foreground: hsl(0 0% 100%);
--color-ring: hsl(173 65% 36%);
}
+8
View File
@@ -0,0 +1,8 @@
/* Dev theme - Red */
@theme {
--color-primary: hsl(0 72% 51%);
--color-primary-foreground: hsl(0 0% 100%);
--color-sidebar-primary: hsl(0 72% 51%);
--color-sidebar-primary-foreground: hsl(0 0% 100%);
--color-ring: hsl(0 72% 51%);
}
+8
View File
@@ -0,0 +1,8 @@
/* Preprod theme - Purple */
@theme {
--color-primary: hsl(270 70% 55%);
--color-primary-foreground: hsl(0 0% 100%);
--color-sidebar-primary: hsl(270 70% 55%);
--color-sidebar-primary-foreground: hsl(0 0% 100%);
--color-ring: hsl(270 70% 55%);
}
+9
View File
@@ -0,0 +1,9 @@
/* Theme variables - values injected by Vite at build time */
:root {
--theme-primary: %%VITE_THEME_PRIMARY%%;
--theme-primary-foreground: 0 0% 100%;
--theme-sidebar-primary: %%VITE_THEME_SIDEBAR_PRIMARY%%;
--theme-sidebar-primary-foreground: 0 0% 100%;
--theme-ring: %%VITE_THEME_RING%%;
--theme-color: %%VITE_THEME_COLOR%%;
}
+29
View File
@@ -0,0 +1,29 @@
/* Theme configuration - injected at runtime from config */
const themeConfig = {
default: {
primary: "173 65% 36%",
sidebarPrimary: "173 65% 36%",
ring: "173 65% 36%",
},
dev: {
primary: "0 72% 51%",
sidebarPrimary: "0 72% 51%",
ring: "0 72% 51%",
},
preprod: {
primary: "270 70% 55%",
sidebarPrimary: "270 70% 55%",
ring: "270 70% 55%",
},
};
const themeName = import.meta.env.VITE_THEME || "default";
const theme = themeConfig[themeName as keyof typeof themeConfig] || themeConfig.default;
const root = document.documentElement;
root.style.setProperty("--primary", theme.primary);
root.style.setProperty("--primary-foreground", "0 0% 100%");
root.style.setProperty("--sidebar-primary", theme.sidebarPrimary);
root.style.setProperty("--sidebar-primary-foreground", "0 0% 100%");
root.style.setProperty("--ring", theme.ring);
+45 -1
View File
@@ -6,6 +6,19 @@ import { componentTagger } from "lovable-tagger";
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), ""); const env = loadEnv(mode, process.cwd(), "");
const themeName = env.VITE_THEME || "default";
const themeColors: Record<string, { primary: string; sidebarPrimary: string; ring: string }> = {
default: { primary: "173 65% 36%", sidebarPrimary: "173 65% 36%", ring: "173 65% 36%" },
dev: { primary: "0 72% 51%", sidebarPrimary: "0 72% 51%", ring: "0 72% 51%" },
preprod: { primary: "270 70% 55%", sidebarPrimary: "270 70% 55%", ring: "270 70% 55%" },
};
const colors = themeColors[themeName] || themeColors.default;
const appName = env.VITE_APP_NAME || (themeName === "dev" ? "Secuird Dev" : themeName === "preprod" ? "Secuird Staging" : "Secuird");
const favicon = env.VITE_FAVICON || (themeName === "dev" ? "/favicon-dev.svg" : themeName === "preprod" ? "/favicon-staging.svg" : "/favicon.svg");
const themeColor = `hsl(${colors.primary})`;
return { return {
server: { server: {
host: "::", host: "::",
@@ -15,7 +28,38 @@ export default defineConfig(({ mode }) => {
"gatehouse-ui.hawkvelt.tech", "gatehouse-ui.hawkvelt.tech",
], ],
}, },
plugins: [react(), mode === "development" && componentTagger()].filter(Boolean), plugins: [
react(),
mode === "development" && componentTagger(),
{
name: "vite-plugin-theme-injection",
transformIndexHtml: {
order: "pre",
handler: (html) => {
return html
.replace(/%VITE_APP_NAME%/g, appName)
.replace(/%VITE_FAVICON%/g, favicon)
.replace(/%VITE_THEME_COLOR%/g, themeColor);
},
},
},
],
css: {
preprocessorOptions: {
css: {
additionalData: `
:root {
--theme-primary: ${colors.primary};
--theme-primary-foreground: 0 0% 100%;
--theme-sidebar-primary: ${colors.sidebarPrimary};
--theme-sidebar-primary-foreground: 0 0% 100%;
--theme-ring: ${colors.ring};
--theme-color: hsl(${colors.primary});
}
`,
},
},
},
resolve: { resolve: {
alias: { alias: {
"@": path.resolve(__dirname, "./src"), "@": path.resolve(__dirname, "./src"),