Thursday, September 13, 2012

HTML5 Calculator with Tape

Here's an HTML5 calculator I created today. It performs the usual core operations and adapts its size for desktop, tablet, or phone using CSS media queries. At desktop or tablet size, it displays a results tape.This is client-side only HTML5, CSS, and JavaScript, so it doesn't require anything more than minimal web serving (I'm hosting these files out of low-cost Windows Azure blob storage).
View online demo  Download source code


The HTML includes the typical meta tag used in mobile web apps to prevent mobile browsers from zooming the page. The calculator body, display, buttons, and tape are all just styled divs. Keypress and mousedown(tap) events are handled with JavaScript code.

<!doctype html>
<html>
<head>
<title>Calculator</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<!--<meta name="viewport" content="width=100%">-->
<link rel="stylesheet" href="styles.css" />
</head>
<body onkeypress="javascript:keypress(event);">

<div class="calculator blackgradient largeshadow">
    <div id="result" class="display graygradient smallshadow">0</div>
    <div class="row">
        <div class="button smallshadow" onmousedown="javascript:allclear();"><div class="button-label">AC</div></div>
        <div class="button smallshadow" onmousedown="javascript:clearentry();"><div class="button-label">CE</div></div>
        <div class="button smallshadow" onmousedown="javascript:backspace();"><div class="button-label">BS</div></div>
        <div class="button blue smallshadow last" onmousedown="javascript:divide();"><div class="button-symbol">÷</div></div>
    </div>
    <div class="row">
        <div class="button smallshadow" onmousedown="javascript:seven();"><div class="button-label">7</div></div>
        <div class="button smallshadow" onmousedown="javascript:eight();"><div class="button-label">8</div></div>
        <div class="button smallshadow" onmousedown="javascript:nine();"><div class="button-label">9</div></div>
        <div class="button blue smallshadow raise last" onmousedown="javascript:multiply();"><div class="button-symbol">×</div></div>
    </div>
    <div class="row">
        <div class="button smallshadow" onmousedown="javascript:four();"><div class="button-label">4</div></div>
        <div class="button smallshadow" onmousedown="javascript:five();"><div class="button-label">5</div></div>
        <div class="button smallshadow" onmousedown="javascript:six();"><div class="button-label">6</div></div>
        <div class="button blue smallshadow raise last" onmousedown="javascript:subtract();"><div class="button-symbol">-</div></div>
    </div>
    <div class="row">
        <div class="button smallshadow" onmousedown="javascript:one();"><div class="button-label">1</div></div>
        <div class="button smallshadow" onmousedown="javascript:two();"><div class="button-label">2</div></div>
        <div class="button smallshadow" onmousedown="javascript:three();"><div class="button-label">3</div></div>
        <div class="button blue smallshadow last" onmousedown="javascript:add();"><div class="button-symbol">+</div></div>
    </div>
    <div class="row">
        <div class="button smallshadow" onmousedown="javascript:zero();"><div class="button-label">0</div></div>
        <div class="button smallshadow" onmousedown="javascript:decimal();"><div class="button-label">.</div></div>
        <div class="button blue smallshadow" onmousedown="javascript:percent();"><div class="button-label">%</div></div>
        <div class="button blue smallshadow raise last" onmousedown="javascript:equals();"><div class="button-symbol">=</div></div>
    </div>
</div>
  
<div id="tape" class="tape"></div>

<script src="default.js" ></script>

</body>
</html>

The CSS styling is below. CSS media queries choose one of three sizes based on screen dimensions. I used the Voces font from Google Web Fonts for the result display.
@font-face {
  font-family: 'Voces';
  font-style: normal;
  font-weight: 400;
  src: url(voces.eot);
  src: local('Voces'), local('Voces-Regular'), url(http://themes.googleusercontent.com/static/fonts/voces/v1/UOrhtr4tgkrQ7HpJQ9-OIQ.eot) format('embedded-opentype'), url(http://themes.googleusercontent.com/static/fonts/voces/v1/ePr7eRI28zZaOtQ8CcT7rA.woff) format('woff');
}


body
{
    font-family: Segoe UI;
    font-size: 1.0em;
}


/* Containers */

.calculator {
    display: inline-block;
    text-align: left;
    border: 1px solid black;
    padding: 16px;
    border-radius: 8px;
}

.display {
    display: inline-block;
    width: 364px;
    text-align: right;
    border: 1px solid black inset;
    color: black;
    font-family: Voces, 'Segoe UI Semibold', Calibri, Arial, Helvetica;
    font-size: 2.0em;
    padding: 0px 4px 1px 4px;
    border-radius: 2px;
    margin: 0 0 12px 0;
}

.row {
    margin: 8px 0 0 0;
}

.button {
    display: inline-block;
    vertical-align: middle;
    width: 80px;
    height: 50px;
    border: 1px solid black inset;
    background: ghostwhite;
    color: black;
    font-family: 'Segoe UI Semibold', Calibri, Arial, Helvetica;
    border-radius: 8px;
    padding: 0px 4px 1px 4px;
    border-radius: 8px;
    margin: 0 4px 0 0;
}

.button :active {
    margin-top: 2px;
    margin-left: 2px;
    margin-right: 8px;
    margin-bottom: -2px;
}


.last {
    margin-right: -4px;
}

.button-label {
    display: inline-block;
    text-align: center;
    vertical-align: middle;
    line-height: 50px;
    width: 80px;
    font-size: 1.4em;
}

.button-symbol {
    display: inline-block;
    text-align: center;
    vertical-align: middle;
    line-height: 50px;
    width: 80px;
    font-size: 1.8em;
    font-weight: bold;
}

.blue {
    background-color: lightblue;
}


/* Shadows */

.largeshadow {
    -moz-box-shadow: 3px 3px 4px #000;
    -webkit-box-shadow: 3px 3px 4px #000;
    -ms-box-shadow: 3px 3px 4px #000;
    box-shadow: 3px 3px 4px #000;
}

.smallshadow {
    -moz-box-shadow: 3px 3px 3px #666666;
    -webkit-box-shadow: 3px 3px 3px #666666;
    box-shadow: 3px 3px 3px #666666;
}


/* Gradients */

.navy {
background-color: navy;
}

.blackgradient {
    background: rgb(149,149,149); /* Old browsers */
    background: -moz-linear-gradient(top,  rgba(149,149,149,1) 0%, rgba(13,13,13,1) 46%, rgba(1,1,1,1) 50%, rgba(10,10,10,1) 53%, rgba(78,78,78,1) 76%, rgba(56,56,56,1) 87%, rgba(27,27,27,1) 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(149,149,149,1)), color-stop(46%,rgba(13,13,13,1)), color-stop(50%,rgba(1,1,1,1)), color-stop(53%,rgba(10,10,10,1)), color-stop(76%,rgba(78,78,78,1)), color-stop(87%,rgba(56,56,56,1)), color-stop(100%,rgba(27,27,27,1))); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  rgba(149,149,149,1) 0%,rgba(13,13,13,1) 46%,rgba(1,1,1,1) 50%,rgba(10,10,10,1) 53%,rgba(78,78,78,1) 76%,rgba(56,56,56,1) 87%,rgba(27,27,27,1) 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  rgba(149,149,149,1) 0%,rgba(13,13,13,1) 46%,rgba(1,1,1,1) 50%,rgba(10,10,10,1) 53%,rgba(78,78,78,1) 76%,rgba(56,56,56,1) 87%,rgba(27,27,27,1) 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  rgba(149,149,149,1) 0%,rgba(13,13,13,1) 46%,rgba(1,1,1,1) 50%,rgba(10,10,10,1) 53%,rgba(78,78,78,1) 76%,rgba(56,56,56,1) 87%,rgba(27,27,27,1) 100%); /* IE10+ */
    background: linear-gradient(to bottom,  rgba(149,149,149,1) 0%,rgba(13,13,13,1) 46%,rgba(1,1,1,1) 50%,rgba(10,10,10,1) 53%,rgba(78,78,78,1) 76%,rgba(56,56,56,1) 87%,rgba(27,27,27,1) 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#959595', endColorstr='#1b1b1b',GradientType=0 ); /* IE6-9 */
}

.darkbluegradient {
    background: rgb(63,76,107); /* Old browsers */
    background: -moz-linear-gradient(top, rgba(63,76,107,1) 0%, rgba(63,76,107,1) 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(63,76,107,1)), color-stop(100%,rgba(63,76,107,1))); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top, rgba(63,76,107,1) 0%,rgba(63,76,107,1) 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top, rgba(63,76,107,1) 0%,rgba(63,76,107,1) 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top, rgba(63,76,107,1) 0%,rgba(63,76,107,1) 100%); /* IE10+ */
    background: linear-gradient(to bottom, rgba(63,76,107,1) 0%,rgba(63,76,107,1) 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3f4c6b', endColorstr='#3f4c6b',GradientType=0 ); /* IE6-9 */
}

.bluegradient {
    background: rgb(30,87,153); /* Old browsers */
    background: -moz-linear-gradient(top,  rgba(30,87,153,1) 0%, rgba(41,137,216,1) 50%, rgba(32,124,202,1) 51%, rgba(125,185,232,1) 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(30,87,153,1)), color-stop(50%,rgba(41,137,216,1)), color-stop(51%,rgba(32,124,202,1)), color-stop(100%,rgba(125,185,232,1))); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  rgba(30,87,153,1) 0%,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  rgba(30,87,153,1) 0%,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  rgba(30,87,153,1) 0%,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%); /* IE10+ */
    background: linear-gradient(to bottom,  rgba(30,87,153,1) 0%,rgba(41,137,216,1) 50%,rgba(32,124,202,1) 51%,rgba(125,185,232,1) 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1e5799', endColorstr='#7db9e8',GradientType=0 ); /* IE6-9 */
}

.graygradient {
    background: rgb(238,238,238); /* Old browsers */
    background: -moz-linear-gradient(top,  rgba(238,238,238,1) 0%, rgba(238,238,238,1) 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(238,238,238,1)), color-stop(100%,rgba(238,238,238,1))); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  rgba(238,238,238,1) 0%,rgba(238,238,238,1) 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  rgba(238,238,238,1) 0%,rgba(238,238,238,1) 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  rgba(238,238,238,1) 0%,rgba(238,238,238,1) 100%); /* IE10+ */
    background: linear-gradient(to bottom,  rgba(238,238,238,1) 0%,rgba(238,238,238,1) 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#eeeeee',GradientType=0 ); /* IE6-9 */
}

.yellowgradient {
    background: rgb(255,255,136); /* Old browsers */
    background: -moz-linear-gradient(top, rgba(255,255,136,1) 0%, rgba(255,255,136,1) 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,136,1)), color-stop(100%,rgba(255,255,136,1))); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top, rgba(255,255,136,1) 0%,rgba(255,255,136,1) 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top, rgba(255,255,136,1) 0%,rgba(255,255,136,1) 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top, rgba(255,255,136,1) 0%,rgba(255,255,136,1) 100%); /* IE10+ */
    background: linear-gradient(to bottom, rgba(255,255,136,1) 0%,rgba(255,255,136,1) 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffff88', endColorstr='#ffff88',GradientType=0 ); /* IE6-9 */
}


.tape {
    display: block;
    width: 396px;
    margin: 16px 0 0 0;
    min-height: 200px;
    background-color: lightyellow;
    color: black;
    text-align: right;
    padding: 8px;
    border: 1px solid black;
}


/* Windows Phone-size phone */

@media screen and (min-width:321px) and (max-width: 480px) {

    .display {
        width: 284px;
    }

    .button {
        width: 60px;
        height: 38px;
    }

    .button-label {
        line-height: 38px;
        width: 60px;
    }

    .button-symbol {
        line-height: 38px;
        width: 60px;
    }

    .tape {
        display: none;
    }
}


/* iPhone-size smartphone, portrait */

@media screen and (max-width : 320px) {

    .display {
        width: 204px;
        font-size: 1.2em;
    }

    .button {
        width: 40px;
        height: 25px;
    }

    .button-label {
        line-height: 25px;
        width: 40px;
        font-size: 1.0em;
    }

    .button-symbol {
        line-height: 25px;
        width: 40px;
        font-size: 1.2em;
    }

    .tape {
        display: none;
    }
}


/* iPhone-size smartphone / landscape */

@media screen and (max-height : 320px) and (orientation : landscape) {

    .display {
        width: 204px;
        font-size: 1.2em;
    }

    .button {
        width: 40px;
        height: 25px;
    }

    .button-label {
        line-height: 25px;
        width: 40px;
        font-size: 1.0em;
    }

    .button-symbol {
        line-height: 25px;
        width: 40px;
        font-size: 1.2em;
    }

    .tape {
        display: none;
    }
}

The JavaScript responds to mousedown (click/tap) events as well as keypress events to store numbers and operators and perform operations.

var number = '';
var result = document.getElementById('result');
var tape = document.getElementById('tape');
tape.scrollTop = 10000;


function keypress(e) {
    switch (e.keyCode) {
        case 8: // backspace
            backspace();
            break;
        case 67: // C
        case 99: // c
            allclear();
            break;
        case 46: // .
            decimal();
            break;
        case 48: // 0
        case 49:
        case 50:
        case 51:
        case 52:
        case 53:
        case 54:
        case 55:
        case 56:
        case 57: // 9
            enter(String.fromCharCode(e.keyCode));
            break;
        case 42: // *
        case 120: // x
        case 88: // X
            multiply();
            break;
        case 47: // /
            divide();
            break;
        case 43: // +
            add();
            break;
        case 45: // -
            subtract();
            break;
        case 13: // ENTER
        case 32: // SPACE
        case 61: // =
            equals();
            break;
    }
}


function allclear() {
    number = '';
    operator = null;
    result.innerText = "0";
    tape.innerHTML = '';
}

function backspace() {
    if (number.length > 0) {
        number = number.substr(0, number.length - 1);
        if (number.length === 0) {
            result.innerText = '0';
        }
        else {
            result.innerText = number;
        }
    }
}

function clearentry() {
    number = '';
    result.innerText = '0';
}


function nine() {
    enter('9');
}

function eight() {
    enter('8');
}
function seven() {
    enter('7');
}

function six() {
    enter('6');
}

function five() {
    enter('5');
}

function four() {
    enter('4');
}

function three() {
    enter('3');
}

function two() {
    enter('2');
}

function one() {
    enter('1');
}

function zero() {
    enter('0');
}

function decimal() {
    if (number && number.indexOf('.')===-1) {
        enter('.');
    }
}



function enter(digit) {
    number = number + digit;
    result.innerText = number;
}


function multiply() {
    if (!number) {
        number = parseFloat(result.innerText);
    }
    number1 = number;
    operator = "x";
    number = '';
    result.innerText = '0';
    tape.innerHTML += number1.toString() + "<br/>× ";
}

function divide() {
    if (!number) {
        number = parseFloat(result.innerText);
    }
    number1 = number;
    operator = '/';
    number = '';
    result.innerText = '0';
    tape.innerHTML += number1.toString() + "<br/>÷ ";
}

function add() {
    if (!number) {
        number = parseFloat(result.innerText);
    }
    number1 = number;
    operator = "+";
    number = '';
    result.innerText = '0';
    tape.innerHTML += number1.toString() + "<br/>+ ";
}

function subtract() {
    if (!number) {
        number = parseFloat(result.innerText);
    }

    // If entered with no number entered, this is a leading minus.

    if (number.length == 0) {
        enter('-');
        return;
    }

    // Subtraction operator.

    number1 = number;
    operator = '-';
    number = '';
    result.innerText = '0';
    tape.innerHTML += number1.toString() + "<br/>- ";
}

function percent() {
    if (!operator) {
        return;
    }
    number = (parseFloat(number1) * (parseFloat(number)/100)).toString();
    result.innerText = number;
}


function equals() {
    if (!operator) {
        return;
    }
    try
    {
        switch (operator) {
            case 'x':
                result.innerText = (parseFloat(number1) * parseFloat(number)).toString();
                tape.innerHTML += number.toString() + "<br/>--------------------<br/>= " + result.innerText + "<br/>&nbsp;<br/>";
                number = '';
                break;
            case '/':
                result.innerText = (parseFloat(number1) / parseFloat(number)).toString();
                tape.innerHTML += number.toString() + "<br/>--------------------<br/>= " + result.innerText + "<br/>&nbsp;<br/>";
                number = '';
                break;
            case '+':
                result.innerText = (parseFloat(number1) + parseFloat(number)).toString();
                tape.innerHTML += number.toString() + "<br/>--------------------<br/>= " + result.innerText + "<br/>&nbsp;<br/>";
                number = '';
                break;
            case '-':
                result.innerText = (parseFloat(number1) - parseFloat(number)).toString();
                tape.innerHTML += number.toString() + "<br/>--------------------<br/>= " + result.innerText + "<br/>&nbsp;<br/>";
                number = '';
                break;
        }
    }
    catch (e) {
        result.innerText = "Error";
    }
    operator = null;
    number = '';
}

I'm writing this and other small HTML5 apps in order to hone my skills in making mobile web apps a good experience that perform well.

Monday, September 10, 2012

Are You a Lopsided Web Developer?

Mastering the modern web stack is a challenge, and it can easily lead to lopsided web developers. In this post we’ll explain why that is and what you can do about it.
 Why We’re Lopsided

The web has always been fast-moving but the “HTML5 wave” (which is so much more than HTML) has brought an explosion of change and it is still unfolding around us. There’s not one technology or API to learn, but many; and some of them have a large surface area. We have to learn new techniques to use our new tools properly. And then there’s the all-important skill of being able to properly combine it all into a well-composed solution, which is really something we learn on-the-job through project work. Projects we want to see succeed, not fail.

Given all this flux, it’s quite likely you and your fellow web developers haven’t learned all the necessary skills to the same degree. Did I say likely? Let’s put it more strongly: we’re all jagged, every one of us. Serious web developers realize the need for a deep and ongoing investment in realigning their web skills—but even for those of us who’ve made that commitment, there’s a real danger of being lopsided.

Don’t make the mistake of thinking of your web developers’ maturity in simplistic terms like senior, intermediate, and beginner—there’s variance across skills which leaves most web developers with an odd mix of maturity and immaturity. Assemble a group of these lopsided web developers into a team and you’re taking quite a gamble. If you want a consistent baseline in your web developers, you’ll have to ensure each vital skill has been sufficiently mastered.




Combatting Lopsidedness
Let’s look at some ways to ensure you’ve got the right skills coverage:

1. Clearly Define Your Web Stack
2. Right-Size Your Web Stack
3. Avoid One-Tool-To-Rule-Them-All Syndrome
4. Learn the Soul of Your Web Tools
5. Master Essential Techniques
6. Use Frameworks with Care
7. Don’t Build a Duck: Compose Elegant Solutions


1. Clearly Define Your Web Stack



In order to learn the new web stack you’ve got to identify what it is. The new web stack isn’t precisely defined because it is open-ended. On the client side, it begins concretely enough with HTML5, CSS3, and JavaScript, but where it goes from there is your call. Hopefully you include those never-leave-home-without-them core libraries like Modernizr and jQuery. There are oodles of other libraries out there you might also want to leverage. On the server side, you’ll have your preferred server platform and the services you make use of. So, your ideal web stack will be similar but not identical to someone else’s. You want to define your core stack very clearly and ensure all of your fellow developers are on the same page about it and know its components well.

In the consulting practice I lead at Neudesic, our current web stack is shown below. A project team will determine their web stack by starting with a standard core, adding some of our “practice favorites” if they make sense, and possibly additional libraries if the project warrants it (such as a charting library). Teams may depart from the standard stack, but have to justify it—and the greater the departure, the greater the justification needed. Consultants all learn the same standard web stack, and that means they arrive at projects with the same baseline.


 
 


2. Have the Right Tools in Your Toolbox



Because the new web stack is open-ended, what you end up with could easily be too small or too large.

If your toolbox is too small, developers are going to misuse some of the tools they do have in order to compensate for what’s missing and end up doing a lot of things the hard way. For example, leaving out Modernizr makes the job of handling browser compatibility quite a bit harder. Or tackling significant DOM manipulation without using jQuery (or an equivalent) will have you writing a whole lot of JavaScript code that could have been a one-liner.

If your toolbox is very large, containing superfluous or seldom-needed tools, that’s going to increase the likelihood of using the wrong tool for the wrong job or add unnecessary baggage to your solution.


3. Avoid One-Tool-To-Rule-Them-All Syndrome
To a man with just a hammer, everything looks like a nail. We’ve already mentioned the danger of having too few tools in your toolbox. But there’s a subtler version of this problem: even if your toolbox has the right collection of tools, it could be that you’ve learned just one of those tools really well and use it to exclusion of everything else. An example of this is doing things in JavaScript code that would be handled more efficiently by leveraging CSS and letting the browser do the work.



4. Learn the Soul of Your Web Tools



Do you know your web technologies and libraries sufficiently well, or do you have gaps? How can you even know?

When learning a tool, memorizing details like syntax matters less than understanding its capabilities, philosophy, and core concepts. You can always look up details online.

Capabilities are something you want to commit to memory. As you think through your solution, it won’t occur to you to use a particular tool if you aren’t aware of what it can do. For example, let’s say you need to do some asynchronous communication with your server. jQuery is a great way to do that—but if you’ve only been thinking of jQuery as a DOM manipulation library it might not occur to you to use it for communication.

Each web technology came into being for a reason, and may have a storied history; knowing that will help you understand its essence. If you grok the philosophy and core concepts of a technology, you’ll find yourself applying it in ways that work well; if not, you’ll sometimes be going against the grain and will run into trouble. For example, CSS exists to separate markup from styling. If you’re aware of and appreciate that intent, you’ll be hesitant to put style attributes directly in your markup.

It’s also necessary to appreciate that some technologies have a dark side that should be avoided. A prime example is JavaScript, a language with some very neat dynamic qualities but also quite a few flaws and oddities. This prompted Douglas Crockford to write JavaScript: The Good Parts, in which he differentiates between the good parts of JavaScript and those areas that should be avoided at all costs. Limiting yourself to the acceptable subset of a technology is a winning strategy in my book.

If you’ve gotten to know the soul of a tool, then books, articles, code samples, etc. from authoritative sources will make eminent sense to you; your own work will progress readily, and you may have one or more “breakthrough moments”. If you find yourself being confused, that suggests you don’t understand the tool’s essence yet.


5. Master Essential Techniques



Craftsmen not only know their tools well, they have mastered essential techniques. Modern web development also requires techniques. Three essential ones are responsive web design, handling browser compatibility, and supporting touch.

Responsive Web Design allows web sites to adapt to different devices and screens large and small. CSS Media Queries are used to invoke conditional style rules based on screen size or other device characteristics. The basic idea of RWD is easily understood, but the finer points often escape developers. Developers should read through Ethan Marcotte’s book Responsive Web Design and keep up with the latest online thinking about the nuances of RWD such as responsive content and responsive images. You can get some aspects of RWD for free by using responsive frameworks such as Twitter Bootstrap, but web developers still need to understand and apply the technique in their own work.

Handling browser compatibility is easier than it used to be but it isn’t automatic and requires attention by designers and web developers. Web projects should always clearly specify the target devices and browser versions. Using the Modernizr library and other shims and polyfills, the latest HTML5 features can be detected and used when present, or replaced by fallback behaviors when not available. You should always provide an intentional experience, even for unsupported devices or when JavaScript isn’t permitted to run.

If you’re going to be supporting mobile devices you’ll need to be sure you are supporting touch well. Your user interface design needs to ensure visual elements are not only large enough to click but large enough to touch. Don’t depend on affordances like hovering that apply to a mouse but not to a finger. While a simple finger tap will be treated like a mouse-click by your browser, sophisticated touch support requires specific programming.


6. Use Frameworks with Care


The web abounds with frameworks, many of them free, open source, and of high quality. Let’s define a framework as more than a passive library and less than a platform. Frameworks are essential but also a two-edged sword: you certainly want to be using some because of the power they provide, but it can be a problem to use the wrong ones or to have too many in the mix. There’s nothing more frustrating than to get into a fragile state where you have many JavaScript libraries and they are fighting each other.

The best frameworks follow the principles of unobtrusive JavaScript. They don’t get in the way, and they play nicely with other frameworks. jQuery and Modernizr are examples. It’s a good idea to endorse frameworks you have come to trust for general use, whereas “newcomer” frameworks should be put on a probationary status until they have proven themselves.

From client-side scaffolding like Twitter Bootstrap to server-side ORMs like Microsoft's Entity Framework, using a framework inherently means you are surrendering some degree of control to the framework. Don't lose control, which can happen if you don't sufficiently understand the framework.
 
Some frameworks such as Knockout or Backbone provide automated behaviors like data binding. Automatic behaviors can be fantastic, but it’s at this point where you to start to run into trouble if you're reckless. Avoid over-using automatic frameworks beyond sensible levels and keep in mind that automatic behaviors usually impose a certain amount of overhead.

Lastly, even if you’ve approved a number of frameworks for general use that doesn’t mean you always need to be using them. It’s pointless to include a framework just because you’re used to doing so. If it isn’t actually doing something useful for you, ask yourself why it is there.

One of Kurt Vonnegut’s eight writing tips is, “Every sentence must do one of two things—reveal character or advance the action.” I’d like to propose a web equivalent: “Every element of your web solution must do one of two things—help create the user experience or provide useful functionality.”



7. Don’t Build a Duck; Compose Elegant Solutions



A duck can walk, fly, and swim—but doesn’t do any of these things well. Don’t build a duck.

Individual tools and techniques have to be combined well in order to create a satisfactory web solution, and that is another skill area that needs to be updated for the modern web stack: artful composition in solution design. Make the right technical decisions and compose them elegantly, and you end up with a Maserati. Fail to do so and you can end up with an overly-complex result resembling a Rube Goldberg device.

We’ve all seen monstrous, hopelessly-tangled markup, CSS, or JavaScript code in our time. If you’ve been developing for any period of time you’ve hopefully also encountered at least once in your career code that shines and feels amazingly right. Well-organized, easy and intuitive to understand, well-balanced, easily extended, artfully combined. Don’t settle for less in your modern web solutions. If what you’re building feels forced, complex, cumbersome, unwieldy, fragile, or out of control it probably is. Go back to the drawing board and start over or refactor.

There are some new application models and design patterns out there, such as Single Page Applications and CQRS, which are exciting but shouldn’t be jumped into blindly. Expect anything new to carry some elements of risk until you know them well enough. Budget time for on-the-job learning and refactoring; it’s unrealistic not to. Since solution design is ultimately learned on-the-job, oversight and mentoring by your most experienced people is critical.



Closing Thoughts

Learning the modern web stack is both a necessity and an opportunity. Preparing for it should not be under-estimated. Avoid being a lopsided web developer by recognizing all of the skills you need to have and committing to mastering each of them to a sufficient level of maturity.

Take advantage of the fact that you are not alone in this. Work with others to help identify and close each other’s gaps. Leverage the many great online web resources and communities as the world's web developers help each other get to where they need to be.