diff --git a/app/(main)/[locale]/login/page.tsx b/app/(main)/[locale]/login/page.tsx
index 6cdc3838..5160a11f 100644
--- a/app/(main)/[locale]/login/page.tsx
+++ b/app/(main)/[locale]/login/page.tsx
@@ -133,9 +133,16 @@ export default function LoginPage() {
const isMobileHandoff = Boolean(mobileRedirectUri);
const { login, loginDemo, isLoading, error, clearError, isAuthenticated } = useAuthStore();
const { theme, setTheme, initializeTheme } = useThemeStore(useShallow((s) => ({ theme: s.theme, setTheme: s.setTheme, initializeTheme: s.initializeTheme })));
- const { appName, jmapServerUrl: configuredServerUrl, oauthEnabled, oauthOnly, oauthClientId: globalOauthClientId, oauthIssuerUrl: globalOauthIssuerUrl, oauthScopes, rememberMeEnabled, devMode, demoMode, loginLogoLightUrl, loginLogoDarkUrl, loginCompanyName, loginImprintUrl, loginPrivacyPolicyUrl, loginWebsiteUrl, isLoading: configLoading, error: configError, autoSsoEnabled, embeddedMode: _embeddedMode, allowCustomJmapEndpoint, jmapServers, jmapServerAutoPickByDomain } = useConfig();
+ const { appName, jmapServerUrl: configuredServerUrl, oauthEnabled, oauthOnly, oauthClientId: globalOauthClientId, oauthIssuerUrl: globalOauthIssuerUrl, oauthScopes, rememberMeEnabled, devMode, demoMode, loginLogoLightUrl, loginLogoDarkUrl, loginCompanyName, loginImprintUrl, loginPrivacyPolicyUrl, loginWebsiteUrl, loginLogoMaxHeight, loginLogoMaxWidth, loginShowHeading, loginShowSubtitle, isLoading: configLoading, error: configError, autoSsoEnabled, embeddedMode: _embeddedMode, allowCustomJmapEndpoint, jmapServers, jmapServerAutoPickByDomain } = useConfig();
const resolvedTheme = useThemeStore((s) => s.resolvedTheme);
+ // Login logo sizing: when a max height/width is configured, drop the fixed
+ // 64×64 box so the logo (e.g. a wide wordmark) can render at its true size.
+ const hasLogoSize = Boolean(loginLogoMaxHeight || loginLogoMaxWidth);
+ const loginLogoStyle = hasLogoSize
+ ? { maxHeight: loginLogoMaxHeight || undefined, maxWidth: loginLogoMaxWidth || undefined }
+ : undefined;
+
const [formData, setFormData] = useState({
username: "",
password: "",
@@ -881,19 +888,24 @@ export default function LoginPage() {
{/* Header section with logo */}
-
+
-
- {isAddAccountMode ? t("add_account_title") : appName}
-
-
- {isAddAccountMode ? t("add_account_subtitle") : (t("title") !== appName ? t("title") : "Sign in to your account")}
-
+ {loginShowHeading && (
+
+ {isAddAccountMode ? t("add_account_title") : appName}
+
+ )}
+ {loginShowSubtitle && (
+
+ {isAddAccountMode ? t("add_account_subtitle") : (t("title") !== appName ? t("title") : "Sign in to your account")}
+
+ )}
{/* Form section */}
diff --git a/app/api/config/route.ts b/app/api/config/route.ts
index d0c79255..d9a7efd7 100644
--- a/app/api/config/route.ts
+++ b/app/api/config/route.ts
@@ -74,6 +74,10 @@ export async function GET(request: NextRequest) {
loginImprintUrl: branded
('loginImprintUrl', ''),
loginPrivacyPolicyUrl: branded('loginPrivacyPolicyUrl', ''),
loginWebsiteUrl: branded('loginWebsiteUrl', ''),
+ loginLogoMaxHeight: configManager.get('loginLogoMaxHeight', ''),
+ loginLogoMaxWidth: configManager.get('loginLogoMaxWidth', ''),
+ loginShowHeading: configManager.get('loginShowHeading', true),
+ loginShowSubtitle: configManager.get('loginShowSubtitle', true),
demoMode: configManager.get('demoMode', false),
allowCustomJmapEndpoint: configManager.get('allowCustomJmapEndpoint', false),
jmapServers: redactJmapServers(parseJmapServers(configManager.get('jmapServers', []))),
diff --git a/hooks/use-config.ts b/hooks/use-config.ts
index db30f363..ccb52394 100644
--- a/hooks/use-config.ts
+++ b/hooks/use-config.ts
@@ -26,6 +26,10 @@ interface ConfigData {
loginImprintUrl: string;
loginPrivacyPolicyUrl: string;
loginWebsiteUrl: string;
+ loginLogoMaxHeight: string;
+ loginLogoMaxWidth: string;
+ loginShowHeading: boolean;
+ loginShowSubtitle: boolean;
demoMode: boolean;
autoSsoEnabled: boolean;
allowCustomJmapEndpoint: boolean;
@@ -105,6 +109,10 @@ export function useConfig(): AppConfig {
loginImprintUrl: configCache?.loginImprintUrl || '',
loginPrivacyPolicyUrl: configCache?.loginPrivacyPolicyUrl || '',
loginWebsiteUrl: configCache?.loginWebsiteUrl || '',
+ loginLogoMaxHeight: configCache?.loginLogoMaxHeight || '',
+ loginLogoMaxWidth: configCache?.loginLogoMaxWidth || '',
+ loginShowHeading: configCache?.loginShowHeading ?? true,
+ loginShowSubtitle: configCache?.loginShowSubtitle ?? true,
demoMode: configCache?.demoMode || false,
autoSsoEnabled: configCache?.autoSsoEnabled || false,
allowCustomJmapEndpoint: configCache?.allowCustomJmapEndpoint || false,
@@ -140,6 +148,10 @@ export function useConfig(): AppConfig {
loginImprintUrl: configCache.loginImprintUrl,
loginPrivacyPolicyUrl: configCache.loginPrivacyPolicyUrl,
loginWebsiteUrl: configCache.loginWebsiteUrl,
+ loginLogoMaxHeight: configCache.loginLogoMaxHeight,
+ loginLogoMaxWidth: configCache.loginLogoMaxWidth,
+ loginShowHeading: configCache.loginShowHeading,
+ loginShowSubtitle: configCache.loginShowSubtitle,
demoMode: configCache.demoMode,
autoSsoEnabled: configCache.autoSsoEnabled,
allowCustomJmapEndpoint: configCache.allowCustomJmapEndpoint,
@@ -176,6 +188,10 @@ export function useConfig(): AppConfig {
loginImprintUrl: data.loginImprintUrl,
loginPrivacyPolicyUrl: data.loginPrivacyPolicyUrl,
loginWebsiteUrl: data.loginWebsiteUrl,
+ loginLogoMaxHeight: data.loginLogoMaxHeight,
+ loginLogoMaxWidth: data.loginLogoMaxWidth,
+ loginShowHeading: data.loginShowHeading,
+ loginShowSubtitle: data.loginShowSubtitle,
demoMode: data.demoMode,
autoSsoEnabled: data.autoSsoEnabled,
allowCustomJmapEndpoint: data.allowCustomJmapEndpoint,
diff --git a/lib/admin/types.ts b/lib/admin/types.ts
index 1a415502..57c9aa61 100644
--- a/lib/admin/types.ts
+++ b/lib/admin/types.ts
@@ -166,6 +166,15 @@ export const CONFIG_ENV_MAP: Record