<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vincent Humeau</title>
    <description>The latest articles on Forem by Vincent Humeau (@vinceumo).</description>
    <link>https://forem.com/vinceumo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F62587%2Fb58199aa-5a39-4ed0-975a-ba6defa08c75.gif</url>
      <title>Forem: Vincent Humeau</title>
      <link>https://forem.com/vinceumo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vinceumo"/>
    <language>en</language>
    <item>
      <title>Slide stacking effect using position sticky</title>
      <dc:creator>Vincent Humeau</dc:creator>
      <pubDate>Wed, 11 Sep 2019 17:29:35 +0000</pubDate>
      <link>https://forem.com/vinceumo/slide-stacking-effect-using-position-sticky-91f</link>
      <guid>https://forem.com/vinceumo/slide-stacking-effect-using-position-sticky-91f</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvinceumo.github.io%2FdevNotes%2Fscroll-gif.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvinceumo.github.io%2FdevNotes%2Fscroll-gif.gif" alt="Demo gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recently I was looking to create a slide (card) stacking effect on scroll. The closest resource I could find about this effect was &lt;a href="https://medium.com/@sanjeevy133/card-stack-scroll-effect-486cc3b55312" rel="noopener noreferrer"&gt;Card Stack: Scroll Effect by Sanjeev Yadav&lt;/a&gt;. I wanted to look if I could recreate a similar effect only with CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic effect with position sticky
&lt;/h2&gt;

&lt;p&gt;Position sticky enables to position an element like position fixed relative to its parent until it reaches the boundary of the parent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sticky positioning can be thought of as a hybrid of relative and fixed positioning. A stickily positioned element is treated as relatively positioned until it crosses a specified threshold, at which point it is treated as fixed until it reaches the boundary of its parent. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/position#Sticky_positioning" rel="noopener noreferrer"&gt;MDN - Sticky positioning&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can use position sticky to stick each slide, at the top at the parent and have a fixe height.&lt;/p&gt;

&lt;p&gt;CSS:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;

&lt;span class="nc"&gt;.stacking-slide&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sticky&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//   Not needed if 100vh&lt;/span&gt;
&lt;span class="c1"&gt;//   &amp;amp;:nth-last-child(1) {&lt;/span&gt;
&lt;span class="c1"&gt;//      height: 100vh;&lt;/span&gt;
&lt;span class="c1"&gt;//   }&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;HTML:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stacking-slide"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Section 1&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stacking-slide"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Section 2&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stacking-slide"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Section 3&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stacking-slide"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Section 4&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stacking-slide"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Section 5&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/KKPoVGL/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Support
&lt;/h3&gt;

&lt;p&gt;Position sticky has great support (&lt;a href="http://caniuse.com/#feat=css-sticky" rel="noopener noreferrer"&gt;Can I Use&lt;/a&gt;), but if you need to support older browsers you can use a &lt;a href="https://github.com/wilddeer/stickyfill/" rel="noopener noreferrer"&gt;polyfill&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vertical Scroll Snap
&lt;/h2&gt;

&lt;p&gt;CSS scroll snap permits us to make each slide position nicely at the top of the viewport after scroll.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CSS Scroll Snap is a module of CSS that introduces scroll snap positions, which enforce the scroll positions that a scroll container’s scrollport may end at after a scrolling operation has completed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap" rel="noopener noreferrer"&gt;MDN - CSS Scroll Snap&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To use scroll snap we are going to wrap our sections into &lt;code&gt;.vertical-scroll-snap&lt;/code&gt;. This element is going to have a fixe height of 100vh and &lt;code&gt;overflow-y: scroll&lt;/code&gt; so the user can scroll between sections.&lt;/p&gt;

&lt;p&gt;Next step we are going to look into scroll snap. First we are going the set &lt;code&gt;scroll-snap-type&lt;/code&gt; (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;). This property defines how strictly snap points are enforced on scroll. We are going to set it to &lt;code&gt;y mandatory&lt;/code&gt;. &lt;code&gt;y&lt;/code&gt; is indicating that the snap positions is in its vertical axis only. &lt;code&gt;mandatory&lt;/code&gt; that the scroll container will rest on a snap point of the section if it isn't currently scrolled.&lt;/p&gt;

&lt;p&gt;Then we need to tell our sections which part need to be aligned to the container. We are going to use &lt;code&gt;scroll-snap-align&lt;/code&gt; (&lt;a href="https://css-tricks.com/almanac/properties/s/scroll-snap-align/" rel="noopener noreferrer"&gt;CSS Tricks&lt;/a&gt;) with the value &lt;code&gt;start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;HTML:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"vertical-scroll-snap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"stacking-slide"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Section 1&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;SCSS:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;

&lt;span class="nc"&gt;.vertical-scroll-snap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="na"&gt;scroll-snap-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="n"&gt;mandatory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.stacking-slide&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;scroll-snap-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/aboYLBW/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Support
&lt;/h3&gt;

&lt;p&gt;CSS Scroll snap has good support (&lt;a href="http://caniuse.com/#feat=css-snappoints" rel="noopener noreferrer"&gt;Can I Use&lt;/a&gt;), if you need to support older browsers you can use a &lt;a href="https://www.npmjs.com/package/css-scroll-snap-polyfill" rel="noopener noreferrer"&gt;polyfill&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add some nice transition using Intersection Observer API.
&lt;/h2&gt;

&lt;p&gt;The Intersection Observer API, lets us to detect when an element enters the viewport. We can trigger a callback function when this element enters or leave the viewport.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer"&gt;MDN - Intersection Observer API&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the following example, we are adding the class &lt;code&gt;.is-intersecting&lt;/code&gt; when a &lt;code&gt;.stacking-slide&lt;/code&gt; enter 10% of the viewport. And we remove this class when the slide leaves it. We can now add some nice transitions using CSS.&lt;/p&gt;

&lt;p&gt;JS:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sectionEls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.stacking-slide&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rootMargin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-10% 0% -10% 0%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is-intersecting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is-intersecting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;sectionEls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/WNeJRZo/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Support
&lt;/h3&gt;

&lt;p&gt;The Intersection Observer API has good support (&lt;a href="http://caniuse.com/#feat=intersectionobserver" rel="noopener noreferrer"&gt;Can I Use&lt;/a&gt;), if you need to support older browsers you can use a &lt;a href="https://github.com/w3c/IntersectionObserver/tree/master/polyfill" rel="noopener noreferrer"&gt;polyfill&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Thanks for reading hope you enjoyed this small article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/vince_umo" rel="noopener noreferrer"&gt;Follow me on twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vinceumo" rel="noopener noreferrer"&gt;Follow me on Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/vinceumo"&gt;Follow me on dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CSS 3D - Scrolling on the z-axis</title>
      <dc:creator>Vincent Humeau</dc:creator>
      <pubDate>Thu, 21 Mar 2019 18:12:46 +0000</pubDate>
      <link>https://forem.com/vinceumo/css-3d---scrolling-on-the-z-axis-45i9</link>
      <guid>https://forem.com/vinceumo/css-3d---scrolling-on-the-z-axis-45i9</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvinceumo.github.io%2FdevNotes%2Fdist%2Fimg%2Fcss3d-z-scroll-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvinceumo.github.io%2FdevNotes%2Fdist%2Fimg%2Fcss3d-z-scroll-demo.gif" alt="Demo gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, we are going to create a small 3D scene, where the user can scroll on the z-axis. You can find the final code of this tutorial on &lt;a href="https://github.com/vinceumo/CSS-3D-Scrolling-z-axis-demo/" rel="noopener noreferrer"&gt;github&lt;/a&gt;, and the demo if you follow this &lt;a href="https://vinceumo.github.io/CSS-3D-Scrolling-z-axis-demo/" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article assumes that you already have some knowledge about CSS and JS. We are going to use CSS custom properties, if you are not familiar with this you can read &lt;a href="https://vinceumo.github.io/devNotes/css/2019/02/20/css-customs-properties.html" rel="noopener noreferrer"&gt;CSS custom properties - Cheatsheet&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to CSS 3D
&lt;/h2&gt;

&lt;p&gt;When speaking of CSS 3D, we really speak about CSS3 transform 3D. This method allows us to use the &lt;code&gt;transform&lt;/code&gt; CSS property to set perspective or rotation on the z-axis to our DOM elements.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The transform CSS property lets you rotate, scale, skew or translate an element. It modifies the coordinate space of the CSS visual formatting model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform" rel="noopener noreferrer"&gt;transform - MDN&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To be allowed to render our Dom elements in a 3D space we need to have a look in the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perspective&lt;/li&gt;
&lt;li&gt;Perspective origin&lt;/li&gt;
&lt;li&gt;Transform Z&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Perspective
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;perspective&lt;/code&gt; is a CSS property that set the distance between z=0 and the user. The smaller is the perspective value the greater will be the distortion of our see. (Try to change the value of &lt;code&gt;scenePerspective&lt;/code&gt; in the codePen example below).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container-scene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value of &lt;code&gt;perspective&lt;/code&gt; is a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/length" rel="noopener noreferrer"&gt;length unit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvinceumo.github.io%2FdevNotes%2Fdist%2Fimg%2Fcss3d-z-scroll-perspective1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvinceumo.github.io%2FdevNotes%2Fdist%2Fimg%2Fcss3d-z-scroll-perspective1.png" alt="perspective illustration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try to set the value of &lt;code&gt;scenePerspective&lt;/code&gt; to 0 and 70 in the example below. You can notice that our cube get no perspective at all if its value is set to 0. If the value is set to 70, you can see a really strong distortion of the cube perspective. The smaller the perspective value is, the deeper it is.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/jdJLge/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To be able to render a 3D space, we need to specify &lt;code&gt;transform-style: preserve-3d;&lt;/code&gt; on the child elements. In the above example, it set to our &lt;code&gt;.cube&lt;/code&gt;. By default, the elements are flattened.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container-scene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.container-scene&lt;/span&gt; &lt;span class="nc"&gt;.cube&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;preserve-3d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Perspective origin
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;perspective-origin&lt;/code&gt; CSS property determines the position at which the viewer is looking. It is used as the vanishing point by the perspective property.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/perspective-origin" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This property basically allows us to move the vanishing point of our 3D scene.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container-scene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;perspective-origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/*X position value, Y position value*/&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.container-scene&lt;/span&gt; &lt;span class="nc"&gt;.cube&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;preserve-3d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For both x and y we can set the position using percentages. but we can as well use the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x position:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;left&lt;/code&gt; = 0%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;center&lt;/code&gt; = 50%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;right&lt;/code&gt; = 100%&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;y position

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;top&lt;/code&gt; = 0%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;center&lt;/code&gt; = 50%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bottom&lt;/code&gt; = 50%&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvinceumo.github.io%2FdevNotes%2Fdist%2Fimg%2Fcss3d-z-scroll-perspective-origin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvinceumo.github.io%2FdevNotes%2Fdist%2Fimg%2Fcss3d-z-scroll-perspective-origin.png" alt="Perspective origin illustration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the following example, you can change the value of &lt;code&gt;perspectiveOriginX&lt;/code&gt; and &lt;code&gt;perspectiveOriginY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/wOwzBY/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Transform Z
&lt;/h3&gt;

&lt;p&gt;We already mentioned earlier that the &lt;code&gt;transform&lt;/code&gt; CSS property allows us to set our elements in a 3D space.&lt;/p&gt;

&lt;p&gt;Transform comes with different functions to transform our elements in 3D:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rotateX(angle) - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotateX" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;rotateY(angle) - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotateY" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;rotateZ(angle) - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotateZ" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;translateZ(tz) - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translateZ" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;scaleZ(sz) - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scaleZ" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we saw in the illustration in the &lt;code&gt;perspective&lt;/code&gt; section. &lt;code&gt;translateZ()&lt;/code&gt; allows us to position an element along the z-axis of the 3D space. Alternately we can use the &lt;code&gt;translate3D(x, y, z)&lt;/code&gt; CSS function.&lt;/p&gt;

&lt;p&gt;In the following example, you can play with the Z-axis position of the &lt;code&gt;.cube&lt;/code&gt; and &lt;code&gt;.face-&lt;/code&gt; by changing the value of &lt;code&gt;cubeTranslateZ&lt;/code&gt; and &lt;code&gt;cubeFacesTranslateZ&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/VRZKEB/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Scroll on the z-axis
&lt;/h2&gt;

&lt;p&gt;Now that we have a good understanding of how CSS 3D works we are going to create a 3D scene, where we are going to be able to scroll on the z-axis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set the scene
&lt;/h3&gt;

&lt;p&gt;We are going to create a page that lists out all the films of Studio Ghibli. Each film is going to be a card positioned on the z-axis of our scene. Feel free to fork or &lt;a href="https://codepen.io/vinceumo/share/zip/JzaXqN" rel="noopener noreferrer"&gt;download&lt;/a&gt; the following codepen as a starter material to follow along. I'm using &lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;axios&lt;/a&gt; with &lt;a href="https://ghibliapi.herokuapp.com/" rel="noopener noreferrer"&gt;Studio Ghibli API&lt;/a&gt; to populate this page.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/JzaXqN/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you want to follow along with your own content we will need the following markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"scene3D-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"scene3D"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Card1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Card2&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!--Etc.--&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Styling
&lt;/h4&gt;

&lt;p&gt;First, we are going to set our &lt;a href="https://vinceumo.github.io/devNotes/css/2019/02/20/css-customs-properties.html" rel="noopener noreferrer"&gt;CSS custom properties&lt;/a&gt; (CSS variables). Some of these variables are going to be transformed using JS. They are going to help us to interact with the scene.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;--scenePerspective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--scenePerspectiveOriginX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--scenePerspectiveOriginY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;--itemZ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Gap between each cards&lt;/span&gt;
  &lt;span class="na"&gt;--cameraSpeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Where 1 is the fastest, this var is a multiplying factor of --scenePerspective and --filmZ&lt;/span&gt;
  &lt;span class="na"&gt;--cameraZ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Initial camera position&lt;/span&gt;
  &lt;span class="na"&gt;--viewportHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Viewport height will allow us to set the depth of our scene&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.viewport&lt;/code&gt; will allow us to set the height of the window. We will later use it to set the depth of the scene and use the scrollbar to navigate in the z-axis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.viewport&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;viewportHeight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.scene3D-container&lt;/code&gt; sets the scene perspective and the perspective origin. It is position fixed so it stays always on screen. We are as well going to set the perspective origin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.viewport&lt;/span&gt; &lt;span class="nc"&gt;.scene3D-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;scenePerspective&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;cameraSpeed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;perspective-origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;scenePerspectiveOriginX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;scenePerspectiveOriginY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1%&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="na"&gt;will-change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;perspective-origin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;translate3d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//Allows Hardware-Accelerated CSS, so transitions are smoother&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.scene3D&lt;/code&gt; sets the position of our scene on the z-axis, This will behave a bit like moving a camera on the z-axis. But really we are moving the scene and the camera (viewport) is fixed. In the rest of this article, we are going to use the camera comparison. &lt;code&gt;.scene3D&lt;/code&gt; takes the full height and width of the viewport.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.viewport&lt;/span&gt; &lt;span class="nc"&gt;.scene3D-container&lt;/span&gt; &lt;span class="nc"&gt;.scene3D&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;preserve-3d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;translateZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;cameraZ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="na"&gt;will-change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last but not least we are going to position our cards in the scene. All items are position absolute. Odd items are position on the left, even ones on the right.&lt;/p&gt;

&lt;p&gt;We use SCSS to programmatically translate each item. On the &lt;strong&gt;X&lt;/strong&gt; and &lt;strong&gt;Y&lt;/strong&gt; axis we randomly translate them between -25% and 25% for X, between -50% and 50% for Y. We use a &lt;code&gt;@for&lt;/code&gt; loop so each item can be translated on the &lt;strong&gt;z&lt;/strong&gt; axis multiply by their indexes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.viewport&lt;/span&gt; &lt;span class="nc"&gt;.scene3D-container&lt;/span&gt; &lt;span class="nc"&gt;.scene3D&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="nf"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;45%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;2n&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;2n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nt"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;@for&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="ow"&gt;from&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;through&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;translate3D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
          &lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
          &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;itemZ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;cameraSpeed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;-1px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CSS is now done, we have a 3D scene. In the following parts of this article, we are going to write some javascript that going to allow us to navigate in the scene.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/WmgxBG/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Scrolling on z-axis (move camera)
&lt;/h4&gt;

&lt;p&gt;To be able to scroll, we need first to set the value of &lt;code&gt;--viewportHeight&lt;/code&gt; which emulates the depth of the scene.&lt;/p&gt;

&lt;p&gt;The depth of the scene is equal to the addition of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The height of the user window

&lt;ul&gt;
&lt;li&gt;window.innerHeight&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The &lt;code&gt;.scene3D-container&lt;/code&gt; perspective

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;var(--scenePerspective) * var(--cameraSpeed)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The translate z value of our last item

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;var(--itemZ) * var(--cameraSpeed) * items.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Let's create a &lt;code&gt;setSceneHeight()&lt;/code&gt; function that will update the value of &lt;code&gt;--viewportHeight&lt;/code&gt; on load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setSceneHeight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setSceneHeight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;numberOfItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;films&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Or number of items you have in `.scene3D`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemZ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--itemZ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scenePerspective&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scenePerspective&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cameraSpeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--cameraSpeed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="nx"&gt;scenePerspective&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cameraSpeed&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="nx"&gt;itemZ&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cameraSpeed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;numberOfItems&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Update --viewportHeight value&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--viewportHeight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our page has now a scrollbar, but we are still unable to scroll. We need to add an event listener that will listen to the user scrolling. The scroll event will call a &lt;code&gt;moveCamera()&lt;/code&gt; function. It will update the value of &lt;code&gt;--cameraZ&lt;/code&gt; with the value of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/pageYOffset" rel="noopener noreferrer"&gt;window.pageYOffset&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moveCamera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setSceneHeight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;moveCamera&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--cameraZ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageYOffset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setSceneHeight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/pYxpLW/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Move camera angle
&lt;/h4&gt;

&lt;p&gt;Finally, let's make our scene a bit more dynamic. On &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event" rel="noopener noreferrer"&gt;mousemove event&lt;/a&gt; we are going to change the values of &lt;code&gt;scenePerspectiveOriginX&lt;/code&gt; and &lt;code&gt;scenePerspectiveOriginY&lt;/code&gt;. This will give the illusion that the camera move. The items will stay straight in the scene. If you want to give a more realistic camera rotation movement, you could apply &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate3d" rel="noopener noreferrer"&gt;rotate3d()&lt;/a&gt; on the scene.&lt;/p&gt;

&lt;p&gt;First, we are going to store the initial values of these two variables in a &lt;code&gt;perspectiveOrigin&lt;/code&gt; object, we are going to set a &lt;code&gt;perspectiveOrigin.maxGap&lt;/code&gt; value which is going to limit the maximum and minimum values of the variables. For example if &lt;code&gt;scenePerspectiveOriginY&lt;/code&gt; is equal to 50%. On mousemove, the new value will be between 40% and 60%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;perspectiveOrigin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scenePerspectiveOriginX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scenePerspectiveOriginY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;maxGap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user cursor is at the centre of the screen, we will set the values of &lt;code&gt;--scenePerspectiveOriginX&lt;/code&gt; and &lt;code&gt;--scenePerspectiveOriginX&lt;/code&gt; as the initial ones. The further the cursor moves away from the centre, the bigger will increase/decrease these values. If the user moves to the top left corner the values will increase, on the bottom right corner they will decrease.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;moveCameraAngle()&lt;/code&gt; function is going to update the values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;xGap&lt;/code&gt; and &lt;code&gt;yGap&lt;/code&gt; return the mouse position of the user in percentage on the X and Y axis, compare to the centre of the window.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;newPerspectiveOriginX&lt;/code&gt; and &lt;code&gt;newPerspectiveOriginY&lt;/code&gt; return the new perspective origin.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moveCamera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousemove&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moveCameraAngle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setSceneHeight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;moveCameraAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xGap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yGap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPerspectiveOriginX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;perspectiveOrigin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xGap&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;perspectiveOrigin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxGap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPerspectiveOriginY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;perspectiveOrigin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;yGap&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;perspectiveOrigin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxGap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scenePerspectiveOriginX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;newPerspectiveOriginX&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--scenePerspectiveOriginY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;newPerspectiveOriginY&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/NJEwwo/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Our scene is now finished 🎉. I hope you enjoyed this article 😃&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tympanus.net/codrops/css_reference/perspective/" rel="noopener noreferrer"&gt;perspective - Codrops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/perspective" rel="noopener noreferrer"&gt;perspective - MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tympanus.net/codrops/css_reference/transform-style/" rel="noopener noreferrer"&gt;transform-style - Codrops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-style" rel="noopener noreferrer"&gt;transform-style - MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/perspective-origin" rel="noopener noreferrer"&gt;perspective-origin - MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/things-watch-working-css-3d/" rel="noopener noreferrer"&gt;Things to Watch Out for When Working with CSS 3D - CSS-tricks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/vince_umo" rel="noopener noreferrer"&gt;Follow me on twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vinceumo" rel="noopener noreferrer"&gt;Follow me on Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/vinceumo"&gt;Follow me on dev.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>CSS custom properties - Cheatsheet</title>
      <dc:creator>Vincent Humeau</dc:creator>
      <pubDate>Thu, 21 Feb 2019 09:08:44 +0000</pubDate>
      <link>https://forem.com/vinceumo/css-customs-properties---cheatsheet-4iec</link>
      <guid>https://forem.com/vinceumo/css-customs-properties---cheatsheet-4iec</guid>
      <description>&lt;p&gt;This note was originally posted on my &lt;a href="https://vinceumo.github.io/devNotes/css/2019/02/20/css-customs-properties.html"&gt;DevNotes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;CSS custom properties, also known as CSS variables. represent custom properties that can be declared and be called in your CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declare a custom property in CSS
&lt;/h2&gt;

&lt;p&gt;To declare a custom property in your CSS, you need to use the &lt;code&gt;--&lt;/code&gt; syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--colorPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsla&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;74%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;:root&lt;/code&gt; pseudo-class selector, we can declare our variables globally using it. We can as well declare them using other selectors, they will be then be scoped in these ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.theme-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--colorPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsla&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;24%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Use a custom property in CSS
&lt;/h2&gt;

&lt;p&gt;To use a CSS custom property in your CSS you can use the &lt;code&gt;var()&lt;/code&gt; function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--colorPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;tomato&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.theme-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--colorPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;lime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--colorPrimary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this case, &lt;code&gt;body&lt;/code&gt; will have a background colour of &lt;code&gt;tomato&lt;/code&gt;. But &lt;code&gt;body.theme-dark&lt;/code&gt; of &lt;code&gt;lime&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use custom properties without units
&lt;/h2&gt;

&lt;p&gt;CSS custom properties can be declared without units if they are used with the &lt;code&gt;calc()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--spacing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/*Doesn't Work 😫*/&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--spacing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/*Will output 2rem 😃*/&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Use custom properties with JavaScript
&lt;/h2&gt;

&lt;p&gt;To get a custom property we can use the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--my-var&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Or if inline&lt;/span&gt;
&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--my-var&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To update the custom property value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--my-var&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newVal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Example of getting and replacing values:
&lt;/h3&gt;

&lt;p&gt;In the following example, we use the &lt;a href="https://workshop.chromeexperiments.com/examples/gui/"&gt;dat.gui controller library&lt;/a&gt; to change the value of &lt;code&gt;--scenePerspective&lt;/code&gt;, &lt;code&gt;--cubeRotateY&lt;/code&gt;, &lt;code&gt;--cubeRotateX&lt;/code&gt; custom properties. This method makes it easier to apply a new style, as do not have to apply inline style on each Dom elements.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vinceumo/embed/jdJLge?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://drafts.csswg.org/css-variables/#defining-variables"&gt;Defining Custom Properties: the --* family of properties&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*"&gt;Custom properties: CSS variables - MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var"&gt;var() - MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables"&gt;Using CSS custom properties (variables) - MDN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/vince_umo"&gt;@vince_umo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://vincent-humeau.com/"&gt;vincent-humeau.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>csscustomsproperties</category>
      <category>cssvariables</category>
    </item>
    <item>
      <title>Cross Browser extensions with WebExtensions API - 101</title>
      <dc:creator>Vincent Humeau</dc:creator>
      <pubDate>Tue, 23 Oct 2018 11:02:41 +0000</pubDate>
      <link>https://forem.com/vinceumo/cross-browser-extensions-with-webextensions-api---101-40hj</link>
      <guid>https://forem.com/vinceumo/cross-browser-extensions-with-webextensions-api---101-40hj</guid>
      <description>&lt;p&gt;This note was originally posted on my &lt;a href="https://vinceumo.github.io/devNotes/javascript/2018/10/23/webextensionapi.html"&gt;DevNotes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Browser extensions add features and functionalities to a browser. It uses web technologies such as HTML, CSS, and JS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manifest.json
&lt;/h2&gt;

&lt;p&gt;The only file that a browser extension need is the &lt;code&gt;manifest.json&lt;/code&gt; file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Using manifest.json, you specify basic metadata about your extension such as the name and version, and can also specify aspects of your extension's functionality, such as background scripts, content scripts, and browser actions.&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json"&gt;MDN&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The manifest.json keys dependent on the extension you are developing.&lt;/p&gt;

&lt;p&gt;You can find the available keys on &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json"&gt;manifest.json - MDN&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//Mandatory&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Name of your extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Recommended&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"default_locale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Extension description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"homepage_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/vinceumo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"16"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/icon16.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"32"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/icon32.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"48"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/icon48.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"128"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/icon128.png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding and Debugging a browser extension locally
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Firefox
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Add extension
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;about:debugging&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Enable add-on debugging&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Load Temporary Add-on&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select the &lt;em&gt;manifest.json&lt;/em&gt; file&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Debugging extension
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;about:debugging&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Find your extension&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Debug&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Developer toll will open&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Chrome / Opera
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Add extension
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;chrome://extensions/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Load unpacked&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select the extension folder&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Debugging extension
&lt;/h4&gt;

&lt;p&gt;Background script errors can be shown when the package is loaded.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;chrome://extensions/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Find your extension&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;errors&lt;/strong&gt; button (Will show only if errors)&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;View in Developer Tools&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To debug a popup extension:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the popup&lt;/li&gt;
&lt;li&gt;Right-click in the popup&lt;/li&gt;
&lt;li&gt;Click on Inspect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/extensions/tut_debugging"&gt;Chrome - Debugging Extensions&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Edge
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Add extension
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;about:flags&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Enable extension developer features&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;Settings and More&lt;/strong&gt; icon (3 dots icon on the right)&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Extensions&lt;/strong&gt; from this menu&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Load extension&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select the extension folder&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Debugging extension
&lt;/h4&gt;

&lt;p&gt;To debug a popup extension:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the popup&lt;/li&gt;
&lt;li&gt;Right-click in the popup&lt;/li&gt;
&lt;li&gt;Click on Inspect element&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/microsoft-edge/extensions/guides/debugging-extensions"&gt;Microsoft Edge - Debugging Extensions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross browser extension using a polyfill
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/mozilla/webextension-polyfill"&gt;mozilla/webextension-polyfill&lt;/a&gt; allows browsers to use Promise-based WebExtension APIs. Some browsers might style need some adjustments.&lt;/p&gt;

&lt;p&gt;Note that opera use Chromium as well as Vivaldi and Brave, your extension will work in all these browsers if it works in Chrome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish an extension
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Chrome / Brave / Vivaldi
&lt;/h3&gt;

&lt;p&gt;Brave and Vivaldi can download extensions from the &lt;a href="https://chrome.google.com/"&gt;Chrome web store&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a zip file of your extension (Manifest file need to be at the root of this file)&lt;/li&gt;
&lt;li&gt;Connect/Create an account on the &lt;a href="https://chrome.google.com/webstore/developer/dashboard"&gt;Google developer dashboard&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;If you submit an extension for the first time you will need to pay a $5 fee&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Upload the zip file&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Firefox
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a zip file of your extension (Manifest file need to be at the root of this file)&lt;/li&gt;
&lt;li&gt;Connect/Create account on the &lt;a href="https://addons.mozilla.org/en-US/developers/"&gt;Add-on developer hub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Submit a new Add-on&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Opera
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a zip file of your extension (Manifest file need to be at the root of this file)&lt;/li&gt;
&lt;li&gt;Connect/Create account on the &lt;a href="https://addons.opera.com/developer/"&gt;opera add-ons platform&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Submit a new Add-on&lt;/li&gt;
&lt;li&gt;You will need to wait for moderation, could take up to a few weeks/months&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example of a popup extension
&lt;/h2&gt;

&lt;p&gt;I developed recently the browser extension &lt;a href="https://github.com/vinceumo/A11Y-Color-Blindness-Empathy-Test-browser-extensions"&gt;A11Y-Color-Blindness-Empathy-Test-browser-extensions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This extension is a popup extension that injects an inline SVG for filtering colours, some style and changes the class of the &lt;code&gt;html&lt;/code&gt; tag of the current tab open, depending on the picked radio button.&lt;/p&gt;

&lt;p&gt;The extension will follow the following APIs and structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions"&gt;manifest.json - permissions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action"&gt;manifest.json - Browser_action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs"&gt;WebExtensions API - tabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts"&gt;Content scripts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WebExtension
|   manifest.json // Give permissions to the active Tab and reference the popup `choose_test.html`
|
+---content_scripts
|       runTest.js // Script interacting with the active tab
|
+---icons
|       icon128.png
|       icon16.png
|       icon32.png
|       icon48.png
|
+---polyfills
|       browser-polyfill.js // Polyfill for chrome and opera
|
\---popup
        choose_test.css // Style of the popup
        choose_test.html // Markup of the popup
        choose_test.js // Script of the popup, click handler
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions"&gt;MDN - Browser Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/extensions"&gt;Chrome - Browser Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/microsoft-edge/extensions/api-support/extension-api-roadmap"&gt;Microsoft Edge extension API roadmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/community/browserext/"&gt;W3C - Browser extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mdn/webextensions-examples"&gt;webextensions-examples - Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.freecodecamp.org/how-to-make-a-cross-browser-extension-using-javascript-and-browser-apis-355c001cebba"&gt;How to make a cross-browser extension using JavaScript and browser APIs - Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webextensions</category>
      <category>webextensionsapi</category>
      <category>javascript</category>
      <category>extensions</category>
    </item>
    <item>
      <title>Photoshop 101, introduction for web developers</title>
      <dc:creator>Vincent Humeau</dc:creator>
      <pubDate>Mon, 23 Jul 2018 21:24:34 +0000</pubDate>
      <link>https://forem.com/vinceumo/photoshop-101-introduction-for-web-developers-3hom</link>
      <guid>https://forem.com/vinceumo/photoshop-101-introduction-for-web-developers-3hom</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Often, when working as web developer, we need to integrate templates. They can be from Photoshop or other software. In this article, we will have a look at the basics of Photoshop for a web developer.&lt;/p&gt;

&lt;p&gt;This content was initially written for a workshop for &lt;a href="http://damdigital.com/" rel="noopener noreferrer"&gt;DAMDigital London&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Adobe Photoshop is a raster graphics editor software, meaning it is a program that allows users to create and edit images.&lt;/p&gt;

&lt;p&gt;It was released in 1988 and have grown since then, as the industry standard for graphics software.&lt;/p&gt;

&lt;p&gt;You can download a free trial version of Photoshop on the &lt;a href="https://www.adobe.com/products/photoshop/free-trial-download.html" rel="noopener noreferrer"&gt;Adobe website&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Workspace
&lt;/h1&gt;

&lt;p&gt;Photoshop's workspace is modular, you can adapt it depending on the work you are doing. Some default workspaces are already set up in Photoshop. In this article, I'm using the &lt;code&gt;Graphic and Web&lt;/code&gt; one. To change to this one go into &lt;code&gt;Window/workspace/Graphic and Web&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's have a look at our workspace&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffgriwqhv7acql0uxf71z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffgriwqhv7acql0uxf71z.png" alt="Layers box screenshot" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A - Menu bar&lt;/strong&gt;: It is where you get most of photoshop options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B - Options bar&lt;/strong&gt;: This bar will give you all the options of the currently selected tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C - Toolbox&lt;/strong&gt;: This panel contains all the available tools from photoshop. Related tools are grouped together, long click on one of them to see all the tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D - Panels&lt;/strong&gt;: It is the area where you have your basic panels open, such as &lt;code&gt;Layers&lt;/code&gt;, &lt;code&gt;History&lt;/code&gt;, etc. To open a panel just go into &lt;code&gt;Window/(Panel that you want to open)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Create a new file
&lt;/h1&gt;

&lt;p&gt;When creating a new file in Photoshop, you need to know first, for what support is my design going to be used for? Meaning, is it going to be used for a screen? (Web, film and video) or for print?&lt;/p&gt;

&lt;p&gt;Depending on the answer to this question, you will need to change the PPI (Pixels Per Inch) value.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pixels per inch (PPI) is a measurement used to define the resolution of a computer display. This metric evaluates the picture/image quality that a particular computing or output display device is able to display. Pixels per inch is also known as pixel density. &lt;em&gt;&lt;a href="https://www.techopedia.com/definition/2741/pixels-per-inch-ppi" rel="noopener noreferrer"&gt;Techopedia&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For print, you normally will need 300PPI, but that will actually depend on the printer and the size of the print document. You should change your colour to CMYK as well, but that will again depend on how you will print your document. If you want to learn more about RGB and CMYK colour check out this &lt;a href="https://printaura.com/difference-between-RGB-and-CMYK" rel="noopener noreferrer"&gt;article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For screens and web 😃, you will need 72PPI and RGB colour. Then you will need to specify your screen size. I would recommend designing first for mobile, then tablet and desktop 😉.&lt;/p&gt;

&lt;p&gt;As a web developer, you might not need to create a new file. Chances are that you will need to work with a design provided by web designers.&lt;/p&gt;

&lt;p&gt;We are now going to look at an existing &lt;code&gt;.psd&lt;/code&gt; template and work with this one to have a look around Photoshop.&lt;/p&gt;

&lt;p&gt;You can download and open this &lt;a href="https://shibbythemes.com/psd-freebies/surfersco-psd-template/" rel="noopener noreferrer"&gt;PSD template&lt;/a&gt; from &lt;a href="http://lucaal.co/" rel="noopener noreferrer"&gt;Luis Costa&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Layers
&lt;/h1&gt;

&lt;p&gt;One of the core features of Photoshop is &lt;strong&gt;layers&lt;/strong&gt;. Layers are like a stack of sheets, you can see through transparent areas or area with low opacity (partially transparent).&lt;/p&gt;

&lt;p&gt;You can open the layer panel in &lt;code&gt;Window/Layers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3ia00zm48yg1irz2frz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3ia00zm48yg1irz2frz.png" alt="Layers box screen shot" width="372" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The top layer, in this panel, will be over all the other ones under. Layers can be organized as well in folders. It is important to correctly name layers and folders. That will help during the integration.&lt;/p&gt;

&lt;p&gt;Next to each folder and layers, you have an eye icon, it allows you to toggle the visibility of these ones.&lt;/p&gt;

&lt;p&gt;Your PSD might have a lot of layers and documents. One way to quickly find a layer is to select the &lt;code&gt;move tool (v)&lt;/code&gt;. Right click on the canvas, where you want to find your layer. You will get all the layers in the area where you have right clicked. By clicking on one, it will select this layer in your layer panel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1q60ddt4modct241ta3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1q60ddt4modct241ta3.jpg" alt="layer tree" width="325" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Toolbox
&lt;/h1&gt;

&lt;p&gt;Photoshop comes with a load of tools. I'm going to show you a few useful ones that will help you 😉.&lt;/p&gt;

&lt;p&gt;First, if you just install Photoshop CC 2018 you will need to restore all tools go to &lt;code&gt;Edit &amp;gt; Toolbar and click on Restore Defaults button&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08psrj9q3y17zxuqtbtw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08psrj9q3y17zxuqtbtw.png" alt="toolbox" width="576" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://helpx.adobe.com/photoshop/using/tools.html" rel="noopener noreferrer"&gt;Image from Adobe&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We are going to see some of the most useful tools, to integrate a template:&lt;/p&gt;

&lt;h2&gt;
  
  
  A - Selection tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Move&lt;/strong&gt;: Allows the user to move a layer around the canvas. As we saw earlier it can be used as well to find a layer if you right click on your canvas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rectangular Marquee&lt;/strong&gt;: This tool is used to select an area of the canvas, to copy and paste, fill it in etc. It can be used as well to measure. When your selection is done you can find the size of the selected area in &lt;code&gt;Window/Info&lt;/code&gt;. You might need to change the default unit in photoshop &lt;code&gt;Edit/preferences/general/Unit &amp;amp; Rulers&lt;/code&gt; then set your units to pixels.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0q2wiv4dpmgkwf40imor.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0q2wiv4dpmgkwf40imor.jpg" alt="select size" width="265" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  B - Crop and Slice tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Crop&lt;/strong&gt;: This tool can... crop an image 🤟. In the tool settings (Options Bar), you can set an aspect ratio that you want to keep.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  C - Measuring tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Eyedropper&lt;/strong&gt;: The eyedropper allows you to quickly get a colour reference in your design. Just click where you want the colour. Then at the bottom of your toolbox, the foreground colour will change to the selected colour. If you click on the foreground colour, it will open the &lt;code&gt;color picker window&lt;/code&gt; from here you can get the value of your colour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Color Sampler&lt;/strong&gt;: When integrating your design you might need to select multiple colours. We are going to use the info window again &lt;code&gt;Window/Info&lt;/code&gt;. this tool allows us to create a colour sampler. Just click on the area of the image you want to get the colours from. You will get each colour in the info panel. You can change the colour type to web by clicking on the Eyedropper icon under the number.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu2b55oxa6hw5hrwq0wp.png" alt="color sampler" width="800" height="519"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruler&lt;/strong&gt;: helps you to measure your template. All the info will appear in your info window. Maintain &lt;code&gt;Shift&lt;/code&gt; when measuring, so your ruler stays straight. You can get angles also.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  G - Navigation tool
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hand&lt;/strong&gt;: This tool helps you to get around the canvas, you can access this tool as well at any time by holding the spacebar and dragging it with your mouse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zoom&lt;/strong&gt;: Allows to zoom in and out, you can you &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;+&lt;/code&gt;, or &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;-&lt;/code&gt; too.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Guides
&lt;/h1&gt;

&lt;p&gt;As you might have noticed when opening our PSD file, we have some green lines on our template. They are guides, they are basically helpers that will help you build or measure things around your canvas.&lt;/p&gt;

&lt;p&gt;You can move existing guides using the &lt;code&gt;move tool&lt;/code&gt;(v)&lt;/p&gt;

&lt;p&gt;To create new guides, you will need to open your ruler. &lt;code&gt;View/Ruler&lt;/code&gt; or &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;R&lt;/code&gt;. The ruler will appear in your workspace. Then from the ruler, you can drag in your canvas, a new guide.&lt;/p&gt;

&lt;p&gt;To remove a guide, use the &lt;code&gt;move tool&lt;/code&gt;(v) and put back the guide in the ruler.&lt;/p&gt;

&lt;p&gt;To hide and show all your guide you can just use &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;H&lt;/code&gt;, or go to &lt;code&gt;View/Extras&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Smart objects
&lt;/h1&gt;

&lt;p&gt;What are smart objects?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Smart Objects are layers that contain image data from raster or vector images, such as Photoshop or Illustrator files. Smart Objects preserve an image’s source content with all its original characteristics, enabling you to perform nondestructive editing to the layer. &lt;em&gt;&lt;a href="https://helpx.adobe.com/photoshop/using/create-smart-objects.html" rel="noopener noreferrer"&gt;Adobe&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Smart objects can be recognized in your layers when they have the following icon in their thumbnails:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6srgcoz0ntsqz6hsf2n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6srgcoz0ntsqz6hsf2n.png" alt="smart object" width="118" height="50"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Smart object is really handy if you work with vector type images (SVG, EPS, AI), but as well as other complex raster files.&lt;/p&gt;

&lt;p&gt;Let's try to import a smart object in our PSD. Download an SVG file from &lt;a href="https://www.flaticon.com/free-icon/surfboard_930944#term=surf&amp;amp;page=1&amp;amp;position=10" rel="noopener noreferrer"&gt;flaticon&lt;/a&gt;. To import our SVG in our canvas, just drag the file into the canvas. We can now edit our SVG in illustrator, or another vector software by double-clicking on the thumbnail or our smart object. Changes will appear in our PSD.&lt;/p&gt;

&lt;p&gt;Smart object can do way more than that, if you want to learn more about them, check out &lt;a href="https://design.tutsplus.com/tutorials/10-things-you-need-to-know-about-smart-objects-in-photoshop--cms-20268" rel="noopener noreferrer"&gt;10 Things You Need to Know About Smart Objects in Photoshop&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Export assets
&lt;/h1&gt;

&lt;p&gt;First, just a reminder that photoshop is a &lt;strong&gt;raster&lt;/strong&gt; software. Not a &lt;strong&gt;vector&lt;/strong&gt; software. Meaning we "can't" export SVG files from photoshop. To do so you will need to export this kind of files from Illustrator or Inkscape, for example.&lt;/p&gt;

&lt;p&gt;In the web, we want to have light image files, for a photography, we would use a compress &lt;code&gt;.jpg&lt;/code&gt; file. If you need to use transparency (Alpha channel) we will use &lt;code&gt;.png&lt;/code&gt; file. For an animated image, we would use a &lt;code&gt;.gif&lt;/code&gt;. If you need a vector image (icons for example) the best is to export your file as &lt;code&gt;.svg&lt;/code&gt;. If you want to have more information about all the files available in Photoshop you can check out &lt;a href="https://helpx.adobe.com/photoshop/using/file-formats.html" rel="noopener noreferrer"&gt;"file formats" in the Adobe's website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export our canvas
&lt;/h2&gt;

&lt;p&gt;To export the canvas just follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to &lt;code&gt;File/Export/Save for Web&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Pick file format&lt;/li&gt;
&lt;li&gt; Pick image size&lt;/li&gt;
&lt;li&gt; Pick quality&lt;/li&gt;
&lt;li&gt; Save&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Export only an asset from the canvas
&lt;/h2&gt;

&lt;p&gt;You will probably, need to export some assets of your template.&lt;/p&gt;

&lt;p&gt;Let's export the left arrow in the product carousel:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frgr18pen6t47s2xa5mr9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frgr18pen6t47s2xa5mr9.jpg" alt="screenshot carousel" width="554" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the move tool we are going to find our layer. Right click on the arrow and select the layer &lt;code&gt;Arrow Left&lt;/code&gt;. Now just right click on this layer in the layer panel. Select &lt;code&gt;export as&lt;/code&gt;. Just select the type of file you need.&lt;/p&gt;

&lt;p&gt;We can as well export folders.&lt;/p&gt;

&lt;h1&gt;
  
  
  Actions
&lt;/h1&gt;

&lt;p&gt;What's an action in Photoshop?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An action is a series of tasks that you playback on a single file or a batch of files—menu commands, panel options, tool actions, and so on. For example, you can create an action that changes the size of an image, applies an effect to the image, and then saves the file in the desired format. &lt;em&gt;&lt;a href="https://helpx.adobe.com/photoshop/using/actions-actions-panel.html" rel="noopener noreferrer"&gt;Adobe&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This feature is really convenient if you want to resize a batch of images for the web!&lt;/p&gt;

&lt;p&gt;Let's create a new action to crop an image and export this one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Download a bunch of images from &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;https://unsplash.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; Open one of our images&lt;/li&gt;
&lt;li&gt; Open the &lt;code&gt;Actions&lt;/code&gt; panel, &lt;code&gt;Window/Actions&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt; Create a new action by clicking on the icon &lt;strong&gt;Create a new action&lt;/strong&gt; (The one on the left to the bin icon) and let's name this action &lt;strong&gt;Export for web - client name&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; We are now recording our action, the record button is in red, you can stop to record by clicking on the stop icon (square icon on the left)&lt;/li&gt;
&lt;li&gt; Select the crop icon and set the ratio to 1x1 and crop the image&lt;/li&gt;
&lt;li&gt; Now we want to export our image, &lt;code&gt;File/Export/Save for Web&lt;/code&gt;, select &lt;code&gt;JPG&lt;/code&gt;, quality 50% and width 500px&lt;/li&gt;
&lt;li&gt; Click save and pick your destination folder&lt;/li&gt;
&lt;li&gt; Close your image without saving&lt;/li&gt;
&lt;li&gt;Stop recording, click on the stop icon (square icon on the left)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We now have our action, we can open an image and just "play" our action by clicking on the play button.&lt;/p&gt;

&lt;p&gt;If we want to apply our action to a batch of images just follow this steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to &lt;code&gt;File/Automate/Batch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Select the &lt;code&gt;Source&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt; Select our action&lt;/li&gt;
&lt;li&gt; Click on &lt;code&gt;Ok&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And voila 😉. All your images are in the export folder.&lt;/p&gt;




&lt;p&gt;I hope you enjoyed this small Photoshop 101 introduction for web developer 😃. If you want to have a 102 version, let me know what you would like to know or to hear about 😉.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/vince_umo" rel="noopener noreferrer"&gt;@vince_umo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://vincent-humeau.com/" rel="noopener noreferrer"&gt;vincent-humeau.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>photoshop</category>
      <category>tool</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Vue.js 101 todo PWA tutorial</title>
      <dc:creator>Vincent Humeau</dc:creator>
      <pubDate>Sat, 21 Jul 2018 09:41:28 +0000</pubDate>
      <link>https://forem.com/vinceumo/vuejs-101-todo-pwa-tutorial-4bne</link>
      <guid>https://forem.com/vinceumo/vuejs-101-todo-pwa-tutorial-4bne</guid>
      <description>&lt;p&gt;The goal of this tutorial is to give an introduction to vue.js ❤.&lt;/p&gt;

&lt;p&gt;This content was initially written for a workshop for &lt;a href="https://www.damdigital.com/"&gt;DAM Digital London&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vue.js is a progressive framework for building user interfaces (UI).&lt;/p&gt;

&lt;p&gt;This tutorial assumes that you already have some intermediate knowledge about HTML, CSS and Javascript. If you don't have any knowledge of these fields, check out &lt;a href="https://www.freecodecamp.org/"&gt;freeCodeCamp&lt;/a&gt;, they have great resources to learn these topics 😉.&lt;/p&gt;

&lt;p&gt;Here are the subjects this tutorial will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;v-bind directive&lt;/li&gt;
&lt;li&gt;Class and Style Bindings&lt;/li&gt;
&lt;li&gt;Event Handling&lt;/li&gt;
&lt;li&gt;Data binding in attributes&lt;/li&gt;
&lt;li&gt;List Rendering&lt;/li&gt;
&lt;li&gt;Interpolations - Mustache&lt;/li&gt;
&lt;li&gt;Form Input Bindings - Checkbox&lt;/li&gt;
&lt;li&gt;v-model&lt;/li&gt;
&lt;li&gt;Methods&lt;/li&gt;
&lt;li&gt;Conditional Rendering&lt;/li&gt;
&lt;li&gt;v-if&lt;/li&gt;
&lt;li&gt;v-else&lt;/li&gt;
&lt;li&gt;Introduction to PWA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are going to create a todo application with the ability to have multiple lists of todos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tQs4uCsA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://image.ibb.co/g3g8Gy/Screenshot_2018_07_17_Todo_list.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tQs4uCsA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://image.ibb.co/g3g8Gy/Screenshot_2018_07_17_Todo_list.png" alt="todo app screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Clone the &lt;a href="https://github.com/vinceumo/todo-vue-starter-materials"&gt;starter materials from github&lt;/a&gt;. It includes the base HTML, CSS and JS.&lt;/p&gt;

&lt;p&gt;You can find the final code of this tutorial on &lt;a href="https://github.com/vinceumo/todo"&gt;github&lt;/a&gt;, and the demo if you follow this &lt;a href="http://vinceumo.github.io/todo"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vue CDN is already included in our index.html, as well as our CSS and content/js/app.js 😃.&lt;/p&gt;

&lt;h2&gt;
  
  
  Toggle navigation on mobile
&lt;/h2&gt;

&lt;p&gt;First thing first, the side navigation on mobile (📱 &amp;lt; 850px) should show and hide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6QKWGQPi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://image.ibb.co/c7qGmy/menu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6QKWGQPi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://image.ibb.co/c7qGmy/menu.gif" alt="toggle nav"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we click on &lt;strong&gt;Menu&lt;/strong&gt; we want &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; to toggle the class &lt;code&gt;.is-open&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In vue.js, &lt;code&gt;v-bind&lt;/code&gt; allow us to bind data inside an html attribute. E.g. &lt;code&gt;v-bind:id=""&lt;/code&gt;, &lt;code&gt;v-bind:style=""&lt;/code&gt;, &lt;code&gt;v-bind:data-target=""&lt;/code&gt;, etc. The shorthand for &lt;code&gt;v-bind&lt;/code&gt; is &lt;code&gt;:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;index.html&lt;/code&gt;, we will dynamically pass &lt;code&gt;.is-open&lt;/code&gt; using &lt;code&gt;v-bind:class&lt;/code&gt;. If &lt;code&gt;isNavOpen&lt;/code&gt; is true, then we will add our class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"{'is-open': isNavOpen}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;content/js/app.js&lt;/code&gt;, we need &lt;code&gt;isNavOpen&lt;/code&gt; in our data. If you change the value of this last one to &lt;strong&gt;true&lt;/strong&gt;, the nav will show up.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;data&lt;/strong&gt; property in vue.js is where we store the data of our application but as well the state of our UI. For example, &lt;code&gt;isNavOpen&lt;/code&gt; is by default set to false, but by changing its value to true, we can bind the class &lt;em&gt;is-open&lt;/em&gt; to the DOM.&lt;/p&gt;

&lt;p&gt;In our app.js, we need to add &lt;code&gt;isNavOpen: false&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isNavOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we want to change the value of &lt;code&gt;isNavOpen&lt;/code&gt; when we click on the &lt;strong&gt;Menu&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;We are going to use the event handler 'on click'. In vue.js, we can use &lt;code&gt;v-on:&lt;/code&gt; or &lt;code&gt;@&lt;/code&gt; (Shorthands), to listen to DOM events. In our case, we want to listen to a click event. We are then going to use &lt;code&gt;v-on:click&lt;/code&gt;/&lt;code&gt;@click&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isNavOpen = !isNavOpen"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Menu&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we can pass an inline javascript statement, we can as well use a method (function), we will see later in this tutorial how to use this last one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/class-and-style.html"&gt;Class and Style Bindings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/events.html"&gt;Event Handling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/api/#v-bind"&gt;v-bind directive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/syntax.html#Attributes"&gt;Data binding in attributes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bind our todo lists to the side navigation
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;content/js/app.js&lt;/code&gt;, let's add some dummy lists so we can start to integrate our side navigation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isNavOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✈️ Trip to japan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;japan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Eat ramen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Visit mt Fuji&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Learn japanese&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🏂 Ski trip to the Alps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Find a chalet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Learn how to ski&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🍉 Groceries&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Food&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Apples&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Banana&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tomatoes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bread&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To render our lists in the side navigation, we need to use the &lt;code&gt;v-for&lt;/code&gt; directive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"{'is-open': isNavOpen}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"todoList in todoLists"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;
        {{todoList.title}}
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
          {{todoList.items.length}}
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-add"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Create a new list&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;todoLists&lt;/code&gt; is the source data and &lt;code&gt;todoList&lt;/code&gt; is the alias used to iterate in our array.&lt;/p&gt;

&lt;p&gt;We are using the 'moustache' syntax &lt;code&gt;{{}}&lt;/code&gt; to bind our text to the view. The moustache tag gets replace by the targeted value in &lt;code&gt;todoLists&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/list.html"&gt;List Rendering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/syntax.html#Text"&gt;Interpolations - Mustache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Main section
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Header
&lt;/h3&gt;

&lt;p&gt;We want to be able to see our todos in the main section. For now, we will only render the first &lt;code&gt;todoLists&lt;/code&gt; list (index 0).&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;content/js/app.js&lt;/code&gt; =&amp;gt; &lt;code&gt;data&lt;/code&gt; add &lt;code&gt;currentListIndex: 0&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isNavOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currentListIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bind the title of the list using the moustache syntax in the header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{todoLists[currentListIndex].title}}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The header has a background image. We are using &lt;a href="https://source.unsplash.com/"&gt;Unsplash Source&lt;/a&gt; to get a random image. We can specify a keyword to get a relevant image for our header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://source.unsplash.com/featured/?{KEYWORD},{KEYWORD}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we bind our keyword inside an attribute, we are using &lt;code&gt;v-bind&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;v-bind:style=&lt;/span&gt;&lt;span class="s"&gt;"'background-image: url(https://source.unsplash.com/featured/?' + todoLists[currentListIndex].keyword + ')'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Todos
&lt;/h3&gt;

&lt;p&gt;To render our todos in the main section, we will need to use &lt;code&gt;v-for&lt;/code&gt;. As we want an individual ID and names for each input we are passing the index in our for loop &lt;code&gt;v-for="(value, index) in object"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We use &lt;code&gt;v-bind&lt;/code&gt; to &lt;em&gt;check/tick&lt;/em&gt; our todos inputs if these ones are already checked.&lt;/p&gt;

&lt;p&gt;We are using &lt;code&gt;v-model&lt;/code&gt; to update the value of &lt;code&gt;isCompleted&lt;/code&gt; from our todos when we click on the checkbox. When our checkbox is checked, isCompleted will get the value of true and the parent &lt;code&gt;li&lt;/code&gt; will automatically get the class &lt;code&gt;.is-completed&lt;/code&gt; as isCompleted is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;v-model&lt;/code&gt; directive creates a two-way data bindings, meaning when the value gets updated the UI will be updated as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(todo, index) in todoLists[currentListIndex].items"&lt;/span&gt; &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"{'is-completed': todo.isCompleted}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;v-bind:for=&lt;/span&gt;&lt;span class="s"&gt;"'todo' + index"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;
      &lt;span class="na"&gt;v-bind:name=&lt;/span&gt;&lt;span class="s"&gt;"'todo' + index"&lt;/span&gt;
      &lt;span class="na"&gt;v-bind:id=&lt;/span&gt;&lt;span class="s"&gt;"'todo' + index"&lt;/span&gt;
      &lt;span class="na"&gt;v-bind:checked=&lt;/span&gt;&lt;span class="s"&gt;"todo.isCompleted"&lt;/span&gt;
      &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"todo.isCompleted"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {{todo.name}}
    &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-danger"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Edit todo&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-add"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;New Todo&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Documentation references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/forms.html#Checkbox"&gt;Form Input Bindings - Checkbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/api/#v-model"&gt;v-model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Change current list
&lt;/h2&gt;

&lt;p&gt;We want to be able to change the current list being displayed. This one is set by &lt;code&gt;currentListIndex&lt;/code&gt; in our app's data. When we click on one of the list items we want to change &lt;code&gt;currentListIndex&lt;/code&gt; to the index of this one and close the side navigation if open.&lt;/p&gt;

&lt;p&gt;We need as well, to show the user the current list being displayed, to do so, we are adding the class &lt;code&gt;.is-active&lt;/code&gt; if &lt;code&gt;currentListIndex === index&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(todoList, index) in todoLists"&lt;/span&gt;  &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"{'is-active' : currentListIndex === index}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"currentListIndex = index; isNavOpen = false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        {{todoList.title}}
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
            {{todoList.items.length}}
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a new list
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Toggle the sidebar
&lt;/h3&gt;

&lt;p&gt;When clicking on &lt;strong&gt;Create a new list&lt;/strong&gt;, we show the &lt;code&gt;.sidebar&lt;/code&gt;. To do so we want to add the class &lt;code&gt;.is-open&lt;/code&gt; to this one, then close the nav bar if this one is open on mobile. The way to do this is quite similar to what we have done with the navigation on mobile.&lt;/p&gt;

&lt;p&gt;In our data, we will first, add a new entry of &lt;code&gt;isSidebarOpen: false&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isNavOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currentListIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's bind our class &lt;code&gt;.is-open&lt;/code&gt; to our &lt;code&gt;.sidebar&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sidebar"&lt;/span&gt; &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"{'is-open' : isSidebarOpen}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to add an event handler on click on &lt;strong&gt;Create a new list&lt;/strong&gt;, that will open the sidebar and close the navigation on mobile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-add"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isSidebarOpen = true; isNavOpen = false;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Create a new list&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice, now we can open our sidebar 🎉.&lt;/p&gt;

&lt;p&gt;Now let's close the sidebar when we click on &lt;strong&gt;cancel&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-danger"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isSidebarOpen = false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the new list
&lt;/h3&gt;

&lt;p&gt;To create a new list, we need the values of the &lt;em&gt;title&lt;/em&gt; and &lt;em&gt;keyword&lt;/em&gt; inputs. When the user clicks on &lt;strong&gt;Create List&lt;/strong&gt;, we will push our new values to &lt;code&gt;todoLists&lt;/code&gt; in our data. If one of our input is empty we will show up a default value.&lt;/p&gt;

&lt;p&gt;In our &lt;em&gt;app.js&lt;/em&gt;, add a &lt;code&gt;tempNewList&lt;/code&gt; array, it will store the values of our inputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isNavOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currentListIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tempNewList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we will bind our inputs using &lt;code&gt;v-model&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Create a new list&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"listTitle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Title:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"listTitle"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"listTitle"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"My amazing next trip to south america"&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"tempNewList.title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"listKeyword"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Keyword:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"listKeyword"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"listKeyword"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Colombia"&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"tempNewList.keyword"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"buttons"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-danger"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isSidebarOpen = false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-confirm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Create List&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, now let's push our new &lt;code&gt;tempNewList&lt;/code&gt; values to &lt;code&gt;todoLists&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a &lt;strong&gt;method&lt;/strong&gt; called &lt;code&gt;addNewList&lt;/code&gt;. A &lt;strong&gt;method&lt;/strong&gt; is a function stored as an object property. Here the object is the vue instance. In vue, our method will be stored in a &lt;code&gt;methods&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;addNewList&lt;/code&gt; method will follow this scenario:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; If &lt;em&gt;title&lt;/em&gt; is empty, use a default string of &lt;code&gt;"🕵️‍ List with no name"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; If &lt;em&gt;keyword&lt;/em&gt; is empty, use a default string of &lt;code&gt;"earth"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Push our values to &lt;code&gt;todoLists&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Change our current list to our new list&lt;/li&gt;
&lt;li&gt; Close the sidebar&lt;/li&gt;
&lt;li&gt; Reset the values of our inputs
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;addNewList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;listTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tempNewList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;listKeyword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tempNewList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listTitle&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;listTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🕵️‍ List with no name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listKeyword&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;listKeyword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;earth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;listTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;listKeyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentListIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tempNewList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tempNewList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we are going to bind our method to our &lt;strong&gt;Create list&lt;/strong&gt; button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-confirm"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"addNewList"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Create List&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Documentation references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/events.html#Method-Event-Handlers"&gt;Method Event Handlers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Edit a list
&lt;/h2&gt;

&lt;p&gt;Good, now that we can create a new list, we want the ability to edit existing ones. We will be able to edit the title, the keyword and delete a list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Toggle the sidebar content
&lt;/h3&gt;

&lt;p&gt;Create a new method &lt;code&gt;openSidebar&lt;/code&gt;. This one will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open the sidebar&lt;/li&gt;
&lt;li&gt; Show the form that we want to use&lt;/li&gt;
&lt;li&gt; Close the navigation if this one is open&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In data, let's add &lt;code&gt;sidebarContentToShow: null&lt;/code&gt;, This will allow us to know what form should be shown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;isNavOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sidebarContentToShow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currentListIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have 4 forms in our sidebar that we will toggle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;"createNewList"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; &lt;code&gt;"editList"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; &lt;code&gt;"createNewTodo"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; &lt;code&gt;"editTodo"&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our HTML, we will conditionally render our forms depending on the value of &lt;code&gt;sidebarContentToShow&lt;/code&gt;. To do so we are using the &lt;code&gt;v-if&lt;/code&gt; directive. It will allow us to render our block if a condition is true. We need to uncomment our forms and add a &lt;code&gt;v-if&lt;/code&gt; directive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sidebar"&lt;/span&gt; &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"{'is-open' : isSidebarOpen}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sidebar-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"sidebarContentToShow === 'createNewList'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Create a new list&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
          &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"sidebarContentToShow === 'editList'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Edit list&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
          &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"sidebarContentToShow === 'createNewTodo'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Create a new todo&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
          &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"sidebarContentToShow === 'editTodo'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Edit todo&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
          &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we click on the &lt;strong&gt;Create a new list&lt;/strong&gt;, the sidebar appears and we see... Nothing 😱. That normal remember, &lt;code&gt;sidebarContentToShow&lt;/code&gt; is set to null 😉.&lt;/p&gt;

&lt;p&gt;To change the value of &lt;code&gt;sidebarContentToShow&lt;/code&gt; we are going to create an &lt;code&gt;openSidebar&lt;/code&gt; method that will open the sidebar and change the form that we want to show.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;openSidebar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contentToShow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isNavOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sidebarContentToShow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contentToShow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can change &lt;strong&gt;Create a new list&lt;/strong&gt; so we can use &lt;code&gt;openSidebar&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-add"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"openSidebar('createNewList')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Create a new list&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And ta-dah we are now rendering the &lt;strong&gt;create a new list form&lt;/strong&gt;. As you may have already guessed we will reuse our method with the &lt;strong&gt;Edit list&lt;/strong&gt; button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-primary"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"openSidebar('editList')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Edit list&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Edit list form
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Delete a list
&lt;/h4&gt;

&lt;p&gt;We are going to start with the &lt;strong&gt;delete list&lt;/strong&gt; button. Create a new method called &lt;code&gt;deleteList&lt;/code&gt;. It will remove the currently shown list and show the first one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="nx"&gt;deleteList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentListIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentListIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-danger"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"deleteList"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Delete list&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can remove lists, but if we try to remove all lists we get an error and our app stop to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Vue warn]: Error in render: "TypeError: todoLists[currentListIndex] is undefined"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you might have guessed, we have this error because our &lt;code&gt;todoLists&lt;/code&gt; is empty and we still try to render some part of our application that relies on the values of &lt;code&gt;todoLists&lt;/code&gt;. We will use conditional rendering &lt;code&gt;v-if&lt;/code&gt; and &lt;code&gt;v-else&lt;/code&gt;, to fix this issue we will render our main content only if &lt;code&gt;todoLists.length &amp;gt; 0&lt;/code&gt;. Moreover, we want the user to able to create a new list, we will use &lt;code&gt;v-else&lt;/code&gt; to show an alternative main content that will help the user create a new list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"todoLists.length &amp;gt; 0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-image: url(https://source.unsplash.com/featured/?cat"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Please create a new list&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-add"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"openSidebar('createNewList')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Create a new list&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Change the title and keyword value
&lt;/h4&gt;

&lt;p&gt;Let's get back to our &lt;strong&gt;editList&lt;/strong&gt; form. We want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bind our inputs with the right &lt;code&gt;todoLists&lt;/code&gt; element using &lt;code&gt;v-model&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When we click on &lt;strong&gt;done&lt;/strong&gt;, we want to close our slider.&lt;/li&gt;
&lt;li&gt;Only want to render this form if &lt;code&gt;todoLists.length &amp;gt; 0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"sidebarContentToShow === 'editList' &amp;amp;&amp;amp; todoLists.length &amp;gt; 0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Edit list&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"listTitle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Title:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"listTitle"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"listTitle"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"My amazing next trip to south america"&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"todoLists[currentListIndex].title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"listKeyword"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Keyword:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"listKeyword"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"listKeyword"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Colombia"&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"todoLists[currentListIndex].keyword"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"buttons"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-danger"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"deleteList"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Delete list&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-confirm"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isSidebarOpen = false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Done&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Documentation references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/conditional.html"&gt;Conditional Rendering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/api/#v-if"&gt;v-if&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/conditional.html#v-else"&gt;v-else&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create and edit a todo
&lt;/h2&gt;

&lt;p&gt;The UI of our app is almost finished, we still need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new todo in a list&lt;/li&gt;
&lt;li&gt;Edit and delete an existing todo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sound similar to what we have done with the lists right? It is going to be almost the same steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a todo
&lt;/h4&gt;

&lt;p&gt;In our data create a new element of &lt;code&gt;tempNewList&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tempNewTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need a new method, so we can add our new todo to a list in &lt;code&gt;todoLists&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;addNewTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;todoName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tempNewTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;todoCompleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tempNewTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoName&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;todoName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🕵️‍ unnamed todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentListIndex&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todoName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todoCompleted&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tempNewTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tempNewTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isCompleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's dive in our HTML.&lt;/p&gt;

&lt;p&gt;We need to open the sidebar with the &lt;strong&gt;createNewTodo&lt;/strong&gt; form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-add"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"openSidebar('createNewTodo')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;New Todo&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we did before, we will bind our inputs using &lt;code&gt;v-model&lt;/code&gt; and use the &lt;code&gt;addNewTodo&lt;/code&gt; method to push our new values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"sidebarContentToShow === 'createNewTodo'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Create a new todo&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"todoName"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Name:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todoName"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"todoName"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Do things..."&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"tempNewTodo.name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"todoCompleted"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"todoCompleted"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todoCompleted"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;v-bind:checked=&lt;/span&gt;&lt;span class="s"&gt;"tempNewTodo.isCompleted"&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"tempNewTodo.isCompleted"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Is completed&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"buttons"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-danger"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isSidebarOpen = false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-confirm"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"addNewTodo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Create todo&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we are now binding the data for &lt;code&gt;isCompleted&lt;/code&gt; in our todos, we will show, in our navigation the number of completed todos.&lt;/p&gt;

&lt;p&gt;In our &lt;strong&gt;app.js&lt;/strong&gt;, create a &lt;code&gt;totalTodosCompleted&lt;/code&gt; method passing the index of the current todoList.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;totalTodosCompleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now in our &lt;code&gt;navigation&lt;/code&gt;, we are going to use our new method to return the total of completed todos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(todoList, index) in todoLists"&lt;/span&gt; &lt;span class="na"&gt;v-bind:class=&lt;/span&gt;&lt;span class="s"&gt;"{'is-active' : currentListIndex === index}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"currentListIndex = index; isNavOpen = false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        {{todoList.title}}
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;
            {{totalTodosCompleted(index)}} / {{todoList.items.length}}
        &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Edit a todo
&lt;/h4&gt;

&lt;p&gt;To edit a todo, first, we need to know the index of the todo that will be editing, in our data, create &lt;code&gt;currentTodoIndex&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;currentTodoIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will need a &lt;code&gt;deleteTodo&lt;/code&gt; method that will remove the current todo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentListIndex&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTodoIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTodoIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's have look at our HTML.&lt;/p&gt;

&lt;p&gt;First, we want to open our slider and change the value of &lt;code&gt;currentTodoIndex&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-primary"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"openSidebar('editTodo'); currentTodoIndex = index"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Edit todo&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;strong&gt;editTodo&lt;/strong&gt; form we will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show our form only if &lt;code&gt;todoLists[currentListIndex].items.length &amp;gt; 0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Bind the todo name and if completed using &lt;code&gt;v-model&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When we click on &lt;em&gt;Delete todo&lt;/em&gt;, trigger the method &lt;code&gt;deleteTodo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When we click on &lt;em&gt;Done&lt;/em&gt;, close our sidebar
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"sidebarContentToShow === 'editTodo' &amp;amp;&amp;amp; todoLists[currentListIndex].items.length &amp;gt; 0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Edit todo&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"todoName"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Todo:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todoName"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"todoName"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Do things..."&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"todoLists[currentListIndex].items[currentTodoIndex].name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"todoCompleted"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"todoCompleted"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todoCompleted"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;v-bind:checked=&lt;/span&gt;&lt;span class="s"&gt;"todoLists[currentListIndex].items[currentTodoIndex].isCompleted"&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"todoLists[currentListIndex].items[currentTodoIndex].isCompleted"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Is completed&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"buttons"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-danger"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"deleteTodo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Delete todo&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"is-confirm"&lt;/span&gt; &lt;span class="na"&gt;v-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isSidebarOpen = false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Done&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉🎉🎉🎉🎉 The UI of our todo is now done!&lt;/p&gt;

&lt;h2&gt;
  
  
  LocalStorage
&lt;/h2&gt;

&lt;p&gt;When we reload the page, it goes back to our dummy values. How great would it be if we could store our lists and todos locally?&lt;/p&gt;

&lt;p&gt;We are going to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage"&gt;window.localStorage&lt;/a&gt;. It is part of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API"&gt;Web Storage API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;localStorage&lt;/strong&gt; allow us to store data with no expiration date.&lt;/p&gt;

&lt;p&gt;In our &lt;strong&gt;app.js&lt;/strong&gt;, create a new method &lt;code&gt;updateTodoLocalStorage&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="nx"&gt;updateTodoLocalStorage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todoLocalStorage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are using &lt;code&gt;setItem()&lt;/code&gt; method from the Web Storage API. We pass the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;setItem(keyName, keyValue);&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;keyName&lt;/code&gt;: name of the key we want to create/update (&lt;code&gt;'todoLocalStorage'&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;keyValue&lt;/code&gt;: value we want to give the key you are creating/updating (&lt;code&gt;JSON.stringify(this.todoLists)&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We want now to use this method every time we update the values of our todos or lists. Vue allows us to react to data changes with the &lt;code&gt;watch&lt;/code&gt; option. Every time we have a change in our &lt;code&gt;todoLists&lt;/code&gt;, we will call our &lt;code&gt;updateTodoLocalStorage&lt;/code&gt; method. As our object has nested values, we want to detect changes inside this values. We can pass &lt;code&gt;deep: true&lt;/code&gt; to do so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateTodoLocalStorage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;deep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="na"&gt;updateTodoLocalStorage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todoLocalStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's inspect our app and look into &lt;strong&gt;Local Storage&lt;/strong&gt;. If we create/update a list or a todo, we can see our &lt;code&gt;todoLocalStorage&lt;/code&gt; storage being updated.&lt;/p&gt;

&lt;p&gt;Now, when we load our page, we need to set our &lt;code&gt;todoLists&lt;/code&gt; as our &lt;code&gt;todoLocalStorage&lt;/code&gt;. Vue comes with &lt;a href="https://vuejs.org/v2/api/#created"&gt;Lifecycle Hooks&lt;/a&gt;. We will use the &lt;code&gt;created: function()&lt;/code&gt; one to set our values. We are going, as well, to remove our dummy values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="na"&gt;todoLists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoLists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todoLocalStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we reload, close and reopen our app all our todos and list have been saved 🤟.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/guide/computed.html#Watchers"&gt;Watchers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/v2/api/#created"&gt;Created&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Bonus - Offline Progressive Web App (PWA)
&lt;/h1&gt;

&lt;p&gt;In this bonus part of this tutorial, we will set up a Progressive Web App (PWA) and services workers, so we can use this web application offline on a smartphone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up a PWA
&lt;/h2&gt;

&lt;p&gt;A PWA is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Progressive Web Apps are web applications that are regular web pages or websites, but can appear to the user like traditional applications or native mobile applications. The application type attempts to combine features offered by most modern browsers with the benefits of a mobile experience. &lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Progressive_Web_Apps"&gt;wikipedia&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is basically a web app that takes advantages of the latest technologies to act and feels like a native app.&lt;/p&gt;

&lt;p&gt;To set up our PWA we will need to create a &lt;code&gt;manifest.json&lt;/code&gt; file and set up our service workers.&lt;/p&gt;

&lt;p&gt;PWA must be served from a secure origin (HTTPS).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.smashingmagazine.com/2016/08/a-beginners-guide-to-progressive-web-apps/"&gt;A Beginner's Guide To Progressive Web Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://duckduckgo.com/?q=progressive+web+app+definition&amp;amp;t=ffab&amp;amp;atb=v123-1&amp;amp;ia=about"&gt;Progressive Web Apps - Google&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Generate icon assets
&lt;/h3&gt;

&lt;p&gt;First, include all the icon assets in our project. The icons have already been generated using &lt;a href="https://realfavicongenerator.net/"&gt;https://realfavicongenerator.net/&lt;/a&gt;. They are included in &lt;code&gt;content/img/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the head of our HTML we want to include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"180x180"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"content/img/apple-touch-icon.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"32x32"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"content/img/favicon-32x32.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"16x16"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"content/img/favicon-16x16.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"mask-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"content/img/safari-pinned-tab.svg"&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;"#5bbad5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"shortcut icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"content/img/favicon.ico"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"msapplication-TileColor"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"#ffffff"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"msapplication-config"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"content/img/browserconfig.xml"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme-color"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"#77c4d3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Web App Manifest
&lt;/h3&gt;

&lt;p&gt;Web App Manifest (manifest.json) is a file that provides information about our web app such as icons, the name of our application, etc. It is part of the technologies used for PWAs. You can get more information about the Web App Manifest on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Manifest"&gt;MDN web docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create this file at the root of our project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vincent Humeau"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lang"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en-GB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"content/img/android-chrome-192x192.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192x192"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"content/img/android-chrome-512x512.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512x512"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#77c4d3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#77c4d3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standalone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orientation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"portrait"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/todo/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/todo/"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our HTML file, we want to include it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"manifest.json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service workers
&lt;/h3&gt;

&lt;p&gt;What are service workers?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Service workers are a new browser feature that provide event-driven scripts that run independently of web pages. Unlike other workers, service workers can be shut down at the end of events, note the lack of retained references from documents, and they have access to domain-wide events such as network fetches. Service workers also have scriptable caches. Along with the ability to respond to network requests from certain web pages via script, this provides a way for applications to “go offline”. &lt;em&gt;&lt;a href="https://github.com/w3c/ServiceWorker"&gt;w3c/ServiceWorker - Github&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This tutorial does not aim to go in depth about service workers, you can find some great tutorial and resources online:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=BfL3pprhnms"&gt;How to Setup a Basic Service Worker (with Caching) - bitsofcode (Video)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/fundamentals/primers/service-workers/"&gt;Service Workers: an Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pwabuilder.com/serviceworker"&gt;pwabuilder - service workers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pwabuilder.com/serviceworker"&gt;pwabuilder - service workers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/tools/lighthouse/"&gt;Lighthouse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/serviceworker-for-offline/"&gt;Making a Simple Site Work Offline with ServiceWorker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sitepoint.com/getting-started-with-service-workers/"&gt;Getting Started with Service Workers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For our services workers we are using this &lt;a href="https://gist.github.com/Omranic/4e648fa38caab7b8207d3e237fde0c77#create-service-worker"&gt;gist from Omranic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;sw.js&lt;/code&gt; file at the root of our project.&lt;/p&gt;

&lt;p&gt;In our &lt;strong&gt;index.html&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sw.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in our &lt;strong&gt;sw.js&lt;/strong&gt; we will cache all our assets that will allow our app to be usable offline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;shellCacheName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pwa-todo-v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;filesToCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./content/css/screen.min.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./content/js/app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.jsdelivr.net/npm/vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we only have to follow this gist and add the parts &lt;strong&gt;Listen to installation event&lt;/strong&gt;, &lt;strong&gt;Update Assets Cache&lt;/strong&gt; and &lt;strong&gt;Serve App Shell Offline From Cache&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;This tutorial is now finally done. Our &lt;strong&gt;todo vue.js PWA&lt;/strong&gt; can now be accessed here &lt;a href="https://vinceumo.github.io/todo"&gt;https://vinceumo.github.io/todo&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/vince_umo"&gt;@vince_umo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://vincent-humeau.com/"&gt;vincent-humeau.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vue</category>
      <category>pwa</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
