Do you like surprises? Your customer might. We all know about the value of personalisation in email, but is there room too for a touch of randomisation?
Randomisation as a feature
First things first. We don’t mean dropping random content into emails like some kind of scattershot A/B test, or A/B/C/D/E/F/G test for that matter. We’re talking about the element of chance as an interactive feature. Think along the lines of spin-the-wheel, where the user is aware that they can trigger a random result. And, crucially, that they want to.
How it works
Randomisation is a form of interactive email. The state of an input element – i.e. a checkbox or radio button – can be used to toggle styling on any number of other elements on the page. That lets you show, hide, move or otherwise change content in your email based on the user’s actions. This technique is known as the checkbox hack.

While JavaScript has built-in functionality to generate a random number, HTML and CSS do not. And email of course is a JavaScript-free environment. But there is still one variable and unpredictable factor – user timing. We don’t know when the reader will hit the button. We can capitalise on that.
First, we place a series of identical-looking triggers. Next, we cycle through them rapidly. We’re talking milliseconds. The result of a click is equally random to developer and customer alike.
Let’s build it
We need some inputs. Radio buttons to be precise. Let’s say we have ten possible random results. Therefore we need ten triggers:
<input type="radio" name="radio-group" id="radio-1">
<input type="radio" name="radio-group" id="radio-2">
<input type="radio" name="radio-group" id="radio-3">
<input type="radio" name="radio-group" id="radio-4">
<input type="radio" name="radio-group" id="radio-5">
<input type="radio" name="radio-group" id="radio-6">
<input type="radio" name="radio-group" id="radio-7">
<input type="radio" name="radio-group" id="radio-8">
<input type="radio" name="radio-group" id="radio-9">
<input type="radio" name="radio-group" id="radio-10">
The name attribute is the same for all of our radio buttons, thus grouping them. Only one can be active at a time. This is extremely useful functionality for interactive email in general, and will be helpful here for testing.
The id attribute on the other hand is unique to each radio button. We’ll use these to identify which radio button has been pressed.
Secret message
Our triggers need something to, well, trigger. We’ll create ten div-wrapped messages to go along with our ten triggers. They’ll all be hidden at the start.
Even though this is a no-frills, bare-bones example, we’ll stick them in a container. That better resembles the structure of a full-on interactive email.
<div class="messages">
<div class="message message-1">You pressed trigger 1</div>
<div class="message message-2">You pressed trigger 2</div>
<div class="message message-3">You pressed trigger 3</div>
<div class="message message-4">You pressed trigger 4</div>
<div class="message message-5">You pressed trigger 5</div>
<div class="message message-6">You pressed trigger 6</div>
<div class="message message-7">You pressed trigger 7</div>
<div class="message message-8">You pressed trigger 8</div>
<div class="message message-9">You pressed trigger 9</div>
<div class="message message-10">You pressed trigger 10</div>
</div>
You’ll notice that they each have two class names. We’ll use one of those to to neatly hide all messages by default.
.message {
display: none;
}
The other class name will act as a unique identifier. We could use an id tag, like we did for the radio buttons, but that can lead to CSS specificity problems for elements that we want to restyle in an interactive email.
Hook them up
Our triggers are in place. Our messages are in place. But they’re not connected yet. We’ll use the following CSS for that:
- The general sibling selector. That’s the tilde. It’ll let us refer our inputs to the messages div which sits later in the document on the same level.
- The descendant combinator. That’s a space between two target elements. It’ll let us connect to specific messages within their container.
#radio-1:checked ~ .messages .message-1 {
display: block;
}
#radio-2:checked ~ .messages .message-2 {
display: block;
}
#radio-3:checked ~ .messages .message-3 {
display: block;
}
#radio-4:checked ~ .messages .message-4 {
display: block;
}
#radio-5:checked ~ .messages .message-5 {
display: block;
}
#radio-6:checked ~ .messages .message-6 {
display: block;
}
#radio-7:checked ~ .messages .message-7 {
display: block;
}
#radio-8:checked ~ .messages .message-8 {
display: block;
}
#radio-9:checked ~ .messages .message-9 {
display: block;
}
#radio-10:checked ~ .messages .message-10 {
display: block;
}
Let’s try it. By clicking any radio button, the corresponding message will be displayed.

Label it
We’re going to hide all of our radio buttons – permanently. That’s because there are very limited styling options for input elements and we don’t want our triggers to actually look like radio buttons. It’s better to think of them as the circuitry inside your interactive email, not the buttons that people push.
input[type=radio] {
display: none;
}
And we’ll now create our labels. Using their for attribute we can tie them to their corresponding radio button:
<label for="radio-1">Surprise me</label>
<label for="radio-2">Surprise me</label>
<label for="radio-3">Surprise me</label>
<label for="radio-4">Surprise me</label>
<label for="radio-5">Surprise me</label>
<label for="radio-6">Surprise me</label>
<label for="radio-7">Surprise me</label>
<label for="radio-8">Surprise me</label>
<label for="radio-9">Surprise me</label>
<label for="radio-10">Surprise me</label>
They need some styling. How about a garish lime green and black outline?
label {
display: block;
width: 400px;
text-align: center;
padding: 20px 0;
box-sizing: border-box;
border: solid 2px black;
box-sizing: border-box;
background-color: lime;
cursor: pointer;
}
That cursor property is handy for desktop users. Upon hover, the pointer icon will appear, indicating that the ‘button’ is pushable.
Start cycling
Our buttons are functional, but they’re just sitting there. In a big tower of buttons.

We need to house them in two divs:
- A viewing window with a hidden overflow. It’ll be sized to match the dimensions of a single button, while the other nine are hidden at any time.
- A slider that will pull each button into view in turn. And to reiterate: this is a process that will take mere milliseconds.
We can put the HTML code into place first:
<div class="inputs-window">
<div class="inputs-slider">
<label for="radio-1">Surprise me<label>
<label for="radio-2">Surprise me<label>
<label for="radio-3">Surprise me<label>
<label for="radio-4">Surprise me<label>
<label for="radio-5">Surprise me<label>
<label for="radio-6">Surprise me<label>
<label for="radio-7">Surprise me<label>
<label for="radio-8">Surprise me<label>
<label for="radio-9">Surprise me<label>
<label for="radio-10">Surprise me<label>
<div>
<div>
And now for the CSS. Let’s start with the window. It’s pretty simple:
.inputs-window {
width: 400px;
overflow: hidden;
}
The width matches that of a button. We don’t need to specify a height, as it’ll automatically match that of its contents.
Next, we need the slider. There’s a lot more to explain here.
.inputs-slider {
width: 1000%;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
animation-name: cycle-triggers;
animation-duration: 0.01s;
animation-iteration-count: infinite;
animation-timing-function: steps(10);
}
@keyframes cycle-triggers {
from { margin-left: 0; }
to { margin-left: -1000%; }
}
The width is 1000% – i.e. ten times the width of the viewing window. That sizes it for our ten buttons.
We’re using a grid layout. Normally unheard of in email due to a general lack of compatibility, but we can use it here. Any email applications that can run the checkbox hack have decent CSS support in general. We’ve set up 10 columns, each sized to one fractional unit and thus taking up a tenth of the slider.
We use a keyframe animation to pull each button into place. An entire animation loop takes just 10 milliseconds, and runs infinitely. We don’t need to code all ten button positions into the animation. Instead we can make use of the steps functionality and set the ‘to’ value to the full 1000% slider width. Nice and succinct.
Hit pause
Uh oh – there’s a problem. It doesn’t work. That’s because a click is comprised of two parts: pressing the mouse button… and releasing it. These both need to take place on the same trigger. But we know that the triggers are cycling at a blistering pace. A human finger is glacially slow by comparison and a click could not possibly be completed on the same trigger.
We therefore need a means of pausing the trigger cycle. Thankfully, there’s a straightforward solution. We can pause the keyframe animation upon hover:
.inputs-slider:hover {
animation-play-state: paused;
}
This hover state doubles up as ‘on touch’ for touchscreens. Now our triggers are held in place when the user interacts with them, allowing a full click to be completed.
Don’t leave functionality to chance
All emails need to be tested, but particularly those with interactive content. Things can and do go wrong!
In order to test all of our random triggers, we’ll temporarily slooooow our animation speed right down:
animation-duration: 5s;
And we can place an identifying number inside each of our otherwise-identical labels:
<label for="radio-1">Surprise me 1</label>
<label for="radio-2">Surprise me 2</label>
<label for="radio-3">Surprise me 3</label>
<label for="radio-4">Surprise me 4</label>
<label for="radio-5">Surprise me 5</label>
<label for="radio-6">Surprise me 6</label>
<label for="radio-7">Surprise me 7</label>
<label for="radio-8">Surprise me 8</label>
<label for="radio-9">Surprise me 9</label>
<label for="radio-10">Surprise me 10</label>
That gives you a solid half second to press each trigger in turn as they rotate. Now that you know everything works, you can revert to its previous state, safe in the knowledge that everything works as it should.
Jazz it up
Our example is purely functional. In a real interactive email, you can harness all of the power of CSS animation and create something special.
What’s the point?
Do you remember that amazing marketing email you got from [brand]? Me neither. Most marketing emails, frankly, are nothing special. But yours could be.
Randomisation encourages exploration of your products and services. It’s fun to interact with. Your subscribers might wonder what cool content you’ll be sending next time. And when your email engagement rates go up, there will be nothing random about that.