Skip to content

conference page — missing OG images #2057

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export default withLess(

return config
},
// Comment this out if you're working on OG images.
output: "export",
images: {
loader: "custom",
Expand Down
43 changes: 23 additions & 20 deletions src/app/(development)/workroom/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { SpeakerOpengraphImage } from "@/app/conf/2025/components/speaker-opengraph-image"
import { SessionOpengraphImage } from "@/app/conf/2025/components/session-opengraph-image"
import { SpeakerOpengraphImage } from "@/app/conf/2025/components/og-images/speaker-opengraph-image"
import { SessionOpengraphImage } from "@/app/conf/2025/components/og-images/session-opengraph-image"
import { GenericOpengraphImage } from "@/app/conf/2025/components/og-images/generic-opengraph-image"
import { SchedSpeaker } from "@/app/conf/2023/types"

/**
* This is cheaper than maintaining a Storybook config.
*/
export default function WorkroomPage() {
const dateAndLocation = {
date: "September 8-10",
year: "2025",
location: "Amsterdam, Netherlands",
}

const enisdenjo: SchedSpeaker = {
name: "Denis Badurina",
username: "enisdenjo",
Expand Down Expand Up @@ -35,12 +42,7 @@ export default function WorkroomPage() {
return (
<main className="gql-conf-section gql-conf-container [&>p]:pt-8 [&>p]:font-mono [&>p]:text-sm [&>p]:text-neu-600">
<p>SpeakerOpengraphImage</p>
<SpeakerOpengraphImage
speaker={enisdenjo}
date="September 8-10"
year="2025"
location="Amsterdam, Netherlands"
/>
<SpeakerOpengraphImage speaker={enisdenjo} {...dateAndLocation} />

<p>ScheduleOpengraphImage / no speakers</p>
<SessionOpengraphImage
Expand All @@ -50,9 +52,7 @@ export default function WorkroomPage() {
event_type: "",
event_subtype: "",
}}
date="September 8-10"
year="2025"
location="Amsterdam, Netherlands"
{...dateAndLocation}
/>

<p>ScheduleOpengraphImage / single speaker</p>
Expand All @@ -63,9 +63,7 @@ export default function WorkroomPage() {
event_type: "Keynote Sessions",
event_subtype: "",
}}
date="September 8-10"
year="2025"
location="Amsterdam, Netherlands"
{...dateAndLocation}
/>

<p>ScheduleOpengraphImage / multiple speakers</p>
Expand All @@ -76,9 +74,7 @@ export default function WorkroomPage() {
event_type: "Developer Experience",
event_subtype: "Backend",
}}
date="September 8-10"
year="2025"
location="Amsterdam, Netherlands"
{...dateAndLocation}
/>

<p>SpeakerOpengraphImage / very long title</p>
Expand Down Expand Up @@ -107,10 +103,17 @@ export default function WorkroomPage() {
event_type: "Keynote Sessions",
event_subtype: "",
}}
date="September 8-10"
year="2025"
location="Amsterdam, Netherlands"
{...dateAndLocation}
/>

<p>GenericOpengraphImage / GraphQLConf 2025</p>
<GenericOpengraphImage
pageTitle="GraphQLConf 2025"
{...dateAndLocation}
/>

<p>GenericOpengraphImage / Sponsors</p>
<GenericOpengraphImage pageTitle="Sponsors" {...dateAndLocation} />
</main>
)
}
10 changes: 10 additions & 0 deletions src/app/conf/2025/code-of-conduct/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { SimpleOpengraphImage } from "../components/og-images/simple-opengraph-image"
export {
generateStaticParams,
contentType,
size,
} from "../components/og-images/simple-opengraph-image"

export default SimpleOpengraphImage.bind(null, {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're using bind here instead of an arrow or a new function because @types/react doesn't seem to know we can return Promise<JSX> just yet.

This should be solved after migrating to React 19, but I'm fine with taking this small stylistic/readability hit just to a void dealing with React versions for now.

pageTitle: "Code of Conduct",
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { CalendarIcon } from "@/app/conf/_design-system/pixelarticons/calendar-icon"
import { PinIcon } from "@/app/conf/_design-system/pixelarticons/pin-icon"

import { GraphQLLogo } from "../graphql-conf-logo-link"
import { colors, fonts, RIGHT_COLUMN_WIDTH_PX } from "./speaker-opengraph-image"

export const OG_IMAGE_HEADER_HEIGHT = 154

export function ConferenceOpengraphImageHeader({
year,
date,
location,
style,
}: {
year: string
date: string
location: string
style?: React.CSSProperties
}) {
return (
<header
style={{
display: "flex",
alignItems: "center",
borderBottom: `2px solid ${colors.neu600}`,
...style,
}}
>
<div
style={{
display: "flex",
flex: 1,
alignItems: "center",
gap: "1.5rem",
borderRight: `2px solid ${colors.neu600}`,
padding: "2.5rem",
paddingRight: "4rem",
height: OG_IMAGE_HEADER_HEIGHT,
}}
>
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
<div
style={{
fontFamily: fonts.mono,
display: "flex",
fontWeight: "normal",
textTransform: "uppercase",
lineHeight: "1",
color: colors.neu900,
}}
>
<div
style={{
display: "flex",
height: "74px",
alignItems: "center",
gap: "1rem",
fontSize: "40px",
lineHeight: "1",
textTransform: "uppercase",
}}
>
<GraphQLLogo
style={{
height: "3rem",
width: "3rem",
color: colors.priBase,
/* hack: satori aligns this SVG differently than browsers, it will look off center in /workroom,
but centered in the images */
marginTop: "-6px",
}}
/>
<span>/</span>
<div
style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
>
GraphQLConf{" "}
<span style={{ color: colors.priBase }}>{year}</span>
</div>
</div>
</div>
</div>
</div>

<div
style={{
display: "flex",
height: "100%",
flexShrink: 0,
flexDirection: "column",
justifyContent: "center",
width: RIGHT_COLUMN_WIDTH_PX,
}}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: "1.5rem",
borderBottom: `2px solid ${colors.neu600}`,
paddingLeft: "1.5rem",
paddingRight: "1.5rem",
paddingTop: "26px",
paddingBottom: "26px",
}}
>
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
<CalendarIcon
width="24"
height="24"
style={{
/* hack: different across satori and browsers */
transform: "translateY(-3px)",
color: colors.priBase,
}}
/>
<span
style={{
fontFamily: fonts.mono,
display: "flex",
fontSize: "1.25rem",
fontWeight: "normal",
textTransform: "uppercase",
lineHeight: "1.2",
color: colors.neu900,
}}
>
{date}, {year}
</span>
</div>
</div>

<div
style={{
display: "flex",
alignItems: "center",
gap: "1.5rem",
paddingLeft: "1.5rem",
paddingRight: "1.5rem",
paddingTop: "26px",
paddingBottom: "26px",
}}
>
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
<PinIcon
width="24"
height="24"
style={{
/* hack: different across satori and browsers */
transform: "translateY(-2px)",
color: colors.priBase,
}}
/>
<span
style={{
fontFamily: fonts.mono,
fontSize: "1.25rem",
fontWeight: "normal",
textTransform: "uppercase",
lineHeight: "1.2",
color: colors.neu900,
}}
>
{location}
</span>
</div>
</div>
</div>
</header>
)
}
80 changes: 80 additions & 0 deletions src/app/conf/2025/components/og-images/generic-opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { colors, fonts } from "./speaker-opengraph-image"
import {
ConferenceOpengraphImageHeader,
OG_IMAGE_HEADER_HEIGHT,
} from "./conference-opengraph-image-header"

import graphqlLogoStripes from "./graphql-logo-stripes.png"

export interface GenericOpengraphImageProps
extends React.HTMLAttributes<HTMLElement> {
date: string
year: string
location: string
pageTitle: string
}

export function GenericOpengraphImage({
date,
location,
year,
pageTitle,
...rest
}: GenericOpengraphImageProps) {
const basePath =
`https://${process.env.VERCEL_URL}` || process.env.__NEXT_PRIVATE_ORIGIN

const height = 630

return (
<article
style={{
display: "flex",
height,
width: "1200px",
flexDirection: "column",
overflow: "hidden",
borderWidth: "2px",
borderColor: colors.neu600,
backgroundColor: colors.neu100,
fontFamily: fonts.sans,
}}
{...rest}
>
<ConferenceOpengraphImageHeader
year={year}
date={date}
location={location}
/>

<div
style={{
display: "flex",
flex: 1,
flexDirection: "column",
justifyContent: "flex-end",
padding: "2.5rem",
position: "relative",
}}
>
<h1
style={{
margin: 0,
fontFamily: fonts.sans,
lineHeight: "1.25",
color: colors.neu900,
fontSize: "96px",
}}
>
{pageTitle}
</h1>
<img
src={`${basePath}${graphqlLogoStripes.src}`}
style={{ position: "absolute", right: 0, bottom: -5 }}
height={height - OG_IMAGE_HEADER_HEIGHT}
width={673}
/>
</div>
</article>
)
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function normalizeProtocolRelativeUrl(url: string) {
if (url.startsWith("//")) {
return `https:${url}`
}
return url
}
Loading