Wednesday, 22 January 2014
Creating a File Encryption App with JavaScript

 

|

Security and privacy are hot topics at the moment. This is an opportunity for us to take an introspective look into the way we approach security. It is all a matter of compromise – convenience versus total lock down. Today’s tutorial is an attempt to mix a bit of both.
The app we are going to build today is an experiment that will allow people to choose files from their computers and encrypt them client-side with a pass phrase. No server-side code will be necessary, and no information will be transferred between client and server. To make this possible we will use the HTML5 FileReader API, and a JavaScript encryption library - CryptoJS.
Note that the app doesn’t encrypt the actual file, but a copy of it, so you won’t lose the original. But before we start, here are a few issues and limitations:

Issues and limitations

The 1MB limit
If you play with the demo, you will notice that it doesn’t allow you to encrypt files larger than 1mb. I placed the limit, because the HTML5 download attribute, which I use to offer the encrypted file for download, doesn’t play well with large amounts of data. Otherwise it would cause the tab to crash in Chrome, and the entire browser to crash when using Firefox. The way around this would be to use the File System API and to write the actual binary data there, but it is supported only in Chrome for now. This is not an issue with the encryption speed (which is quite fast), but with offering the file for download.
What about HTTPS?
When it comes to encrypting data and securing information, people naturally expect the page to be loaded through HTTPS. In this case I believe it is not necessary, as apart from the initial download of the HTML and assets, no data is transferred between you and the server – everything is done client-side with JavaScript.  If this bothers you, you can just download the demo and open it directly from your computer.
How secure is it?
The library that I use - CryptoJS - is open source, so I believe it to be trustworthy. I use the AES algorithm from the collection, which is known to be secure. For best results, use a long pass phrase that is difficult to guess.
JavaScript File Encryption App
JavaScript File Encryption App

The HTML

The markup of the app consists of a regular HTML5 document and a few divs that separate the app into several individual screens. You will see how these interact in the JavaScript and CSS sections of the tutorial.

index.html

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8"/>
        <title>JavaScript File Encryption App</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link href="http://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet" />
        <link href="assets/css/style.css" rel="stylesheet" />
    </head>

    <body>

        <a class="back"></a>

        <div id="stage">

            <div id="step1">
                <div class="content">
                    <h1>What do you want to do?</h1>
                    <a class="button encrypt green">Encrypt a file</a>
                    <a class="button decrypt magenta">Decrypt a file</a>
                </div>
            </div>

            <div id="step2">

                <div class="content if-encrypt">
                    <h1>Choose which file to encrypt</h1>
                    <h2>An encrypted copy of the file will be generated. No data is sent to our server.</h2>
                    <a class="button browse blue">Browse</a>

                    <input type="file" id="encrypt-input" />
                </div>

                <div class="content if-decrypt">
                    <h1>Choose which file to decrypt</h1>
                    <h2>Only files encrypted by this tool are accepted.</h2>
                    <a class="button browse blue">Browse</a>

                    <input type="file" id="decrypt-input" />
                </div>

            </div>

            <div id="step3">

                <div class="content if-encrypt">
                    <h1>Enter a pass phrase</h1>
                    <h2>This phrase will be used as an encryption key. Write it down or remember it; you won't be able to restore the file without it. </h2>

                    <input type="password" />
                    <a class="button process red">Encrypt!</a>
                </div>

                <div class="content if-decrypt">
                    <h1>Enter the pass phrase</h1>
                    <h2>Enter the pass phrase that was used to encrypt this file. It is not possible to decrypt it without it.</h2>

                    <input type="password" />
                    <a class="button process red">Decrypt!</a>
                </div>

            </div>

            <div id="step4">

                <div class="content">
                    <h1>Your file is ready!</h1>
                    <a class="button download green">Download</a>
                </div>

            </div>
        </div>

    </body>

    <script src="assets/js/aes.js"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="assets/js/script.js"></script>

</html>
Only one of the step divs is visible at a time. Depending on the choice of the user – to encrypt or decrypt – a class name is set on the body element. With CSS, this class name hides the elements with either the if-encrypt or if-decrypt classes. This simple gating allows us to write cleaner JavaScript that is minimally involved with the UI.
Choose File To Encrypt
Choose File To Encrypt

The JavaScript Code

As I mentioned in the intro, we are going to use the HTML5 FileReader API (support) and the CryptoJS library together. The FileReader object lets us read the contents of local files using JavaScript, but only of files that have been selected explicitly by the user through the file input’s browse dialog. You can see how this is done in the code below. Notice that most of the code handles the transitions between the different screens of the app, and the actual reading of the file happens from line 85.

assets/js/script.js

$(function(){

    var body = $('body'),
        stage = $('#stage'),
        back = $('a.back');

    /* Step 1 */

    $('#step1 .encrypt').click(function(){
        body.attr('class', 'encrypt');

        // Go to step 2
        step(2);
    });

    $('#step1 .decrypt').click(function(){
        body.attr('class', 'decrypt');
        step(2);
    });

    /* Step 2 */

    $('#step2 .button').click(function(){
        // Trigger the file browser dialog
        $(this).parent().find('input').click();
    });

    // Set up events for the file inputs

    var file = null;

    $('#step2').on('change', '#encrypt-input', function(e){

        // Has a file been selected?

        if(e.target.files.length!=1){
            alert('Please select a file to encrypt!');
            return false;
        }

        file = e.target.files[0];

        if(file.size > 1024*1024){
            alert('Please choose files smaller than 1mb, otherwise you may crash your browser. \nThis is a known issue. See the tutorial.');
            return;
        }

        step(3);
    });

    $('#step2').on('change', '#decrypt-input', function(e){

        if(e.target.files.length!=1){
            alert('Please select a file to decrypt!');
            return false;
        }

        file = e.target.files[0];
        step(3);
    });

    /* Step 3 */

    $('a.button.process').click(function(){

        var input = $(this).parent().find('input[type=password]'),
            a = $('#step4 a.download'),
            password = input.val();

        input.val('');

        if(password.length<5){
            alert('Please choose a longer password!');
            return;
        }

        // The HTML5 FileReader object will allow us to read the 
        // contents of the selected file.

        var reader = new FileReader();

        if(body.hasClass('encrypt')){

            // Encrypt the file!

            reader.onload = function(e){

                // Use the CryptoJS library and the AES cypher to encrypt the 
                // contents of the file, held in e.target.result, with the password

                var encrypted = CryptoJS.AES.encrypt(e.target.result, password);

                // The download attribute will cause the contents of the href
                // attribute to be downloaded when clicked. The download attribute
                // also holds the name of the file that is offered for download.

                a.attr('href', 'data:application/octet-stream,' + encrypted);
                a.attr('download', file.name + '.encrypted');

                step(4);
            };

            // This will encode the contents of the file into a data-uri.
            // It will trigger the onload handler above, with the result

            reader.readAsDataURL(file);
        }
        else {

            // Decrypt it!

            reader.onload = function(e){

                var decrypted = CryptoJS.AES.decrypt(e.target.result, password)
                                        .toString(CryptoJS.enc.Latin1);

                if(!/^data:/.test(decrypted)){
                    alert("Invalid pass phrase or file! Please try again.");
                    return false;
                }

                a.attr('href', decrypted);
                a.attr('download', file.name.replace('.encrypted',''));

                step(4);
            };

            reader.readAsText(file);
        }
    });

    /* The back button */

    back.click(function(){

        // Reinitialize the hidden file inputs,
        // so that they don't hold the selection 
        // from last time

        $('#step2 input[type=file]').replaceWith(function(){
            return $(this).clone();
        });

        step(1);
    });

    // Helper function that moves the viewport to the correct step div

    function step(i){

        if(i == 1){
            back.fadeOut();
        }
        else{
            back.fadeIn();
        }

        // Move the #stage div. Changing the top property will trigger
        // a css transition on the element. i-1 because we want the
        // steps to start from 1:

        stage.css('top',(-(i-1)*100)+'%');
    }

});
I obtain the contents of the files as a data uri string (support). Browsers allow you to use these URIs everywhere a regular URL would go. The benefit is that they let you store the content of the resource directly in the URI, so we can, for example, place the contents of the file as the href of a link, and add the download attribute (read more) to it, to force it to download as a file when clicked.
I use the AES algorithm to encrypt the data uri with the chosen password, and to offer it as a download. The reverse happens when decrypting it. No data ever reaches the server. You don’t even need a server for that matter, you can open the HTML directly from a folder on your computer, and use it as is.
Enter a Pass Phrase
Enter a Pass Phrase

The CSS

I will present only the more interesting parts of the CSS here, you can see the rest in the stylesheet from the downloadable zip. The first thing to present, are the styles that create the layout and its ability to scroll smoothly between screens by changing the top property of the #stage element.

assets/css/styles.css

body{
    font:15px/1.3 'Raleway', sans-serif;
    color: #fff;
    width:100%;
    height:100%;
    position:absolute;
    overflow:hidden;
}

#stage{
    width:100%;
    height:100%;
    position:absolute;
    top:0;
    left:0;

    transition:top 0.4s;
}

#stage > div{  /* The step divs */
    height:100%;
    position:relative;
}

#stage h1{
    font-weight:normal;
    font-size:48px;
    text-align:center;
    color:#fff;
    margin-bottom:60px;
}

#stage h2{
    font-weight: normal;
    font-size: 14px;
    font-family: Arial, Helvetica, sans-serif;
    margin: -40px 0 45px;
    font-style: italic;
}

.content{
    position:absolute;
    text-align:center;
    left:0;
    top:50%;
    width:100%;
}
Because the step divs are set to a 100% width and height, they automatically take the full dimensions of the browser window without having to be resized.
Another interesting piece of code, are the conditional classes that greatly simplify our JavaScript:
[class*="if-"]{
    display:none;
}

body.encrypt .if-encrypt{
    display:block;
}

body.decrypt .if-decrypt{
    display:block;
}
This way, the encrypt and decrypt classes of the body control the visibility of the elements that have the respective if-* class.

We’re done!

With this our JavaScript encryption app is ready! You can use it to share pictures and documents with friends by sending them the version encrypted with a pre-agreed pass phrase. Or you can put the HTML of the app on a flash drive, along with your encrypted files, and open the index.html directly to decrypt them.
Tuesday, 21 January 2014
 Building a Circular Navigation with CSS Transforms


In this tutorial I’m going to show you how to create circular navigations using CSS transforms. I’m going to take you through the steps for creating these styles one by one, and explain the math (yikes!) and simple logic behind them so you get a clear understanding of the technique.
Like I mentioned, there’s going to be some basic math involved, along with CSS transforms to create these styles. But don’t worry, the math is really very simple and I’ll be going through it step by step.
I also want to mention that credit for the original technique goes to Ana Tudor. I tweaked it to achieve the results that I wanted, which is also what I’m hoping you’ll be able to do by the end of this tutorial: have a deep and clear understanding of the technique so you can start fiddling around and creating your own styles.
So, without further ado, let’s get started!

The Markup

We’re going to be building a navigation, so we’ll start with a regular navigation structure. We’ll need a div to contain our unordered list of items, a button to trigger the opening and closing of the navigation, the list of items, and for the first demo we’ll need an extra overlay to cover the page when the navigation is open.
1
2
3
4
5
6
7
8
9
10
11
<button class="cn-button" id="cn-button">+</button>
<div class="cn-wrapper" id="cn-wrapper">
   <ul>
       <li><a href="#"><span class="icon-picture"></span></a></li>
       <li><a href="#"><span class="icon-headphones"></span></a></li>
       <li><a href="#"><span class="icon-home"></span></a></li>
       <li><a href="#"><span class="icon-facetime-video"></span></a></li>
       <li><a href="#"><span class="icon-envelope-alt"></span></a></li>
   </ul>
</div>
<div id="cn-overlay" class="cn-overlay"></div>
The icons we’re using in this demo are from Font Awesome.

The Math Behind the CSS Transforms

The best way to explain the math is to use a visual explanation instead of a written one. So I’ll start with the logic and we’ll apply the math as we go, and after we’re done with our explanation we’ll get to the coding part, at which point you’ll be able to know what each CSS rule exactly does.
First let’s go over what a “central angle” is. This is a visual representation followed by a simple explanation:
cn-central-angle
Suppose you want to distribute all navigation items on a semi-circle like the example we’re making here, and you have 6 items in your navigation list, then each angle would have a central angle of:
180deg / 6 = 30deg
If you want the items to take up a complete circle, and you have 6 items you want to have in this circle, the central angle for each item would be:
360deg / 6 = 60deg
And so on. So you calculate the value of the central angle you want, and from here on we’ll start applying some simple math to CSS transforms to actually create these angles.
To create an angle of value equal to our desired central angle value, we’ll have to skew the items (using the skew() CSS function) by:
90deg – x deg, where x is the value of the central angle we want.
Simple. But in this case, all the content inside the list items will also be skewed and the content will look distorted, which is not what we want, so we’ll “unskew” the anchors inside each item so that the content looks fine, but we’ll get to that in a moment.
I’ve set up a live and interactive demo that will show you how the transforms are applied to the navigation items step by step so you get a clearer understanding of what we’ll be doing in the code. (Note that the walkthrough in the demo may differ slightly in order of steps from the steps we’ll be taking in the tutorial)
You might want to play the step-by-step demo at this point to get a clearer understanding of what we’ll be doing next. I’ve also added a walkthrough to the demo which explains what’s happening in each step, so take a minute to play the demo and try to gain a better understanding of what we’ll do. You can play the demo from start to finish using the Start Demo Button, or control moving from step to step using the Next Step Button, and you can reset it anytime using the Reset Button.
View the interactive demo
Here are screenshots of the steps you’re going to see in the demo:

Initial state:

Step-0

Step 1:

Step-1

Step 2:

step-2

Step 4:

Step-4

Step 5:

Step-5

Step 6:

Step-6

Step 7

Step-7
So let’s go over this again:
  • we’ll need to position the items absolutely inside their container.
  • We’ll set the transform-origin for each item to be the bottom right corner.
  • We’ll then translate the items up and to the left just enough so that their transform origin coincides with their container’s center.
  • We’ll then rotate the items clockwise into their positions using this formula: For each item of index i we rotate it by: i * x , where x is again the value of the central angle
  • then skew them to get the central angle we want (using the formula we mentioned above)
In our example, we have 5 items, which means 5 central angles, that we want to cover only the upper half of the circle, so according to the math we explained above, every item would have a central angle of 36deg, but in our example I’ve set the central angle value to 40deg (because it provides a bigger clickable area), so the sum of all angles will be 5 * 40 = 200deg which is greater than 180deg. In this case, all we do is just rotate the items “back” counter-clockwise by (200-180)/2 deg to ensure they are balanced on both sides.
At this point we have created our central angles and positioned them. But skewing the list items has also caused their content (anchor tags) to skew too, and thus caused their content to be distorted, so the last mathematical rule we’ll be applying here (phew!) is one that will make sure the anchor tags aren’t distorted and their content be visible. The rule is:
You unskew the anchor tags, which means you skew them by the opposite value of that you used to skew the list items, and then you unrotate the anchors by a value of:
- [90 – (x/2) ] , with x being the value of the central angle
so, for a central angle value of 40deg, we have to skew the anchors by -40deg and rotate them by:
-[ 90 – (40/2) ] = -70 deg
The anchors are positioned absolutely inside their parents, and the overflow is set to hidden on the list items, which means that part of the anchors is cut off, so in order to make sure that the text/icon content of the anchors lies within their visible part, we’ll set their text to be aligned at the center.
And that’s all the math you need to create the slices inside the navigation! Phew, finally, right?
So let’s go over this one more time, quickly:
  1. Rotate the items into position by: angle y = i * x (where i = index of item, and x = value of central angle)
  2. Skew them by 90deg – x (where x is the value of central angle, again)
  3. Rotate items in the opposite direction if/when needed to balance (this step is actually merged with the previous one, you subtract the value by which you want to rotate the angles back from the value of the angle you rotate them by)
  4. “Unskew” and “unrotate” the anchors inside them (and set their text align to center”)
Of course I’ve skipped part where you move the list items so that their transform origin coincides with their container’s center (as shown in the demo). And that’s pretty much all you need to create the angles, but that’s not everything you need to create the whole navigation. A few simple steps remain, and they are pretty much just regular styling, so let’s start with the CSS and talk about these steps as we go!

The CSS

We’re first going to style the first demo.
CircularNavigation_Demo1
We’re going to use Modernizr‘s classes applied to the body tag to target supporting and non-supporting browsers, and provide a very simple and basic fallback for older browsers that do not support CSS transforms.
Let’s start with the styles for the navigation wrapper. It will get a fixed position at the bottom center of the page, and will initially be scaled down, and when the open button is clicked it will open / scale up.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.csstransforms .cn-wrapper {
  font-size:1em;
  width: 26em;
  height: 26em;
  overflow: hidden;
  position: fixed;
  z-index: 10;
  bottom: -13em;
  left: 50%;
  border-radius: 50%;
  margin-left: -13em;
  transform: scale(0.1);
  transition: all .3s ease;
}
/* class applied to the container via JavaScript that will scale the navigation up */
.csstransforms .opened-nav {
  border-radius: 50%;
  transform: scale(1);
}
We’ll also style and position the button which will trigger the opening and closing of our navigation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.cn-button {
  border:none;
  background:none;
  color: white;
  text-align: Center;
  font-size: 1.5em;
  padding-bottom: 1em;
  height: 3.5em;
  width: 3.5em;
  background-color: #111;
  position: fixed;
  left: 50%;
  margin-left: -1.75em;
  bottom: -1.75em;
  border-radius: 50%;
  cursor: pointer;
  z-index: 11
}
.cn-button:hover,
.cn-button:active,
.cn-button:focus{
  background-color: #222;
}
When the navigation is opened, an overlay covers the page. Here are the styles for the overlay.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.cn-overlay{
  width:100%
  height:100%;
  background-color: rgba(0,0,0,0.6);
  position:fixed;
  top:0;
  left:0;
  bottom:0;
  right:0;
  opacity:0;
  transition: all .3s ease;
  z-index:2;
  pointer-events:none;
}
/* Class added to the overlay via JavaScript to show it when navigation is open */
.cn-overlay.on-overlay{
  pointer-events:auto;
  opacity:1;
}
Now we’ll style the navigation items and their anchors, applying the logic and math-based transformed we explained earlier.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
.csstransforms .cn-wrapper li {
  position: absolute;
  font-size: 1.5em;
  width: 10em;
  height: 10em;
  transform-origin: 100% 100%;
  overflow: hidden;
  left: 50%;
  top: 50%;
  margin-top: -1.3em;
  margin-left: -10em;
  transition: border .3s ease;
}
.csstransforms .cn-wrapper li a {
  display: block;
  font-size: 1.18em;
  height: 14.5em;
  width: 14.5em;
  position: absolute;
  position: fixed; /* fix the "displacement" bug in webkit browsers when using tab key */
  bottom: -7.25em;
  right: -7.25em;
  border-radius: 50%;
  text-decoration: none;
  color: #fff;
  padding-top: 1.8em;
  text-align: center;
  transform: skew(-50deg) rotate(-70deg) scale(1);
  transition: opacity 0.3s, color 0.3s;
}
.csstransforms .cn-wrapper li a span {
  font-size: 1.1em;
  opacity: 0.7;
}
/* for a central angle x, the list items must be skewed by 90-x degrees
in our case x=40deg so skew angle is 50deg
items should be rotated by x, minus (sum of angles - 180)2s (for this demo) */
.csstransforms .cn-wrapper li:first-child {
  transform: rotate(-10deg) skew(50deg);
}
.csstransforms .cn-wrapper li:nth-child(2) {
  transform: rotate(30deg) skew(50deg);
}
.csstransforms .cn-wrapper li:nth-child(3) {
  transform: rotate(70deg) skew(50deg)
}
.csstransforms .cn-wrapper li:nth-child(4) {
  transform: rotate(110deg) skew(50deg);
}
.csstransforms .cn-wrapper li:nth-child(5) {
  transform: rotate(150deg) skew(50deg);
}
.csstransforms .cn-wrapper li:nth-child(odd) a {
  background-color: #a11313;
  background-color: hsla(0, 88%, 63%, 1);
}
.csstransforms .cn-wrapper li:nth-child(even) a {
  background-color: #a61414;
  background-color: hsla(0, 88%, 65%, 1);
}
/* active style */
.csstransforms .cn-wrapper li.active a {
  background-color: #b31515;
  background-color: hsla(0, 88%, 70%, 1);
}
/* hover style */
.csstransforms .cn-wrapper li:not(.active) a:hover,
.csstransforms .cn-wrapper li:not(.active) a:active,
.csstransforms .cn-wrapper li:not(.active) a:focus {
  background-color: #b31515;
  background-color: hsla(0, 88%, 70%, 1);
}
We’ll provide a simple and basic fallback for browsers that do not support CSS transforms.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
.no-csstransforms .cn-wrapper{
  font-size:1em;
  height:5em;
  width:25.15em;
  bottom:0;
  margin-left: -12.5em;
  overflow: hidden;
  position: fixed;
  z-index: 10;
  left:50%;
  border:1px solid #ddd;
}
.no-csstransforms .cn-button{
  display:none;
}
.no-csstransforms .cn-wrapper li{
  position:static;
  float:left;
  font-size:1em;
  height:5em;
  width:5em;
  background-color: #eee;
  text-align:center;
  line-height:5em;
}
.no-csstransforms .cn-wrapper li a{
  display:block;
  width:100%;
  height:100%;
  text-decoration:none;
  color:inherit;
  font-size:1.3em;
  border-right: 1px solid #ddd;
}
.no-csstransforms .cn-wrapper li a:last-child{
  border:none;
}
.no-csstransforms .cn-wrapper li a:hover,
.no-csstransforms .cn-wrapper li a:active,
.no-csstransforms .cn-wrapper li a:focus{
  background-color: white;
}
.no-csstransforms .cn-wrapper li.active a {
  background-color: #6F325C;
  color: #fff;
}
Of course we want our navigation to be responsive, so it will shrink to fit smaller screens. Here are the responsive styles for both the circular and the simple styles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@media screen and (max-width:480px){
  .csstransforms .cn-wrapper{
    font-size:.68em;
  }
  .cn-button{
    font-size:1em;
  }
  .csstransforms .cn-wrapper li {
    font-size:1.52em;
  }
}
@media screen and (max-width:320px){
  .no-csstransforms .cn-wrapper{
    width:15.15px;
    margin-left: -7.5em;
  }
  .no-csstransforms .cn-wrapper li{
    height:3em;
    width:3em;
  }
}
And that’s it for the first demo! Let’s move on to the next one.
CircularNavigation_Demo2
The second circular style is different from the first one, but all the math and logic and transforms needed to create this style is pretty much the same as the previous one, except for three differences. So we won’t be going over the same explanation again, we’ll only cover the three different steps needed for this style.
Let’s take the above example again, and change just a small simple CSS rule, and see what difference it makes to the shape of the items.
We’ll apply a radial gradient background to the anchors with a transparent background color. The result will look like this:
cn-radial-gradient
You can see where we’re headed from here. Next, we’re going to add space between the items, by changing the angle of rotation for each item a little bit. We’ll also remove the background colors of the list items and the container, and the borders used, and decrease the top padding of the anchor tags to pull the icons up a little bit to center them vertically. The result obtained looks like this:

cn-demo2-step1
As you see the navigation is already starting to look different; we’re more than halfway through.
We still have one important thing to do. At the current state of this style, the clickable area of the anchors is still bigger than what we want. What we do want, is that only the colored part of the navigation shown in the image be clickable/hoverable. The image below shows the extra clickable area of the anchors.
cn-demo2-cover
When you hover over the red area shown in the image, which is the lower part of the anchor tags, the hover state of the anchors is fired, which is the normal thing to happen, but because we want it to seem like the anchors are only the purple area, we need to prevent mouse events from firing on the lower part of the anchors. For this we’re going to use a “cover” which we’ll place over at the center of the navigation container, which will be a circular shape covering the lower parts of the anchors, and therefore blocking the mouse events on these parts. We’ll be using a pseudo-element for this to avoid adding additional empty tags to our markup.
So, applying these three steps to our CSS, and changing the overall styles (colors and size) of the navigation and navigation items to look like the preview image above, we end up with the following CSS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
.csstransforms .cn-wrapper {
  position: absolute;
  top: 100%;
  left: 50%;
  z-index: 10;
  margin-top: -13em;
  margin-left: -13.5em;
  width: 27em;
  height: 27em;
  border-radius: 50%;
  background: transparent;
  opacity: 0;
  transition: all .3s ease 0.3s;
  transform: scale(0.1);
  pointer-events: none;
  overflow: hidden;
}
/*cover to prevent extra space of anchors from being clickable*/
.csstransforms .cn-wrapper:after{
  color: transparent;
  content:".";
  display:block;
  font-size:2em;
  width:6.2em;
  height:6.2em;
  position: absolute;
  left: 50%;
  margin-left: -3.1em;
  top:50%;
  margin-top: -3.1em;
  border-radius: 50%;
  z-index:10;
}
.csstransforms .cn-wrapper li {
  position: absolute;
  top: 50%;
  left: 50%;
  overflow: hidden;
  margin-top: -1.3em;
  margin-left: -10em;
  width: 10em;
  height: 10em;
  font-size: 1.5em;
  transition: all .3s ease;
  transform: rotate(76deg) skew(60deg);
  transform-origin: 100% 100%;
  pointer-events: none;
}
.csstransforms .cn-wrapper li a {
  position: absolute;
  position: fixed; /* fix the "displacement" bug in webkit browsers when using tab key */
  right: -7.25em;
  bottom: -7.25em;
  display: block;
  width: 14.5em;
  height: 14.5em;
  border-radius: 50%;
  background: #429a67;
  background: radial-gradient(transparent 35%, #429a67 35%);
  color: #fff;
  text-align: center;
  text-decoration: none;
  font-size: 1.2em;
  line-height: 2;
  transition: all .3s ease;
  transform: skew(-60deg) rotate(-75deg) scale(1);
  pointer-events: auto;
}
.csstransforms .cn-wrapper li a span {
  position: relative;
  top: 1.8em;
  display: block;
  font-size: .5em;
  font-weight: 700;
  text-transform: uppercase;
}
.csstransforms .cn-wrapper li a:hover,
.csstransforms .cn-wrapper li a:active,
.csstransforms .cn-wrapper li a:focus {
    background: radial-gradient(transparent 35%, #449e6a 35%);
}
We want the navigation items in the second demo to spread out in a fan-like effect when the navigation is opened.
To achieve this effect, we have positioning the items in the same place and with the same rotation/skew of rotate(76deg) skew(60deg).
Using transition delays we allow the items to spread out after we scale the wrapper up. When the navigation gets closed, we’ll wait for the items to move back in, before we scale the wrapper down again.
When the open button is clicked we’re going to spread the items out by rotating each of them to their final position on the circle.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
.csstransforms .opened-nav {
  border-radius: 50%;
  opacity: 1;
  transition: all .3s ease;
  transform: scale(1);
  pointer-events: auto;
}
.csstransforms .opened-nav li {
  transition: all .3s ease .3s;
}
.csstransforms .opened-nav li:first-child {
  transform: rotate(-20deg) skew(60deg);
}
.csstransforms .opened-nav li:nth-child(2) {
  transform: rotate(12deg) skew(60deg);
}
.csstransforms .opened-nav  li:nth-child(3) {
  transform: rotate(44deg) skew(60deg);
}
.csstransforms .opened-nav li:nth-child(4) {
  transform: rotate(76deg) skew(60deg);
}
.csstransforms .opened-nav li:nth-child(5) {
  transform: rotate(108deg) skew(60deg);
}
.csstransforms .opened-nav li:nth-child(6) {
  transform: rotate(140deg) skew(60deg);
}
.csstransforms .opened-nav li:nth-child(7) {
  transform: rotate(172deg) skew(60deg);
}
Of course, we’ll also provide a basic fallback for non-supporting browsers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
.no-csstransforms .cn-wrapper{
  margin:10em auto;
  overflow:hidden;
  text-align:center;
  padding:1em;
}
.no-csstransforms .cn-wrapper ul{
  display:inline-block;
}
.no-csstransforms li{
  font-size:1em;
  width:5em;
  height:5em;
  float:left;
  line-height:5em;
  text-align:center;
  background-color: #fff;
}
.no-csstransforms li a{
  display:block;
  height:100%;
  width:100%;
  text-decoration: none;
  color: inherit;
}
.no-csstransforms .cn-wrapper li a:hover,
.no-csstransforms .cn-wrapper li a:active,
.no-csstransforms .cn-wrapper li a:focus{
  background-color: #f8f8f8;
}
.no-csstransforms .cn-wrapper li.active a {
  background-color: #6F325C;
  color: #fff;
}
.no-csstransforms .cn-button{
  display:none;
}
And the navigation is also going to be responsive, so we’ll shrink it on smaller screens. Here are the responsive styles for both circular and simple styles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@media only screen and (max-width: 620px) {
  .no-csstransforms li{
    width:4em;
    height:4em;
    line-height:4em;
  }
}
@media only screen and (max-width: 500px) {
  .no-ccstransforms .cn-wrapper{
    padding:.5em;
  }
  .no-csstransforms .cn-wrapper li{
    font-size:.9em;
    width:4em;
    height:4em;
    line-height:4em;
  }
}
@media only screen and (max-width: 480px) {
  .csstransforms .cn-wrapper{
    font-size: .68em;
  }
  .cn-button{
    font-size:1em;
  }
}
@media only screen and (max-width:420px){
  .no-csstransforms .cn-wrapper li{
    width:100%;
    height:3em;
    line-height:3em;
  }
}
And that’s pretty much all the CSS you need to create these styles!

The JavaScript

We won’t be using any JavaScript framework for these demos. I will be using David De Sandro’s Classie.js to add and remove classes. And finally for browsers that don’t support the addEventListener and removeEventListener we’ll use Jonathan Neal’s EventListener polyfill.
We’ll add an event handler to the button in each of the two demos. Clicking the button and/or focusing it will trigger opening/closing of the navigation.
Also, for the first demo, clicking anywhere outside the navigation when it’s open will also close it.
Let’s start with the JavaScript for the first demo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(function(){
  var button = document.getElementById('cn-button'),
    wrapper = document.getElementById('cn-wrapper'),
    overlay = document.getElementById('cn-overlay');
  //open and close menu when the button is clicked
  var open = false;
  button.addEventListener('click', handler, false);
  button.addEventListener('focus', handler, false);
  wrapper.addEventListener('click', cnhandle, false);
  function cnhandle(e){
    e.stopPropagation();
  }
  function handler(e){
    if (!e) var e = window.event;
    e.stopPropagation();//so that it doesn't trigger click event on document
      if(!open){
        openNav();
      }
    else{
        closeNav();
      }
  }
  function openNav(){
    open = true;
      button.innerHTML = "-";
      classie.add(overlay, 'on-overlay');
      classie.add(wrapper, 'opened-nav');
  }
  function closeNav(){
    open = false;
    button.innerHTML = "+";
    classie.remove(overlay, 'on-overlay');
    classie.remove(wrapper, 'opened-nav');
  }
  document.addEventListener('click', closeNav);
})();
The JavaScript for the second demo is similar to the previous one, except that we customize it for this case:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(function(){
  var button = document.getElementById('cn-button'),
    wrapper = document.getElementById('cn-wrapper');
    //open and close menu when the button is clicked
  var open = false;
  button.addEventListener('click', handler, false);
  button.addEventListener('focus', handler, false);
  function handler(){
    if(!open){
      this.innerHTML = "Close";
      classie.add(wrapper, 'opened-nav');
    }
    else{
      this.innerHTML = "Menu";
    classie.remove(wrapper, 'opened-nav');
    }
    open = !open;
  }
  function closeWrapper(){
    classie.remove(wrapper, 'opened-nav');
  }
})();
And that’s pretty much it! I hope you liked this tutorial and found it useful!