Interface Experience Maps

One of the greatest challenges of progressive enhancement lies not with the coding, but with the planning. It can be incredibly challenging to articulate how a single interface might adapt to a wide variety of situations. Interface Experience Maps (Ix Maps) can help.


Back in 2007, I was presented with this challenge while putting together a talk called Ruining the User Experience (which I co-presented for the first time at SXSW with Sarah B. Nelson). In the talk, Sarah and I discussed how to treat JavaScript as an enhancement and what happens when you don’t—in 2007, I know!

Anyway, in the talk I was struggling to convey the various decision points and interface adjustments that would happen as a result of those decisions. I don’t remember which of us came up with the idea—it was probably Sarah—but we opted to use a flowchart to visually describe what we were talking about on stage. Here was the first one we did:

An early flowchart used to describe progressive enhancement with JavaScript

It was pretty rudimentary, but it got the point across.

In future iterations of the talk, I expounded upon the idea of a flowchart for describing how an interface might adapt to different circumstances and browser capabilities. Here’s a particularly complex one I used to describe how a FAQ might function:

A flowchart describing the progressive enhancement and interaction options for an FAQ.

Over the years I found more and more ways to put these artifacts to use. And at a certain point, “flowchart” didn’t seem to cut it, so I began calling them “UI construction flows”—which, admittedly, was a mouthful—and then finally settled on the name “Interaction Experience Maps” with the help of a client.

The Benefits of Ix Maps

Ix Maps have become an invaluable tool to me and the teams I’ve worked with. They excel at articulating the different ways in which a given interface might adapt and what the end results of each adaptation might be.

This sort of documentation is invaluable to just about everyone on the team:

  • Copywriters get a clear picture of the different experience possibilities so they can craft the copy accordingly;
  • Designers can see the different experience possibilities and can create wireframes and visual designs that account for each;
  • Developers get a clear outline of what functionality is expected and know exactly what features and capability detection to employ in generating each experience; and
  • The quality assurance team has a clear picture of what they should be looking for in each component of an interface.

In short, Ix Maps ensure everyone on the team has a clear picture of what’s expected so they can work toward the common goal. One company I worked with found Ix Maps so useful that they created one for each and every pattern in their pattern library. Then they included the drawings as part of each pattern’s documentation.

Ix Maps Facilitate Collaboration and Iteration

An Ix Map is a pretty simple concept for anyone to come to grips with, making it a fantastic tool for enabling mixed teams—designers, developers, content folks, business strategists, etc.—to brainstorm ideas and build a strategy around progressive enhancement.

Time and time again, I have seen these simple diagrams bring a diverse team together and help them quickly and easily come up with very creative ways to address complex interface problems. That’s why I frequently use them as a tool in the workshops I lead.

Because they are so basic, Ix Maps can be sketched out quickly on paper, a whiteboard, or in software like OmniGraffle. And their simplicity also makes it quite easy to explore different ideas of how to adapt things and you don’t have to worry about throwing away an idea that doesn’t play out because it’s only a few boxes and arrows… you haven’t invested any time in design or production.

Here’s an example from my Beyond Responsive workshop that illustrates the evolution of a tabbed interface Ix Map from basic into something that is far more appropriate for differently-sized screens:

Pass 1: If JavaScript’s available, make a tabbed interface out of linear content. If not, leave it as it was.

Pass 2: Add a live width test into the mix to see if there’s enough room for the tabs and make it an accordion if the screen is too narrow.

Pass 3: Test for native details/summary support and use the native functionality if available.

Simple, Powerful

Ix Maps have been incredibly useful to me and the dozens of teams I’ve worked with. They’ve helped us explore innovative ways to solve design challenges and have been an amazing touchstone to organize our work around. Give them a shot and I’m sure you’ll discover lots of ways to put them to use in your own projects.

[{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"http://news.publicel.net/2016/08/30/building-in-10k-content-and-strategy/","published":null,"wm-received":"2016-08-30T17:16:40Z","wm-id":372097,"wm-source":"http://news.publicel.net/2016/08/30/building-in-10k-content-and-strategy/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"Building in 10k: Content and Strategy","content":{"content-type":"text/html","value":"<p><em>Editor’s note: This is the first in a series of posts from the team that built the <a href=\"https://a-k-apart.com/\">10k Apart contest page</a>, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB.</em></p>\n<p>When <a href=\"https://twitter.com/zeldman/\">Jeffrey Zeldman</a> first approached me about bringing back <a href=\"https://a-k-apart.com/\">the 10k Apart contest</a>, my mind began to race. I’d been a judge on <a href=\"http://trentwalton.com/2011/08/03/10k-apart-the-responsive-edition/\">the 2011 incarnation of the contest</a>, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today.</p>\n<p>The 10k Apart (and <a href=\"http://www.alistapart.com/articles/5k/\">the 5k</a> before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement.</p>\n<p>And so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”.</p>\n<p>I was so excited when Jeffrey and <a href=\"https://twitter.com/meyerweb\">Eric Meyer</a> responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble.</p>\n<p>This is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved.</p>\n<p>Thanks for joining us on this journey…</p>\n<h1><strong>What are you here to do?</strong></h1>\n<p>Before tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time?</p>\n<p>Asking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them.</p>\n<p>To accomplish this, the site would need to evolve with our audience through several phases:</p>\n<ol><li><strong>Launch</strong> – When we don’t have any entries (which is what is live as I am writing this);</li>\n<li><strong>In progress</strong> – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon);</li>\n<li><strong>Close</strong> – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and</li>\n<li><strong>Winner announcement</strong> – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best.</li>\n</ol><p>With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the <em>nice to have</em> bits. In many ways, I followed the model Luke Wroblewski wrote about in <a href=\"https://abookapart.com/products/mobile-first\">Mobile First</a>, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began.</p>\n<h1><strong>How do we talk to one another?</strong></h1>\n<p><a href=\"https://twitter.com/steph_hay\">Steph Hay</a> is often quick to point out that <a href=\"http://alistapart.com/blog/post/content-first-design\">every interface is a conversation we’re having with out users</a>. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest.</p>\n<p>In their book <a href=\"http://www.nicelysaid.co/\">Nicely Said</a>, <a href=\"https://twitter.com/katekiefer\">Kate Kiefer Lee</a> and <a href=\"https://twitter.com/nicoleslaw\">Nicole Fenton</a> offer a ridiculous amount of great advice on not just how to write, but how to write <em>for people</em>. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing!</p>\n<p>I applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out.</p>\n<h1><strong>Where are the patterns?</strong></h1>\n<p>Once I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well.</p>\n<p><a href=\"https://winblogs.azureedge.net/win/2016/08/patterns.png\"><img alt=\"Example wireframe from https://a-k-apart.com\" height=\"500\" src=\"https://winblogs.azureedge.net/win/2016/08/patterns-993x1024.png\" width=\"485\" /></a></p>\n<p>While working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example.</p>\n<p>For more complicated interactions, I used <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">Interface Experience Maps</a> (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available.</p>\n<p><a href=\"https://winblogs.azureedge.net/win/2016/08/ix-map.png\"><img alt=\"IxMap exploration of lazy loading screenshots when JavaScript is available. This IxMap depicts lazy loading a picture element and supplying alternate image formats for browsers that support WebP.\" height=\"500\" src=\"https://winblogs.azureedge.net/win/2016/08/ix-map-951x1024.png\" width=\"464\" /></a></p>\n<p>In that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this.</p>\n<p>Once the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, <a href=\"https://twitter.com/seaotta\">Stephanie Stimac</a>, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series.</p>\n<h1><strong>What did we learn?</strong></h1>\n<p>They may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture:</p>\n<ul><li><strong>Minimize distractions</strong> – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that;</li>\n<li><strong>Write like you speak</strong> — Write for people like you would speak to them in person and read your work aloud;</li>\n<li><strong>Set content priorities</strong> — Ensure the flow is right and leads your users where they want (or need) to go;</li>\n<li><strong>Look for opportunities to enhance</strong> — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and</li>\n<li><strong>Look for patterns</strong> — Focus on creating a design system rather than individual pages.</li>\n</ul><h1><strong>Where to next?</strong></h1>\n<p>With a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned!</p>\n<p>― <a href=\"https://www.aaron-gustafson.com/\">Aaron Gustafson</a>, Web Standards Advocate</p>\n<p>Source: Windows\nVia: <a href=\"http://blogs.windows.com/msedgedev/2016/08/30/building-in-10k-content-and-strategy/\">Building in 10k: Content and Strategy</a></p>\n<a href=\"http://www.hupso.com/share/\"><img alt=\"Share Button\" src=\"http://static.hupso.com/share/buttons/share-small.png\" style=\"border:0px;padding-top:2px;float:left;\" /></a>","html":"<p><em>Editor’s note: This is the first in a series of posts from the team that built the <a href=\"https://a-k-apart.com/\">10k Apart contest page</a>, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB.</em></p>\n<p>When <a href=\"https://twitter.com/zeldman/\">Jeffrey Zeldman</a> first approached me about bringing back <a href=\"https://a-k-apart.com/\">the 10k Apart contest</a>, my mind began to race. I’d been a judge on <a href=\"http://trentwalton.com/2011/08/03/10k-apart-the-responsive-edition/\">the 2011 incarnation of the contest</a>, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today.</p>\n<p>The 10k Apart (and <a href=\"http://www.alistapart.com/articles/5k/\">the 5k</a> before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement.</p>\n<p>And so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”.</p>\n<p>I was so excited when Jeffrey and <a href=\"https://twitter.com/meyerweb\">Eric Meyer</a> responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble.</p>\n<p>This is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved.</p>\n<p>Thanks for joining us on this journey…</p>\n<h1><strong>What are you here to do?</strong></h1>\n<p>Before tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time?</p>\n<p>Asking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them.</p>\n<p>To accomplish this, the site would need to evolve with our audience through several phases:</p>\n<ol><li><strong>Launch</strong> – When we don’t have any entries (which is what is live as I am writing this);</li>\n<li><strong>In progress</strong> – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon);</li>\n<li><strong>Close</strong> – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and</li>\n<li><strong>Winner announcement</strong> – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best.</li>\n</ol><p>With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the <em>nice to have</em> bits. In many ways, I followed the model Luke Wroblewski wrote about in <a href=\"https://abookapart.com/products/mobile-first\">Mobile First</a>, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began.</p>\n<h1><strong>How do we talk to one another?</strong></h1>\n<p><a href=\"https://twitter.com/steph_hay\">Steph Hay</a> is often quick to point out that <a href=\"http://alistapart.com/blog/post/content-first-design\">every interface is a conversation we’re having with out users</a>. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest.</p>\n<p>In their book <a href=\"http://www.nicelysaid.co/\">Nicely Said</a>, <a href=\"https://twitter.com/katekiefer\">Kate Kiefer Lee</a> and <a href=\"https://twitter.com/nicoleslaw\">Nicole Fenton</a> offer a ridiculous amount of great advice on not just how to write, but how to write <em>for people</em>. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing!</p>\n<p>I applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out.</p>\n<h1><strong>Where are the patterns?</strong></h1>\n<p>Once I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well.</p>\n<p><a href=\"https://winblogs.azureedge.net/win/2016/08/patterns.png\"><img alt=\"Example wireframe from https://a-k-apart.com\" height=\"500\" src=\"https://winblogs.azureedge.net/win/2016/08/patterns-993x1024.png\" width=\"485\" /></a></p>\n<p>While working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example.</p>\n<p>For more complicated interactions, I used <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">Interface Experience Maps</a> (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available.</p>\n<p><a href=\"https://winblogs.azureedge.net/win/2016/08/ix-map.png\"><img alt=\"IxMap exploration of lazy loading screenshots when JavaScript is available. This IxMap depicts lazy loading a picture element and supplying alternate image formats for browsers that support WebP.\" height=\"500\" src=\"https://winblogs.azureedge.net/win/2016/08/ix-map-951x1024.png\" width=\"464\" /></a></p>\n<p>In that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this.</p>\n<p>Once the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, <a href=\"https://twitter.com/seaotta\">Stephanie Stimac</a>, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series.</p>\n<h1><strong>What did we learn?</strong></h1>\n<p>They may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture:</p>\n<ul><li><strong>Minimize distractions</strong> – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that;</li>\n<li><strong>Write like you speak</strong> — Write for people like you would speak to them in person and read your work aloud;</li>\n<li><strong>Set content priorities</strong> — Ensure the flow is right and leads your users where they want (or need) to go;</li>\n<li><strong>Look for opportunities to enhance</strong> — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and</li>\n<li><strong>Look for patterns</strong> — Focus on creating a design system rather than individual pages.</li>\n</ul><h1><strong>Where to next?</strong></h1>\n<p>With a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned!</p>\n<p>― <a href=\"https://www.aaron-gustafson.com/\">Aaron Gustafson</a>, Web Standards Advocate</p>\n<p>Source: Windows\nVia: <a href=\"http://blogs.windows.com/msedgedev/2016/08/30/building-in-10k-content-and-strategy/\">Building in 10k: Content and Strategy</a></p>\n<a href=\"http://www.hupso.com/share/\"><img alt=\"Share Button\" src=\"http://static.hupso.com/share/buttons/share-small.png\" style=\"border:0px;padding-top:2px;float:left;\" /></a>","text":"Editor’s note: This is the first in a series of posts from the team that built the 10k Apart contest page, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB. \nWhen Jeffrey Zeldman first approached me about bringing back the 10k Apart contest, my mind began to race. I’d been a judge on the 2011 incarnation of the contest, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today. \nThe 10k Apart (and the 5k before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement. \nAnd so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”. \nI was so excited when Jeffrey and Eric Meyer responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble. \nThis is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved. \nThanks for joining us on this journey… \nWhat are you here to do? \nBefore tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time? \nAsking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them. \nTo accomplish this, the site would need to evolve with our audience through several phases: \nLaunch – When we don’t have any entries (which is what is live as I am writing this); \nIn progress – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon); \nClose – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and \nWinner announcement – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best. \n With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the nice to have bits. In many ways, I followed the model Luke Wroblewski wrote about in Mobile First, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began. \nHow do we talk to one another? \nSteph Hay is often quick to point out that every interface is a conversation we’re having with out users. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest. \nIn their book Nicely Said, Kate Kiefer Lee and Nicole Fenton offer a ridiculous amount of great advice on not just how to write, but how to write for people. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing! \nI applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out. \nWhere are the patterns? \nOnce I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well. \nExample wireframe from https://a-k-apart.com \nWhile working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example. \nFor more complicated interactions, I used Interface Experience Maps (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available. \nIxMap exploration of lazy loading screenshots when JavaScript is available. This IxMap depicts lazy loading a picture element and supplying alternate image formats for browsers that support WebP. \nIn that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this. \nOnce the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, Stephanie Stimac, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series. \nWhat did we learn? \nThey may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture: \nMinimize distractions – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that; \nWrite like you speak — Write for people like you would speak to them in person and read your work aloud; \nSet content priorities — Ensure the flow is right and leads your users where they want (or need) to go; \nLook for opportunities to enhance — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and \nLook for patterns — Focus on creating a design system rather than individual pages. \n Where to next? \nWith a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned! \n― Aaron Gustafson, Web Standards Advocate \nSource: Windows\n\nVia: Building in 10k: Content and Strategy \nShare Button"},"mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"http://cooltech4fun.com/blog/2016/08/30/building-in-10k-content-and-strategy/","published":null,"wm-received":"2016-08-30T17:57:09Z","wm-id":372131,"wm-source":"http://cooltech4fun.com/blog/2016/08/30/building-in-10k-content-and-strategy/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"Building in 10k: Content and Strategy","content":{"content-type":"text/html","value":"DISCLAIMER: !!! <a href=\"http://blogs.windows.com/msedgedev/2016/08/30/building-in-10k-content-and-strategy/\">This post</a> was originally published <a href=\"https://blogs.windows.com/\">here</a> !!!<p>FROM <a href=\"https://blogs.windows.com\">WINDOWS BLOG</a> © (All Rights Reserved).</p>\n<p><em>Editor’s note: This is the first in a series of posts from the team that built the <a href=\"https://a-k-apart.com/\">10k Apart contest page</a>, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB.</em></p>\n<p>When <a href=\"https://twitter.com/zeldman/\">Jeffrey Zeldman</a> first approached me about bringing back <a href=\"https://a-k-apart.com/\">the 10k Apart contest</a>, my mind began to race. I’d been a judge on <a href=\"http://trentwalton.com/2011/08/03/10k-apart-the-responsive-edition/\">the 2011 incarnation of the contest</a>, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today.</p>\n<p>The 10k Apart (and <a href=\"http://www.alistapart.com/articles/5k/\">the 5k</a> before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement.</p>\n<p>And so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”.</p>\n<p>I was so excited when Jeffrey and <a href=\"https://twitter.com/meyerweb\">Eric Meyer</a> responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble.</p>\n<p>This is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved.</p>\n<p>Thanks for joining us on this journey…</p>\n<h1><strong>What are you here to do?</strong></h1>\n<p>Before tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time?</p>\n<p>Asking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them.</p>\n<p>To accomplish this, the site would need to evolve with our audience through several phases:</p>\n<ol><li><strong>Launch</strong> – When we don’t have any entries (which is what is live as I am writing this);</li>\n<li><strong>In progress</strong> – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon);</li>\n<li><strong>Close</strong> – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and</li>\n<li><strong>Winner announcement</strong> – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best.</li>\n</ol><p>With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the <em>nice to have</em> bits. In many ways, I followed the model Luke Wroblewski wrote about in <a href=\"https://abookapart.com/products/mobile-first\">Mobile First</a>, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began.</p>\n<h1><strong>How do we talk to one another?</strong></h1>\n<p><a href=\"https://twitter.com/steph_hay\">Steph Hay</a> is often quick to point out that <a href=\"http://alistapart.com/blog/post/content-first-design\">every interface is a conversation we’re having with out users</a>. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest.</p>\n<p>In their book <a href=\"http://www.nicelysaid.co/\">Nicely Said</a>, <a href=\"https://twitter.com/katekiefer\">Kate Kiefer Lee</a> and <a href=\"https://twitter.com/nicoleslaw\">Nicole Fenton</a> offer a ridiculous amount of great advice on not just how to write, but how to write <em>for people</em>. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing!</p>\n<p>I applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out.</p>\n<h1><strong>Where are the patterns?</strong></h1>\n<p>Once I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well.</p>\n<p><a href=\"http://i0.wp.com/winblogs.azureedge.net/win/2016/08/patterns.png?ssl=1\"><img alt=\"Example wireframe from https://a-k-apart.com\" src=\"http://i1.wp.com/winblogs.azureedge.net/win/2016/08/patterns-500.png?resize=485%2C500&amp;ssl=1\" /></a></p>\n<p>While working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example.</p>\n<p>For more complicated interactions, I used <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">Interface Experience Maps</a> (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available.</p>\n<p><a href=\"http://i2.wp.com/winblogs.azureedge.net/win/2016/08/ix-map.png?ssl=1\"><img alt=\"IxMap exploration of lazy loading screenshots when JavaScript is available. This IxMap depicts lazy loading a picture element and supplying alternate image formats for browsers that support WebP.\" src=\"http://i1.wp.com/winblogs.azureedge.net/win/2016/08/ix-map-500.png?resize=464%2C500&amp;ssl=1\" /></a></p>\n<p>In that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this.</p>\n<p>Once the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, <a href=\"https://twitter.com/seaotta\">Stephanie Stimac</a>, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series.</p>\n<h1><strong>What did we learn?</strong></h1>\n<p>They may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture:</p>\n<ul><li><strong>Minimize distractions</strong> – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that;</li>\n<li><strong>Write like you speak</strong> — Write for people like you would speak to them in person and read your work aloud;</li>\n<li><strong>Set content priorities</strong> — Ensure the flow is right and leads your users where they want (or need) to go;</li>\n<li><strong>Look for opportunities to enhance</strong> — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and</li>\n<li><strong>Look for patterns</strong> — Focus on creating a design system rather than individual pages.</li>\n</ul><h1><strong>Where to next?</strong></h1>\n<p>With a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned!</p>\n<p>― <a href=\"https://www.aaron-gustafson.com/\">Aaron Gustafson</a>, Web Standards Advocate</p>\n<h3>Share this:</h3><ul><li><a href=\"http://cooltech4fun.com/blog/2016/08/30/building-in-10k-content-and-strategy/?share=twitter\" title=\"Click to share on Twitter\">Click to share on Twitter (Opens in new window)</a></li><li><a href=\"http://cooltech4fun.com/blog/2016/08/30/building-in-10k-content-and-strategy/?share=facebook\" title=\"Click to share on Facebook\">Click to share on Facebook (Opens in new window)</a></li><li><a href=\"http://cooltech4fun.com/blog/2016/08/30/building-in-10k-content-and-strategy/?share=google-plus-1\" title=\"Click to share on Google+\">Click to share on Google+ (Opens in new window)</a></li><li></li></ul>\n\n\t<h3><em>Related</em></h3>","html":"DISCLAIMER: !!! <a href=\"http://blogs.windows.com/msedgedev/2016/08/30/building-in-10k-content-and-strategy/\">This post</a> was originally published <a href=\"https://blogs.windows.com/\">here</a> !!!<p>FROM <a href=\"https://blogs.windows.com\">WINDOWS BLOG</a> © (All Rights Reserved).</p>\n<p><em>Editor’s note: This is the first in a series of posts from the team that built the <a href=\"https://a-k-apart.com/\">10k Apart contest page</a>, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB.</em></p>\n<p>When <a href=\"https://twitter.com/zeldman/\">Jeffrey Zeldman</a> first approached me about bringing back <a href=\"https://a-k-apart.com/\">the 10k Apart contest</a>, my mind began to race. I’d been a judge on <a href=\"http://trentwalton.com/2011/08/03/10k-apart-the-responsive-edition/\">the 2011 incarnation of the contest</a>, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today.</p>\n<p>The 10k Apart (and <a href=\"http://www.alistapart.com/articles/5k/\">the 5k</a> before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement.</p>\n<p>And so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”.</p>\n<p>I was so excited when Jeffrey and <a href=\"https://twitter.com/meyerweb\">Eric Meyer</a> responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble.</p>\n<p>This is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved.</p>\n<p>Thanks for joining us on this journey…</p>\n<h1><strong>What are you here to do?</strong></h1>\n<p>Before tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time?</p>\n<p>Asking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them.</p>\n<p>To accomplish this, the site would need to evolve with our audience through several phases:</p>\n<ol><li><strong>Launch</strong> – When we don’t have any entries (which is what is live as I am writing this);</li>\n<li><strong>In progress</strong> – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon);</li>\n<li><strong>Close</strong> – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and</li>\n<li><strong>Winner announcement</strong> – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best.</li>\n</ol><p>With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the <em>nice to have</em> bits. In many ways, I followed the model Luke Wroblewski wrote about in <a href=\"https://abookapart.com/products/mobile-first\">Mobile First</a>, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began.</p>\n<h1><strong>How do we talk to one another?</strong></h1>\n<p><a href=\"https://twitter.com/steph_hay\">Steph Hay</a> is often quick to point out that <a href=\"http://alistapart.com/blog/post/content-first-design\">every interface is a conversation we’re having with out users</a>. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest.</p>\n<p>In their book <a href=\"http://www.nicelysaid.co/\">Nicely Said</a>, <a href=\"https://twitter.com/katekiefer\">Kate Kiefer Lee</a> and <a href=\"https://twitter.com/nicoleslaw\">Nicole Fenton</a> offer a ridiculous amount of great advice on not just how to write, but how to write <em>for people</em>. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing!</p>\n<p>I applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out.</p>\n<h1><strong>Where are the patterns?</strong></h1>\n<p>Once I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well.</p>\n<p><a href=\"http://i0.wp.com/winblogs.azureedge.net/win/2016/08/patterns.png?ssl=1\"><img alt=\"Example wireframe from https://a-k-apart.com\" src=\"http://i1.wp.com/winblogs.azureedge.net/win/2016/08/patterns-500.png?resize=485%2C500&amp;ssl=1\" /></a></p>\n<p>While working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example.</p>\n<p>For more complicated interactions, I used <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">Interface Experience Maps</a> (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available.</p>\n<p><a href=\"http://i2.wp.com/winblogs.azureedge.net/win/2016/08/ix-map.png?ssl=1\"><img alt=\"IxMap exploration of lazy loading screenshots when JavaScript is available. This IxMap depicts lazy loading a picture element and supplying alternate image formats for browsers that support WebP.\" src=\"http://i1.wp.com/winblogs.azureedge.net/win/2016/08/ix-map-500.png?resize=464%2C500&amp;ssl=1\" /></a></p>\n<p>In that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this.</p>\n<p>Once the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, <a href=\"https://twitter.com/seaotta\">Stephanie Stimac</a>, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series.</p>\n<h1><strong>What did we learn?</strong></h1>\n<p>They may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture:</p>\n<ul><li><strong>Minimize distractions</strong> – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that;</li>\n<li><strong>Write like you speak</strong> — Write for people like you would speak to them in person and read your work aloud;</li>\n<li><strong>Set content priorities</strong> — Ensure the flow is right and leads your users where they want (or need) to go;</li>\n<li><strong>Look for opportunities to enhance</strong> — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and</li>\n<li><strong>Look for patterns</strong> — Focus on creating a design system rather than individual pages.</li>\n</ul><h1><strong>Where to next?</strong></h1>\n<p>With a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned!</p>\n<p>― <a href=\"https://www.aaron-gustafson.com/\">Aaron Gustafson</a>, Web Standards Advocate</p>\n<h3>Share this:</h3><ul><li><a href=\"http://cooltech4fun.com/blog/2016/08/30/building-in-10k-content-and-strategy/?share=twitter\" title=\"Click to share on Twitter\">Click to share on Twitter (Opens in new window)</a></li><li><a href=\"http://cooltech4fun.com/blog/2016/08/30/building-in-10k-content-and-strategy/?share=facebook\" title=\"Click to share on Facebook\">Click to share on Facebook (Opens in new window)</a></li><li><a href=\"http://cooltech4fun.com/blog/2016/08/30/building-in-10k-content-and-strategy/?share=google-plus-1\" title=\"Click to share on Google+\">Click to share on Google+ (Opens in new window)</a></li><li></li></ul>\n\n\t<h3><em>Related</em></h3>","text":"DISCLAIMER: !!! This post was originally published here !!!FROM WINDOWS BLOG © (All Rights Reserved). \nEditor’s note: This is the first in a series of posts from the team that built the 10k Apart contest page, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB. \nWhen Jeffrey Zeldman first approached me about bringing back the 10k Apart contest, my mind began to race. I’d been a judge on the 2011 incarnation of the contest, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today. \nThe 10k Apart (and the 5k before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement. \nAnd so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”. \nI was so excited when Jeffrey and Eric Meyer responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble. \nThis is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved. \nThanks for joining us on this journey… \nWhat are you here to do? \nBefore tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time? \nAsking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them. \nTo accomplish this, the site would need to evolve with our audience through several phases: \nLaunch – When we don’t have any entries (which is what is live as I am writing this); \nIn progress – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon); \nClose – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and \nWinner announcement – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best. \n With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the nice to have bits. In many ways, I followed the model Luke Wroblewski wrote about in Mobile First, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began. \nHow do we talk to one another? \nSteph Hay is often quick to point out that every interface is a conversation we’re having with out users. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest. \nIn their book Nicely Said, Kate Kiefer Lee and Nicole Fenton offer a ridiculous amount of great advice on not just how to write, but how to write for people. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing! \nI applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out. \nWhere are the patterns? \nOnce I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well. \nExample wireframe from https://a-k-apart.com \nWhile working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example. \nFor more complicated interactions, I used Interface Experience Maps (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available. \nIxMap exploration of lazy loading screenshots when JavaScript is available. This IxMap depicts lazy loading a picture element and supplying alternate image formats for browsers that support WebP. \nIn that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this. \nOnce the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, Stephanie Stimac, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series. \nWhat did we learn? \nThey may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture: \nMinimize distractions – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that; \nWrite like you speak — Write for people like you would speak to them in person and read your work aloud; \nSet content priorities — Ensure the flow is right and leads your users where they want (or need) to go; \nLook for opportunities to enhance — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and \nLook for patterns — Focus on creating a design system rather than individual pages. \n Where to next? \nWith a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned! \n― Aaron Gustafson, Web Standards Advocate \nShare this: Click to share on Twitter (Opens in new window) Click to share on Facebook (Opens in new window) Click to share on Google+ (Opens in new window)      \n\n\tRelated"},"mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"http://www.x-vision.co.at/building-in-10k-content-and-strategy/","published":null,"wm-received":"2016-08-30T19:09:51Z","wm-id":372150,"wm-source":"http://www.x-vision.co.at/building-in-10k-content-and-strategy/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"Building in 10k: Content and Strategy","mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"http://www.gmycinstudio.com/building-in-10k-content-and-strategy/","published":null,"wm-received":"2016-08-31T09:29:51Z","wm-id":372317,"wm-source":"http://www.gmycinstudio.com/building-in-10k-content-and-strategy/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"Building in 10k: Content and Strategy","content":{"content-type":"text/html","value":"<a href=\"http://blogs.windows.com/msedgedev/2016/08/30/building-in-10k-content-and-strategy/\"><img alt=\"Example wireframe from https://a-k-apart.com\" src=\"https://winblogs.azureedge.net/win/2016/08/patterns-500.png\" width=\"150\" /></a>\n<p><em>Editor’s note: This is the first in a series of posts from the team that built the <a href=\"https://a-k-apart.com/\">10k Apart contest page</a>, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB.</em></p>\n<p>When <a href=\"https://twitter.com/zeldman/\">Jeffrey Zeldman</a> first approached me about bringing back <a href=\"https://a-k-apart.com/\">the 10k Apart contest</a>, my mind began to race. I’d been a judge on <a href=\"http://trentwalton.com/2011/08/03/10k-apart-the-responsive-edition/\">the 2011 incarnation of the contest</a>, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today.</p>\n<p>The 10k Apart (and <a href=\"http://www.alistapart.com/articles/5k/\">the 5k</a> before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement.</p>\n<p>And so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”.</p>\n<p>I was so excited when Jeffrey and <a href=\"https://twitter.com/meyerweb\">Eric Meyer</a> responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble.</p>\n<p>This is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved.</p>\n<p>Thanks for joining us on this journey…</p>\n<h1><strong>What are you here to do?</strong></h1>\n<p>Before tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time?</p>\n<p>Asking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them.</p>\n<p>To accomplish this, the site would need to evolve with our audience through several phases:</p>\n<ol><li><strong>Launch</strong> – When we don’t have any entries (which is what is live as I am writing this);</li>\n<li><strong>In progress</strong> – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon);</li>\n<li><strong>Close</strong> – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and</li>\n<li><strong>Winner announcement</strong> – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best.</li>\n</ol><p>With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the <em>nice to have</em> bits. In many ways, I followed the model Luke Wroblewski wrote about in <a href=\"https://abookapart.com/products/mobile-first\">Mobile First</a>, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began.</p>\n<h1><strong>How do we talk to one another?</strong></h1>\n<p><a href=\"https://twitter.com/steph_hay\">Steph Hay</a> is often quick to point out that <a href=\"http://alistapart.com/blog/post/content-first-design\">every interface is a conversation we’re having with out users</a>. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest.</p>\n<p>In their book <a href=\"http://www.nicelysaid.co/\">Nicely Said</a>, <a href=\"https://twitter.com/katekiefer\">Kate Kiefer Lee</a> and <a href=\"https://twitter.com/nicoleslaw\">Nicole Fenton</a> offer a ridiculous amount of great advice on not just how to write, but how to write <em>for people</em>. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing!</p>\n<p>I applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out.</p>\n<h1><strong>Where are the patterns?</strong></h1>\n<p>Once I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well.</p>\n\n<p>While working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example.</p>\n<p>For more complicated interactions, I used <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">Interface Experience Maps</a> (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available.</p>\n<p><a href=\"https://winblogs.azureedge.net/win/2016/08/ix-map.png\"></a></p>\n<p>In that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this.</p>\n<p>Once the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, <a href=\"https://twitter.com/seaotta\">Stephanie Stimac</a>, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series.</p>\n<h1><strong>What did we learn?</strong></h1>\n<p>They may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture:</p>\n<ul><li><strong>Minimize distractions</strong> – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that;</li>\n<li><strong>Write like you speak</strong> — Write for people like you would speak to them in person and read your work aloud;</li>\n<li><strong>Set content priorities</strong> — Ensure the flow is right and leads your users where they want (or need) to go;</li>\n<li><strong>Look for opportunities to enhance</strong> — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and</li>\n<li><strong>Look for patterns</strong> — Focus on creating a design system rather than individual pages.</li>\n</ul><h1><strong>Where to next?</strong></h1>\n<p>With a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned!</p>\n<p>― <a href=\"https://www.aaron-gustafson.com/\">Aaron Gustafson</a>, Web Standards Advocate</p>","html":"<a href=\"http://blogs.windows.com/msedgedev/2016/08/30/building-in-10k-content-and-strategy/\"><img alt=\"Example wireframe from https://a-k-apart.com\" src=\"https://winblogs.azureedge.net/win/2016/08/patterns-500.png\" width=\"150\" /></a>\n<p><em>Editor’s note: This is the first in a series of posts from the team that built the <a href=\"https://a-k-apart.com/\">10k Apart contest page</a>, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB.</em></p>\n<p>When <a href=\"https://twitter.com/zeldman/\">Jeffrey Zeldman</a> first approached me about bringing back <a href=\"https://a-k-apart.com/\">the 10k Apart contest</a>, my mind began to race. I’d been a judge on <a href=\"http://trentwalton.com/2011/08/03/10k-apart-the-responsive-edition/\">the 2011 incarnation of the contest</a>, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today.</p>\n<p>The 10k Apart (and <a href=\"http://www.alistapart.com/articles/5k/\">the 5k</a> before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement.</p>\n<p>And so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”.</p>\n<p>I was so excited when Jeffrey and <a href=\"https://twitter.com/meyerweb\">Eric Meyer</a> responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble.</p>\n<p>This is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved.</p>\n<p>Thanks for joining us on this journey…</p>\n<h1><strong>What are you here to do?</strong></h1>\n<p>Before tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time?</p>\n<p>Asking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them.</p>\n<p>To accomplish this, the site would need to evolve with our audience through several phases:</p>\n<ol><li><strong>Launch</strong> – When we don’t have any entries (which is what is live as I am writing this);</li>\n<li><strong>In progress</strong> – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon);</li>\n<li><strong>Close</strong> – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and</li>\n<li><strong>Winner announcement</strong> – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best.</li>\n</ol><p>With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the <em>nice to have</em> bits. In many ways, I followed the model Luke Wroblewski wrote about in <a href=\"https://abookapart.com/products/mobile-first\">Mobile First</a>, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began.</p>\n<h1><strong>How do we talk to one another?</strong></h1>\n<p><a href=\"https://twitter.com/steph_hay\">Steph Hay</a> is often quick to point out that <a href=\"http://alistapart.com/blog/post/content-first-design\">every interface is a conversation we’re having with out users</a>. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest.</p>\n<p>In their book <a href=\"http://www.nicelysaid.co/\">Nicely Said</a>, <a href=\"https://twitter.com/katekiefer\">Kate Kiefer Lee</a> and <a href=\"https://twitter.com/nicoleslaw\">Nicole Fenton</a> offer a ridiculous amount of great advice on not just how to write, but how to write <em>for people</em>. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing!</p>\n<p>I applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out.</p>\n<h1><strong>Where are the patterns?</strong></h1>\n<p>Once I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well.</p>\n\n<p>While working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example.</p>\n<p>For more complicated interactions, I used <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">Interface Experience Maps</a> (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available.</p>\n<p><a href=\"https://winblogs.azureedge.net/win/2016/08/ix-map.png\"></a></p>\n<p>In that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this.</p>\n<p>Once the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, <a href=\"https://twitter.com/seaotta\">Stephanie Stimac</a>, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series.</p>\n<h1><strong>What did we learn?</strong></h1>\n<p>They may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture:</p>\n<ul><li><strong>Minimize distractions</strong> – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that;</li>\n<li><strong>Write like you speak</strong> — Write for people like you would speak to them in person and read your work aloud;</li>\n<li><strong>Set content priorities</strong> — Ensure the flow is right and leads your users where they want (or need) to go;</li>\n<li><strong>Look for opportunities to enhance</strong> — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and</li>\n<li><strong>Look for patterns</strong> — Focus on creating a design system rather than individual pages.</li>\n</ul><h1><strong>Where to next?</strong></h1>\n<p>With a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned!</p>\n<p>― <a href=\"https://www.aaron-gustafson.com/\">Aaron Gustafson</a>, Web Standards Advocate</p>","text":"Example wireframe from https://a-k-apart.com \nEditor’s note: This is the first in a series of posts from the team that built the 10k Apart contest page, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB. \nWhen Jeffrey Zeldman first approached me about bringing back the 10k Apart contest, my mind began to race. I’d been a judge on the 2011 incarnation of the contest, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today. \nThe 10k Apart (and the 5k before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement. \nAnd so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”. \nI was so excited when Jeffrey and Eric Meyer responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble. \nThis is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved. \nThanks for joining us on this journey… \nWhat are you here to do? \nBefore tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time? \nAsking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them. \nTo accomplish this, the site would need to evolve with our audience through several phases: \nLaunch – When we don’t have any entries (which is what is live as I am writing this); \nIn progress – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon); \nClose – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and \nWinner announcement – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best. \n With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the nice to have bits. In many ways, I followed the model Luke Wroblewski wrote about in Mobile First, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began. \nHow do we talk to one another? \nSteph Hay is often quick to point out that every interface is a conversation we’re having with out users. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest. \nIn their book Nicely Said, Kate Kiefer Lee and Nicole Fenton offer a ridiculous amount of great advice on not just how to write, but how to write for people. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing! \nI applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out. \nWhere are the patterns? \nOnce I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well. \n\nWhile working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example. \nFor more complicated interactions, I used Interface Experience Maps (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available. \n \nIn that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this. \nOnce the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, Stephanie Stimac, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series. \nWhat did we learn? \nThey may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture: \nMinimize distractions – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that; \nWrite like you speak — Write for people like you would speak to them in person and read your work aloud; \nSet content priorities — Ensure the flow is right and leads your users where they want (or need) to go; \nLook for opportunities to enhance — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and \nLook for patterns — Focus on creating a design system rather than individual pages. \n Where to next? \nWith a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned! \n― Aaron Gustafson, Web Standards Advocate"},"mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"http://windowsadmins.com/building-in-10k-content-and-strategy/","published":null,"wm-received":"2016-08-31T12:40:40Z","wm-id":372359,"wm-source":"http://windowsadmins.com/building-in-10k-content-and-strategy/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"http://readings.space/2016/08/30/building-in-10k-content-and-strategy/","published":null,"wm-received":"2016-08-31T16:05:27Z","wm-id":372406,"wm-source":"http://readings.space/2016/08/30/building-in-10k-content-and-strategy/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"August 30, 2016\r\n\t\t\tBuilding in 10k: Content and Strategy\r\n\t\t \r\n\t\t\r\n\t\t\t\t\t\r\n\t\t\t\tadmin\r\n\r\n\t\t\t\t Uncategorized\r\n\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t0 Comments\t\t\t\t\t\r\n\t\t\t\t\t\t\t \r\n\t\t\t\r\n\t\t\r\n\t\t\t\t\tEditor’s note: This is the first in a series of posts from the team that built the 10k Apart contest page, exploring the process of building for interoperability, accessibility, and progressive enhancement in less than 10kB. \nWhen Jeffrey Zeldman first approached me about bringing back the 10k Apart contest, my mind began to race. I’d been a judge on the 2011 incarnation of the contest, so I had seen it from that angle before, but he presented me with an amazing opportunity… not just bring it back, but to evolve it into the 10k Apart contest our industry so desperately needs today. \nThe 10k Apart (and the 5k before it) have never operated in a vacuum. They’ve always taken the pulse of industry trends and and then challenged us to do more, to do better, and with less. In 2010, the contest pushed us to embrace HTML5. In 2011, we were challenged to make our work responsive. And so I began to ask myself: what are the challenges we, as an industry, are struggling with today. Anyone who has followed my work could probably have guessed my answer: progressive enhancement. \nAnd so I put together a list of changes to the contest that would help us move away from the very JavaScript-heavy entries I’d judged last time toward entries that would be usable by anyone. Entries that respected low-bandwidth connections, were considerate of users who rely on assistive technology, and embraced the inherent flexibility and malleability of the Web. And, of course, entries that broke the stereotype of progressively-enhanced projects being “boring”. \nI was so excited when Jeffrey and Eric Meyer responded to my suggestions with overwhelming enthusiasm. Their encouragement challenged me to think about the rules I’d drafted to govern the contest and inspired me to make the contest site abide by those very same rules as a way of demonstrating how progressive enhancement can enable our web projects to do so much more. It’s not a yoke, holding us back; it’s a powerful philosophy that challenges us to look at experience as a continuum and pushes us to think outside of our comfortable high-tech bubble. \nThis is the first in a series of posts about the process of building the 2016 10k Apart contest site. I, and the wonderful team that helped me make it a realty, wanted to share what we did, but moreover why we did it. We’ll talk about the sacrifices we made in designing and building the site as well as the ways markup, style, and script took a simple transactional site and gave it the polish it so richly deserved. \nThanks for joining us on this journey… \nWhat are you here to do? \nBefore tucking into code, information architecture, or even copy, I took some time to stroll through previous incarnations of the contest. I poured over the structure of the sites and examined the tone of voice they used, of course, but I also took the time to examine the purpose of every page. Who were the different audiences coming to the sites? What did they want to do? Did their goals change over time? \nAsking all of these questions helped me to break the site’s audience into two main camps: Folks interested in entering the contest and folks interested in spectating. I also recognized that the motivations and goals of these groups would change as the contest progressed. Folks who entered might become spectators once they’d submitted their entry. Some spectators might be inspired to enter the contest themselves after seeing someone else’s project. And so I set about organizing the site to not only support these different, potentially overlapping audiences, but to make it easy to transition back and forth between them. \nTo accomplish this, the site would need to evolve with our audience through several phases: \nLaunch – When we don’t have any entries (which is what is live as I am writing this); \nIn progress – When we have entries that we want to show off, but the contest is still open (this phase will be kicking in soon); \nClose – When the contest is over and we aren’t accepting new entries, but instead focus on highlighting the folks that entered and ask you to vote for your favorites; and \nWinner announcement – When we celebrate the awesome works judged by our expert panel and you, the community, to be the best of the best. \n With that outline in place, I began putting together lists of the sorts of content we would need on each page in each phase of the contest. I was ruthless in stripping away the cruft and the nice to have bits. In many ways, I followed the model Luke Wroblewski wrote about in Mobile First, by focusing on the core purpose of each page. I got rid of any bit of content or UI was a distraction from that purpose or that simply failed to propel people toward accomplishing their goal on that page. Each page, each step in the process, was ruthlessly stripped to its essence. And then the real work began. \nHow do we talk to one another? \nSteph Hay is often quick to point out that every interface is a conversation we’re having with out users. With that in mind, I set about authoring copy that embodied that idea. I wanted your experience of the site to be just like sitting down next to me and talking about the contest. \nIn their book Nicely Said, Kate Kiefer Lee and Nicole Fenton offer a ridiculous amount of great advice on not just how to write, but how to write for people. In it, they talk about writing like you speak and even go so far as to recommend you read your work aloud. Looking to the future of “headless” UIs—Cortana, Alexa, Siri—and the current crop of screen reading options out there, it was pretty obvious that this was not only good advice… it was beta testing! \nI applied the wisdom I learned from these amazing content strategists (and no doubt countless others) to everything from the page titles and body copy all the way down to form labels and error messaging. I read the content aloud and in many cases had my computer read it to me as well. I know there’s room for improvement (there always is), but I’m pretty happy with the way it turned out. \nWhere are the patterns? \nOnce I had drafted copy for each page in the site, I began to organize the content into basic wireframes. In the interest of time, I focused on wireframes for large-screen views of each page, making note of the content priorities so I would know how to arrange things on smaller screens as well. \nExample wireframe from https://a-k-apart.com \nWhile working on the wireframes, I made notes (some mental, some in the wireframes themselves) about where we could shave off some page size or where certain content was helpful, but more of an enhancement than core to the purpose of the page. I also looked for places where we could use markup or style to improve the experience greatly for very little cost. HTML5 input types, for example. \nFor more complicated interactions, I used Interface Experience Maps (IxMaps, which are effectively flowcharts) to outline the different ways users might be able to experience an interface. For example, on an entry page the most important information is the name of the project, the name of the person (or people) who made it, the description of the project, and a link to view it live. A screenshot, while helpful, is unnecessary. So I used an IxMap to explore lazy loading the screenshots only when JavaScript was available. \nIxMap exploration of lazy loading screenshots when JavaScript is available. This IxMap depicts lazy loading a picture element and supplying alternate image formats for browsers that support WebP. \nIn that exploration, it dawned on me that I didn’t have to settle for only one image format—I could lazy load a picture element and supply alternate image formats for browsers that support WebP (which tends to be much smaller than JPGs and PNGs). That’s one of the reasons I love IxMaps: they allow for low-cost exploration and discoveries like this. \nOnce the wireframes and IxMaps were complete, I went through them and teased out repeating patterns and unique components, copying them to a new page in the wireframe document. In doing so, I also found opportunities to reuse enhancements I’d come up with for specific page components. Taken together, this pattern guide have our designer, Stephanie Stimac, a good overview of the site’s overall UI needs. It helped her avoid the trap of designing pages and let her create a design system instead. Stephanie will join me to talk about the design process in the third installment of this series. \nWhat did we learn? \nThey may seem obvious, but in case you’re a fan of takeaways, here are a few relating to content, strategy, and information architecture: \nMinimize distractions – Focus on the core purpose of every page, get rid of anything that is not supportive or detracts from that; \nWrite like you speak — Write for people like you would speak to them in person and read your work aloud; \nSet content priorities — Ensure the flow is right and leads your users where they want (or need) to go; \nLook for opportunities to enhance — Some supportive content may be nice to have but non-essential, consider removing it by default and bringing it back in certain scenarios; and \nLook for patterns — Focus on creating a design system rather than individual pages. \n Where to next? \nWith a good set of bones in place, the next step for me was to tuck into the markup, but that’s the story for another post. Stay tuned! \n― Aaron Gustafson, Web Standards Advocate \nSource: Windows \n\r\n\t\t\t\t\t\tFollow @satysmith","mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/","published":null,"wm-received":"2017-03-02T13:10:59Z","wm-id":426205,"wm-source":"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"When Your Code Has To Work: Complying With Legal Mandates\n\t \n\n\t\t\t\nBy Aaron Gustafson \n\t\t\t\tMarch 2nd, 2017 \n\t\t\t\tJavaScriptTechniques \n\t\t0 Comments  \n\t\t \n\t \n \n\n\tDouglas Crockford famously declared browsers to be “the most hostile software engineering environment imaginable,” and that wasn’t hyperbole. Ensuring that our websites work across a myriad of different devices, screen sizes and browsers our users depend on to access the web is a tall order, but it’s necessary. If our websites don’t enable users to accomplish the key tasks they come to do, we’ve failed them. \nWe should do everything in our power to ensure our websites function under even the harshest of scenarios, but at the same, we can’t expect our users to have the exact same experience in every browser, on every device. Yahoo realized this more than a decade ago and made it a central concept in its “Graded Browser Support1” strategy: \nSupport does not mean that everybody gets the same thing. Expecting two users using different browser software to have an identical experience fails to embrace or acknowledge the heterogeneous essence of the Web. In fact, requiring the same experience for all users creates an artificial barrier to participation. Availability and accessibility of content should be our key priority.  \nAnd that was a few years before the iPhone was introduced! \nProviding alternate experience pathways for our core functionality should be a no-brainer, but when it comes to implementing stuff we’d rather not think about, we often reach for the simplest drop-in solution, despite the potential negative impact it could have on our business. \nConsider the EU’s “cookie law.”2 If you’re unfamiliar, this somewhat contentious law3 is privacy legislation that requires websites to obtain consent from visitors before storing or retrieving information from their device. We call it the cookie law, but the legislation also applies to web storage4, IndexedDB5 and other client-side data storage and retrieval APIs. \nCompliance with this law is achieved by: \nnotifying users that the website requires the ability to store and read information on their device; \nproviding a link to the website’s privacy statement, which includes information about the storage mechanisms being used and what they are being used for; \nprompting users to confirm their acceptance of this requirement. \n If you operate a website aimed at folks living in the EU and fail to do this, you could be subject to a substantial fine. You could even open yourself up to a lawsuit. \nIf you’ve had to deal with the EU cookie law before, you’re probably keenly aware that a ton of “solutions” are available to provide compliance. Those quotation marks are fully intentional because nearly every one I found — including the one provided by the EU6 itself — has been a drop-in JavaScript file that enables compliance. If we’re talking about the letter of the law, however, they don’t actually. The problem is that, as awesome and comprehensive as some of these solutions are, we can never be guaranteed that our JavaScript programs will actually run7. In order to truly comply with the letter of the law, we should provide a fallback version of the utility — just in case. Most people will never see it, but at least we know we’re covered if something goes wrong. \nI stumbled into this morass while building the 10k Apart contest website8. We weren’t using cookies for much on the website — mainly analytics and vote-tracking — but we were using the Web Storage API9 to speed up the performance of the website and to save form data temporarily while folks were filling out the form. Because the contest was open to folks who live in the EU, we needed to abide by the cookie law. And because none of the solutions I found actually complied with the law in either spirit or reality — the notable exception being WordPress’ EU Cookie Law10 plugin, which works both with and without JavaScript, but the contest website wasn’t built in WordPress or even PHP, so I had to do something else — I opted to roll my own robust solution. \nPlanning It Out Link \nI’m a big fan of using interface experience (IX) maps11 to diagram functionality. I find their simple nature easy to understand and to tweak as I increase the fidelity of an experience. For this feature, I started with a (relatively) simple IX map that diagrammed what would happen when a user requests a page on the website. \nIX map for the basic interaction and fallback.12IX map for the basic interaction and fallback. (View large version13)  This IX map outlines several potential experiences that vary based on the user’s choice and feature availability. I’ll walk through the ideal scenario first: \nA user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything. \nThe server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance. \nThe browser renders the page with the banner. \nThe user clicks to accept the use of cookies and web storage. \nClient-side JavaScript sets the accepts cookie and closes the banner. \nOn subsequent page requests, the server reads the accepts cookie and does not inject the banner code. JavaScript sees the cookie and enables the cookie and web storage code. \n For the vast majority of users, this is the experience they’ll get, and that’s awesome. That said, however, we can never be 100% guaranteed our client-side JavaScript code will run, so we need a backup plan. Here’s the fallback experience: \nA user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything. \nThe server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance. \nThe browser renders the page with the banner. \nThe user clicks to accept the use of cookies and web storage. \nThe click initiates a form post to the server, which responds by setting the accepts cookie before redirecting the user back to the page they were on. \nOn subsequent page requests, the server reads the accepts cookie and does not inject the banner code. \nIf JavaScript becomes available later, it will see the cookie and enable its cookie and web storage code. \n Not bad. There’s an extra roundtrip to the server, but it’s a quick one, and, more importantly, it provides a foolproof fallback in the absence of our preferred JavaScript-driven option. True, it could fall victim to a networking issue, but there’s not much we can do to mitigate that without JavaScript in play. \nSpeaking of mitigating networking issues, the 10k Apart contest website uses a service worker14 to do some pretty aggressive caching; the service worker intercepts any page request and supplies a cached version if one exists. That could result in users getting a copy of the page with the banner still in it, even if they’ve already agreed to allow cookies. Time to update the IX map. \nIX map for the basic interaction and fallback.15The original IX map, adjusted to handle cached pages. (View large version16)  This is one of the reasons I like IX maps so much: They are really easy to generate and simple to update when you want to add features or handle more scenarios. With a few adjustments in place, I can account for the scenario in which a stale page includes the banner unnecessarily and have JavaScript remove it. \nWith this plan in place, it was time to implement it. \nServer-Side Implementation Link \n10k Apart’s back end is written in Node.js17 and uses Express18. I’m not going to get into the nitty-gritty of our installation and configuration, but I do want to talk about how I implemented this feature. First off, I opted to use Express’ cookie-parser19 middleware to let me get and set the cookie. \n// enable cookie-parser for Express\r\nvar cookieParser = require('cookie-parser');\r\napp.use(cookieParser()); \nOnce that was set up, I created my own custom Express middleware20 that would intercept requests and check for the approves_cookies cookie: \nvar checkCookie = function(req, res, next) {\r\n  res.locals.approves_cookies = ( req.cookies['approves_cookies'] === 'yes' );\r\n  res.locals.current_url = req.url || '/';\r\n  next();\r\n}; \nThis code establishes a middleware function named checkCookie(). All Express middleware gets access to the request (req), the response (res) and the next middleware function (next), so you’ll see those accounted for as the three arguments to that function. Then, within the function, I am modifying the response object to include two local variables (res.locals) to capture whether the cookie has already been set (res.locals.approves_cookies) and the currently requested URL (res.locals.current_url). Then, I call the next middleware function. \nWith that written, I can include this middleware in Express: \napp.use(checkCookie); \nAll of the templates for the website are Mustache21 files, and Express automatically pipes res.locals into those templates. Knowing that, I created a Mustache partial22 to handle the banner: \n{{^approves_cookies}}\r\n  <div id=\"cookie-banner\" role=\"alert\">\r\n    <form action=\"/cookies-ok\" method=\"post\">\r\n      <input type=\"hidden\" name=\"redirect_to\" value=\"{{current_url}}\">\r\n      <p>This site uses cookies for analytics and to track voting. If you're interested, more details can be found in <a href=\"{{privacy_url}}#maincookiessimilartechnologiesmodule\">our cookie policy</a>.</p>\r\n      <button type=\"submit\">I'm cool with that</button>\r\n    </form>\r\n  </div>\r\n{{/approves_cookies}} \nThis template uses an inverted section23 that only renders the div when approves_cookies is false. Within that markup, you can also see the current_url getting piped into a hidden input to indicate where a user should be redirected if the form method of setting the cookie is used. You remembered: the fallback. \nSpeaking of the fallback, since we have one, we also need to handle that on the server side. Here’s the Node.js code for that: \nvar affirmCookies = function (req, res) {\r\n  if ( ! req.cookies['approves_cookies'] )\r\n  {\r\n    res.cookie('approves_cookies', 'yes', {\r\n      secure: true,\r\n      maxAge: ( 365 * 24 * 60 * 60 ) // 1 year\r\n    });\r\n  }\r\n  res.redirect(req.body.redirect_to);\r\n};\r\napp.post('/cookies-ok', affirmCookies); \nThis ensures that if the form is submitted, Express will respond by setting the approves_cookies cookie (if it’s not already set) and then redirecting the user to the page they were on. Taken altogether, this gives us a solid baseline experience for every user. \nNow, it’s worth noting that none of this code is going to be useful to you if your projects don’t involve the specific stack I was working with on this project (Node.js, Express, Mustache). That said, the logic I’ve outlined here and in the IX map is portable to pretty much any language or framework you happen to know and love. \nOK, let’s switch gears and work some magic on the front end. \nFront-End Implementation Link \nWhen JavaScript is available and running properly, we’ll want to take full advantage of it, but it doesn’t make sense to run any code against the banner if it doesn’t exist, so first things first: I should check to see whether the banner is even in the page. \nvar $cookie_banner = document.getElementById('cookie-banner');\r\n\r\nif ( $cookie_banner )\r\n{\r\n  // actual code will go here\r\n} \nIn order to streamline the application logic, I’m going to add another conditional within to check for the accepts_cookies cookie. I know from my second pass on the IX map there’s an outside chance that the banner might be served up by my service worker even if the accepts cookie exists, so checking for the cookie early lets me run only the bit of JavaScript that removes the banner. But before I jump into all of that, I’ll create a function I can call in any of my code to let me know whether the user has agreed to let me cookie them: \nfunction cookiesApproved(){\r\n  return document.cookie.indexOf('approves_cookies') > -1;\r\n} \nI need this check in multiple places throughout my JavaScript, so it makes sense to break it out into a separate function. Now, let’s revisit my banner-handling logic: \nvar $cookie_banner = document.getElementById('cookie-banner');\r\n\r\nif ( $cookie_banner )\r\n{\r\n\r\n  // banner exists but cookie is set\r\n  if ( cookiesApproved() )\r\n  {\r\n    // hide the banner immediately!\r\n  }\r\n  // cookie has not been set \r\n  else\r\n  {\r\n    // add the logic to set the cookie\r\n    // and close the banner\r\n  }\r\n\r\n} \nSetting cookies in JavaScript is a little convoluted because you need to set it as a string, but it’s not too ghastly. I broke out the process into its own function so that I could set it as an event handler on the form: \nfunction approveCookies( e ) {\r\n\r\n  // prevent the form from submitting\r\n  e.preventDefault();\r\n\r\n  var cookie,               // placeholder for the cookie\r\n      expires = new Date(); // start building expiry date\r\n\r\n  // expire in one year\r\n  expires.setFullYear( expires.getFullYear() + 1 );\r\n\r\n  // build the cookie\r\n  cookie = [\r\n    'approves_cookies=yes',\r\n    'expires=' + expires.toUTCString(),\r\n    'domain=' + window.location.hostname,\r\n    window.location.protocol == 'https:' ? 'secure' : ''\r\n  ];\r\n\r\n  // set it\r\n  document.cookie = cookie.join('; ');\r\n\r\n  // close up the banner\r\n  closeCookieBanner();\r\n\r\n  // return\r\n  return false;\r\n\r\n};\r\n\r\n// find the form inside the banner\r\nvar $form = $cookie_banner.getElementsByTagName('form')[0];\r\n\r\n// hijack the submit event\r\n$form.addEventListener( 'submit', approveCookies, false ); \nThe comments in the code should make it pretty clear, but just in case, here’s what I’m doing: \nHijack the form submission event (e) and cancel its default action using e.preventDefault(). \nUse the Date object to construct a date one year out. \nAssemble the bits of the cookie, including the approves_cookies value, the expiry date, the domain the cookie is bound to, and whether the cookie should be secure (so I can test locally). \nSet document.cookie equal to the assembled cookie string. \nTrigger a separate method — closeCookieBanner() — to close the banner (which I will cover in a moment). \n With that in place, I can define closeCookieBanner() to handle, well, closing up the banner. There are actually two instances in which I need this functionality: after setting the cookie (as we just saw) and if the service worker serves up a stale page that still has the banner in it. Even though each requires roughly the same functionality, I want to make the stale-page cleanup version a little more aggressive. Here’s the code: \nfunction closeCookieBanner( immediate ) {\r\n\r\n  // How fast to close? Animation takes .5s\r\n  var close_speed = immediate ? 0 : 600;\r\n\r\n  // remove\r\n  window.setTimeout(function(){\r\n\r\n    $cookie_banner.parentNode.removeChild( $cookie_banner );\r\n\r\n    // remove the DOM reference\r\n    $cookie_banner = null;\r\n\r\n  }, close_speed);\r\n\r\n  // animate closed\r\n  if ( ! immediate ) {\r\n    $cookie_banner.className = 'closing';\r\n  }\r\n\r\n} \nThis function takes a single optional argument. If true (or anything “truthy”24) is passed in, the banner is immediately removed from the page (and its reference is deleted). If no argument is passed in, that doesn’t happen for 0.6 seconds, which is 0.1 seconds after the animation finishes up (we’ll get to the animation momentarily). The class change triggers that animation. \nYou already saw one instance of this function referenced in the previous code block. Here it is in the cached template branch of the conditional you saw earlier: \n…\r\n// banner exists but cookie is set\r\nif ( cookiesApproved() )\r\n{\r\n  // close immediately\r\n  closeCookieBanner( true );\r\n}\r\n… \nAdding Some Visual Sizzle Link \nBecause I brought up animations, I’ll discuss the CSS I’m using for the cookie banner component, too. Like most implementations of cookie notices, I opted for a visual full-width banner. On small screens, I wanted the banner to appear above the content and push it down the page. On larger screens I opted to affix it to the top of the viewport because it would not obstruct reading to nearly the same degree as it would on a small screen. Accomplishing this involved very little code: \n#cookie-banner {\r\n  background: #000;\r\n  color: #fff;\r\n  font-size: .875rem;\r\n  text-align: center;\r\n}\r\n\r\n@media (min-width: 60em) {\r\n  #cookie-banner {\r\n    position: fixed;\r\n    top: 0;\r\n    left: 0;\r\n    right: 0;\r\n    z-index: 1000;      \r\n  }\r\n} \nUsing the browser’s default styles, the cookie banner already displays block, so I didn’t really need to do much apart from set some basic text styles and colors. For the large screen (the “full-screen” version comes in at 60 ems), I affix it to the top of the screen using position: fixed, with a top offset of 0. Setting its left and right offsets to 0 ensures it will always take up the full width of the viewport. I also set the z-index quite high so it sits on top of everything else in the stack. \nHere’s the result: \n\nA video showing the browser viewport being enlarged from 240 to 960 pixels width. When the design hits the 60-em breakpoint, the banner becomes fixed-positioned.  Once the basic design was there, I took another pass to spice it up a bit. I decided to have the banner animate in and out using CSS. First things first: I created two animations. Initially, I tried to run a single animation in two directions for each state (opening and closing) but ran into problems triggering the reversal — you might be better at CSS animations than I am, so feel free to give it a shot. In the end, I also decided to tweak the two animations to be slightly different, so I’m fine with having two of them: \n@keyframes cookie-banner {\r\n  0% {\r\n    max-height: 0;\r\n  }\r\n  100% {\r\n    max-height: 20em;\r\n  }\r\n}\r\n@keyframes cookie-banner-reverse {\r\n  0% {\r\n    max-height: 20em;\r\n  }\r\n  100% {\r\n    max-height: 0;\r\n    display: none;\r\n  }\r\n} \nNot knowing how tall the banner would be (this is responsive design, after all), I needed it to animate to and from a height of auto. Thankfully, Nikita Vasilyev25 published a fantastic overview of how to transition values to and from auto26 a few years back. In short, animate max-height instead. The only thing to keep in mind is that the size of the non-zero max-height value you are transitioning to and from needs to be larger than your max, and it will also directly affect the speed of the animation. I found 20 ems to be more than adequate for this use case, but your project may require a different value. \nIt’s also worth noting that I used display: none at the conclusion of my cookie-banner-reverse animation (the closing one) to ensure the banner becomes unreachable to users of assistive technology such as screen readers. It’s probably unnecessary, but I did it as a failsafe just in case something happens and JavaScript doesn’t remove the banner from the DOM. \nWiring it up required only a few minor tweaks to the CSS: \n#cookie-banner {\r\n  …\r\n  box-sizing: border-box;\r\n  overflow: hidden;\r\n  animation: cookie-banner 1s 1s linear forwards;\r\n}\r\n\r\n#cookie-banner.closing {\r\n  animation: cookie-banner-reverse .5s linear forwards;\r\n} \nThis assigned the two animations to the two different banner states: The opening and resting state, cookie-banner, runs for one second after a one-second delay; the closing state, cookie-banner-reverse, runs for only half a second with no delay. I am using a class of closing, set via the JavaScript I showed earlier, to trigger the state change. Just for completeness, I’ll note that this code also stabilizes the dimensions of the banner with box-sizing: border-box and keeps the contents from spilling out of the banner using overflow: hidden. \nOne last bit of CSS tweaking and we’re done. On small screens, I’m leaving a margin between the cookie notice (#cookie-banner) and the page header (.banner). I want that to go away when the banner collapses, even if the cookie notice is not removed from the DOM. I can accomplish that with an adjacent-sibling selector: \n#cookie-banner + .banner {\r\n  transition: margin-top .5s;\r\n}\r\n\r\n#cookie-banner.closing + .banner {\r\n  margin-top: 0;\r\n} \nIt’s worth noting that I am setting the top margin on every element but the first one, using Heydon Pickering’s clever “lobotomized owl27” selector. So, the transition of margin-top on .banner will be from a specific value (in my case, 1.375 rem) to 0. With this code in place, the top margin will collapse over the same duration as the one used for the closing animation of the cookie banner and will be triggered by the very same class addition. \n\nA video showing the final implementation on a wide screen, both with and without JavaScript.  Simple, Robust, Resilient Link \nWhat I like about this approach is that it is fairly simple. It took only about an hour or two to research and implement, and it checks all of the compliance boxes with respect to the EU law. It has minimal dependencies, offers several fallback options, cleans up after itself and is a relatively back-end-agnostic pattern. \nWhen tasked with adding features we may not like — and, yes, I’d count a persistent nagging banner as one of those features — it’s often tempting to throw some code at it to get it done and over with. JavaScript is often a handy tool to accomplish that, especially because the logic can often be self-contained in an external script, configured and forgotten. But there’s a risk in that approach: JavaScript is never guaranteed28. If the feature is “nice to have,” you might be able to get away with it, but it’s probably not a good idea to play fast and loose with a legal mandate like this. Taking a few minutes to step back and explore how the feature can be implemented with minimal effort on all fronts will pay dividends down the road. Believe me29. \n(rb, vf, il, al) \nFootnotes Link 1 https://developer.yahoo.com/yui/articles/gbs/#define-support 2 http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT 3 https://webdevlaw.uk/category/eu-cookie-law/ 4 https://developer.mozilla.org/docs/Web/API/Web_Storage_API 5 https://developer.mozilla.org/docs/Web/API/IndexedDB_API 6 http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4 7 https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/ 8 https://a-k-apart.com/ 9 https://developer.mozilla.org/docs/Web/API/Web_Storage_API 10 https://wordpress.org/plugins/eu-cookie-law/ 11 https://www.aaron-gustafson.com/notebook/interface-experience-maps/ 12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png 13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png 14 https://developer.mozilla.org/docs/Web/API/Service_Worker_API 15 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png 16 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png 17 https://nodejs.org/ 18 http://expressjs.com/ 19 https://github.com/expressjs/cookie-parser 20 http://expressjs.com/en/guide/using-middleware.html 21 https://mustache.github.io/ 22 https://mustache.github.io/mustache.5.html#Partials 23 https://mustache.github.io/mustache.5.html#Inverted-Sections 24 https://developer.mozilla.org/docs/Glossary/Truthy 25 https://twitter.com/ELV1S 26 http://n12v.com/css-transition-to-from-auto/ 27 http://alistapart.com/article/axiomatic-css-and-lobotomized-owls 28 http://kryogenix.org/code/browser/everyonehasjs.html 29 https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979  JavaScriptTechniques \n\n\t↑ Back to top\n\n\tTweet itShare on Facebook","mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"http://webdesign.jitheshpr.com/when-your-code-has-to-work-complying-with-legal-mandates/","published":null,"wm-received":"2017-03-02T14:01:02Z","wm-id":426213,"wm-source":"http://webdesign.jitheshpr.com/when-your-code-has-to-work-complying-with-legal-mandates/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"https://newze.net/when-your-code-has-to-work-complying-with-legal-mandates/","published":null,"wm-received":"2017-03-02T15:13:11Z","wm-id":426219,"wm-source":"https://newze.net/when-your-code-has-to-work-complying-with-legal-mandates/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"When Your Code Has To Work: Complying With Legal Mandates","content":{"content-type":"text/html","value":"Original Post Credits: <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/\">LINK HERE</a>  <p>Douglas Crockford famously declared browsers to be “the most hostile software engineering environment imaginable,” and that wasn’t hyperbole. <strong>Ensuring that our websites work across a myriad of different devices</strong>, screen sizes and browsers our users depend on to access the web is a tall order, but it’s necessary. If our websites don’t enable users to accomplish the key tasks they come to do, we’ve failed them.</p><p>We should do everything in our power to ensure our websites function under even the harshest of scenarios, but at the same, we can’t expect our users to have the exact same experience in every browser, on every device. Yahoo realized this more than a decade ago and made it a central concept in its “<a href=\"https://github.com/yui/yui3/wiki/Graded-Browser-Support\">Graded Browser Support</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#1\">1</a>” strategy:</p><blockquote><p>Support does not mean that everybody gets the same thing. Expecting two users using different browser software to have an identical experience fails to embrace or acknowledge the heterogeneous essence of the Web. In fact, requiring the same experience for all users creates an artificial barrier to participation. Availability and accessibility of content should be our key priority.</p></blockquote><p>And that was a few years before the iPhone was introduced!</p><p>Providing alternate experience pathways for our core functionality should be a no-brainer, but when it comes to implementing stuff we’d rather not think about, we often reach for the simplest drop-in solution, despite the potential negative impact it could have on our business.</p><p>Consider the <a href=\"http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT\">EU’s “cookie law.”</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#2\">2</a> If you’re unfamiliar, this <a href=\"https://webdevlaw.uk/category/eu-cookie-law/\">somewhat contentious law</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#3\">3</a> is privacy legislation that requires websites to obtain consent from visitors before storing or retrieving information from their device. We call it the cookie law, but the legislation also applies to <a href=\"https://developer.mozilla.org/docs/Web/API/Web_Storage_API\">web storage</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#4\">4</a>, <a href=\"https://developer.mozilla.org/docs/Web/API/IndexedDB_API\">IndexedDB</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#5\">5</a> and other client-side data storage and retrieval APIs.</p><p>Compliance with this law is achieved by:</p><ol><li>notifying users that the website requires the ability to store and read information on their device;</li><li>providing a link to the website’s privacy statement, which includes information about the storage mechanisms being used and what they are being used for;</li><li>prompting users to confirm their acceptance of this requirement.</li></ol><p>If you operate a website aimed at folks living in the EU and fail to do this, you could be subject to a substantial fine. You could even open yourself up to a lawsuit.</p><p>If you’ve had to deal with the EU cookie law before, you’re probably keenly aware that a ton of “solutions” are available to provide compliance. Those quotation marks are fully intentional because nearly every one I found — including the <a href=\"http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4\">one provided by the EU</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#6\">6</a> itself — has been a drop-in JavaScript file that <em>enables</em> compliance. If we’re talking about the letter of the law, however, they don’t actually. The problem is that, as awesome and comprehensive as some of these solutions are, we can never be <a href=\"https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/\">guaranteed that our JavaScript programs will actually run</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#7\">7</a>. In order to truly comply with the letter of the law, we should provide a fallback version of the utility — just in case. Most people will never see it, but at least we know we’re covered if something goes wrong.</p><p>I stumbled into this morass while building the <a href=\"https://a-k-apart.com/\">10k Apart contest website</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#8\">8</a>. We weren’t using cookies for much on the website — mainly analytics and vote-tracking — but we were using the <a href=\"https://developer.mozilla.org/docs/Web/API/Web_Storage_API\">Web Storage API</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#9\">9</a> to speed up the performance of the website and to save form data temporarily while folks were filling out the form. Because the contest was open to folks who live in the EU, we needed to abide by the cookie law. And because none of the solutions I found actually complied with the law in either spirit or reality — the notable exception being WordPress’ <a href=\"https://wordpress.org/plugins/eu-cookie-law/\">EU Cookie Law</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#10\">10</a> plugin, which works both with and without JavaScript, but the contest website wasn’t built in WordPress or even PHP, so I had to do something else — I opted to roll my own robust solution.</p><h3>Planning It Out <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#planning-it-out\">Link</a></h3><p>I’m a big fan of using <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">interface experience (IX) maps</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#11\">11</a> to diagram functionality. I find their simple nature easy to understand and to tweak as I increase the fidelity of an experience. For this feature, I started with a (relatively) simple IX map that diagrammed what would happen when a user requests a page on the website.</p><p><a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png\"></a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#12\">12</a>IX map for the basic interaction and fallback. (<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png\">View large version</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#13\">13</a>)</p><p>This IX map outlines several potential experiences that vary based on the user’s choice and feature availability. I’ll walk through the ideal scenario first:</p><ol><li>A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything.</li><li>The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance.</li><li>The browser renders the page with the banner.</li><li>The user clicks to accept the use of cookies and web storage.</li><li>Client-side JavaScript sets the <code>accepts</code> cookie and closes the banner.</li><li>On subsequent page requests, the server reads the <code>accepts</code> cookie and does not inject the banner code. JavaScript sees the cookie and enables the cookie and web storage code.</li></ol><p>For the vast majority of users, this is the experience they’ll get, and that’s awesome. That said, however, we can never be 100% guaranteed our client-side JavaScript code will run, so we need a backup plan. Here’s the fallback experience:</p><ol><li>A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything.</li><li>The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance.</li><li>The browser renders the page with the banner.</li><li>The user clicks to accept the use of cookies and web storage.</li><li>The click initiates a form post to the server, which responds by setting the <code>accepts</code> cookie before redirecting the user back to the page they were on.</li><li>On subsequent page requests, the server reads the <code>accepts</code> cookie and does not inject the banner code.</li><li>If JavaScript becomes available later, it will see the cookie and enable its cookie and web storage code.</li></ol><p>Not bad. There’s an extra roundtrip to the server, but it’s a quick one, and, more importantly, it provides a foolproof fallback in the absence of our preferred JavaScript-driven option. True, it could fall victim to a networking issue, but there’s not much we can do to mitigate that without JavaScript in play.</p><p>Speaking of mitigating networking issues, the 10k Apart contest website uses a <a href=\"https://developer.mozilla.org/docs/Web/API/Service_Worker_API\">service worker</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#14\">14</a> to do some pretty aggressive caching; the service worker intercepts any page request and supplies a cached version if one exists. That <em>could</em> result in users getting a copy of the page with the banner still in it, even if they’ve already agreed to allow cookies. Time to update the IX map.</p><p><a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png\"><img alt=\"IX map for the basic interaction and fallback.\" height=\"461\" src=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png\" width=\"800\" /></a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#15\">15</a>The original IX map, adjusted to handle cached pages. (<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png\">View large version</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#16\">16</a>)</p><p>This is one of the reasons I like IX maps so much: They are really easy to generate and simple to update when you want to add features or handle more scenarios. With a few adjustments in place, I can account for the scenario in which a stale page includes the banner unnecessarily and have JavaScript remove it.</p><p>With this plan in place, it was time to implement it.</p><h3>Server-Side Implementation <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#server-side-implementation\">Link</a></h3><p>10k Apart’s back end is written in <a href=\"https://nodejs.org/\">Node.js</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#17\">17</a> and uses <a href=\"http://expressjs.com/\">Express</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#18\">18</a>. I’m not going to get into the nitty-gritty of our installation and configuration, but I do want to talk about how I implemented this feature. First off, I opted to use <a href=\"https://github.com/expressjs/cookie-parser\">Express’ cookie-parser</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#19\">19</a> middleware to let me get and set the cookie.</p><pre>\n<code>// enable cookie-parser for Express\nvar cookieParser = require('cookie-parser');\napp.use(cookieParser());</code>\n</pre><p>Once that was set up, I created my own <a href=\"http://expressjs.com/en/guide/using-middleware.html\">custom Express middleware</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#20\">20</a> that would intercept requests and check for the <code>approves_cookies</code> cookie:</p><pre>\n<code>var checkCookie = function(req, res, next) {\n  res.locals.approves_cookies = ( req.cookies['approves_cookies'] === 'yes' );\n  res.locals.current_url = req.url || '/';\n  next();\n};</code>\n</pre><p>This code establishes a middleware function named <code>checkCookie()</code>. All Express middleware gets access to the request (<code>req</code>), the response (<code>res</code>) and the next middleware function (<code>next</code>), so you’ll see those accounted for as the three arguments to that function. Then, within the function, I am modifying the response object to include two local variables (<code>res.locals</code>) to capture whether the cookie has already been set (<code>res.locals.approves_cookies</code>) and the currently requested URL (<code>res.locals.current_url</code>). Then, I call the next middleware function.</p><p>With that written, I can include this middleware in Express:</p><pre>\n<code>app.use(checkCookie);</code>\n</pre><p>All of the templates for the website are <a href=\"https://mustache.github.io/\">Mustache</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#21\">21</a> files, and Express automatically pipes <code>res.locals</code> into those templates. Knowing that, I created a <a href=\"https://mustache.github.io/mustache.5.html#Partials\">Mustache partial</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#22\">22</a> to handle the banner:</p><pre>\n<code>{{^approves_cookies}}\n  &lt;div id=\"cookie-banner\" role=\"alert\"&gt;\n    &lt;form action=\"/cookies-ok\" method=\"post\"&gt;\n      &lt;input type=\"hidden\" name=\"redirect_to\" value=\"{{current_url}}\"&gt;\n      &lt;p&gt;This site uses cookies for analytics and to track voting. If you're interested, more details can be found in &lt;a href=\"{{privacy_url}}#maincookiessimilartechnologiesmodule\"&gt;our cookie policy&lt;/a&gt;.&lt;/p&gt;\n      &lt;button type=\"submit\"&gt;I'm cool with that&lt;/button&gt;\n    &lt;/form&gt;\n  &lt;/div&gt;\n{{/approves_cookies}}</code>\n</pre><p>This template uses an <a href=\"https://mustache.github.io/mustache.5.html#Inverted-Sections\">inverted section</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#23\">23</a> that only renders the <code>div</code> when <code>approves_cookies</code> is false. Within that markup, you can also see the <code>current_url</code> getting piped into a hidden <code>input</code> to indicate where a user should be redirected if the form method of setting the cookie is used. You remembered: the fallback.</p><p>Speaking of the fallback, since we have one, we also need to handle that on the server side. Here’s the Node.js code for that:</p><pre>\n<code>var affirmCookies = function (req, res) {\n  if ( ! req.cookies['approves_cookies'] )\n  {\n    res.cookie('approves_cookies', 'yes', {\n      secure: true,\n      maxAge: ( 365 * 24 * 60 * 60 ) // 1 year\n    });\n  }\n  res.redirect(req.body.redirect_to);\n};\napp.post('/cookies-ok', affirmCookies);</code>\n</pre><p>This ensures that if the form is submitted, Express will respond by setting the <code>approves_cookies</code> cookie (if it’s not already set) and then redirecting the user to the page they were on. Taken altogether, this gives us a solid baseline experience for every user.</p><p>Now, it’s worth noting that none of this code is going to be useful to you if your projects don’t involve the specific stack I was working with on this project (Node.js, Express, Mustache). That said, the logic I’ve outlined here and in the IX map is portable to pretty much any language or framework you happen to know and love.</p><p>OK, let’s switch gears and work some magic on the front end.</p><h3>Front-End Implementation <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#front-end-implementation\">Link</a></h3><p>When JavaScript is available and running properly, we’ll want to take full advantage of it, but it doesn’t make sense to run any code against the banner if it doesn’t exist, so first things first: I should check to see whether the banner is even in the page.</p><pre>\n<code>var $cookie_banner = document.getElementById('cookie-banner');\n\nif ( $cookie_banner )\n{\n  // actual code will go here\n}</code>\n</pre><p>In order to streamline the application logic, I’m going to add another conditional within to check for the <code>accepts_cookies</code> cookie. I know from my second pass on the IX map there’s an outside chance that the banner might be served up by my service worker even if the <code>accepts</code> cookie exists, so checking for the cookie early lets me run only the bit of JavaScript that removes the banner. But before I jump into all of that, I’ll create a function I can call in any of my code to let me know whether the user has agreed to let me cookie them:</p><pre>\n<code>function cookiesApproved(){\n  return document.cookie.indexOf('approves_cookies') &gt; -1;\n}</code>\n</pre><p>I need this check in multiple places throughout my JavaScript, so it makes sense to break it out into a separate function. Now, let’s revisit my banner-handling logic:</p><pre>\n<code>var $cookie_banner = document.getElementById('cookie-banner');\n\nif ( $cookie_banner )\n{\n\n  // banner exists but cookie is set\n  if ( cookiesApproved() )\n  {\n    // hide the banner immediately!\n  }\n  // cookie has not been set \n  else\n  {\n    // add the logic to set the cookie\n    // and close the banner\n  }\n\n}</code>\n</pre><p>Setting cookies in JavaScript is a little convoluted because you need to set it as a string, but it’s not too ghastly. I broke out the process into its own function so that I could set it as an event handler on the form:</p><pre>\n<code>function approveCookies( e ) {\n\n  // prevent the form from submitting\n  e.preventDefault();\n\n  var cookie,               // placeholder for the cookie\n      expires = new Date(); // start building expiry date\n\n  // expire in one year\n  expires.setFullYear( expires.getFullYear() + 1 );\n\n  // build the cookie\n  cookie = [\n    'approves_cookies=yes',\n    'expires=' + expires.toUTCString(),\n    'domain=' + window.location.hostname,\n    window.location.protocol == 'https:' ? 'secure' : ''\n  ];\n\n  // set it\n  document.cookie = cookie.join('; ');\n\n  // close up the banner\n  closeCookieBanner();\n\n  // return\n  return false;\n\n};\n\n// find the form inside the banner\nvar $form = $cookie_banner.getElementsByTagName('form')[0];\n\n// hijack the submit event\n$form.addEventListener( 'submit', approveCookies, false );</code>\n</pre><p>The comments in the code should make it pretty clear, but just in case, here’s what I’m doing:</p><ol><li>Hijack the form submission event (<code>e</code>) and cancel its default action using <code>e.preventDefault()</code>.</li><li>Use the <code>Date</code> object to construct a date one year out.</li><li>Assemble the bits of the cookie, including the <code>approves_cookies</code> value, the expiry date, the domain the cookie is bound to, and whether the cookie should be secure (so I can test locally).</li><li>Set <code>document.cookie</code> equal to the assembled cookie string.</li><li>Trigger a separate method — <code>closeCookieBanner()</code> — to close the banner (which I will cover in a moment).</li></ol><p>With that in place, I can define <code>closeCookieBanner()</code> to handle, well, closing up the banner. There are actually two instances in which I need this functionality: after setting the cookie (as we just saw) and if the service worker serves up a stale page that still has the banner in it. Even though each requires roughly the same functionality, I want to make the stale-page cleanup version a little more aggressive. Here’s the code:</p><pre>\n<code>function closeCookieBanner( immediate ) {\n\n  // How fast to close? Animation takes .5s\n  var close_speed = immediate ? 0 : 600;\n\n  // remove\n  window.setTimeout(function(){\n\n    $cookie_banner.parentNode.removeChild( $cookie_banner );\n\n    // remove the DOM reference\n    $cookie_banner = null;\n\n  }, close_speed);\n\n  // animate closed\n  if ( ! immediate ) {\n    $cookie_banner.className = 'closing';\n  }\n\n}</code>\n</pre><p>This function takes a single optional argument. If <code>true</code> (or <a href=\"https://developer.mozilla.org/docs/Glossary/Truthy\">anything “truthy”</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#24\">24</a>) is passed in, the banner is immediately removed from the page (and its reference is deleted). If no argument is passed in, that doesn’t happen for 0.6 seconds, which is 0.1 seconds after the animation finishes up (we’ll get to the animation momentarily). The <code>class</code> change triggers that animation.</p><p>You already saw one instance of this function referenced in the previous code block. Here it is in the cached template branch of the conditional you saw earlier:</p><pre>\n<code>…\n// banner exists but cookie is set\nif ( cookiesApproved() )\n{\n  // close immediately\n  closeCookieBanner( true );\n}\n…</code>\n</pre><h3>Adding Some Visual Sizzle <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#adding-some-visual-sizzle\">Link</a></h3><p>Because I brought up animations, I’ll discuss the CSS I’m using for the cookie banner component, too. Like most implementations of cookie notices, I opted for a visual full-width banner. On small screens, I wanted the banner to appear above the content and push it down the page. On larger screens I opted to affix it to the top of the viewport because it would not obstruct reading to nearly the same degree as it would on a small screen. Accomplishing this involved very little code:</p><pre>\n<code>#cookie-banner {\n  background: #000;\n  color: #fff;\n  font-size: .875rem;\n  text-align: center;\n}\n\n@media (min-width: 60em) {\n  #cookie-banner {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    z-index: 1000;      \n  }\n}</code>\n</pre><p>Using the browser’s default styles, the cookie banner already displays <code>block</code>, so I didn’t really need to do much apart from set some basic text styles and colors. For the large screen (the “full-screen” version comes in at 60 ems), I affix it to the top of the screen using <code>position: fixed</code>, with a <code>top</code> offset of <code>0</code>. Setting its <code>left</code> and <code>right</code> offsets to <code>0</code> ensures it will always take up the full width of the viewport. I also set the <code>z-index</code> quite high so it sits on top of everything else in the stack.</p><p>Here’s the result:</p><p>[embedded content]A video showing the browser viewport being enlarged from 240 to 960 pixels width. When the design hits the 60-em breakpoint, the banner becomes fixed-positioned.</p><p>Once the basic design was there, I took another pass to spice it up a bit. I decided to have the banner animate in and out using CSS. First things first: I created two animations. Initially, I tried to run a single animation in two directions for each state (opening and closing) but ran into problems triggering the reversal — you might be better at CSS animations than I am, so feel free to give it a shot. In the end, I also decided to tweak the two animations to be slightly different, so I’m fine with having two of them:</p><pre>\n<code>@keyframes cookie-banner {\n  0% {\n    max-height: 0;\n  }\n  100% {\n    max-height: 20em;\n  }\n}\n@keyframes cookie-banner-reverse {\n  0% {\n    max-height: 20em;\n  }\n  100% {\n    max-height: 0;\n    display: none;\n  }\n}</code>\n</pre><p>Not knowing how tall the banner would be (this is responsive design, after all), I needed it to animate to and from a <code>height</code> of <code>auto</code>. Thankfully, <a href=\"https://twitter.com/ELV1S\">Nikita Vasilyev</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#25\">25</a> published a fantastic overview of how to <a href=\"http://n12v.com/css-transition-to-from-auto/\">transition values to and from <code>auto</code></a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#26\">26</a> a few years back. In short, animate <code>max-height</code> instead. The only thing to keep in mind is that the size of the non-zero <code>max-height</code> value you are transitioning to and from needs to be larger than your max, and it will also directly affect the speed of the animation. I found 20 ems to be more than adequate for this use case, but your project may require a different value.</p><p>It’s also worth noting that I used <code>display: none</code> at the conclusion of my <code>cookie-banner-reverse</code> animation (the closing one) to ensure the banner becomes unreachable to users of assistive technology such as screen readers. It’s probably unnecessary, but I did it as a failsafe just in case something happens and JavaScript doesn’t remove the banner from the DOM.</p><p>Wiring it up required only a few minor tweaks to the CSS:</p><pre>\n<code>#cookie-banner {\n  …\n  box-sizing: border-box;\n  overflow: hidden;\n  animation: cookie-banner 1s 1s linear forwards;\n}\n\n#cookie-banner.closing {\n  animation: cookie-banner-reverse .5s linear forwards;\n}</code>\n</pre><p>This assigned the two animations to the two different banner states: The opening and resting state, <code>cookie-banner</code>, runs for one second after a one-second delay; the closing state, <code>cookie-banner-reverse</code>, runs for only half a second with no delay. I am using a class of <code>closing</code>, set via the JavaScript I showed earlier, to trigger the state change. Just for completeness, I’ll note that this code also stabilizes the dimensions of the banner with <code>box-sizing: border-box</code> and keeps the contents from spilling out of the banner using <code>overflow: hidden</code>.</p><p>One last bit of CSS tweaking and we’re done. On small screens, I’m leaving a margin between the cookie notice (<code>#cookie-banner</code>) and the page header (<code>.banner</code>). I want that to go away when the banner collapses, even if the cookie notice is not removed from the DOM. I can accomplish that with an adjacent-sibling selector:</p><pre>\n<code>#cookie-banner + .banner {\n  transition: margin-top .5s;\n}\n\n#cookie-banner.closing + .banner {\n  margin-top: 0;\n}</code>\n</pre><p>It’s worth noting that I am setting the top margin on every element but the first one, using Heydon Pickering’s clever “<a href=\"http://alistapart.com/article/axiomatic-css-and-lobotomized-owls\">lobotomized owl</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#27\">27</a>” selector. So, the transition of <code>margin-top</code> on <code>.banner</code> will be from a specific value (in my case, <code>1.375 rem</code>) to <code>0</code>. With this code in place, the top margin will collapse over the same duration as the one used for the closing animation of the cookie banner and will be triggered by the very same class addition.</p><p>[embedded content]A video showing the final implementation on a wide screen, both with and without JavaScript.</p><h3>Simple, Robust, Resilient <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#simple-robust-resilient\">Link</a></h3><p>What I like about this approach is that it is fairly simple. It took only about an hour or two to research and implement, and it checks all of the compliance boxes with respect to the EU law. It has minimal dependencies, offers several fallback options, cleans up after itself and is a relatively back-end-agnostic pattern.</p><p>When tasked with adding features we may not like — and, yes, I’d count a persistent nagging banner as one of those features — it’s often tempting to throw some code at it to get it done and over with. <strong>JavaScript is often a handy tool</strong> to accomplish that, especially because the logic can often be self-contained in an external script, configured and forgotten. But there’s a risk in that approach: JavaScript is <a href=\"http://kryogenix.org/code/browser/everyonehasjs.html\">never guaranteed</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#28\">28</a>. If the feature is “nice to have,” you might be able to get away with it, but it’s probably not a good idea to play fast and loose with a legal mandate like this. Taking a few minutes to step back and explore how the feature can be implemented with minimal effort on all fronts will pay dividends down the road. <a href=\"https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979\">Believe me</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#29\">29</a>.</p><p><em>(rb, vf, il, al)</em></p><p><em>Front page image credit: <a href=\"https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/\">Pexels</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#30\">30</a>.</em></p><ol><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-1\">1 https://github.com/yui/yui3/wiki/Graded-Browser-Support</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-2\">2 http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-3\">3 https://webdevlaw.uk/category/eu-cookie-law/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-4\">4 https://developer.mozilla.org/docs/Web/API/Web_Storage_API</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-5\">5 https://developer.mozilla.org/docs/Web/API/IndexedDB_API</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-6\">6 http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-7\">7 https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-8\">8 https://a-k-apart.com/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-9\">9 https://developer.mozilla.org/docs/Web/API/Web_Storage_API</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-10\">10 https://wordpress.org/plugins/eu-cookie-law/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-11\">11 https://www.aaron-gustafson.com/notebook/interface-experience-maps/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-12\">12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-13\">13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-14\">14 https://developer.mozilla.org/docs/Web/API/Service_Worker_API</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-15\">15 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-16\">16 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-17\">17 https://nodejs.org/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-18\">18 http://expressjs.com/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-19\">19 https://github.com/expressjs/cookie-parser</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-20\">20 http://expressjs.com/en/guide/using-middleware.html</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-21\">21 https://mustache.github.io/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-22\">22 https://mustache.github.io/mustache.5.html#Partials</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-23\">23 https://mustache.github.io/mustache.5.html#Inverted-Sections</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-24\">24 https://developer.mozilla.org/docs/Glossary/Truthy</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-25\">25 https://twitter.com/ELV1S</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-26\">26 http://n12v.com/css-transition-to-from-auto/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-27\">27 http://alistapart.com/article/axiomatic-css-and-lobotomized-owls</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-28\">28 http://kryogenix.org/code/browser/everyonehasjs.html</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-29\">29 https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-30\">30 https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/</a></li></ol><p><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#top\">↑ Back to top</a> <a href=\"https://twitter.com/intent/tweet?original_referer=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/&amp;source=tweetbutton&amp;text=When%20Your%20Code%20Has%20To%20Work%3A%20Complying%20With%20Legal%20Mandates&amp;url=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/&amp;via=smashingmag\">Tweet it</a><a href=\"http://www.facebook.com/sharer/sharer.php?u=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/\">Share on Facebook</a></p><p>Collected and Posted by <a href=\"https://newze.net\"><strong>Newze</strong></a></p><p><strong>Original Post Link</strong>: https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/</p>","html":"Original Post Credits: <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/\">LINK HERE</a>  <p>Douglas Crockford famously declared browsers to be “the most hostile software engineering environment imaginable,” and that wasn’t hyperbole. <strong>Ensuring that our websites work across a myriad of different devices</strong>, screen sizes and browsers our users depend on to access the web is a tall order, but it’s necessary. If our websites don’t enable users to accomplish the key tasks they come to do, we’ve failed them.</p><p>We should do everything in our power to ensure our websites function under even the harshest of scenarios, but at the same, we can’t expect our users to have the exact same experience in every browser, on every device. Yahoo realized this more than a decade ago and made it a central concept in its “<a href=\"https://github.com/yui/yui3/wiki/Graded-Browser-Support\">Graded Browser Support</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#1\">1</a>” strategy:</p><blockquote><p>Support does not mean that everybody gets the same thing. Expecting two users using different browser software to have an identical experience fails to embrace or acknowledge the heterogeneous essence of the Web. In fact, requiring the same experience for all users creates an artificial barrier to participation. Availability and accessibility of content should be our key priority.</p></blockquote><p>And that was a few years before the iPhone was introduced!</p><p>Providing alternate experience pathways for our core functionality should be a no-brainer, but when it comes to implementing stuff we’d rather not think about, we often reach for the simplest drop-in solution, despite the potential negative impact it could have on our business.</p><p>Consider the <a href=\"http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT\">EU’s “cookie law.”</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#2\">2</a> If you’re unfamiliar, this <a href=\"https://webdevlaw.uk/category/eu-cookie-law/\">somewhat contentious law</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#3\">3</a> is privacy legislation that requires websites to obtain consent from visitors before storing or retrieving information from their device. We call it the cookie law, but the legislation also applies to <a href=\"https://developer.mozilla.org/docs/Web/API/Web_Storage_API\">web storage</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#4\">4</a>, <a href=\"https://developer.mozilla.org/docs/Web/API/IndexedDB_API\">IndexedDB</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#5\">5</a> and other client-side data storage and retrieval APIs.</p><p>Compliance with this law is achieved by:</p><ol><li>notifying users that the website requires the ability to store and read information on their device;</li><li>providing a link to the website’s privacy statement, which includes information about the storage mechanisms being used and what they are being used for;</li><li>prompting users to confirm their acceptance of this requirement.</li></ol><p>If you operate a website aimed at folks living in the EU and fail to do this, you could be subject to a substantial fine. You could even open yourself up to a lawsuit.</p><p>If you’ve had to deal with the EU cookie law before, you’re probably keenly aware that a ton of “solutions” are available to provide compliance. Those quotation marks are fully intentional because nearly every one I found — including the <a href=\"http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4\">one provided by the EU</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#6\">6</a> itself — has been a drop-in JavaScript file that <em>enables</em> compliance. If we’re talking about the letter of the law, however, they don’t actually. The problem is that, as awesome and comprehensive as some of these solutions are, we can never be <a href=\"https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/\">guaranteed that our JavaScript programs will actually run</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#7\">7</a>. In order to truly comply with the letter of the law, we should provide a fallback version of the utility — just in case. Most people will never see it, but at least we know we’re covered if something goes wrong.</p><p>I stumbled into this morass while building the <a href=\"https://a-k-apart.com/\">10k Apart contest website</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#8\">8</a>. We weren’t using cookies for much on the website — mainly analytics and vote-tracking — but we were using the <a href=\"https://developer.mozilla.org/docs/Web/API/Web_Storage_API\">Web Storage API</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#9\">9</a> to speed up the performance of the website and to save form data temporarily while folks were filling out the form. Because the contest was open to folks who live in the EU, we needed to abide by the cookie law. And because none of the solutions I found actually complied with the law in either spirit or reality — the notable exception being WordPress’ <a href=\"https://wordpress.org/plugins/eu-cookie-law/\">EU Cookie Law</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#10\">10</a> plugin, which works both with and without JavaScript, but the contest website wasn’t built in WordPress or even PHP, so I had to do something else — I opted to roll my own robust solution.</p><h3>Planning It Out <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#planning-it-out\">Link</a></h3><p>I’m a big fan of using <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">interface experience (IX) maps</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#11\">11</a> to diagram functionality. I find their simple nature easy to understand and to tweak as I increase the fidelity of an experience. For this feature, I started with a (relatively) simple IX map that diagrammed what would happen when a user requests a page on the website.</p><p><a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png\"></a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#12\">12</a>IX map for the basic interaction and fallback. (<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png\">View large version</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#13\">13</a>)</p><p>This IX map outlines several potential experiences that vary based on the user’s choice and feature availability. I’ll walk through the ideal scenario first:</p><ol><li>A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything.</li><li>The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance.</li><li>The browser renders the page with the banner.</li><li>The user clicks to accept the use of cookies and web storage.</li><li>Client-side JavaScript sets the <code>accepts</code> cookie and closes the banner.</li><li>On subsequent page requests, the server reads the <code>accepts</code> cookie and does not inject the banner code. JavaScript sees the cookie and enables the cookie and web storage code.</li></ol><p>For the vast majority of users, this is the experience they’ll get, and that’s awesome. That said, however, we can never be 100% guaranteed our client-side JavaScript code will run, so we need a backup plan. Here’s the fallback experience:</p><ol><li>A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything.</li><li>The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance.</li><li>The browser renders the page with the banner.</li><li>The user clicks to accept the use of cookies and web storage.</li><li>The click initiates a form post to the server, which responds by setting the <code>accepts</code> cookie before redirecting the user back to the page they were on.</li><li>On subsequent page requests, the server reads the <code>accepts</code> cookie and does not inject the banner code.</li><li>If JavaScript becomes available later, it will see the cookie and enable its cookie and web storage code.</li></ol><p>Not bad. There’s an extra roundtrip to the server, but it’s a quick one, and, more importantly, it provides a foolproof fallback in the absence of our preferred JavaScript-driven option. True, it could fall victim to a networking issue, but there’s not much we can do to mitigate that without JavaScript in play.</p><p>Speaking of mitigating networking issues, the 10k Apart contest website uses a <a href=\"https://developer.mozilla.org/docs/Web/API/Service_Worker_API\">service worker</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#14\">14</a> to do some pretty aggressive caching; the service worker intercepts any page request and supplies a cached version if one exists. That <em>could</em> result in users getting a copy of the page with the banner still in it, even if they’ve already agreed to allow cookies. Time to update the IX map.</p><p><a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png\"><img alt=\"IX map for the basic interaction and fallback.\" height=\"461\" src=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png\" width=\"800\" /></a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#15\">15</a>The original IX map, adjusted to handle cached pages. (<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png\">View large version</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#16\">16</a>)</p><p>This is one of the reasons I like IX maps so much: They are really easy to generate and simple to update when you want to add features or handle more scenarios. With a few adjustments in place, I can account for the scenario in which a stale page includes the banner unnecessarily and have JavaScript remove it.</p><p>With this plan in place, it was time to implement it.</p><h3>Server-Side Implementation <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#server-side-implementation\">Link</a></h3><p>10k Apart’s back end is written in <a href=\"https://nodejs.org/\">Node.js</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#17\">17</a> and uses <a href=\"http://expressjs.com/\">Express</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#18\">18</a>. I’m not going to get into the nitty-gritty of our installation and configuration, but I do want to talk about how I implemented this feature. First off, I opted to use <a href=\"https://github.com/expressjs/cookie-parser\">Express’ cookie-parser</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#19\">19</a> middleware to let me get and set the cookie.</p><pre>\n<code>// enable cookie-parser for Express\nvar cookieParser = require('cookie-parser');\napp.use(cookieParser());</code>\n</pre><p>Once that was set up, I created my own <a href=\"http://expressjs.com/en/guide/using-middleware.html\">custom Express middleware</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#20\">20</a> that would intercept requests and check for the <code>approves_cookies</code> cookie:</p><pre>\n<code>var checkCookie = function(req, res, next) {\n  res.locals.approves_cookies = ( req.cookies['approves_cookies'] === 'yes' );\n  res.locals.current_url = req.url || '/';\n  next();\n};</code>\n</pre><p>This code establishes a middleware function named <code>checkCookie()</code>. All Express middleware gets access to the request (<code>req</code>), the response (<code>res</code>) and the next middleware function (<code>next</code>), so you’ll see those accounted for as the three arguments to that function. Then, within the function, I am modifying the response object to include two local variables (<code>res.locals</code>) to capture whether the cookie has already been set (<code>res.locals.approves_cookies</code>) and the currently requested URL (<code>res.locals.current_url</code>). Then, I call the next middleware function.</p><p>With that written, I can include this middleware in Express:</p><pre>\n<code>app.use(checkCookie);</code>\n</pre><p>All of the templates for the website are <a href=\"https://mustache.github.io/\">Mustache</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#21\">21</a> files, and Express automatically pipes <code>res.locals</code> into those templates. Knowing that, I created a <a href=\"https://mustache.github.io/mustache.5.html#Partials\">Mustache partial</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#22\">22</a> to handle the banner:</p><pre>\n<code>{{^approves_cookies}}\n  &lt;div id=\"cookie-banner\" role=\"alert\"&gt;\n    &lt;form action=\"/cookies-ok\" method=\"post\"&gt;\n      &lt;input type=\"hidden\" name=\"redirect_to\" value=\"{{current_url}}\"&gt;\n      &lt;p&gt;This site uses cookies for analytics and to track voting. If you're interested, more details can be found in &lt;a href=\"{{privacy_url}}#maincookiessimilartechnologiesmodule\"&gt;our cookie policy&lt;/a&gt;.&lt;/p&gt;\n      &lt;button type=\"submit\"&gt;I'm cool with that&lt;/button&gt;\n    &lt;/form&gt;\n  &lt;/div&gt;\n{{/approves_cookies}}</code>\n</pre><p>This template uses an <a href=\"https://mustache.github.io/mustache.5.html#Inverted-Sections\">inverted section</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#23\">23</a> that only renders the <code>div</code> when <code>approves_cookies</code> is false. Within that markup, you can also see the <code>current_url</code> getting piped into a hidden <code>input</code> to indicate where a user should be redirected if the form method of setting the cookie is used. You remembered: the fallback.</p><p>Speaking of the fallback, since we have one, we also need to handle that on the server side. Here’s the Node.js code for that:</p><pre>\n<code>var affirmCookies = function (req, res) {\n  if ( ! req.cookies['approves_cookies'] )\n  {\n    res.cookie('approves_cookies', 'yes', {\n      secure: true,\n      maxAge: ( 365 * 24 * 60 * 60 ) // 1 year\n    });\n  }\n  res.redirect(req.body.redirect_to);\n};\napp.post('/cookies-ok', affirmCookies);</code>\n</pre><p>This ensures that if the form is submitted, Express will respond by setting the <code>approves_cookies</code> cookie (if it’s not already set) and then redirecting the user to the page they were on. Taken altogether, this gives us a solid baseline experience for every user.</p><p>Now, it’s worth noting that none of this code is going to be useful to you if your projects don’t involve the specific stack I was working with on this project (Node.js, Express, Mustache). That said, the logic I’ve outlined here and in the IX map is portable to pretty much any language or framework you happen to know and love.</p><p>OK, let’s switch gears and work some magic on the front end.</p><h3>Front-End Implementation <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#front-end-implementation\">Link</a></h3><p>When JavaScript is available and running properly, we’ll want to take full advantage of it, but it doesn’t make sense to run any code against the banner if it doesn’t exist, so first things first: I should check to see whether the banner is even in the page.</p><pre>\n<code>var $cookie_banner = document.getElementById('cookie-banner');\n\nif ( $cookie_banner )\n{\n  // actual code will go here\n}</code>\n</pre><p>In order to streamline the application logic, I’m going to add another conditional within to check for the <code>accepts_cookies</code> cookie. I know from my second pass on the IX map there’s an outside chance that the banner might be served up by my service worker even if the <code>accepts</code> cookie exists, so checking for the cookie early lets me run only the bit of JavaScript that removes the banner. But before I jump into all of that, I’ll create a function I can call in any of my code to let me know whether the user has agreed to let me cookie them:</p><pre>\n<code>function cookiesApproved(){\n  return document.cookie.indexOf('approves_cookies') &gt; -1;\n}</code>\n</pre><p>I need this check in multiple places throughout my JavaScript, so it makes sense to break it out into a separate function. Now, let’s revisit my banner-handling logic:</p><pre>\n<code>var $cookie_banner = document.getElementById('cookie-banner');\n\nif ( $cookie_banner )\n{\n\n  // banner exists but cookie is set\n  if ( cookiesApproved() )\n  {\n    // hide the banner immediately!\n  }\n  // cookie has not been set \n  else\n  {\n    // add the logic to set the cookie\n    // and close the banner\n  }\n\n}</code>\n</pre><p>Setting cookies in JavaScript is a little convoluted because you need to set it as a string, but it’s not too ghastly. I broke out the process into its own function so that I could set it as an event handler on the form:</p><pre>\n<code>function approveCookies( e ) {\n\n  // prevent the form from submitting\n  e.preventDefault();\n\n  var cookie,               // placeholder for the cookie\n      expires = new Date(); // start building expiry date\n\n  // expire in one year\n  expires.setFullYear( expires.getFullYear() + 1 );\n\n  // build the cookie\n  cookie = [\n    'approves_cookies=yes',\n    'expires=' + expires.toUTCString(),\n    'domain=' + window.location.hostname,\n    window.location.protocol == 'https:' ? 'secure' : ''\n  ];\n\n  // set it\n  document.cookie = cookie.join('; ');\n\n  // close up the banner\n  closeCookieBanner();\n\n  // return\n  return false;\n\n};\n\n// find the form inside the banner\nvar $form = $cookie_banner.getElementsByTagName('form')[0];\n\n// hijack the submit event\n$form.addEventListener( 'submit', approveCookies, false );</code>\n</pre><p>The comments in the code should make it pretty clear, but just in case, here’s what I’m doing:</p><ol><li>Hijack the form submission event (<code>e</code>) and cancel its default action using <code>e.preventDefault()</code>.</li><li>Use the <code>Date</code> object to construct a date one year out.</li><li>Assemble the bits of the cookie, including the <code>approves_cookies</code> value, the expiry date, the domain the cookie is bound to, and whether the cookie should be secure (so I can test locally).</li><li>Set <code>document.cookie</code> equal to the assembled cookie string.</li><li>Trigger a separate method — <code>closeCookieBanner()</code> — to close the banner (which I will cover in a moment).</li></ol><p>With that in place, I can define <code>closeCookieBanner()</code> to handle, well, closing up the banner. There are actually two instances in which I need this functionality: after setting the cookie (as we just saw) and if the service worker serves up a stale page that still has the banner in it. Even though each requires roughly the same functionality, I want to make the stale-page cleanup version a little more aggressive. Here’s the code:</p><pre>\n<code>function closeCookieBanner( immediate ) {\n\n  // How fast to close? Animation takes .5s\n  var close_speed = immediate ? 0 : 600;\n\n  // remove\n  window.setTimeout(function(){\n\n    $cookie_banner.parentNode.removeChild( $cookie_banner );\n\n    // remove the DOM reference\n    $cookie_banner = null;\n\n  }, close_speed);\n\n  // animate closed\n  if ( ! immediate ) {\n    $cookie_banner.className = 'closing';\n  }\n\n}</code>\n</pre><p>This function takes a single optional argument. If <code>true</code> (or <a href=\"https://developer.mozilla.org/docs/Glossary/Truthy\">anything “truthy”</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#24\">24</a>) is passed in, the banner is immediately removed from the page (and its reference is deleted). If no argument is passed in, that doesn’t happen for 0.6 seconds, which is 0.1 seconds after the animation finishes up (we’ll get to the animation momentarily). The <code>class</code> change triggers that animation.</p><p>You already saw one instance of this function referenced in the previous code block. Here it is in the cached template branch of the conditional you saw earlier:</p><pre>\n<code>…\n// banner exists but cookie is set\nif ( cookiesApproved() )\n{\n  // close immediately\n  closeCookieBanner( true );\n}\n…</code>\n</pre><h3>Adding Some Visual Sizzle <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#adding-some-visual-sizzle\">Link</a></h3><p>Because I brought up animations, I’ll discuss the CSS I’m using for the cookie banner component, too. Like most implementations of cookie notices, I opted for a visual full-width banner. On small screens, I wanted the banner to appear above the content and push it down the page. On larger screens I opted to affix it to the top of the viewport because it would not obstruct reading to nearly the same degree as it would on a small screen. Accomplishing this involved very little code:</p><pre>\n<code>#cookie-banner {\n  background: #000;\n  color: #fff;\n  font-size: .875rem;\n  text-align: center;\n}\n\n@media (min-width: 60em) {\n  #cookie-banner {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    z-index: 1000;      \n  }\n}</code>\n</pre><p>Using the browser’s default styles, the cookie banner already displays <code>block</code>, so I didn’t really need to do much apart from set some basic text styles and colors. For the large screen (the “full-screen” version comes in at 60 ems), I affix it to the top of the screen using <code>position: fixed</code>, with a <code>top</code> offset of <code>0</code>. Setting its <code>left</code> and <code>right</code> offsets to <code>0</code> ensures it will always take up the full width of the viewport. I also set the <code>z-index</code> quite high so it sits on top of everything else in the stack.</p><p>Here’s the result:</p><p>[embedded content]A video showing the browser viewport being enlarged from 240 to 960 pixels width. When the design hits the 60-em breakpoint, the banner becomes fixed-positioned.</p><p>Once the basic design was there, I took another pass to spice it up a bit. I decided to have the banner animate in and out using CSS. First things first: I created two animations. Initially, I tried to run a single animation in two directions for each state (opening and closing) but ran into problems triggering the reversal — you might be better at CSS animations than I am, so feel free to give it a shot. In the end, I also decided to tweak the two animations to be slightly different, so I’m fine with having two of them:</p><pre>\n<code>@keyframes cookie-banner {\n  0% {\n    max-height: 0;\n  }\n  100% {\n    max-height: 20em;\n  }\n}\n@keyframes cookie-banner-reverse {\n  0% {\n    max-height: 20em;\n  }\n  100% {\n    max-height: 0;\n    display: none;\n  }\n}</code>\n</pre><p>Not knowing how tall the banner would be (this is responsive design, after all), I needed it to animate to and from a <code>height</code> of <code>auto</code>. Thankfully, <a href=\"https://twitter.com/ELV1S\">Nikita Vasilyev</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#25\">25</a> published a fantastic overview of how to <a href=\"http://n12v.com/css-transition-to-from-auto/\">transition values to and from <code>auto</code></a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#26\">26</a> a few years back. In short, animate <code>max-height</code> instead. The only thing to keep in mind is that the size of the non-zero <code>max-height</code> value you are transitioning to and from needs to be larger than your max, and it will also directly affect the speed of the animation. I found 20 ems to be more than adequate for this use case, but your project may require a different value.</p><p>It’s also worth noting that I used <code>display: none</code> at the conclusion of my <code>cookie-banner-reverse</code> animation (the closing one) to ensure the banner becomes unreachable to users of assistive technology such as screen readers. It’s probably unnecessary, but I did it as a failsafe just in case something happens and JavaScript doesn’t remove the banner from the DOM.</p><p>Wiring it up required only a few minor tweaks to the CSS:</p><pre>\n<code>#cookie-banner {\n  …\n  box-sizing: border-box;\n  overflow: hidden;\n  animation: cookie-banner 1s 1s linear forwards;\n}\n\n#cookie-banner.closing {\n  animation: cookie-banner-reverse .5s linear forwards;\n}</code>\n</pre><p>This assigned the two animations to the two different banner states: The opening and resting state, <code>cookie-banner</code>, runs for one second after a one-second delay; the closing state, <code>cookie-banner-reverse</code>, runs for only half a second with no delay. I am using a class of <code>closing</code>, set via the JavaScript I showed earlier, to trigger the state change. Just for completeness, I’ll note that this code also stabilizes the dimensions of the banner with <code>box-sizing: border-box</code> and keeps the contents from spilling out of the banner using <code>overflow: hidden</code>.</p><p>One last bit of CSS tweaking and we’re done. On small screens, I’m leaving a margin between the cookie notice (<code>#cookie-banner</code>) and the page header (<code>.banner</code>). I want that to go away when the banner collapses, even if the cookie notice is not removed from the DOM. I can accomplish that with an adjacent-sibling selector:</p><pre>\n<code>#cookie-banner + .banner {\n  transition: margin-top .5s;\n}\n\n#cookie-banner.closing + .banner {\n  margin-top: 0;\n}</code>\n</pre><p>It’s worth noting that I am setting the top margin on every element but the first one, using Heydon Pickering’s clever “<a href=\"http://alistapart.com/article/axiomatic-css-and-lobotomized-owls\">lobotomized owl</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#27\">27</a>” selector. So, the transition of <code>margin-top</code> on <code>.banner</code> will be from a specific value (in my case, <code>1.375 rem</code>) to <code>0</code>. With this code in place, the top margin will collapse over the same duration as the one used for the closing animation of the cookie banner and will be triggered by the very same class addition.</p><p>[embedded content]A video showing the final implementation on a wide screen, both with and without JavaScript.</p><h3>Simple, Robust, Resilient <a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#simple-robust-resilient\">Link</a></h3><p>What I like about this approach is that it is fairly simple. It took only about an hour or two to research and implement, and it checks all of the compliance boxes with respect to the EU law. It has minimal dependencies, offers several fallback options, cleans up after itself and is a relatively back-end-agnostic pattern.</p><p>When tasked with adding features we may not like — and, yes, I’d count a persistent nagging banner as one of those features — it’s often tempting to throw some code at it to get it done and over with. <strong>JavaScript is often a handy tool</strong> to accomplish that, especially because the logic can often be self-contained in an external script, configured and forgotten. But there’s a risk in that approach: JavaScript is <a href=\"http://kryogenix.org/code/browser/everyonehasjs.html\">never guaranteed</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#28\">28</a>. If the feature is “nice to have,” you might be able to get away with it, but it’s probably not a good idea to play fast and loose with a legal mandate like this. Taking a few minutes to step back and explore how the feature can be implemented with minimal effort on all fronts will pay dividends down the road. <a href=\"https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979\">Believe me</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#29\">29</a>.</p><p><em>(rb, vf, il, al)</em></p><p><em>Front page image credit: <a href=\"https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/\">Pexels</a><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#30\">30</a>.</em></p><ol><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-1\">1 https://github.com/yui/yui3/wiki/Graded-Browser-Support</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-2\">2 http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-3\">3 https://webdevlaw.uk/category/eu-cookie-law/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-4\">4 https://developer.mozilla.org/docs/Web/API/Web_Storage_API</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-5\">5 https://developer.mozilla.org/docs/Web/API/IndexedDB_API</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-6\">6 http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-7\">7 https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-8\">8 https://a-k-apart.com/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-9\">9 https://developer.mozilla.org/docs/Web/API/Web_Storage_API</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-10\">10 https://wordpress.org/plugins/eu-cookie-law/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-11\">11 https://www.aaron-gustafson.com/notebook/interface-experience-maps/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-12\">12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-13\">13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-14\">14 https://developer.mozilla.org/docs/Web/API/Service_Worker_API</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-15\">15 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-16\">16 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-17\">17 https://nodejs.org/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-18\">18 http://expressjs.com/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-19\">19 https://github.com/expressjs/cookie-parser</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-20\">20 http://expressjs.com/en/guide/using-middleware.html</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-21\">21 https://mustache.github.io/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-22\">22 https://mustache.github.io/mustache.5.html#Partials</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-23\">23 https://mustache.github.io/mustache.5.html#Inverted-Sections</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-24\">24 https://developer.mozilla.org/docs/Glossary/Truthy</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-25\">25 https://twitter.com/ELV1S</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-26\">26 http://n12v.com/css-transition-to-from-auto/</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-27\">27 http://alistapart.com/article/axiomatic-css-and-lobotomized-owls</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-28\">28 http://kryogenix.org/code/browser/everyonehasjs.html</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-29\">29 https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979</a></li><li><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#note-30\">30 https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/</a></li></ol><p><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/#top\">↑ Back to top</a> <a href=\"https://twitter.com/intent/tweet?original_referer=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/&amp;source=tweetbutton&amp;text=When%20Your%20Code%20Has%20To%20Work%3A%20Complying%20With%20Legal%20Mandates&amp;url=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/&amp;via=smashingmag\">Tweet it</a><a href=\"http://www.facebook.com/sharer/sharer.php?u=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/\">Share on Facebook</a></p><p>Collected and Posted by <a href=\"https://newze.net\"><strong>Newze</strong></a></p><p><strong>Original Post Link</strong>: https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/</p>","text":"Original Post Credits: LINK HERE   Douglas Crockford famously declared browsers to be “the most hostile software engineering environment imaginable,” and that wasn’t hyperbole. Ensuring that our websites work across a myriad of different devices, screen sizes and browsers our users depend on to access the web is a tall order, but it’s necessary. If our websites don’t enable users to accomplish the key tasks they come to do, we’ve failed them. We should do everything in our power to ensure our websites function under even the harshest of scenarios, but at the same, we can’t expect our users to have the exact same experience in every browser, on every device. Yahoo realized this more than a decade ago and made it a central concept in its “Graded Browser Support1” strategy: Support does not mean that everybody gets the same thing. Expecting two users using different browser software to have an identical experience fails to embrace or acknowledge the heterogeneous essence of the Web. In fact, requiring the same experience for all users creates an artificial barrier to participation. Availability and accessibility of content should be our key priority.  And that was a few years before the iPhone was introduced! Providing alternate experience pathways for our core functionality should be a no-brainer, but when it comes to implementing stuff we’d rather not think about, we often reach for the simplest drop-in solution, despite the potential negative impact it could have on our business. Consider the EU’s “cookie law.”2 If you’re unfamiliar, this somewhat contentious law3 is privacy legislation that requires websites to obtain consent from visitors before storing or retrieving information from their device. We call it the cookie law, but the legislation also applies to web storage4, IndexedDB5 and other client-side data storage and retrieval APIs. Compliance with this law is achieved by: notifying users that the website requires the ability to store and read information on their device; providing a link to the website’s privacy statement, which includes information about the storage mechanisms being used and what they are being used for; prompting users to confirm their acceptance of this requirement.  If you operate a website aimed at folks living in the EU and fail to do this, you could be subject to a substantial fine. You could even open yourself up to a lawsuit. If you’ve had to deal with the EU cookie law before, you’re probably keenly aware that a ton of “solutions” are available to provide compliance. Those quotation marks are fully intentional because nearly every one I found — including the one provided by the EU6 itself — has been a drop-in JavaScript file that enables compliance. If we’re talking about the letter of the law, however, they don’t actually. The problem is that, as awesome and comprehensive as some of these solutions are, we can never be guaranteed that our JavaScript programs will actually run7. In order to truly comply with the letter of the law, we should provide a fallback version of the utility — just in case. Most people will never see it, but at least we know we’re covered if something goes wrong. I stumbled into this morass while building the 10k Apart contest website8. We weren’t using cookies for much on the website — mainly analytics and vote-tracking — but we were using the Web Storage API9 to speed up the performance of the website and to save form data temporarily while folks were filling out the form. Because the contest was open to folks who live in the EU, we needed to abide by the cookie law. And because none of the solutions I found actually complied with the law in either spirit or reality — the notable exception being WordPress’ EU Cookie Law10 plugin, which works both with and without JavaScript, but the contest website wasn’t built in WordPress or even PHP, so I had to do something else — I opted to roll my own robust solution. Planning It Out Link I’m a big fan of using interface experience (IX) maps11 to diagram functionality. I find their simple nature easy to understand and to tweak as I increase the fidelity of an experience. For this feature, I started with a (relatively) simple IX map that diagrammed what would happen when a user requests a page on the website. 12IX map for the basic interaction and fallback. (View large version13) This IX map outlines several potential experiences that vary based on the user’s choice and feature availability. I’ll walk through the ideal scenario first: A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything. The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance. The browser renders the page with the banner. The user clicks to accept the use of cookies and web storage. Client-side JavaScript sets the accepts cookie and closes the banner. On subsequent page requests, the server reads the accepts cookie and does not inject the banner code. JavaScript sees the cookie and enables the cookie and web storage code.  For the vast majority of users, this is the experience they’ll get, and that’s awesome. That said, however, we can never be 100% guaranteed our client-side JavaScript code will run, so we need a backup plan. Here’s the fallback experience: A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything. The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance. The browser renders the page with the banner. The user clicks to accept the use of cookies and web storage. The click initiates a form post to the server, which responds by setting the accepts cookie before redirecting the user back to the page they were on. On subsequent page requests, the server reads the accepts cookie and does not inject the banner code. If JavaScript becomes available later, it will see the cookie and enable its cookie and web storage code.  Not bad. There’s an extra roundtrip to the server, but it’s a quick one, and, more importantly, it provides a foolproof fallback in the absence of our preferred JavaScript-driven option. True, it could fall victim to a networking issue, but there’s not much we can do to mitigate that without JavaScript in play. Speaking of mitigating networking issues, the 10k Apart contest website uses a service worker14 to do some pretty aggressive caching; the service worker intercepts any page request and supplies a cached version if one exists. That could result in users getting a copy of the page with the banner still in it, even if they’ve already agreed to allow cookies. Time to update the IX map. IX map for the basic interaction and fallback.15The original IX map, adjusted to handle cached pages. (View large version16) This is one of the reasons I like IX maps so much: They are really easy to generate and simple to update when you want to add features or handle more scenarios. With a few adjustments in place, I can account for the scenario in which a stale page includes the banner unnecessarily and have JavaScript remove it. With this plan in place, it was time to implement it. Server-Side Implementation Link 10k Apart’s back end is written in Node.js17 and uses Express18. I’m not going to get into the nitty-gritty of our installation and configuration, but I do want to talk about how I implemented this feature. First off, I opted to use Express’ cookie-parser19 middleware to let me get and set the cookie. \n// enable cookie-parser for Express\nvar cookieParser = require('cookie-parser');\napp.use(cookieParser());\n Once that was set up, I created my own custom Express middleware20 that would intercept requests and check for the approves_cookies cookie: \nvar checkCookie = function(req, res, next) {\n  res.locals.approves_cookies = ( req.cookies['approves_cookies'] === 'yes' );\n  res.locals.current_url = req.url || '/';\n  next();\n};\n This code establishes a middleware function named checkCookie(). All Express middleware gets access to the request (req), the response (res) and the next middleware function (next), so you’ll see those accounted for as the three arguments to that function. Then, within the function, I am modifying the response object to include two local variables (res.locals) to capture whether the cookie has already been set (res.locals.approves_cookies) and the currently requested URL (res.locals.current_url). Then, I call the next middleware function. With that written, I can include this middleware in Express: \napp.use(checkCookie);\n All of the templates for the website are Mustache21 files, and Express automatically pipes res.locals into those templates. Knowing that, I created a Mustache partial22 to handle the banner: \n{{^approves_cookies}}\n  <div id=\"cookie-banner\" role=\"alert\">\n    <form action=\"/cookies-ok\" method=\"post\">\n      <input type=\"hidden\" name=\"redirect_to\" value=\"{{current_url}}\">\n      <p>This site uses cookies for analytics and to track voting. If you're interested, more details can be found in <a href=\"{{privacy_url}}#maincookiessimilartechnologiesmodule\">our cookie policy</a>.</p>\n      <button type=\"submit\">I'm cool with that</button>\n    </form>\n  </div>\n{{/approves_cookies}}\n This template uses an inverted section23 that only renders the div when approves_cookies is false. Within that markup, you can also see the current_url getting piped into a hidden input to indicate where a user should be redirected if the form method of setting the cookie is used. You remembered: the fallback. Speaking of the fallback, since we have one, we also need to handle that on the server side. Here’s the Node.js code for that: \nvar affirmCookies = function (req, res) {\n  if ( ! req.cookies['approves_cookies'] )\n  {\n    res.cookie('approves_cookies', 'yes', {\n      secure: true,\n      maxAge: ( 365 * 24 * 60 * 60 ) // 1 year\n    });\n  }\n  res.redirect(req.body.redirect_to);\n};\napp.post('/cookies-ok', affirmCookies);\n This ensures that if the form is submitted, Express will respond by setting the approves_cookies cookie (if it’s not already set) and then redirecting the user to the page they were on. Taken altogether, this gives us a solid baseline experience for every user. Now, it’s worth noting that none of this code is going to be useful to you if your projects don’t involve the specific stack I was working with on this project (Node.js, Express, Mustache). That said, the logic I’ve outlined here and in the IX map is portable to pretty much any language or framework you happen to know and love. OK, let’s switch gears and work some magic on the front end. Front-End Implementation Link When JavaScript is available and running properly, we’ll want to take full advantage of it, but it doesn’t make sense to run any code against the banner if it doesn’t exist, so first things first: I should check to see whether the banner is even in the page. \nvar $cookie_banner = document.getElementById('cookie-banner');\n\nif ( $cookie_banner )\n{\n  // actual code will go here\n}\n In order to streamline the application logic, I’m going to add another conditional within to check for the accepts_cookies cookie. I know from my second pass on the IX map there’s an outside chance that the banner might be served up by my service worker even if the accepts cookie exists, so checking for the cookie early lets me run only the bit of JavaScript that removes the banner. But before I jump into all of that, I’ll create a function I can call in any of my code to let me know whether the user has agreed to let me cookie them: \nfunction cookiesApproved(){\n  return document.cookie.indexOf('approves_cookies') > -1;\n}\n I need this check in multiple places throughout my JavaScript, so it makes sense to break it out into a separate function. Now, let’s revisit my banner-handling logic: \nvar $cookie_banner = document.getElementById('cookie-banner');\n\nif ( $cookie_banner )\n{\n\n  // banner exists but cookie is set\n  if ( cookiesApproved() )\n  {\n    // hide the banner immediately!\n  }\n  // cookie has not been set \n  else\n  {\n    // add the logic to set the cookie\n    // and close the banner\n  }\n\n}\n Setting cookies in JavaScript is a little convoluted because you need to set it as a string, but it’s not too ghastly. I broke out the process into its own function so that I could set it as an event handler on the form: \nfunction approveCookies( e ) {\n\n  // prevent the form from submitting\n  e.preventDefault();\n\n  var cookie,               // placeholder for the cookie\n      expires = new Date(); // start building expiry date\n\n  // expire in one year\n  expires.setFullYear( expires.getFullYear() + 1 );\n\n  // build the cookie\n  cookie = [\n    'approves_cookies=yes',\n    'expires=' + expires.toUTCString(),\n    'domain=' + window.location.hostname,\n    window.location.protocol == 'https:' ? 'secure' : ''\n  ];\n\n  // set it\n  document.cookie = cookie.join('; ');\n\n  // close up the banner\n  closeCookieBanner();\n\n  // return\n  return false;\n\n};\n\n// find the form inside the banner\nvar $form = $cookie_banner.getElementsByTagName('form')[0];\n\n// hijack the submit event\n$form.addEventListener( 'submit', approveCookies, false );\n The comments in the code should make it pretty clear, but just in case, here’s what I’m doing: Hijack the form submission event (e) and cancel its default action using e.preventDefault(). Use the Date object to construct a date one year out. Assemble the bits of the cookie, including the approves_cookies value, the expiry date, the domain the cookie is bound to, and whether the cookie should be secure (so I can test locally). Set document.cookie equal to the assembled cookie string. Trigger a separate method — closeCookieBanner() — to close the banner (which I will cover in a moment).  With that in place, I can define closeCookieBanner() to handle, well, closing up the banner. There are actually two instances in which I need this functionality: after setting the cookie (as we just saw) and if the service worker serves up a stale page that still has the banner in it. Even though each requires roughly the same functionality, I want to make the stale-page cleanup version a little more aggressive. Here’s the code: \nfunction closeCookieBanner( immediate ) {\n\n  // How fast to close? Animation takes .5s\n  var close_speed = immediate ? 0 : 600;\n\n  // remove\n  window.setTimeout(function(){\n\n    $cookie_banner.parentNode.removeChild( $cookie_banner );\n\n    // remove the DOM reference\n    $cookie_banner = null;\n\n  }, close_speed);\n\n  // animate closed\n  if ( ! immediate ) {\n    $cookie_banner.className = 'closing';\n  }\n\n}\n This function takes a single optional argument. If true (or anything “truthy”24) is passed in, the banner is immediately removed from the page (and its reference is deleted). If no argument is passed in, that doesn’t happen for 0.6 seconds, which is 0.1 seconds after the animation finishes up (we’ll get to the animation momentarily). The class change triggers that animation. You already saw one instance of this function referenced in the previous code block. Here it is in the cached template branch of the conditional you saw earlier: \n…\n// banner exists but cookie is set\nif ( cookiesApproved() )\n{\n  // close immediately\n  closeCookieBanner( true );\n}\n…\n Adding Some Visual Sizzle Link Because I brought up animations, I’ll discuss the CSS I’m using for the cookie banner component, too. Like most implementations of cookie notices, I opted for a visual full-width banner. On small screens, I wanted the banner to appear above the content and push it down the page. On larger screens I opted to affix it to the top of the viewport because it would not obstruct reading to nearly the same degree as it would on a small screen. Accomplishing this involved very little code: \n#cookie-banner {\n  background: #000;\n  color: #fff;\n  font-size: .875rem;\n  text-align: center;\n}\n\n@media (min-width: 60em) {\n  #cookie-banner {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    z-index: 1000;      \n  }\n}\n Using the browser’s default styles, the cookie banner already displays block, so I didn’t really need to do much apart from set some basic text styles and colors. For the large screen (the “full-screen” version comes in at 60 ems), I affix it to the top of the screen using position: fixed, with a top offset of 0. Setting its left and right offsets to 0 ensures it will always take up the full width of the viewport. I also set the z-index quite high so it sits on top of everything else in the stack. Here’s the result: [embedded content]\nA video showing the browser viewport being enlarged from 240 to 960 pixels width. When the design hits the 60-em breakpoint, the banner becomes fixed-positioned. Once the basic design was there, I took another pass to spice it up a bit. I decided to have the banner animate in and out using CSS. First things first: I created two animations. Initially, I tried to run a single animation in two directions for each state (opening and closing) but ran into problems triggering the reversal — you might be better at CSS animations than I am, so feel free to give it a shot. In the end, I also decided to tweak the two animations to be slightly different, so I’m fine with having two of them: \n@keyframes cookie-banner {\n  0% {\n    max-height: 0;\n  }\n  100% {\n    max-height: 20em;\n  }\n}\n@keyframes cookie-banner-reverse {\n  0% {\n    max-height: 20em;\n  }\n  100% {\n    max-height: 0;\n    display: none;\n  }\n}\n Not knowing how tall the banner would be (this is responsive design, after all), I needed it to animate to and from a height of auto. Thankfully, Nikita Vasilyev25 published a fantastic overview of how to transition values to and from auto26 a few years back. In short, animate max-height instead. The only thing to keep in mind is that the size of the non-zero max-height value you are transitioning to and from needs to be larger than your max, and it will also directly affect the speed of the animation. I found 20 ems to be more than adequate for this use case, but your project may require a different value. It’s also worth noting that I used display: none at the conclusion of my cookie-banner-reverse animation (the closing one) to ensure the banner becomes unreachable to users of assistive technology such as screen readers. It’s probably unnecessary, but I did it as a failsafe just in case something happens and JavaScript doesn’t remove the banner from the DOM. Wiring it up required only a few minor tweaks to the CSS: \n#cookie-banner {\n  …\n  box-sizing: border-box;\n  overflow: hidden;\n  animation: cookie-banner 1s 1s linear forwards;\n}\n\n#cookie-banner.closing {\n  animation: cookie-banner-reverse .5s linear forwards;\n}\n This assigned the two animations to the two different banner states: The opening and resting state, cookie-banner, runs for one second after a one-second delay; the closing state, cookie-banner-reverse, runs for only half a second with no delay. I am using a class of closing, set via the JavaScript I showed earlier, to trigger the state change. Just for completeness, I’ll note that this code also stabilizes the dimensions of the banner with box-sizing: border-box and keeps the contents from spilling out of the banner using overflow: hidden. One last bit of CSS tweaking and we’re done. On small screens, I’m leaving a margin between the cookie notice (#cookie-banner) and the page header (.banner). I want that to go away when the banner collapses, even if the cookie notice is not removed from the DOM. I can accomplish that with an adjacent-sibling selector: \n#cookie-banner + .banner {\n  transition: margin-top .5s;\n}\n\n#cookie-banner.closing + .banner {\n  margin-top: 0;\n}\n It’s worth noting that I am setting the top margin on every element but the first one, using Heydon Pickering’s clever “lobotomized owl27” selector. So, the transition of margin-top on .banner will be from a specific value (in my case, 1.375 rem) to 0. With this code in place, the top margin will collapse over the same duration as the one used for the closing animation of the cookie banner and will be triggered by the very same class addition. [embedded content]\nA video showing the final implementation on a wide screen, both with and without JavaScript. Simple, Robust, Resilient Link What I like about this approach is that it is fairly simple. It took only about an hour or two to research and implement, and it checks all of the compliance boxes with respect to the EU law. It has minimal dependencies, offers several fallback options, cleans up after itself and is a relatively back-end-agnostic pattern. When tasked with adding features we may not like — and, yes, I’d count a persistent nagging banner as one of those features — it’s often tempting to throw some code at it to get it done and over with. JavaScript is often a handy tool to accomplish that, especially because the logic can often be self-contained in an external script, configured and forgotten. But there’s a risk in that approach: JavaScript is never guaranteed28. If the feature is “nice to have,” you might be able to get away with it, but it’s probably not a good idea to play fast and loose with a legal mandate like this. Taking a few minutes to step back and explore how the feature can be implemented with minimal effort on all fronts will pay dividends down the road. Believe me29. (rb, vf, il, al) Front page image credit: Pexels30. 1 https://github.com/yui/yui3/wiki/Graded-Browser-Support 2 http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT 3 https://webdevlaw.uk/category/eu-cookie-law/ 4 https://developer.mozilla.org/docs/Web/API/Web_Storage_API 5 https://developer.mozilla.org/docs/Web/API/IndexedDB_API 6 http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4 7 https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/ 8 https://a-k-apart.com/ 9 https://developer.mozilla.org/docs/Web/API/Web_Storage_API 10 https://wordpress.org/plugins/eu-cookie-law/ 11 https://www.aaron-gustafson.com/notebook/interface-experience-maps/ 12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png 13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png 14 https://developer.mozilla.org/docs/Web/API/Service_Worker_API 15 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png 16 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png 17 https://nodejs.org/ 18 http://expressjs.com/ 19 https://github.com/expressjs/cookie-parser 20 http://expressjs.com/en/guide/using-middleware.html 21 https://mustache.github.io/ 22 https://mustache.github.io/mustache.5.html#Partials 23 https://mustache.github.io/mustache.5.html#Inverted-Sections 24 https://developer.mozilla.org/docs/Glossary/Truthy 25 https://twitter.com/ELV1S 26 http://n12v.com/css-transition-to-from-auto/ 27 http://alistapart.com/article/axiomatic-css-and-lobotomized-owls 28 http://kryogenix.org/code/browser/everyonehasjs.html 29 https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979 30 https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/  ↑ Back to top Tweet itShare on Facebook  Collected and Posted by Newze Original Post Link: https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/"},"mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"http://www.webhostingreviewsbynerds.com/when-your-code-has-to-work-complying-with-legal-mandates/","published":null,"wm-received":"2017-03-02T23:52:32Z","wm-id":426341,"wm-source":"http://www.webhostingreviewsbynerds.com/when-your-code-has-to-work-complying-with-legal-mandates/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"When Your Code Has To Work: Complying With Legal Mandates\n By Aaron Gustafson March 2nd, 2017 JavaScriptTechniques 8 Comments  Douglas Crockford famously declared browsers to be “the most hostile software engineering environment imaginable,” and that wasn’t hyperbole. Ensuring that our websites work across a myriad of different devices, screen sizes and browsers our users depend on to access the web is a tall order, but it’s necessary. If our websites don’t enable users to accomplish the key tasks they come to do, we’ve failed them. We should do everything in our power to ensure our websites function under even the harshest of scenarios, but at the same, we can’t expect our users to have the exact same experience in every browser, on every device. Yahoo realized this more than a decade ago and made it a central concept in its “Graded Browser Support1” strategy: Support does not mean that everybody gets the same thing. Expecting two users using different browser software to have an identical experience fails to embrace or acknowledge the heterogeneous essence of the Web. In fact, requiring the same experience for all users creates an artificial barrier to participation. Availability and accessibility of content should be our key priority.  And that was a few years before the iPhone was introduced! Providing alternate experience pathways for our core functionality should be a no-brainer, but when it comes to implementing stuff we’d rather not think about, we often reach for the simplest drop-in solution, despite the potential negative impact it could have on our business. Consider the EU’s “cookie law.”2 If you’re unfamiliar, this somewhat contentious law3 is privacy legislation that requires websites to obtain consent from visitors before storing or retrieving information from their device. We call it the cookie law, but the legislation also applies to web storage4, IndexedDB5 and other client-side data storage and retrieval APIs. Compliance with this law is achieved by: notifying users that the website requires the ability to store and read information on their device; providing a link to the website’s privacy statement, which includes information about the storage mechanisms being used and what they are being used for; prompting users to confirm their acceptance of this requirement.  If you operate a website aimed at folks living in the EU and fail to do this, you could be subject to a substantial fine. You could even open yourself up to a lawsuit. If you’ve had to deal with the EU cookie law before, you’re probably keenly aware that a ton of “solutions” are available to provide compliance. Those quotation marks are fully intentional because nearly every one I found — including the one provided by the EU6 itself — has been a drop-in JavaScript file that enables compliance. If we’re talking about the letter of the law, however, they don’t actually. The problem is that, as awesome and comprehensive as some of these solutions are, we can never be guaranteed that our JavaScript programs will actually run7. In order to truly comply with the letter of the law, we should provide a fallback version of the utility — just in case. Most people will never see it, but at least we know we’re covered if something goes wrong. I stumbled into this morass while building the 10k Apart contest website8. We weren’t using cookies for much on the website — mainly analytics and vote-tracking — but we were using the Web Storage API9 to speed up the performance of the website and to save form data temporarily while folks were filling out the form. Because the contest was open to folks who live in the EU, we needed to abide by the cookie law. And because none of the solutions I found actually complied with the law in either spirit or reality — the notable exception being WordPress’ EU Cookie Law10 plugin, which works both with and without JavaScript, but the contest website wasn’t built in WordPress or even PHP, so I had to do something else — I opted to roll my own robust solution. Planning It Out Link I’m a big fan of using interface experience (IX) maps11 to diagram functionality. I find their simple nature easy to understand and to tweak as I increase the fidelity of an experience. For this feature, I started with a (relatively) simple IX map that diagrammed what would happen when a user requests a page on the website.  IX map for the basic interaction and fallback.12\nIX map for the basic interaction and fallback. (View large version13)  This IX map outlines several potential experiences that vary based on the user’s choice and feature availability. I’ll walk through the ideal scenario first: A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything. The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance. The browser renders the page with the banner. The user clicks to accept the use of cookies and web storage. Client-side JavaScript sets the accepts cookie and closes the banner. On subsequent page requests, the server reads the accepts cookie and does not inject the banner code. JavaScript sees the cookie and enables the cookie and web storage code.  For the vast majority of users, this is the experience they’ll get, and that’s awesome. That said, however, we can never be 100% guaranteed our client-side JavaScript code will run, so we need a backup plan. Here’s the fallback experience: A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything. The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance. The browser renders the page with the banner. The user clicks to accept the use of cookies and web storage. The click initiates a form post to the server, which responds by setting the accepts cookie before redirecting the user back to the page they were on. On subsequent page requests, the server reads the accepts cookie and does not inject the banner code. If JavaScript becomes available later, it will see the cookie and enable its cookie and web storage code.  Not bad. There’s an extra roundtrip to the server, but it’s a quick one, and, more importantly, it provides a foolproof fallback in the absence of our preferred JavaScript-driven option. True, it could fall victim to a networking issue, but there’s not much we can do to mitigate that without JavaScript in play. Speaking of mitigating networking issues, the 10k Apart contest website uses a service worker14 to do some pretty aggressive caching; the service worker intercepts any page request and supplies a cached version if one exists. That could result in users getting a copy of the page with the banner still in it, even if they’ve already agreed to allow cookies. Time to update the IX map.  IX map for the basic interaction and fallback.15\nThe original IX map, adjusted to handle cached pages. (View large version16)  This is one of the reasons I like IX maps so much: They are really easy to generate and simple to update when you want to add features or handle more scenarios. With a few adjustments in place, I can account for the scenario in which a stale page includes the banner unnecessarily and have JavaScript remove it. With this plan in place, it was time to implement it. Server-Side Implementation Link 10k Apart’s back end is written in Node.js17 and uses Express18. I’m not going to get into the nitty-gritty of our installation and configuration, but I do want to talk about how I implemented this feature. First off, I opted to use Express’ cookie-parser19 middleware to let me get and set the cookie. // enable cookie-parser for Express\nvar cookieParser = require('cookie-parser');\napp.use(cookieParser()); Once that was set up, I created my own custom Express middleware20 that would intercept requests and check for the approves_cookies cookie: var checkCookie = function(req, res, next) {\n  res.locals.approves_cookies = ( req.cookies['approves_cookies'] === 'yes' );\n  res.locals.current_url = req.url || '/';\n  next();\n}; This code establishes a middleware function named checkCookie(). All Express middleware gets access to the request (req), the response (res) and the next middleware function (next), so you’ll see those accounted for as the three arguments to that function. Then, within the function, I am modifying the response object to include two local variables (res.locals) to capture whether the cookie has already been set (res.locals.approves_cookies) and the currently requested URL (res.locals.current_url). Then, I call the next middleware function. With that written, I can include this middleware in Express: app.use(checkCookie); All of the templates for the website are Mustache21 files, and Express automatically pipes res.locals into those templates. Knowing that, I created a Mustache partial22 to handle the banner: {{^approves_cookies}}\n  div id=\"cookie-banner\" role=\"alert\"\n    form action=\"/cookies-ok\" method=\"post\"\n      input type=\"hidden\" name=\"redirect_to\" value=\"{{current_url}}\"\n      pThis site uses cookies for analytics and to track voting. If you're interested, more details can be found in a href=\"{{privacy_url}}#maincookiessimilartechnologiesmodule\"our cookie policy/a./p\n      button type=\"submit\"I'm cool with that/button\n    /form\n  /div\n{{/approves_cookies}} This template uses an inverted section23 that only renders the div when approves_cookies is false. Within that markup, you can also see the current_url getting piped into a hidden input to indicate where a user should be redirected if the form method of setting the cookie is used. You remembered: the fallback. Speaking of the fallback, since we have one, we also need to handle that on the server side. Here’s the Node.js code for that: var affirmCookies = function (req, res) {\n  if ( ! req.cookies['approves_cookies'] )\n  {\n    res.cookie('approves_cookies', 'yes', {\n      secure: true,\n      maxAge: ( 365 * 24 * 60 * 60 ) // 1 year\n    });\n  }\n  res.redirect(req.body.redirect_to);\n};\napp.post('/cookies-ok', affirmCookies); This ensures that if the form is submitted, Express will respond by setting the approves_cookies cookie (if it’s not already set) and then redirecting the user to the page they were on. Taken altogether, this gives us a solid baseline experience for every user. Now, it’s worth noting that none of this code is going to be useful to you if your projects don’t involve the specific stack I was working with on this project (Node.js, Express, Mustache). That said, the logic I’ve outlined here and in the IX map is portable to pretty much any language or framework you happen to know and love. OK, let’s switch gears and work some magic on the front end. Front-End Implementation Link When JavaScript is available and running properly, we’ll want to take full advantage of it, but it doesn’t make sense to run any code against the banner if it doesn’t exist, so first things first: I should check to see whether the banner is even in the page. var $cookie_banner = document.getElementById('cookie-banner');\n\nif ( $cookie_banner )\n{\n  // actual code will go here\n} In order to streamline the application logic, I’m going to add another conditional within to check for the accepts_cookies cookie. I know from my second pass on the IX map there’s an outside chance that the banner might be served up by my service worker even if the accepts cookie exists, so checking for the cookie early lets me run only the bit of JavaScript that removes the banner. But before I jump into all of that, I’ll create a function I can call in any of my code to let me know whether the user has agreed to let me cookie them: function cookiesApproved(){\n  return document.cookie.indexOf('approves_cookies')  -1;\n} I need this check in multiple places throughout my JavaScript, so it makes sense to break it out into a separate function. Now, let’s revisit my banner-handling logic: var $cookie_banner = document.getElementById('cookie-banner');\n\nif ( $cookie_banner )\n{\n\n  // banner exists but cookie is set\n  if ( cookiesApproved() )\n  {\n    // hide the banner immediately!\n  }\n  // cookie has not been set \n  else\n  {\n    // add the logic to set the cookie\n    // and close the banner\n  }\n\n} Setting cookies in JavaScript is a little convoluted because you need to set it as a string, but it’s not too ghastly. I broke out the process into its own function so that I could set it as an event handler on the form: function approveCookies( e ) {\n\n  // prevent the form from submitting\n  e.preventDefault();\n\n  var cookie,               // placeholder for the cookie\n      expires = new Date(); // start building expiry date\n\n  // expire in one year\n  expires.setFullYear( expires.getFullYear() + 1 );\n\n  // build the cookie\n  cookie = [\n    'approves_cookies=yes',\n    'expires=' + expires.toUTCString(),\n    'domain=' + window.location.hostname,\n    window.location.protocol == 'https:' ? 'secure' : ''\n  ];\n\n  // set it\n  document.cookie = cookie.join('; ');\n\n  // close up the banner\n  closeCookieBanner();\n\n  // return\n  return false;\n\n};\n\n// find the form inside the banner\nvar $form = $cookie_banner.getElementsByTagName('form')[0];\n\n// hijack the submit event\n$form.addEventListener( 'submit', approveCookies, false ); The comments in the code should make it pretty clear, but just in case, here’s what I’m doing: Hijack the form submission event (e) and cancel its default action using e.preventDefault(). Use the Date object to construct a date one year out. Assemble the bits of the cookie, including the approves_cookies value, the expiry date, the domain the cookie is bound to, and whether the cookie should be secure (so I can test locally). Set document.cookie equal to the assembled cookie string. Trigger a separate method — closeCookieBanner() — to close the banner (which I will cover in a moment).  With that in place, I can define closeCookieBanner() to handle, well, closing up the banner. There are actually two instances in which I need this functionality: after setting the cookie (as we just saw) and if the service worker serves up a stale page that still has the banner in it. Even though each requires roughly the same functionality, I want to make the stale-page cleanup version a little more aggressive. Here’s the code: function closeCookieBanner( immediate ) {\n\n  // How fast to close? Animation takes .5s\n  var close_speed = immediate ? 0 : 600;\n\n  // remove\n  window.setTimeout(function(){\n\n    $cookie_banner.parentNode.removeChild( $cookie_banner );\n\n    // remove the DOM reference\n    $cookie_banner = null;\n\n  }, close_speed);\n\n  // animate closed\n  if ( ! immediate ) {\n    $cookie_banner.className = 'closing';\n  }\n\n} This function takes a single optional argument. If true (or anything “truthy”24) is passed in, the banner is immediately removed from the page (and its reference is deleted). If no argument is passed in, that doesn’t happen for 0.6 seconds, which is 0.1 seconds after the animation finishes up (we’ll get to the animation momentarily). The class change triggers that animation. You already saw one instance of this function referenced in the previous code block. Here it is in the cached template branch of the conditional you saw earlier: …\n// banner exists but cookie is set\nif ( cookiesApproved() )\n{\n  // close immediately\n  closeCookieBanner( true );\n}\n… Adding Some Visual Sizzle Link Because I brought up animations, I’ll discuss the CSS I’m using for the cookie banner component, too. Like most implementations of cookie notices, I opted for a visual full-width banner. On small screens, I wanted the banner to appear above the content and push it down the page. On larger screens I opted to affix it to the top of the viewport because it would not obstruct reading to nearly the same degree as it would on a small screen. Accomplishing this involved very little code: #cookie-banner {\n  background: \n  color: \n  font-size: .875rem;\n  text-align: center;\n}\n\n@media (min-width: 60em) {\n  #cookie-banner {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    z-index: 1000;      \n  }\n} Using the browser’s default styles, the cookie banner already displays block, so I didn’t really need to do much apart from set some basic text styles and colors. For the large screen (the “full-screen” version comes in at 60 ems), I affix it to the top of the screen using position: fixed, with a top offset of 0. Setting its left and right offsets to 0 ensures it will always take up the full width of the viewport. I also set the z-index quite high so it sits on top of everything else in the stack. Here’s the result:  A video showing the browser viewport being enlarged from 240 to 960 pixels width. When the design hits the 60-em breakpoint, the banner becomes fixed-positioned.  Once the basic design was there, I took another pass to spice it up a bit. I decided to have the banner animate in and out using CSS. First things first: I created two animations. Initially, I tried to run a single animation in two directions for each state (opening and closing) but ran into problems triggering the reversal — you might be better at CSS animations than I am, so feel free to give it a shot. In the end, I also decided to tweak the two animations to be slightly different, so I’m fine with having two of them: @keyframes cookie-banner {\n  0% {\n    max-height: 0;\n  }\n  100% {\n    max-height: 20em;\n  }\n}\n@keyframes cookie-banner-reverse {\n  0% {\n    max-height: 20em;\n  }\n  100% {\n    max-height: 0;\n    display: none;\n  }\n} Not knowing how tall the banner would be (this is responsive design, after all), I needed it to animate to and from a height of auto. Thankfully, Nikita Vasilyev25 published a fantastic overview of how to transition values to and from auto26 a few years back. In short, animate max-height instead. The only thing to keep in mind is that the size of the non-zero max-height value you are transitioning to and from needs to be larger than your max, and it will also directly affect the speed of the animation. I found 20 ems to be more than adequate for this use case, but your project may require a different value. It’s also worth noting that I used display: none at the conclusion of my cookie-banner-reverse animation (the closing one) to ensure the banner becomes unreachable to users of assistive technology such as screen readers. It’s probably unnecessary, but I did it as a failsafe just in case something happens and JavaScript doesn’t remove the banner from the DOM. Wiring it up required only a few minor tweaks to the CSS: #cookie-banner {\n  …\n  box-sizing: border-box;\n  overflow: hidden;\n  animation: cookie-banner 1s 1s linear forwards;\n}\n\n#cookie-banner.closing {\n  animation: cookie-banner-reverse .5s linear forwards;\n} This assigned the two animations to the two different banner states: The opening and resting state, cookie-banner, runs for one second after a one-second delay; the closing state, cookie-banner-reverse, runs for only half a second with no delay. I am using a class of closing, set via the JavaScript I showed earlier, to trigger the state change. Just for completeness, I’ll note that this code also stabilizes the dimensions of the banner with box-sizing: border-box and keeps the contents from spilling out of the banner using overflow: hidden. One last bit of CSS tweaking and we’re done. On small screens, I’m leaving a margin between the cookie notice (#cookie-banner) and the page header (.banner). I want that to go away when the banner collapses, even if the cookie notice is not removed from the DOM. I can accomplish that with an adjacent-sibling selector: #cookie-banner + .banner {\n  transition: margin-top .5s;\n}\n\n#cookie-banner.closing + .banner {\n  margin-top: 0;\n} It’s worth noting that I am setting the top margin on every element but the first one, using Heydon Pickering’s clever “lobotomized owl27” selector. So, the transition of margin-top on .banner will be from a specific value (in my case, 1.375 rem) to 0. With this code in place, the top margin will collapse over the same duration as the one used for the closing animation of the cookie banner and will be triggered by the very same class addition.  A video showing the final implementation on a wide screen, both with and without JavaScript.  Simple, Robust, Resilient Link What I like about this approach is that it is fairly simple. It took only about an hour or two to research and implement, and it checks all of the compliance boxes with respect to the EU law. It has minimal dependencies, offers several fallback options, cleans up after itself and is a relatively back-end-agnostic pattern. When tasked with adding features we may not like — and, yes, I’d count a persistent nagging banner as one of those features — it’s often tempting to throw some code at it to get it done and over with. JavaScript is often a handy tool to accomplish that, especially because the logic can often be self-contained in an external script, configured and forgotten. But there’s a risk in that approach: JavaScript is never guaranteed28. If the feature is “nice to have,” you might be able to get away with it, but it’s probably not a good idea to play fast and loose with a legal mandate like this. Taking a few minutes to step back and explore how the feature can be implemented with minimal effort on all fronts will pay dividends down the road. Believe me29. (rb, vf, il, al) Front page image credit: Pexels30. Footnotes Link 1 https://github.com/yui/yui3/wiki/Graded-Browser-Support 2 http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT 3 https://webdevlaw.uk/category/eu-cookie-law/ 4 https://developer.mozilla.org/docs/Web/API/Web_Storage_API 5 https://developer.mozilla.org/docs/Web/API/IndexedDB_API 6 http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4 7 https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/ 8 https://a-k-apart.com/ 9 https://developer.mozilla.org/docs/Web/API/Web_Storage_API 10 https://wordpress.org/plugins/eu-cookie-law/ 11 https://www.aaron-gustafson.com/notebook/interface-experience-maps/ 12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png 13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png 14 https://developer.mozilla.org/docs/Web/API/Service_Worker_API 15 http://www.webhostingreviewsbynerds.com/wp-content/plugins/rss-poster/cache/6289f_cookie-law-large-2-opt.png 16 http://www.webhostingreviewsbynerds.com/wp-content/plugins/rss-poster/cache/6289f_cookie-law-1-opt.png 17 https://nodejs.org/ 18 http://expressjs.com/ 19 https://github.com/expressjs/cookie-parser 20 http://expressjs.com/en/guide/using-middleware.html 21 https://mustache.github.io/ 22 https://mustache.github.io/mustache.5.html#Partials 23 https://mustache.github.io/mustache.5.html#Inverted-Sections 24 https://developer.mozilla.org/docs/Glossary/Truthy 25 https://twitter.com/ELV1S 26 http://n12v.com/css-transition-to-from-auto/ 27 http://alistapart.com/article/axiomatic-css-and-lobotomized-owls 28 http://kryogenix.org/code/browser/everyonehasjs.html 29 https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979 30 https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/   ↑ Back to top  Tweet itShare on Facebook","mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"dumingu.m","photo":"","url":"https://www.tech-chat.co.za/author/dumingu.m/"},"url":"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/","published":null,"wm-received":"2017-03-03T05:11:45Z","wm-id":426410,"wm-source":"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","content":{"content-type":"text/html","value":"<h1>\n\t\t\t\t\t\t\t\tWhen Your Code Has To Work: Complying With Legal Mandates\t\t\t\t\t\t\t\t</h1>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t<ul><li>\n\t\t\t\t<a class=\"dt-updated\" href=\"https://www.tech-chat.co.za/2017/03/03/\">03 March 2017</a>\n\t\t\t</li>\n\t\t\t\t\t\t\t<li class=\"h-card p-author h-card\">\n\t\t\t\t\t<i>by: </i>\n\t\t\t\t\t<a class=\"p-name u-url\" href=\"https://www.tech-chat.co.za/author/dumingu.m/\" title=\"View all posts by dumingu.m\">\n\t\t\t\t\t\tdumingu.m\t\t\t\t\t</a>\n\t\t\t\t</li>\n\t\t\t\n\t\t\t<li><i>in: </i><a href=\"https://www.tech-chat.co.za/category/coding/\">Coding</a>,<a href=\"https://www.tech-chat.co.za/category/javascript/\">JavaScript</a>,<a href=\"https://www.tech-chat.co.za/category/techniques/\">Techniques</a>,<a href=\"https://www.tech-chat.co.za/category/uncategorized/\">Uncategorized</a></li>\t\t\t\t <li>\n\t\t\t\t \t<i>note: </i>\n\t\t            <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#respond\" title=\"Click to leave a comment\">\n\t\t            \tno comments\t\t            </a>\n\t\t        </li>\n\t\t    </ul><p> </p>\n\n<p>Douglas Crockford famously declared browsers to be “the most hostile software engineering environment imaginable,” and that wasn’t hyperbole. <strong>Ensuring that our websites work across a myriad of different devices</strong>, screen sizes and browsers our users depend on to access the web is a tall order, but it’s necessary. If our websites don’t enable users to accomplish the key tasks they come to do, we’ve failed them.</p>\n<p>We should do everything in our power to ensure our websites function under even the harshest of scenarios, but at the same, we can’t expect our users to have the exact same experience in every browser, on every device. Yahoo realized this more than a decade ago and made it a central concept in its “<a href=\"https://github.com/yui/yui3/wiki/Graded-Browser-Support\">Graded Browser Support</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#1\">1</a>” strategy:</p>\n<blockquote>\n<p>Support does not mean that everybody gets the same thing. Expecting two users using different browser software to have an identical experience fails to embrace or acknowledge the heterogeneous essence of the Web. In fact, requiring the same experience for all users creates an artificial barrier to participation. Availability and accessibility of content should be our key priority.</p>\n</blockquote>\n<p>And that was a few years before the iPhone was introduced!</p>\n<p>Providing alternate experience pathways for our core functionality should be a no-brainer, but when it comes to implementing stuff we’d rather not think about, we often reach for the simplest drop-in solution, despite the potential negative impact it could have on our business.</p>\n<p>Consider the <a href=\"http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT\">EU’s “cookie law.”</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#2\">2</a> If you’re unfamiliar, this <a href=\"https://webdevlaw.uk/category/eu-cookie-law/\">somewhat contentious law</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#3\">3</a> is privacy legislation that requires websites to obtain consent from visitors before storing or retrieving information from their device. We call it the cookie law, but the legislation also applies to <a href=\"https://developer.mozilla.org/docs/Web/API/Web_Storage_API\">web storage</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#4\">4</a>, <a href=\"https://developer.mozilla.org/docs/Web/API/IndexedDB_API\">IndexedDB</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#5\">5</a> and other client-side data storage and retrieval APIs.</p>\n<p>Compliance with this law is achieved by:</p>\n<ol><li>notifying users that the website requires the ability to store and read information on their device;</li>\n<li>providing a link to the website’s privacy statement, which includes information about the storage mechanisms being used and what they are being used for;</li>\n<li>prompting users to confirm their acceptance of this requirement.</li>\n</ol><p>If you operate a website aimed at folks living in the EU and fail to do this, you could be subject to a substantial fine. You could even open yourself up to a lawsuit.</p>\n<p>If you’ve had to deal with the EU cookie law before, you’re probably keenly aware that a ton of “solutions” are available to provide compliance. Those quotation marks are fully intentional because nearly every one I found — including the <a href=\"http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4\">one provided by the EU</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#6\">6</a> itself — has been a drop-in JavaScript file that <em>enables</em> compliance. If we’re talking about the letter of the law, however, they don’t actually. The problem is that, as awesome and comprehensive as some of these solutions are, we can never be <a href=\"https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/\">guaranteed that our JavaScript programs will actually run</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#7\">7</a>. In order to truly comply with the letter of the law, we should provide a fallback version of the utility — just in case. Most people will never see it, but at least we know we’re covered if something goes wrong.</p>\n<p>I stumbled into this morass while building the <a href=\"https://a-k-apart.com/\">10k Apart contest website</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#8\">8</a>. We weren’t using cookies for much on the website — mainly analytics and vote-tracking — but we were using the <a href=\"https://developer.mozilla.org/docs/Web/API/Web_Storage_API\">Web Storage API</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#9\">9</a> to speed up the performance of the website and to save form data temporarily while folks were filling out the form. Because the contest was open to folks who live in the EU, we needed to abide by the cookie law. And because none of the solutions I found actually complied with the law in either spirit or reality — the notable exception being WordPress’ <a href=\"https://wordpress.org/plugins/eu-cookie-law/\">EU Cookie Law</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#10\">10</a> plugin, which works both with and without JavaScript, but the contest website wasn’t built in WordPress or even PHP, so I had to do something else — I opted to roll my own robust solution.</p>\n<h3>Planning It Out <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#planning-it-out\">Link</a></h3>\n<p>I’m a big fan of using <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">interface experience (IX) maps</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#11\">11</a> to diagram functionality. I find their simple nature easy to understand and to tweak as I increase the fidelity of an experience. For this feature, I started with a (relatively) simple IX map that diagrammed what would happen when a user requests a page on the website.</p>\n<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png\"><img alt=\"IX map for the basic interaction and fallback.\" height=\"530\" src=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png\" width=\"800\" /></a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#12\">12</a>IX map for the basic interaction and fallback. (<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png\">View large version</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#13\">13</a>)<p>This IX map outlines several potential experiences that vary based on the user’s choice and feature availability. I’ll walk through the ideal scenario first:</p>\n<ol><li>A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything.</li>\n<li>The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance.</li>\n<li>The browser renders the page with the banner.</li>\n<li>The user clicks to accept the use of cookies and web storage.</li>\n<li>Client-side JavaScript sets the <code>accepts</code> cookie and closes the banner.</li>\n<li>On subsequent page requests, the server reads the <code>accepts</code> cookie and does not inject the banner code. JavaScript sees the cookie and enables the cookie and web storage code.</li>\n</ol><p>For the vast majority of users, this is the experience they’ll get, and that’s awesome. That said, however, we can never be 100% guaranteed our client-side JavaScript code will run, so we need a backup plan. Here’s the fallback experience:</p>\n<ol><li>A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything.</li>\n<li>The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance.</li>\n<li>The browser renders the page with the banner.</li>\n<li>The user clicks to accept the use of cookies and web storage.</li>\n<li>The click initiates a form post to the server, which responds by setting the <code>accepts</code> cookie before redirecting the user back to the page they were on.</li>\n<li>On subsequent page requests, the server reads the <code>accepts</code> cookie and does not inject the banner code.</li>\n<li>If JavaScript becomes available later, it will see the cookie and enable its cookie and web storage code.</li>\n</ol><p>Not bad. There’s an extra roundtrip to the server, but it’s a quick one, and, more importantly, it provides a foolproof fallback in the absence of our preferred JavaScript-driven option. True, it could fall victim to a networking issue, but there’s not much we can do to mitigate that without JavaScript in play.</p>\n<p>Speaking of mitigating networking issues, the 10k Apart contest website uses a <a href=\"https://developer.mozilla.org/docs/Web/API/Service_Worker_API\">service worker</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#14\">14</a> to do some pretty aggressive caching; the service worker intercepts any page request and supplies a cached version if one exists. That <em>could</em> result in users getting a copy of the page with the banner still in it, even if they’ve already agreed to allow cookies. Time to update the IX map.</p>\n<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png\"><img alt=\"IX map for the basic interaction and fallback.\" height=\"461\" src=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png\" width=\"800\" /></a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#15\">15</a>The original IX map, adjusted to handle cached pages. (<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png\">View large version</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#16\">16</a>)<p>This is one of the reasons I like IX maps so much: They are really easy to generate and simple to update when you want to add features or handle more scenarios. With a few adjustments in place, I can account for the scenario in which a stale page includes the banner unnecessarily and have JavaScript remove it.</p>\n<p>With this plan in place, it was time to implement it.</p>\n<h3>Server-Side Implementation <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#server-side-implementation\">Link</a></h3>\n<p>10k Apart’s back end is written in <a href=\"https://nodejs.org/\">Node.js</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#17\">17</a> and uses <a href=\"http://expressjs.com/\">Express</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#18\">18</a>. I’m not going to get into the nitty-gritty of our installation and configuration, but I do want to talk about how I implemented this feature. First off, I opted to use <a href=\"https://github.com/expressjs/cookie-parser\">Express’ cookie-parser</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#19\">19</a> middleware to let me get and set the cookie.</p>\n<pre><code>// enable cookie-parser for Express\r\nvar cookieParser = require('cookie-parser');\r\napp.use(cookieParser());</code></pre>\n<p>Once that was set up, I created my own <a href=\"http://expressjs.com/en/guide/using-middleware.html\">custom Express middleware</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#20\">20</a> that would intercept requests and check for the <code>approves_cookies</code> cookie:</p>\n<pre><code>var checkCookie = function(req, res, next) {\r\n  res.locals.approves_cookies = ( req.cookies['approves_cookies'] === 'yes' );\r\n  res.locals.current_url = req.url || '/';\r\n  next();\r\n};</code></pre>\n<p>This code establishes a middleware function named <code>checkCookie()</code>. All Express middleware gets access to the request (<code>req</code>), the response (<code>res</code>) and the next middleware function (<code>next</code>), so you’ll see those accounted for as the three arguments to that function. Then, within the function, I am modifying the response object to include two local variables (<code>res.locals</code>) to capture whether the cookie has already been set (<code>res.locals.approves_cookies</code>) and the currently requested URL (<code>res.locals.current_url</code>). Then, I call the next middleware function.</p>\n<p>With that written, I can include this middleware in Express:</p>\n<pre><code>app.use(checkCookie);</code></pre>\n<p>All of the templates for the website are <a href=\"https://mustache.github.io/\">Mustache</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#21\">21</a> files, and Express automatically pipes <code>res.locals</code> into those templates. Knowing that, I created a <a href=\"https://mustache.github.io/mustache.5.html#Partials\">Mustache partial</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#22\">22</a> to handle the banner:</p>\n<pre><code>{{^approves_cookies}}\r\n  &lt;div id=\"cookie-banner\" role=\"alert\"&gt;\r\n    &lt;form action=\"/cookies-ok\" method=\"post\"&gt;\r\n      &lt;input type=\"hidden\" name=\"redirect_to\" value=\"{{current_url}}\"&gt;\r\n      &lt;p&gt;This site uses cookies for analytics and to track voting. If you're interested, more details can be found in &lt;a href=\"{{privacy_url}}#maincookiessimilartechnologiesmodule\"&gt;our cookie policy&lt;/a&gt;.&lt;/p&gt;\r\n      &lt;button type=\"submit\"&gt;I'm cool with that&lt;/button&gt;\r\n    &lt;/form&gt;\r\n  &lt;/div&gt;\r\n{{/approves_cookies}}</code></pre>\n<p>This template uses an <a href=\"https://mustache.github.io/mustache.5.html#Inverted-Sections\">inverted section</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#23\">23</a> that only renders the <code>div</code> when <code>approves_cookies</code> is false. Within that markup, you can also see the <code>current_url</code> getting piped into a hidden <code>input</code> to indicate where a user should be redirected if the form method of setting the cookie is used. You remembered: the fallback.</p>\n<p>Speaking of the fallback, since we have one, we also need to handle that on the server side. Here’s the Node.js code for that:</p>\n<pre><code>var affirmCookies = function (req, res) {\r\n  if ( ! req.cookies['approves_cookies'] )\r\n  {\r\n    res.cookie('approves_cookies', 'yes', {\r\n      secure: true,\r\n      maxAge: ( 365 * 24 * 60 * 60 ) // 1 year\r\n    });\r\n  }\r\n  res.redirect(req.body.redirect_to);\r\n};\r\napp.post('/cookies-ok', affirmCookies);</code></pre>\n<p>This ensures that if the form is submitted, Express will respond by setting the <code>approves_cookies</code> cookie (if it’s not already set) and then redirecting the user to the page they were on. Taken altogether, this gives us a solid baseline experience for every user.</p>\n<p>Now, it’s worth noting that none of this code is going to be useful to you if your projects don’t involve the specific stack I was working with on this project (Node.js, Express, Mustache). That said, the logic I’ve outlined here and in the IX map is portable to pretty much any language or framework you happen to know and love.</p>\n<p>OK, let’s switch gears and work some magic on the front end.</p>\n<h3>Front-End Implementation <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#front-end-implementation\">Link</a></h3>\n<p>When JavaScript is available and running properly, we’ll want to take full advantage of it, but it doesn’t make sense to run any code against the banner if it doesn’t exist, so first things first: I should check to see whether the banner is even in the page.</p>\n<pre><code>var $cookie_banner = document.getElementById('cookie-banner');\r\n\r\nif ( $cookie_banner )\r\n{\r\n  // actual code will go here\r\n}</code></pre>\n<p>In order to streamline the application logic, I’m going to add another conditional within to check for the <code>accepts_cookies</code> cookie. I know from my second pass on the IX map there’s an outside chance that the banner might be served up by my service worker even if the <code>accepts</code> cookie exists, so checking for the cookie early lets me run only the bit of JavaScript that removes the banner. But before I jump into all of that, I’ll create a function I can call in any of my code to let me know whether the user has agreed to let me cookie them:</p>\n<pre><code>function cookiesApproved(){\r\n  return document.cookie.indexOf('approves_cookies') &gt; -1;\r\n}</code></pre>\n<p>I need this check in multiple places throughout my JavaScript, so it makes sense to break it out into a separate function. Now, let’s revisit my banner-handling logic:</p>\n<pre><code>var $cookie_banner = document.getElementById('cookie-banner');\r\n\r\nif ( $cookie_banner )\r\n{\r\n\r\n  // banner exists but cookie is set\r\n  if ( cookiesApproved() )\r\n  {\r\n    // hide the banner immediately!\r\n  }\r\n  // cookie has not been set \r\n  else\r\n  {\r\n    // add the logic to set the cookie\r\n    // and close the banner\r\n  }\r\n\r\n}</code></pre>\n<p>Setting cookies in JavaScript is a little convoluted because you need to set it as a string, but it’s not too ghastly. I broke out the process into its own function so that I could set it as an event handler on the form:</p>\n<pre><code>function approveCookies( e ) {\r\n\r\n  // prevent the form from submitting\r\n  e.preventDefault();\r\n\r\n  var cookie,               // placeholder for the cookie\r\n      expires = new Date(); // start building expiry date\r\n\r\n  // expire in one year\r\n  expires.setFullYear( expires.getFullYear() + 1 );\r\n\r\n  // build the cookie\r\n  cookie = [\r\n    'approves_cookies=yes',\r\n    'expires=' + expires.toUTCString(),\r\n    'domain=' + window.location.hostname,\r\n    window.location.protocol == 'https:' ? 'secure' : ''\r\n  ];\r\n\r\n  // set it\r\n  document.cookie = cookie.join('; ');\r\n\r\n  // close up the banner\r\n  closeCookieBanner();\r\n\r\n  // return\r\n  return false;\r\n\r\n};\r\n\r\n// find the form inside the banner\r\nvar $form = $cookie_banner.getElementsByTagName('form')[0];\r\n\r\n// hijack the submit event\r\n$form.addEventListener( 'submit', approveCookies, false );</code></pre>\n<p>The comments in the code should make it pretty clear, but just in case, here’s what I’m doing:</p>\n<ol><li>Hijack the form submission event (<code>e</code>) and cancel its default action using <code>e.preventDefault()</code>.</li>\n<li>Use the <code>Date</code> object to construct a date one year out.</li>\n<li>Assemble the bits of the cookie, including the <code>approves_cookies</code> value, the expiry date, the domain the cookie is bound to, and whether the cookie should be secure (so I can test locally).</li>\n<li>Set <code>document.cookie</code> equal to the assembled cookie string.</li>\n<li>Trigger a separate method — <code>closeCookieBanner()</code> — to close the banner (which I will cover in a moment).</li>\n</ol><p>With that in place, I can define <code>closeCookieBanner()</code> to handle, well, closing up the banner. There are actually two instances in which I need this functionality: after setting the cookie (as we just saw) and if the service worker serves up a stale page that still has the banner in it. Even though each requires roughly the same functionality, I want to make the stale-page cleanup version a little more aggressive. Here’s the code:</p>\n<pre><code>function closeCookieBanner( immediate ) {\r\n\r\n  // How fast to close? Animation takes .5s\r\n  var close_speed = immediate ? 0 : 600;\r\n\r\n  // remove\r\n  window.setTimeout(function(){\r\n\r\n    $cookie_banner.parentNode.removeChild( $cookie_banner );\r\n\r\n    // remove the DOM reference\r\n    $cookie_banner = null;\r\n\r\n  }, close_speed);\r\n\r\n  // animate closed\r\n  if ( ! immediate ) {\r\n    $cookie_banner.className = 'closing';\r\n  }\r\n\r\n}</code></pre>\n<p>This function takes a single optional argument. If <code>true</code> (or <a href=\"https://developer.mozilla.org/docs/Glossary/Truthy\">anything “truthy”</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#24\">24</a>) is passed in, the banner is immediately removed from the page (and its reference is deleted). If no argument is passed in, that doesn’t happen for 0.6 seconds, which is 0.1 seconds after the animation finishes up (we’ll get to the animation momentarily). The <code>class</code> change triggers that animation.</p>\n<p>You already saw one instance of this function referenced in the previous code block. Here it is in the cached template branch of the conditional you saw earlier:</p>\n<pre><code>…\r\n// banner exists but cookie is set\r\nif ( cookiesApproved() )\r\n{\r\n  // close immediately\r\n  closeCookieBanner( true );\r\n}\r\n…</code></pre>\n<h3>Adding Some Visual Sizzle <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#adding-some-visual-sizzle\">Link</a></h3>\n<p>Because I brought up animations, I’ll discuss the CSS I’m using for the cookie banner component, too. Like most implementations of cookie notices, I opted for a visual full-width banner. On small screens, I wanted the banner to appear above the content and push it down the page. On larger screens I opted to affix it to the top of the viewport because it would not obstruct reading to nearly the same degree as it would on a small screen. Accomplishing this involved very little code:</p>\n<pre><code>#cookie-banner {\r\n  background: #000;\r\n  color: #fff;\r\n  font-size: .875rem;\r\n  text-align: center;\r\n}\r\n\r\n@media (min-width: 60em) {\r\n  #cookie-banner {\r\n    position: fixed;\r\n    top: 0;\r\n    left: 0;\r\n    right: 0;\r\n    z-index: 1000;      \r\n  }\r\n}</code></pre>\n<p>Using the browser’s default styles, the cookie banner already displays <code>block</code>, so I didn’t really need to do much apart from set some basic text styles and colors. For the large screen (the “full-screen” version comes in at 60 ems), I affix it to the top of the screen using <code>position: fixed</code>, with a <code>top</code> offset of <code>0</code>. Setting its <code>left</code> and <code>right</code> offsets to <code>0</code> ensures it will always take up the full width of the viewport. I also set the <code>z-index</code> quite high so it sits on top of everything else in the stack.</p>\n<p>Here’s the result:</p>\n\nA video showing the browser viewport being enlarged from 240 to 960 pixels width. When the design hits the 60-em breakpoint, the banner becomes fixed-positioned.<p>Once the basic design was there, I took another pass to spice it up a bit. I decided to have the banner animate in and out using CSS. First things first: I created two animations. Initially, I tried to run a single animation in two directions for each state (opening and closing) but ran into problems triggering the reversal — you might be better at CSS animations than I am, so feel free to give it a shot. In the end, I also decided to tweak the two animations to be slightly different, so I’m fine with having two of them:</p>\n<pre><code>@keyframes cookie-banner {\r\n  0% {\r\n    max-height: 0;\r\n  }\r\n  100% {\r\n    max-height: 20em;\r\n  }\r\n}\r\n@keyframes cookie-banner-reverse {\r\n  0% {\r\n    max-height: 20em;\r\n  }\r\n  100% {\r\n    max-height: 0;\r\n    display: none;\r\n  }\r\n}</code></pre>\n<p>Not knowing how tall the banner would be (this is responsive design, after all), I needed it to animate to and from a <code>height</code> of <code>auto</code>. Thankfully, <a href=\"https://twitter.com/ELV1S\">Nikita Vasilyev</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#25\">25</a> published a fantastic overview of how to <a href=\"http://n12v.com/css-transition-to-from-auto/\">transition values to and from <code>auto</code></a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#26\">26</a> a few years back. In short, animate <code>max-height</code> instead. The only thing to keep in mind is that the size of the non-zero <code>max-height</code> value you are transitioning to and from needs to be larger than your max, and it will also directly affect the speed of the animation. I found 20 ems to be more than adequate for this use case, but your project may require a different value.</p>\n<p>It’s also worth noting that I used <code>display: none</code> at the conclusion of my <code>cookie-banner-reverse</code> animation (the closing one) to ensure the banner becomes unreachable to users of assistive technology such as screen readers. It’s probably unnecessary, but I did it as a failsafe just in case something happens and JavaScript doesn’t remove the banner from the DOM.</p>\n<p>Wiring it up required only a few minor tweaks to the CSS:</p>\n<pre><code>#cookie-banner {\r\n  …\r\n  box-sizing: border-box;\r\n  overflow: hidden;\r\n  animation: cookie-banner 1s 1s linear forwards;\r\n}\r\n\r\n#cookie-banner.closing {\r\n  animation: cookie-banner-reverse .5s linear forwards;\r\n}</code></pre>\n<p>This assigned the two animations to the two different banner states: The opening and resting state, <code>cookie-banner</code>, runs for one second after a one-second delay; the closing state, <code>cookie-banner-reverse</code>, runs for only half a second with no delay. I am using a class of <code>closing</code>, set via the JavaScript I showed earlier, to trigger the state change. Just for completeness, I’ll note that this code also stabilizes the dimensions of the banner with <code>box-sizing: border-box</code> and keeps the contents from spilling out of the banner using <code>overflow: hidden</code>.</p>\n<p>One last bit of CSS tweaking and we’re done. On small screens, I’m leaving a margin between the cookie notice (<code>#cookie-banner</code>) and the page header (<code>.banner</code>). I want that to go away when the banner collapses, even if the cookie notice is not removed from the DOM. I can accomplish that with an adjacent-sibling selector:</p>\n<pre><code>#cookie-banner + .banner {\r\n  transition: margin-top .5s;\r\n}\r\n\r\n#cookie-banner.closing + .banner {\r\n  margin-top: 0;\r\n}</code></pre>\n<p>It’s worth noting that I am setting the top margin on every element but the first one, using Heydon Pickering’s clever “<a href=\"http://alistapart.com/article/axiomatic-css-and-lobotomized-owls\">lobotomized owl</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#27\">27</a>” selector. So, the transition of <code>margin-top</code> on <code>.banner</code> will be from a specific value (in my case, <code>1.375 rem</code>) to <code>0</code>. With this code in place, the top margin will collapse over the same duration as the one used for the closing animation of the cookie banner and will be triggered by the very same class addition.</p>\n\nA video showing the final implementation on a wide screen, both with and without JavaScript.<h3>Simple, Robust, Resilient <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#simple-robust-resilient\">Link</a></h3>\n<p>What I like about this approach is that it is fairly simple. It took only about an hour or two to research and implement, and it checks all of the compliance boxes with respect to the EU law. It has minimal dependencies, offers several fallback options, cleans up after itself and is a relatively back-end-agnostic pattern.</p>\n<p>When tasked with adding features we may not like — and, yes, I’d count a persistent nagging banner as one of those features — it’s often tempting to throw some code at it to get it done and over with. <strong>JavaScript is often a handy tool</strong> to accomplish that, especially because the logic can often be self-contained in an external script, configured and forgotten. But there’s a risk in that approach: JavaScript is <a href=\"http://kryogenix.org/code/browser/everyonehasjs.html\">never guaranteed</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#28\">28</a>. If the feature is “nice to have,” you might be able to get away with it, but it’s probably not a good idea to play fast and loose with a legal mandate like this. Taking a few minutes to step back and explore how the feature can be implemented with minimal effort on all fronts will pay dividends down the road. <a href=\"https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979\">Believe me</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#29\">29</a>.</p>\n<p><em>(rb, vf, il, al)</em></p>\n<p><em>Front page image credit: <a href=\"https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/\">Pexels</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#30\">30</a>.</em></p>\n<ol><li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-1\">1 https://github.com/yui/yui3/wiki/Graded-Browser-Support</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-2\">2 http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-3\">3 https://webdevlaw.uk/category/eu-cookie-law/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-4\">4 https://developer.mozilla.org/docs/Web/API/Web_Storage_API</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-5\">5 https://developer.mozilla.org/docs/Web/API/IndexedDB_API</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-6\">6 http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-7\">7 https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-8\">8 https://a-k-apart.com/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-9\">9 https://developer.mozilla.org/docs/Web/API/Web_Storage_API</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-10\">10 https://wordpress.org/plugins/eu-cookie-law/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-11\">11 https://www.aaron-gustafson.com/notebook/interface-experience-maps/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-12\">12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-13\">13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-14\">14 https://developer.mozilla.org/docs/Web/API/Service_Worker_API</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-15\">15 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-16\">16 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-17\">17 https://nodejs.org/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-18\">18 http://expressjs.com/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-19\">19 https://github.com/expressjs/cookie-parser</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-20\">20 http://expressjs.com/en/guide/using-middleware.html</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-21\">21 https://mustache.github.io/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-22\">22 https://mustache.github.io/mustache.5.html#Partials</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-23\">23 https://mustache.github.io/mustache.5.html#Inverted-Sections</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-24\">24 https://developer.mozilla.org/docs/Glossary/Truthy</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-25\">25 https://twitter.com/ELV1S</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-26\">26 http://n12v.com/css-transition-to-from-auto/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-27\">27 http://alistapart.com/article/axiomatic-css-and-lobotomized-owls</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-28\">28 http://kryogenix.org/code/browser/everyonehasjs.html</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-29\">29 https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-30\">30 https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/</a></li>\n</ol><p>\n\t<a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#top\">↑ Back to top</a></p>\n<p>\t<a href=\"https://twitter.com/intent/tweet?original_referer=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/&amp;source=tweetbutton&amp;text=When%20Your%20Code%20Has%20To%20Work%3A%20Complying%20With%20Legal%20Mandates&amp;url=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/&amp;via=smashingmag\">Tweet it</a><a href=\"http://www.facebook.com/sharer/sharer.php?u=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/\">Share on Facebook</a></p>\n\n<p><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/\">Source link </a></p>","html":"<h1>\n\t\t\t\t\t\t\t\tWhen Your Code Has To Work: Complying With Legal Mandates\t\t\t\t\t\t\t\t</h1>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t<ul><li>\n\t\t\t\t<a class=\"dt-updated\" href=\"https://www.tech-chat.co.za/2017/03/03/\">03 March 2017</a>\n\t\t\t</li>\n\t\t\t\t\t\t\t<li class=\"h-card p-author h-card\">\n\t\t\t\t\t<i>by: </i>\n\t\t\t\t\t<a class=\"p-name u-url\" href=\"https://www.tech-chat.co.za/author/dumingu.m/\" title=\"View all posts by dumingu.m\">\n\t\t\t\t\t\tdumingu.m\t\t\t\t\t</a>\n\t\t\t\t</li>\n\t\t\t\n\t\t\t<li><i>in: </i><a href=\"https://www.tech-chat.co.za/category/coding/\">Coding</a>,<a href=\"https://www.tech-chat.co.za/category/javascript/\">JavaScript</a>,<a href=\"https://www.tech-chat.co.za/category/techniques/\">Techniques</a>,<a href=\"https://www.tech-chat.co.za/category/uncategorized/\">Uncategorized</a></li>\t\t\t\t <li>\n\t\t\t\t \t<i>note: </i>\n\t\t            <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#respond\" title=\"Click to leave a comment\">\n\t\t            \tno comments\t\t            </a>\n\t\t        </li>\n\t\t    </ul><p> </p>\n\n<p>Douglas Crockford famously declared browsers to be “the most hostile software engineering environment imaginable,” and that wasn’t hyperbole. <strong>Ensuring that our websites work across a myriad of different devices</strong>, screen sizes and browsers our users depend on to access the web is a tall order, but it’s necessary. If our websites don’t enable users to accomplish the key tasks they come to do, we’ve failed them.</p>\n<p>We should do everything in our power to ensure our websites function under even the harshest of scenarios, but at the same, we can’t expect our users to have the exact same experience in every browser, on every device. Yahoo realized this more than a decade ago and made it a central concept in its “<a href=\"https://github.com/yui/yui3/wiki/Graded-Browser-Support\">Graded Browser Support</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#1\">1</a>” strategy:</p>\n<blockquote>\n<p>Support does not mean that everybody gets the same thing. Expecting two users using different browser software to have an identical experience fails to embrace or acknowledge the heterogeneous essence of the Web. In fact, requiring the same experience for all users creates an artificial barrier to participation. Availability and accessibility of content should be our key priority.</p>\n</blockquote>\n<p>And that was a few years before the iPhone was introduced!</p>\n<p>Providing alternate experience pathways for our core functionality should be a no-brainer, but when it comes to implementing stuff we’d rather not think about, we often reach for the simplest drop-in solution, despite the potential negative impact it could have on our business.</p>\n<p>Consider the <a href=\"http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT\">EU’s “cookie law.”</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#2\">2</a> If you’re unfamiliar, this <a href=\"https://webdevlaw.uk/category/eu-cookie-law/\">somewhat contentious law</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#3\">3</a> is privacy legislation that requires websites to obtain consent from visitors before storing or retrieving information from their device. We call it the cookie law, but the legislation also applies to <a href=\"https://developer.mozilla.org/docs/Web/API/Web_Storage_API\">web storage</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#4\">4</a>, <a href=\"https://developer.mozilla.org/docs/Web/API/IndexedDB_API\">IndexedDB</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#5\">5</a> and other client-side data storage and retrieval APIs.</p>\n<p>Compliance with this law is achieved by:</p>\n<ol><li>notifying users that the website requires the ability to store and read information on their device;</li>\n<li>providing a link to the website’s privacy statement, which includes information about the storage mechanisms being used and what they are being used for;</li>\n<li>prompting users to confirm their acceptance of this requirement.</li>\n</ol><p>If you operate a website aimed at folks living in the EU and fail to do this, you could be subject to a substantial fine. You could even open yourself up to a lawsuit.</p>\n<p>If you’ve had to deal with the EU cookie law before, you’re probably keenly aware that a ton of “solutions” are available to provide compliance. Those quotation marks are fully intentional because nearly every one I found — including the <a href=\"http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4\">one provided by the EU</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#6\">6</a> itself — has been a drop-in JavaScript file that <em>enables</em> compliance. If we’re talking about the letter of the law, however, they don’t actually. The problem is that, as awesome and comprehensive as some of these solutions are, we can never be <a href=\"https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/\">guaranteed that our JavaScript programs will actually run</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#7\">7</a>. In order to truly comply with the letter of the law, we should provide a fallback version of the utility — just in case. Most people will never see it, but at least we know we’re covered if something goes wrong.</p>\n<p>I stumbled into this morass while building the <a href=\"https://a-k-apart.com/\">10k Apart contest website</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#8\">8</a>. We weren’t using cookies for much on the website — mainly analytics and vote-tracking — but we were using the <a href=\"https://developer.mozilla.org/docs/Web/API/Web_Storage_API\">Web Storage API</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#9\">9</a> to speed up the performance of the website and to save form data temporarily while folks were filling out the form. Because the contest was open to folks who live in the EU, we needed to abide by the cookie law. And because none of the solutions I found actually complied with the law in either spirit or reality — the notable exception being WordPress’ <a href=\"https://wordpress.org/plugins/eu-cookie-law/\">EU Cookie Law</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#10\">10</a> plugin, which works both with and without JavaScript, but the contest website wasn’t built in WordPress or even PHP, so I had to do something else — I opted to roll my own robust solution.</p>\n<h3>Planning It Out <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#planning-it-out\">Link</a></h3>\n<p>I’m a big fan of using <a href=\"https://www.aaron-gustafson.com/notebook/interface-experience-maps/\">interface experience (IX) maps</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#11\">11</a> to diagram functionality. I find their simple nature easy to understand and to tweak as I increase the fidelity of an experience. For this feature, I started with a (relatively) simple IX map that diagrammed what would happen when a user requests a page on the website.</p>\n<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png\"><img alt=\"IX map for the basic interaction and fallback.\" height=\"530\" src=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png\" width=\"800\" /></a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#12\">12</a>IX map for the basic interaction and fallback. (<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png\">View large version</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#13\">13</a>)<p>This IX map outlines several potential experiences that vary based on the user’s choice and feature availability. I’ll walk through the ideal scenario first:</p>\n<ol><li>A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything.</li>\n<li>The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance.</li>\n<li>The browser renders the page with the banner.</li>\n<li>The user clicks to accept the use of cookies and web storage.</li>\n<li>Client-side JavaScript sets the <code>accepts</code> cookie and closes the banner.</li>\n<li>On subsequent page requests, the server reads the <code>accepts</code> cookie and does not inject the banner code. JavaScript sees the cookie and enables the cookie and web storage code.</li>\n</ol><p>For the vast majority of users, this is the experience they’ll get, and that’s awesome. That said, however, we can never be 100% guaranteed our client-side JavaScript code will run, so we need a backup plan. Here’s the fallback experience:</p>\n<ol><li>A user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything.</li>\n<li>The server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance.</li>\n<li>The browser renders the page with the banner.</li>\n<li>The user clicks to accept the use of cookies and web storage.</li>\n<li>The click initiates a form post to the server, which responds by setting the <code>accepts</code> cookie before redirecting the user back to the page they were on.</li>\n<li>On subsequent page requests, the server reads the <code>accepts</code> cookie and does not inject the banner code.</li>\n<li>If JavaScript becomes available later, it will see the cookie and enable its cookie and web storage code.</li>\n</ol><p>Not bad. There’s an extra roundtrip to the server, but it’s a quick one, and, more importantly, it provides a foolproof fallback in the absence of our preferred JavaScript-driven option. True, it could fall victim to a networking issue, but there’s not much we can do to mitigate that without JavaScript in play.</p>\n<p>Speaking of mitigating networking issues, the 10k Apart contest website uses a <a href=\"https://developer.mozilla.org/docs/Web/API/Service_Worker_API\">service worker</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#14\">14</a> to do some pretty aggressive caching; the service worker intercepts any page request and supplies a cached version if one exists. That <em>could</em> result in users getting a copy of the page with the banner still in it, even if they’ve already agreed to allow cookies. Time to update the IX map.</p>\n<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png\"><img alt=\"IX map for the basic interaction and fallback.\" height=\"461\" src=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png\" width=\"800\" /></a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#15\">15</a>The original IX map, adjusted to handle cached pages. (<a href=\"https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png\">View large version</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#16\">16</a>)<p>This is one of the reasons I like IX maps so much: They are really easy to generate and simple to update when you want to add features or handle more scenarios. With a few adjustments in place, I can account for the scenario in which a stale page includes the banner unnecessarily and have JavaScript remove it.</p>\n<p>With this plan in place, it was time to implement it.</p>\n<h3>Server-Side Implementation <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#server-side-implementation\">Link</a></h3>\n<p>10k Apart’s back end is written in <a href=\"https://nodejs.org/\">Node.js</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#17\">17</a> and uses <a href=\"http://expressjs.com/\">Express</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#18\">18</a>. I’m not going to get into the nitty-gritty of our installation and configuration, but I do want to talk about how I implemented this feature. First off, I opted to use <a href=\"https://github.com/expressjs/cookie-parser\">Express’ cookie-parser</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#19\">19</a> middleware to let me get and set the cookie.</p>\n<pre><code>// enable cookie-parser for Express\r\nvar cookieParser = require('cookie-parser');\r\napp.use(cookieParser());</code></pre>\n<p>Once that was set up, I created my own <a href=\"http://expressjs.com/en/guide/using-middleware.html\">custom Express middleware</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#20\">20</a> that would intercept requests and check for the <code>approves_cookies</code> cookie:</p>\n<pre><code>var checkCookie = function(req, res, next) {\r\n  res.locals.approves_cookies = ( req.cookies['approves_cookies'] === 'yes' );\r\n  res.locals.current_url = req.url || '/';\r\n  next();\r\n};</code></pre>\n<p>This code establishes a middleware function named <code>checkCookie()</code>. All Express middleware gets access to the request (<code>req</code>), the response (<code>res</code>) and the next middleware function (<code>next</code>), so you’ll see those accounted for as the three arguments to that function. Then, within the function, I am modifying the response object to include two local variables (<code>res.locals</code>) to capture whether the cookie has already been set (<code>res.locals.approves_cookies</code>) and the currently requested URL (<code>res.locals.current_url</code>). Then, I call the next middleware function.</p>\n<p>With that written, I can include this middleware in Express:</p>\n<pre><code>app.use(checkCookie);</code></pre>\n<p>All of the templates for the website are <a href=\"https://mustache.github.io/\">Mustache</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#21\">21</a> files, and Express automatically pipes <code>res.locals</code> into those templates. Knowing that, I created a <a href=\"https://mustache.github.io/mustache.5.html#Partials\">Mustache partial</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#22\">22</a> to handle the banner:</p>\n<pre><code>{{^approves_cookies}}\r\n  &lt;div id=\"cookie-banner\" role=\"alert\"&gt;\r\n    &lt;form action=\"/cookies-ok\" method=\"post\"&gt;\r\n      &lt;input type=\"hidden\" name=\"redirect_to\" value=\"{{current_url}}\"&gt;\r\n      &lt;p&gt;This site uses cookies for analytics and to track voting. If you're interested, more details can be found in &lt;a href=\"{{privacy_url}}#maincookiessimilartechnologiesmodule\"&gt;our cookie policy&lt;/a&gt;.&lt;/p&gt;\r\n      &lt;button type=\"submit\"&gt;I'm cool with that&lt;/button&gt;\r\n    &lt;/form&gt;\r\n  &lt;/div&gt;\r\n{{/approves_cookies}}</code></pre>\n<p>This template uses an <a href=\"https://mustache.github.io/mustache.5.html#Inverted-Sections\">inverted section</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#23\">23</a> that only renders the <code>div</code> when <code>approves_cookies</code> is false. Within that markup, you can also see the <code>current_url</code> getting piped into a hidden <code>input</code> to indicate where a user should be redirected if the form method of setting the cookie is used. You remembered: the fallback.</p>\n<p>Speaking of the fallback, since we have one, we also need to handle that on the server side. Here’s the Node.js code for that:</p>\n<pre><code>var affirmCookies = function (req, res) {\r\n  if ( ! req.cookies['approves_cookies'] )\r\n  {\r\n    res.cookie('approves_cookies', 'yes', {\r\n      secure: true,\r\n      maxAge: ( 365 * 24 * 60 * 60 ) // 1 year\r\n    });\r\n  }\r\n  res.redirect(req.body.redirect_to);\r\n};\r\napp.post('/cookies-ok', affirmCookies);</code></pre>\n<p>This ensures that if the form is submitted, Express will respond by setting the <code>approves_cookies</code> cookie (if it’s not already set) and then redirecting the user to the page they were on. Taken altogether, this gives us a solid baseline experience for every user.</p>\n<p>Now, it’s worth noting that none of this code is going to be useful to you if your projects don’t involve the specific stack I was working with on this project (Node.js, Express, Mustache). That said, the logic I’ve outlined here and in the IX map is portable to pretty much any language or framework you happen to know and love.</p>\n<p>OK, let’s switch gears and work some magic on the front end.</p>\n<h3>Front-End Implementation <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#front-end-implementation\">Link</a></h3>\n<p>When JavaScript is available and running properly, we’ll want to take full advantage of it, but it doesn’t make sense to run any code against the banner if it doesn’t exist, so first things first: I should check to see whether the banner is even in the page.</p>\n<pre><code>var $cookie_banner = document.getElementById('cookie-banner');\r\n\r\nif ( $cookie_banner )\r\n{\r\n  // actual code will go here\r\n}</code></pre>\n<p>In order to streamline the application logic, I’m going to add another conditional within to check for the <code>accepts_cookies</code> cookie. I know from my second pass on the IX map there’s an outside chance that the banner might be served up by my service worker even if the <code>accepts</code> cookie exists, so checking for the cookie early lets me run only the bit of JavaScript that removes the banner. But before I jump into all of that, I’ll create a function I can call in any of my code to let me know whether the user has agreed to let me cookie them:</p>\n<pre><code>function cookiesApproved(){\r\n  return document.cookie.indexOf('approves_cookies') &gt; -1;\r\n}</code></pre>\n<p>I need this check in multiple places throughout my JavaScript, so it makes sense to break it out into a separate function. Now, let’s revisit my banner-handling logic:</p>\n<pre><code>var $cookie_banner = document.getElementById('cookie-banner');\r\n\r\nif ( $cookie_banner )\r\n{\r\n\r\n  // banner exists but cookie is set\r\n  if ( cookiesApproved() )\r\n  {\r\n    // hide the banner immediately!\r\n  }\r\n  // cookie has not been set \r\n  else\r\n  {\r\n    // add the logic to set the cookie\r\n    // and close the banner\r\n  }\r\n\r\n}</code></pre>\n<p>Setting cookies in JavaScript is a little convoluted because you need to set it as a string, but it’s not too ghastly. I broke out the process into its own function so that I could set it as an event handler on the form:</p>\n<pre><code>function approveCookies( e ) {\r\n\r\n  // prevent the form from submitting\r\n  e.preventDefault();\r\n\r\n  var cookie,               // placeholder for the cookie\r\n      expires = new Date(); // start building expiry date\r\n\r\n  // expire in one year\r\n  expires.setFullYear( expires.getFullYear() + 1 );\r\n\r\n  // build the cookie\r\n  cookie = [\r\n    'approves_cookies=yes',\r\n    'expires=' + expires.toUTCString(),\r\n    'domain=' + window.location.hostname,\r\n    window.location.protocol == 'https:' ? 'secure' : ''\r\n  ];\r\n\r\n  // set it\r\n  document.cookie = cookie.join('; ');\r\n\r\n  // close up the banner\r\n  closeCookieBanner();\r\n\r\n  // return\r\n  return false;\r\n\r\n};\r\n\r\n// find the form inside the banner\r\nvar $form = $cookie_banner.getElementsByTagName('form')[0];\r\n\r\n// hijack the submit event\r\n$form.addEventListener( 'submit', approveCookies, false );</code></pre>\n<p>The comments in the code should make it pretty clear, but just in case, here’s what I’m doing:</p>\n<ol><li>Hijack the form submission event (<code>e</code>) and cancel its default action using <code>e.preventDefault()</code>.</li>\n<li>Use the <code>Date</code> object to construct a date one year out.</li>\n<li>Assemble the bits of the cookie, including the <code>approves_cookies</code> value, the expiry date, the domain the cookie is bound to, and whether the cookie should be secure (so I can test locally).</li>\n<li>Set <code>document.cookie</code> equal to the assembled cookie string.</li>\n<li>Trigger a separate method — <code>closeCookieBanner()</code> — to close the banner (which I will cover in a moment).</li>\n</ol><p>With that in place, I can define <code>closeCookieBanner()</code> to handle, well, closing up the banner. There are actually two instances in which I need this functionality: after setting the cookie (as we just saw) and if the service worker serves up a stale page that still has the banner in it. Even though each requires roughly the same functionality, I want to make the stale-page cleanup version a little more aggressive. Here’s the code:</p>\n<pre><code>function closeCookieBanner( immediate ) {\r\n\r\n  // How fast to close? Animation takes .5s\r\n  var close_speed = immediate ? 0 : 600;\r\n\r\n  // remove\r\n  window.setTimeout(function(){\r\n\r\n    $cookie_banner.parentNode.removeChild( $cookie_banner );\r\n\r\n    // remove the DOM reference\r\n    $cookie_banner = null;\r\n\r\n  }, close_speed);\r\n\r\n  // animate closed\r\n  if ( ! immediate ) {\r\n    $cookie_banner.className = 'closing';\r\n  }\r\n\r\n}</code></pre>\n<p>This function takes a single optional argument. If <code>true</code> (or <a href=\"https://developer.mozilla.org/docs/Glossary/Truthy\">anything “truthy”</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#24\">24</a>) is passed in, the banner is immediately removed from the page (and its reference is deleted). If no argument is passed in, that doesn’t happen for 0.6 seconds, which is 0.1 seconds after the animation finishes up (we’ll get to the animation momentarily). The <code>class</code> change triggers that animation.</p>\n<p>You already saw one instance of this function referenced in the previous code block. Here it is in the cached template branch of the conditional you saw earlier:</p>\n<pre><code>…\r\n// banner exists but cookie is set\r\nif ( cookiesApproved() )\r\n{\r\n  // close immediately\r\n  closeCookieBanner( true );\r\n}\r\n…</code></pre>\n<h3>Adding Some Visual Sizzle <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#adding-some-visual-sizzle\">Link</a></h3>\n<p>Because I brought up animations, I’ll discuss the CSS I’m using for the cookie banner component, too. Like most implementations of cookie notices, I opted for a visual full-width banner. On small screens, I wanted the banner to appear above the content and push it down the page. On larger screens I opted to affix it to the top of the viewport because it would not obstruct reading to nearly the same degree as it would on a small screen. Accomplishing this involved very little code:</p>\n<pre><code>#cookie-banner {\r\n  background: #000;\r\n  color: #fff;\r\n  font-size: .875rem;\r\n  text-align: center;\r\n}\r\n\r\n@media (min-width: 60em) {\r\n  #cookie-banner {\r\n    position: fixed;\r\n    top: 0;\r\n    left: 0;\r\n    right: 0;\r\n    z-index: 1000;      \r\n  }\r\n}</code></pre>\n<p>Using the browser’s default styles, the cookie banner already displays <code>block</code>, so I didn’t really need to do much apart from set some basic text styles and colors. For the large screen (the “full-screen” version comes in at 60 ems), I affix it to the top of the screen using <code>position: fixed</code>, with a <code>top</code> offset of <code>0</code>. Setting its <code>left</code> and <code>right</code> offsets to <code>0</code> ensures it will always take up the full width of the viewport. I also set the <code>z-index</code> quite high so it sits on top of everything else in the stack.</p>\n<p>Here’s the result:</p>\n\nA video showing the browser viewport being enlarged from 240 to 960 pixels width. When the design hits the 60-em breakpoint, the banner becomes fixed-positioned.<p>Once the basic design was there, I took another pass to spice it up a bit. I decided to have the banner animate in and out using CSS. First things first: I created two animations. Initially, I tried to run a single animation in two directions for each state (opening and closing) but ran into problems triggering the reversal — you might be better at CSS animations than I am, so feel free to give it a shot. In the end, I also decided to tweak the two animations to be slightly different, so I’m fine with having two of them:</p>\n<pre><code>@keyframes cookie-banner {\r\n  0% {\r\n    max-height: 0;\r\n  }\r\n  100% {\r\n    max-height: 20em;\r\n  }\r\n}\r\n@keyframes cookie-banner-reverse {\r\n  0% {\r\n    max-height: 20em;\r\n  }\r\n  100% {\r\n    max-height: 0;\r\n    display: none;\r\n  }\r\n}</code></pre>\n<p>Not knowing how tall the banner would be (this is responsive design, after all), I needed it to animate to and from a <code>height</code> of <code>auto</code>. Thankfully, <a href=\"https://twitter.com/ELV1S\">Nikita Vasilyev</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#25\">25</a> published a fantastic overview of how to <a href=\"http://n12v.com/css-transition-to-from-auto/\">transition values to and from <code>auto</code></a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#26\">26</a> a few years back. In short, animate <code>max-height</code> instead. The only thing to keep in mind is that the size of the non-zero <code>max-height</code> value you are transitioning to and from needs to be larger than your max, and it will also directly affect the speed of the animation. I found 20 ems to be more than adequate for this use case, but your project may require a different value.</p>\n<p>It’s also worth noting that I used <code>display: none</code> at the conclusion of my <code>cookie-banner-reverse</code> animation (the closing one) to ensure the banner becomes unreachable to users of assistive technology such as screen readers. It’s probably unnecessary, but I did it as a failsafe just in case something happens and JavaScript doesn’t remove the banner from the DOM.</p>\n<p>Wiring it up required only a few minor tweaks to the CSS:</p>\n<pre><code>#cookie-banner {\r\n  …\r\n  box-sizing: border-box;\r\n  overflow: hidden;\r\n  animation: cookie-banner 1s 1s linear forwards;\r\n}\r\n\r\n#cookie-banner.closing {\r\n  animation: cookie-banner-reverse .5s linear forwards;\r\n}</code></pre>\n<p>This assigned the two animations to the two different banner states: The opening and resting state, <code>cookie-banner</code>, runs for one second after a one-second delay; the closing state, <code>cookie-banner-reverse</code>, runs for only half a second with no delay. I am using a class of <code>closing</code>, set via the JavaScript I showed earlier, to trigger the state change. Just for completeness, I’ll note that this code also stabilizes the dimensions of the banner with <code>box-sizing: border-box</code> and keeps the contents from spilling out of the banner using <code>overflow: hidden</code>.</p>\n<p>One last bit of CSS tweaking and we’re done. On small screens, I’m leaving a margin between the cookie notice (<code>#cookie-banner</code>) and the page header (<code>.banner</code>). I want that to go away when the banner collapses, even if the cookie notice is not removed from the DOM. I can accomplish that with an adjacent-sibling selector:</p>\n<pre><code>#cookie-banner + .banner {\r\n  transition: margin-top .5s;\r\n}\r\n\r\n#cookie-banner.closing + .banner {\r\n  margin-top: 0;\r\n}</code></pre>\n<p>It’s worth noting that I am setting the top margin on every element but the first one, using Heydon Pickering’s clever “<a href=\"http://alistapart.com/article/axiomatic-css-and-lobotomized-owls\">lobotomized owl</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#27\">27</a>” selector. So, the transition of <code>margin-top</code> on <code>.banner</code> will be from a specific value (in my case, <code>1.375 rem</code>) to <code>0</code>. With this code in place, the top margin will collapse over the same duration as the one used for the closing animation of the cookie banner and will be triggered by the very same class addition.</p>\n\nA video showing the final implementation on a wide screen, both with and without JavaScript.<h3>Simple, Robust, Resilient <a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#simple-robust-resilient\">Link</a></h3>\n<p>What I like about this approach is that it is fairly simple. It took only about an hour or two to research and implement, and it checks all of the compliance boxes with respect to the EU law. It has minimal dependencies, offers several fallback options, cleans up after itself and is a relatively back-end-agnostic pattern.</p>\n<p>When tasked with adding features we may not like — and, yes, I’d count a persistent nagging banner as one of those features — it’s often tempting to throw some code at it to get it done and over with. <strong>JavaScript is often a handy tool</strong> to accomplish that, especially because the logic can often be self-contained in an external script, configured and forgotten. But there’s a risk in that approach: JavaScript is <a href=\"http://kryogenix.org/code/browser/everyonehasjs.html\">never guaranteed</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#28\">28</a>. If the feature is “nice to have,” you might be able to get away with it, but it’s probably not a good idea to play fast and loose with a legal mandate like this. Taking a few minutes to step back and explore how the feature can be implemented with minimal effort on all fronts will pay dividends down the road. <a href=\"https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979\">Believe me</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#29\">29</a>.</p>\n<p><em>(rb, vf, il, al)</em></p>\n<p><em>Front page image credit: <a href=\"https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/\">Pexels</a><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#30\">30</a>.</em></p>\n<ol><li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-1\">1 https://github.com/yui/yui3/wiki/Graded-Browser-Support</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-2\">2 http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-3\">3 https://webdevlaw.uk/category/eu-cookie-law/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-4\">4 https://developer.mozilla.org/docs/Web/API/Web_Storage_API</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-5\">5 https://developer.mozilla.org/docs/Web/API/IndexedDB_API</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-6\">6 http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-7\">7 https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-8\">8 https://a-k-apart.com/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-9\">9 https://developer.mozilla.org/docs/Web/API/Web_Storage_API</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-10\">10 https://wordpress.org/plugins/eu-cookie-law/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-11\">11 https://www.aaron-gustafson.com/notebook/interface-experience-maps/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-12\">12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-13\">13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-14\">14 https://developer.mozilla.org/docs/Web/API/Service_Worker_API</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-15\">15 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-16\">16 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-17\">17 https://nodejs.org/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-18\">18 http://expressjs.com/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-19\">19 https://github.com/expressjs/cookie-parser</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-20\">20 http://expressjs.com/en/guide/using-middleware.html</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-21\">21 https://mustache.github.io/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-22\">22 https://mustache.github.io/mustache.5.html#Partials</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-23\">23 https://mustache.github.io/mustache.5.html#Inverted-Sections</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-24\">24 https://developer.mozilla.org/docs/Glossary/Truthy</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-25\">25 https://twitter.com/ELV1S</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-26\">26 http://n12v.com/css-transition-to-from-auto/</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-27\">27 http://alistapart.com/article/axiomatic-css-and-lobotomized-owls</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-28\">28 http://kryogenix.org/code/browser/everyonehasjs.html</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-29\">29 https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979</a></li>\n<li><a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#note-30\">30 https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/</a></li>\n</ol><p>\n\t<a href=\"https://www.tech-chat.co.za/2017/03/03/when-your-code-has-to-work-complying-with-legal-mandates/#top\">↑ Back to top</a></p>\n<p>\t<a href=\"https://twitter.com/intent/tweet?original_referer=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/&amp;source=tweetbutton&amp;text=When%20Your%20Code%20Has%20To%20Work%3A%20Complying%20With%20Legal%20Mandates&amp;url=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/&amp;via=smashingmag\">Tweet it</a><a href=\"http://www.facebook.com/sharer/sharer.php?u=https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/\">Share on Facebook</a></p>\n\n<p><a href=\"https://www.smashingmagazine.com/2017/03/code-complying-with-legal-mandates/\">Source link </a></p>","text":"When Your Code Has To Work: Complying With Legal Mandates\t\t\t\t\t\t\t\t \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\n\t\t\t\t03 March 2017\n\t\t\t \n\t\t\t\t\t\t\t\n\t\t\t\t\tby: \n\t\t\t\t\t\n\t\t\t\t\t\tdumingu.m\t\t\t\t\t\n\t\t\t\t \n\t\t\t\n\t\t\tin: Coding,JavaScript,Techniques,Uncategorized \t\t\t\t \n\t\t\t\t \tnote: \n\t\t            \n\t\t            \tno comments\t\t            \n\t\t         \n\t\t       \n \n\nDouglas Crockford famously declared browsers to be “the most hostile software engineering environment imaginable,” and that wasn’t hyperbole. Ensuring that our websites work across a myriad of different devices, screen sizes and browsers our users depend on to access the web is a tall order, but it’s necessary. If our websites don’t enable users to accomplish the key tasks they come to do, we’ve failed them. \nWe should do everything in our power to ensure our websites function under even the harshest of scenarios, but at the same, we can’t expect our users to have the exact same experience in every browser, on every device. Yahoo realized this more than a decade ago and made it a central concept in its “Graded Browser Support1” strategy: \n\nSupport does not mean that everybody gets the same thing. Expecting two users using different browser software to have an identical experience fails to embrace or acknowledge the heterogeneous essence of the Web. In fact, requiring the same experience for all users creates an artificial barrier to participation. Availability and accessibility of content should be our key priority. \n \nAnd that was a few years before the iPhone was introduced! \nProviding alternate experience pathways for our core functionality should be a no-brainer, but when it comes to implementing stuff we’d rather not think about, we often reach for the simplest drop-in solution, despite the potential negative impact it could have on our business. \nConsider the EU’s “cookie law.”2 If you’re unfamiliar, this somewhat contentious law3 is privacy legislation that requires websites to obtain consent from visitors before storing or retrieving information from their device. We call it the cookie law, but the legislation also applies to web storage4, IndexedDB5 and other client-side data storage and retrieval APIs. \nCompliance with this law is achieved by: \nnotifying users that the website requires the ability to store and read information on their device; \nproviding a link to the website’s privacy statement, which includes information about the storage mechanisms being used and what they are being used for; \nprompting users to confirm their acceptance of this requirement. \n If you operate a website aimed at folks living in the EU and fail to do this, you could be subject to a substantial fine. You could even open yourself up to a lawsuit. \nIf you’ve had to deal with the EU cookie law before, you’re probably keenly aware that a ton of “solutions” are available to provide compliance. Those quotation marks are fully intentional because nearly every one I found — including the one provided by the EU6 itself — has been a drop-in JavaScript file that enables compliance. If we’re talking about the letter of the law, however, they don’t actually. The problem is that, as awesome and comprehensive as some of these solutions are, we can never be guaranteed that our JavaScript programs will actually run7. In order to truly comply with the letter of the law, we should provide a fallback version of the utility — just in case. Most people will never see it, but at least we know we’re covered if something goes wrong. \nI stumbled into this morass while building the 10k Apart contest website8. We weren’t using cookies for much on the website — mainly analytics and vote-tracking — but we were using the Web Storage API9 to speed up the performance of the website and to save form data temporarily while folks were filling out the form. Because the contest was open to folks who live in the EU, we needed to abide by the cookie law. And because none of the solutions I found actually complied with the law in either spirit or reality — the notable exception being WordPress’ EU Cookie Law10 plugin, which works both with and without JavaScript, but the contest website wasn’t built in WordPress or even PHP, so I had to do something else — I opted to roll my own robust solution. \nPlanning It Out Link \nI’m a big fan of using interface experience (IX) maps11 to diagram functionality. I find their simple nature easy to understand and to tweak as I increase the fidelity of an experience. For this feature, I started with a (relatively) simple IX map that diagrammed what would happen when a user requests a page on the website. \nIX map for the basic interaction and fallback.12IX map for the basic interaction and fallback. (View large version13)  This IX map outlines several potential experiences that vary based on the user’s choice and feature availability. I’ll walk through the ideal scenario first: \nA user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything. \nThe server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance. \nThe browser renders the page with the banner. \nThe user clicks to accept the use of cookies and web storage. \nClient-side JavaScript sets the accepts cookie and closes the banner. \nOn subsequent page requests, the server reads the accepts cookie and does not inject the banner code. JavaScript sees the cookie and enables the cookie and web storage code. \n For the vast majority of users, this is the experience they’ll get, and that’s awesome. That said, however, we can never be 100% guaranteed our client-side JavaScript code will run, so we need a backup plan. Here’s the fallback experience: \nA user comes to the website for the first time. The server checks to see whether they have accepted the use of cookies and web storage but doesn’t find anything. \nThe server injects a banner into the HTML, containing the necessary messaging and a form that, when submitted, confirms acceptance. \nThe browser renders the page with the banner. \nThe user clicks to accept the use of cookies and web storage. \nThe click initiates a form post to the server, which responds by setting the accepts cookie before redirecting the user back to the page they were on. \nOn subsequent page requests, the server reads the accepts cookie and does not inject the banner code. \nIf JavaScript becomes available later, it will see the cookie and enable its cookie and web storage code. \n Not bad. There’s an extra roundtrip to the server, but it’s a quick one, and, more importantly, it provides a foolproof fallback in the absence of our preferred JavaScript-driven option. True, it could fall victim to a networking issue, but there’s not much we can do to mitigate that without JavaScript in play. \nSpeaking of mitigating networking issues, the 10k Apart contest website uses a service worker14 to do some pretty aggressive caching; the service worker intercepts any page request and supplies a cached version if one exists. That could result in users getting a copy of the page with the banner still in it, even if they’ve already agreed to allow cookies. Time to update the IX map. \nIX map for the basic interaction and fallback.15The original IX map, adjusted to handle cached pages. (View large version16)  This is one of the reasons I like IX maps so much: They are really easy to generate and simple to update when you want to add features or handle more scenarios. With a few adjustments in place, I can account for the scenario in which a stale page includes the banner unnecessarily and have JavaScript remove it. \nWith this plan in place, it was time to implement it. \nServer-Side Implementation Link \n10k Apart’s back end is written in Node.js17 and uses Express18. I’m not going to get into the nitty-gritty of our installation and configuration, but I do want to talk about how I implemented this feature. First off, I opted to use Express’ cookie-parser19 middleware to let me get and set the cookie. \n// enable cookie-parser for Express\r\nvar cookieParser = require('cookie-parser');\r\napp.use(cookieParser()); \nOnce that was set up, I created my own custom Express middleware20 that would intercept requests and check for the approves_cookies cookie: \nvar checkCookie = function(req, res, next) {\r\n  res.locals.approves_cookies = ( req.cookies['approves_cookies'] === 'yes' );\r\n  res.locals.current_url = req.url || '/';\r\n  next();\r\n}; \nThis code establishes a middleware function named checkCookie(). All Express middleware gets access to the request (req), the response (res) and the next middleware function (next), so you’ll see those accounted for as the three arguments to that function. Then, within the function, I am modifying the response object to include two local variables (res.locals) to capture whether the cookie has already been set (res.locals.approves_cookies) and the currently requested URL (res.locals.current_url). Then, I call the next middleware function. \nWith that written, I can include this middleware in Express: \napp.use(checkCookie); \nAll of the templates for the website are Mustache21 files, and Express automatically pipes res.locals into those templates. Knowing that, I created a Mustache partial22 to handle the banner: \n{{^approves_cookies}}\r\n  <div id=\"cookie-banner\" role=\"alert\">\r\n    <form action=\"/cookies-ok\" method=\"post\">\r\n      <input type=\"hidden\" name=\"redirect_to\" value=\"{{current_url}}\">\r\n      <p>This site uses cookies for analytics and to track voting. If you're interested, more details can be found in <a href=\"{{privacy_url}}#maincookiessimilartechnologiesmodule\">our cookie policy</a>.</p>\r\n      <button type=\"submit\">I'm cool with that</button>\r\n    </form>\r\n  </div>\r\n{{/approves_cookies}} \nThis template uses an inverted section23 that only renders the div when approves_cookies is false. Within that markup, you can also see the current_url getting piped into a hidden input to indicate where a user should be redirected if the form method of setting the cookie is used. You remembered: the fallback. \nSpeaking of the fallback, since we have one, we also need to handle that on the server side. Here’s the Node.js code for that: \nvar affirmCookies = function (req, res) {\r\n  if ( ! req.cookies['approves_cookies'] )\r\n  {\r\n    res.cookie('approves_cookies', 'yes', {\r\n      secure: true,\r\n      maxAge: ( 365 * 24 * 60 * 60 ) // 1 year\r\n    });\r\n  }\r\n  res.redirect(req.body.redirect_to);\r\n};\r\napp.post('/cookies-ok', affirmCookies); \nThis ensures that if the form is submitted, Express will respond by setting the approves_cookies cookie (if it’s not already set) and then redirecting the user to the page they were on. Taken altogether, this gives us a solid baseline experience for every user. \nNow, it’s worth noting that none of this code is going to be useful to you if your projects don’t involve the specific stack I was working with on this project (Node.js, Express, Mustache). That said, the logic I’ve outlined here and in the IX map is portable to pretty much any language or framework you happen to know and love. \nOK, let’s switch gears and work some magic on the front end. \nFront-End Implementation Link \nWhen JavaScript is available and running properly, we’ll want to take full advantage of it, but it doesn’t make sense to run any code against the banner if it doesn’t exist, so first things first: I should check to see whether the banner is even in the page. \nvar $cookie_banner = document.getElementById('cookie-banner');\r\n\r\nif ( $cookie_banner )\r\n{\r\n  // actual code will go here\r\n} \nIn order to streamline the application logic, I’m going to add another conditional within to check for the accepts_cookies cookie. I know from my second pass on the IX map there’s an outside chance that the banner might be served up by my service worker even if the accepts cookie exists, so checking for the cookie early lets me run only the bit of JavaScript that removes the banner. But before I jump into all of that, I’ll create a function I can call in any of my code to let me know whether the user has agreed to let me cookie them: \nfunction cookiesApproved(){\r\n  return document.cookie.indexOf('approves_cookies') > -1;\r\n} \nI need this check in multiple places throughout my JavaScript, so it makes sense to break it out into a separate function. Now, let’s revisit my banner-handling logic: \nvar $cookie_banner = document.getElementById('cookie-banner');\r\n\r\nif ( $cookie_banner )\r\n{\r\n\r\n  // banner exists but cookie is set\r\n  if ( cookiesApproved() )\r\n  {\r\n    // hide the banner immediately!\r\n  }\r\n  // cookie has not been set \r\n  else\r\n  {\r\n    // add the logic to set the cookie\r\n    // and close the banner\r\n  }\r\n\r\n} \nSetting cookies in JavaScript is a little convoluted because you need to set it as a string, but it’s not too ghastly. I broke out the process into its own function so that I could set it as an event handler on the form: \nfunction approveCookies( e ) {\r\n\r\n  // prevent the form from submitting\r\n  e.preventDefault();\r\n\r\n  var cookie,               // placeholder for the cookie\r\n      expires = new Date(); // start building expiry date\r\n\r\n  // expire in one year\r\n  expires.setFullYear( expires.getFullYear() + 1 );\r\n\r\n  // build the cookie\r\n  cookie = [\r\n    'approves_cookies=yes',\r\n    'expires=' + expires.toUTCString(),\r\n    'domain=' + window.location.hostname,\r\n    window.location.protocol == 'https:' ? 'secure' : ''\r\n  ];\r\n\r\n  // set it\r\n  document.cookie = cookie.join('; ');\r\n\r\n  // close up the banner\r\n  closeCookieBanner();\r\n\r\n  // return\r\n  return false;\r\n\r\n};\r\n\r\n// find the form inside the banner\r\nvar $form = $cookie_banner.getElementsByTagName('form')[0];\r\n\r\n// hijack the submit event\r\n$form.addEventListener( 'submit', approveCookies, false ); \nThe comments in the code should make it pretty clear, but just in case, here’s what I’m doing: \nHijack the form submission event (e) and cancel its default action using e.preventDefault(). \nUse the Date object to construct a date one year out. \nAssemble the bits of the cookie, including the approves_cookies value, the expiry date, the domain the cookie is bound to, and whether the cookie should be secure (so I can test locally). \nSet document.cookie equal to the assembled cookie string. \nTrigger a separate method — closeCookieBanner() — to close the banner (which I will cover in a moment). \n With that in place, I can define closeCookieBanner() to handle, well, closing up the banner. There are actually two instances in which I need this functionality: after setting the cookie (as we just saw) and if the service worker serves up a stale page that still has the banner in it. Even though each requires roughly the same functionality, I want to make the stale-page cleanup version a little more aggressive. Here’s the code: \nfunction closeCookieBanner( immediate ) {\r\n\r\n  // How fast to close? Animation takes .5s\r\n  var close_speed = immediate ? 0 : 600;\r\n\r\n  // remove\r\n  window.setTimeout(function(){\r\n\r\n    $cookie_banner.parentNode.removeChild( $cookie_banner );\r\n\r\n    // remove the DOM reference\r\n    $cookie_banner = null;\r\n\r\n  }, close_speed);\r\n\r\n  // animate closed\r\n  if ( ! immediate ) {\r\n    $cookie_banner.className = 'closing';\r\n  }\r\n\r\n} \nThis function takes a single optional argument. If true (or anything “truthy”24) is passed in, the banner is immediately removed from the page (and its reference is deleted). If no argument is passed in, that doesn’t happen for 0.6 seconds, which is 0.1 seconds after the animation finishes up (we’ll get to the animation momentarily). The class change triggers that animation. \nYou already saw one instance of this function referenced in the previous code block. Here it is in the cached template branch of the conditional you saw earlier: \n…\r\n// banner exists but cookie is set\r\nif ( cookiesApproved() )\r\n{\r\n  // close immediately\r\n  closeCookieBanner( true );\r\n}\r\n… \nAdding Some Visual Sizzle Link \nBecause I brought up animations, I’ll discuss the CSS I’m using for the cookie banner component, too. Like most implementations of cookie notices, I opted for a visual full-width banner. On small screens, I wanted the banner to appear above the content and push it down the page. On larger screens I opted to affix it to the top of the viewport because it would not obstruct reading to nearly the same degree as it would on a small screen. Accomplishing this involved very little code: \n#cookie-banner {\r\n  background: #000;\r\n  color: #fff;\r\n  font-size: .875rem;\r\n  text-align: center;\r\n}\r\n\r\n@media (min-width: 60em) {\r\n  #cookie-banner {\r\n    position: fixed;\r\n    top: 0;\r\n    left: 0;\r\n    right: 0;\r\n    z-index: 1000;      \r\n  }\r\n} \nUsing the browser’s default styles, the cookie banner already displays block, so I didn’t really need to do much apart from set some basic text styles and colors. For the large screen (the “full-screen” version comes in at 60 ems), I affix it to the top of the screen using position: fixed, with a top offset of 0. Setting its left and right offsets to 0 ensures it will always take up the full width of the viewport. I also set the z-index quite high so it sits on top of everything else in the stack. \nHere’s the result: \n\nA video showing the browser viewport being enlarged from 240 to 960 pixels width. When the design hits the 60-em breakpoint, the banner becomes fixed-positioned.  Once the basic design was there, I took another pass to spice it up a bit. I decided to have the banner animate in and out using CSS. First things first: I created two animations. Initially, I tried to run a single animation in two directions for each state (opening and closing) but ran into problems triggering the reversal — you might be better at CSS animations than I am, so feel free to give it a shot. In the end, I also decided to tweak the two animations to be slightly different, so I’m fine with having two of them: \n@keyframes cookie-banner {\r\n  0% {\r\n    max-height: 0;\r\n  }\r\n  100% {\r\n    max-height: 20em;\r\n  }\r\n}\r\n@keyframes cookie-banner-reverse {\r\n  0% {\r\n    max-height: 20em;\r\n  }\r\n  100% {\r\n    max-height: 0;\r\n    display: none;\r\n  }\r\n} \nNot knowing how tall the banner would be (this is responsive design, after all), I needed it to animate to and from a height of auto. Thankfully, Nikita Vasilyev25 published a fantastic overview of how to transition values to and from auto26 a few years back. In short, animate max-height instead. The only thing to keep in mind is that the size of the non-zero max-height value you are transitioning to and from needs to be larger than your max, and it will also directly affect the speed of the animation. I found 20 ems to be more than adequate for this use case, but your project may require a different value. \nIt’s also worth noting that I used display: none at the conclusion of my cookie-banner-reverse animation (the closing one) to ensure the banner becomes unreachable to users of assistive technology such as screen readers. It’s probably unnecessary, but I did it as a failsafe just in case something happens and JavaScript doesn’t remove the banner from the DOM. \nWiring it up required only a few minor tweaks to the CSS: \n#cookie-banner {\r\n  …\r\n  box-sizing: border-box;\r\n  overflow: hidden;\r\n  animation: cookie-banner 1s 1s linear forwards;\r\n}\r\n\r\n#cookie-banner.closing {\r\n  animation: cookie-banner-reverse .5s linear forwards;\r\n} \nThis assigned the two animations to the two different banner states: The opening and resting state, cookie-banner, runs for one second after a one-second delay; the closing state, cookie-banner-reverse, runs for only half a second with no delay. I am using a class of closing, set via the JavaScript I showed earlier, to trigger the state change. Just for completeness, I’ll note that this code also stabilizes the dimensions of the banner with box-sizing: border-box and keeps the contents from spilling out of the banner using overflow: hidden. \nOne last bit of CSS tweaking and we’re done. On small screens, I’m leaving a margin between the cookie notice (#cookie-banner) and the page header (.banner). I want that to go away when the banner collapses, even if the cookie notice is not removed from the DOM. I can accomplish that with an adjacent-sibling selector: \n#cookie-banner + .banner {\r\n  transition: margin-top .5s;\r\n}\r\n\r\n#cookie-banner.closing + .banner {\r\n  margin-top: 0;\r\n} \nIt’s worth noting that I am setting the top margin on every element but the first one, using Heydon Pickering’s clever “lobotomized owl27” selector. So, the transition of margin-top on .banner will be from a specific value (in my case, 1.375 rem) to 0. With this code in place, the top margin will collapse over the same duration as the one used for the closing animation of the cookie banner and will be triggered by the very same class addition. \n\nA video showing the final implementation on a wide screen, both with and without JavaScript.  Simple, Robust, Resilient Link \nWhat I like about this approach is that it is fairly simple. It took only about an hour or two to research and implement, and it checks all of the compliance boxes with respect to the EU law. It has minimal dependencies, offers several fallback options, cleans up after itself and is a relatively back-end-agnostic pattern. \nWhen tasked with adding features we may not like — and, yes, I’d count a persistent nagging banner as one of those features — it’s often tempting to throw some code at it to get it done and over with. JavaScript is often a handy tool to accomplish that, especially because the logic can often be self-contained in an external script, configured and forgotten. But there’s a risk in that approach: JavaScript is never guaranteed28. If the feature is “nice to have,” you might be able to get away with it, but it’s probably not a good idea to play fast and loose with a legal mandate like this. Taking a few minutes to step back and explore how the feature can be implemented with minimal effort on all fronts will pay dividends down the road. Believe me29. \n(rb, vf, il, al) \nFront page image credit: Pexels30. \n1 https://github.com/yui/yui3/wiki/Graded-Browser-Support \n2 http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32009L0136:EN:NOT \n3 https://webdevlaw.uk/category/eu-cookie-law/ \n4 https://developer.mozilla.org/docs/Web/API/Web_Storage_API \n5 https://developer.mozilla.org/docs/Web/API/IndexedDB_API \n6 http://ec.europa.eu/ipg/basics/legal/cookies/index_en.htm#section_4 \n7 https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/ \n8 https://a-k-apart.com/ \n9 https://developer.mozilla.org/docs/Web/API/Web_Storage_API \n10 https://wordpress.org/plugins/eu-cookie-law/ \n11 https://www.aaron-gustafson.com/notebook/interface-experience-maps/ \n12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png \n13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-1-opt.png \n14 https://developer.mozilla.org/docs/Web/API/Service_Worker_API \n15 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-large-2-opt.png \n16 https://www.smashingmagazine.com/wp-content/uploads/2017/01/cookie-law-1-opt.png \n17 https://nodejs.org/ \n18 http://expressjs.com/ \n19 https://github.com/expressjs/cookie-parser \n20 http://expressjs.com/en/guide/using-middleware.html \n21 https://mustache.github.io/ \n22 https://mustache.github.io/mustache.5.html#Partials \n23 https://mustache.github.io/mustache.5.html#Inverted-Sections \n24 https://developer.mozilla.org/docs/Glossary/Truthy \n25 https://twitter.com/ELV1S \n26 http://n12v.com/css-transition-to-from-auto/ \n27 http://alistapart.com/article/axiomatic-css-and-lobotomized-owls \n28 http://kryogenix.org/code/browser/everyonehasjs.html \n29 https://medium.com/@AaronGustafson/the-true-cost-of-progressive-enhancement-d395b6502979 \n30 https://www.pexels.com/photo/yellow-and-black-arrow-road-signage-during-daytime-102644/ \n \n\t↑ Back to top \n\tTweet itShare on Facebook \n \n\n\nSource link"},"mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"Amy Carney","photo":"https://webmention.io/avatar/pbs.twimg.com/c1d90cbc976b9a3e3f1e82cd856a0130fea79f1d68af8bce08cca287be42391e.jpg","url":"http://carneydevelopit.wordpress.com"},"url":"https://twitter.com/click2carney/status/897483086006374400","published":"2017-08-15T15:40:16+00:00","wm-received":"2017-08-15T15:56:15Z","wm-id":451297,"wm-source":"https://brid-gy.appspot.com/comment/twitter/AaronGustafson/897457431931912192/897483086006374400","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","content":{"content-type":"text/plain","value":"Thanks for the tip!","text":"Thanks for the tip!"},"in-reply-to":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"in-reply-to","wm-private":false},{"type":"entry","author":{"type":"card","name":"Amy Carney","photo":"https://webmention.io/avatar/pbs.twimg.com/c1d90cbc976b9a3e3f1e82cd856a0130fea79f1d68af8bce08cca287be42391e.jpg","url":"https://twitter.com/click2carney"},"url":"https://twitter.com/AaronGustafson/status/897457431931912192#favorited-by-49144047","published":null,"wm-received":"2017-08-15T15:56:35Z","wm-id":451298,"wm-source":"https://brid-gy.appspot.com/like/twitter/AaronGustafson/897457431931912192/49144047","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","name":"tag:twitter.com,2013:897457431931912192_favorited_by_49144047\n  \n  \n  \n  \n      Amy Carney\n    click2carney\n    \n\n  https://twitter.com/AaronGustafson/status/897457431931912192#favorited-by-49144047","like-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"like-of","wm-private":false},{"type":"entry","author":{"type":"card","name":"","photo":"","url":""},"url":"https://devchat.tv/adventures-in-dotnet/net-049-a-web-for-everyone-with-aaron-gustafson/","published":null,"wm-received":"2021-02-09T15:11:23Z","wm-id":1024637,"wm-source":"https://devchat.tv/adventures-in-dotnet/net-049-a-web-for-everyone-with-aaron-gustafson/","wm-target":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","mention-of":"https://www.aaron-gustafson.com/notebook/interface-experience-maps/","wm-property":"mention-of","wm-private":false}]

Webmentions