Add a link to skip to the main content

While people using screen readers already have keyboard shortcuts to
jump to the <main> tag, there are people who navigate the page with the
keyboard using just the tab key, and for them, this link provides a way
to save time and start reading the main content instead of having to
manually go through all the navigation links every time a new page is
loaded.

Note that we had to add an additional `width: 0` rule because
Foundation's `element-invisible` would apply `1px` and the test checking
for `visible: :hidden` would faile.
This commit is contained in:
Javi Martín
2023-09-29 22:30:34 +02:00
parent 2b962f2789
commit 45c1f93562
16 changed files with 61 additions and 9 deletions

View File

@@ -0,0 +1,22 @@
.skip-to-main-content {
@include grid-column-gutter;
a {
&:not(:focus) {
@include element-invisible;
width: 0 !important;
}
&:focus {
@include body-colors;
$outline-size: $focus-inner-width + $focus-middle-width + $focus-outer-width;
padding: 0.4rem;
position: absolute;
$global-left: $outline-size;
top: $outline-size;
z-index: 1000;
}
}
}

View File

@@ -0,0 +1,3 @@
<div class="skip-to-main-content">
<%= link_to t("layouts.skip_to_main_content"), "#main" %>
</div>

View File

@@ -0,0 +1,2 @@
class Layout::SkipToMainContentComponent < ApplicationComponent
end

View File

@@ -7,6 +7,7 @@
</head>
<body class="admin">
<%= render Layout::SkipToMainContentComponent.new %>
<%= render Layout::AdminHeaderComponent.new(current_user) %>
<div class="menu-and-content">
@@ -22,7 +23,7 @@
<% end %>
</nav>
<main class="admin-content <%= yield(:main_class) %>">
<main id="main" class="admin-content <%= yield(:main_class) %>">
<%= label_tag :show_menu, t("admin.menu.admin"),
"aria-hidden": true, class: "button hollow expanded" %>

View File

@@ -13,12 +13,13 @@
<%= raw setting["html.per_page_code_head"] %>
</head>
<body class="<%= yield(:body_class) %> public">
<%= render Layout::SkipToMainContentComponent.new %>
<%= raw setting["html.per_page_code_body"] %>
<div class="wrapper <%= yield(:wrapper_class) %>">
<%= render "layouts/header", with_subnavigation: true %>
<main class="public-content <%= yield(:main_class) %>">
<main id="main" class="public-content <%= yield(:main_class) %>">
<%= render "layouts/flash" %>
<%= yield %>
</main>

View File

@@ -13,6 +13,7 @@
<%= raw setting["per_page_code_head"] %>
</head>
<body class="proposal-dashboard">
<%= render Layout::SkipToMainContentComponent.new %>
<%= raw setting["per_page_code_body"] %>
<h1 class="show-for-sr"><%= setting["org_name"] %></h1>
@@ -26,7 +27,7 @@
<%= render "dashboard/menu" %>
</nav>
<main class="admin-content <%= yield(:main_class) %>">
<main id="main" class="admin-content <%= yield(:main_class) %>">
<%= label_tag :show_menu, t("admin.menu.admin"),
"aria-hidden": true, class: "button hollow expanded" %>

View File

@@ -7,6 +7,7 @@
</head>
<body class="auth-page">
<%= render Layout::SkipToMainContentComponent.new %>
<%= raw setting["html.per_page_code_body"] %>
<div class="wrapper">
<div class="auth-image small-12 medium-3 column"
@@ -21,7 +22,7 @@
<div class="small-12 medium-9 column">
<div class="row">
<div class="small-12 medium-9 large-7 small-centered column">
<main class="auth-form margin <%= yield(:main_class) %>">
<main id="main" class="auth-form margin <%= yield(:main_class) %>">
<%= render "layouts/flash" %>
<%= yield %>

View File

@@ -7,6 +7,7 @@
</head>
<body class="admin">
<%= render Layout::SkipToMainContentComponent.new %>
<%= render Layout::AdminHeaderComponent.new(manager_logged_in) %>
<div class="menu-and-content">
@@ -16,7 +17,7 @@
<%= render "/management/menu" %>
</nav>
<main class="admin-content">
<main id="main" class="admin-content <%= yield(:main_class) %>">
<%= label_tag :show_menu, t("admin.menu.admin"),
"aria-hidden": true, class: "button hollow expanded" %>

View File

@@ -228,6 +228,7 @@ en:
zero: "You don't have new notifications"
notifications: Notifications
sdg: "SDG"
skip_to_main_content: "Skip to main content"
notifications:
index:
empty_notifications: You don't have new notifications.

View File

@@ -228,6 +228,7 @@ es:
zero: "No tienes notificaciones nuevas"
notifications: Notificaciones
sdg: "ODS"
skip_to_main_content: "Saltar al contenido principal"
notifications:
index:
empty_notifications: No tienes notificaciones nuevas.

View File

@@ -33,7 +33,7 @@
</head>
<body>
<main class="error">
<main id="main" class="error">
<h1>403</h1>
<h2>Access to this page has been disabled by the administrators.</h2>
</main>

View File

@@ -33,7 +33,7 @@
</head>
<body>
<main class="error">
<main id="main" class="error">
<h1>404</h1>
<h2>Not found.</h2>
</main>

View File

@@ -33,7 +33,7 @@
</head>
<body>
<main class="error">
<main id="main" class="error">
<h1>422</h1>
<h2>The change you wanted was rejected.</h2>
</main>

View File

@@ -33,7 +33,7 @@
</head>
<body>
<main class="error">
<main id="main" class="error">
<h1>500</h1>
<h2>Internal server error.</h2>
</main>

View File

@@ -62,6 +62,8 @@ module Capybara
unless url.match?("robots.txt") || url.match?("active_storage/representations")
expect(page).to have_css "main", count: 1
expect(page).to have_css "#main", count: 1
expect(page).to have_css "main#main"
end
end
end

View File

@@ -183,4 +183,20 @@ describe "Home" do
within(".header-card") { expect(page).not_to have_link }
end
end
describe "Link to skip to main content" do
it "is visible on focus" do
visit root_path
expect(page).to have_link "Skip to main content", visible: :hidden
expect(page).to have_css "main"
expect(page).not_to have_css "main:target"
page.execute_script("$('.skip-to-main-content a').focus()")
sleep 0.01 until page.has_link?("Skip to main content", visible: :visible)
click_link "Skip to main content"
expect(page).to have_css "main:target"
end
end
end