Skip to content

Commit 533541a

Browse files
committed
Match OG card design to existing static images
1 parent 93a4c9a commit 533541a

4 files changed

Lines changed: 55 additions & 49 deletions

File tree

59.4 KB
Binary file not shown.
59.6 KB
Binary file not shown.

src/components/Layout/Page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export function Page({
134134
? `/images/og-` + section + '.png'
135135
: `/api/og?title=${encodeURIComponent(
136136
title
137-
)}&section=${encodeURIComponent(section)}`
137+
)}&path=${encodeURIComponent(cleanedPath)}`
138138
}
139139
searchOrder={searchOrder}
140140
/>

src/pages/api/og.tsx

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,77 +5,71 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
import fs from 'fs';
9+
import path from 'path';
810
import {ImageResponse} from 'next/og';
9-
import type {NextRequest} from 'next/server';
11+
import type {NextApiRequest, NextApiResponse} from 'next';
1012

11-
export const config = {
12-
runtime: 'edge',
13-
};
13+
// Satori can't read woff2, so the site fonts are also vendored as
14+
// TTF in public/fonts (converted from the woff2 files there).
15+
const fontsDir = path.join(process.cwd(), 'public', 'fonts');
16+
const bold = fs.readFileSync(
17+
path.join(fontsDir, 'Optimistic_Display_W_Bd.ttf')
18+
);
19+
const medium = fs.readFileSync(
20+
path.join(fontsDir, 'Optimistic_Display_W_Md.ttf')
21+
);
1422

15-
const SECTION_LABELS: Record<string, string> = {
16-
learn: 'Learn React',
17-
reference: 'API Reference',
18-
community: 'Community',
19-
blog: 'Blog',
20-
};
23+
export default async function handler(
24+
req: NextApiRequest,
25+
res: NextApiResponse
26+
) {
27+
const title = String(req.query.title ?? 'react.dev').slice(0, 80);
28+
const pagePath = String(req.query.path ?? '').slice(0, 120);
29+
const footer = ('react.dev' + pagePath).toUpperCase();
2130

22-
// Satori doesn't support woff2, so the site fonts can't be reused here.
23-
// Source Code Pro matches the code-styled titles used across the docs.
24-
const fontPromise = fetch(
25-
'https://raw.githubusercontent.com/adobe-fonts/source-code-pro/release/TTF/SourceCodePro-Semibold.ttf'
26-
).then((res) => res.arrayBuffer());
27-
28-
export default async function handler(request: NextRequest) {
29-
const {searchParams} = new URL(request.url);
30-
const title = searchParams.get('title')?.slice(0, 80) ?? 'react.dev';
31-
const section = searchParams.get('section') ?? '';
32-
const label = SECTION_LABELS[section] ?? 'react.dev';
33-
const fontData = await fontPromise;
34-
35-
return new ImageResponse(
31+
const image = new ImageResponse(
3632
(
3733
<div
3834
style={{
3935
width: '100%',
4036
height: '100%',
4137
display: 'flex',
4238
flexDirection: 'column',
43-
justifyContent: 'center',
44-
padding: '80px',
39+
padding: '72px 80px',
4540
backgroundColor: '#23272f',
41+
backgroundImage:
42+
'radial-gradient(circle at 25% 30%, #343a46 0%, #23272f 55%)',
4643
}}>
4744
<div
4845
style={{
4946
display: 'flex',
5047
alignItems: 'center',
5148
gap: '20px',
5249
}}>
53-
<svg
54-
width="72"
55-
height="72"
56-
viewBox="-11.5 -10.23 23 20.46"
57-
fill="none">
58-
<circle cx="0" cy="0" r="2.05" fill="#58c4dc" />
50+
<svg width="80" height="72" viewBox="-10.5 -9.45 21 18.9" fill="none">
51+
<circle cx="0" cy="0" r="2" fill="#58c4dc" />
5952
<g stroke="#58c4dc" strokeWidth="1" fill="none">
60-
<ellipse rx="11" ry="4.2" />
61-
<ellipse rx="11" ry="4.2" transform="rotate(60)" />
62-
<ellipse rx="11" ry="4.2" transform="rotate(120)" />
53+
<ellipse rx="10" ry="4.5" />
54+
<ellipse rx="10" ry="4.5" transform="rotate(60)" />
55+
<ellipse rx="10" ry="4.5" transform="rotate(120)" />
6356
</g>
6457
</svg>
6558
<div
6659
style={{
67-
fontSize: 36,
68-
color: '#99a1b3',
69-
textTransform: 'uppercase',
70-
letterSpacing: '0.1em',
60+
fontSize: 48,
61+
fontFamily: 'Optimistic Display Bold',
62+
color: '#f6f7f9',
7163
}}>
72-
{label}
64+
React
7365
</div>
7466
</div>
7567
<div
7668
style={{
77-
marginTop: 48,
78-
fontSize: title.length > 24 ? 64 : 96,
69+
marginTop: 'auto',
70+
marginBottom: 'auto',
71+
fontSize: title.length > 24 ? 72 : 96,
72+
fontFamily: 'Optimistic Display Bold',
7973
color: '#f6f7f9',
8074
lineHeight: 1.1,
8175
wordBreak: 'break-word',
@@ -84,11 +78,12 @@ export default async function handler(request: NextRequest) {
8478
</div>
8579
<div
8680
style={{
87-
marginTop: 'auto',
88-
fontSize: 32,
81+
fontSize: 28,
82+
fontFamily: 'Optimistic Display Medium',
8983
color: '#99a1b3',
84+
letterSpacing: '0.08em',
9085
}}>
91-
react.dev
86+
{footer}
9287
</div>
9388
</div>
9489
),
@@ -97,12 +92,23 @@ export default async function handler(request: NextRequest) {
9792
height: 630,
9893
fonts: [
9994
{
100-
name: 'Source Code Pro',
101-
data: fontData,
95+
name: 'Optimistic Display Bold',
96+
data: bold,
10297
style: 'normal',
103-
weight: 600,
98+
weight: 700,
99+
},
100+
{
101+
name: 'Optimistic Display Medium',
102+
data: medium,
103+
style: 'normal',
104+
weight: 500,
104105
},
105106
],
106107
}
107108
);
109+
110+
const buffer = Buffer.from(await image.arrayBuffer());
111+
res.setHeader('Content-Type', 'image/png');
112+
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
113+
res.end(buffer);
108114
}

0 commit comments

Comments
 (0)