diff --git a/src/components/branding/SecuirdLogo.tsx b/src/components/branding/SecuirdLogo.tsx
index f8fd0dc..a77c51c 100644
--- a/src/components/branding/SecuirdLogo.tsx
+++ b/src/components/branding/SecuirdLogo.tsx
@@ -7,12 +7,8 @@ interface SecuirdLogoProps {
}
/**
- * Secuird Logo - Abstract gate/doorway mark
- * Represents controlled entry and policy enforcement
- * Two vertical pillars forming a gateway with negative space
- *
- * Uses inline styles for theme colors to ensure they work correctly
- * across all theme configurations (default, dev, preprod)
+ * Secuird Logo — Abstract gate/doorway mark
+ * Uses CSS variables so the color automatically adapts to the active theme.
*/
export function SecuirdLogo({
size = "md",
@@ -25,61 +21,38 @@ export function SecuirdLogo({
lg: "w-12 h-12",
};
- // Use inline styles for theme colors to ensure they work at runtime
- const getBgStyle = () => {
- if (variant === "light") {
- return {
- backgroundColor: "hsl(var(--theme-sidebar-primary, 173 65% 36%))",
- color: "hsl(var(--theme-sidebar-primary-foreground, 0 0% 100%))"
- };
- }
- return {
- backgroundColor: "hsl(var(--theme-primary, 173 65% 36%))",
- color: "hsl(var(--theme-primary-foreground, 0 0% 100%))"
- };
+ // Tailwind classes resolve to hsl(var(--primary)) / hsl(var(--sidebar-primary))
+ // which pick up the injected --theme-primary / --theme-sidebar-primary values.
+ const bgClasses = {
+ default: "bg-primary",
+ light: "bg-sidebar-primary",
};
-
- const bgStyle = getBgStyle();
+
+ const iconColor = variant === "light"
+ ? "text-sidebar-primary-foreground"
+ : "text-primary-foreground";
return (
);
diff --git a/src/components/branding/ThemeIndicator.tsx b/src/components/branding/ThemeIndicator.tsx
index 8b600b7..8ab13a1 100644
--- a/src/components/branding/ThemeIndicator.tsx
+++ b/src/components/branding/ThemeIndicator.tsx
@@ -6,37 +6,26 @@ interface ThemeIndicatorProps {
}
/**
- * Visual theme indicator - shows environment name and color
- * Appears as a banner in dev/preprod modes
+ * Visual theme indicator — solid-colored banner for dev/preprod environments.
*/
-export function ThemeIndicator({ showBanner = true, className }: ThemeIndicatorProps) {
+export function ThemeIndicator({ showBanner = true }: ThemeIndicatorProps) {
const theme = config.theme.name;
const appName = config.app.name;
-
- // Only show for non-default themes
- if (theme === "default") {
- return null;
- }
-
- const themeLabels: Record = {
+
+ if (theme === "default") return null;
+
+ const labels: Record = {
dev: "Development Environment",
preprod: "Staging / Pre-Production",
};
-
- const themeLabel = themeLabels[theme] || theme;
-
+
if (showBanner) {
- return (
-
- {themeLabel} — {appName}
-
- );
+ return {labels[theme] || theme} — {appName}
;
}
-
+
return (
-
-
- {themeLabel}
+
+ {labels[theme] || theme}
);
-}
\ No newline at end of file
+}
diff --git a/src/index.css b/src/index.css
index 3c0d577..7404a0d 100644
--- a/src/index.css
+++ b/src/index.css
@@ -12,16 +12,16 @@
@layer base {
:root {
/* Core palette - Light blue-gray with teal accent */
- --background: 216 22% 94%; /* cool blue-gray — cards lift clearly off this */
- --foreground: 222 47% 9%; /* near-black navy */
+ --background: 216 22% 94%;
+ --foreground: 222 47% 9%;
- --card: 0 0% 100%; /* pure white — 6% lightness gap over bg */
+ --card: 0 0% 100%;
--card-foreground: 222 47% 9%;
--popover: 0 0% 100%;
--popover-foreground: 222 47% 9%;
- /* Primary — theme-configurable (teal default, red for dev, purple for staging) */
+ /* Primary — theme-configurable */
--primary: var(--theme-primary);
--primary-foreground: var(--theme-primary-foreground);
@@ -29,7 +29,7 @@
--secondary: 216 20% 91%;
--secondary-foreground: 222 47% 18%;
- /* Muted — noticeably darker than secondary, used for section bg */
+ /* Muted */
--muted: 216 18% 88%;
--muted-foreground: 222 18% 42%;
@@ -51,7 +51,7 @@
--info-foreground: 0 0% 100%;
/* UI chrome */
- --border: 216 18% 84%; /* clearly visible on white card */
+ --border: 216 18% 84%;
--input: 216 18% 92%;
--ring: var(--theme-ring);
@@ -68,12 +68,7 @@
--sidebar-ring: var(--theme-ring);
--sidebar-muted: 222 20% 48%;
- /* Gradients */
- --gradient-brand: linear-gradient(135deg, hsl(var(--theme-primary)), hsl(var(--theme-primary) / 0.8));
- --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%));
-
- /* Shadows — stronger alpha so cards lift off the bg */
+ /* Shadows */
--shadow-sm: 0 1px 2px 0 hsl(222 47% 9% / 0.10);
--shadow-md: 0 4px 6px -1px hsl(222 47% 9% / 0.14), 0 2px 4px -2px hsl(222 47% 9% / 0.10);
--shadow-lg: 0 10px 15px -3px hsl(222 47% 9% / 0.14), 0 4px 6px -4px hsl(222 47% 9% / 0.10);
@@ -151,40 +146,34 @@
}
@layer components {
- /* Theme-specific branding enhancements */
-
- /* Marketing header - theme colored */
- .marketing-header {
- @apply border-b transition-colors duration-300;
- background-color: hsl(var(--theme-primary) / 0.03);
- border-color: hsl(var(--theme-primary) / 0.2);
+ /* Environment banner — solid theme color */
+ .env-banner {
+ @apply fixed top-0 left-0 right-0 z-[100] px-3 py-1.5 text-center text-xs font-bold uppercase tracking-wider;
+ background-color: hsl(var(--theme-primary));
+ color: hsl(var(--theme-primary-foreground));
+ box-shadow: 0 2px 4px 0 hsl(var(--theme-primary) / 0.3);
}
-
- /* Theme indicator badge */
- .theme-badge {
- @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
- background-color: hsl(var(--theme-primary) / 0.15);
+
+ /* Sidebar header — solid theme color background */
+ .sidebar-header-themed {
+ background-color: hsl(var(--theme-sidebar-primary));
+ color: hsl(var(--theme-sidebar-primary-foreground));
+ border-bottom: 1px solid hsl(var(--theme-sidebar-primary) / 0.3);
+ }
+
+ /* Top bar — theme-colored bottom border + subtle tint */
+ .top-bar-themed {
+ border-bottom: 2px solid hsl(var(--theme-primary));
+ background-color: hsl(var(--theme-primary) / 0.04);
+ }
+
+ /* Active nav item — theme color background */
+ .active-nav-themed {
+ background-color: hsl(var(--theme-primary) / 0.12);
color: hsl(var(--theme-primary));
+ border-radius: var(--radius);
}
-
- /* Theme-colored sidebar header */
- .sidebar-header-theme {
- background: linear-gradient(135deg,
- hsl(var(--theme-sidebar-primary)) 0%,
- hsl(var(--theme-sidebar-primary) / 0.85) 100%
- );
- }
-
- /* Theme-colored buttons */
- .btn-theme {
- @apply bg-primary text-primary-foreground hover:opacity-90 transition-opacity;
- }
-
- /* Theme ring focus */
- .focus-ring:focus-visible {
- @apply outline-none ring-2 ring-ring ring-offset-2 ring-offset-background;
- }
-
+
/* Auth card for public pages */
.auth-card {
@apply bg-card rounded-xl p-8 shadow-card border border-border;
@@ -236,33 +225,9 @@
.page-description {
@apply text-muted-foreground mt-1;
}
-
- /* Dev/Staging environment banner */
- .env-banner {
- @apply fixed top-0 left-0 right-0 z-50 px-3 py-1 text-center text-xs font-bold uppercase tracking-wider;
- background: linear-gradient(90deg,
- hsl(var(--theme-primary)) 0%,
- hsl(var(--theme-primary) / 0.8) 50%,
- hsl(var(--theme-primary)) 100%
- );
- color: hsl(var(--theme-primary-foreground));
- }
-
- /* Additional env indicator on page */
- .env-indicator {
- @apply inline-block w-2 h-2 rounded-full ml-2;
- background-color: hsl(var(--theme-primary));
- animation: pulse 2s ease-in-out infinite;
- }
-
- @keyframes pulse {
- 0%, 100% { opacity: 1; }
- 50% { opacity: 0.5; }
- }
}
@layer utilities {
- /* Animation utilities */
.animate-fade-in {
animation: fadeIn 0.3s ease-out;
}
@@ -272,22 +237,12 @@
}
@keyframes fadeIn {
- from {
- opacity: 0;
- }
- to {
- opacity: 1;
- }
+ from { opacity: 0; }
+ to { opacity: 1; }
}
@keyframes slideIn {
- from {
- opacity: 0;
- transform: translateY(-8px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
+ from { opacity: 0; transform: translateY(-8px); }
+ to { opacity: 1; transform: translateY(0); }
}
}
diff --git a/vite.config.ts b/vite.config.ts
index 5fee80e..f5c40e5 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -17,9 +17,21 @@ export default defineConfig(({ mode }) => {
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})`;
+ const themeColorHsl = `hsl(${colors.primary})`;
+
+ // Build a CSS block that injects directly into before any stylesheets load.
+ const themeStyles = `
+`;
-
return {
server: {
host: "::",
@@ -37,30 +49,18 @@ export default defineConfig(({ mode }) => {
transformIndexHtml: {
order: "pre",
handler: (html) => {
- return html
+ const headClose = html.indexOf("");
+ const withStyle = headClose > -1
+ ? html.slice(0, headClose) + themeStyles + "\n" + html.slice(headClose)
+ : html;
+ return withStyle
.replace(/%VITE_APP_NAME%/g, appName)
.replace(/%VITE_FAVICON%/g, favicon)
- .replace(/%VITE_THEME_COLOR%/g, themeColor);
+ .replace(/%VITE_THEME_COLOR%/g, themeColorHsl);
},
},
},
],
- 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: {
alias: {
"@": path.resolve(__dirname, "./src"),