@@ -14,6 +14,7 @@ import path from 'path';
1414import satori from 'satori' ;
1515import { Resvg } from '@resvg/resvg-js' ;
1616import matter from 'gray-matter' ;
17+ import opentype from '@shuding/opentype.js' ;
1718
1819const ROOT = process . cwd ( ) ;
1920const CONTENT_DIR = path . join ( ROOT , 'src' , 'content' ) ;
@@ -34,6 +35,33 @@ const medium = fs.readFileSync(
3435 path . join ( ROOT , 'public' , 'fonts' , 'Optimistic_Display_W_Md.ttf' )
3536) ;
3637
38+ // Title area width: card width minus the horizontal padding (80px per side).
39+ const TITLE_MAX_WIDTH = 1200 - 80 * 2 ;
40+ const TITLE_MAX_FONT = 96 ;
41+ const TITLE_MIN_FONT = 56 ;
42+ // Small margin so borderline titles don't wrap and orphan a single trailing
43+ // character (e.g. the "p" in "renderToStaticMarkup", which is 1px too wide
44+ // to fit on one line at 96px).
45+ const TITLE_SAFETY = 8 ;
46+
47+ const boldFont = opentype . parse (
48+ bold . buffer . slice ( bold . byteOffset , bold . byteOffset + bold . byteLength )
49+ ) ;
50+
51+ // Multi-word titles wrap cleanly at spaces, so a length bucket is fine for
52+ // them. Single-word titles (most API names) can only break mid-word, which
53+ // leaves an orphaned letter on its own line, so instead shrink them just
54+ // enough to fit on a single line.
55+ function titleFontSize ( title ) {
56+ const trimmed = title . trim ( ) ;
57+ if ( / \s / . test ( trimmed ) ) {
58+ return trimmed . length > 24 ? 72 : 96 ;
59+ }
60+ const widthPerPx = boldFont . getAdvanceWidth ( trimmed , 1 ) ;
61+ const fit = Math . floor ( ( TITLE_MAX_WIDTH - TITLE_SAFETY ) / widthPerPx ) ;
62+ return Math . max ( TITLE_MIN_FONT , Math . min ( TITLE_MAX_FONT , fit ) ) ;
63+ }
64+
3765function el ( type , style , children ) {
3866 return { type, props : { style, children} } ;
3967}
@@ -103,7 +131,7 @@ function card(title, pagePath) {
103131 flexGrow : 1 ,
104132 display : 'flex' ,
105133 alignItems : 'center' ,
106- fontSize : title . length > 24 ? 72 : 96 ,
134+ fontSize : titleFontSize ( title ) ,
107135 fontFamily : 'Optimistic Display Bold' ,
108136 color : '#f6f7f9' ,
109137 lineHeight : 1.1 ,
0 commit comments