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 <cursoragent@cursor.com>
Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
This commit is contained in:
Rotimi Best
2026-07-08 03:28:38 +01:00
committed by GitHub
co-authored by Cursor Agent Rotimi Best
parent 3d15bc0a01
commit 22a3e6484b
7 changed files with 47 additions and 22 deletions
+18 -6
View File
@@ -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
? `<img class="logo-image" src="${escapeHtml(input.logoUrl)}" alt="" />`
: `<span class="logo-initials" aria-hidden="true">${escapeHtml(initials)}</span>`;
@@ -159,7 +162,7 @@ function renderOrgSiteOgTemplate(input: OrgSiteOgInput): { body: string; styles:
const body = `
<div class="canvas">
<div class="decor-circle"></div>
<div class="logo-wrap">${logoMarkup}</div>
<div class="${logoWrapClass}">${logoMarkup}</div>
<div class="content">
<p class="academy-label">Academy</p>
<div class="accent-bar"></div>
@@ -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;
}
-2
View File
@@ -6,8 +6,6 @@
<meta name="theme-color" content="#333333" />
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
<link rel="icon" type="image/png" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="/logo-32.png" />
%sveltekit.head%
</head>
@@ -1,5 +1,6 @@
<script lang="ts">
import type { AccountOrg } from '$features/app/types';
import { page } from '$app/state';
import { getOrgFaviconHref } from '$lib/utils/functions/org-branding';
interface Props {
@@ -8,11 +9,13 @@
let { org }: Props = $props();
const faviconHref = $derived(getOrgFaviconHref(org));
const faviconHref = $derived(getOrgFaviconHref(org, page.url.origin));
</script>
<svelte:head>
{#if faviconHref}
<link rel="icon" href={faviconHref} />
<link rel="shortcut icon" href={faviconHref} />
<link rel="apple-touch-icon" href={faviconHref} />
{/if}
</svelte:head>
@@ -1,15 +1,20 @@
import type { AccountOrg } from '$features/app/types';
export function getOrgFaviconHref(org: Pick<AccountOrg, 'favicon' | 'avatarUrl'> | null | undefined): string | null {
export function getOrgFaviconHref(
org: Pick<AccountOrg, 'favicon' | 'avatarUrl'> | 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;
}
try {
return new URL(rawHref, pageOrigin || undefined).href;
} catch {
return rawHref;
}
}
@@ -1,6 +1,5 @@
<script lang="ts">
import { PageRestricted } from '$features/ui';
import OrgSiteFavicon from '$features/app/org-site-favicon.svelte';
import { currentOrg } from '$lib/utils/store/org';
import { onMount } from 'svelte';
import { afterNavigate } from '$app/navigation';
@@ -41,8 +40,6 @@
});
</script>
<OrgSiteFavicon org={data.org} />
{#if data.org?.isRestricted || $currentOrg.isRestricted}
<PageRestricted />
{:else}
+12
View File
@@ -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 @@
});
</script>
{#if data.isOrgSite}
<OrgSiteFavicon org={data.org} />
{/if}
<svelte:head>
{#if !data.isOrgSite}
<link rel="icon" type="image/png" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="/logo-32.png" />
{/if}
</svelte:head>
<div>
<ModeWatcher />
-2
View File
@@ -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 @@
</svelte:head>
{#if data.isOrgSite && data.org}
<OrgSiteFavicon org={data.org} />
{#if data.ThemeComponent && landingPageProps}
<svelte:component this={data.ThemeComponent} {...landingPageProps} />
{/if}