Skip to content

Commit 5c59402

Browse files
committed
Improve open graph image of website
1 parent 7ad1ead commit 5c59402

File tree

4 files changed

+97
-121
lines changed

4 files changed

+97
-121
lines changed

pnpm-lock.yaml

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"eslint-plugin-qwik": "1.1.5",
4747
"gray-matter": "^4.0.3",
4848
"netlify-cli": "^15.0.0",
49+
"og-img": "^0.1.0",
4950
"postcss": "^8.4.27",
5051
"prettier": "2.8.8",
5152
"rehype-external-links": "^2.1.0",

website/src/components/Head.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const Head = component$(() => {
3939
)}&description=${encodeURIComponent(
4040
head.meta.find((item) => item.name === 'description')
4141
?.content || ''
42-
)}`
42+
)}&path=${location.url.pathname.split('/')[1]}`
4343
}
4444
/>
4545

website/src/routes/og-image/index.ts

Lines changed: 74 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import type { RequestHandler } from '@builder.io/qwik-city';
2-
import { Resvg, initWasm } from '@resvg/resvg-wasm';
3-
import satori from 'satori';
4-
5-
let wasmPromise: Promise<void> | undefined;
2+
import { ImageResponse, html } from 'og-img';
63

74
async function getFontData(fileName: string) {
85
const resposne = await fetch(
@@ -12,9 +9,10 @@ async function getFontData(fileName: string) {
129
}
1310

1411
export const onGet: RequestHandler = async ({ send, url }) => {
15-
// Get title and description from search params
12+
// Get data from search params
1613
const title = url.searchParams.get('title');
17-
const description = url.searchParams.get('description') || 'valibot.dev';
14+
const description = url.searchParams.get('description');
15+
const path = url.searchParams.get('path');
1816

1917
// Create Lexend 400 font object
2018
const lexend400 = {
@@ -40,131 +38,87 @@ export const onGet: RequestHandler = async ({ send, url }) => {
4038
weight: 500,
4139
} as const;
4240

43-
// Generate SVG string using Satori
44-
const svg = title
45-
? // If title is available, return SVG with text
46-
await satori(
47-
{
48-
type: 'div',
49-
props: {
50-
tw: 'flex h-full w-full flex-col justify-between bg-gray-900 p-16',
51-
style: { fontFamily: 'Lexend' },
52-
children: [
53-
{
54-
type: 'div',
55-
props: {
56-
tw: 'flex items-center',
57-
children: [
58-
{
59-
type: 'img',
60-
props: {
61-
tw: 'w-16 h-16',
62-
src: `${
63-
import.meta.env.PUBLIC_WEBSITE_URL
64-
}/icon-192px.png`,
65-
},
66-
},
67-
{
68-
type: 'div',
69-
props: {
70-
tw: 'text-4xl font-medium text-slate-300 ml-4',
71-
style: { fontFamily: 'Lexend Exa' },
72-
children: 'Valibot',
73-
},
74-
},
75-
],
76-
},
77-
},
78-
{
79-
type: 'div',
80-
props: {
81-
tw: 'flex flex-col',
82-
children: [
83-
{
84-
type: 'h1',
85-
props: {
86-
tw: 'max-w-[80%] text-6xl font-medium leading-normal text-slate-200',
87-
style: {
88-
overflow: 'hidden',
89-
textOverflow: 'ellipsis',
90-
whiteSpace: 'nowrap',
91-
},
92-
children: title,
93-
},
94-
},
95-
{
96-
type: 'p',
97-
props: {
98-
tw: 'text-4xl text-slate-400 leading-loose',
99-
children:
100-
description.length > 110
101-
? description.slice(0, 110).trimEnd() + '...'
102-
: description,
103-
},
104-
},
105-
],
106-
},
107-
},
108-
],
109-
},
110-
},
41+
// If title is available, return image with text
42+
if (title) {
43+
send(
44+
new ImageResponse(
45+
html`
46+
<div
47+
tw="flex h-full w-full flex-col justify-between bg-gray-900 p-16"
48+
style="font-family: 'Lexend'"
49+
>
50+
<div tw="flex items-center justify-between">
51+
<div tw="flex items-center">
52+
<img
53+
tw="w-16 h-16"
54+
src="${import.meta.env.PUBLIC_WEBSITE_URL}/icon-192px.png"
55+
/>
56+
<div
57+
tw="text-4xl font-medium text-slate-300 ml-4"
58+
style="font-family: 'Lexend Exa'"
59+
>
60+
Valibot
61+
</div>
62+
</div>
63+
<div
64+
tw="max-w-[50%] text-4xl text-slate-500"
65+
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
66+
>
67+
valibot.dev${path ? `/` + path : ''}
68+
</div>
69+
</div>
70+
<div tw="flex flex-col">
71+
<h1
72+
tw="max-w-[80%] text-6xl font-medium leading-normal text-slate-200"
73+
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
74+
>
75+
${title}
76+
</h1>
77+
<p
78+
tw="text-4xl text-slate-400 leading-loose"
79+
style="${description ? '' : 'display: none'}}"
80+
>
81+
${description
82+
? description.length > 110
83+
? description.slice(0, 110).trimEnd() + '...'
84+
: description
85+
: ''}
86+
</p>
87+
</div>
88+
</div>
89+
`,
11190
{
11291
width: 1200,
11392
height: 630,
11493
fonts: [lexend400, lexend500, lexendExa500],
11594
}
11695
)
117-
: // Otherwise, return SVG with logo
118-
await satori(
119-
{
120-
type: 'div',
121-
props: {
122-
tw: 'flex h-full w-full items-center justify-center bg-gray-900',
123-
style: { fontFamily: 'Lexend Exa' },
124-
children: {
125-
type: 'div',
126-
props: {
127-
tw: 'flex items-center',
128-
children: [
129-
{
130-
type: 'img',
131-
props: {
132-
tw: 'w-36 h-36',
133-
src: `${
134-
import.meta.env.PUBLIC_WEBSITE_URL
135-
}/icon-192px.png`,
136-
},
137-
},
138-
{
139-
type: 'div',
140-
props: {
141-
tw: 'text-8xl font-medium text-slate-300 ml-10',
142-
children: 'Valibot',
143-
},
144-
},
145-
],
146-
},
147-
},
148-
},
149-
},
96+
);
97+
98+
// Otherwise, return image just with logo
99+
} else {
100+
send(
101+
new ImageResponse(
102+
html`
103+
<div
104+
tw="flex h-full w-full items-center justify-center bg-gray-900"
105+
style="font-family: 'Lexend Exa'"
106+
>
107+
<div tw="flex items-center">
108+
<img
109+
tw="w-36 h-36"
110+
src="${import.meta.env.PUBLIC_WEBSITE_URL}/icon-192px.png"
111+
/>
112+
<div tw="text-8xl font-medium text-slate-300 ml-10">Valibot</div>
113+
</div>
114+
</div>
115+
`,
150116
{
151117
width: 1200,
152118
height: 630,
153119
fonts: [lexendExa500],
154120
}
155-
);
156-
157-
// Lazy initialize WebAssembly module
158-
if (!wasmPromise) {
159-
wasmPromise = initWasm(
160-
fetch('https://unpkg.com/@resvg/resvg-wasm/index_bg.wasm')
121+
)
161122
);
162123
}
163-
await wasmPromise;
164-
165-
// Convert SVG string to PNG image
166-
const image = new Resvg(svg).render().asPng();
167-
168-
// Send PNG image as response
169-
send(new Response(image, { headers: { 'content-type': 'image/png' } }));
170124
};

0 commit comments

Comments
 (0)