Lunchtime Drawtime happens Fridays at lunch in the NYC office, where a handful of folks get together and perform some acts of creation. Sometime we have prompts; today’s was bodies of water.
Here’s what we came up with. Enjoy!
For improved keyboard accessibility, use skip navigation links along with a coherent heading outline, ARIA landmarks, and a javascript polyfill for Webkit based browsers.
It can be frustrating and fatiguing for folks with limited mobility to have to have to repeatedly tab through navigation links to get to the main content of a page. People who use screen readers face similar frustration when the page outline is not well defined. In order to address this issue, version 2.0 of the Web Content Accessibility Guidelines (WCAG 2.0) has specified a guideline for bypassing repetitive blocks of content. One technique recommended by the W3C is to include a skip navigation link at the beginning of the page, that changes focus to the first element after the repeated content.
Add a link at the top of your body content whose href value points to the id of the element that wraps your primary content. Like so:
Disclaimer: The mechanism by which skip navigation links work had been broken in Webkit based browsers for some time and has only recently been fixed. Until these browsers release the fixes, you may need to use a javascript polyfill to make skip nav links work.
Skip nav links are useful for users who use keyboard navigation only, but screen readers now support more sophisticated ways of navigating regions. Specifically, they support heading navigation and ARIA landmarks. You should take advantage of these features by using a clear heading outline and defining page regions.
Note: The last post I authored, Use ARIA Landmarks Instead of Skip Nav Links, presented a strategy that was predicated in part on the fact that skip navigation links have been broken in Webkit for the past three years. This bug has recently been fixed, so I was inspired to revisit the topic.
This is a followup post to Testing accessibility with RSpec and Capybara, in which I explored strategies for automated testing of skip navigation links. We needed to provide a way to skip repetitive navigation, in line with part (o) of the Section 508 accessibility standards, and we went with skip navigation links.
We’ve since taken an alternative approach to the skip navigation technique, since this technique does not change keyboard focus to the targeted content. Instead, we ended up creating an aria landmark using the role attribute with a value of ‘main’. It looks something like this:
The role attribute can be set to a variety of values to help screen readers evaluate the structure of a page. Then the user can use a combination of hotkeys to navigate to these landmarks. In Safari with VoiceOver for example, one would press the VoiceOver keys + U to activate the Rotor, and be presented with a list of page landmarks (including ‘main’) that when navigated to, will immediately get focus and be at the beginning of the read order. JAWS, ChromeVox, and NVDA have similar behavior.
Using an ARIA landmark with a role of ‘main’ is a more robust and elegant solution than using skip navigation links. This technique is also well supported across user agents, and yields disproportionate gains in accessibility compared to development time, making it an easy win for more accessible content.
Edit 04/07/13: See the followup article for an alternative to using skip navigation links.
Today Grant Hutchins and I took on several stories to enhance the accessibility of a site. One of them was to add a skip-navigation link to the application.
To understand why skip-nav links are important, visit Jim Thatcher’s explanation.
Our immediate inclination was to write a request spec with Capybara to drive out the solution. We came up with the following.
require "spec_helper"
feature "Keyboard Navigation" do
scenario "hidden skip navigation link shows when focused and jumps to content", js: true do
login_as(users(:user))
visit root_path
skip_link = page.find("#skip-navigation a")
skip_link.should have_content "Skip navigation"
skip_link.native.location.y.should be < 0
body_element = page.find("body")
body_element.native.send_keys(:tab)
skip_link.native.location.y.should == 0
skip_link.native.send_keys(:return)
skip_link.native.location.y.should be < 0
current_url.should match(/#content$/)
end
end
%body
#skip-navigation
%p= link_to "Skip navigation", "#content", tabindex: 0
...
#content
...
body {
#skip-navigation {
a, a:hover, a:visited {
position:absolute;
left:0px;
top:-500px;
overflow:hidden;
}
a:active, a:focus {
position:absolute;
left:0;
top:0;
}
}
}
We are asserting that the “hidden skip navigation link shows when focused and jumps to content” when clicked.
The most important aspect of what we did was emulating keyboard navigation. We’re using js: true so we have access to Selenium’s native methods and thus the send_keys method. This allows us to send keypress messages to the browser.
Since we’ve used positioning to hide the element, we also have an assertion around that property.
The application behaved as expected and the tests passed locally.
When we ran the tests in CI however, the tests failed. The reason is that _the browser must retain foremost focus in the OS in order for the :focus css pseudo-selector to fire on the skip-nav element. Without the :focus style rules applied, the skip-nav element remained invisible, and the tests failed.
We tried a workaround using within_window and forcing browser focus, but couldn’t get it to work. We’ve got a few more tricks up our sleeve that we’re going to try, and will report back here.
Edit:
We added within_window to the test to force focus on the browser. This makes the test less brittle locally (because it won’t fail if you click out of the window).
window = page.driver.browser.window_handles.last
page.within_window(window) do
skip_link.native.location.y.should == 0
end
SNL Ticket Lottery Open
Lottery for SNL tickets is open. Email snltickets@nbcuni.com once during the month of August with full contact details to enter.
Full details at nbc.com
rspec shared_example names do not get scoped within describe blocks
As of rspec 2.6, shared examples are global. You can declare them in a describe block, but they are not scoped to that description. This ultimately means that shared_example names should be unique across your test suite!