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 (
- {/* Abstract gate - two pillars with archway */} - - - - {/* Keyhole/entry indicator */} - + + + +
); 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"),