@@ -4,6 +4,32 @@ import { isHosted } from '@/lib/core/config/env-flags'
44import { getBaseUrl } from '@/lib/core/utils/urls'
55import { getBrandConfig } from '@/ee/whitelabeling'
66
7+ /**
8+ * Social mark display size. Every `static/*-icon.png` is 40×40, so 20px is a
9+ * clean 2x source — email clients do no responsive image selection, so the
10+ * asset must be authored at 2x and pinned here. This is deliberately NOT the
11+ * platform's 14px UI-icon size: these are brand marks in an image, not
12+ * `--text-icon` glyphs, and 14px renders them illegibly.
13+ */
14+ const SOCIAL_ICON_SIZE = 20
15+
16+ /** Gap between social marks. Halved on each side, so the row reads evenly. */
17+ const SOCIAL_ICON_GAP = 8
18+
19+ /**
20+ * `display: block` removes the 2–3px gap Outlook adds under inline images;
21+ * `border: 0` prevents the blue link border older Outlook draws around a linked
22+ * image.
23+ */
24+ const socialIconStyle = { display : 'block' as const , border : 0 }
25+
26+ const SOCIAL_LINKS = [
27+ { path : 'x' , label : 'X' , icon : 'x-icon.png' } ,
28+ { path : 'linkedin' , label : 'LinkedIn' , icon : 'linkedin-icon.png' } ,
29+ { path : 'github' , label : 'GitHub' , icon : 'github-icon.png' } ,
30+ { path : 'slack' , label : 'Slack' , icon : 'slack-icon.png' } ,
31+ ] as const
32+
733interface EmailFooterProps {
834 baseUrl ?: string
935 messageId ?: string
@@ -32,16 +58,6 @@ export function EmailFooter({
3258
3359 const footerLinkStyle = baseStyles . footerLink
3460
35- /**
36- * Social icons are linked images. `display: block` removes the 2–3px gap
37- * Outlook adds under inline images, and `border: 0` prevents the blue link
38- * border older Outlook versions draw around linked images.
39- */
40- const socialIconStyle = {
41- display : 'block' as const ,
42- border : 0 ,
43- }
44-
4561 return (
4662 < Section
4763 style = { {
@@ -75,50 +91,26 @@ export function EmailFooter({
7591 < table cellPadding = { 0 } cellSpacing = { 0 } style = { { border : 0 } } >
7692 < tbody >
7793 < tr >
78- < td align = 'left' style = { { padding : '0 8px 0 0' } } >
79- < Link href = { `${ baseUrl } /x` } rel = 'noopener noreferrer' >
80- < Img
81- src = { `${ baseUrl } /static/x-icon.png` }
82- width = '20'
83- height = '20'
84- alt = 'X'
85- style = { socialIconStyle }
86- />
87- </ Link >
88- </ td >
89- < td align = 'left' style = { { padding : '0 8px' } } >
90- < Link href = { `${ baseUrl } /linkedin` } rel = 'noopener noreferrer' >
91- < Img
92- src = { `${ baseUrl } /static/linkedin-icon.png` }
93- width = '20'
94- height = '20'
95- alt = 'LinkedIn'
96- style = { socialIconStyle }
97- />
98- </ Link >
99- </ td >
100- < td align = 'left' style = { { padding : '0 8px' } } >
101- < Link href = { `${ baseUrl } /github` } rel = 'noopener noreferrer' >
102- < Img
103- src = { `${ baseUrl } /static/github-icon.png` }
104- width = '20'
105- height = '20'
106- alt = 'GitHub'
107- style = { socialIconStyle }
108- />
109- </ Link >
110- </ td >
111- < td align = 'left' style = { { padding : '0 8px' } } >
112- < Link href = { `${ baseUrl } /slack` } rel = 'noopener noreferrer' >
113- < Img
114- src = { `${ baseUrl } /static/slack-icon.png` }
115- width = '20'
116- height = '20'
117- alt = 'Slack'
118- style = { socialIconStyle }
119- />
120- </ Link >
121- </ td >
94+ { SOCIAL_LINKS . map ( ( { path, label, icon } , index ) => (
95+ < td
96+ key = { path }
97+ align = 'left'
98+ style = { {
99+ paddingLeft : index === 0 ? 0 : SOCIAL_ICON_GAP ,
100+ paddingRight : SOCIAL_ICON_GAP ,
101+ } }
102+ >
103+ < Link href = { `${ baseUrl } /${ path } ` } rel = 'noopener noreferrer' >
104+ < Img
105+ src = { `${ baseUrl } /static/${ icon } ` }
106+ width = { SOCIAL_ICON_SIZE }
107+ height = { SOCIAL_ICON_SIZE }
108+ alt = { label }
109+ style = { socialIconStyle }
110+ />
111+ </ Link >
112+ </ td >
113+ ) ) }
122114 </ tr >
123115 </ tbody >
124116 </ table >
@@ -167,6 +159,11 @@ export function EmailFooter({
167159 </ td >
168160 < td style = { baseStyles . footerText } >
169161 Questions?{ ' ' }
162+ { /*
163+ A raw anchor, not `<Link>`: react-email's Link hardcodes
164+ target="_blank", which on a mailto: opens a blank tab beside
165+ the compose window in most webmail clients.
166+ */ }
170167 < a href = { `mailto:${ brand . supportEmail } ` } style = { footerLinkStyle } >
171168 { brand . supportEmail }
172169 </ a >
@@ -210,24 +207,24 @@ export function EmailFooter({
210207
211208 </ td >
212209 < td style = { baseStyles . footerText } >
213- < a href = { `${ baseUrl } /privacy` } style = { footerLinkStyle } rel = 'noopener noreferrer' >
210+ < Link href = { `${ baseUrl } /privacy` } style = { footerLinkStyle } rel = 'noopener noreferrer' >
214211 Privacy Policy
215- </ a > { ' ' }
212+ </ Link > { ' ' }
216213 •{ ' ' }
217- < a href = { `${ baseUrl } /terms` } style = { footerLinkStyle } rel = 'noopener noreferrer' >
214+ < Link href = { `${ baseUrl } /terms` } style = { footerLinkStyle } rel = 'noopener noreferrer' >
218215 Terms of Service
219- </ a >
216+ </ Link >
220217 { showUnsubscribe && (
221218 < >
222219 { ' ' }
223220 •{ ' ' }
224- < a
221+ < Link
225222 href = { `${ baseUrl } /unsubscribe?token={{UNSUBSCRIBE_TOKEN}}&email={{UNSUBSCRIBE_EMAIL}}` }
226223 style = { footerLinkStyle }
227224 rel = 'noopener noreferrer'
228225 >
229226 Unsubscribe
230- </ a >
227+ </ Link >
231228 </ >
232229 ) }
233230 </ td >
0 commit comments