Skip to content

Technique C43:Using CSS scroll-padding to un-obscure content

About this Technique

This technique relates to:

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.

  1. W3C - CSS padding.
  2. W3C - CSS the scroll-padding property.

Tests

Procedure

For each user interface component that can receive keyboard focus:

  1. Check that the user interface component is not entirely hidden when it receives keyboard focus.
  2. 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.
Back to Top