TL;DR: I coded my first interactive email using radio buttons to toggle on/off states without using JavaScript.
I was first introduced to interactive email in 2015 when I attended Mark Robbins’ presentation at Litmus Live in Boston.
JavaScript isn’t supported in any email client, so up to that point the only interactive things I’d done in email were relatively basic (like creating a :hover
effect on a button using CSS).
But in his presentation, Mark introduced me to the concept of punched card coding: using checkbox and radio button tags in email to mimic on/off functionally that JavaScript typically enables on the web.
I was blown away by Mark’s talk. I told myself to look out for an opportunity to use what I learned at work, but promptly forgot about it for four years as “regular” work took the front seat 😬.
The Pilot
Fast forward to 2019, I now work on Stack Overflow’s private Q&A products. Teams can ask questions, post answers, and vote on the best ones in a private setting.
However some teams have mentioned they’d like to improve their culture of voting. Eg. The team posts useful answers, but folks don’t upvote or accept the best ones to signal a high quality answer to rest of the team.
Folks don't spend all day with Stack Overflow open in a browser tab, so how can we meet folks where they already are?
“What if you could vote on questions right from your email, without even visiting the site? I bet that’s something an interactive email would be able to do!”
Four years after learning about punched card coding, I finally found a good pilot project, tied it to a business need, and set aside a day to code my first interactive email.
What We Built
The “Happy Path UX" allows someone to view an answer to their question and optionally vote on or accept it as the best answer, all without leaving their inbox. This is what I built:
The Code
The HTML uses the radio input along with the :checked
attribute to show and hide content using CSS.
HTML
<input type="radio" name="vote" id="ArrowUpLg" style="display: none !important; max-height: 0; visibility: hidden;">
<label for="ArrowUpLg" class="ArrowUpLg" style="cursor: pointer;">
<img src="ArrowUpLg.png" height="36" width="36">
</label>
<input type="radio" name="vote" id="ArrowUpLgactive" style="display: none!important; max-height: 0; visibility: hidden;">
<label for="ArrowUpLgactive" class="ArrowUpLgactive" style="cursor: pointer; display: none;">
<img src="ArrowUpLgactive.png" height="36" width="36">
</label>
The actual radio button is visually hidden and linked to from their <label>
tag using the for
and id
attributes. The image file is wrapped inside the <label>
to make the click/tap area nice and big.
For each icon, I created two almost identical versions of the code: one for the “initial” state and another for the “active” state. The “active” state is hidden by default with inline CSS (Inline CSS is still the most universal way to style HTML in email).
So the base HTML and CSS inserts and positions each icon state in the email, and hides the “active” image for each icon. With this foundation, the CSS in the <head>
adds the functionality.
CSS
#ArrowUpLg:checked + .ArrowUpLg {
display: none !important;
}
#ArrowUpLg:checked ~ .ArrowUpLgactive {
display: block !important;
}
#ArrowDownLg:checked + .ArrowDownLg {
display: none !important;
}
When an icon is clicked, the <label>
tag, it checks the hidden radio button.
In the CSS, the :checked
selector is used to toggle the display
property of the adjacent <img>
between block
and none
. The first time an icon is clicked or tapped, I hide the initial state (display: none !important
) and show the “active” state (display: block !important
). The !important
is necessary to override inline CSS styles.
The Happy Path UX
The end result is an email with working upvote and downvote arrows, and a checkmark to accept the answer.
We should only be able to upvote or downvote an answer, but not both. So we used radio buttons and included the upvote and downvote arrows in a single group using the name
attribute. This means that if the upvote arrow is active, and then the downvote arrow is selected, the upvote arrow is unselected in the process.
The “accepted” checkmark is independent of the voting arrows, so I gave that its own name
.
Email Client Fallbacks
I’ve been using the term “Happy Path UX” to describe the ideal state. Unfortunately not all email clients support this level of interactivity, so I have to think about fallbacks.
For instance, desktop Outlook on Windows does not support HTML form tags in email. Left alone, Outlook would display each icon… but nothing would happen when they’re clicked. Both confusing and frustrating for a user.
In this cases like this, we can target email clients that don’t support our desired UX and code in a fallback (Like say, static image files that click to the website).
In this example, I targeted Outlook using <!--[if mso]><![endif]—>
tags to hide the interactive form content from Outlook but display it in other email clients.
How to Target Email contains a long list of ways to target different email clients. I'll use this to display the interactivity to clients that support it and hide it from the rest.
Punched Card Coding
This was my first foray into punched card coding. It’s a pretty steep learning curve, but I had a giant “A-Ha!” moment the first time I got a proof of concept working.
Punched card coding can involve radio buttons, checkboxes, or button tags to achieve a number of things. Javascript still isn’t supported in any email client, but we can still toggle state, build tabs, reveal a hidden menu, build a carousel, and more.
Known Issues and Questions
My example above poses several questions outside of the HTML and CSS.
- Can we maintain state? Eg. What happens if I vote in an email, close the email, and re-open it? Is the arrow still active? If it’s not and I vote again, what happens?
- What about security? Eg. If I forward my email, can someone else vote on my behalf?
- What about testing? Eg. Emulators like Litmus only produce static screenshots. I’ll need a working version of every email client we want to test.
If you’re a Stack Overflow user, I’m not sure when we’ll figure this out, but I’m working on it! This is simply and exploration of what’s possible using interactive email.
If you’ve written markup using punched card coding or "the checkbox hack,” I’ve love input on how I could improve and optimize this.
If you’re a Stack Overflow user, would this be useful?
💌 ✌️
Top comments (15)
Neat!
What is the method for form submission if you can’t use JavaScript (or...?)
My first guess would be that the CSS state swaps out various anchor links where the URL query string changes based on upvote or downvote or accept. You could add a submit “button” link below and it would only work if logged in. So it would save a click and the security issue would be resolved.
Yep, no "submit" button. Honestly I haven't figured out that part yet either, hoping to work with one of the devs on my team to figure it out.
You could try using a
background-image
with a unique url and only display the element when the user clicks on another element. There would be lots of state to manage, but the css can be generated.This way you can send arbitrary http requests with meaningful payloads without JS.
background images don't work in most email clients.
Although your suggestion made me think they could use multiple submit buttons and based on selection a different button would be visible containing the values in the url it submits to. [makes for ugly inaccessible code though, but all email code is fugly]
Ah, such is the wild west of HTML emails! I had no idea background-image wasn't supported, that's pretty crazy.
background-image
has pretty good support in email, but nothing's ever a given in the world of email development.I did not immediately put together that you can "toggle" images to generate HTTP requests, that's genius. I love this.
This is awesome! Thanks for sharing the code 😄
This is pretty interesting; however, why not talk about using AMP in emails? It's starting to slowly roll out and is definitely a way to make emails interactive!
I went this way because HTML and forms currently have better support and it'd be less to maintain.
I believe AMP is currently Gmail-only. Also AMP is a separate MIME type, which means 1. It'd be a third version of each email to support (in addition to HTML and text-only), and 2. not all ESPs support sending the AMP MIME type yet.
Still AMP is something I'd like to explore next. It seems a lot leaner than traditional email code.
Just some help for those who don't live email 24/7 like I do (we're an ESP...tailoredmail.com).
1). Background-images are supported in all the same email clients that also support this punchcard coding, so there's nothing to worry about there.
2). AMP4Email has "some" interactivity (hopefully more over time), and is also being implemented by Outlook.com and Yahoo/AOL...so there's that...but also keep in mind that you can't send an AMP email until Google fully approves you as a legit sender (there's an application process that's not altogether clear or easy). Also, for now, AMP email clients (gmail and outlook.com currently) require the user to go into their settings and turn on "dynamic email"...
I think your efforts to do the punchcard approach are worth your time/investment, as it's something you can be assured of working reasonably well. We've use the concept to build little wizards in our app that spit out the punchcard-code for carousels and the thumbs-up/vote concept described in this thread, amongst other interactive behaviors. Clients such as HULU and Aston Martin confidently use the concept to send to millions of people...so code with confidence! Hope this helps.
thanks for the response!
Great read Ted! Hope more folks adopt this technique to make better email experiences.
good stuff
How do you include the style tag in your email template? I tried to add it and it got stripped by Gmail.
Thanx