Skip to content

Commit e114fb6

Browse files
authored
conference page — missing OG images (#2057)
* Add SimpleOpengraphImage * Add OG images for speakers, resources, schedule, and CoC pages * JSDoc doesn't make sense here * Add https:// to VERCEL_URL for preview deployments * Add image width to silence build warnings
1 parent 811a609 commit e114fb6

18 files changed

+588
-406
lines changed

next.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export default withLess(
8585

8686
return config
8787
},
88+
// Comment this out if you're working on OG images.
8889
output: "export",
8990
images: {
9091
loader: "custom",

src/app/(development)/workroom/page.tsx

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
import { SpeakerOpengraphImage } from "@/app/conf/2025/components/speaker-opengraph-image"
2-
import { SessionOpengraphImage } from "@/app/conf/2025/components/session-opengraph-image"
1+
import { SpeakerOpengraphImage } from "@/app/conf/2025/components/og-images/speaker-opengraph-image"
2+
import { SessionOpengraphImage } from "@/app/conf/2025/components/og-images/session-opengraph-image"
3+
import { GenericOpengraphImage } from "@/app/conf/2025/components/og-images/generic-opengraph-image"
34
import { SchedSpeaker } from "@/app/conf/2023/types"
45

56
/**
67
* This is cheaper than maintaining a Storybook config.
78
*/
89
export default function WorkroomPage() {
10+
const dateAndLocation = {
11+
date: "September 8-10",
12+
year: "2025",
13+
location: "Amsterdam, Netherlands",
14+
}
15+
916
const enisdenjo: SchedSpeaker = {
1017
name: "Denis Badurina",
1118
username: "enisdenjo",
@@ -35,12 +42,7 @@ export default function WorkroomPage() {
3542
return (
3643
<main className="gql-conf-section gql-conf-container [&>p]:pt-8 [&>p]:font-mono [&>p]:text-sm [&>p]:text-neu-600">
3744
<p>SpeakerOpengraphImage</p>
38-
<SpeakerOpengraphImage
39-
speaker={enisdenjo}
40-
date="September 8-10"
41-
year="2025"
42-
location="Amsterdam, Netherlands"
43-
/>
45+
<SpeakerOpengraphImage speaker={enisdenjo} {...dateAndLocation} />
4446

4547
<p>ScheduleOpengraphImage / no speakers</p>
4648
<SessionOpengraphImage
@@ -50,9 +52,7 @@ export default function WorkroomPage() {
5052
event_type: "",
5153
event_subtype: "",
5254
}}
53-
date="September 8-10"
54-
year="2025"
55-
location="Amsterdam, Netherlands"
55+
{...dateAndLocation}
5656
/>
5757

5858
<p>ScheduleOpengraphImage / single speaker</p>
@@ -63,9 +63,7 @@ export default function WorkroomPage() {
6363
event_type: "Keynote Sessions",
6464
event_subtype: "",
6565
}}
66-
date="September 8-10"
67-
year="2025"
68-
location="Amsterdam, Netherlands"
66+
{...dateAndLocation}
6967
/>
7068

7169
<p>ScheduleOpengraphImage / multiple speakers</p>
@@ -76,9 +74,7 @@ export default function WorkroomPage() {
7674
event_type: "Developer Experience",
7775
event_subtype: "Backend",
7876
}}
79-
date="September 8-10"
80-
year="2025"
81-
location="Amsterdam, Netherlands"
77+
{...dateAndLocation}
8278
/>
8379

8480
<p>SpeakerOpengraphImage / very long title</p>
@@ -107,10 +103,17 @@ export default function WorkroomPage() {
107103
event_type: "Keynote Sessions",
108104
event_subtype: "",
109105
}}
110-
date="September 8-10"
111-
year="2025"
112-
location="Amsterdam, Netherlands"
106+
{...dateAndLocation}
113107
/>
108+
109+
<p>GenericOpengraphImage / GraphQLConf 2025</p>
110+
<GenericOpengraphImage
111+
pageTitle="GraphQLConf 2025"
112+
{...dateAndLocation}
113+
/>
114+
115+
<p>GenericOpengraphImage / Sponsors</p>
116+
<GenericOpengraphImage pageTitle="Sponsors" {...dateAndLocation} />
114117
</main>
115118
)
116119
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { SimpleOpengraphImage } from "../components/og-images/simple-opengraph-image"
2+
export {
3+
generateStaticParams,
4+
contentType,
5+
size,
6+
} from "../components/og-images/simple-opengraph-image"
7+
8+
export default SimpleOpengraphImage.bind(null, {
9+
pageTitle: "Code of Conduct",
10+
})
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import { CalendarIcon } from "@/app/conf/_design-system/pixelarticons/calendar-icon"
2+
import { PinIcon } from "@/app/conf/_design-system/pixelarticons/pin-icon"
3+
4+
import { GraphQLLogo } from "../graphql-conf-logo-link"
5+
import { colors, fonts, RIGHT_COLUMN_WIDTH_PX } from "./speaker-opengraph-image"
6+
7+
export const OG_IMAGE_HEADER_HEIGHT = 154
8+
9+
export function ConferenceOpengraphImageHeader({
10+
year,
11+
date,
12+
location,
13+
style,
14+
}: {
15+
year: string
16+
date: string
17+
location: string
18+
style?: React.CSSProperties
19+
}) {
20+
return (
21+
<header
22+
style={{
23+
display: "flex",
24+
alignItems: "center",
25+
borderBottom: `2px solid ${colors.neu600}`,
26+
...style,
27+
}}
28+
>
29+
<div
30+
style={{
31+
display: "flex",
32+
flex: 1,
33+
alignItems: "center",
34+
gap: "1.5rem",
35+
borderRight: `2px solid ${colors.neu600}`,
36+
padding: "2.5rem",
37+
paddingRight: "4rem",
38+
height: OG_IMAGE_HEADER_HEIGHT,
39+
}}
40+
>
41+
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
42+
<div
43+
style={{
44+
fontFamily: fonts.mono,
45+
display: "flex",
46+
fontWeight: "normal",
47+
textTransform: "uppercase",
48+
lineHeight: "1",
49+
color: colors.neu900,
50+
}}
51+
>
52+
<div
53+
style={{
54+
display: "flex",
55+
height: "74px",
56+
alignItems: "center",
57+
gap: "1rem",
58+
fontSize: "40px",
59+
lineHeight: "1",
60+
textTransform: "uppercase",
61+
}}
62+
>
63+
<GraphQLLogo
64+
style={{
65+
height: "3rem",
66+
width: "3rem",
67+
color: colors.priBase,
68+
/* hack: satori aligns this SVG differently than browsers, it will look off center in /workroom,
69+
but centered in the images */
70+
marginTop: "-6px",
71+
}}
72+
/>
73+
<span>/</span>
74+
<div
75+
style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
76+
>
77+
GraphQLConf{" "}
78+
<span style={{ color: colors.priBase }}>{year}</span>
79+
</div>
80+
</div>
81+
</div>
82+
</div>
83+
</div>
84+
85+
<div
86+
style={{
87+
display: "flex",
88+
height: "100%",
89+
flexShrink: 0,
90+
flexDirection: "column",
91+
justifyContent: "center",
92+
width: RIGHT_COLUMN_WIDTH_PX,
93+
}}
94+
>
95+
<div
96+
style={{
97+
display: "flex",
98+
alignItems: "center",
99+
gap: "1.5rem",
100+
borderBottom: `2px solid ${colors.neu600}`,
101+
paddingLeft: "1.5rem",
102+
paddingRight: "1.5rem",
103+
paddingTop: "26px",
104+
paddingBottom: "26px",
105+
}}
106+
>
107+
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
108+
<CalendarIcon
109+
width="24"
110+
height="24"
111+
style={{
112+
/* hack: different across satori and browsers */
113+
transform: "translateY(-3px)",
114+
color: colors.priBase,
115+
}}
116+
/>
117+
<span
118+
style={{
119+
fontFamily: fonts.mono,
120+
display: "flex",
121+
fontSize: "1.25rem",
122+
fontWeight: "normal",
123+
textTransform: "uppercase",
124+
lineHeight: "1.2",
125+
color: colors.neu900,
126+
}}
127+
>
128+
{date}, {year}
129+
</span>
130+
</div>
131+
</div>
132+
133+
<div
134+
style={{
135+
display: "flex",
136+
alignItems: "center",
137+
gap: "1.5rem",
138+
paddingLeft: "1.5rem",
139+
paddingRight: "1.5rem",
140+
paddingTop: "26px",
141+
paddingBottom: "26px",
142+
}}
143+
>
144+
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
145+
<PinIcon
146+
width="24"
147+
height="24"
148+
style={{
149+
/* hack: different across satori and browsers */
150+
transform: "translateY(-2px)",
151+
color: colors.priBase,
152+
}}
153+
/>
154+
<span
155+
style={{
156+
fontFamily: fonts.mono,
157+
fontSize: "1.25rem",
158+
fontWeight: "normal",
159+
textTransform: "uppercase",
160+
lineHeight: "1.2",
161+
color: colors.neu900,
162+
}}
163+
>
164+
{location}
165+
</span>
166+
</div>
167+
</div>
168+
</div>
169+
</header>
170+
)
171+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { colors, fonts } from "./speaker-opengraph-image"
2+
import {
3+
ConferenceOpengraphImageHeader,
4+
OG_IMAGE_HEADER_HEIGHT,
5+
} from "./conference-opengraph-image-header"
6+
7+
import graphqlLogoStripes from "./graphql-logo-stripes.png"
8+
9+
export interface GenericOpengraphImageProps
10+
extends React.HTMLAttributes<HTMLElement> {
11+
date: string
12+
year: string
13+
location: string
14+
pageTitle: string
15+
}
16+
17+
export function GenericOpengraphImage({
18+
date,
19+
location,
20+
year,
21+
pageTitle,
22+
...rest
23+
}: GenericOpengraphImageProps) {
24+
const basePath =
25+
`https://${process.env.VERCEL_URL}` || process.env.__NEXT_PRIVATE_ORIGIN
26+
27+
const height = 630
28+
29+
return (
30+
<article
31+
style={{
32+
display: "flex",
33+
height,
34+
width: "1200px",
35+
flexDirection: "column",
36+
overflow: "hidden",
37+
borderWidth: "2px",
38+
borderColor: colors.neu600,
39+
backgroundColor: colors.neu100,
40+
fontFamily: fonts.sans,
41+
}}
42+
{...rest}
43+
>
44+
<ConferenceOpengraphImageHeader
45+
year={year}
46+
date={date}
47+
location={location}
48+
/>
49+
50+
<div
51+
style={{
52+
display: "flex",
53+
flex: 1,
54+
flexDirection: "column",
55+
justifyContent: "flex-end",
56+
padding: "2.5rem",
57+
position: "relative",
58+
}}
59+
>
60+
<h1
61+
style={{
62+
margin: 0,
63+
fontFamily: fonts.sans,
64+
lineHeight: "1.25",
65+
color: colors.neu900,
66+
fontSize: "96px",
67+
}}
68+
>
69+
{pageTitle}
70+
</h1>
71+
<img
72+
src={`${basePath}${graphqlLogoStripes.src}`}
73+
style={{ position: "absolute", right: 0, bottom: -5 }}
74+
height={height - OG_IMAGE_HEADER_HEIGHT}
75+
width={673}
76+
/>
77+
</div>
78+
</article>
79+
)
80+
}
Loading
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export function normalizeProtocolRelativeUrl(url: string) {
2+
if (url.startsWith("//")) {
3+
return `https:${url}`
4+
}
5+
return url
6+
}

0 commit comments

Comments
 (0)