From 22a3e6484b0d7ac9e5402c844798b407563a6e6a Mon Sep 17 00:00:00 2001 From: Rotimi Best Date: Wed, 8 Jul 2026 03:28:38 +0100 Subject: [PATCH] fix(org): show org logo for favicon and OG image (#713) When no custom favicon is set, fall back to the organization logo on public org-site pages instead of the default ClassroomIO icon. For OG images, only apply the theme-color background behind the logo when rendering initials; real logo images now render without the tint. Co-authored-by: Cursor Agent Co-authored-by: Rotimi Best --- apps/api/src/utils/org-site-og.ts | 24 ++++++++++++++----- apps/dashboard/src/app.html | 2 -- .../lib/features/app/org-site-favicon.svelte | 5 +++- .../src/lib/utils/functions/org-branding.ts | 21 +++++++++------- .../src/routes/(org-site)/+layout.svelte | 3 --- apps/dashboard/src/routes/+layout.svelte | 12 ++++++++++ apps/dashboard/src/routes/+page.svelte | 2 -- 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/apps/api/src/utils/org-site-og.ts b/apps/api/src/utils/org-site-og.ts index cafd4607..d4270eb8 100644 --- a/apps/api/src/utils/org-site-og.ts +++ b/apps/api/src/utils/org-site-og.ts @@ -146,7 +146,10 @@ function renderOrgSiteOgTemplate(input: OrgSiteOgInput): { body: string; styles: const labelColor = rgba(themeColor, 0.72); const initials = orgInitials(orgName); - const logoMarkup = input.logoUrl + const hasLogo = Boolean(input.logoUrl); + const logoWrapClass = hasLogo ? 'logo-wrap logo-wrap--image' : 'logo-wrap logo-wrap--initials'; + + const logoMarkup = hasLogo ? `` : ``; @@ -159,7 +162,7 @@ function renderOrgSiteOgTemplate(input: OrgSiteOgInput): { body: string; styles: const body = `
-
${logoMarkup}
+
${logoMarkup}

Academy

@@ -214,17 +217,26 @@ body { width: 88px; height: 88px; border-radius: 20px; - background: ${themeColor}; display: flex; align-items: center; justify-content: center; +} + +.logo-wrap--initials { + background: ${themeColor}; box-shadow: 0 16px 40px ${rgba(themeColor, 0.28)}; } +.logo-wrap--image { + background: transparent; + box-shadow: none; + overflow: hidden; +} + .logo-image { - width: 56px; - height: 56px; - border-radius: 12px; + width: 88px; + height: 88px; + border-radius: 20px; object-fit: cover; } diff --git a/apps/dashboard/src/app.html b/apps/dashboard/src/app.html index 62a2b94d..58aee939 100644 --- a/apps/dashboard/src/app.html +++ b/apps/dashboard/src/app.html @@ -6,8 +6,6 @@ - - %sveltekit.head% diff --git a/apps/dashboard/src/lib/features/app/org-site-favicon.svelte b/apps/dashboard/src/lib/features/app/org-site-favicon.svelte index a3f4ff39..57c55df6 100644 --- a/apps/dashboard/src/lib/features/app/org-site-favicon.svelte +++ b/apps/dashboard/src/lib/features/app/org-site-favicon.svelte @@ -1,5 +1,6 @@ {#if faviconHref} + + {/if} diff --git a/apps/dashboard/src/lib/utils/functions/org-branding.ts b/apps/dashboard/src/lib/utils/functions/org-branding.ts index 12b0768d..1de05d58 100644 --- a/apps/dashboard/src/lib/utils/functions/org-branding.ts +++ b/apps/dashboard/src/lib/utils/functions/org-branding.ts @@ -1,15 +1,20 @@ import type { AccountOrg } from '$features/app/types'; -export function getOrgFaviconHref(org: Pick | null | undefined): string | null { +export function getOrgFaviconHref( + org: Pick | null | undefined, + pageOrigin?: string +): string | null { const favicon = org?.favicon?.trim(); - if (favicon) { - return favicon; - } - const avatarUrl = org?.avatarUrl?.trim(); - if (avatarUrl) { - return avatarUrl; + const rawHref = favicon || avatarUrl; + + if (!rawHref) { + return null; } - return null; + try { + return new URL(rawHref, pageOrigin || undefined).href; + } catch { + return rawHref; + } } diff --git a/apps/dashboard/src/routes/(org-site)/+layout.svelte b/apps/dashboard/src/routes/(org-site)/+layout.svelte index baac3ce1..dfca32b4 100644 --- a/apps/dashboard/src/routes/(org-site)/+layout.svelte +++ b/apps/dashboard/src/routes/(org-site)/+layout.svelte @@ -1,6 +1,5 @@ - - {#if data.org?.isRestricted || $currentOrg.isRestricted} {:else} diff --git a/apps/dashboard/src/routes/+layout.svelte b/apps/dashboard/src/routes/+layout.svelte index 44465f1b..50fadd06 100644 --- a/apps/dashboard/src/routes/+layout.svelte +++ b/apps/dashboard/src/routes/+layout.svelte @@ -13,6 +13,7 @@ import merge from 'lodash/merge'; import { MetaTags } from 'svelte-meta-tags'; import { ModeWatcher } from '@cio/ui/base/dark-mode'; + import OrgSiteFavicon from '$features/app/org-site-favicon.svelte'; import '../app.css'; @@ -62,6 +63,17 @@ }); +{#if data.isOrgSite} + +{/if} + + + {#if !data.isOrgSite} + + + {/if} + +
diff --git a/apps/dashboard/src/routes/+page.svelte b/apps/dashboard/src/routes/+page.svelte index cfb63821..6b4a28be 100644 --- a/apps/dashboard/src/routes/+page.svelte +++ b/apps/dashboard/src/routes/+page.svelte @@ -9,7 +9,6 @@ import { Empty } from '@cio/ui/custom/empty'; import { SimpleLogoNav } from '@cio/ui/custom/simple-logo-nav'; import { buildOrgLandingPageProps, normalizeLandingPageSettings } from '$features/org/utils/landing-page'; - import OrgSiteFavicon from '$features/app/org-site-favicon.svelte'; import { basePath } from '$lib/utils/store/app'; import { t } from '$lib/utils/functions/translations'; import { user } from '$lib/utils/store/user'; @@ -65,7 +64,6 @@ {#if data.isOrgSite && data.org} - {#if data.ThemeComponent && landingPageProps} {/if}