fix: theme colors not applying due to incorrect CSS injection method
This commit is contained in:
@@ -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 (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-lg flex items-center justify-center flex-shrink-0",
|
||||
sizeClasses[size],
|
||||
bgClasses[variant],
|
||||
className
|
||||
)}
|
||||
style={bgStyle}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
className={cn(
|
||||
iconColor,
|
||||
size === "sm" ? "w-4 h-4" : size === "md" ? "w-5 h-5" : "w-6 h-6"
|
||||
)}
|
||||
style={{ color: bgStyle.color }}
|
||||
>
|
||||
{/* Abstract gate - two pillars with archway */}
|
||||
<path
|
||||
d="M4 4h3v16H4V4z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M17 4h3v16h-3V4z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M7 4h10v3H7V4z"
|
||||
fill="currentColor"
|
||||
opacity="0.7"
|
||||
/>
|
||||
{/* Keyhole/entry indicator */}
|
||||
<circle
|
||||
cx="12"
|
||||
cy="14"
|
||||
r="2"
|
||||
fill="currentColor"
|
||||
opacity="0.5"
|
||||
/>
|
||||
<path d="M4 4h3v16H4V4z" fill="currentColor" />
|
||||
<path d="M17 4h3v16h-3V4z" fill="currentColor" />
|
||||
<path d="M7 4h10v3H7V4z" fill="currentColor" opacity="0.7" />
|
||||
<circle cx="12" cy="14" r="2" fill="currentColor" opacity="0.5" />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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<string, string> = {
|
||||
|
||||
if (theme === "default") return null;
|
||||
|
||||
const labels: Record<string, string> = {
|
||||
dev: "Development Environment",
|
||||
preprod: "Staging / Pre-Production",
|
||||
};
|
||||
|
||||
const themeLabel = themeLabels[theme] || theme;
|
||||
|
||||
|
||||
if (showBanner) {
|
||||
return (
|
||||
<div className="env-banner">
|
||||
{themeLabel} — {appName}
|
||||
</div>
|
||||
);
|
||||
return <div className="env-banner">{labels[theme] || theme} — {appName}</div>;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<span className={`theme-badge ${className || ""}`}>
|
||||
<span className="env-indicator" />
|
||||
{themeLabel}
|
||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary/15 text-primary">
|
||||
{labels[theme] || theme}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-81
@@ -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); }
|
||||
}
|
||||
}
|
||||
|
||||
+20
-20
@@ -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 <head> before any stylesheets load.
|
||||
const themeStyles = `
|
||||
<style data-theme="${themeName}">
|
||||
: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: ${themeColorHsl};
|
||||
}
|
||||
</style>`;
|
||||
|
||||
|
||||
return {
|
||||
server: {
|
||||
host: "::",
|
||||
@@ -37,30 +49,18 @@ export default defineConfig(({ mode }) => {
|
||||
transformIndexHtml: {
|
||||
order: "pre",
|
||||
handler: (html) => {
|
||||
return html
|
||||
const headClose = html.indexOf("</head>");
|
||||
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"),
|
||||
|
||||
Reference in New Issue
Block a user