@@ -5,25 +5,25 @@ import type { NextRequest } from 'next/server'
55export const runtime = 'edge'
66
77const TITLE_FONT_SIZE = {
8- large : 105 ,
9- medium : 91 ,
10- small : 81 ,
8+ large : 110 ,
9+ medium : 96 ,
10+ small : 85 ,
1111} as const
1212/** Average glyph width as a fraction of font size, for this weight/family — used to pack words into lines. */
13- const AVG_CHAR_WIDTH_EM = 0.46
13+ const AVG_CHAR_WIDTH_EM = 0.42
1414const TITLE_BOX_WIDTH = 1020
1515const FONT_CACHE_REVALIDATE_SECONDS = 60 * 60 * 24 * 30
16- /** Measured off the reference cover template (`apps/sim/public/library/best-zapier-alternatives/cover.jpg`) . */
17- const INK_COLOR = '#525252 '
16+ /** Exact hex from a vector trace of the reference cover template, not an estimate off compressed JPEG pixels . */
17+ const INK_COLOR = '#515151 '
1818const OG_CONTAINER_STYLE = {
1919 height : '100%' ,
2020 width : '100%' ,
2121 display : 'flex' ,
2222 flexDirection : 'column' ,
2323 justifyContent : 'space-between' ,
2424 padding : '26px' ,
25- background : '#c3c3c3 ' ,
26- fontFamily : 'Season ' ,
25+ background : '#c1c1c1 ' ,
26+ fontFamily : 'Soehne ' ,
2727} satisfies CSSProperties
2828const OG_HEADER_STYLE = {
2929 display : 'flex' ,
@@ -34,10 +34,12 @@ const OG_HEADER_STYLE = {
3434const OG_TITLE_STYLE = {
3535 display : 'flex' ,
3636 flexDirection : 'column' ,
37- fontWeight : 600 ,
37+ fontWeight : 500 ,
3838 color : INK_COLOR ,
39- lineHeight : 1.15 ,
39+ lineHeight : 1.1 ,
4040 width : `${ TITLE_BOX_WIDTH } px` ,
41+ /** Compensates for Satori adding extra invisible leading below the last line instead of splitting it evenly. */
42+ transform : 'translateY(14px)' ,
4143} satisfies CSSProperties
4244
4345function getTitleFontSize ( title : string ) : number {
@@ -83,21 +85,16 @@ function wrapTitleLines(title: string, fontSize: number): string[] {
8385}
8486
8587/**
86- * Loads a static (600/semibold) TTF instance of the site's own Season Sans
87- * font — the platform's real brand/body font, also used by the library/blog
88- * cover template this OG image matches. Instantiated from the variable font
89- * at `apps/docs/app/fonts/SeasonSansUprightsVF.woff2` (`fonttools
90- * varLib.instancer wght=600`, then flavor-stripped to plain TTF) rather than
91- * loading the variable WOFF2 directly: Satori (`next/og`'s renderer) can't
92- * parse variable fonts without excessive memory use, and can't parse WOFF2
93- * at all ("Unsupported OpenType signature wOF2") — it needs an uncompressed
94- * TTF/OTF. Fetched over HTTP since the edge runtime has no filesystem access
95- * — served from `/static/fonts/` (not `/fonts/`) so it isn't intercepted by
96- * the site's i18n proxy (`proxy.ts`), whose matcher excludes `static` but
97- * not `fonts`.
88+ * Loads Söhne Kräftig (weight 500), the typeface used on the reference cover
89+ * template this OG image matches. Converted to a plain TTF from the
90+ * last-shipped `soehne-kraftig.woff2` since Satori (`next/og`'s renderer)
91+ * can't parse WOFF2 or variable fonts. Fetched over HTTP since the edge
92+ * runtime has no filesystem access — served from `/static/fonts/` (not
93+ * `/fonts/`) so it isn't intercepted by the site's i18n proxy (`proxy.ts`),
94+ * whose matcher excludes `static` but not `fonts`.
9895 */
99- async function loadSeasonFont ( baseUrl : string ) : Promise < ArrayBuffer > {
100- const response = await fetch ( new URL ( '/static/fonts/SeasonSans-600-static .ttf' , baseUrl ) , {
96+ async function loadTitleFont ( baseUrl : string ) : Promise < ArrayBuffer > {
97+ const response = await fetch ( new URL ( '/static/fonts/Soehne-Kraftig .ttf' , baseUrl ) , {
10198 next : { revalidate : FONT_CACHE_REVALIDATE_SECONDS } ,
10299 } )
103100
@@ -128,16 +125,16 @@ function SimWordmark() {
128125 )
129126}
130127
131- /** Diagonal "open" arrow, top-right — matches the library/blog cover template . */
128+ /** Diagonal "open" arrow, top-right — square caps and a miter join to match the reference's sharp corners . */
132129function CornerArrow ( ) {
133130 return (
134131 < svg width = '58' height = '58' viewBox = '0 0 24 24' fill = 'none' >
135132 < path
136133 d = 'M2 22 22 2M22 2H12M22 2V12'
137134 stroke = { INK_COLOR }
138135 strokeWidth = { 3.6 }
139- strokeLinecap = 'round '
140- strokeLinejoin = 'round '
136+ strokeLinecap = 'square '
137+ strokeLinejoin = 'miter '
141138 />
142139 </ svg >
143140 )
@@ -153,7 +150,7 @@ export async function GET(request: NextRequest) {
153150 const { searchParams } = new URL ( request . url )
154151 const title = searchParams . get ( 'title' ) || 'Documentation'
155152
156- const fontData = await loadSeasonFont ( request . url )
153+ const fontData = await loadTitleFont ( request . url )
157154 const fontSize = getTitleFontSize ( title )
158155 const titleLines = wrapTitleLines ( title , fontSize )
159156
@@ -175,10 +172,10 @@ export async function GET(request: NextRequest) {
175172 height : 675 ,
176173 fonts : [
177174 {
178- name : 'Season ' ,
175+ name : 'Soehne ' ,
179176 data : fontData ,
180177 style : 'normal' ,
181- weight : 600 ,
178+ weight : 500 ,
182179 } ,
183180 ] ,
184181 }
0 commit comments