Menu Search
Jump to the content X X
Smashing Conf Barcelona

You know, we use ad-blockers as well. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

Developing Dependency Awareness

I’m sure you’ve heard the proverb, “A chain is only as strong as its weakest link,” probably many times. Its written origin dates back to the 18th century, but I wouldn’t be surprised if it was much, much older. And though the work we do has little to do with actual chains, this proverb is every bit as relevant to us.

Remember when Azer Koçulu unpublished more than 250 of his modules from npm1 (Node Package Manager)? If that name doesn’t ring a bell, perhaps this function name will: left-pad. In case you’re still scratching your head wondering what the heck I’m talking about, Azer removed a bunch of functions from the canonical library of reusable Node.js code and, in doing so, brought thousands of projects to their knees, including high-profile ones like Babel and React. You see, each of these larger libraries included his left-pad module as a dependency. When that dependency was no longer available, building and deploying these projects became impossible.

Further Reading on SmashingMag:

And left-pad was only eleven lines of JavaScript that added padding to the left side of a string. Dependencies are a huge cause for concern.

But perhaps you’re not a Node.js user. If that’s the case, do you like jQuery? How about CDNs? The jQuery CDN? Well, here’s a little story about that.

Late on the night of January 25, 2014 the parental filter used by Sky Broadband – one of the UK’s largest internet service providers (ISPs) – began classifying code.jquery.com5 as a “malware and phishing” website. The jQuery CDN is at that URL. No big deal – jQuery is only the JavaScript library that nearly three-quarters of the world’s top 10,000 websites rely on to make their web pages work.

With that domain so sadly mischaracterized, Sky’s firewall leapt into action and began protecting their customers from this malicious code. All of a sudden, huge swaths of the web abruptly stopped working for each and every Sky Broadband customer who had not explicitly opted out of this protection. To put it another way: any site that relied on a version of jQuery hosted by the jQuery CDN to load content or enable users to do things was dead on arrival.

In this particular instance, the weak link wasn’t jQuery per se; it was the CDN. You see, as a dependency, jQuery existed externally from the HTML documents and required a separate request (assuming it wasn’t already in the cache). Any such request was being denied by Sky’s firewall, so the file was never delivered. The dependency was not met and brought numerous sites to their knees.

Networks are fickle beasts and firewalls aren’t the only things that can cause a request to be denied or go unanswered. Mobile networks, for example, rely on transmission through the air via various wavelengths. Depending on the topography of the region, surrounding buildings, the materials they are made of, and even other networks, your user might venture into (or even reside within) a dead zone where mobile coverage is spotty or non-existent. Or there’s the oft-referenced tunnel scenario, which can cause a mobile connection to be dropped.

Similarly, slow networks can often give the impression of lost connectivity. Mobile networks often suffer from high latency, meaning requests and responses can be delayed. Hotel Wi-Fi and other public hotspots are also often crippled by transfer speed caps or high usage. On numerous occasions, I’ve waited several minutes for a page to load. Sometimes that page is even the “Join this network” splash screen.

To combat the issues brought about by high-latency networks, it became a best practice to embed your CSS and JavaScript in pages aimed at mobile devices. While this approach increased the size of the HTML files being delivered, it mitigated the risk of the network causing your site to break by minimizing external dependencies. Interestingly, this practice has come back in vogue, with many folks recommending we embed critical CSS and JavaScript to reduce rendering times6 and embedding graphics using data URIs7.

Reducing dependencies improves the likelihood that your site will be usable by the greatest number of people in the widest variety of scenarios. Even knowing this, however, it’s easy to overlook the most basic dependencies our projects have, undermining their resilience in the process. To illustrate this point, consider the humble submit button.

Not All Buttons Are Created Equal Link

There are several ways you could mark up a submit button. The simplest uses the input element:

<input type="submit" value="Sign Up">

Another option is the button element:

<button type="submit">Sign Up</button>

I prefer button[type=submit] over input[type=submit] because the button’s text can be enhanced with other semantic elements like em and strong, but that’s a subject for another day.

A seemingly simple HTML button8

A seemingly simple HTML button. (View large version9)

Another option we often see on the web uses an anchor (a):

<a href="#">Sign Up</a>

Like button above, the a element can contain other markup, which is handy.

For the purposes of this discussion, the final markup pattern I’m going to talk about uses a division element (div):

<div>Sign Up</div>

This is a markup pattern that was popularized by Gmail and has become pretty common in the single-page apps space.

If we subscribe to common wisdom, these are all valid options for coding buttons. They can be, but how they get there is far more complicated. Let’s dissect each and see where we end up.

I Value Your input Link

An input[type=submit] is about as simple as you can get. Visually, it looks like a button, even in a text-based browser. Assistive technology sees this element as a button. It’s capable of receiving focus and it can be activated via the mouse, touch, and the keyboard (using either the space bar or Enter key). And finally, and most importantly, using this markup creates a button capable of submitting whatever form contains it.

A submit button rendered as text in the Lynx browser. When the cursor is on the button, text informs you it can be used to submit the form using the <code><kbd></code>Enter key.10

A submit button rendered as text in the Lynx browser. When the cursor is on the button, text informs you it can be used to submit the form using the Enter key. (View large version11)

You get all of this functionality for free. The input[type=submit] has no dependencies apart from a browser supporting HTML forms, which they all do (forms were introduced in HTML 2.0).

Cute As a button Link

A button[type=submit] has exactly the same feature-set with the same number of dependencies: zero, zilch, nada. Sure, you can spice up the design with a little CSS or hijack the form submission to post the form asynchronously with JavaScript, but those are enhancements to the basic design and functionality you get out of the box with these elements.

Anchors Away! Link

The a element is a different story altogether. First off, by default, an a is rendered as inline text with an underline; you will need to involve CSS to make it look like a button. That’s dependency #1. By default, assistive technology will see this a as a generic element because it’s an anchor link to nowhere; you will need to use the role attribute12 to expose it as a button. That’s dependency #2.

<a href="#" role="button">Sign Up</a>

Like a true button, an a is inherently capable of receiving focus, so you’re good there. One issue, however, is that a elements can only be activated via the Enter key, whereas true buttons can also be activated by the space bar; you’ll need to use JavaScript to listen for a space bar keypress. That’s dependency #3. Finally, an a can’t submit a form, which means you’ll need to involve JavaScript for that as well. That brings the total number of dependencies for this pattern to four, involving additional markup, CSS, and JavaScript.

The Vanilla Box Link

The final pattern I mentioned used a div, but could just as easily be a span or some other element with no (or few) browser default styles applied to it. This markup pattern has all of the dependencies of the a tag, and it brings a few of its own. On the CSS end of things, you’ll probably want to render it as an inline-block element and you’ll definitely need to give it a cursor pointer to make it appear interactive for sighted users (although it won’t actually be until JavaScript kicks in).

Unlike the a element, a div (or span, etc.) is not focusable. To add it to the default tab order of the page, you’d need to assign it a tabindex of 0:

<div role="button" tabindex="0">Sign Up</div>

While not a dependency in the same sense that CSS, JavaScript, and ARIA are (which we’ll get to in a moment), this additional markup is a dependency in the development process because you need to remember to add it. Failing to do so makes the div completely inaccessible to keyboard users.

Button Dependencies At A Glance Link

Since that was a substantial amount of information to follow, here’s a quick overview of the default state of affairs.

Pattern Display Semantics Focusable? Activate By Submits Forms
input[type=submit] Button Button Yes
  • Mouse
  • touch
  • Enter key
  • spacebar
Yes
button[type=submit] Button Button Yes
  • Mouse
  • touch
  • Enter key
  • spacebar
Yes
a Link Named Generic Yes
  • Mouse
  • touch
  • Enter key
No
div Block Not exposed No Nothing No
Button coding patterns and their default capabilities

Now let’s look at the same patterns through the lens of dependencies required to achieve button-ness.

Pattern Display Semantics Focus Activation Form Submission
input[type=submit] None None None None None
button[type=submit] None None None None None
a CSS ARIA None JavaScript JavaScript
div CSS ARIA HTML JavaScript JavaScript
Button coding patterns and required dependencies

While it may appear on the surface that these approaches are similar, by using either of the latter two patterns (a and div), we are greatly increasing the number of dependencies our button requires to do its one and only job: enable users to submit a form.

Some of you may be wondering why this is such a big deal. After all, everyone has CSS and JavaScript at least, right? Well, no. Not necessarily. You could probably argue that most users today have access to a browser that has some amount of CSS and JavaScript support, but that is by no means a thumbs up to depend on it being there when you need it.

Here are a few things that can cause your CSS dependency to remain unmet:

  • The browser doesn’t support CSS.
  • The user disabled CSS for performance reasons.
  • The user is applying a user style sheet (which trumps your rules) to improve accessibility or for some other personal preference.
  • A networking issue caused the external CSS to be unavailable.
  • The selector you are using is too advanced for the browser.
  • The rules are contained in a media query and the browser doesn’t support them or the query doesn’t apply.

On the JavaScript side of things, there are some similar potential blockers and some other things to consider:

  • The browser doesn’t support JavaScript.
  • JavaScript was disabled by the user.
  • A networking issue caused the JavaScript to be unavailable.
  • A firewall blocked requests for JavaScript.
  • A browser plugin blocked the JavaScript download or execution.
  • A third-party JavaScript error caused the JavaScript program to stop.
  • A bug in your code caused the JavaScript program to stop.
  • The browser failed a feature detection test and exited the program early.
  • The user is still waiting for the browser to download, parse, and execute your JavaScript program.

Even ARIA is not without pitfalls. If the browser and assistive technology are not in sync in terms of their level of support, weird things can happen. Another potential issue is if the ARIA role is understood and applied, but the JavaScript is not available to make the a or div function like a true button, your users will be pretty frustrated when it seems like they should be able to use a button and they can’t.

Note: I’ve put together a demo of these different markup patterns13 that enables you to view them in a few different scenarios. Feel free to have a play.

Hope For The Best, Plan For The Worst Link

We don’t control where our web-based products go or how our users access them. All we can do is imagine as many less-than-perfect scenarios as possible and do our best to ensure our creations will continue to do what they’re supposed to do. One of the easiest ways to do that is to be aware of and limit our dependencies.

Do you only have a few enhancements you want to add to your site using JavaScript? Don’t bother with a JavaScript library. Vanilla JavaScript is often the best choice.14 If it’s code that only pertains to a single page, consider embedding it before the closing body tag.

Do you have a hard dependency on jQuery or some other JavaScript library? Go ahead and use a public CDN to include it – since that will give you a performance boost – but fall back to a local copy if that one’s not available. The HTML5 Boilerplate15 does this quite elegantly:

<script src="https://code.jquery.com/jquery-{{JQUERY_VERSION}}.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-{{JQUERY_VERSION}}.min.js"><\/script>')</script>

In this simple code example, the first script element requests whichever jQuery version you require from the jQuery CDN. The second script element – which executes after the first one is evaluated – checks to make sure jQuery is available. If it isn’t, then another script element is inserted into the document, referencing a local copy on the server.

Of course, it’s possible that the browser might fail to retrieve both copies of jQuery, so any plugins or jQuery-dependent code you write should also test for the jQuery object before attempting to do anything:

(function(window){
  // Do we have jQuery?
  if(! 'jQuery' in window){ return; }
  // Phew! It’s safe to use jQuery now.
}(this));

And, of course, you should always assume there’s going to be a scenario where a user doesn’t get your JavaScript enhancements at all, whether jQuery-based or otherwise. Have a fallback that uses HTML and the server. It may seem old-school, but it will ensure your users can sign up for your service, buy your products, or post photos of their kittens, no matter what.

Dependencies are everywhere. They’re unavoidable. They aren’t inherently bad, but if you don’t consider the possibility a given dependency might not be met, you run the risk of frustrating your users. You might even drive them into the arms of your competition. So be aware of dependencies. Address them proactively. And do everything you can to build a baseline experience with no dependencies at all and then use them to enhance the experience as they are met.

(rb, ml, og, il)

Front page image credits: NASA16.

Footnotes Link

  1. 1 http://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/
  2. 2 https://www.smashingmagazine.com/2014/03/better-dependency-management-team-based-wordpress-projects-composer/
  3. 3 https://www.smashingmagazine.com/2017/02/a-detailed-introduction-to-webpack/
  4. 4 https://www.smashingmagazine.com/2016/06/harness-machines-productive-task-runners/
  5. 5 http://code.jquery.com
  6. 6 https://www.filamentgroup.com/lab/performance-rwd.html
  7. 7 https://css-tricks.com/data-uris/
  8. 8 https://www.smashingmagazine.com/wp-content/uploads/2016/05/01-button-opt.png
  9. 9 https://www.smashingmagazine.com/wp-content/uploads/2016/05/01-button-opt.png
  10. 10 https://www.smashingmagazine.com/wp-content/uploads/2016/05/02-text-browser-opt.png
  11. 11 https://www.smashingmagazine.com/wp-content/uploads/2016/05/02-text-browser-opt.png
  12. 12 https://www.w3.org/TR/wai-aria/roles#button
  13. 13 http://s.codepen.io/aarongustafson/debug/qZQzmO
  14. 14 http://vanilla-js.com/
  15. 15 https://html5boilerplate.com/
  16. 16 https://www.flickr.com/photos/gsfc/6962625139

↑ Back to top Tweet itShare on Facebook

As would be expected from a former manager of the Web Standards Project, Aaron Gustafson is passionate about web standards and accessibility. He has been working on the Web for two decades now and is a web standards advocate at Microsoft, working closely with their browser team. He writes about whatever’s on his mind at aaron-gustafson.com.

  1. 1

    Gunnar Bittersmann

    May 23, 2016 2:44 pm

    “By default, assistive technology will see this a as a generic element because it’s an anchor link to nowhere”

    <a href="#"> is a link to somewhere, actually: to the top of the page. To prevent a jump, you need JavaScript to suppress the default action (following the link), that’s dependency #1½.

    5
    • 2

      Thierry Koblentz

      May 23, 2016 7:17 pm

      If all you need is to avoid a “jump to top” then you do not need JavaScript for that, you can simply use a non-existing fragment-identifier, for example:

      <a href="#0">

      0
      • 3

        Thierry Koblentz

        May 23, 2016 7:18 pm

        This was supposed to show <a href=”#0″>

        0
      • 4

        Gunnar Bittersmann

        May 24, 2016 9:01 am

        If all you need is to avoid a jump, then don’t use the ‘a’ element.
        If you don’t want to link to another ressource or to a fragement of the current ressource, ‘a’ is the wrong element. Use ‘button’.
        And CSS to style that ‘button’ element as you wish.

        1
        • 5

          Thierry Koblentz

          May 24, 2016 6:34 pm

          I was not promoting the use of `link` in lieu of `button`, I was responding to your statement that read:

          “To prevent a jump, you need JavaScript to suppress the default action”.

          0
        • 6

          “And CSS to style that ‘button’ element as you wish.”

          False. Button, fieldset, legend, table, input[type=”file”], and few others are heavily restricted.

          0
          • 7

            Gunnar Bittersmann

            May 31, 2016 3:39 pm

            fieldset, legend, input[type=”file”] are heavily restricted, indeed.
            button and table are not.

            1
    • 8

      True enough. I was speaking specifically of the semantics in terms of the accessibility tree, but you are correct. Preventing the default behavior would be another requirement and would fall to JavaScript as well.

      0
  2. 9

    Andrew Khan

    May 23, 2016 3:26 pm

    NPM is kind of a blessing and a curse at the same time.

    Inexperienced devs seem to fill their projects with novelty packages (especially with pre-processors) only for them to break 6 months later when I have to make changes.

    1
  3. 10

    Came here for the famous John Houbolt photo. The process he is describing is LOR (Lunar Orbit Rendezvous). In the begining, “direct descent” was favored and LOR was ridiculed as too complex. But after a couple of years and Mr. Houbolt’s perseverance, it was determined that LOR wasn’t the best way, it was the ONLY way. When Apollo 11’s Eagle LEM landed on the moon, Werner Von Braun was in misson control and turned behind him a few rows back and said; “Thank you, John”. Off-topic, I know. But the space program is full of fascinating stories and analogies that are useful today.

    5
  4. 11

    Art Vandelay

    May 24, 2016 8:12 am

    IMHO, having a dependency on 11 lines of code is a pretty bad mistake.

    2
  5. 13

    Great article, but I had to read the section about buttons for quite a while before it was clear why we were suddenly talking about them. Might be good to stick in a line earlier on otherwise it feels like a strange change of subject when you read.

    1
    • 14

      I wonder if the image & caption placement made that transition harder. I tried to provide a smooth transition into that section with “To illustrate this point, consider the humble submit button.” Looking into (re)moving that interruption…

      0
  6. 15

    Back in the day, we were suspicious of external dependencies. Prefer to have everything on our own server just in case. Now I feel old.

    2
  7. 16

    leftpad-as-a-service

    May 24, 2016 6:04 pm

    Remember when that whiny brat Azer wrote profanity-laden tripe back at NPM staff? Or are we still demonizing the company for protecting the community’s interests?

    0
  8. 17

    Michał Sadowski

    May 25, 2016 11:27 am

    Every time I tell people I prefer working with vanilla js they look at me like I’m crazy. Whenever I say I don’t like frameworks and I use them more of a necessity they treat me like an alien. I think the dependencies culture is getting us nowhere — we’re already dealing with a very high level language and yet we still use more and more things that pile up points of failure and, at the very least, that completely gimp performance of code.

    4

↑ Back to top