"Redesign email templates: update layout, spacing, typography, and styling for improved readability, consistency, and UI cohesion."

This commit is contained in:
MartinBraquet
2026-05-08 20:06:38 +02:00
parent e2bdab6f1c
commit cdadad387d
5 changed files with 473 additions and 164 deletions

View File

@@ -1,18 +1,16 @@
import {
Body,
Button,
Column,
Container,
Head,
Html,
Preview,
Row,
Section,
Text,
} from '@react-email/components'
import {DOMAIN} from 'common/envs/constants'
import {type User} from 'common/user'
import {button, container, content, Footer, main, paragraph} from 'email/utils'
import { container, content, Footer, main} from 'email/utils'
import React from 'react'
import {createT} from 'shared/locale'
@@ -60,36 +58,93 @@ export const NewEndorsementEmail = ({
{/*</Section>*/}
<Section style={content}>
<Text style={paragraph}>
{t('email.new_endorsement.greeting', 'Hi {name},', {name})}
</Text>
<div
style={{
textAlign: 'center',
marginBottom: '32px',
}}
>
<Text
style={{
fontSize: '28px',
fontFamily: "'Cormorant Garamond', serif",
fontWeight: '500',
color: '#1e1a14',
marginBottom: '8px',
letterSpacing: '-0.01em',
lineHeight: '1.1',
}}
>
{t('email.new_endorsement.greeting', 'Hi {name},', {name})}
</Text>
</div>
<Text style={paragraph}>
<Text
style={{
fontSize: '18px',
fontFamily: "'Cormorant Garamond', serif",
fontWeight: '500',
color: '#1e1a14',
marginBottom: '24px',
letterSpacing: '0.01em',
lineHeight: '1.3',
textAlign: 'center',
}}
>
{t('email.new_endorsement.message', '{fromUserName} endorsed you!', {
fromUserName: fromUser.name,
})}
</Text>
<Section style={endorsementContainer}>
<Row>
{/*<Column>*/}
{/* <Img*/}
{/* src={fromUser.avatarUrl}*/}
{/* width="50"*/}
{/* height="50"*/}
{/* alt=""*/}
{/* style={avatarImage}*/}
{/* />*/}
{/*</Column>*/}
<Column>
<Text style={endorsementTextStyle}>"{endorsementText}"</Text>
</Column>
</Row>
<div
style={{
backgroundColor: '#f7f4ef',
border: '1px solid #dee5b2',
borderRadius: '14px',
padding: '24px',
margin: '24px 0',
textAlign: 'center',
}}
>
<div
style={{
backgroundColor: '#faf3e9',
border: '1px solid #e8c99e',
borderRadius: '12px',
padding: '20px',
marginBottom: '20px',
fontStyle: 'italic',
fontFamily: "'Cormorant Garamond', serif",
fontSize: '17px',
lineHeight: '1.65',
color: '#1e1a14',
position: 'relative',
}}
>
"{endorsementText}"
</div>
<Button href={endorsementUrl} style={button}>
<Button
href={endorsementUrl}
style={{
backgroundColor: '#c17f3e',
borderRadius: '10px',
color: '#ffffff',
fontFamily: "'DM Sans', sans-serif",
fontSize: '15px',
fontWeight: '600',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'inline-block',
padding: '14px 32px',
margin: '0',
border: '1px solid #a6682e',
transition: 'all 0.12s ease',
}}
>
{t('email.new_endorsement.viewButton', 'View endorsement')}
</Button>
</Section>
</div>
</Section>
<Footer unsubscribeUrl={unsubscribeUrl} email={email ?? name} locale={locale} />
@@ -108,18 +163,18 @@ NewEndorsementEmail.PreviewProps = {
email: 'someone@gmail.com',
} as NewEndorsementEmailProps
const endorsementContainer = {
margin: '20px 0',
padding: '15px',
backgroundColor: '#f9f9f9',
borderRadius: '8px',
}
const endorsementTextStyle = {
fontSize: '16px',
lineHeight: '22px',
fontStyle: 'italic',
color: '#333333',
}
// const endorsementContainer = {
// margin: '20px 0',
// padding: '15px',
// backgroundColor: '#f9f9f9',
// borderRadius: '8px',
// }
//
// const endorsementTextStyle = {
// fontSize: '16px',
// lineHeight: '22px',
// fontStyle: 'italic',
// color: '#333333',
// }
export default NewEndorsementEmail

View File

@@ -13,7 +13,7 @@ import {ANDROID_APP_URL} from 'common/constants'
import {DOMAIN} from 'common/envs/constants'
import {type ProfileRow} from 'common/profiles/profile'
import {type User} from 'common/user'
import {button, container, content, Footer, imageContainer, main, paragraph} from 'email/utils'
import { container, content, Footer, main} from 'email/utils'
import React from 'react'
import {createT} from 'shared/locale'
@@ -62,29 +62,82 @@ export const NewMessageEmail = ({
{/*</Section>*/}
<Section style={content}>
<Text style={paragraph}>{t('email.new_message.greeting', 'Hi {name},', {name})}</Text>
<div
style={{
textAlign: 'center',
marginBottom: '32px',
}}
>
<Text
style={{
fontSize: '28px',
fontFamily: "'Cormorant Garamond', serif",
fontWeight: '500',
color: '#1e1a14',
marginBottom: '8px',
letterSpacing: '-0.01em',
lineHeight: '1.1',
}}
>
{t('email.new_message.greeting', 'Hi {name},', {name})}
</Text>
</div>
<Text style={paragraph}>
{t('email.new_message.message', '{creatorName} just messaged you!', {creatorName})}
</Text>
<div
style={{
backgroundColor: '#f7f4ef',
border: '1px solid #dee5b2',
borderRadius: '14px',
padding: '24px',
margin: '24px 0',
textAlign: 'center',
}}
>
<Text
style={{
fontSize: '18px',
fontFamily: "'Cormorant Garamond', serif",
fontWeight: '500',
color: '#1e1a14',
marginBottom: '20px',
letterSpacing: '0.01em',
lineHeight: '1.3',
}}
>
{t('email.new_message.message', '{creatorName} just messaged you!', {creatorName})}
</Text>
<Section style={imageContainer}>
{/*<Link href={messagesUrl}>*/}
{/* <Img*/}
{/* src={userImgSrc}*/}
{/* width="375"*/}
{/* height="200"*/}
{/* alt={`${creatorName}'s profile`}*/}
{/* style={profileImage}*/}
{/* />*/}
{/*</Link>*/}
<Button href={messagesUrl} style={button}>
<Button
href={messagesUrl}
style={{
backgroundColor: '#c17f3e',
borderRadius: '10px',
color: '#ffffff',
fontFamily: "'DM Sans', sans-serif",
fontSize: '15px',
fontWeight: '600',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'inline-block',
padding: '14px 32px',
margin: '0',
border: '1px solid #a6682e',
transition: 'all 0.12s ease',
}}
>
{t('email.new_message.viewButton', 'View message')}
</Button>
</Section>
</div>
<Text style={{...paragraph, fontSize: '12px', color: '#888', marginTop: '32px'}}>
<Text
style={{
fontSize: '13px',
lineHeight: '1.75',
color: '#8c8070',
marginTop: '32px',
textAlign: 'center',
}}
>
{t(
'email.new_message.daily_limit',
"To avoid overloading your inbox, you'll receive at most one email per day per conversation. If {creatorName} sends more messages today, you won't be notified by email.",
@@ -94,7 +147,14 @@ export const NewMessageEmail = ({
'email.new_message.app_prompt',
'Want real-time notifications? Download the Compass app for Android:',
)}{' '}
<Link href={ANDROID_APP_URL} style={{color: '#2563eb', textDecoration: 'none'}}>
<Link
href={ANDROID_APP_URL}
style={{
color: '#c17f3e',
textDecoration: 'none',
fontWeight: '500',
}}
>
{t('email.new_message.app_link', 'Get the app')}
</Link>
{'.'}

View File

@@ -51,29 +51,65 @@ export const NewSearchAlertsEmail = ({
{(matches || []).map((match) => (
<Section key={match.id} style={{marginBottom: '20px'}}>
<Text style={{fontWeight: 'bold', marginBottom: '5px'}}>
{formatFilters(
match.description.filters as Partial<FilterFields>,
match.description.location as locationType,
optionIdsToLabels,
measurementSystem,
t,
)?.join(' • ')}
</Text>
<Text style={{margin: 0}}>
{match.matches.map((p, i) => (
<span key={p.username}>
{p.name} (
<Link
href={`https://${DOMAIN}/${p.username}`}
style={{color: '#2563eb', textDecoration: 'none'}}
<div
style={{
backgroundColor: '#f7f4ef',
border: '1px solid #dee5b2',
borderRadius: '14px',
padding: '0px 20px 10px 20px',
width: '100%',
boxSizing: 'border-box',
}}
>
<Text
style={{
fontWeight: '600',
marginBottom: '14px',
color: '#1e1a14',
fontFamily: "'Cormorant Garamond', serif",
fontSize: '18px',
letterSpacing: '0.01em',
lineHeight: '1.3',
}}
>
{formatFilters(
match.description.filters as Partial<FilterFields>,
match.description.location as locationType,
optionIdsToLabels,
measurementSystem,
t,
)?.join(' • ')}
</Text>
<div style={{display: 'flex', flexWrap: 'wrap', gap: '8px'}}>
{match.matches.map((p) => (
<div
key={p.username}
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '6px',
padding: '6px 12px',
backgroundColor: '#faf3e9',
border: '1px solid #e8c99e',
borderRadius: '100px',
fontSize: '13px',
}}
>
@{p.username}
</Link>
){i < match.matches.length - 1 && ', '}
</span>
))}
</Text>
<span style={{color: '#1e1a14', fontWeight: '500'}}>{p.name}</span>
<Link
href={`https://${DOMAIN}/${p.username}`}
style={{
color: '#c17f3e',
textDecoration: 'none',
fontSize: '12px',
}}
>
@{p.username}
</Link>
</div>
))}
</div>
</div>
</Section>
))}
@@ -88,12 +124,16 @@ export const NewSearchAlertsEmail = ({
href={`https://${DOMAIN}/messages`}
style={{
display: 'inline-block',
backgroundColor: '#2563eb',
backgroundColor: '#c17f3e',
color: '#ffffff',
padding: '12px 20px',
borderRadius: '6px',
padding: '14px 24px',
borderRadius: '10px',
textDecoration: 'none',
fontWeight: 'bold',
fontWeight: '600',
fontSize: '15px',
fontFamily: "'DM Sans', sans-serif",
border: '1px solid #a6682e',
transition: 'all 0.12s ease',
}}
>
{t('email.search_alerts.startConversation', 'Start a Conversation')}
@@ -139,6 +179,7 @@ const matchSamples = [
description: {
filters: {
genders: ['female'],
education: ['doctorate'],
orderBy: 'created_time',
},
location: null,

View File

@@ -12,72 +12,138 @@ interface Props {
export const Footer = ({email, unsubscribeUrl, locale}: Props) => {
const t = createT(locale)
return (
<Section style={footer}>
<hr style={{border: 'none', borderTop: '1px solid #e0e0e0', margin: '10px 0'}} />
<Row>
<Column align="center">
<Link href={`${DEPLOYED_WEB_URL}/github`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/github-logo.png`}
width="24"
height="24"
alt="GitHub"
style={{display: 'inline-block', margin: '0 8px'}}
/>
</Link>
<Link href={`${DEPLOYED_WEB_URL}/discord`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/discord-logo.png`}
width="24"
height="24"
alt="Discord"
style={{display: 'inline-block', margin: '0 8px'}}
/>
</Link>
<Link href={`${DEPLOYED_WEB_URL}/x`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/x-logo.png`}
width="24"
height="24"
alt="X"
style={{display: 'inline-block', margin: '0 8px'}}
/>
</Link>
<Link href={`${DEPLOYED_WEB_URL}/patreon`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/patreon-logo.png`}
width="24"
height="24"
alt="Patreon"
style={{display: 'inline-block', margin: '0 8px'}}
/>
</Link>
<Link href={`${DEPLOYED_WEB_URL}/paypal`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/paypal-logo.png`}
width="24"
height="24"
alt="PayPal"
style={{display: 'inline-block', margin: '0 8px'}}
/>
</Link>
</Column>
</Row>
<Section
style={{
margin: '32px 0 20px 0',
textAlign: 'center' as const,
padding: '20px 0 0 0',
}}
>
<div
style={{
borderTop: '1px solid #dee5b2',
paddingTop: '24px',
marginBottom: '20px',
}}
>
<Row>
<Column align="center">
<Link href={`${DEPLOYED_WEB_URL}/github`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/github-logo.png`}
width="24"
height="24"
alt="GitHub"
style={{
display: 'inline-block',
margin: '0 6px',
opacity: '0.7',
transition: 'opacity 0.12s ease',
}}
/>
</Link>
<Link href={`${DEPLOYED_WEB_URL}/discord`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/discord-logo.png`}
width="24"
height="24"
alt="Discord"
style={{
display: 'inline-block',
margin: '0 6px',
opacity: '0.7',
transition: 'opacity 0.12s ease',
}}
/>
</Link>
<Link href={`${DEPLOYED_WEB_URL}/x`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/x-logo.png`}
width="24"
height="24"
alt="X"
style={{
display: 'inline-block',
margin: '0 6px',
opacity: '0.7',
transition: 'opacity 0.12s ease',
}}
/>
</Link>
<Link href={`${DEPLOYED_WEB_URL}/patreon`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/patreon-logo.png`}
width="24"
height="24"
alt="Patreon"
style={{
display: 'inline-block',
margin: '0 6px',
opacity: '0.7',
transition: 'opacity 0.12s ease',
}}
/>
</Link>
<Link href={`${DEPLOYED_WEB_URL}/paypal`} target="_blank">
<Img
src={`${DEPLOYED_WEB_URL}/images/paypal-logo.png`}
width="24"
height="24"
alt="PayPal"
style={{
display: 'inline-block',
margin: '0 6px',
opacity: '0.7',
transition: 'opacity 0.12s ease',
}}
/>
</Link>
</Column>
</Row>
<Row>
<Text style={{fontSize: '12px', color: '#888', marginTop: '12px'}}>
© {new Date().getFullYear()} Compass
</Text>
<Row>
<Column align="center">
<Text
style={{
fontSize: '12px',
color: '#beaea2',
marginTop: '20px',
fontFamily: "'DM Sans', sans-serif",
fontWeight: '400',
}}
>
© {new Date().getFullYear()} Compass
</Text>
<Text style={{fontSize: '10px', color: '#888', marginTop: '12px'}}>
{t(
'email.footer.sent_to',
'The email was sent to {email}. To no longer receive these emails, unsubscribe',
{email},
)}{' '}
<Link href={unsubscribeUrl}>{t('email.footer.unsubscribe_link', 'here')}</Link>.
</Text>
</Row>
<Text
style={{
fontSize: '11px',
color: '#beaea2',
marginTop: '8px',
lineHeight: '1.6',
fontFamily: "'DM Sans', sans-serif",
}}
>
{t(
'email.footer.sent_to',
'The email was sent to {email}. To no longer receive these emails, unsubscribe',
{email},
)}{' '}
<Link
href={unsubscribeUrl}
style={{
color: '#c17f3e',
textDecoration: 'none',
fontWeight: '500',
}}
>
{t('email.footer.unsubscribe_link', 'here')}
</Link>
.
</Text>
</Column>
</Row>
</div>
</Section>
)
}

View File

@@ -1,6 +1,6 @@
import {Body, Button, Container, Head, Html, Preview, Section, Text} from '@react-email/components'
import {type User} from 'common/user'
import {button, container, content, Footer, main, paragraph} from 'email/utils'
import { container, content, Footer, main, paragraph} from 'email/utils'
import React from 'react'
import {createT} from 'shared/locale'
@@ -33,34 +33,121 @@ export const WelcomeEmail = ({
<Body style={main}>
<Container style={container}>
<Section style={content}>
<Text style={paragraph}>
{t('email.welcome.title', 'Welcome to Compass, {name}!', {name})}
</Text>
<div
style={{
textAlign: 'center',
marginBottom: '32px',
}}
>
<Text
style={{
fontSize: '32px',
fontFamily: "'Cormorant Garamond', serif",
fontWeight: '500',
color: '#1e1a14',
marginBottom: '8px',
letterSpacing: '-0.01em',
lineHeight: '1.1',
}}
>
{t('email.welcome.title', 'Welcome to Compass, {name}!', {name})}
</Text>
</div>
<Text style={paragraph}>
<Text
style={{
...paragraph,
fontSize: '15px',
lineHeight: '1.75',
color: '#8c8070',
marginBottom: '24px',
}}
>
{t(
'email.welcome.intro',
'Compass is a free, community-owned platform built to help people form deep, meaningful connections — platonic, romantic, or collaborative. There are no ads, no hidden algorithms, and no subscriptions — just a transparent, open-source space shaped by people like you.',
)}
</Text>
<Text style={paragraph}>
{t(
'email.welcome.confirmation',
'To finish creating your account and start exploring Compass, please confirm your email below:',
)}
</Text>
<div
style={{
backgroundColor: '#f7f4ef',
border: '1px solid #dee5b2',
borderRadius: '14px',
padding: '24px',
margin: '24px 0',
textAlign: 'center',
}}
>
<Text
style={{
fontSize: '16px',
fontWeight: '500',
color: '#1e1a14',
marginBottom: '16px',
fontFamily: "'DM Sans', sans-serif",
}}
>
{t(
'email.welcome.confirmation',
'To finish creating your account and start exploring Compass, please confirm your email below:',
)}
</Text>
<Button style={button} href={verificationLink}>
{t('email.welcome.confirmButton', 'Confirm My Email')}
</Button>
<Button
href={verificationLink}
style={{
backgroundColor: '#c17f3e',
borderRadius: '10px',
color: '#ffffff',
fontFamily: "'DM Sans', sans-serif",
fontSize: '15px',
fontWeight: '600',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'inline-block',
padding: '14px 32px',
margin: '0',
border: '1px solid #a6682e',
transition: 'all 0.12s ease',
}}
>
{t('email.welcome.confirmButton', 'Confirm My Email')}
</Button>
</div>
<Text style={{marginTop: '40px', fontSize: '10px', color: '#555'}}>
<Text
style={{
marginTop: '32px',
fontSize: '12px',
color: '#beaea2',
textAlign: 'center',
lineHeight: '1.6',
}}
>
{t('email.welcome.copyLink', 'Or copy and paste this link into your browser:')} <br />
<a href={verificationLink}>{verificationLink}</a>
<a
href={verificationLink}
style={{
color: '#c17f3e',
textDecoration: 'none',
wordBreak: 'break-all',
}}
>
{verificationLink}
</a>
</Text>
<Text style={{marginTop: '40px', fontSize: '12px', color: '#555'}}>
<Text
style={{
marginTop: '40px',
fontSize: '13px',
color: '#8c8070',
lineHeight: '1.75',
fontStyle: 'italic',
fontFamily: "'Cormorant Garamond', serif",
}}
>
{t(
'email.welcome.thanks',
'Your presence and participation are what make Compass possible. Thank you for helping us build an internet space that prioritizes depth, trust, and community over monetization.',