Bypass Blocks - Skip Links
Skip links help users to skip past blocks of content or repetitive content. A block of repetitive content SHOULD have some way to bypass them.
Providing correct landmarks and headings can help screen reader users to bypass the repeated content. To know more on providing landmarks to bypass the content, refer “Landmark” and “Landmark Regions” component.
NOTE:
- New to accessibility or uncertain of requirements, it will be helpful to review all sections below.
- Already familiar with requirements, skip to the “Working Example” section for sample HTML, CSS and JavaScript (when needed), along with a working demo.
- The landmark regions and headings will NOT work for keyboard only users as they both are screen reader oriented.
- Skip links CAN be provided to help keyboard only users to bypass the repeated content.
- The skip link should be provided before the content which needs to be bypassed.
- The link text MUST be at least 4.5:1 against the background color if it’s a standard text in default, focused and hover state.
- The minimum contrast requirement ratio of 3:1 for the large text or text with 14pts and bold MUST be met in default, focused and hover state.
-
Provide skip link at the start of the content using
<a>
element. For example, at the top of the page to skip to the main content.-
The id value of the targeted container SHOULD be referenced to the
<a>
element via href attribute. -
The targeted container SHOULD have
tabindex="-1"
to ensure cross browser compatibility.
-
The id value of the targeted container SHOULD be referenced to the
-
Appropriate link text SHOULD be given within the
<a>
element. For example, the skip link that take users to the main content can be specified with “Skip to main content” as link text. -
If an image is used to identify the skip links, then a textual description MUST
be defined for the image links.
-
If the image is defined using
<img>
element, use an alt attribute with descriptive value. -
If the image is defined using
<svg>
element, userole="img"
andaria-label
attribute to provide a role and an accessible name for the element.
Note: Providing ARIA based role and attribute on SVG image ensures robust support across different environments.
-
If the image is defined using
- Provide a clear visual focus indicator with a good contrast to the skip links. For more information refer to the Focus Indicator and Hover components.
Example,
<header>
<a href="#mainContent">Skip to Main content</a>
...
</header>
<main tabindex="-1" id="mainContent">
<h1>Main heading of the page</h1>
...
</main>
- Skip links also can be hidden and surfaced when the users programmatic focus is on the link.
- Skip links CAN be hidden using various CSS properties. For more information on hiding techniques visit https://css-tricks.com/comparing-various-ways-to-hide-things-in-css/
Example,
// Skip link //
<header>
<a href="#maincontent">Skip to Main content</a>
...
<style>
/* Clipping technique in CSS to place the link off-screen */
.show-on-focus {
position: absolute;
width: 1px;
height: 1px;
clip: rect(1px,1px,1px,1px);
top: 32px;
background: var(--nv-site-bg);
padding: 10px 15px;
}
/* When the skip link is focused */
.show-on-focus:focus {
z-index: 999999;
width: auto;
height: auto;
clip: auto;
}
</style>
</header>
<main tabindex="-1" id="maincontent">
<h1>Main heading of the page</h1>
...
</main>
A well-defined skip links benefits majorly the below users.
- People with limited dexterity
- People using keyboard only
- People using screen readers
<link href='//cdnjs.cloudflare.com/ajax/libs/boxicons/2.1.4/css/boxicons.min.css' rel='stylesheet'>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
<div id="nav-menu" role="banner">
<a href="#main" class="skip-link">Skip to Main Content</a>
<div class="nav-container">
<div class="nav-start">
<a class="logo" href="/"><span>Pearson</span></a>
<nav aria-label="Primary" class="menu">
<ul class="menu-bar" role="list">
<li>
<a class="nav-link" href="#">Home</a>
</li>
<li>
<a class="nav-link" href="#">About</a>
</li>
<li>
<a class="nav-link" href="#">Service</a>
</li>
<li>
<a class="nav-link" href="#">Contact</a>
</li>
<li>
<a class="nav-link" href="#">Careers</a>
</li>
</ul>
</nav>
</div>
<nav aria-label="Breadcrumb" class="nav-end">
<button id="hamburger" aria-label="hamburger" aria-haspopup="true" aria-expanded="false">
<i class="bx bx-menu" aria-hidden="true"></i>
</button>
</nav>
</div>
</div>
<div role="main" id="main">
<a href="#aside" class="skip-link">skip to right navigation</a>
<h1>Bypass blocks - Skip Links</h1>
<div class="container">
<div class="cards card-container">
<article class="card reorder">
<h2><a href="#" id="cardtitle2">StatCrunch</a></h2>
<img src="../common-images/educator.jpg" alt="" loading="lazy" width="400" height="200">
<p>
Put the power of data in students’ hands as they
analyze data sets easily from their text and exercises
</p>
<a href="#" aria-describedby="cardtitle2">
Explore StatCrunch
</a>
</article>
<article class="card reorder">
<h2><a href="#" id="cardtitle1">Learning Catalytics</a></h2>
<img src="../common-images/student-with-raised-hand.jpg" alt="" loading="lazy" width="400" height="200">
<p>
Hear from every student when it matters most as they
respond using their own smartphones, tablets or laptops
</p>
<a href="#">Explore Learning Catalytics</a>
</article>
<article class="card reorder">
<h2><a href="#" id="cardtitle3">MediaShare</a></h2>
<img src="../common-images/student-working-at-computer.jpg" alt="" loading="lazy" width="400" height="200">
<p>
Bring course concepts to life and engage students with
media-rich activities, created with Pearson or your own
content
</p>
<a href="#" aria-describedby="cardtitle3">
Explore MediaShare
</a>
</article>
</div>
<div class="aside" role="complementary" aria-labelledby="content-title" id="aside">
<a href="#footer" class="skip-link">Skip to Footer</a>
<div class="fld-press-release-report-item">
<div class="press-release-content-box">
<div class="section-heading-content-title" id="content-title">
<h2>Latest Annual Report</h2>
</div>
<div class="press-release-image-box">
<div class="press-release-content-image">
<img src="./investors-annual-report.png" alt="Annual Report 2022">
</div>
</div>
<div class="section-heading-link-download">
<ul role="list">
<li>
<a href="#" class="button-secondary filelink">
Annual Report 2022
</a>
</li>
<li>
<a href="#" class="button-secondary filelink">
Annual Report 2021
</a>
</li>
<li>
<a href="#" class="button-secondary filelink">
Annual Report 2020
</a>
</li>
<li>
<a href="#" class="button-secondary filelink">
Annual Report 2019
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="footer" role="contentinfo" id="footer">
<nav aria-label="Secondary">
<ul class="footer-link" role="list">
<li>
<a href="#">Terms of Use</a>
</li>
<li>
<a href="#">Privacy Statement</a>
</li>
<li>
<a href="#">Patent Notice</a>
</li>
<li>
<a href="#">Accessibility Statement</a>
</li>
<li>
<a href="#">Accessibility Conformance Reports</a>
</li>
<li>
<a href="#">Contact Us</a>
</li>
</ul>
</nav>
<p>© 1996–2023 Pearson All rights reserved.</p>
</div>
</div>
<button id="scroll-top-btn" class="scroll-top-btn" title="Go to top">
<span class="visually-hidden">Back To Top</span><i class="fas fa-arrow-up"></i>
</button>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Inter", sans-serif;
}
:root {
--light-grey: #eeeeee;
--border: 1px solid var(--light-grey);
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
}
button {
border: none;
background-color: transparent;
cursor: pointer;
color: inherit;
}
.icon {
padding: 0.5rem;
background-color: var(--light-grey);
border-radius: 10px;
}
.logo {
margin-right: 1.5rem;
}
#nav-menu {
border-bottom: var(--border);
}
.nav-container {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 2rem;
height: 90px;
padding: 1.2rem 3rem;
background-color: #005a70;
}
.menu {
position: relative;
background: #005a70;
color: #fff;
}
.menu-bar li:first-child .dropdown {
flex-direction: initial;
min-width: 480px;
}
.menu-bar li:first-child ul:nth-child(1) {
border-right: var(--border);
}
.menu-bar li:nth-child(n + 2) ul:nth-child(1) {
border-bottom: var(--border);
}
.menu-bar .dropdown-link-title {
font-weight: 600;
}
.menu-bar .nav-link {
font-size: 1rem;
font-weight: 500;
letter-spacing: -0.6px;
padding: 0.3rem;
min-width: 60px;
margin: 0 0.6rem;
}
.menu-bar .nav-link:hover,
.dropdown-link:hover {
text-decoration: underline;
}
.nav-start,
.nav-end,
.menu-bar,
.right-container,
.right-container .search {
display: flex;
align-items: center;
z-index: 1000;
}
#hamburger {
display: none;
padding: 0.1rem;
margin-left: 1rem;
font-size: 1.9rem;
}
@media (max-width: 1100px) {
#hamburger {
display: block;
color: #fff;
}
.nav-container {
padding: 1.2rem;
}
.menu {
display: none;
position: absolute;
top: 87px;
left: 0;
min-height: 100vh;
width: 100vw;
}
.menu-bar li:first-child ul:nth-child(1) {
border-right: none;
border-bottom: var(--border);
}
.dropdown {
display: none;
min-width: 100%;
border: none !important;
border-radius: 5px;
position: static;
top: 0;
left: 0;
visibility: visible;
opacity: 1;
transform: none;
box-shadow: none;
}
.menu.show,
.dropdown.active {
display: block;
}
.dropdown ul {
padding-left: 0.3rem;
}
.menu-bar {
display: flex;
flex-direction: column;
align-items: stretch;
row-gap: 1rem;
padding: 1rem;
}
.menu-bar .nav-link {
display: flex;
justify-content: space-between;
width: 100%;
font-weight: 600;
font-size: 1.2rem;
margin: 0;
}
.menu-bar li:first-child .dropdown {
min-width: 100%;
}
.menu-bar > li:not(:last-child) {
padding-bottom: 0.5rem;
border-bottom: var(--border);
}
}
.cards {
flex: 0 1 60%;
margin-right: 20px;
}
.cards h2 {
font-size: 18px;
margin: 10px 0;
color:rgb(0, 102, 204);
}
.cards p {
font-size: 16px;
line-height: 1.5;
}
#main {
margin: 20px;
padding: 20px;
}
.card-container {
display: flex;
flex-direction: row;
justify-content: center;
flex-wrap: wrap;
list-style: none;
margin: 0;
padding: 0;
}
.cards > * {
flex: 0 1 20em;
}
.card {
margin: .75em;
padding: .75em;
border-radius: 3px;
border: 2px #ccc solid;
border-radius: .6em;
background-color: white;
position: relative;
}
img {
width: 100%;
height: auto;
}
.reorder {
display: flex;
flex-direction: column;
}
.reorder img {
max-width: 100%;
order: -1;
}
.cards > * a {
display: block;
color: rgb(0, 102, 204);
}
.cards h2 > a {
text-decoration: none;
}
article:hover {
box-shadow: 0 0 0 0 0.25rem;
}
.footer {
position: relative;
bottom: 0;
width: 100%;
text-align: center;
padding: 20px 0;
margin: 0;
background-color: #007fa3;
color: #fff;
}
.footer ul li {
list-style: none;
display: inline-block;
padding: 0;
margin: 10px;
}
.footer ul li a {
font-size: 18px;
color: #fff;
}
.footer ul li a:hover {
text-decoration: underline;
}
.aside {
background-color: #f2f2f2;
padding: 20px;
margin: 20px;
flex: 0 1 30%;
height: fit-content;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: 2rem;
}
@media (max-width: 768px) {
.cards {
flex: 0 1 90%;
}
}
.fld-press-release-report-item {
padding-top: 0;
padding-left: 24px;
padding-bottom: 23px;
float: left;
}
.press-release-content-box{
display: block;
float: left;
max-width: 100%;
}
.section-heading-content-title h2 {
font-size: 24px;
font-weight: 700;
font-stretch: normal;
font-style: normal;
line-height: 1.42;
letter-spacing: 0.5px;
color: #000;
margin-right: 35px;
}
.section-heading-link-download a{
position: relative;
display: inline-block;
font-size: 16px;
font-weight: 700;
font-stretch: normal;
font-style: normal;
line-height: 1.5;
letter-spacing: 1px;
color: #000;
width: 100%;
}
.press-release-image-box {
display: block;
float: left;
margin-bottom: 3rem;
}
.press-release-content-image {
position: relative;
height: 275px;
width: 212px;
}
.press-release-content-image img {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
}
.press-release-content-image:after {
position: absolute;
content: "";
width: 212px;
height: 275px;
background: #fff;
border-radius: 2px;
z-index: 0;
left: 22px;
margin-top: 22px;
}
span {
font-size: 24px;
color: #fff;
}
.skip-link {
top: -40px;
left: 0;
background-color: #000;
color: #fff;
padding: 8px;
z-index: 999;
opacity: 1;
margin: 14px 0;
}
/* Hide skip links visually but keep them accessible */
.skip-link:not(:focus) {
clip: rect(0 0 0 0);
clip-path: inset(100%);
height: 1px;
overflow: hidden;
width: 1px;
white-space: nowrap;
opacity: 0;
}
h1 {
margin-top: 10px;
}
#scroll-top-btn {
display: none;
position: fixed;
bottom: 20px;
right: 30px;
z-index: 99;
font-size: 18px;
border: none;
outline: none;
background-color: #005a70;
color: white;
cursor: pointer;
padding: 15px;
border-radius: 4px;
}
.scroll-top-btn:focus {
border: 2px solid #000 !important;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
function handleSkipLinkClick(event) {
event.preventDefault();
const targetId = event.currentTarget.getAttribute("href").substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
targetElement.setAttribute("tabindex", "-1");
targetElement.focus();
const containers = document.querySelectorAll('[role="main"], [role="complementary"]');
containers.forEach((container) => {
if (container !== targetElement) {
container.removeAttribute("tabindex");
}
});
}
}
const skipLinks = document.querySelectorAll(".skip-link");
skipLinks.forEach((link) => {
link.addEventListener("click", handleSkipLinkClick);
});
const hamburgerBtn = document.getElementById("hamburger");
const navMenu = document.querySelector(".menu");
function toggleHamburger() {
navMenu.classList.toggle("show");
hamburgerBtn.setAttribute(
"aria-expanded",
hamburgerBtn.getAttribute("aria-expanded") === "false" ? "true" : "false"
);
}
hamburgerBtn.addEventListener("click", toggleHamburger);
let mybutton = document.getElementById("scroll-top-btn");
mybutton.addEventListener("click", topFunction);
window.onscroll = function () {
scrollFunction();
};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}