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.

Expected Results

  • Check #1 is true.
Back to Top