TL;DR - Angular and Skip Links

Cassettes, man...

We've all got that one friend who's a little long in the mouth when it comes to their stories. "You're never going to believe what happened..." Pretty sure I will believe it. And pretty sure it will be 15 minutes spent before an underwhelming payoff. More often than not, we let them drone on, making occasional nods toward inconsequential plot points or details we've heard before, secretly hoping they would just get to the point.

If you were born before the 90s, transport yourself back to the height of the cassette tape era, when you cursed your favorite artist for not putting that one track in the first three positions, or worse still, putting it somewhere in the nebulous expanse of side B. And then remember the glorious experience of your first compact disc and being able to listen to songs on demand. Being rescued from analog was practically a Damascus experience.

I see what you did didn't do there...

For persons without a visual impairment, you now have all the frame of reference you need to imagine experiencing a website without the sense of sight. Sadly, for many web developers, assistive technology users are a secondary consideration, if a part of their development lifecycle at all. This is less true when you look at various public-facing or service-oriented entities in private and government sectors. Successful corporations and agencies avoid a litigious fate by dedicating resources toward or at least bundling accessibility concerns within user experience frameworks, etc. In the case of government, Section 508 of the ADA mandates are interpreted and fulfilled by each agency.

As a sighted, mobile developer who is inordinately dependent on a mouse, I frequently need to orient my mind toward keyboard users. I need to remind myself about the importance of semantic HTML and a well-structured document. As I stretch to learn the latest and greatest thing to hit the interwebs, the more important it is that I consider its potential impact on AT users. And while the solution for some may be to find a turnkey template that addresses all of these concerns (some people really don't like going down this path), I find a lot of merit in trying to build the pieces. But it starts small, and some of these things are as easy to implement as they are to overlook. I've already given you an example.

Again, imagine experiencing a website as a blind or low-vision user, who relies on screen-reading software to read website content to them. Friends of Bootstrap, what typically characterizes the beginning content of the <body> tag? Navigation. And what is typically repeated again and again for every page in your website? Navigation. And what do you think taxes the patience of AT users if they have to listen to it over and over again? Navigation. It just so happens that there's guidance in the WCAG on how to remedy this: Skipping Links. AKA Skip to content links. AKA Bypass Blocks. Here are the specs:

WCAG 2.4.1

... to allow people who navigate sequentially through content more direct access to the primary content of the Web page. Web pages and applications often have content that appears on other pages or screens. Examples of repeated blocks of content include but are not limited to navigation links, heading graphics, and advertising frames.

This is in contrast to a sighted user's ability to ignore the repeated material either by focusing on the center of the screen (where main content usually appears) or a mouse user's ability to select a link with a single mouse click rather than encountering every link or form control that comes before the item they want.

Bypass Blocks, Understanding SC 2.4.1

ADA Section 508

A method shall be provided that permits users to skip repetitive navigation links.

repetitive navigation links - a set of routine navigation links that appear on the top or the side on a web page.

§1194.22 Web based intranet and Internet information and applications

Angular, not Angry-lar

It seems like a pretty straightforward component to implement. Remember these days?

<a href="#cheddar">Get to the good stuff, dummy.</a>  
...
<div id="cheddar">Finally. Cheese, er uh, Jeez.</div>  

But jump links aren't as straightforward when you're looking to implement them in Angular 2 4, especially if you are making use of the router, which already hijacks default anchor tag behavior. What follows is the result of a deep dive into the API documentation for a simple, easy, and practically pedestrian implementation of bypass blocks.

Use Angular CLI to generate a new project, followed by installing Bootstrap and its dependencies for some quick styling.

ng new tldr

npm install bootstrap@4.0.0-alpha.6 jquery tether --save  

Make sure you bundle these dependencies into your builds.

.angular-cli-json
...
"styles": [
  "../node_modules/bootstrap/dist/css/bootstrap.min.css",
  "styles.css"
],
"scripts": [
  "../node_modules/jquery/dist/jquery.slim.min.js",
  "../node_modules/tether/dist/js/tether.min.js",
  "../node_modules/bootstrap/dist/js/bootstrap.min.js"
],
...

Generate a nav component, and give it some bootstrap components in its template.

ng g c nav  
<header>  
  <nav class="navbar navbar-toggleable-md navbar-light bg-faded">
    <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <a class="navbar-brand" href="#">
      <h1>Manx Spanx</h1>
    </a>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
          <a class="nav-link" href="#">About</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Cat Facts</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Kitty Couture Store</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Contact</a>
        </li>
      </ul>
    </div>
  </nav>
</header>  

In the component template, wire up a click event on a link with an empty href. There is the question of preventing link activation from invoking the router. After trying a couple of options like href="javascript:void(0);" or href="javascript:;", I settled on onclick="return false;". Decorate your target with a template reference variable, as well as tabindex="-1" to make it focusable.

app.component.html
<a (click)="skipLink()"  
   href 
   onclick="return false;" 
   class="sr-only sr-only-focusable btn btn-link"
   accesskey="k">
  Skip to content
</a>  
<app-nav></app-nav>  
<div #main  
     tabindex="-1">
  ...
</div>  

In your component, use @ViewChild to pull in the template reference variable. Inject the Renderer service, which is the Angular abstraction for manipulating the application environment, whether DOM, desktop, mobile, or server. Finally, give the click event method some teeth, courtesy of Renderer.

app.component.ts
import { Component, ViewChild, ElementRef, Renderer, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {  
  @ViewChild('main') main: ElementRef;

  constructor(
    private renderer: Renderer
  ) {}

  skipLink() {
    this.renderer.invokeElementMethod(this.main.nativeElement, 'focus');
  }

  ngOnInit() {

  }
}

Run ng serve and tab / shift+tab to discover the skip link and test.

Skip Link Demo screenshot

Wrap It Up

Whatever your Angular implementation, here are a couple of items that should be on your checklist.

  • Navigation skip link should be the first focusable element on your page.
  • Target areas to where one is skipping need to be programmatically focusable. Be sure to add tabindex="-1". Some browsers, like Chrome, have a visible focus indicator, so consider overriding this in your Sass/CSS.
  • Skip links should be hidden visually. Make sure that sighted keyboard users can view your skip links when focused. Bootstrap makes this easy with the .sr-only-focusable class.
  • Give your skip links keyboard shortcuts with the accesskey attribute.
  • Skip links aren't just for navigation. Consider using them for any web components that are repeated often throughout your application. Or for ad sections. Yuck.

GitHub Repo: tldr-angular-skip-links