diff --git a/brand/README.md b/brand/README.md
index 2a3feb67d..7838b5ee0 100644
--- a/brand/README.md
+++ b/brand/README.md
@@ -21,13 +21,16 @@ are cached for five minutes, so runtime logo changes can take that long to appea
## Runtime browser favicons
-In **Settings → Branding**, upload **Favicon for light mode** and **Favicon for
-dark mode** independently. Each accepts SVG, PNG, or ICO up to 100 KiB. Selection
+In **Settings → Branding**, upload three independent icons: **Default favicon**,
+**Light mode favicon**, and **Dark mode favicon**. Each accepts SVG, PNG, or ICO
+up to 100 KiB. Use an icon with a background for the default and transparent
+light/dark overrides if desired; uploads are served unchanged. Override selection
follows the browser/OS `prefers-color-scheme`, not the app's theme setting, and
changes without reloading the page.
-The branding keys are `faviconLight` and `faviconDark`. A missing variant uses
-the existing `favicon` (the **Favicon (fallback)** slot), then the bundled mark.
+The branding keys are `favicon` (default), `faviconLight`, and `faviconDark`.
+Clients without theme selection and schemes without an override use the default;
+if no default is uploaded, they use the bundled mark.
The same keys travel with branding ZIP exports/imports and can be set through
the branding CLI or XRPC. No custom frontend build is needed for uploads.
diff --git a/js/app/components/settings/branding-admin.tsx b/js/app/components/settings/branding-admin.tsx
index c3c3af72d..2276bb135 100644
--- a/js/app/components/settings/branding-admin.tsx
+++ b/js/app/components/settings/branding-admin.tsx
@@ -2402,7 +2402,11 @@ export function BrandingAdmin() {
{t(label)}
{asset?.data && (
- ``;
const ICONS: Record = {
- favicon: svg("#808080"),
- faviconLight: svg("#ffd000"),
- faviconDark: svg("#2040ff"),
+ favicon: ``,
+ faviconLight: ``,
+ faviconDark: ``,
};
type Icon = { type: string; body: string };
@@ -141,7 +139,14 @@ test("branding-favicons: light and dark favicons follow the color scheme", async
.toBe("image/png");
const bundled = await shownIcon(page, "dom");
- for (const key of Object.keys(ICONS)) await uploadIcon(page, key);
+ // The background-backed default works in either scheme on its own.
+ await uploadIcon(page, "favicon");
+ await expectShown(page, "light", icon("favicon"));
+ await expectShown(page, "dark", icon("favicon"));
+
+ // Transparent variants override it independently without altering the files.
+ for (const key of ["faviconLight", "faviconDark"])
+ await uploadIcon(page, key);
// Each scheme gets its own icon, live: no reload between the switches.
await expectShown(page, "light", icon("faviconLight"));
diff --git a/js/i18n/locales/en-US/settings.ftl b/js/i18n/locales/en-US/settings.ftl
index d02dd21d5..e6edc3db6 100644
--- a/js/i18n/locales/en-US/settings.ftl
+++ b/js/i18n/locales/en-US/settings.ftl
@@ -307,10 +307,11 @@ branding-accent-color = Accent Color (secondary surfaces, highlights)
branding-accent-color-placeholder = #8b5cf6
branding-main-logo = Main Logo
branding-main-logo-description = SVG, PNG, or JPEG (max 500KB)
-branding-favicon = Favicon (fallback)
-branding-favicon-light = Favicon for light mode
-branding-favicon-dark = Favicon for dark mode
-branding-favicon-description = SVG, PNG, or ICO (max 100KB). Light/dark icons follow the browser or OS appearance, not the app theme. Missing variants use the fallback favicon, or the bundled icon if none is uploaded.
+branding-favicon = Default favicon
+branding-favicon-light = Light mode favicon
+branding-favicon-dark = Dark mode favicon
+branding-favicon-description = SVG, PNG, or ICO (max 100KB). Used when an override is absent or the client does not support theme selection. An icon with a background works well as the default.
+branding-favicon-override-description = SVG, PNG, or ICO (max 100KB). Overrides the default for the browser or OS appearance, not the app theme. Transparent backgrounds are supported.
branding-sidebar-bg = Sidebar Background Image
branding-sidebar-bg-description = SVG, PNG, or JPEG (max 500kb) - appears aligned to bottom of sidebar, full width. Upload an image with opacity for best results, as there is not currently a separate opacity option.
branding-current = Current: { $value }
diff --git a/js/i18n/public/locales/en-US/settings.json b/js/i18n/public/locales/en-US/settings.json
index 189780e36..e213dca21 100644
--- a/js/i18n/public/locales/en-US/settings.json
+++ b/js/i18n/public/locales/en-US/settings.json
@@ -247,10 +247,11 @@
"branding-accent-color-placeholder": "#8b5cf6",
"branding-main-logo": "Main Logo",
"branding-main-logo-description": "SVG, PNG, or JPEG (max 500KB)",
- "branding-favicon": "Favicon (fallback)",
- "branding-favicon-light": "Favicon for light mode",
- "branding-favicon-dark": "Favicon for dark mode",
- "branding-favicon-description": "SVG, PNG, or ICO (max 100KB). Light/dark icons follow the browser or OS appearance, not the app theme. Missing variants use the fallback favicon, or the bundled icon if none is uploaded.",
+ "branding-favicon": "Default favicon",
+ "branding-favicon-light": "Light mode favicon",
+ "branding-favicon-dark": "Dark mode favicon",
+ "branding-favicon-description": "SVG, PNG, or ICO (max 100KB). Used when an override is absent or the client does not support theme selection. An icon with a background works well as the default.",
+ "branding-favicon-override-description": "SVG, PNG, or ICO (max 100KB). Overrides the default for the browser or OS appearance, not the app theme. Transparent backgrounds are supported.",
"branding-sidebar-bg": "Sidebar Background Image",
"branding-sidebar-bg-description": "SVG, PNG, or JPEG (max 500kb) - appears aligned to bottom of sidebar, full width. Upload an image with opacity for best results, as there is not currently a separate opacity option.",
"branding-current": "Current: {$value}",
diff --git a/js/web/public/locales/en-US/settings.json b/js/web/public/locales/en-US/settings.json
index 189780e36..e213dca21 100644
--- a/js/web/public/locales/en-US/settings.json
+++ b/js/web/public/locales/en-US/settings.json
@@ -247,10 +247,11 @@
"branding-accent-color-placeholder": "#8b5cf6",
"branding-main-logo": "Main Logo",
"branding-main-logo-description": "SVG, PNG, or JPEG (max 500KB)",
- "branding-favicon": "Favicon (fallback)",
- "branding-favicon-light": "Favicon for light mode",
- "branding-favicon-dark": "Favicon for dark mode",
- "branding-favicon-description": "SVG, PNG, or ICO (max 100KB). Light/dark icons follow the browser or OS appearance, not the app theme. Missing variants use the fallback favicon, or the bundled icon if none is uploaded.",
+ "branding-favicon": "Default favicon",
+ "branding-favicon-light": "Light mode favicon",
+ "branding-favicon-dark": "Dark mode favicon",
+ "branding-favicon-description": "SVG, PNG, or ICO (max 100KB). Used when an override is absent or the client does not support theme selection. An icon with a background works well as the default.",
+ "branding-favicon-override-description": "SVG, PNG, or ICO (max 100KB). Overrides the default for the browser or OS appearance, not the app theme. Transparent backgrounds are supported.",
"branding-sidebar-bg": "Sidebar Background Image",
"branding-sidebar-bg-description": "SVG, PNG, or JPEG (max 500kb) - appears aligned to bottom of sidebar, full width. Upload an image with opacity for best results, as there is not currently a separate opacity option.",
"branding-current": "Current: {$value}",