Skip to content

Commit 977a0d7

Browse files
authored
fix(landing): refresh content previews and remove legacy logos (#7684)
* fix(landing): refresh content previews and remove legacy logos * fix(seo): omit unsupported author avatar dimensions
1 parent 4eb8be1 commit 977a0d7

29 files changed

Lines changed: 153 additions & 25 deletions

File tree

apps/docs/public/static/logo.png

-10.7 KB
Loading

apps/sim/content/authors/sim.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
"name": "The Sim Team",
44
"url": "https://x.com/simdotai",
55
"xHandle": "simdotai",
6-
"avatarUrl": "/logo/primary/small.png"
6+
"avatarUrl": "/brandbook/logo/small.png"
77
}

apps/sim/lib/content/seo.test.ts

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import path from 'node:path'
5+
import sharp from 'sharp'
6+
import { describe, expect, it, vi } from 'vitest'
7+
import type { ContentMeta } from '@/lib/content/schema'
8+
import {
9+
buildArticleJsonLd,
10+
buildAuthorMetadata,
11+
buildCollectionPageJsonLd,
12+
buildIndexMetadata,
13+
buildPostMetadata,
14+
} from '@/lib/content/seo'
15+
import { buildLandingMetadata, LANDING_SOCIAL_IMAGE } from '@/lib/landing/seo'
16+
import teamAuthor from '@/content/authors/sim.json'
17+
18+
vi.mock('@/lib/core/utils/urls', () => ({ SITE_URL: 'https://example.com' }))
19+
20+
const SECTIONS = [
21+
{ name: 'Blog', basePath: '/blog', description: 'Latest posts' },
22+
{ name: 'Library', basePath: '/library', description: 'Guides and comparisons' },
23+
{ name: 'Customers', basePath: '/customers', description: 'Customer stories' },
24+
]
25+
26+
const POST: ContentMeta = {
27+
slug: 'example',
28+
title: 'Example article',
29+
description: 'An article with its own cover image.',
30+
date: '2026-01-01T00:00:00.000Z',
31+
author: teamAuthor,
32+
authors: [teamAuthor],
33+
tags: ['guides'],
34+
ogImage: '/blog/example/cover.jpg',
35+
ogImageWidth: 1600,
36+
ogImageHeight: 900,
37+
canonical: 'https://example.com/blog/example',
38+
draft: false,
39+
featured: false,
40+
technical: true,
41+
}
42+
43+
describe('content social images', () => {
44+
it.each(SECTIONS)('$name shares the landing image across Open Graph and Twitter', (section) => {
45+
const metadata = buildIndexMetadata(section, { pageNum: 1 })
46+
const landing = buildLandingMetadata({
47+
title: 'Home',
48+
description: 'The workspace',
49+
path: '',
50+
})
51+
52+
expect(metadata.openGraph?.images).toEqual([
53+
{
54+
...LANDING_SOCIAL_IMAGE,
55+
url: `https://example.com${LANDING_SOCIAL_IMAGE.url}`,
56+
alt: `Sim ${section.name}`,
57+
},
58+
])
59+
expect(metadata.twitter?.images).toEqual({
60+
url: `https://example.com${LANDING_SOCIAL_IMAGE.url}`,
61+
alt: `Sim ${section.name}`,
62+
})
63+
expect(landing.openGraph?.images).toEqual([{ ...LANDING_SOCIAL_IMAGE, alt: 'Home' }])
64+
expect(metadata.alternates?.canonical).toBe(`https://example.com${section.basePath}`)
65+
})
66+
67+
it('declares the actual dimensions and format of the shared image', async () => {
68+
const metadata = await sharp(path.join('public', LANDING_SOCIAL_IMAGE.url)).metadata()
69+
70+
expect(metadata).toMatchObject({
71+
width: LANDING_SOCIAL_IMAGE.width,
72+
height: LANDING_SOCIAL_IMAGE.height,
73+
format: 'png',
74+
})
75+
expect(LANDING_SOCIAL_IMAGE.type).toBe('image/png')
76+
})
77+
78+
it('preserves filtered titles, canonical URLs, and noindex policy', () => {
79+
const metadata = buildIndexMetadata(SECTIONS[0], { tag: 'guides', pageNum: 2 })
80+
81+
expect(metadata.title).toBe('Blog | guides | Page 2')
82+
expect(metadata.alternates?.canonical).toBe('https://example.com/blog')
83+
expect(metadata.robots).toEqual({ index: false, follow: true })
84+
expect(metadata.openGraph?.images).toEqual(
85+
buildIndexMetadata(SECTIONS[0], { pageNum: 1 }).openGraph?.images
86+
)
87+
})
88+
89+
it('retains article-specific artwork and dimensions', () => {
90+
const metadata = buildPostMetadata(POST)
91+
92+
expect(metadata.openGraph?.images).toEqual([
93+
{
94+
url: `https://example.com${POST.ogImage}`,
95+
width: 1600,
96+
height: 900,
97+
alt: POST.title,
98+
},
99+
])
100+
expect(metadata.twitter?.images).toEqual([POST.ogImage])
101+
expect(buildArticleJsonLd(POST).image[0].url).toBe(`https://example.com${POST.ogImage}`)
102+
})
103+
104+
it('uses the current square logo for publishers and the team avatar', async () => {
105+
const article = buildArticleJsonLd(POST)
106+
const collection = buildCollectionPageJsonLd(SECTIONS[0], [POST])
107+
108+
expect(teamAuthor.avatarUrl).toBe('/brandbook/logo/small.png')
109+
expect(article.publisher.logo.url).toBe(`https://example.com${teamAuthor.avatarUrl}`)
110+
expect(collection.publisher.logo.url).toBe(article.publisher.logo.url)
111+
expect(await sharp(path.join('public', teamAuthor.avatarUrl)).metadata()).toMatchObject({
112+
width: 1200,
113+
height: 1200,
114+
format: 'png',
115+
})
116+
})
117+
118+
it.each([teamAuthor.avatarUrl, 'https://example.com/avatar.jpg'])(
119+
'keeps the author avatar without inventing dimensions for %s',
120+
(avatarUrl) => {
121+
const metadata = buildAuthorMetadata(SECTIONS[0], teamAuthor.id, {
122+
...teamAuthor,
123+
avatarUrl,
124+
})
125+
126+
expect(metadata.openGraph?.images).toEqual([{ url: avatarUrl, alt: teamAuthor.name }])
127+
}
128+
)
129+
})

apps/sim/lib/content/seo.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type { Metadata } from 'next'
22
import type { Author, ContentMeta } from '@/lib/content/schema'
33
import { SITE_URL } from '@/lib/core/utils/urls'
4-
import { withFilteredNoindex } from '@/lib/landing/seo'
4+
import { LANDING_SOCIAL_IMAGE, withFilteredNoindex } from '@/lib/landing/seo'
5+
6+
const PUBLISHER_LOGO_URL = `${SITE_URL}/brandbook/logo/small.png`
57

68
/**
79
* Identifies the content section a post/collection belongs to, so the
@@ -113,7 +115,7 @@ export function buildArticleJsonLd(post: ContentMeta) {
113115
url: SITE_URL,
114116
logo: {
115117
'@type': 'ImageObject',
116-
url: `${SITE_URL}/logo/primary/medium.png`,
118+
url: PUBLISHER_LOGO_URL,
117119
},
118120
},
119121
mainEntityOfPage: {
@@ -215,9 +217,8 @@ export function buildIndexMetadata(
215217
type: 'website',
216218
images: [
217219
{
218-
url: `${SITE_URL}/logo/primary/medium.png`,
219-
width: 1200,
220-
height: 630,
220+
...LANDING_SOCIAL_IMAGE,
221+
url: `${SITE_URL}${LANDING_SOCIAL_IMAGE.url}`,
221222
alt: `Sim ${section.name}`,
222223
},
223224
],
@@ -227,6 +228,10 @@ export function buildIndexMetadata(
227228
title: `${title} | Sim`,
228229
description,
229230
site: '@simdotai',
231+
images: {
232+
url: `${SITE_URL}${LANDING_SOCIAL_IMAGE.url}`,
233+
alt: `Sim ${section.name}`,
234+
},
230235
},
231236
},
232237
isFiltered
@@ -297,9 +302,7 @@ export function buildAuthorMetadata(
297302
url: canonical,
298303
siteName: 'Sim',
299304
type: 'profile',
300-
...(author?.avatarUrl
301-
? { images: [{ url: author.avatarUrl, width: 400, height: 400, alt: name }] }
302-
: {}),
305+
...(author?.avatarUrl ? { images: [{ url: author.avatarUrl, alt: name }] } : {}),
303306
},
304307
twitter: {
305308
card: 'summary',
@@ -389,7 +392,7 @@ export function buildCollectionPageJsonLd(
389392
url: SITE_URL,
390393
logo: {
391394
'@type': 'ImageObject',
392-
url: `${SITE_URL}/logo/primary/medium.png`,
395+
url: PUBLISHER_LOGO_URL,
393396
},
394397
},
395398
inLanguage: 'en-US',

apps/sim/lib/landing/seo.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import type { Metadata } from 'next'
22
import { SITE_URL } from '@/lib/core/utils/urls'
33

4-
/** Shared OpenGraph/Twitter card image for landing pages. */
5-
const OG_IMAGE_URL = '/logo/426-240/reverse/small.png'
6-
const OG_IMAGE_WIDTH = 2130
7-
const OG_IMAGE_HEIGHT = 1200
4+
/** Shared Open Graph/Twitter image for landing pages and content indexes. */
5+
export const LANDING_SOCIAL_IMAGE = {
6+
url: '/logo/426-240/reverse/small.png',
7+
width: 2130,
8+
height: 1200,
9+
type: 'image/png',
10+
} as const
811

912
interface LandingMetadataInput {
1013
/** Absolute `<title>`, rendered as-is (no site template applied). */
@@ -59,11 +62,8 @@ export function buildLandingMetadata({
5962
locale: 'en_US',
6063
images: [
6164
{
62-
url: OG_IMAGE_URL,
63-
width: OG_IMAGE_WIDTH,
64-
height: OG_IMAGE_HEIGHT,
65+
...LANDING_SOCIAL_IMAGE,
6566
alt: ogAlt,
66-
type: 'image/png',
6767
},
6868
],
6969
},
@@ -73,7 +73,7 @@ export function buildLandingMetadata({
7373
creator: '@simdotai',
7474
title,
7575
description,
76-
images: { url: OG_IMAGE_URL, alt: twitterAlt },
76+
images: { url: LANDING_SOCIAL_IMAGE.url, alt: twitterAlt },
7777
},
7878
alternates: {
7979
canonical: url,
-327 KB
Binary file not shown.
-96.7 KB
Binary file not shown.

apps/sim/public/logo/426-240/primary/primary.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.
-8.12 KB
Binary file not shown.
-326 KB
Binary file not shown.

0 commit comments

Comments
 (0)