2 days ago

An easy-to-follow guide on making an interactive Fortune Cookie game (code samples included)

Anton Diduh Content writer and video content creator at Stripo
Rolenko Liubov HTML developer at Stripo

Summarize

ChatGPT Perplexity

Fortune cookies are a classic among many restaurants and food delivery services. A delicious, crispy dessert with an intriguing filling that can amuse with its prediction or even inspire thought. But what if you offered this "dessert" at the end of an email? We're marketers, not chefs, but we know and will show you how to "make" these cookies for your email newsletters, and we're pretty sure your subscribers will love them. We are pleased to present to you our interactive Fortune Cookie game.

How this game works

The entire game is quite simple in its mechanics. There are three fortune cookies. Clicking on a cookie opens it, after which a text message with the prediction appears on the screen. After opening one cookie, you can restart the game and open the others. That's basically the entire game mechanic

Despite its overall simplicity, you can adapt both the predictions and the visuals to your own needs, and the mechanics themselves are quite useful for:

  • teasing new products, events, or promotions for your brand;
  • congratulate your subscribers on holidays by making themed predictions or greetings that they open;
  • distributing promo codes or other small gifts to your audience in an interactive and fun way;
  • and so on.

In this article, we will show you in detail how to create a full-fledged game from scratch that is compatible with major email clients, so all your subscribers will be able to interact with the game.

Our guide will include the following:

  • AMP version of the game;
  • kinetic version, built with HTML5 and CSS3;
  • fallback version for email clients that do not support interactivity.

The kinetic (HTML5 and CSS3) version is the main version and will be shown when the email client supports both technologies. The AMP version will be shown if email clients support AMP and will replace your kinetic version in these cases. Meanwhile, the fallback version will be shown in cases when email clients don’t support both HTML5/СSS3 and AMP. 

So, without further ado, let’s start creating our game.

AMP version

As always with our games, we start with a one-column structure, and “include it in AMP HTML” only so it is visible only in AMP HTML.

After this, you need to add an HTML block and paste the following code into this block.

<style amp-custom>
  .fortune-cookie-amp h2 { text-align: center; padding-bottom: 20px; } .fortune-cookie-amp { text-align: center; } .fortune-cookie-amp .main-text { width: 100%; box-sizing: border-box; text-align: center; } .fortune-cookie-amp .main-img div { display: inline-block; width: 140px; margin: 0 14px; position: relative; } .fortune-cookie-amp .curtain { display: block; width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: #fff; opacity: 0.4; } .fortune-cookie-amp .result { padding-top: 15px; } .fortune-cookie-amp .result-img { max-width: 280px; margin: 0 auto; } .fortune-cookie-amp .result-text { font-size: 18px; padding:5px 0 25px 0; } .fortune-cookie-amp .vhide { display: none; } .fortune-cookie-amp .vshow { display: block; } @media only screen and (max-width: 600px) { .fortune-cookie-amp .main-img div { width: 32%; margin: 0; } } .fortune-cookie-amp .btn { display: inline-block; background-color: #fff; border: 2px solid #329CA0; border-radius: 16px; color: #329CA0; cursor: pointer; font-size: 16px; line-height: 28px; padding: 4px 32px }
</style>
<div class="fortune-cookie-amp">
  <div class="main-img">
    <div>
      <span style="cursor: pointer">
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie02.png" width="140"></amp-img>
      </span>
      <span>
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
      </span>
      <span class="curtain"></span>
    </div>
    <div>
      <span style="cursor: pointer">
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie01.png" width="140"></amp-img>
      </span>
      <span>
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
      </span>
      <span class="curtain"></span>
    </div>
    <div>
      <span style="cursor: pointer">
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie03.png" width="140"></amp-img>
      </span>
      <span>
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
      </span>
      <span class="curtain"></span>
    </div>
  </div>
</div>

At this point, we have a basic form for our game. For the game to take shape, we need to make it so that clicking on a cookie hides it and reveals an image of a broken cookie. We also need to prevent clicking on other elements once one is open.

Let's take a closer look at how to do this using the first cookie as an example.

The first span tag needs the following attributes (just paste the whole line): 

role="button" tabindex="1" [hidden]="wish == 1" on="tap:AMP.setState({wish: 1})".

Place it between span and styles.

To make it look like this:

This line of code contains the following important elements:

  • role="button" is an attribute that specifies that the element will be used as a button. In AMP, it is required along with on="tap:" as the tag we're adding it to isn't a button;
  • tabindex="1" is the focus order when navigating between elements using the Tab key. This attribute is required when using on="tap:" if the tag we're adding it to isn't a button;
  • on="tap:" is the “click” event handler;
  • AMP.setState({wish: 1}) is a special AMP syntax for creating variables. In our example, we create a variable named wish and assign it the value 1. Each cookie will have its own value, in order;
  • [hidden]="wish == 1" is an AMP attribute that hides the element when the expression inside is executed. In our case, when the variable wish is equal to 1, each cookie will have its own value, in order.

The next span tag needs to have the attribute [hidden]="wish != 1" hidden added so it will be hidden until the wish variable is equal to 1.

It must look like this:

And to the last span tag with class="curtain" we need to add [hidden]="wish == null || wish == 1" hidden. This code means that the block will be hidden from all cookies at the start of the game. Once the recipient clicks on a cookie, it will appear on all cookies except the one they clicked. This prevents the recipient from selecting other cookies after the prediction appears.

Once pasted, the whole thing must look like this:

And that's it for the first cookie. There are two more, but to save you time, here's the complete code for all three cookies (the code itself differs only in that the number 1 is replaced with the corresponding number for the new cookie). Just replace the whole segment that starts from here.

<div class="main-img">
    <div>
      <span [hidden]="wish == 1" on="tap:AMP.setState({wish: 1})" role="button" tabindex="1" style="cursor: pointer">
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie02.png" width="140"></amp-img>
      </span>
      <span [hidden]="wish != 1" hidden>
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
      </span>
      <span [hidden]="wish == null || wish == 1" hidden class="curtain"></span>
    </div>
    <div>
      <span [hidden]="wish == 2" on="tap:AMP.setState({wish: 2})" role="button" tabindex="1" style="cursor: pointer">
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie01.png" width="140"></amp-img>
      </span>
      <span [hidden]="wish != 2" hidden>
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
      </span>
      <span [hidden]="wish == null || wish == 2" hidden class="curtain"></span>
    </div>
    <div>
      <span [hidden]="wish == 3" on="tap:AMP.setState({wish: 3})" role="button" tabindex="1" style="cursor: pointer">
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie03.png" width="140"></amp-img>
      </span>
      <span [hidden]="wish != 3" hidden>
        <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
      </span>
      <span [hidden]="wish == null || wish == 3" hidden class="curtain"></span>
    </div>
  </div>

 Next, we need to add the "Open another cookie" messages and buttons. This is easy to do: just insert the following code before the last </div> tag.

<div [class]="wish !=1 ? 'result vhide' : 'result vshow' " class="result vhide">
    <p class="result-text">
      Higher open rates await.
    </p>
    <button on="tap:AMP.setState({wish: null})" type="button" class="btn">
      Open another cookie
    </button>
  </div>
  <div [class]="wish !=2 ? 'result vhide' : 'result vshow' " class="result vhide">
    <p class="result-text">
      Engage with interactive content.
    </p>
    <button on="tap:AMP.setState({wish: null})" type="button" class="btn">
      Open another cookie
    </button>
  </div>
  <div [class]="wish !=3 ? 'result vhide' : 'result vshow' " class="result vhide">
    <p class="result-text">
      Inspire through innovative designs.
    </p>
    <button on="tap:AMP.setState({wish: null})" type="button" class="btn">
      Open another cookie
    </button>
  </div>

In this code, each prediction has a div block with text and a button. This block has the attribute [class]="wish !=1 ? 'result vhide' : 'result vshow'". This attribute changes the class from vhide (which hides the block) to vshow (which shows the block), depending on the value of the wish variable. If the variable is 1, the vshow class is assigned to the first block; if 2, to the second, and so on.

The styles required for this are already in the code, and there is no need to write anything additional in the code for messages.

The code also contains an important attribute for the buttons: on="tap:AMP.setState({wish: null})". This attribute is the same for all "Open another cookie" buttons. When clicked, the value of the wish variable is cleared and the game restarts.

To change images in the game, replace these links with your own: (arrows show links for normal cookie images, while square selection shows links for broken cookies).

To change all texts in the game (predictions and buttons), replace these ones with your own (arrows for predictions, square selection for restart buttons).

The AMP version is set and done. Here’s the full code below so that you can check whether you have done everything correctly according to the guide. If you want to text it, just create another empty structure, add an HTML block inside, and paste this code.

<style amp-custom>
  .fortune-cookie-amp h2 { text-align: center; padding-bottom: 20px; } .fortune-cookie-amp { text-align: center; } .fortune-cookie-amp .main-text { width: 100%; box-sizing: border-box; text-align: center; } .fortune-cookie-amp .main-img div { display: inline-block; width: 140px; margin: 0 14px; position: relative; } .fortune-cookie-amp .curtain { display: block; width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: #fff; opacity: 0.4; } .fortune-cookie-amp .result { padding-top: 15px; } .fortune-cookie-amp .result-img { max-width: 280px; margin: 0 auto; } .fortune-cookie-amp .result-text { font-size: 18px; padding:5px 0 25px 0; } .fortune-cookie-amp .vhide { display: none; } .fortune-cookie-amp .vshow { display: block; } @media only screen and (max-width: 600px) { .fortune-cookie-amp .main-img div { width: 32%; margin: 0; } } .fortune-cookie-amp .btn { display: inline-block; background-color: #fff; border: 2px solid #329CA0; border-radius: 16px; color: #329CA0; cursor: pointer; font-size: 16px; line-height: 28px; padding: 4px 32px }
</style>
<div class="fortune-cookie-amp">
    <div class="main-img">
      <div>
        <span [hidden]="wish == 1" on="tap:AMP.setState({wish: 1})" role="button" tabindex="1" style="cursor: pointer">
          <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie02.png" width="140"></amp-img>
        </span>
        <span [hidden]="wish != 1" hidden>
          <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
        </span>
        <span [hidden]="wish == null || wish == 1" hidden class="curtain"></span>
      </div>
      <div>
        <span [hidden]="wish == 2" on="tap:AMP.setState({wish: 2})" role="button" tabindex="1" style="cursor: pointer">
          <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie01.png" width="140"></amp-img>
        </span>
        <span [hidden]="wish != 2" hidden>
          <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
        </span>
        <span [hidden]="wish == null || wish == 2" hidden class="curtain"></span>
      </div>
      <div>
        <span [hidden]="wish == 3" on="tap:AMP.setState({wish: 3})" role="button" tabindex="1" style="cursor: pointer">
          <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie03.png" width="140"></amp-img>
        </span>
        <span [hidden]="wish != 3" hidden>
          <amp-img height="145" layout="responsive" noloading src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="140"></amp-img>
        </span>
        <span [hidden]="wish == null || wish == 3" hidden class="curtain"></span>
      </div>
    </div>
  <div [class]="wish !=1 ? 'result vhide' : 'result vshow' " class="result vhide">
    <p class="result-text">
      Higher open rates await.
    </p>
    <button on="tap:AMP.setState({wish: null})" type="button" class="btn">
      Open another cookie
    </button>
  </div>
  <div [class]="wish !=2 ? 'result vhide' : 'result vshow' " class="result vhide">
    <p class="result-text">
      Engage with interactive content.
    </p>
    <button on="tap:AMP.setState({wish: null})" type="button" class="btn">
      Open another cookie
    </button>
  </div>
  <div [class]="wish !=3 ? 'result vhide' : 'result vshow' " class="result vhide">
    <p class="result-text">
      Inspire through innovative designs.
    </p>
    <button on="tap:AMP.setState({wish: null})" type="button" class="btn">
      Open another cookie
    </button>
  </div>
</div>

Kinetic version built with HTML5 and CSS3

The next thing on our “to-do” list is a kinetic version, which can also be called an interactive HTML version, built with HTML5 and CSS3. We need to add another empty one-column structure that we place below our AMP version. Select it and pick an “Include in HTML only” option.

After that, add an HTML block to this structure and paste this code:

<style>
  .fortune-cookie-html { margin: 0 auto; text-align: center; } .fortune-cookie-html .btn {display: inline-block; background-color: #fff; border: 2px solid #329CA0; border-radius: 16px; color: #329CA0; cursor: pointer; font-size: 16px; line-height: 28px; padding: 4px 32px } .fortune-cookie-html h2 { text-align: center; padding-bottom: 20px; } .fortune-cookie-html .curtain { display: none; width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: #fff; opacity: 0.4; } .fortune-cookie-html .main-img div { display: inline-block; width: 140px; margin: 0 14px; position: relative; } .fortune-cookie-html .main-img img { width: 100%; height: auto; } .fortune-cookie-html .main-img label { cursor: pointer; } .fortune-cookie-html .result, .fortune-cookie-html .cookie-1-2, .fortune-cookie-html .cookie-2-2, .fortune-cookie-html .cookie-3-2 { display: none; } .fortune-cookie-html .result .result-text { font-size: 18px !important; padding: 15px 0 25px 0; } #result-1:checked~div .result1, #result-2:checked~div .result2, #result-3:checked~div .result3 { display: block; } #result-1:checked~div .cookie-1-2, #result-2:checked~div .cookie-2-2, #result-3:checked~div .cookie-3-2 { display: block; } #result-1:checked~div .cookie-1-1, #result-2:checked~div .cookie-2-1, #result-3:checked~div .cookie-3-1 { display: none; } #result-1:checked~div .curtain-2, #result-1:checked~div .curtain-3, #result-2:checked~div .curtain-1, #result-2:checked~div .curtain-3, #result-3:checked~div .curtain-2, #result-3:checked~div .curtain-1 { display: block !important; } @media only screen and (max-width: 600px) { .fortune-cookie-html .main-img div { width: 32%; margin: 0; } }
</style>
  <form class="fortune-cookie-html">
    <input name="result" type="radio" id="result-1" style="display:none">
    <input name="result" type="radio" id="result-2" style="display:none">
    <input name="result" type="radio" id="result-3" style="display:none">
    <div>
      <div class="main-block">
        <div class="main-img">
          <div>
            <label for="result-1" class="cookie-1-1">
                <img src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie02.png" width="150" alt="" height="142">
            </label>
            <span class="cookie-1-2">
              <img alt="" height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="150">
            </span>
            <span class="curtain curtain-1"></span>
          </div>
          <div>
            <label for="result-2" class="cookie-2-1">
                <img height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie01.png" width="150" alt="">
            </label>
            <span class="cookie-2-2">
              <img width="150" alt="" height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png">
            </span>
            <span class="curtain curtain-2"></span>
          </div>
          <div>
            <label for="result-3" class="cookie-3-1">
                <img alt="" height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie03.png" width="150">
            </label>
            <span class="cookie-3-2">
              <img alt="" height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="150">
            </span>
            <span class="curtain curtain-3"></span>
          </div>
        </div>
      </div>
      <div class="result result1">
        <p class="result-text">
          Higher open rates await.
        </p>
        <button type="reset" class="btn">
          Open another cookie
        </button>
      </div>
      <div class="result result2">
        <p class="result-text">
          Engage with interactive content.
        </p>
        <button type="reset" class="btn">
          Open another cookie
        </button>
      </div>
      <div class="result result3">
        <p class="result-text">
          Inspire through innovative designs.
        </p>
        <button type="reset" class="btn">
          Open another cookie
        </button>
      </div>
    </div>
  </form>

This code you just pasted is complete, and no changes or additions is equired. However, it’s worth breaking down its nuances.

The HTML version has the same structure as the game version described above. However, the span tags containing the cookies have been replaced with label tags linked to the inputs via the for attribute. This is necessary to display the result using the :checked state.

All input tags for cookies are placed at the very top. Don't move them, as the styles use the CSS selector "~", which is tightly bound to the code structure, and if the layout changes, the game may stop working.

The entire game is wrapped in a form tag, and the "Open another cookie" button has been replaced with a button type="reset." This button resets all inputs in the form, and the game starts over.

Showing the image of the broken cookie and hiding the image of the intact cookie looks like this in the code:

Here's what the code for displaying a block that blocks clicks on adjacent cookies looks like:

And last but not least, this is the code for displaying the result:

Changing images is the same as for the AMP version. Replace these links with the ones you need (arrows for cookies, square selections for broken cookies):

The texts are also the same, and changing them is a piece of cake. Replace these ones with your own (arrows for predictions, square selections for buttons):

Fallback version

All that's left to do is create a fallback version of this game. Fallback is required for email clients that don't support HTML5 and CSS3, or AMP. Our fallback version of this game features a simple layout without interactivity, and the fallback itself consists of an image and a prediction.

We continue to work on the kinetic HTML block that we already have. Insert the following code between the </style> and the <form> tag in the HTML version you already created previously:

<!--[if !mso]><!-- -->
<input checked id="fallback_ctrl" type="checkbox" class="fallback_ctrl" style="display:none !important;mso-hide:all">
<!--<![endif]-->
<!-- FALLBACK -->
<span id="fallback" class="fallback">
  <table cellpadding="0" cellspacing="0" width="100%">
    <tbody>
      <tr>
        <td align="center" valign="top" width="560">
          <table cellspacing="0" width="100%" cellpadding="0">
            <tbody>
              <tr>
                <td align="center" class="esd-block-image" style="font-size:0px">
                  <img width="362" alt="" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie02.png" class="adapt-img" style="display:block">
                </td>
              </tr>
              <tr>
                <td align="center" class="es-p20t">
                  <p style="color:#000">
                    Higher open rates await.
                  </p>
                </td>
              </tr>
            </tbody>
          </table>
        </td>
      </tr>
    </tbody>
  </table>
</span>
<!-- /FALLBACK -->
<!--[if !mso]><!-- -->
<!-- INTERACTIVE ELEMENT -->
<div class="container" style="mso-hide:all;display:none">

You must also add this piece at the end of the code:

</div><!-- /INTERACTIVE ELEMENT -->
<!--<![endif]-->

You can change the image and prediction text by replacing these elements in the code (image link and text, respectively).

The last thing to do is add the styles that ensure only the version appropriate for the email client is displayed. Add this code to the end of the style tag:

    /* --- */
    @media screen and (-webkit-min-device-pixel-ratio: 0) {
        input.fallback_ctrl:checked~.container {
            display: block !important;
        }
 
        input.fallback_ctrl:checked~#fallback {
            display: none !important;
        }
    }
 
    [owa] .container {
        display: none !important;
    }
 
    [class~="x_container"] {
        display: none !important;
    }
 
    [id~="x_fallback"] {
        display: block !important;
    }
 
    @media screen and (max-width: 600px) {
        body[data-outlook-cycle] #fallback {
            display: block !important;
        }
 
        body[data-outlook-cycle] .container {
            display: none !important;
        }
    }

This input is used to show or hide the fallback by using styles. It’s enclosed in comments <!--[if !mso]><!--> … <!--<![endif]--> to ensure that it’s hidden in the Outlook Desktop client.

Also worth noting is the <span id=“fallback” class=“fallback”></span> section, which contains all of our fallback layout. It should have a simple, tabular layout suitable for Outlook. In our example, this is a table with links to the web version. You can create your own layout, but just make sure it’s understandable for Outlook.

While these styles don’t have clear rules for each email client, there is a set of hacks that can be used to control the display:

  • styles that start with [owa] are used for Outlook;
  • the [class~="x_container"] styles are needed for Outlook in case [owa] does not work;
  • body[data-outlook-cycle] styles are needed for Outlook on iOS and Android mobile devices;
  • mso-hide:all; is used for Outlook.com.

The full code

Here is the full code of the game, including the kinetic HTML part and the fallback version:

<style>
  .fortune-cookie-html { margin: 0 auto; text-align: center; } .fortune-cookie-html .btn {display: inline-block; background-color: #fff; border: 2px solid #329CA0; border-radius: 16px; color: #329CA0; cursor: pointer; font-size: 16px; line-height: 28px; padding: 4px 32px } .fortune-cookie-html h2 { text-align: center; padding-bottom: 20px; } .fortune-cookie-html .curtain { display: none; width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: #fff; opacity: 0.4; } .fortune-cookie-html .main-img div { display: inline-block; width: 140px; margin: 0 14px; position: relative; } .fortune-cookie-html .main-img img { width: 100%; height: auto; } .fortune-cookie-html .main-img label { cursor: pointer; } .fortune-cookie-html .result, .fortune-cookie-html .cookie-1-2, .fortune-cookie-html .cookie-2-2, .fortune-cookie-html .cookie-3-2 { display: none; } .fortune-cookie-html .result .result-text { font-size: 18px !important; padding: 15px 0 25px 0; } #result-1:checked~div .result1, #result-2:checked~div .result2, #result-3:checked~div .result3 { display: block; } #result-1:checked~div .cookie-1-2, #result-2:checked~div .cookie-2-2, #result-3:checked~div .cookie-3-2 { display: block; } #result-1:checked~div .cookie-1-1, #result-2:checked~div .cookie-2-1, #result-3:checked~div .cookie-3-1 { display: none; } #result-1:checked~div .curtain-2, #result-1:checked~div .curtain-3, #result-2:checked~div .curtain-1, #result-2:checked~div .curtain-3, #result-3:checked~div .curtain-2, #result-3:checked~div .curtain-1 { display: block !important; } @media only screen and (max-width: 600px) { .fortune-cookie-html .main-img div { width: 32%; margin: 0; } } /* --- */ @media screen and (-webkit-min-device-pixel-ratio: 0) { input.fallback_ctrl:checked~.container { display: block !important; } input.fallback_ctrl:checked~#fallback { display: none !important; } } [owa] .container { display: none !important; } [class~="x_container"] { display: none !important; } [id~="x_fallback"] { display: block !important; } @media screen and (max-width: 600px) { body[data-outlook-cycle] #fallback { display: block !important; } body[data-outlook-cycle] .container { display: none !important; } }
</style>
<!--[if !mso]><!-- -->
<input checked id="fallback_ctrl" type="checkbox" class="fallback_ctrl" style="display:none !important;mso-hide:all">
<!--<![endif]-->
<!-- FALLBACK -->
<span id="fallback" class="fallback">
  <table cellpadding="0" cellspacing="0" width="100%">
    <tbody>
      <tr>
        <td align="center" valign="top" width="560">
          <table cellspacing="0" width="100%" cellpadding="0">
            <tbody>
              <tr>
                <td align="center" class="esd-block-image" style="font-size:0px">
                  <img width="362" alt="" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie02.png" class="adapt-img" style="display:block">
                </td>
              </tr>
              <tr>
                <td align="center" class="es-p20t">
                  <p style="color:#000">
                    Higher open rates await.
                  </p>
                </td>
              </tr>
            </tbody>
          </table>
        </td>
      </tr>
    </tbody>
  </table>
</span>
<!-- /FALLBACK -->
<!--[if !mso]><!-- -->
<!-- INTERACTIVE ELEMENT -->
<div class="container" style="mso-hide:all;display:none">
  <form class="fortune-cookie-html">
    <input type="radio" id="result-1" name="result" style="display:none">
    <input name="result" type="radio" id="result-2" style="display:none">
    <input name="result" type="radio" id="result-3" style="display:none">
    <div class="divination">
      <div class="main-block">
        <div class="main-img">
          <div>
            <label for="result-1" class="cookie-1-1">
              <a>
                <img src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie02.png" width="150" alt="" height="142">
              </a>
            </label>
            <span class="cookie-1-2">
              <img alt="" height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="150">
            </span>
            <span class="curtain curtain-1"></span>
          </div>
          <div>
            <label for="result-2" class="cookie-2-1">
              <a>
                <img height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/_cookie01.png" width="150" alt="">
              </a>
            </label>
            <span class="cookie-2-2">
              <img width="150" alt="" height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png">
            </span>
            <span class="curtain curtain-2"></span>
          </div>
          <div>
            <label for="result-3" class="cookie-3-1">
              <a>
                <img alt="" height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie03.png" width="150">
              </a>
            </label>
            <span class="cookie-3-2">
              <img alt="" height="142" src="https://zlnfb.stripocdn.email/content/guids/CABINET_b85bd9088f5d940aba0226abb5f78b0194b9e9ed0fc2e1c47cd8f6dd6f56284c/images/cookie05.png" width="150">
            </span>
            <span class="curtain curtain-3"></span>
          </div>
        </div>
      </div>
      <div class="result result1">
        <p class="result-text">
          Higher open rates await.
        </p>
        <button type="reset" class="btn">
          Open another cookie
        </button>
      </div>
      <div class="result result2">
        <p class="result-text">
          Engage with interactive content.
        </p>
        <button type="reset" class="btn">
          Open another cookie
        </button>
      </div>
      <div class="result result3">
        <p class="result-text">
          Inspire through innovative designs.
        </p>
        <button type="reset" class="btn">
          Open another cookie
        </button>
      </div>
    </div>
  </form>
</div>
<!-- /INTERACTIVE ELEMENT -->
<!--<![endif]-->

Wrapping up

As you can see, the game is quite simple to create and not as cumbersome to code as some of the other mechanics we've previously discussed. However, this means you can easily and quickly create your own game, adapt it to your needs, and launch it live to see how your audience interacts with your interactive emails.

Create exceptional emails with Stripo