Reader Mode – Mobile Email Pagination

Here’s an experimental mobile email technique that allows you to paginate a content heavy email so readers can navigate from article to article without scrolling. I hacked it up over a few days as an entry to Litmus’ Creative Navigation in Email Community Contest.

Responsive email design has made emails easier to read on mobile devices. However a drawback of responsive emails is that content heavy emails tend to be narrow and tall, requiring the user to scroll and scroll to get to a piece of content at the bottom. This is an attempt at addressing the problem by allowing recipients to quickly navigate content in an email.

Reader Mode - Mobile Email Pagination


This version only works on iPhone Mail although I’m working on getting it working on Android shortly. Don’t you think the fact Apple mysteriously broke anchor links within email in iOS 8 makes this a timely solution? :)

Update: Fixed a bug where it doesn’t work in iOS7 (issues with calc() – thanks Clinton!)

Reader Mode

I’m calling this technique Reader Mode. It allows the user in a push of a button to convert the email into a reading pane where content are formatted as separate pages and the user can access them by clicking on arrows or through a “table of contents” menu.

Activation
The reader mode button is wrapped with a label that toggles a checkbox with the id navbox. Using the :checked pseudoclass the Reader mode elements are displayed, with the menu being set at position:fixed at the top of the pane.

<style>
.toolbar-container{
  position:fixed;
  top:0px; left:0px;
  display:none;
  ...
}

#navbox:checked ~ .toolbar-container{
  display:block!important;
}

#navbox:checked ~ .articles-container{
  height:100%;
  overflow:hidden;
  position:fixed;
  top:50px;
  left:0px;
}
</style>

<input id="navbox" type="checkbox">
<label for="navbox" >Activate Reader Mode</label>

<div class="toolbar-container">...hidden toolbar content ... <div>
<div class="articles-container">
  article1, article2, article3...
</div>


Article pagination
The articles are wrapped in two containers. When the :checked selector is triggered on the navbox checkbox, the outer container is set to display the full width and height of the viewport. The outer container is also set to a fixed position at top of the email minus the space held by the menu.

The inner container is set to the width of the viewport times the number of articles. The articles are set to the width of the viewport and floated left. This allows the articles to be arranged in a horizontal position, with only a single article being displayed at one time.

We then then create a radio element for each article, that when the :checked selector is triggered for a specific radio element, the inner container is shifted to left or right by the number of “view port widths” (vw) of each article. Adding a transition to the container allows for a sliding effect.

#navbox:checked ~ #a1radio:checked ~ .articles-cont .articles{
  left:0px;
}
#navbox:checked ~ #a2radio:checked ~ .articles-cont .articles{
  left:-100vw;
}
#navbox:checked ~ #a3radio:checked ~ .articles-cont .articles{
  left:-200vw;
}

Apparently vw does not work on the Android client so we may either have to resort to stacking the articles on top of each other and showing each article either by selective hiding or adjusting their z-index.

Navigating between articles
For the right and left arrows, we create a pair of right and left labels for each article that triggers the radio of the next and previous article when tapped. These labels are initially hidden and only the pair associated with the current visible article is displayed.

<style>
#navbox:checked ~ #a1radio:checked ~ .toolbar-cont .a1nav,
#navbox:checked ~ #a2radio:checked ~ .toolbar-cont .a2nav,
#navbox:checked ~ #a3radio:checked ~ .toolbar-cont .a3nav{
  display:block;
}
</style>

<div class="nav-arrows a1nav">
   <label>&nbsp;</label><label for="a2radio">&raquo;</label>
</div>
<div class="nav-arrows a2nav">
   <label for="a1radio">&laquo;</label><label for="a3radio">&raquo;</label>
</div>
<div class="nav-arrows a3nav">
   <label for="a2radio">&laquo;</label><label>&nbsp;</label>
</div>


Article index menu
The article index menu contains a list of labels associated with the articles in a hidden absolutely positioned div and is displayed when the user taps on the menu trigger using the :hover selector.

One interesting thing I noticed was that if the menu hides immediately after an item is selected, the email client ignores the selection. I found out that adding a transition and a transition delay solved that problem. I’m sure there’s a more elegant fix for this but for now that works.

Code

Here’s the code!
(The code was modified to remove the use of vw (viewport widths) in calculation as it stopped working in iOS9).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style>
* { 
  margin:0;
  padding:0;  
  font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; 
  font-size: 100%;
  line-height: 1.5;
  color:#333333;
}
img{
  max-width:100%;
}
body {
  -webkit-text-size-adjust:none; 
  width: 100%!important; 
  height: 100%;
}
h1,h2{
  margin-bottom:15px;
  line-height: 1.3;
  font-weight:300; 
}
h1 {
  font-size: 32px;
}
h2 {
  font-size: 22px;
}
input{
  max-height:0;
  display:none;
}
@media screen and (max-width: 900px) {
  .qmenu-cont{
    max-height:none!important;
    overflow:visible!important;
    z-index:10;
    width:100%;
    position:fixed;
    top:0px;
    left:0px;
    display:none;
    height:50px; 
    background-color:#eeeeee;
    border-bottom:1px solid #222222; 
  }
  .qmenu{
    height:50px;
    line-height:50px;
    display:block;
    text-decoration:none;
  }
  .qmenu-menu{
    z-index:10;
    text-align:center;
    position:fixed;
    top:51px;
    overflow:hidden;
    transition: all 0.3s;
    -webkit-transition: all 0.3s;
    transform: rotateX(90deg);
    -webkit-transform: rotateX(90deg);
    transform-origin: top;
    -webkit-transform-origin: top;
    -webkit-transition-delay: 0.5s;
    transition-delay: 0.5s;
  }
  .qmenu-menu label{
    display:block;
    padding:5px 20px 5px 20px;
  }
  .qmenu:hover + .qmenu-menu{
    display:block;
    transform: rotateX(0deg);
    -webkit-transform: rotateX(0deg);
  } 
  .articles-cont{
    background-color:#ffffff;
  }
  #navbox:checked + #main .articles-cont{
    width:100%;
    height:100%;
    overflow:hidden;
    position:fixed;
    top:50px;
    left:0px;
  }
  #navbox:checked + #main .articles-cont .articles{
    position:absolute;
    left:0px;
    width:100%;
    height:100%;
  }
  #navbox:checked + #main .articles-cont .articles .art{
    background-color:#ffffff;
    position:absolute;
    top:0px;
    left:0px;
    width:100%;
    height:1000px;
    overflow:hidden;  
    z-index:1;
    opacity:0;
  }
  #navbox:checked + #main .articles-cont .articles .art div{
    width:85%;
    margin:20px auto;
  }
  .nav-arrows{
    float:right;
    display:none;
  }
  .nav-arrows label{
    display:inline-block;
    vertical-align:middle;
    text-align:center;
    height:50px;
    width:50px;
    font-size:30px;
    font-weight:bold;
  }
  #navbox:checked + #main #a1radio:checked + div .articles-cont #a1,
  #navbox:checked + #main #a2radio:checked + div .articles-cont #a2,
  #navbox:checked + #main #a3radio:checked + div .articles-cont #a3{
    z-index:2;
    height:100%;
    overflow-y:scroll;
    opacity:1;
    transition: opacity 1s;
    -webkit-transition: opacity 1s;
  }
  #navbox:checked + #main #a1radio:checked + div .qmenu-cont #a1nav,
  #navbox:checked + #main #a2radio:checked + div .qmenu-cont #a2nav,
  #navbox:checked + #main #a3radio:checked + div .qmenu-cont #a3nav{
    display:block;
  }
  #navbox:checked + #main #a1radio:checked + div #a1label,
  #navbox:checked + #main #a2radio:checked + div #a2label,
  #navbox:checked + #main #a3radio:checked + div #a3label{
    background-color:#98AECA;
  }  
  .closer{
    display:inline-block;
    vertical-align:middle;
    text-align:center;
    line-height:50px;
    width:50px;
    font-size:20px;
    font-weight:bold;
    float:left;
  }

  #cbox-capable:checked ~ .opener div{
    margin:10px auto;
    background-color:#1abc9c;
    color:#ffffff;
    border-radius:5px;
    display: block !important;
    max-height:none !important; 
    line-height:50px;
    text-align:center;
    vertical-align:middle;
  }
  #navbox:checked + #main .opener{
    display:none;
  }
  #navbox:checked + #main .qmenu-cont{
    display:block!important;
  }
}  
</style>
</head>
<!-- this NEEDS to be absolute so .articles-cont can be 100% screen, otherwise it will only populate half of the screen if relative??? ALSO android requires a parent with position absolute or relative or else it don't position elements absolutely -->
<body style="position:absolute;top:0px;left:0px;">
<div >
<input id="navbox" type="checkbox" style="display:none!important;max-height:0;visibility:hidden;">
<div id="main" style="padding:20px;">
<input id="a1radio" name="qradio" type="radio" checked style="display:none!important;max-height:0;visibility:hidden;">
<div>
<input id="a2radio" name="qradio" type="radio" style="display:none!important;max-height:0;visibility:hidden;">
<div>
<input id="a3radio" name="qradio" type="radio" style="display:none!important;max-height:0;visibility:hidden;">
<div>
<h1>Newsletter</h1>
<div style="dislay:block;width:350px;margin:0px auto;max-width:100%">
<img src="http://placehold.it/350x150">
</div>
<!--[if !mso 9]><!-->
<input id="cbox-capable" type="checkbox" style="display:none!important;max-height:0;visibility:hidden;" checked>
<label for="navbox" class="opener">
<div style="display:none;max-height:0px;overflow:hidden;">Reader Mode</div></label>
<div class="qmenu-cont" style="display:none;max-height:0px;overflow:hidden;">
  <label for="navbox" class="closer">x</label>
  <div class="nav-arrows" id="a1nav">
     <label>&nbsp;</label><label for="a2radio">&raquo;</label>
  </div>
  <div class="nav-arrows" id="a2nav">
     <label for="a1radio">&laquo;</label><label for="a3radio">&raquo;</label>
  </div>
  <div class="nav-arrows" id="a3nav">
     <label for="a2radio">&laquo;</label><label>&nbsp;</label>
  </div>

  <a href="#" class="qmenu">Article Index...</a>
  <div class="qmenu-menu" style="text-align:left;background-color:#C8DEFA;border:1px solid #888888;font-size:15px;">
      <label id="a1label" for="a1radio">The 10 least expensive new cars to insure</label><BR>
      <label id="a2label" for="a2radio">What Should Your College Major Have Been, Really?</label><BR>
      <label id="a3label" for="a3radio">Fitness Trackers vs. Smartphones</label><BR>
  </div>
</div>
<!--<![endif]-->
<BR>
<div class="articles-cont">
  <div class="articles">
    <div class="art" id="a1"><div>
    <h2>The 10 least expensive new cars to insure</h2>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<BR><BR>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<BR><BR>
    <HR><BR><BR>
    </div></div>
    <div class="art" id="a2"><div>
    <h2>What Should Your College Major Have Been, Really?</h2>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<BR><BR>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<BR><BR>
    <HR><BR><BR>
    </div></div>
    <div class="art" id="a3"><div>
    <h2>Fitness Trackers vs. Smartphones</h2>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<BR><BR>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<BR><BR>
    <HR><BR><BR>
    </div></div>
  </div>
</div>
</div></div></div>
</div>
</div>
</body>
</html>


Next steps

Next step is to get this working for Android clients though if their new Lollipop version gains traction, this may be moot as Lollipop only comes with Gmail and Gmail doesn’t support any of these techniques!

I can see other uses for this, for example showing brief snippets in the regular layout but showing the full article or more of it when in reader mode. Another use might be to convert attachments into HTML and then encode them as hidden articles so recipients on clients that support modern HTML can read those attachments in the email itself!

I haven’t done a ton of tests on this but feel free to experiment with this and let me know what you think!

Subscribe to the #EmailGeeks Newsletter


Latest Comments
  1. Jody Gibbons

    Hey Justin,
    Wow I super impressed with your ‘reader mode’ technique! I work in the food and drink business and this would be a really good way for showing the contents of our menu’s! I’m often surprised at what can be achieved by pushing the envelope of email design and testing the waters on a new idea, this is very cool. Thanks for sharing and I’ll try to experiment with this myself shortly! :)

    Cheers
    Jody

    • Justin

      Thank you Jody! I’m really excited to see what others do with this. Feel free to share your experiments with me.

  2. Christine S

    Justin, love this technique! I work for a company that owns almost 100 automobile dealerships. Multiple emails are designed/sent out on a daily basis. This would be great for different car deals as well as service and/or parts offers. Thank you for sharing your expertise and congrats on winning the Litmus Community Contest for Email Navigation!

    • Justin

      Thank you Christine!

  3. Amanda

    Wow Justin, this is genius, I love it! Have read through your blog post and the write up on Litmus about it and have sent myself a copy of this email; I am trying it out now and it’s very cool. I hope this can be supported by most key email clients/apps and would love to see this become a commonly used responsive coding technique!
    Keep up the awesome.

Leave a Reply to Amanda Cancel reply

Your email address will not be published. Required fields are marked *