Tag: Email

How to Safely Share Your Email Address on a Website

Spammers are a huge deal nowadays. If you want to share your contact information without getting overwhelmed by spam email you need a solution. I run into this problem a few months ago. While I was researching how to solve it, I found different interesting solutions. Only one of them was perfect for my needs.

In this article, I am going to show you how to easily protect your email address from spam bots with multiple solutions. It’s up to you to decide what technique fits your needs.

The traditional case

Let’s say that you have a website. You want to share your contact details, and you don’t want to share only your social links. The email address must be there. Easy, right? You type something like this:

<a href="mailto:email@address.com">Send me an Email</a>

And then you style it according to your tastes.

Well, even if this solution works, it has a problem. It makes your email address available to literally everyone, including website crawlers and all sorts of spam bots. This means that your inbox can be flooded with tons of unwanted rubbish like promotional offers or even some phishing campaign.

We are looking for a compromise. We want to make it hard for bots to get our email addresses, but as simple as possible for normal users.

The solution is obfuscation.

Obfuscation is the practice of making something difficult to understand. This strategy is used with source code for multiple reasons. One of them is hiding the purpose of the source code to make tampering or reverse-engineering more difficult. We will first look at different solutions that are all based on the idea of obfuscation.

The HTML approach

We can think of bots as software that browse the web and crawl through web pages. Once a bot obtains an HTML document, it interprets the content in it and extracts information. This extraction process is called web scraping. If a bot is looking for a pattern that matches the email format, we can try to disguise it by using a different format. For example, we could use HTML comments:

<p>If you want to get in touch, please drop me an email at<!-- fhetydagzzzgjds --> email@<!-- sdfjsdhfkjypcs -->addr<!-- asjoxp -->ess.com</p>

It looks messy, but the user will see the email address like this:

If you want to get in touch, please drop me an email at email@address.com


  • Easy to set up.
  • It works with JavaScript disabled.
  • It can be read by assistive technology.


  • Spam bots can skip known sequences like comments.
  • It doesn’t work with a mailto: link.

The HTML & CSS approach

What if we use the styling power of CSS to remove some content placed only to fool spam bots? Let’s say that we have the same content as before, but this time we place a span element inside:

<p>If you want to get in touch, please drop me an email at <span class="blockspam" aria-hidden="true">PLEASE GO AWAY!</span> email@<!-- sdfjsdhfkjypcs -->address.com</p>.

Then, we use the following CSS style rule:

span.blockspam {   display: none; }

The final user will only see this:

If you want to get in touch, please drop me an email at email@address.com.

…which is the content we truly care about.


  • It works with JavaScript disabled.
  • It’s more difficult for bots to get the email address.
  • It can be read by assistive technology.


  • It doesn’t work with a mailto: link.

The JavaScript approach

In this example, we use JavaScript to make our email address unreadable. Then, when the page is loaded, JavaScript makes the email address readable again. This way, our users can get the email address.

The easiest solution uses the Base64 encoding algorithm to decode the email address. First, we need to encode the email address in Base64. We can use some websites like Base64Encode.org to do this. Type in your email address like this:

A large textarea to paste an email address with a series of options beneath it for how to encode the text.

Then, click the button to encode. With these few lines of JavaScript we decode the email address and set the href attribute in the HTML link:

var encEmail = "ZW1haWxAYWRkcmVzcy5jb20="; const form = document.getElementById("contact"); form.setAttribute("href", "mailto:".concat(atob(encEmail)));

Then we have to make sure the email link includes id="contact" in the markup, like this:

<a id="contact" href="">Send me an Email</a>

We are using the atob method to decode a string of Base64-encoded data. An alternative is to use some basic encryption algorithm like the Caesar cipher, which is fairly straightforward to implement in JavaScript.


  • It’s more complicated for bots to get the email address, especially if you use an encryption algorithm.
  • It works with a mailto: link.
  • It can be read by assistive technology.


  • JavaScript must be enabled on the browser, otherwise, the link will be empty.

The embedded form approach

Contact forms are everywhere. You certainly have used one of them at least once. If you want a way for people to directly contact you, one of the possible solutions is implementing a contact form service on your website.

Formspree is one example of service which provides you all the benefits of a contact form without worrying about server-side code. Wufoo is too. In fact, here is a bunch you can consider for handling contact form submissions for you.

The first step to using any form service is to sign up and create an account. Pricing varies, of course, as do the features offered between services. But one thing most of them do is provide you with an HTML snippet to embed a form you create into any website or app. Here’s an example I pulled straight from a form I created in my Formspring account

<form action="https://formspree.io/f/[my-key]" method="POST">   <label> Your email:     <input type="email" name="email" />   </label>   <label> Your message:     <textarea name="message"></textarea>   </label>   <!-- honeypot spam filtering -->   <input type="text" name="_gotcha" style="display:none" />   <button type="submit">Send</button> </form>

In the first line, you should customize action based on your endpoint. This form quite basic, but you can add as many fields as you wish.

Notice the hidden input tag on line 9. This input tag helps you filter the submissions made by regular users and bots. In fact, if Formspree’s back-end sees a submission with that input filled, it will discard it. A regular user wouldn’t do that, so it must be a bot.


  • Your email address is safe since it is not public.
  • It works with Javascript disabled.


  • Relies on a third-party service (which may be a pro, depending on your needs)

There is one other disadvantage to this solution but I left it out of the list since it’s quite subjective and it depends on your use case. With this solution, you are not sharing your email address. You are giving people a way to contact you. What if people want to email you? What if people are looking for your email address, and they don’t want a contact form? A contact form may be a heavy-handed solution in that sort of situation.


We reached the end! In this tutorial, we talked about different solutions to the problem of online email sharing. We walked through different ideas, involving HTML code, JavaScript and even some online services like Formspree to build contact forms. At the end of this tutorial, you should be aware of all the pros and cons of the strategies shown. Now, it’s up to you to pick up the most suitable one for the your specific use case.

How to Safely Share Your Email Address on a Website originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , ,

Collecting Email Signups With the Notion API

A lot of people these days are setting up their own newsletters. You’ve got the current big names like Substack and MailChimp, companies like Twitter are getting into it with Revue, and even Facebook is getting into the newsletter business. Some folks are trying to bring it closer to home with a self-managed WordPress solution via MailPoet.

Let’s talk about another possibility: collecting your own email subscribers the good ol’ fashioned way: a <form> that submits to an API and puts them into data storage.

The first hurdle in setting up a newsletter is a mechanism to collect emails. Most apps that help you with building and sending newsletters will offer some kind of copy-and-paste signup form example, but most of these options are paid and we are looking for something that is free and completely hosted by us.

Nothing is handier than setting up our own system which we can control. In this tutorial, we will set up our own system to collect emails for our newsletters based on the Jamstack architecture. We will implement an HTML form to collect emails for our newsletter, process those emails with Netlify Functions, and save them to a Notion database with the Notion API.

But before we begin…

We need the following things:

All of these services are free (with limits). And as far as Mailgun goes, we could really use any email service provider that offers an API.

Get the entire code used in this tutorial over at GitHub.

First off, Notion

What is Notion, you ask? Let’s let them describe it:

Notion is a single space where you can think, write, and plan. Capture thoughts, manage projects, or even run an entire company — and do it exactly the way you want.

A screenshot of Notion's homepage. It says Notion is an all-in-one workspace and there is a form to enter an email address and create an account. Black and white illustrations of four people are below the form.

In other words, Notion is sort of like a database but with a nice visual interface. And each row in the database gets its own “page” where writing content is not unlike writing a blog post in the WordPress block editor.

And those databases can be visualized in a number of ways, from a simple spreadsheet to a Trello-like board, or a gallery, or a calendar, or a timeline, or a… you get the picture.

We are using Notion because we can set up tables that store our form responses, making it basically a data store. Notion also just so happened to recently release a full API for public use. We can take advantage of that API and interact with it using…

Netlify Functions

Netlify Functions are serverless API endpoints provided by, of course, Netlify’s hosting platform. We can write them in JavaScript or Go.

A screenshot of the Netlify Functions webpage. It has a large black heading in bold type that reads Build scalable, dynamic applications. There is a teal colored button with rounded corners below to get started with Netlify, then a text link beside it that reads talk to an expert.

We could use Netlify forms here to collect form submissions. In fact, Matthew Ström already shared how that works. But on a free plan, we can only receive 100 submissions per month. So, that’s why we’re using Netlify Functions — they can be invoked 125,000 times a month, meaning we can collect a quarter-million emails every month without paying a single penny.

Let’s set up the Notion database

The first thing we need to do is create a database in Notion. All that takes is creating a new page, then inserting a full-page table block by typing /table.

Screenshot of a Notion page with the block inserter open and the full page table option selected. The page has a dark background and a sidebar on the left with navigation for the user account.
Typing a command in Notion, like /table, opens up a contextual menu to insert a block on a page.

Let’s give our database a name: Newsletter Emails.

We want to keep things pretty simple, so all we’re collecting is an email address when someone submits the form. So, really, all we need is a column in the table called Email.

A screenshot of the Notion database, which is in a spreadsheet or table view. The white heading is titled Newsletter Emails, then there is one column for an email address.

But Notion is a little smarter than that and includes advanced properties that provide additional information. One of those is a “Created time” property which, true to its name, prints a timestamp for when a new submission is created.

The Notion database showing a second property being added to the database to display the created time of an email entry. A contextual menu is open with a Created time property type selected and an additional contextual menu beside it showing the different types of available properties.
Now, when a new “Email” entry is added to the table, the date it was added is displayed as well. That can be nice for other things, like calculating how long someone has been a subscriber.

We also need a Notion API Token

In order to interact with our Notion database, we need to create a Notion integration and get an API token. New integrations are made, not in your notion account, but on the Notion site when you’re logged into your account. We’ll give this integration a name that’s equally creative as the Notion table: Newsletter Signups.

A plain white webpage for Notion integrations. It has a heading that says Create a new integration with form fields for name, logo, associated workspace, and a submit button.
Create a new Notion integration and associate it with the workspace where the Newsletter Emails table lives.

Once the integration has been created, we can grab the Internal Integration Token (API Token). Hold onto it because we’ll use it in just a bit.

The Notion integrations conformation screen displaying the secret internal integration token, which is obscured by the app.
Once the integration has been created, Notion provides a secret API token that’s used to authenticate connections.

By default, Notion Integrations are actually unable to access Notion pages or databases. We need to explicitly share the specific page or database with our integration in order for the integration to properly read and use information in the table. Click on the Share link at the top-right corner of the Notion database, use the selector to find the integration by its name, then click Invite.

The Notion database with Share options displayed in a modal with a list of available integrations and a light blue invite button.

Next is Netlify

Our database is all set to start receiving form submissions from Notion’s API. Now is the time to create a form and a serverless function for handling the form submissions.

Since we are setting up our website on Netlify, let’s install the Netlify CLI on our local machine. Open up Terminal and use this command:

npm install netlify-cli -g

Based on your taste, we can authenticate to Netlify in different ways. You can follow Netlify’s amazing guide as a starting point. Once we have authenticated with Netlify, the next thing we have to do is to create a Netlify project. Here are the commands to do that:

$  mkdir newsletter-subscription-notion-netlify-function $  cd newsletter-subscription-notion-netlify-function $  npm init

Follow along with this commit in the GitHub project.

Let’s write a Netlify Function

Before we get into writing our Netlify function, we need to do two things.

First, we need to install the [@notionhq/client](https://github.com/makenotion/notion-sdk-js) npm package, which is an official Notion JavaScript SDK for using the Notion API.

$  npm install @notionhq/client --save

Second, we need to create a .env file at the root of the project and add the following two environment variables:

NOTION_API_TOKEN=<your Notion API token> NOTION_DATABASE_ID=<your Notion database>

The database ID can be found in its URL. A typical Notion page has a URL like https://www.notion.so/{workspace_name}/{database_id}?v={view_id} where {database_id} corresponds to the ID.

We are writing our Netlify functions in the functions directory. So, we need to create a netlify.toml file at the root of the project to tell Netlify that our functions need to be built from this directory.

[build] functions = "functions"

Now, let’s create a functions directory at the root of the project. In the functions directory, we need to create an index.js file and add the following code. This function is available as an API endpoint at /.netlify/functions/index.

const { Client, LogLevel } = require('@notionhq/client');  const { NOTION_API_TOKEN, NOTION_DATABASE_ID } = process.env;  async function addEmail(email) {   // Initialize Notion client   const notion = new Client({     auth: NOTION_API_TOKEN,     logLevel: LogLevel.DEBUG,   });    await notion.pages.create({     parent: {       database_id: NOTION_DATABASE_ID,     },     properties: {       Email: {         title: [           {             text: {               content: email,             },           },         ],       },     },   }); }  function validateEmail(email) {   const re =     /^(([^&lt;&gt;()[]\.,;:s@&quot;]+(.[^&lt;&gt;()[]\.,;:s@&quot;]+)*)|(&quot;.+&quot;))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$ /;   return re.test(String(email).toLowerCase()); }  module.exports.handler = async function (event, context) {   // Check the request method   if (event.httpMethod != 'POST') {     return { statusCode: 405, body: 'Method not allowed' };   }    // Get the body   try {     var body = JSON.parse(event.body);   } catch (err) {     return {       statusCode: 400,       body: err.toString(),     };     return;   }    // Validate the email   const { email } = body;   if (!validateEmail(email)) {     return { statusCode: 400, body: 'Email is not valid' };   }    // Store email in Notion   await addEmail(email);   return { statusCode: 200, body: 'ok' }; }; 

Let’s break this down, block by block.

The addEmail function is used to add the email to our Notion database. It takes an email parameter which is a string.

We create a new client for authenticating with the Notion API by providing our NOTION_API_TOKEN. We have also set the log level parameter to get information related to the execution of commands while we are developing our project, which we can remove once we are ready to deploy the project to production.

In a Notion database, each entry is known as a page. So, we use the notion.pages.create method to create a new entry in our Notion database. The module.exports.handler function is used by Netlify functions that handle the requests made to it.

The first thing we check is the request method. If the request method is something other than POST, we send back a 405 - Method not allowed response.

Then we parse the payload (event.body) object sent by the front end. If we are unable to parse the payload object, we send back a 400 - Bad request response.

Once we have parsed the request payload, we check for the presence of an email address. We use the validateEmail function to check the validity of the email. If the email is invalid, we send back a 400 - Bad request response with a custom message saying the Email is not valid.

After we have completed all the checks, we call the addEmail function to store the email in our Notion database and send back a 200 - Success response that everything went successfully.

Follow along with the code in the GitHub project with commit ed607db.

Now for a basic HTML form

Our serverless back-end is ready and the last step is to create a form to collect email submissions for our newsletter.

Let’s create an index.html file at the root of the project and add the following HTML code to it. Note that the markup and classes are pulled from Bootstrap 5.

<!DOCTYPE html> <html lang="en"> <head>   <meta charset="UTF-8">   <meta http-equiv="X-UA-Compatible" content="IE=edge">   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <title>Notion + Netlify Functions Newsletter</title>   <link href="<https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css>" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous"> </head>  <body>   <div class="container py-5">     <p>       Get daily tips related to Jamstack in your inbox.     </p>      <div id="form" class="row">       <div class="col-sm-8">         <input name="email" type="email" class="form-control" placeholder="Your email" autocomplete="off">         <div id="feedback-box" class="invalid-feedback">             Please enter a valid email         </div>       </div>       <div class="col-sm-4">         <button class="btn btn-primary w-100" type="button" onclick="registerUser()">Submit form</button>       </div>     </div>      <div id="success-box" class="alert alert-success d-none">       Thanks for subscribing to our newsletter.     </div>     <div id="error-box" class="alert alert-danger d-none">       There was some problem adding your email.     </div>   </div> </body> <script> </script> </html>

We have an input field for collecting user emails. We have a button that, when clicked, calls the registerUser function. We have two alerts that are shown, one for a successful submission and one for an unsuccessful submission.

Since we have installed Netlify CLI, we can use the netlify dev command to run our website on the localhost server:

$  netlify dev
Showing the email form with a heading that says Get daily tips related to Jamstack in your inbox above a form field for an email and a blue button next to it to submit the form. There is a read error message below the email input that reads Please enter a valid email.
Once the server is up and running, we can visit localhost:8888 and see our webpage with the form.

Next, we need to write some JavaScript that validates the email and sends an HTTP request to the Netlify function. Let’s add the following code in the <script> tag in the index.html file:

function validateEmail(email) {   const re =     /^(([^<>()[]\.,;:s@"]+(.[^<>()[]\.,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$ /;   return re.test(String(email).toLowerCase()); }  async function registerUser() {   const form = document.getElementById('form');   const successBox = document.getElementById('success-box');   const errorBox = document.getElementById('error-box');   const feedbackBox = document.getElementById('feedback-box');   const email = document.getElementsByName('email')[0].value;    if (!validateEmail(email)) {     feedbackBox.classList.add('d-block');     return;   }    const headers = new Headers(); headers.append('Content-Type', 'application/json');      const options = {     method: 'POST',     headers,     body: JSON.stringify({email}),   };    const response = await fetch('/.netlify/functions/index', options);    if (response.ok) {     form.classList.add('d-none');     successBox.classList.remove('d-none');   }   else {     form.classList.add('d-none');     errorBox.classList.remove('d-none');   } }

In the registerUser function, we check the validity of the email input using a RegEx function (validateEmail). If the email is invalid, we prompt the user to try again. Once we have validated the email, we call the fetch method to send a POST request to our Netlify function which is available at /.netlify/functions/index with a payload object containing the user’s email.

Based on the response, we show a success alert to the user otherwise we show an error alert.

Follow along with the code in the GitHub project with commit cd9791b.

Getting the Mailgun credentials

Getting emails in our database is good, but not enough, because we don’t know how we are going to use them. So, the final step is to send a confirmation email to a subscriber when they sign up.

We are not going to send a message immediately after a user signs up. Instead, we will set up a method to fetch the email signups in the last 30 minutes, then send the email. Adding a delay to the auto-generated email makes the welcome message feel a bit more personal, I think.

We’re using Mailgun to send, but again, we could really use any email service provider with an API. Let’s visit the Mailgun dashboard and, from the sidebar, go to the Settings → API Keys screen.

Showing the Mailgun API security screen. The page has a white background with a dark blue left sidebar that contains navigation. The page displays the private API key, the public validation key, and the HTTP web hook signing key.
We want the “Private API key.”

Let’s copy the Private API key and add it to the .env file as:

MAILGUN_API_KEY=<your Mailgun api key>

Every email needs to come from a sending address, so one more thing we need is the Mailgun domain from which the emails are sent. To get our Mailgun domain, we need to go to the Sending → Domains screen.

The Mailgun domains screen showing a table with one row that contains a sandbox URL with a series of statistics.
Mailgun offers a sandbox domain for testing.

Let’s copy our Mailgun sandbox domain and add it to the .env file as:

MAILGUN_DOMAIN=<your Mailgun domain>

Sending a confrimation email

Before we get into writing our Netlify function for sending an email confirmation, we need to install the [mailgun-js](https://github.com/mailgun/mailgun-js) npm package, which is an official Mailgun JavaScript SDK for using the Mailgun API:

$  npm install mailgun-js --save

We are using the /.netlify/functions/index endpoint for sending welcome emails. So, in the functions directory, let’s create a new a welcome.js file and add the following code:

const { Client, LogLevel } = require('@notionhq/client'); const mailgun = require('mailgun-js');  const {   NOTION_API_TOKEN,   NOTION_DATABASE_ID,   MAILGUN_API_KEY,   MAILGUN_DOMAIN, } = process.env;  async function fetchNewSignups() {   // Initialize notion client   const notion = new Client({     auth: NOTION_API_TOKEN,     logLevel: LogLevel.DEBUG,   });    // Create a datetime that is 30 mins earlier than the current time   let fetchAfterDate = new Date();   fetchAfterDate.setMinutes(fetchAfterDate.getMinutes() - 30);    // Query the database   // and fetch only entries created in the last 30 mins   const response = await notion.databases.query({     database_id: NOTION_DATABASE_ID,     filter: {       or: [         {           property: 'Added On',           date: {             after: fetchAfterDate,           },         },       ],     },   });    const emails = response.results.map((entry) => entry.properties.Email.title[0].plain_text);    return emails; }  async function sendWelcomeEmail(to) {   const mg = mailgun({ apiKey: MAILGUN_API_KEY, domain: MAILGUN_DOMAIN });    const data = {     from: `Ravgeet Dhillon <postmaster@$ {MAILGUN_DOMAIN}>`,     to: to,     subject: 'Thank you for subscribing',     text: "Thank you for subscribing to my newsletter. I'll be sending daily tips related to JavScript.",   };    await mg.messages().send(data); }  module.exports.handler = async function (event, context) {   // Check the request method   if (event.httpMethod != 'POST') {     return { statusCode: 405, body: 'Method not allowed' };   }    const emails = await fetchNewSignups();    emails.forEach(async (email) => {     await sendWelcomeEmail(email);   });    return { statusCode: 200, body: 'ok' }; };

The above code has two main functions which we need to be understood. First, we have a fetchNewSignups function. We use the Notion SDK client to fetch the entries in our Notion database. We have used a filter to fetch only those emails which were added in the last 30 minutes. Once we get a response from the Notion API, we loop over the response and map the email into an emails array.

Second, we have a sendWelcomeEmail function. This function is used to send an email to the new subscriber via Mailgun API.

To bring everything together, we loop over the new emails in the module.exports.handler function and send a welcome email to each new email.

This function is available at localhost:8888/.netlify/functions/welcome.

Follow along with the code in the GitHub project with commit b802f93.

Let’s test this out!

Now that our project is ready, the first thing we need to do is to test the form and see whether the email is stored in our Notion database. Let’s go to localhost:8888 and enter a valid email address.

Perfect! We got the success message. Now let’s head over to our Notion database to see if the email address was captured.

Awesome, it’s there! We can also see that the Added On field has also been auto-filled by Notion, which is exactly what we want.

Next, let’s send a POST request to localhost:8888/.netlify/functions/welcome from Postman and check our inbox for the subscription confirmation message.

Woah! We have received the welcome email for subscribing to the newsletter.

Not seeing the email in your inbox? Make sure to check your spam folder.

What’s next?

The next step from here would be to set up a script that runs every 30 minutes and calls the welcome endpoint. As of now, Netlify doesn’t provide any way to run CRON jobs. So, we can set up GitHub Actions to handle it.

Also, we can secure the welcome endpoint using security mechanisms like a simple password or a JWT token. This will prevent anyone on the Internet from calling the welcome endpoint and sending tons of spam to our subscribers.

So, there we have it! We successfully implemented a Jamstack way to collect emails for a newsletter. The biggest advantage of doing something like this is that we can learn a lot about different technologies at once. We wired together services and APIs that collect email addresses, validate submissions, post to databases, and even send emails. It’s pretty cool that we can do so much with relatively so little.

I hope you enjoyed reading and learning something from this article as much as I enjoyed writing it for you. Let me know in the comments how you used this method for your own benefit. Happy coding!

The post Collecting Email Signups With the Notion API appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, , ,

Can I Email…

While I’m 85% certain you’ve seen and used Can I Use…, I bet there is only a 13% chance that you’ve seen and used Can I Email…. It’s the same vibe—detailed support information for web platform features—except instead of support information for web browsers, it is email clients. Campaign Monitor has maintained a guide for a long time as well, but I admit I like the design style pioneered by Can I Use….

HTML email is often joked about in how you have to code for it in such an antiquated way (<table> tags galore!) but that’s perhaps not a fair shake. Kevin Mandeville talked about how he used CSS grid (not kidding) in an email back in 2017:

Our Apple Mail audience at Litmus is approximately 30%, so a good portion of our subscriber base is able to see the grid desktop layout.

Where CSS Grid isn’t supported (and for device/window widths of less than 850 pixels), we fell back to a one-column layout.

Just like websites, right? They don’t have to look the same everywhere, as long as the experience is acceptable everywhere.

Me, I don’t do nearly as as much HTML email work as I do for-web-browsers work, but I do some! For example, the newsletter for CSS-Tricks is an RSS feed that feeds a MailChimp template. The email we send out to announce new shows for ShopTalk is similar. Both of those are pretty simple MailChimp templates that are customized with a bit of CSS.

But the most direct CSSin’ I do with HTML email is the templates for CodePen emails. We have all sorts of them, from totally custom templates, to special templates for The Spark and Challenges and, of course, transactional emails.

Those are all entirely from-scratch email templates. I’s very nice to know what kind of CSS features I can count on using. For example, I was surprised by how well flexbox is supported in email.

It’s always worth thinking about fallbacks. There is nothing stopping you from creating an email that is completely laid out with CSS grid. Some email clients will support it, and some won’t. When it is supported, a fancy layout happens. When it is not supported, assuming you leave the source order intelligible, you just get a column of blocks (which is what most emails are anyway) and should be perfectly workable.

Progressive enhancement is almost more straightforward in email where there is rarely any functionality to be concerned with.

Direct Link to ArticlePermalink

The post Can I Email… appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



Jetpack 8.9: Take Donations, Capture Email Subscribers, AMP integration, and More

(This is a sponsored post.)

Jetpack 8.9 shipped on September 1 and it shows why the plugin continues to be the premier way to take a WordPress site from good to holy smokes! Several new features are packed into the release, but a few really stand out.

Take donations with a new block

The first is donations, and a quick demo of how easy it is to drop a donation form into a page is going to excite anyone who has ever had to cobble together multiple third party scripts and tools to get something like this on a site.

That’s right — it’s as easy as any other block and it connects directly to your Stripe account when you upgrade to a Jetpack paid plan. Non-profits are sure to love this, but even if you’re a plugin developer looking for a way to collect “tips” in exchange for your work, you’ll get a lot of mileage out of something like this.

I’d drop a donations block right here to show you, but if you’re so inclined (😍) we have a MVP supporter thing set up that handles that, which is powered by WooCommerce Memberships.

Collect newsletter signups and automate email marketing

Another feature that stands out is a newsletter signup form. Instead of relying on another plugin for form functionality and another to connect the form to an email newsletter service, Jetpack handles it all with a new block that not only collects subscribers, but integrates directly with Creative Mail by Constant Contact.

That means you not only take signups directly from your site, but you get a way to pull WordPress content and WooCommerce products into emails that support all kinds of automatons, like scheduled sends, action-based triggers, and multi-step marketing journeys. It’s a lot of power in a single package!

It’s worth noting that the newsletter form is in addition to a growing number of forms that are built right into Jetpack, including RSVP, contact, registration, feedback, and appointments.

AMP-ify your content

There isn’t a whole lot of details on this feature, but it certainly warrants attention. Automattic and Google have been working closely together the past several months to ship the 2.0 version of the official AMP plugin for WordPress.

The plan is for the Jetpack team to write up a detailed post sometime soon that thoroughly outlines the integration. Just a guess? Perhaps Jetpack blocks will natively support valid AMP markup in way that maintains the functionality while meeting AMP’s performance standards. We’ll see!

Jetpack 8.9 is the latest release in what’s proving to be a rapidly evolving one-stop shop for the most common and useful WordPress features that normally would require plugin overload. The past year alone has seen a slideshow block, on-site instant search, built-in customer relationship management and security monitoring — and those are just the highlights! You really can’t go wrong with Jetpack if you’re looking for the most powerful set of features in one place. Plug it in, purchase a plan, and you get access to literally dozens of features and enhancements for WordPress without having to hunt them down, one-by-one. Hey, that’s why we use Jetpack around here at CSS-Tricks… and love it!

Direct Link to ArticlePermalink

The post Jetpack 8.9: Take Donations, Capture Email Subscribers, AMP integration, and More appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , , , , ,

The Ultimate Guide to Dark Mode for Email Marketers

On the regular web (I suppose) we handle “dark mode” with the CSS prefers-color-scheme media query. But, and to nobody’s surprise, it’s way weirder in the land of HTML email. The weirdness is that across different email clients, they handle the dark mode thing differently, starting with the fact that the email client itself might have its own toggle for dark mode.

Say that toggle has dark mode activated. There are three things that might happen:

  1. The UI of the app goes dark mode, but it leaves the email alone (e.g. Apple Mail).
  2. It tries to apply dark mode to your emails as well, but only when it detects areas that are light. Those areas become dark while leaving dark areas alone (e.g. Outlook.com).
  3. It goes full-bore and force-inverts email colors (e.g. Gmail app on iOS 13).

That last one is wacky-town. As Alice Li says:

This is the most invasive color scheme: it not only inverts the areas with light backgrounds but impacts dark backgrounds as well. So if you already designed your emails to have a dark theme, this scheme will ironically force them to become light.
Emphasis hers.

Direct Link to ArticlePermalink

The post The Ultimate Guide to Dark Mode for Email Marketers appeared first on CSS-Tricks.


, , , , ,

CSS Animations and Transitions in Email

We don’t generally think of CSS animations or transitions inside of email, or really any movement at all outside of an awkward occasional GIF. But there is really no reason you can’t use them inside HTML emails, particularly if you do it in a progressive enhancement-friendly way. Like, you could style a link with a hover state and a shaking animation, but if the animation (or even the hover) doesn’t work, it’s still a functional link. Heck, you can use CSS grid in email, believe it or not.

Jason Rodriguez just wrote Understanding CSS Animations in Email: Transitions and Keyframe Animations that covers some of the possibilities. On the supported list of email clients that support CSS transitions and keyframe animations is Apple Mail, Outlook, and AOL mail, among others.

Other things to look at:

The post CSS Animations and Transitions in Email appeared first on CSS-Tricks.


, ,