@@ -7,11 +7,18 @@ interface OgImageProps {
77 tags : string [ ] ;
88}
99
10- export function OgImage ( { title, author, date, tags } : OgImageProps ) : ReactNode {
11- // Truncate title if too long
12- const displayTitle = title . length > 100 ? title . slice ( 0 , 97 ) + "…" : title ;
13- const displayTags = tags . slice ( 0 , 4 ) ;
10+ interface OgPageImageProps {
11+ title : string ;
12+ subtitle : string ;
13+ detail ?: string ;
14+ }
15+
16+ const pythonLogoBlue =
17+ "M126.916.072c-64.832 0-60.784 28.115-60.784 28.115l.072 29.128h61.868v8.745H41.631S.145 61.355.145 126.77c0 65.417 36.21 63.097 36.21 63.097h21.61v-30.356s-1.165-36.21 35.632-36.21h61.362s34.475.557 34.475-33.319V33.97S194.67.072 126.916.072zM92.802 19.66a11.12 11.12 0 0 1 11.13 11.13 11.12 11.12 0 0 1-11.13 11.13 11.12 11.12 0 0 1-11.13-11.13 11.12 11.12 0 0 1 11.13-11.13z" ;
18+ const pythonLogoYellow =
19+ "M128.757 254.126c64.832 0 60.784-28.115 60.784-28.115l-.072-29.127H127.6v-8.745h86.441s41.486 4.705 41.486-60.712c0-65.416-36.21-63.096-36.21-63.096h-21.61v30.355s1.165 36.21-35.632 36.21h-61.362s-34.475-.557-34.475 33.32v56.013s-5.235 33.897 62.518 33.897zm34.114-19.586a11.12 11.12 0 0 1-11.13-11.13 11.12 11.12 0 0 1 11.13-11.131 11.12 11.12 0 0 1 11.13 11.13 11.12 11.12 0 0 1-11.13 11.13z" ;
1420
21+ function OgShell ( { children } : { children : ReactNode } ) : ReactNode {
1522 return (
1623 < div
1724 style = { {
@@ -25,7 +32,7 @@ export function OgImage({ title, author, date, tags }: OgImageProps): ReactNode
2532 overflow : "hidden" ,
2633 } }
2734 >
28- { /* Subtle geometric pattern — diagonal lines */ }
35+ { /* Subtle geometric pattern */ }
2936 < div
3037 style = { {
3138 position : "absolute" ,
@@ -40,7 +47,6 @@ export function OgImage({ title, author, date, tags }: OgImageProps): ReactNode
4047 backgroundSize : "56px 56px" ,
4148 } }
4249 />
43-
4450 { /* Top accent bar */ }
4551 < div
4652 style = { {
@@ -51,8 +57,63 @@ export function OgImage({ title, author, date, tags }: OgImageProps): ReactNode
5157 flexShrink : 0 ,
5258 } }
5359 />
60+ { children }
61+ </ div >
62+ ) ;
63+ }
5464
55- { /* Content area */ }
65+ function SiteBadge ( ) : ReactNode {
66+ return (
67+ < div style = { { display : "flex" , alignItems : "center" , gap : "10" } } >
68+ < div
69+ style = { {
70+ width : "32" ,
71+ height : "32" ,
72+ borderRadius : "8" ,
73+ background : "linear-gradient(135deg, #306998, #4B8BBE)" ,
74+ display : "flex" ,
75+ alignItems : "center" ,
76+ justifyContent : "center" ,
77+ } }
78+ >
79+ < svg width = "18" height = "18" viewBox = "0 0 256 255" xmlns = "http://www.w3.org/2000/svg" >
80+ < path d = { pythonLogoBlue } fill = "white" />
81+ < path d = { pythonLogoYellow } fill = "rgba(255,255,255,0.6)" />
82+ </ svg >
83+ </ div >
84+ < span
85+ style = { { fontSize : "20" , fontWeight : 600 , color : "#a1a1aa" , letterSpacing : "-0.01em" } }
86+ >
87+ Python Insider
88+ </ span >
89+ </ div >
90+ ) ;
91+ }
92+
93+ function LogoWatermark ( ) : ReactNode {
94+ return (
95+ < div
96+ style = { {
97+ position : "absolute" ,
98+ right : "40" ,
99+ bottom : "60" ,
100+ width : "280" ,
101+ height : "280" ,
102+ display : "flex" ,
103+ opacity : 0.06 ,
104+ } }
105+ >
106+ < svg viewBox = "0 0 256 255" width = "280" height = "280" xmlns = "http://www.w3.org/2000/svg" >
107+ < path d = { pythonLogoBlue } fill = "#306998" />
108+ < path d = { pythonLogoYellow } fill = "#ffd43b" />
109+ </ svg >
110+ </ div >
111+ ) ;
112+ }
113+
114+ export function OgPageImage ( { title, subtitle, detail } : OgPageImageProps ) : ReactNode {
115+ return (
116+ < OgShell >
56117 < div
57118 style = { {
58119 display : "flex" ,
@@ -63,91 +124,137 @@ export function OgImage({ title, author, date, tags }: OgImageProps): ReactNode
63124 position : "relative" ,
64125 } }
65126 >
66- { /* Python logo watermark */ }
127+ < LogoWatermark />
128+ < SiteBadge />
129+
130+ { /* Center: large title + subtitle */ }
67131 < div
68132 style = { {
69- position : "absolute" ,
70- right : "40" ,
71- bottom : "60" ,
72- width : "280" ,
73- height : "280" ,
74133 display : "flex" ,
75- opacity : 0.06 ,
134+ flexDirection : "column" ,
135+ flex : 1 ,
136+ justifyContent : "center" ,
137+ marginTop : "-16" ,
138+ gap : "16" ,
76139 } }
77140 >
78- < svg
79- viewBox = "0 0 256 255"
80- width = "280"
81- height = "280"
82- xmlns = "http://www.w3.org/2000/svg"
141+ < div
142+ style = { {
143+ fontSize : "64" ,
144+ fontWeight : 700 ,
145+ color : "#fafafa" ,
146+ lineHeight : 1.15 ,
147+ letterSpacing : "-0.03em" ,
148+ } }
83149 >
84- < path
85- d = "M126.916.072c-64.832 0-60.784 28.115-60.784 28.115l.072 29.128h61.868v8.745H41.631S.145 61.355.145 126.77c0 65.417 36.21 63.097 36.21 63.097h21.61v-30.356s-1.165-36.21 35.632-36.21h61.362s34.475.557 34.475-33.319V33.97S194.67.072 126.916.072zM92.802 19.66a11.12 11.12 0 0 1 11.13 11.13 11.12 11.12 0 0 1-11.13 11.13 11.12 11.12 0 0 1-11.13-11.13 11.12 11.12 0 0 1 11.13-11.13z"
86- fill = "#306998"
87- />
88- < path
89- d = "M128.757 254.126c64.832 0 60.784-28.115 60.784-28.115l-.072-29.127H127.6v-8.745h86.441s41.486 4.705 41.486-60.712c0-65.416-36.21-63.096-36.21-63.096h-21.61v30.355s1.165 36.21-35.632 36.21h-61.362s-34.475-.557-34.475 33.32v56.013s-5.235 33.897 62.518 33.897zm34.114-19.586a11.12 11.12 0 0 1-11.13-11.13 11.12 11.12 0 0 1 11.13-11.131 11.12 11.12 0 0 1 11.13 11.13 11.12 11.12 0 0 1-11.13 11.13z"
90- fill = "#ffd43b"
91- />
92- </ svg >
150+ { title }
151+ </ div >
152+ < div
153+ style = { {
154+ fontSize : "24" ,
155+ fontWeight : 400 ,
156+ color : "#a1a1aa" ,
157+ lineHeight : 1.4 ,
158+ maxWidth : "700" ,
159+ } }
160+ >
161+ { subtitle }
162+ </ div >
93163 </ div >
94164
95- { /* Top section: site name */ }
165+ { /* Bottom detail */ }
166+ { detail && (
167+ < div
168+ style = { {
169+ display : "flex" ,
170+ alignItems : "center" ,
171+ } }
172+ >
173+ < span style = { { fontSize : "18" , color : "#71717a" } } > { detail } </ span >
174+ </ div >
175+ ) }
176+ </ div >
177+ </ OgShell >
178+ ) ;
179+ }
180+
181+ export function OgAuthorImage ( {
182+ name,
183+ postCount,
184+ } : {
185+ name : string ;
186+ postCount : number ;
187+ } ) : ReactNode {
188+ return (
189+ < OgShell >
190+ < div
191+ style = { {
192+ display : "flex" ,
193+ flexDirection : "column" ,
194+ flex : 1 ,
195+ padding : "56px 64px 48px" ,
196+ justifyContent : "space-between" ,
197+ position : "relative" ,
198+ } }
199+ >
200+ < LogoWatermark />
201+ < SiteBadge />
202+
96203 < div
97204 style = { {
98205 display : "flex" ,
99- alignItems : "center" ,
100- gap : "12" ,
206+ flexDirection : "column" ,
207+ flex : 1 ,
208+ justifyContent : "center" ,
209+ marginTop : "-16" ,
210+ gap : "16" ,
101211 } }
102212 >
213+ < div style = { { fontSize : "24" , fontWeight : 400 , color : "#71717a" } } > Author</ div >
103214 < div
104215 style = { {
105- display : "flex" ,
106- alignItems : "center" ,
107- gap : "10" ,
216+ fontSize : "64" ,
217+ fontWeight : 700 ,
218+ color : "#fafafa" ,
219+ lineHeight : 1.15 ,
220+ letterSpacing : "-0.03em" ,
108221 } }
109222 >
110- < div
111- style = { {
112- width : "32" ,
113- height : "32" ,
114- borderRadius : "8" ,
115- background : "linear-gradient(135deg, #306998, #4B8BBE)" ,
116- display : "flex" ,
117- alignItems : "center" ,
118- justifyContent : "center" ,
119- } }
120- >
121- < svg
122- width = "18"
123- height = "18"
124- viewBox = "0 0 256 255"
125- xmlns = "http://www.w3.org/2000/svg"
126- >
127- < path
128- d = "M126.916.072c-64.832 0-60.784 28.115-60.784 28.115l.072 29.128h61.868v8.745H41.631S.145 61.355.145 126.77c0 65.417 36.21 63.097 36.21 63.097h21.61v-30.356s-1.165-36.21 35.632-36.21h61.362s34.475.557 34.475-33.319V33.97S194.67.072 126.916.072zM92.802 19.66a11.12 11.12 0 0 1 11.13 11.13 11.12 11.12 0 0 1-11.13 11.13 11.12 11.12 0 0 1-11.13-11.13 11.12 11.12 0 0 1 11.13-11.13z"
129- fill = "white"
130- />
131- < path
132- d = "M128.757 254.126c64.832 0 60.784-28.115 60.784-28.115l-.072-29.127H127.6v-8.745h86.441s41.486 4.705 41.486-60.712c0-65.416-36.21-63.096-36.21-63.096h-21.61v30.355s1.165 36.21-35.632 36.21h-61.362s-34.475-.557-34.475 33.32v56.013s-5.235 33.897 62.518 33.897zm34.114-19.586a11.12 11.12 0 0 1-11.13-11.13 11.12 11.12 0 0 1 11.13-11.131 11.12 11.12 0 0 1 11.13 11.13 11.12 11.12 0 0 1-11.13 11.13z"
133- fill = "rgba(255,255,255,0.6)"
134- />
135- </ svg >
136- </ div >
137- < span
138- style = { {
139- fontSize : "20" ,
140- fontWeight : 600 ,
141- color : "#a1a1aa" ,
142- letterSpacing : "-0.01em" ,
143- } }
144- >
145- Python Insider
146- </ span >
223+ { name }
147224 </ div >
148225 </ div >
149226
150- { /* Middle section: title */ }
227+ < div style = { { display : "flex" , alignItems : "center" , gap : "16" } } >
228+ < span style = { { fontSize : "18" , fontWeight : 600 , color : "#ffd43b" } } >
229+ { postCount } { postCount === 1 ? "post" : "posts" }
230+ </ span >
231+ < span style = { { fontSize : "18" , color : "#52525b" } } > ·</ span >
232+ < span style = { { fontSize : "18" , color : "#71717a" } } > Python Insider</ span >
233+ </ div >
234+ </ div >
235+ </ OgShell >
236+ ) ;
237+ }
238+
239+ export function OgImage ( { title, author, date, tags } : OgImageProps ) : ReactNode {
240+ const displayTitle = title . length > 100 ? title . slice ( 0 , 97 ) + "…" : title ;
241+ const displayTags = tags . slice ( 0 , 4 ) ;
242+
243+ return (
244+ < OgShell >
245+ < div
246+ style = { {
247+ display : "flex" ,
248+ flexDirection : "column" ,
249+ flex : 1 ,
250+ padding : "56px 64px 48px" ,
251+ justifyContent : "space-between" ,
252+ position : "relative" ,
253+ } }
254+ >
255+ < LogoWatermark />
256+ < SiteBadge />
257+
151258 < div
152259 style = { {
153260 display : "flex" ,
@@ -171,48 +278,21 @@ export function OgImage({ title, author, date, tags }: OgImageProps): ReactNode
171278 </ div >
172279 </ div >
173280
174- { /* Bottom section: author, date, tags */ }
175281 < div
176282 style = { {
177283 display : "flex" ,
178284 alignItems : "center" ,
179285 justifyContent : "space-between" ,
180286 } }
181287 >
182- < div
183- style = { {
184- display : "flex" ,
185- alignItems : "center" ,
186- gap : "16" ,
187- } }
188- >
189- < span
190- style = { {
191- fontSize : "18" ,
192- fontWeight : 600 ,
193- color : "#ffd43b" ,
194- } }
195- >
196- { author }
197- </ span >
288+ < div style = { { display : "flex" , alignItems : "center" , gap : "16" } } >
289+ < span style = { { fontSize : "18" , fontWeight : 600 , color : "#ffd43b" } } > { author } </ span >
198290 < span style = { { fontSize : "18" , color : "#52525b" } } > ·</ span >
199- < span
200- style = { {
201- fontSize : "18" ,
202- color : "#71717a" ,
203- } }
204- >
205- { date }
206- </ span >
291+ < span style = { { fontSize : "18" , color : "#71717a" } } > { date } </ span >
207292 </ div >
208293
209294 { displayTags . length > 0 && (
210- < div
211- style = { {
212- display : "flex" ,
213- gap : "8" ,
214- } }
215- >
295+ < div style = { { display : "flex" , gap : "8" } } >
216296 { displayTags . map ( ( tag ) => (
217297 < div
218298 key = { tag }
@@ -233,6 +313,6 @@ export function OgImage({ title, author, date, tags }: OgImageProps): ReactNode
233313 ) }
234314 </ div >
235315 </ div >
236- </ div >
316+ </ OgShell >
237317 ) ;
238318}
0 commit comments