feat: add configurable logo to login page with light/dark mode support

- Display Bulwark logo on the login page, with theme-aware switching
  between light and dark variants based on the resolved theme
- Add LOGIN_LOGO_LIGHT_URL and LOGIN_LOGO_DARK_URL env variables to
  allow custom logo overrides (defaults to bundled Bulwark branding)
- Expose logo config through /api/config endpoint and useConfig hook
- Add logo URL prompts to the setup.sh installer branding step
- Rename branding assets to use underscores instead of spaces
This commit is contained in:
Linus Rath
2026-03-13 19:45:09 +01:00
parent 56f4195ab5
commit 2fe1b01d4d
17 changed files with 47 additions and 8 deletions
+9 -4
View File
@@ -28,8 +28,9 @@ export default function LoginPage() {
const t = useTranslations("login");
const params = useParams();
const { login, isLoading, error, clearError, isAuthenticated } = useAuthStore();
const { theme, setTheme, initializeTheme } = useThemeStore();
const { appName, jmapServerUrl: serverUrl, oauthEnabled, oauthOnly, oauthClientId, oauthIssuerUrl, rememberMeEnabled, devMode, loginCompanyName, loginImprintUrl, loginPrivacyPolicyUrl, loginWebsiteUrl, isLoading: configLoading, error: configError } = useConfig();
const { theme, setTheme, initializeTheme } = useThemeStore((s) => ({ theme: s.theme, setTheme: s.setTheme, initializeTheme: s.initializeTheme }));
const { appName, jmapServerUrl: serverUrl, oauthEnabled, oauthOnly, oauthClientId, oauthIssuerUrl, rememberMeEnabled, devMode, loginLogoLightUrl, loginLogoDarkUrl, loginCompanyName, loginImprintUrl, loginPrivacyPolicyUrl, loginWebsiteUrl, isLoading: configLoading, error: configError } = useConfig();
const resolvedTheme = useThemeStore((s) => s.resolvedTheme);
const [formData, setFormData] = useState({
username: "",
@@ -416,8 +417,12 @@ export default function LoginPage() {
<div className="rounded-2xl border border-border/60 bg-background/80 backdrop-blur-sm shadow-xl shadow-black/5 dark:shadow-black/20 overflow-hidden">
{/* Header section with logo */}
<div className="px-8 pt-10 pb-6 text-center">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-gradient-to-br from-primary to-primary/80 mb-5 shadow-lg shadow-primary/25">
<Mail className="w-8 h-8 text-primary-foreground" />
<div className="inline-flex items-center justify-center w-16 h-16 mb-5">
<img
src={resolvedTheme === 'dark' ? loginLogoDarkUrl : loginLogoLightUrl}
alt={appName}
className="max-w-16 max-h-16 object-contain"
/>
</div>
<h1 className="text-2xl font-semibold text-foreground tracking-tight">
{appName}
+2
View File
@@ -26,6 +26,8 @@ export async function GET() {
settingsSyncEnabled: process.env.SETTINGS_SYNC_ENABLED === 'true' && !!process.env.SESSION_SECRET,
stalwartFeaturesEnabled: process.env.STALWART_FEATURES !== 'false',
devMode: process.env.DEV_MOCK_JMAP === 'true',
loginLogoLightUrl: process.env.LOGIN_LOGO_LIGHT_URL || '/branding/Bulwark_Logo_Color.svg',
loginLogoDarkUrl: process.env.LOGIN_LOGO_DARK_URL || '/branding/Bulwark_Logo_White.svg',
loginCompanyName: process.env.LOGIN_COMPANY_NAME || '',
loginImprintUrl: process.env.LOGIN_IMPRINT_URL || '',
loginPrivacyPolicyUrl: process.env.LOGIN_PRIVACY_POLICY_URL || '',