Technique C43:Using CSS scroll-padding
to un-obscure content
About this Technique
This technique relates to:
- 2.4.11: Focus Not Obscured (Minimum) (Sufficient)
- 2.4.12: Focus Not Obscured (Enhanced) (Sufficient)
This technique applies to all technologies that support CSS.
Description
The objective of this technique is to ensure that user interface components (for example: links, buttons, and form fields) that are initially completely obscured by a fixed-position component can still be accessed by users. In this example, this is achieved using CSS padding
and scroll-padding
properties to create space underneath the site footer and allow the link in the footer to scroll into view when it is focused with a keyboard.
Examples
Example 1: Using CSS scroll-padding
to un-obscure content
This example shows a situation where there is a fixed-position banner at the bottom of the screen that is covering up the site footer, which contains a link. This type of fixed-position banner is a common design for cookie-consent banners.
Working example: Using CSS scroll-padding
to un-obscure content.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Using CSS scroll-padding to un-obscure content</title>
<style>
...
:root{
--height-dialog: 400px;
--breathing-room: 20px;
--scroll-padding: calc(var(--height-dialog) + var(--breathing-room));
}
.wrapper {
display:grid;
gap:1rem;
grid-template-columns:repeat(9, 1fr);
grid-template-rows:8rem auto minmax(10rem, max-content);
min-block-size: 100vh;
}
.wrapper > * {
border:1px solid var(--black);
padding:1rem;
}
header {
grid-column:1 / -1;
grid-row:1;
}
main {
grid-column:1 / 8;
}
aside {
grid-column:8 / 10;
}
footer {
grid-column:1 / -1;
}
@media (max-width:50rem) {
main {
grid-column:1 / -1;
}
aside {
grid-column:1 / -1;
}
}
.fixed-position-banner {
background:var(--banner-background);
border:3px solid var(--banner-border);
margin-block-end:0.5rem;
padding:1rem 1rem 5rem;
position:relative;
width:calc(100vw - 1rem);
}
@media (min-width: 50rem) {
html {
padding-bottom:var(--height-dialog);
scroll-padding-bottom:var(--scroll-padding);
}
.fixed-position-banner {
margin-block-end:0;
position:fixed;
inset:auto 0 0 0;
}
}
...
</style>
</head>
<body>
<dialog class="fixed-position-banner">
<h2 tabindex="-1">Fixed-Position Banner</h2>
<button aria-label="close fixed-position banner" class="close-banner" type="button">
...
</button>
</dialog>
<div class="wrapper">
<header>
<p>Header Content</p>
</header>
<main>
<h1>Main Content</h1>
</main>
<aside>
<h2>Sidebar Content</h2>
<p><a href="https://example.com">Here's an example link in the sidebar</a>.</p>
</aside>
<footer>
<h2>Footer Content</h2>
<p><a href="https://example.com">Here's an example link in the footer</a>.</p>
</footer>
</div>
<script>
window.addEventListener('DOMContentLoaded', () => {
const elFixedBanner = document.querySelector('dialog');
const elCloseBannerBtn = document.querySelector(".close-banner");
elFixedBanner.show();
const getDialogHeight = () => {
const height = elFixedBanner.offsetHeight;
document.documentElement.style.setProperty('--height-dialog', `${height}px`);
document.documentElement.style.setProperty('--breathing-room', `${height ? 20 : 0}px`);
}
const observer = new ResizeObserver(getDialogHeight);
observer.observe(elFixedBanner);
elCloseBannerBtn.addEventListener("click", function(e){
elFixedBanner.close();
}, false);
});
</script>
</body>
</html>
Related Resources
No endorsement implied.
Tests
Procedure
For each user interface component that can receive keyboard focus:
- Check that the user interface component is not entirely hidden when it receives keyboard focus.
- Check that no part of the user interface component is hidden when it receives keyboard focus.
Expected Results
- Check #1 is true.
- For Focus Not Obscured (Enhanced) (Level AAA), #2 is also true.