a tool for shared writing and social publishing

removed example files, componentized the post email a litte

+303 -739
+123
emails/confirmEmail.tsx
··· 1 + import { 2 + Body, 3 + Column, 4 + Container, 5 + Head, 6 + Heading, 7 + Hr, 8 + Html, 9 + Img, 10 + Link, 11 + Text, 12 + Section, 13 + Row, 14 + Button, 15 + CodeBlock, 16 + } from "@react-email/components"; 17 + import { Tailwind, pixelBasedPreset } from "@react-email/components"; 18 + import { LeafletWatermark } from "./post"; 19 + 20 + export const ConfirmEmail = (props: {}) => ( 21 + <Html> 22 + <Tailwind 23 + config={{ 24 + presets: [pixelBasedPreset], 25 + theme: { 26 + screens: { 27 + sm: "640px", 28 + md: "960px", 29 + lg: "1280px", 30 + }, 31 + borderRadius: { 32 + none: "0", 33 + md: "0.25rem", 34 + lg: "0.5rem", 35 + full: "9999px", 36 + }, 37 + 38 + colors: { 39 + inherit: "inherit", 40 + transparent: "transparent", 41 + current: "currentColor", 42 + 43 + //TEXT COLORS. 44 + primary: "rgb(39, 39, 39)", 45 + secondary: 46 + "color-mix(in oklab, rgb(39, 39, 39), rgb(255, 255, 255) 25%)", 47 + tertiary: 48 + "color-mix(in oklab, rgb(39, 39, 39), rgb(255, 255, 255) 55%)", 49 + border: 50 + "color-mix(in oklab, rgb(39, 39, 39), rgb(255, 255, 255) 75%)", 51 + "border-light": 52 + "color-mix(in oklab, rgb(39, 39, 39), rgb(255, 255, 255) 85%)", 53 + 54 + white: "#FFFFFF", 55 + 56 + //ACCENT COLORS 57 + "accent-1": "rgb(0, 0, 225)", 58 + "accent-2": "rgb(255, 255, 255;)", 59 + "accent-contrast": "rgb(0, 0, 225)", 60 + 61 + //BG COLORS (defined as css variables in global.css) 62 + "bg-leaflet": "rgb(240, 247, 250)", 63 + "bg-page": "rgba(255, 255, 255, 1)", 64 + 65 + // HIGHLIGHT COLORS 66 + "highlight-1": "rgb(255, 177, 177)", 67 + "highlight-2": "rgb(253, 245, 203)", 68 + "highlight-3": "rgb(255, 205, 195)", 69 + 70 + //DO NOT USE IN PRODUCTION. Test colors to aid development, ie, setting bg color on element to see edges of div. DO. NOT. USE. IN. PRODUCTION 71 + test: "#E18181", 72 + "test-blue": "#48D1EF", 73 + }, 74 + fontSize: { 75 + xs: ".75rem", 76 + sm: ".875rem", 77 + base: "1rem", 78 + lg: "1.125rem", 79 + xl: "1.625rem", 80 + "2xl": "2rem", 81 + }, 82 + 83 + extend: { 84 + fontFamily: { 85 + sans: ["Verdana"], 86 + serif: ["Georgia"], 87 + }, 88 + }, 89 + }, 90 + }} 91 + > 92 + <Head /> 93 + 94 + <Body className={`bg-bg-leaflet font-sans p-2 sm:px-4 sm:py-6 !m-0 `}> 95 + <Container className={`bg-transparent rounded-lg border border-border`}> 96 + <Text>Thank you for subscribing to</Text> 97 + <Text> [[PUB NAME HERE]]</Text> 98 + <Text>Here is your verification code</Text> 99 + <Container> 100 + <Heading as="h2">000000</Heading> 101 + </Container> 102 + 103 + <LeafletWatermark /> 104 + </Container> 105 + </Body> 106 + </Tailwind> 107 + </Html> 108 + ); 109 + export default ConfirmEmail; 110 + 111 + const blockPadding = "mt-1 mb-3 sm:mb-4"; 112 + const headerPadding = "mt-1 mb-0"; 113 + 114 + const h1 = `text-xl font-bold ${blockPadding}`; 115 + const h2 = `text-lg font-bold ${headerPadding}`; 116 + const h3 = `text-base font-bold text-secondary ${headerPadding}`; 117 + const primary = `text-base text-primary ${blockPadding}`; 118 + const secondary = `text-base text-secondary ${blockPadding}`; 119 + const tertiary = `text-base text-tertiary ${blockPadding}`; 120 + const list = `my-0 !pl-6`; 121 + const listItem = `${headerPadding} !ml-2`; 122 + const link = `text-base text-accent-contrast ${blockPadding}`; 123 + const image = `${blockPadding} text-center`;
-149
emails/examples-delete/notion-magic-link.tsx
··· 1 - import { 2 - Body, 3 - Container, 4 - Head, 5 - Heading, 6 - Html, 7 - Img, 8 - Link, 9 - Preview, 10 - Text, 11 - } from '@react-email/components'; 12 - 13 - interface NotionMagicLinkEmailProps { 14 - loginCode?: string; 15 - } 16 - 17 - const baseUrl = process.env.VERCEL_URL 18 - ? `https://${process.env.VERCEL_URL}` 19 - : ''; 20 - 21 - export const NotionMagicLinkEmail = ({ 22 - loginCode, 23 - }: NotionMagicLinkEmailProps) => ( 24 - <Html> 25 - <Head /> 26 - <Preview>Log in with this magic link</Preview> 27 - <Body style={main}> 28 - <Container style={container}> 29 - <Heading style={h1}>Login</Heading> 30 - <Link 31 - href="https://notion.so" 32 - target="_blank" 33 - style={{ 34 - ...link, 35 - display: 'block', 36 - marginBottom: '16px', 37 - }} 38 - > 39 - Click here to log in with this magic link 40 - </Link> 41 - <Text style={{ ...text, marginBottom: '14px' }}> 42 - Or, copy and paste this temporary login code: 43 - </Text> 44 - <code style={code}>{loginCode}</code> 45 - <Text 46 - style={{ 47 - ...text, 48 - color: '#ababab', 49 - marginTop: '14px', 50 - marginBottom: '16px', 51 - }} 52 - > 53 - If you didn&apos;t try to login, you can safely ignore this email. 54 - </Text> 55 - <Text 56 - style={{ 57 - ...text, 58 - color: '#ababab', 59 - marginTop: '12px', 60 - marginBottom: '38px', 61 - }} 62 - > 63 - Hint: You can set a permanent password in Settings & members → My 64 - account. 65 - </Text> 66 - <Img 67 - src={`${baseUrl}/static/notion-logo.png`} 68 - width="32" 69 - height="32" 70 - alt="Notion's Logo" 71 - /> 72 - <Text style={footer}> 73 - <Link 74 - href="https://notion.so" 75 - target="_blank" 76 - style={{ ...link, color: '#898989' }} 77 - > 78 - Notion.so 79 - </Link> 80 - , the all-in-one-workspace 81 - <br /> 82 - for your notes, tasks, wikis, and databases. 83 - </Text> 84 - </Container> 85 - </Body> 86 - </Html> 87 - ); 88 - 89 - NotionMagicLinkEmail.PreviewProps = { 90 - loginCode: 'sparo-ndigo-amurt-secan', 91 - } as NotionMagicLinkEmailProps; 92 - 93 - export default NotionMagicLinkEmail; 94 - 95 - const main = { 96 - backgroundColor: '#ffffff', 97 - }; 98 - 99 - const container = { 100 - paddingLeft: '12px', 101 - paddingRight: '12px', 102 - margin: '0 auto', 103 - }; 104 - 105 - const h1 = { 106 - color: '#333', 107 - fontFamily: 108 - "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", 109 - fontSize: '24px', 110 - fontWeight: 'bold', 111 - margin: '40px 0', 112 - padding: '0', 113 - }; 114 - 115 - const link = { 116 - color: '#2754C5', 117 - fontFamily: 118 - "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", 119 - fontSize: '14px', 120 - textDecoration: 'underline', 121 - }; 122 - 123 - const text = { 124 - color: '#333', 125 - fontFamily: 126 - "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", 127 - fontSize: '14px', 128 - margin: '24px 0', 129 - }; 130 - 131 - const footer = { 132 - color: '#898989', 133 - fontFamily: 134 - "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", 135 - fontSize: '12px', 136 - lineHeight: '22px', 137 - marginTop: '12px', 138 - marginBottom: '24px', 139 - }; 140 - 141 - const code = { 142 - display: 'inline-block', 143 - padding: '16px 4.5%', 144 - width: '90.5%', 145 - backgroundColor: '#f4f4f4', 146 - borderRadius: '5px', 147 - border: '1px solid #eee', 148 - color: '#333', 149 - };
-157
emails/examples-delete/plaid-verify-identity.tsx
··· 1 - import { 2 - Body, 3 - Container, 4 - Head, 5 - Heading, 6 - Html, 7 - Img, 8 - Link, 9 - Section, 10 - Text, 11 - } from '@react-email/components'; 12 - 13 - interface PlaidVerifyIdentityEmailProps { 14 - validationCode?: string; 15 - } 16 - 17 - const baseUrl = process.env.VERCEL_URL 18 - ? `https://${process.env.VERCEL_URL}` 19 - : ''; 20 - 21 - export const PlaidVerifyIdentityEmail = ({ 22 - validationCode, 23 - }: PlaidVerifyIdentityEmailProps) => ( 24 - <Html> 25 - <Head /> 26 - <Body style={main}> 27 - <Container style={container}> 28 - <Img 29 - src={`${baseUrl}/static/plaid-logo.png`} 30 - width="212" 31 - height="88" 32 - alt="Plaid" 33 - style={logo} 34 - /> 35 - <Text style={tertiary}>Verify Your Identity</Text> 36 - <Heading style={secondary}> 37 - Enter the following code to finish linking Venmo. 38 - </Heading> 39 - <Section style={codeContainer}> 40 - <Text style={code}>{validationCode}</Text> 41 - </Section> 42 - <Text style={paragraph}>Not expecting this email?</Text> 43 - <Text style={paragraph}> 44 - Contact{' '} 45 - <Link href="mailto:login@plaid.com" style={link}> 46 - login@plaid.com 47 - </Link>{' '} 48 - if you did not request this code. 49 - </Text> 50 - </Container> 51 - <Text style={footer}>Securely powered by Plaid.</Text> 52 - </Body> 53 - </Html> 54 - ); 55 - 56 - PlaidVerifyIdentityEmail.PreviewProps = { 57 - validationCode: '144833', 58 - } as PlaidVerifyIdentityEmailProps; 59 - 60 - export default PlaidVerifyIdentityEmail; 61 - 62 - const main = { 63 - backgroundColor: '#ffffff', 64 - fontFamily: 'HelveticaNeue,Helvetica,Arial,sans-serif', 65 - }; 66 - 67 - const container = { 68 - backgroundColor: '#ffffff', 69 - border: '1px solid #eee', 70 - borderRadius: '5px', 71 - boxShadow: '0 5px 10px rgba(20,50,70,.2)', 72 - marginTop: '20px', 73 - maxWidth: '360px', 74 - margin: '0 auto', 75 - padding: '68px 0 130px', 76 - }; 77 - 78 - const logo = { 79 - margin: '0 auto', 80 - }; 81 - 82 - const tertiary = { 83 - color: '#0a85ea', 84 - fontSize: '11px', 85 - fontWeight: 700, 86 - fontFamily: 'HelveticaNeue,Helvetica,Arial,sans-serif', 87 - height: '16px', 88 - letterSpacing: '0', 89 - lineHeight: '16px', 90 - margin: '16px 8px 8px 8px', 91 - textTransform: 'uppercase' as const, 92 - textAlign: 'center' as const, 93 - }; 94 - 95 - const secondary = { 96 - color: '#000', 97 - display: 'inline-block', 98 - fontFamily: 'HelveticaNeue-Medium,Helvetica,Arial,sans-serif', 99 - fontSize: '20px', 100 - fontWeight: 500, 101 - lineHeight: '24px', 102 - marginBottom: '0', 103 - marginTop: '0', 104 - textAlign: 'center' as const, 105 - }; 106 - 107 - const codeContainer = { 108 - background: 'rgba(0,0,0,.05)', 109 - borderRadius: '4px', 110 - margin: '16px auto 14px', 111 - verticalAlign: 'middle', 112 - width: '280px', 113 - }; 114 - 115 - const code = { 116 - color: '#000', 117 - display: 'inline-block', 118 - fontFamily: 'HelveticaNeue-Bold', 119 - fontSize: '32px', 120 - fontWeight: 700, 121 - letterSpacing: '6px', 122 - lineHeight: '40px', 123 - paddingBottom: '8px', 124 - paddingTop: '8px', 125 - margin: '0 auto', 126 - width: '100%', 127 - textAlign: 'center' as const, 128 - }; 129 - 130 - const paragraph = { 131 - color: '#444', 132 - fontSize: '15px', 133 - fontFamily: 'HelveticaNeue,Helvetica,Arial,sans-serif', 134 - letterSpacing: '0', 135 - lineHeight: '23px', 136 - padding: '0 40px', 137 - margin: '0', 138 - textAlign: 'center' as const, 139 - }; 140 - 141 - const link = { 142 - color: '#444', 143 - textDecoration: 'underline', 144 - }; 145 - 146 - const footer = { 147 - color: '#000', 148 - fontSize: '12px', 149 - fontWeight: 800, 150 - letterSpacing: '0', 151 - lineHeight: '23px', 152 - margin: '0', 153 - marginTop: '20px', 154 - fontFamily: 'HelveticaNeue,Helvetica,Arial,sans-serif', 155 - textAlign: 'center' as const, 156 - textTransform: 'uppercase' as const, 157 - };
emails/examples-delete/static/notion-logo.png

This is a binary file and will not be displayed.

emails/examples-delete/static/plaid-logo.png

This is a binary file and will not be displayed.

emails/examples-delete/static/plaid.png

This is a binary file and will not be displayed.

emails/examples-delete/static/stripe-logo.png

This is a binary file and will not be displayed.

emails/examples-delete/static/vercel-arrow.png

This is a binary file and will not be displayed.

emails/examples-delete/static/vercel-logo.png

This is a binary file and will not be displayed.

emails/examples-delete/static/vercel-team.png

This is a binary file and will not be displayed.

emails/examples-delete/static/vercel-user.png

This is a binary file and will not be displayed.

-151
emails/examples-delete/stripe-welcome.tsx
··· 1 - import { 2 - Body, 3 - Button, 4 - Container, 5 - Head, 6 - Hr, 7 - Html, 8 - Img, 9 - Link, 10 - Preview, 11 - Section, 12 - Text, 13 - } from '@react-email/components'; 14 - 15 - const baseUrl = process.env.VERCEL_URL 16 - ? `https://${process.env.VERCEL_URL}` 17 - : ''; 18 - 19 - export const StripeWelcomeEmail = () => ( 20 - <Html> 21 - <Head /> 22 - <Preview>You're now ready to make live transactions with Stripe!</Preview> 23 - <Body style={main}> 24 - <Container style={container}> 25 - <Section style={box}> 26 - <Img 27 - src={`${baseUrl}/static/stripe-logo.png`} 28 - width="49" 29 - height="21" 30 - alt="Stripe" 31 - /> 32 - <Hr style={hr} /> 33 - <Text style={paragraph}> 34 - Thanks for submitting your account information. You're now ready to 35 - make live transactions with Stripe! 36 - </Text> 37 - <Text style={paragraph}> 38 - You can view your payments and a variety of other information about 39 - your account right from your dashboard. 40 - </Text> 41 - <Button style={button} href="https://dashboard.stripe.com/login"> 42 - View your Stripe Dashboard 43 - </Button> 44 - <Hr style={hr} /> 45 - <Text style={paragraph}> 46 - If you haven't finished your integration, you might find our{' '} 47 - <Link style={anchor} href="https://stripe.com/docs"> 48 - docs 49 - </Link>{' '} 50 - handy. 51 - </Text> 52 - <Text style={paragraph}> 53 - Once you're ready to start accepting payments, you'll just need to 54 - use your live{' '} 55 - <Link 56 - style={anchor} 57 - href="https://dashboard.stripe.com/login?redirect=%2Fapikeys" 58 - > 59 - API keys 60 - </Link>{' '} 61 - instead of your test API keys. Your account can simultaneously be 62 - used for both test and live requests, so you can continue testing 63 - while accepting live payments. Check out our{' '} 64 - <Link style={anchor} href="https://stripe.com/docs/dashboard"> 65 - tutorial about account basics 66 - </Link> 67 - . 68 - </Text> 69 - <Text style={paragraph}> 70 - Finally, we've put together a{' '} 71 - <Link 72 - style={anchor} 73 - href="https://stripe.com/docs/checklist/website" 74 - > 75 - quick checklist 76 - </Link>{' '} 77 - to ensure your website conforms to card network standards. 78 - </Text> 79 - <Text style={paragraph}> 80 - We'll be here to help you with any step along the way. You can find 81 - answers to most questions and get in touch with us on our{' '} 82 - <Link style={anchor} href="https://support.stripe.com/"> 83 - support site 84 - </Link> 85 - . 86 - </Text> 87 - <Text style={paragraph}>— The Stripe team</Text> 88 - <Hr style={hr} /> 89 - <Text style={footer}> 90 - Stripe, 354 Oyster Point Blvd, South San Francisco, CA 94080 91 - </Text> 92 - </Section> 93 - </Container> 94 - </Body> 95 - </Html> 96 - ); 97 - 98 - export default StripeWelcomeEmail; 99 - 100 - const main = { 101 - backgroundColor: '#f6f9fc', 102 - fontFamily: 103 - '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif', 104 - }; 105 - 106 - const container = { 107 - backgroundColor: '#ffffff', 108 - margin: '0 auto', 109 - padding: '20px 0 48px', 110 - marginBottom: '64px', 111 - }; 112 - 113 - const box = { 114 - padding: '0 48px', 115 - }; 116 - 117 - const hr = { 118 - borderColor: '#e6ebf1', 119 - margin: '20px 0', 120 - }; 121 - 122 - const paragraph = { 123 - color: '#525f7f', 124 - 125 - fontSize: '16px', 126 - lineHeight: '24px', 127 - textAlign: 'left' as const, 128 - }; 129 - 130 - const anchor = { 131 - color: '#556cd6', 132 - }; 133 - 134 - const button = { 135 - backgroundColor: '#656ee8', 136 - borderRadius: '5px', 137 - color: '#fff', 138 - fontSize: '16px', 139 - fontWeight: 'bold', 140 - textDecoration: 'none', 141 - textAlign: 'center' as const, 142 - display: 'block', 143 - width: '100%', 144 - padding: '10px', 145 - }; 146 - 147 - const footer = { 148 - color: '#8898aa', 149 - fontSize: '12px', 150 - lineHeight: '16px', 151 - };
-153
emails/examples-delete/vercel-invite-user.tsx
··· 1 - import { 2 - Body, 3 - Button, 4 - Column, 5 - Container, 6 - Head, 7 - Heading, 8 - Hr, 9 - Html, 10 - Img, 11 - Link, 12 - Preview, 13 - Row, 14 - Section, 15 - Tailwind, 16 - Text, 17 - } from '@react-email/components'; 18 - 19 - interface VercelInviteUserEmailProps { 20 - username?: string; 21 - userImage?: string; 22 - invitedByUsername?: string; 23 - invitedByEmail?: string; 24 - teamName?: string; 25 - teamImage?: string; 26 - inviteLink?: string; 27 - inviteFromIp?: string; 28 - inviteFromLocation?: string; 29 - } 30 - 31 - const baseUrl = process.env.VERCEL_URL 32 - ? `https://${process.env.VERCEL_URL}` 33 - : ''; 34 - 35 - export const VercelInviteUserEmail = ({ 36 - username, 37 - userImage, 38 - invitedByUsername, 39 - invitedByEmail, 40 - teamName, 41 - teamImage, 42 - inviteLink, 43 - inviteFromIp, 44 - inviteFromLocation, 45 - }: VercelInviteUserEmailProps) => { 46 - const previewText = `Join ${invitedByUsername} on Vercel`; 47 - 48 - return ( 49 - <Html> 50 - <Head /> 51 - <Preview>{previewText}</Preview> 52 - <Tailwind> 53 - <Body className="mx-auto my-auto bg-white px-2 font-sans"> 54 - <Container className="mx-auto my-[40px] max-w-[465px] rounded border border-[#eaeaea] border-solid p-[20px]"> 55 - <Section className="mt-[32px]"> 56 - <Img 57 - src={`${baseUrl}/static/vercel-logo.png`} 58 - width="40" 59 - height="37" 60 - alt="Vercel" 61 - className="mx-auto my-0" 62 - /> 63 - </Section> 64 - <Heading className="mx-0 my-[30px] p-0 text-center font-normal text-[24px] text-black"> 65 - Join <strong>{teamName}</strong> on <strong>Vercel</strong> 66 - </Heading> 67 - <Text className="text-[14px] text-black leading-[24px]"> 68 - Hello {username}, 69 - </Text> 70 - <Text className="text-[14px] text-black leading-[24px]"> 71 - <strong>{invitedByUsername}</strong> ( 72 - <Link 73 - href={`mailto:${invitedByEmail}`} 74 - className="text-blue-600 no-underline" 75 - > 76 - {invitedByEmail} 77 - </Link> 78 - ) has invited you to the <strong>{teamName}</strong> team on{' '} 79 - <strong>Vercel</strong>. 80 - </Text> 81 - <Section> 82 - <Row> 83 - <Column align="right"> 84 - <Img 85 - className="rounded-full" 86 - src={userImage} 87 - width="64" 88 - height="64" 89 - /> 90 - </Column> 91 - <Column align="center"> 92 - <Img 93 - src={`${baseUrl}/static/vercel-arrow.png`} 94 - width="12" 95 - height="9" 96 - alt="invited you to" 97 - /> 98 - </Column> 99 - <Column align="left"> 100 - <Img 101 - className="rounded-full" 102 - src={teamImage} 103 - width="64" 104 - height="64" 105 - /> 106 - </Column> 107 - </Row> 108 - </Section> 109 - <Section className="mt-[32px] mb-[32px] text-center"> 110 - <Button 111 - className="rounded bg-[#000000] px-5 py-3 text-center font-semibold text-[12px] text-white no-underline" 112 - href={inviteLink} 113 - > 114 - Join the team 115 - </Button> 116 - </Section> 117 - <Text className="text-[14px] text-black leading-[24px]"> 118 - or copy and paste this URL into your browser:{' '} 119 - <Link href={inviteLink} className="text-blue-600 no-underline"> 120 - {inviteLink} 121 - </Link> 122 - </Text> 123 - <Hr className="mx-0 my-[26px] w-full border border-[#eaeaea] border-solid" /> 124 - <Text className="text-[#666666] text-[12px] leading-[24px]"> 125 - This invitation was intended for{' '} 126 - <span className="text-black">{username}</span>. This invite was 127 - sent from <span className="text-black">{inviteFromIp}</span>{' '} 128 - located in{' '} 129 - <span className="text-black">{inviteFromLocation}</span>. If you 130 - were not expecting this invitation, you can ignore this email. If 131 - you are concerned about your account's safety, please reply to 132 - this email to get in touch with us. 133 - </Text> 134 - </Container> 135 - </Body> 136 - </Tailwind> 137 - </Html> 138 - ); 139 - }; 140 - 141 - VercelInviteUserEmail.PreviewProps = { 142 - username: 'alanturing', 143 - userImage: `${baseUrl}/static/vercel-user.png`, 144 - invitedByUsername: 'Alan', 145 - invitedByEmail: 'alan.turing@example.com', 146 - teamName: 'Enigma', 147 - teamImage: `${baseUrl}/static/vercel-team.png`, 148 - inviteLink: 'https://vercel.com/teams/invite/foo', 149 - inviteFromIp: '204.13.186.218', 150 - inviteFromLocation: 'São Paulo, Brazil', 151 - } as VercelInviteUserEmailProps; 152 - 153 - export default VercelInviteUserEmail;
+180 -129
emails/post.tsx
··· 3 3 Column, 4 4 Container, 5 5 Head, 6 - Heading, 6 + Heading as ReactEmailHeading, 7 7 Hr, 8 8 Html, 9 9 Img, 10 10 Link, 11 - Text, 11 + Text as ReactEmailText, 12 12 Section, 13 13 Row, 14 14 Button, 15 - CodeBlock, 15 + CodeBlock as ReactEmailCodeBlock, 16 16 dracula, 17 17 } from "@react-email/components"; 18 18 import { Tailwind, pixelBasedPreset } from "@react-email/components"; 19 - 20 - let borderless = false; 19 + import { Block } from "components/Blocks/Block"; 20 + import React from "react"; 21 21 22 22 export const PostEmail = (props: {}) => ( 23 23 <Html> ··· 110 110 </Row> 111 111 </Button> 112 112 113 - <Heading as="h1" className={`${h1} !mt-0 !mb-0`}> 113 + <Heading as="h1" noPadding> 114 114 Post Title Here 115 115 </Heading> 116 - <Text className={`${secondary} italic !mb-0 !mt-1`}> 116 + <Text noPadding className={`text-secondary italic pt-1`}> 117 117 Hello this is a description of everything that is to come 118 118 </Text> 119 119 120 - <Section className={`${tertiary} text-sm !mb-7 !mt-3`}> 120 + <Section className={`!mb-7 !mt-3`}> 121 121 <Row> 122 - <Column width="auto">celine | Jun 23, 2025 </Column> 122 + <Column width="auto"> 123 + <Text className="text-sm text-tertiary"> 124 + celine | Jun 23, 2025 125 + </Text> 126 + </Column> 123 127 <Column style={{ width: "16px" }}> 124 128 <Button href="/"> 125 129 <Img width={16} height={16} src="/static/quote.png" /> ··· 142 146 </Row> 143 147 </Section> 144 148 <Section className="postContent"> 145 - <Text className={primary}> 149 + <Text> 146 150 This would be the post. I'll give it a little lorem ipsum to make 147 151 it look longer so i don't forget which thing is what. 148 152 </Text> 149 - <Text className={primary}> 153 + <Text> 150 154 Here's what an image block looks like. It also needs to align if 151 155 you have a small picture. Let's see what that looks like. I will 152 156 need to import a new one and that sounds like so much work but 153 157 whatever, it's easy actually im just lazy 154 158 </Text> 155 159 <Img width="100%" src="/static/test.jpg" className={`${image}`} /> 156 - <Text className={primary}>And here we have all the Headers</Text> 157 - <Heading as="h1" className={h1}> 158 - This is an Title 159 - </Heading> 160 - <Text className={primary}> 160 + <Text>And here we have all the Headers</Text> 161 + <Heading as="h1">This is an Title</Heading> 162 + <Text> 161 163 We'll keep it nice an separate to i can see what it looks like. 162 164 After all i want this to look like a real text document. 163 165 </Text> 164 166 165 - <Heading as="h2" className={h2}> 166 - And a Header 167 - </Heading> 168 - <Text className={primary}> 167 + <Heading as="h2">And a Header</Heading> 168 + <Text> 169 169 If i didn't do this it would be difficult to know waht things look 170 170 like so its important to get jiggy with the flavor text 171 171 </Text> 172 - <Heading as="h2" className={h3}> 173 - And finally a SubHeader 174 - </Heading> 175 - <Text className={primary}> 172 + <Heading as="h3">And finally a SubHeader</Heading> 173 + <Text> 176 174 It ain't easy to be jiggy but I make it look breezy. Like you 177 175 rhymes are cheesy, I'm allergic they make me sneezy. Besides, 178 176 relying on rhymes is sleazy like yo, it's measely. 179 177 </Text> 180 178 181 - <Text className={primary}>how about lists????? </Text> 182 - <Section className={`${blockPadding} !-mt-1`}> 183 - <ul className={list}> 184 - <li className={listItem}>fruits</li> 185 - <ul className={list}> 186 - <li className={listItem}>apple</li> 187 - <li className={listItem}>banana</li> 188 - <li className={listItem}>loop</li> 189 - </ul> 190 - <li className={listItem}>veggies</li> 191 - <li className={listItem}>other</li> 192 - </ul> 193 - </Section> 179 + <Text>how about lists????? </Text> 180 + <List /> 194 181 195 - <Text className={primary}>and blockquote!!! </Text> 182 + <Text>and blockquote!!! </Text> 196 183 <Row className={blockPadding}> 197 184 <Column className="!my-0 w-[2px] bg-border" /> 198 185 <Column className="w-2" /> 199 186 <Column> 200 - <Text className="my-0.5 text-base"> 187 + <Text className="!my-0.5"> 201 188 Hi this is some text. I want to make it wrap this so that it's 202 189 multiline but idk how long that will end up being 203 190 </Text> 204 191 </Column> 205 192 </Row> 206 - <Text className={primary}>code block??? </Text> 207 - <CodeBlock 208 - className={`${blockPadding} !p-2 rounded-md border border-light`} 209 - code={`export default async (req, res) => { 210 - try { 211 - const html = await renderAsync( 212 - EmailTemplate({ firstName: 'John' }) 213 - ); 214 - return NextResponse.json({ html }); 215 - } catch (error) { 216 - return NextResponse.json({ error }); 217 - } 218 - }`} 219 - theme={dracula} 220 - language="javascript" 221 - /> 222 - <Text className={primary}> 193 + <Text>code block??? </Text> 194 + <CodeBlock /> 195 + 196 + <Text> 223 197 I don't even know if we an do external link but why not give it a 224 - go.{" "} 198 + go. 225 199 </Text> 200 + <LinkBlock /> 226 201 227 - <Row 228 - border={1} 229 - className={`${blockPadding} h-[104px] border-accent-contrast rounded-lg !p-0 border-solid`} 230 - > 231 - <Column 232 - style={{ verticalAlign: "top" }} 233 - className="border-transparent py-1 px-2 " 234 - > 235 - <Text className={`text-base font-bold !my-0 `}> 236 - Link Title Here 237 - </Text> 238 - {/* what happens if the description is super long? is there 239 - anyway to truncate it? overflow is only partially supported by 240 - email */} 241 - <Text className={`text-base text-secondary !my-0`}> 242 - Description on the link if such a thing is applicable. 243 - </Text> 244 - <Text className={`text-accent-contrast italic text-sm !my-0 `}> 245 - www.example.com 246 - </Text> 247 - </Column> 248 - <Column className="border-none w-24 pr-2 pt-2"> 249 - <Container className="bg-test rounded-t-md w-full h-full" /> 250 - </Column> 251 - </Row> 252 - 253 - <Text className={primary}> 254 - can't forget about the horizontal rule 255 - </Text> 202 + <Text>can't forget about the horizontal rule</Text> 256 203 257 204 <Hr className="border-border-light my-3" /> 258 205 259 - <Text className={primary}> 206 + <Text> 260 207 and what if we don't support it? like math and code blocks? though 261 208 maybe we should support code? there's a component after all. 262 209 </Text> 263 - <Container 264 - className={`bg-border-light h-20 rounded-md text-tertiary ${blockPadding}`} 265 - > 266 - <Text className={"text-tertiary text-sm text-center my-0 italic"}> 267 - This media isn't supported in email... 268 - </Text> 269 - <Text className="text-center my-0"> 270 - <Link 271 - className={`w-full text-accent-contrast text-sm text-center font-bold`} 272 - > 273 - See full post 274 - </Link> 275 - </Text> 276 - </Container> 210 + <BlockNotSupported /> 277 211 </Section> 278 212 279 - <Text className="my-0 text-center leading-5"> 213 + <Text noPadding className="text-center leading-5"> 280 214 <Button className={`${link} font-bold text-sm leading-5 !my-0`}> 281 215 See Full Post 282 216 </Button> 283 217 </Text> 284 - <Text className="my-0 text-center leading-5"> 285 - <Button className={`${tertiary} text-sm leading-5 !my-0`}> 286 - Unsubscribe 287 - </Button> 218 + <Text 219 + noPadding 220 + className="text-sm text-tertiary text-center leading-5" 221 + > 222 + <Button className={`leading-5 !my-0`}>Unsubscribe</Button> 288 223 </Text> 289 224 </Container> 290 225 <Hr className="border-border-light mt-6 mb-3" /> 291 226 292 - <Container className={` w-fit `}> 293 - <Button href="/"> 294 - <Row className={`${tertiary} italic text-sm`}> 295 - <Column style={{ width: "16px" }}> 296 - <Img width={16} height={16} src="/static/leaflet.png" /> 297 - </Column> 298 - <Column width="4px" /> 299 - <Column style={{ width: "164px" }}> 300 - Published with{" "} 301 - <Link className={`${link} font-bold text-sm`}>Leaflet</Link> 302 - </Column> 303 - </Row> 304 - </Button> 305 - </Container> 227 + <LeafletWatermark /> 306 228 </Body> 307 229 </Tailwind> 308 230 </Html> 309 231 ); 310 232 export default PostEmail; 311 233 234 + export const LeafletWatermark = () => { 235 + return ( 236 + <Container className={` w-fit `}> 237 + <Button href="/"> 238 + <Row className={`text-tertiary italic text-sm`}> 239 + <Column style={{ width: "16px" }}> 240 + <Img width={16} height={16} src="/static/leaflet.png" /> 241 + </Column> 242 + <Column width="4px" /> 243 + <Column style={{ width: "164px" }}> 244 + Published with{" "} 245 + <Link className={`${link} font-bold text-sm`}>Leaflet</Link> 246 + </Column> 247 + </Row> 248 + </Button> 249 + </Container> 250 + ); 251 + }; 252 + 312 253 const blockPadding = "mt-1 mb-3 sm:mb-4"; 313 - const headerPadding = "mt-1 mb-0"; 254 + const headingPadding = "mt-1 mb-0"; 255 + 256 + export const Text = (props: { 257 + children: React.ReactNode; 258 + noPadding?: boolean; 259 + small?: boolean; 260 + className?: string; 261 + }) => { 262 + return ( 263 + <ReactEmailText 264 + className={`text-primary ${props.small ? "text-sm" : "text-base"} ${props.noPadding ? "!my-0" : blockPadding} ${props.className}`} 265 + > 266 + {props.children} 267 + </ReactEmailText> 268 + ); 269 + }; 314 270 315 - const h1 = `text-xl font-bold ${blockPadding}`; 316 - const h2 = `text-lg font-bold ${headerPadding}`; 317 - const h3 = `text-base font-bold text-secondary ${headerPadding}`; 318 - const primary = `text-base text-primary ${blockPadding}`; 319 - const secondary = `text-base text-secondary ${blockPadding}`; 320 - const tertiary = `text-base text-tertiary ${blockPadding}`; 321 - const list = `my-0 !pl-6`; 322 - const listItem = `${headerPadding} !ml-2`; 271 + export const Heading = (props: { 272 + children: React.ReactNode; 273 + noPadding?: boolean; 274 + as: "h1" | "h2" | "h3"; 275 + className?: string; 276 + }) => { 277 + return ( 278 + <ReactEmailHeading 279 + as={props.as} 280 + className={` font-bold ${props.noPadding ? "!my-0" : headingPadding} ${props.as === "h1" ? "text-xl" : props.as === "h2" ? "text-lg" : "text-base text-secondary"}`} 281 + > 282 + {props.children} 283 + </ReactEmailHeading> 284 + ); 285 + }; 286 + 287 + export const List = () => { 288 + let list = `my-0 !pl-6`; 289 + let listItem = `${headingPadding} !ml-2`; 290 + return ( 291 + <Section className={`${blockPadding} !-mt-1`}> 292 + <ul className={list}> 293 + <li className={listItem}>fruits</li> 294 + <ul className={list}> 295 + <li className={listItem}>apple</li> 296 + <li className={listItem}>banana</li> 297 + <li className={listItem}>loop</li> 298 + </ul> 299 + <li className={listItem}>veggies</li> 300 + <li className={listItem}>other</li> 301 + </ul> 302 + </Section> 303 + ); 304 + }; 305 + 306 + export const LinkBlock = () => { 307 + return ( 308 + <Row 309 + border={1} 310 + className={`${blockPadding} h-[104px] border-accent-contrast rounded-lg !p-0 border-solid`} 311 + > 312 + <Column 313 + style={{ verticalAlign: "top" }} 314 + className="border-transparent py-1 px-2 " 315 + > 316 + <Text noPadding className={`font-bold`}> 317 + Link Title Here 318 + </Text> 319 + {/* what happens if the description is super long? is there 320 + anyway to truncate it? overflow is only partially supported by 321 + email */} 322 + <Text noPadding className={`text-secondary`}> 323 + Description on the link if such a thing is applicable. 324 + </Text> 325 + <Text small noPadding className={`text-accent-contrast italic`}> 326 + www.example.com 327 + </Text> 328 + </Column> 329 + <Column className="border-none w-28 pr-2 pt-2"> 330 + <Container className="bg-test rounded-t-md w-full h-full" /> 331 + </Column> 332 + </Row> 333 + ); 334 + }; 335 + 336 + export const CodeBlock = () => { 337 + return ( 338 + <ReactEmailCodeBlock 339 + className={`${blockPadding} !p-2 rounded-md border border-light`} 340 + code={`export default async (req, res) => { 341 + try { 342 + const html = await renderAsync( 343 + EmailTemplate({ firstName: 'John' }) 344 + ); 345 + return NextResponse.json({ html }); 346 + } catch (error) { 347 + return NextResponse.json({ error }); 348 + } 349 + }`} 350 + theme={dracula} 351 + language="javascript" 352 + /> 353 + ); 354 + }; 355 + export const BlockNotSupported = () => { 356 + return ( 357 + <Container 358 + className={`bg-border-light h-20 rounded-md text-tertiary ${blockPadding}`} 359 + > 360 + <Text noPadding small className={"text-tertiary text-center italic"}> 361 + This media isn't supported in email... 362 + </Text> 363 + <Text noPadding small className="text-center"> 364 + <Link 365 + className={`w-full text-accent-contrast text-sm text-center font-bold`} 366 + > 367 + See full post 368 + </Link> 369 + </Text> 370 + </Container> 371 + ); 372 + }; 373 + 323 374 const link = `text-base text-accent-contrast ${blockPadding}`; 324 375 const image = `${blockPadding} text-center`;